no-mistakes(review): Fix orphaned photo files and add upload content-type validation
This commit is contained in:
+27
-4
@@ -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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user