""" Router — Files : sert les fichiers locaux via URLs signées HMAC. Monté uniquement quand STORAGE_BACKEND == "local". """ import logging from pathlib import Path from fastapi import APIRouter, HTTPException, status from fastapi.responses import FileResponse from app.config import settings from app.services.storage_backend import get_storage_backend, LocalStorage logger = logging.getLogger(__name__) router = APIRouter(prefix="/files", tags=["Fichiers"]) @router.get( "/signed/{token}", summary="Télécharger un fichier via URL signée", description="Valide le token HMAC et retourne le fichier correspondant.", ) async def serve_signed_file(token: str): """Sert un fichier local via un token HMAC signé.""" backend = get_storage_backend() if not isinstance(backend, LocalStorage): raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Endpoint non disponible avec le backend de stockage actuel", ) # Valider le token path = backend.validate_token(token) if path is None: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Token invalide ou expiré", ) # Vérifier que le fichier existe abs_path = backend.get_absolute_path(path) if not abs_path.exists(): raise HTTPException( status_code=status.HTTP_410_GONE, detail="Le fichier n'existe plus", ) # Détecter le content type suffix = abs_path.suffix.lower() mime_map = { ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".png": "image/png", ".gif": "image/gif", ".webp": "image/webp", ".bmp": "image/bmp", ".tiff": "image/tiff", } media_type = mime_map.get(suffix, "application/octet-stream") return FileResponse( path=str(abs_path), media_type=media_type, filename=abs_path.name, )