"""Thumbnail endpoint: previews addressed only by asset ID and bounded size. The browser never supplies a filesystem path. Errors return a consistent JSON envelope with the service's typed code and status; successful responses carry the image with an immutable cache header (the URL's identity comes from the versioned, content-keyed cache). """ from __future__ import annotations from fastapi import APIRouter, Query, Request from fastapi.responses import FileResponse, JSONResponse from photo_pipeline.services.thumbnails import ThumbnailError, ThumbnailService router = APIRouter(tags=["thumbnails"]) @router.get("/assets/{asset_id}/thumbnail") def get_thumbnail(asset_id: str, request: Request, size: int = Query(512)): service = ThumbnailService( request.app.state.session_factory, request.app.state.config ) try: path = service.generate(asset_id, size) except ThumbnailError as error: return JSONResponse( status_code=error.http_status, content={"error": {"code": error.code, "message": str(error)}}, ) return FileResponse( path, media_type="image/webp", # private: the URL is versioned and immutable, but these bytes are the user's # photos and must never sit in a shared cache (US07-02). headers={"Cache-Control": "private, max-age=31536000, immutable"}, )