fix: harden week-two ordering, artwork and operations
This commit is contained in:
@@ -12,6 +12,7 @@ from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
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 ..runtime import PART_BYTES, storage, upload_row
|
||||
|
||||
@@ -19,8 +20,8 @@ router = APIRouter()
|
||||
|
||||
@router.post('/api/uploads')
|
||||
def begin_upload(body: UploadStart, session_id=Depends(owner)):
|
||||
if body.size > int(os.environ.get('MAX_UPLOAD_BYTES', '5368709120')):
|
||||
raise HTTPException(413, 'File exceeds the upload limit')
|
||||
if body.size > upload_limit_bytes():
|
||||
raise HTTPException(413, 'File exceeds the malware scan limit; select a smaller file')
|
||||
uid = uuid4()
|
||||
key = f'originals/{uid}'
|
||||
rate_limit('upload-start', str(session_id), 60, 900)
|
||||
@@ -30,14 +31,16 @@ def begin_upload(body: UploadStart, session_id=Depends(owner)):
|
||||
usage = c.execute('''SELECT COALESCE(sum(size),0) AS total,
|
||||
COALESCE(sum(size) FILTER(WHERE owner=%s),0) AS owned,
|
||||
count(*) FILTER(WHERE owner=%s AND NOT complete) AS pending
|
||||
FROM dtf_local.uploads WHERE purged_at IS NULL''', (session_id,session_id)).fetchone()
|
||||
FROM dtf_local.uploads WHERE purged_at IS NULL''',
|
||||
(session_id,session_id)).fetchone()
|
||||
if (usage['total']+body.size > int(os.environ.get('STORAGE_QUOTA_BYTES','53687091200')) or
|
||||
usage['owned']+body.size > int(os.environ.get('OWNER_UPLOAD_QUOTA_BYTES','10737418240')) or
|
||||
usage['pending'] >= int(os.environ.get('MAX_PENDING_UPLOADS','10'))):
|
||||
audit('upload_quota_rejected')
|
||||
raise HTTPException(429, 'Local storage quota or pending upload limit reached')
|
||||
multipart = storage.begin(key)
|
||||
c.execute('INSERT INTO dtf_local.uploads(id,owner,name,size,object_key,multipart_id) VALUES(%s,%s,%s,%s,%s,%s)',
|
||||
c.execute('''INSERT INTO dtf_local.uploads(id,owner,name,size,object_key,multipart_id,expires_at)
|
||||
VALUES(%s,%s,%s,%s,%s,%s,now()+interval '1 hour')''',
|
||||
(uid, session_id, body.name, body.size, key, multipart))
|
||||
return {'id': uid, 'part_bytes': PART_BYTES}
|
||||
|
||||
@@ -81,5 +84,16 @@ 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 WHERE id=%s', (uid,))
|
||||
c.execute("UPDATE dtf_local.uploads SET complete=true,expires_at=now()+interval '30 days' WHERE id=%s", (uid,))
|
||||
return {'id': uid, 'complete': True}
|
||||
|
||||
@router.delete('/api/uploads/{uid}')
|
||||
def cancel_upload(uid: UUID, session_id=Depends(owner)):
|
||||
with db.connect() as c:
|
||||
row=upload_row(c,uid,session_id,lock=True)
|
||||
if row['complete']:
|
||||
raise HTTPException(409, 'Completed upload cannot be cancelled')
|
||||
storage.discard(row['object_key'],row['multipart_id'],False)
|
||||
c.execute('UPDATE dtf_local.uploads SET purged_at=now() WHERE id=%s',(uid,))
|
||||
audit('upload_cancelled', upload=str(uid))
|
||||
return {'id':uid,'cancelled':True}
|
||||
|
||||
Reference in New Issue
Block a user