Skip to content

Commit fb3d1ba

Browse files
authored
Merge pull request #4551 from magfest/add-file-service
Overhaul file uploads
2 parents 6875411 + 40ebb1b commit fb3d1ba

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1353
-964
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
"""Overhaul file uploads
2+
3+
Revision ID: 4df6bfee2c69
4+
Revises: 6902e1cceec6
5+
Create Date: 2026-03-11 20:40:21.564545
6+
7+
"""
8+
9+
10+
# revision identifiers, used by Alembic.
11+
revision = '4df6bfee2c69'
12+
down_revision = '6902e1cceec6'
13+
branch_labels = None
14+
depends_on = None
15+
16+
from alembic import op
17+
import sqlalchemy as sa
18+
from sqlalchemy.dialects import postgresql
19+
20+
21+
try:
22+
is_sqlite = op.get_context().dialect.name == 'sqlite'
23+
except Exception:
24+
is_sqlite = False
25+
26+
if is_sqlite:
27+
op.get_context().connection.execute('PRAGMA foreign_keys=ON;')
28+
utcnow_server_default = "(datetime('now', 'utc'))"
29+
else:
30+
utcnow_server_default = "timezone('utc', current_timestamp)"
31+
32+
def sqlite_column_reflect_listener(inspector, table, column_info):
33+
"""Adds parenthesis around SQLite datetime defaults for utcnow."""
34+
if column_info['default'] == "datetime('now', 'utc')":
35+
column_info['default'] = utcnow_server_default
36+
37+
sqlite_reflect_kwargs = {
38+
'listeners': [('column_reflect', sqlite_column_reflect_listener)]
39+
}
40+
41+
# ===========================================================================
42+
# HOWTO: Handle alter statements in SQLite
43+
#
44+
# def upgrade():
45+
# if is_sqlite:
46+
# with op.batch_alter_table('table_name', reflect_kwargs=sqlite_reflect_kwargs) as batch_op:
47+
# batch_op.alter_column('column_name', type_=sa.Unicode(), server_default='', nullable=False)
48+
# else:
49+
# op.alter_column('table_name', 'column_name', type_=sa.Unicode(), server_default='', nullable=False)
50+
#
51+
# ===========================================================================
52+
53+
54+
def upgrade():
55+
# ### commands auto generated by Alembic - please adjust! ###
56+
op.create_table('file',
57+
sa.Column('id', sa.Uuid(as_uuid=False), nullable=False),
58+
sa.Column('created', sa.DateTime(timezone=True), nullable=False),
59+
sa.Column('last_updated', sa.DateTime(timezone=True), nullable=False),
60+
sa.Column('external_id', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
61+
sa.Column('last_synced', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
62+
sa.Column('fk_id', sa.Uuid(as_uuid=False), nullable=False),
63+
sa.Column('fk_model', sa.Unicode(), nullable=False),
64+
sa.Column('description', sa.Unicode(), nullable=False),
65+
sa.Column('filename', sa.Unicode(), nullable=False),
66+
sa.Column('content_type', sa.Unicode(), nullable=False),
67+
sa.Column('extension', sa.Unicode(), nullable=False),
68+
sa.Column('filepath', sa.Unicode(), nullable=False),
69+
sa.Column('download_url', sa.Unicode(), nullable=False),
70+
sa.Column('flags', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
71+
sa.PrimaryKeyConstraint('id', name=op.f('pk_file'))
72+
)
73+
op.drop_table('guest_image')
74+
op.drop_table('indie_game_image')
75+
op.drop_table('mits_picture')
76+
op.drop_table('guest_track')
77+
op.drop_table('mits_document')
78+
op.drop_column('guest_stage_plot', 'content_type')
79+
op.drop_column('guest_stage_plot', 'filename')
80+
op.drop_index(op.f('uq_promo_code_word_normalized_word_part_of_speech'), table_name='promo_code_word')
81+
op.create_index('uq_promo_code_word_normalized_word_part_of_speech', 'promo_code_word', [sa.literal_column('lower(trim(word))'), 'part_of_speech'], unique=True)
82+
# ### end Alembic commands ###
83+
84+
85+
def downgrade():
86+
# ### commands auto generated by Alembic - please adjust! ###
87+
op.drop_index('uq_promo_code_word_normalized_word_part_of_speech', table_name='promo_code_word')
88+
op.create_index(op.f('uq_promo_code_word_normalized_word_part_of_speech'), 'promo_code_word', [sa.literal_column('lower(TRIM(BOTH FROM word))'), 'part_of_speech'], unique=True)
89+
op.add_column('guest_stage_plot', sa.Column('filename', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False))
90+
op.add_column('guest_stage_plot', sa.Column('content_type', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False))
91+
op.create_table('mits_document',
92+
sa.Column('id', sa.UUID(), autoincrement=False, nullable=False),
93+
sa.Column('filename', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
94+
sa.Column('description', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
95+
sa.Column('game_id', sa.UUID(), autoincrement=False, nullable=False),
96+
sa.Column('created', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, CURRENT_TIMESTAMP)"), autoincrement=False, nullable=False),
97+
sa.Column('last_updated', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, CURRENT_TIMESTAMP)"), autoincrement=False, nullable=False),
98+
sa.Column('external_id', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), autoincrement=False, nullable=False),
99+
sa.Column('last_synced', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), autoincrement=False, nullable=False),
100+
sa.ForeignKeyConstraint(['game_id'], ['mits_game.id'], name=op.f('fk_mits_document_game_id_mits_game'), ondelete='CASCADE'),
101+
sa.PrimaryKeyConstraint('id', name=op.f('pk_mits_document'))
102+
)
103+
op.create_table('guest_track',
104+
sa.Column('id', sa.UUID(), autoincrement=False, nullable=False),
105+
sa.Column('created', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, CURRENT_TIMESTAMP)"), autoincrement=False, nullable=False),
106+
sa.Column('last_updated', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, CURRENT_TIMESTAMP)"), autoincrement=False, nullable=False),
107+
sa.Column('external_id', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), autoincrement=False, nullable=False),
108+
sa.Column('last_synced', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), autoincrement=False, nullable=False),
109+
sa.Column('guest_id', sa.UUID(), autoincrement=False, nullable=False),
110+
sa.Column('filename', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
111+
sa.Column('content_type', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
112+
sa.Column('extension', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
113+
sa.ForeignKeyConstraint(['guest_id'], ['guest_group.id'], name=op.f('fk_guest_track_guest_id_guest_group'), ondelete='CASCADE'),
114+
sa.PrimaryKeyConstraint('id', name=op.f('pk_guest_track')),
115+
sa.UniqueConstraint('guest_id', name=op.f('uq_guest_track_guest_id'), postgresql_include=[], postgresql_nulls_not_distinct=False)
116+
)
117+
op.create_table('mits_picture',
118+
sa.Column('id', sa.UUID(), autoincrement=False, nullable=False),
119+
sa.Column('filename', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
120+
sa.Column('content_type', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
121+
sa.Column('extension', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
122+
sa.Column('description', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
123+
sa.Column('game_id', sa.UUID(), autoincrement=False, nullable=False),
124+
sa.Column('created', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, CURRENT_TIMESTAMP)"), autoincrement=False, nullable=False),
125+
sa.Column('last_updated', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, CURRENT_TIMESTAMP)"), autoincrement=False, nullable=False),
126+
sa.Column('external_id', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), autoincrement=False, nullable=False),
127+
sa.Column('last_synced', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), autoincrement=False, nullable=False),
128+
sa.Column('is_header', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False),
129+
sa.Column('is_thumbnail', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False),
130+
sa.ForeignKeyConstraint(['game_id'], ['mits_game.id'], name=op.f('fk_mits_picture_game_id_mits_game'), ondelete='CASCADE'),
131+
sa.PrimaryKeyConstraint('id', name=op.f('pk_mits_picture'))
132+
)
133+
op.create_table('indie_game_image',
134+
sa.Column('id', sa.UUID(), autoincrement=False, nullable=False),
135+
sa.Column('game_id', sa.UUID(), autoincrement=False, nullable=False),
136+
sa.Column('filename', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
137+
sa.Column('content_type', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
138+
sa.Column('extension', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
139+
sa.Column('description', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
140+
sa.Column('is_screenshot', sa.BOOLEAN(), server_default=sa.text('true'), autoincrement=False, nullable=False),
141+
sa.Column('use_in_promo', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False),
142+
sa.Column('created', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, CURRENT_TIMESTAMP)"), autoincrement=False, nullable=False),
143+
sa.Column('last_updated', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, CURRENT_TIMESTAMP)"), autoincrement=False, nullable=False),
144+
sa.Column('external_id', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), autoincrement=False, nullable=False),
145+
sa.Column('last_synced', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), autoincrement=False, nullable=False),
146+
sa.Column('is_header', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False),
147+
sa.Column('is_thumbnail', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False),
148+
sa.ForeignKeyConstraint(['game_id'], ['indie_game.id'], name=op.f('fk_indie_game_image_game_id_indie_game'), ondelete='CASCADE'),
149+
sa.PrimaryKeyConstraint('id', name=op.f('pk_indie_game_image'))
150+
)
151+
op.create_table('guest_image',
152+
sa.Column('filename', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
153+
sa.Column('content_type', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
154+
sa.Column('extension', sa.VARCHAR(), server_default=sa.text("''::character varying"), autoincrement=False, nullable=False),
155+
sa.Column('is_header', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False),
156+
sa.Column('is_thumbnail', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False),
157+
sa.Column('id', sa.UUID(), autoincrement=False, nullable=False),
158+
sa.Column('created', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, CURRENT_TIMESTAMP)"), autoincrement=False, nullable=False),
159+
sa.Column('last_updated', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, CURRENT_TIMESTAMP)"), autoincrement=False, nullable=False),
160+
sa.Column('external_id', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), autoincrement=False, nullable=False),
161+
sa.Column('last_synced', postgresql.JSONB(astext_type=sa.Text()), server_default=sa.text("'{}'::jsonb"), autoincrement=False, nullable=False),
162+
sa.Column('guest_id', sa.UUID(), autoincrement=False, nullable=False),
163+
sa.ForeignKeyConstraint(['guest_id'], ['guest_group.id'], name=op.f('fk_guest_image_guest_id_guest_group'), ondelete='CASCADE'),
164+
sa.PrimaryKeyConstraint('id', name=op.f('pk_guest_image'))
165+
)
166+
op.drop_table('file')
167+
# ### end Alembic commands ###

uber/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from uber import server # noqa: F401
2323
from uber import tasks # noqa: F401
2424
from uber import validations # noqa: F401
25+
from uber import files # noqa: F401
2526
from uber.serializer import serializer # noqa: F401
2627

2728
# NOTE: this will decrease the precision of some serialized decimal.Decimals

uber/configspec.ini

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,14 +1051,7 @@ privateKey = string(default="")
10511051
[[terminal_ids]]
10521052

10531053
[data_dirs]
1054-
# Directories on the server filesystem where uploaded files are saved
10551054
uploaded_files_dir = string(default="%(root)s/uploaded_files")
1056-
guests_bio_pics_dir = string(default="%(uploaded_files_dir)s/guests_bio_pics")
1057-
guests_w9_forms_dir = string(default="%(uploaded_files_dir)s/guests_w9_forms")
1058-
guests_stage_plots_dir = string(default="%(uploaded_files_dir)s/guests_stage_plots")
1059-
guests_inventory_dir = string(default="%(uploaded_files_dir)s/guests_inventory")
1060-
mivs_game_image_dir = string(default="%(uploaded_files_dir)s/mivs_game_images")
1061-
mits_picture_dir = string(default="%(uploaded_files_dir)s/mits_game_images")
10621055

10631056

10641057
[dates]

uber/custom_tags.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import sys
1717
from datetime import datetime, timedelta
1818
from urllib.parse import quote_plus
19+
from pytz import UTC
1920

2021
import cherrypy
2122
import jinja2

0 commit comments

Comments
 (0)