From 8eb0821c9f32fe07dd4e961c877dd9188fb80222 Mon Sep 17 00:00:00 2001 From: michael Date: Fri, 19 Dec 2025 06:25:02 -0600 Subject: [PATCH] Delete associated files when proof or campaign is deleted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, deleting a proof or campaign only removed database records, leaving orphaned files in the storage directory. Now the delete endpoints extract file_storage_key from proof versions and delete files via storage_service before removing database records. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- backend/app/api/routes.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/backend/app/api/routes.py b/backend/app/api/routes.py index 6337be0..ba97a1f 100755 --- a/backend/app/api/routes.py +++ b/backend/app/api/routes.py @@ -31,6 +31,7 @@ from app.repositories import ( AuditRepository, DropdownRepository, ) +from app.services.storage_service import storage_service router = APIRouter() @@ -192,12 +193,23 @@ async def delete_campaign( db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user), ): - """Delete a campaign.""" + """Delete a campaign and all associated files.""" repo = CampaignRepository(db) - success = await repo.delete(campaign_id) - if not success: + + # Get campaign with proofs and versions to extract file keys + campaign = await repo.get_by_id(campaign_id) + if not campaign: raise HTTPException(status_code=404, detail="Campaign not found") + # Delete files from storage for all proofs + for proof in campaign.proofs: + for version in proof.versions: + if version.file_storage_key: + await storage_service.delete_file(version.file_storage_key) + + # Delete database records (cascades to proofs and versions) + await repo.delete(campaign_id) + # Proof endpoints @router.get("/campaigns/{campaign_id}/proofs", response_model=list[ProofResponse]) @@ -279,12 +291,22 @@ async def delete_proof( db: AsyncSession = Depends(get_db), user: dict = Depends(get_current_user), ): - """Delete a proof.""" + """Delete a proof and its associated files.""" repo = ProofRepository(db) - success = await repo.delete(proof_id) - if not success: + + # Get proof with versions to extract file keys + proof = await repo.get_by_id(proof_id) + if not proof: raise HTTPException(status_code=404, detail="Proof not found") + # Delete files from storage + for version in proof.versions: + if version.file_storage_key: + await storage_service.delete_file(version.file_storage_key) + + # Delete database records + await repo.delete(proof_id) + # Audit endpoints @router.post("/proofs/{proof_id}/versions/{version}/flag", response_model=FlaggedItemResponse, status_code=201)