"""Defect photos — upload to PM volume, serve (LAN-internal, unauthenticated).""" import uuid from fastapi import APIRouter, Depends, UploadFile from fastapi.responses import FileResponse from sqlalchemy.ext.asyncio import AsyncSession from app.authz import AuthContext, Perm, require_permission from app.config import settings from app.db import get_db from app.errors import ValidationError router = APIRouter(prefix="/files", tags=["files"]) ALLOWED = {"image/jpeg": ".jpg", "image/png": ".png", "image/webp": ".webp"} MAX_SIZE = 10 * 1024 * 1024 # photos are attached to QC defects, so both roles may upload upload_file = require_permission(Perm.files_upload) @router.post("") async def upload( file: UploadFile, _: AuthContext = Depends(upload_file), db: AsyncSession = Depends(get_db), ): _ = db ext = ALLOWED.get(file.content_type or "") if ext is None: raise ValidationError("فقط تصویر jpg/png/webp پذیرفته میشود", field="file") data = await file.read() if len(data) > MAX_SIZE: raise ValidationError("حجم فایل بیش از حد مجاز است (۱۰ مگابایت)", field="file") settings.FILES_DIR.mkdir(parents=True, exist_ok=True) file_id = uuid.uuid4().hex path = settings.FILES_DIR / f"{file_id}{ext}" path.write_bytes(data) return {"data": {"id": file_id, "url": f"/api/v1/files/{file_id}", "filename": file.filename}} @router.get("/{file_id}") async def serve(file_id: str, db: AsyncSession = Depends(get_db)): _ = db if not file_id.isalnum(): raise ValidationError("شناسه فایل نامعتبر است") for ext in ALLOWED.values(): path = settings.FILES_DIR / f"{file_id}{ext}" if path.exists(): media = next(k for k, v in ALLOWED.items() if v == ext) return FileResponse(path, media_type=media) from app.errors import NotFoundError raise NotFoundError("فایل یافت نشد")