no-mistakes(review): Fix orphaned photo files and add upload content-type validation

This commit is contained in:
root
2026-07-23 17:21:46 +00:00
parent 31ae78ec07
commit 2de3c8514c
+27 -4
View File
@@ -224,6 +224,10 @@ async def check_sla(
# ── Photo Uploads ────────────────────────────────────────────────────
ALLOWED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
ALLOWED_MIME_TYPES = {"image/jpeg", "image/png", "image/gif", "image/webp"}
@router.post("/{ticket_id}/photos", response_model=list[TicketPhotoOut], status_code=status.HTTP_201_CREATED)
async def upload_photos(
ticket_id: int,
@@ -236,18 +240,32 @@ async def upload_photos(
# Verify ticket exists
await ticket_service.get_ticket(db, ticket_id)
# Validate all files before any write operations
for file in files:
if file.content_type not in ALLOWED_MIME_TYPES:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unsupported content type '{file.content_type}'. Allowed: image/jpeg, image/png, image/gif, image/webp",
)
ext = Path(file.filename or "photo.jpg").suffix.lower() if file.filename else ".jpg"
if ext not in ALLOWED_EXTENSIONS:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unsupported file extension '{ext}'. Allowed: .jpg, .jpeg, .png, .gif, .webp",
)
created_photos: list[TicketPhoto] = []
file_data: list[tuple[Path, bytes]] = []
for file in files:
# Generate unique filename
ext = Path(file.filename or "photo.jpg").suffix if file.filename else ".jpg"
ext = Path(file.filename or "photo.jpg").suffix.lower() if file.filename else ".jpg"
unique_name = f"{uuid.uuid4().hex}{ext}"
file_path = UPLOADS_DIR / unique_name
# Write file
content = await file.read()
file_path.write_bytes(content)
file_data.append((file_path, content))
# Database record
# Database record (no file write yet)
photo = TicketPhoto(
ticket_id=ticket_id,
photo_url=f"/uploads/{unique_name}",
@@ -259,6 +277,11 @@ async def upload_photos(
await db.flush()
for p in created_photos:
await db.refresh(p)
# Write files to disk only after successful DB flush
for file_path, content in file_data:
file_path.write_bytes(content)
return created_photos