chore: snapshot workflow migration progress

This commit is contained in:
2026-04-12 11:49:04 +02:00
parent 0cd02513d5
commit 3e810c74a3
163 changed files with 31774 additions and 2753 deletions
+14 -41
View File
@@ -10,6 +10,7 @@ from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.core.render_paths import resolve_result_path
from app.domains.auth.models import User
from app.domains.media.models import MediaAsset, MediaAssetType
from app.domains.media.schemas import MediaAssetOut, MediaAssetBrowseItem, MediaAssetBrowseResponse
@@ -19,6 +20,10 @@ from app.utils.auth import get_current_user
router = APIRouter(prefix="/api/media", tags=["media"], redirect_slashes=False)
def _resolve_asset_candidate(key: str):
return resolve_result_path(key)
async def _resolve_thumbnails_bulk(db: AsyncSession, assets: list) -> None:
"""Resolve thumbnail_url for assets using the same priority as product pages.
@@ -275,15 +280,8 @@ async def thumbnail_asset(
raise HTTPException(404, "Not a previewable asset")
key = asset.storage_key
from app.config import settings
candidate = Path(key) if Path(key).is_absolute() else Path(settings.upload_dir) / key
if not candidate.exists() and "/shared/renders/" in key:
parts = key.split("/")
if len(parts) >= 2:
remapped = Path(settings.upload_dir) / "renders" / parts[-2] / parts[-1]
if remapped.exists():
candidate = remapped
if candidate.exists():
candidate = _resolve_asset_candidate(key)
if candidate is not None and candidate.exists():
return FileResponse(
str(candidate), media_type=mime,
headers={"Cache-Control": "max-age=86400, public"},
@@ -314,22 +312,8 @@ async def download_asset(
mime = asset.mime_type or "application/octet-stream"
# Local file path (absolute or relative to UPLOAD_DIR)
from app.config import settings
candidate = Path(key)
if not candidate.is_absolute():
candidate = Path(settings.upload_dir) / key
# Legacy path remapping: /shared/renders/{uuid}/{file} → UPLOAD_DIR/renders/{uuid}/{file}
if not candidate.exists() and "/shared/renders/" in key:
import logging
parts = key.split("/")
if len(parts) >= 2:
remapped = Path(settings.upload_dir) / "renders" / parts[-2] / parts[-1]
if remapped.exists():
logging.getLogger(__name__).warning(
"Remapped legacy path %s%s", key, remapped
)
candidate = remapped
if candidate.exists():
candidate = _resolve_asset_candidate(key)
if candidate is not None and candidate.exists():
ext = candidate.suffix.lstrip(".")
fname = f"{asset.asset_type.value}_{asset_id}.{ext or 'bin'}"
return FileResponse(
@@ -395,11 +379,8 @@ async def zip_download(
fname = base
try:
# Check absolute path first (local filesystem)
candidate = Path(key)
if not candidate.is_absolute():
from app.config import settings
candidate = Path(settings.upload_dir) / key
if candidate.exists():
candidate = _resolve_asset_candidate(key)
if candidate is not None and candidate.exists():
data = candidate.read_bytes()
else:
data = storage.download_bytes(key)
@@ -440,7 +421,7 @@ async def batch_delete_assets(
):
"""Permanently delete multiple MediaAsset records."""
from app.utils.auth import require_global_admin
require_global_admin(_user)
await require_global_admin(_user)
deleted = 0
for aid in asset_ids:
@@ -461,23 +442,15 @@ async def cleanup_orphaned_assets(
"""
import logging
from pathlib import Path
from app.config import settings
from app.core.storage import get_storage
logger = logging.getLogger(__name__)
storage = get_storage()
def _file_exists(key: str) -> bool:
candidate = Path(key) if Path(key).is_absolute() else Path(settings.upload_dir) / key
if candidate.exists():
candidate = _resolve_asset_candidate(key)
if candidate is not None and candidate.exists():
return True
# Legacy path remapping
if "/shared/renders/" in key:
parts = key.split("/")
if len(parts) >= 2:
remapped = Path(settings.upload_dir) / "renders" / parts[-2] / parts[-1]
if remapped.exists():
return True
# Check MinIO
try:
storage.download_bytes(key)