diff --git a/backend/routers/media_assets.py b/backend/routers/media_assets.py index 6fd65a6..8b264df 100644 --- a/backend/routers/media_assets.py +++ b/backend/routers/media_assets.py @@ -102,6 +102,27 @@ _MEDIA_KIND_FILTERS = frozenset({"all", "image", "video", "pdf", "other"}) _MAX_TAGS = 40 _MAX_TAG_LEN = 48 +_MEDIA_ASSETS_TAGS_COLUMN: Optional[bool] = None + + +def _media_assets_tags_column_present(cur: Any) -> bool: + """True nach Migration 046; verhindert 500 wenn Code neuer ist als das Schema.""" + global _MEDIA_ASSETS_TAGS_COLUMN + if _MEDIA_ASSETS_TAGS_COLUMN is not None: + return _MEDIA_ASSETS_TAGS_COLUMN + cur.execute( + """ + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'media_assets' + AND column_name = 'tags' + LIMIT 1 + """ + ) + _MEDIA_ASSETS_TAGS_COLUMN = cur.fetchone() is not None + return _MEDIA_ASSETS_TAGS_COLUMN + def _normalize_media_tags(raw: Union[list[str], None]) -> list[str]: """PostgreSQL text[]; Einträge gekürzt und ohne Dubletten (case-insensitive).""" @@ -479,6 +500,9 @@ def list_media_assets( if uploaded_by is not None and not show_uploader: raise HTTPException(status_code=403, detail="Uploader-Filter nicht erlaubt") + has_tags_col = _media_assets_tags_column_present(cur) + tags_select = "ma.tags," if has_tags_col else "ARRAY[]::text[] AS tags," + uploaded_sql = "" uploaded_params: list[Any] = [] if uploaded_by is not None: @@ -489,12 +513,19 @@ def list_media_assets( search_params: list[Any] = [] if needle: like = f"%{needle}%" - search_params = [like, like, like, like] - search_sql = ( - " AND (ma.original_filename ILIKE %s OR ma.storage_key ILIKE %s" - " OR COALESCE(ma.copyright_notice, '') ILIKE %s" - " OR EXISTS (SELECT 1 FROM unnest(ma.tags) AS t WHERE t::text ILIKE %s))" - ) + if has_tags_col: + search_params = [like, like, like, like] + search_sql = ( + " AND (ma.original_filename ILIKE %s OR ma.storage_key ILIKE %s" + " OR COALESCE(ma.copyright_notice, '') ILIKE %s" + " OR EXISTS (SELECT 1 FROM unnest(ma.tags) AS t WHERE t::text ILIKE %s))" + ) + else: + search_params = [like, like, like] + search_sql = ( + " AND (ma.original_filename ILIKE %s OR ma.storage_key ILIKE %s" + " OR COALESCE(ma.copyright_notice, '') ILIKE %s)" + ) params: list[Any] = ( [is_adm, profile_id, profile_id] @@ -507,7 +538,7 @@ def list_media_assets( cur.execute( f"""SELECT ma.id, ma.mime_type, ma.byte_size, ma.original_filename, ma.visibility, ma.club_id, ma.uploaded_by_profile_id, ma.lifecycle_state, ma.created_at, ma.sha256, - ma.copyright_notice, ma.storage_key, ma.tags, + ma.copyright_notice, ma.storage_key, {tags_select} pr.name AS uploader_name, pr.email AS uploader_email, cl.name AS club_name @@ -682,6 +713,15 @@ def bulk_media_patch( asset = r2d(row) assert_can_edit_media_asset_metadata(cur, tenant, asset) + if "tags" in patch_fields and not _media_assets_tags_column_present(cur): + failed.append( + { + "id": asset_id, + "detail": "Schlagwörter (tags) erfordern DB-Migration 046.", + } + ) + continue + eff = _effective_media_patch_fields(patch_fields, asset) next_vis = str(eff.get("visibility", asset["visibility"])).strip().lower() next_cid = eff["club_id"] if "club_id" in eff else asset.get("club_id") @@ -753,6 +793,12 @@ def patch_media_asset( asset = r2d(row) assert_can_edit_media_asset_metadata(cur, tenant, asset) + if "tags" in data and not _media_assets_tags_column_present(cur): + raise HTTPException( + status_code=503, + detail="Schlagwörter (tags) erfordern die Datenbank-Migration 046. Bitte Migration ausführen.", + ) + eff = _effective_media_patch_fields(data, asset) next_vis = str(eff.get("visibility", asset["visibility"])).strip().lower() next_cid = eff["club_id"] if "club_id" in eff else asset.get("club_id") @@ -789,11 +835,22 @@ def patch_media_asset( tuple(vals), ) conn.commit() - cur.execute( - """SELECT id, mime_type, byte_size, original_filename, visibility, club_id, - uploaded_by_profile_id, lifecycle_state, created_at, sha256, copyright_notice, tags - FROM media_assets WHERE id = %s""", - (asset_id,), - ) + has_tags = _media_assets_tags_column_present(cur) + if has_tags: + cur.execute( + """SELECT id, mime_type, byte_size, original_filename, visibility, club_id, + uploaded_by_profile_id, lifecycle_state, created_at, sha256, copyright_notice, tags + FROM media_assets WHERE id = %s""", + (asset_id,), + ) + else: + cur.execute( + """SELECT id, mime_type, byte_size, original_filename, visibility, club_id, + uploaded_by_profile_id, lifecycle_state, created_at, sha256, copyright_notice + FROM media_assets WHERE id = %s""", + (asset_id,), + ) out = r2d(cur.fetchone()) - return out + if not has_tags: + out["tags"] = [] + return out