feat: an unpaid cart's files are kept 2 days, a paid order's 30

Files are uploaded before payment so the price and the security check use
the file itself, but an abandoned cart kept them for 30 days. Now a finished
upload is held 2 days, a quote waiting for review 7, an approved quote 2 more
to be paid, and the paid order keeps its originals for 30 days from upload.
A payment never starts for files that are gone; one under way holds them a
day. Files attached to an order take the order's window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cauê Faleiros
2026-09-30 12:09:02 -03:00
parent eb254da501
commit 933bd30cbd
7 changed files with 71 additions and 2 deletions

View File

@@ -14,6 +14,7 @@ from ..core import db
from ..core.auth import audit, owner, rate_limit
from ..core.limits import upload_limit_bytes
from ..core.models import UploadStart
from ..payments import UNPAID_HOLD
from ..runtime import PART_BYTES, storage, upload_row
router = APIRouter()
@@ -87,7 +88,10 @@ def complete_upload(uid: UUID, session_id=Depends(owner)):
existing_size = storage.size(row['object_key'])
if existing_size != row['size']:
raise HTTPException(409, 'Stored size differs from declared size')
c.execute("UPDATE dtf_local.uploads SET complete=true,expires_at=now()+interval '30 days' WHERE id=%s", (uid,))
# Held while the cart is unpaid: an abandoned cart's files go after
# UNPAID_HOLD; a paid order keeps them for its 30 days (app/payments.py).
c.execute('UPDATE dtf_local.uploads SET complete=true,expires_at=now()+%s::interval WHERE id=%s',
(UNPAID_HOLD, uid))
return {'id': uid, 'complete': True}
@router.delete('/api/uploads/{uid}')