"""Add media_assets table. Revision ID: 040 Revises: 039 """ import sqlalchemy as sa from alembic import op from sqlalchemy.dialects.postgresql import UUID, JSONB revision = '040' down_revision = '039' branch_labels = None depends_on = None def upgrade(): # Use raw SQL throughout for full idempotency (enum may exist from a partial run) op.execute(""" DO $$ BEGIN CREATE TYPE media_asset_type AS ENUM ( 'thumbnail','still','turntable','stl_low','stl_high', 'gltf_geometry','gltf_production','blend_production' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; """) op.execute(""" CREATE TABLE IF NOT EXISTS media_assets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE, product_id UUID REFERENCES products(id) ON DELETE CASCADE, cad_file_id UUID REFERENCES cad_files(id) ON DELETE SET NULL, order_line_id UUID REFERENCES order_lines(id) ON DELETE SET NULL, workflow_run_id UUID REFERENCES workflow_runs(id) ON DELETE SET NULL, asset_type media_asset_type NOT NULL, storage_key TEXT NOT NULL, file_size_bytes BIGINT, mime_type VARCHAR(100), width INTEGER, height INTEGER, duration_s FLOAT, render_config JSONB, is_archived BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMP NOT NULL DEFAULT NOW() ) """) op.execute("CREATE INDEX IF NOT EXISTS ix_media_assets_product ON media_assets (product_id)") op.execute("CREATE INDEX IF NOT EXISTS ix_media_assets_tenant ON media_assets (tenant_id)") op.execute("CREATE INDEX IF NOT EXISTS ix_media_assets_order_line ON media_assets (order_line_id)") op.execute("CREATE INDEX IF NOT EXISTS ix_media_assets_asset_type ON media_assets (asset_type)") op.execute("ALTER TABLE media_assets ENABLE ROW LEVEL SECURITY") op.execute(""" DO $$ BEGIN CREATE POLICY tenant_isolation ON media_assets USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid); EXCEPTION WHEN duplicate_object THEN NULL; END $$; """) op.execute(""" DO $$ BEGIN CREATE POLICY admin_bypass ON media_assets USING (current_setting('app.current_tenant_id', true) = 'bypass'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; """) def downgrade(): op.drop_table('media_assets') op.execute("DROP TYPE IF EXISTS media_asset_type")