feat: layout hamburger, media browser filters+previews, billing fixes

- Layout: mobile hamburger menu + overlay backdrop + close button; content area always full-width
- Media browser: filter chips (default still+turntable); advanced toggle for GLB/STL; thumbnail_url previews for non-image types; video hover-play for turntable
- Backend: asset_types multi-filter, thumbnail_url in MediaAssetOut, download proxy endpoint for MinIO/local files
- Admin: "Import Existing Media" button → POST /api/admin/import-media-assets
- Billing: fix invoice create 500 (MissingGreenlet — use selectinload after commit); PDF download uses axios blob instead of bare <a href> (auth header missing); fix storage.upload() accepting str|Path
- SSE task logs: task_logs.py core + router, LiveRenderLog component
- CadPreview: fix infinite loop when no gltf_geometry assets; loading screen before ThreeDViewer render
- render-worker: add trimesh layer to Dockerfile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 00:09:27 +01:00
parent 9bf6e72718
commit f5ca91ee02
25 changed files with 792 additions and 299 deletions
+5
View File
@@ -1,6 +1,7 @@
"""Celery tasks for STEP file processing and thumbnail generation."""
import logging
from app.tasks.celery_app import celery_app
from app.core.task_logs import log_task_event
logger = logging.getLogger(__name__)
@@ -268,9 +269,11 @@ def generate_gltf_geometry_task(self, cad_file_id: str):
step_path_str = cad_file.stored_path
eng.dispose()
log_task_event(self.request.id, f"Starting generate_gltf_geometry_task: cad_file={cad_file_id}", "info")
step = _Path(step_path_str)
stl_path = step.parent / f"{step.stem}_low.stl"
if not stl_path.exists():
log_task_event(self.request.id, f"Failed: STL cache not found: {stl_path}", "error")
logger.error("generate_gltf_geometry_task: STL not found %s", stl_path)
raise RuntimeError(f"STL cache not found: {stl_path}")
@@ -279,8 +282,10 @@ def generate_gltf_geometry_task(self, cad_file_id: str):
import trimesh
mesh = trimesh.load(str(stl_path))
mesh.export(str(output_path))
log_task_event(self.request.id, f"Completed successfully: {output_path.name}", "done")
logger.info("generate_gltf_geometry_task: exported %s", output_path.name)
except Exception as exc:
log_task_event(self.request.id, f"Failed: {exc}", "error")
logger.error("generate_gltf_geometry_task failed for %s: %s", cad_file_id, exc)
raise self.retry(exc=exc, countdown=15)