fix: burn file items after download, not metadata fetch

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
肉末
2026-07-14 12:48:42 +08:00
parent eda7010cae
commit f2a1f3a1d6
3 changed files with 51 additions and 7 deletions
+17
View File
@@ -1,8 +1,11 @@
from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, Request, UploadFile
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session
from starlette.background import BackgroundTask
from app import db as db_module
from app.db import get_db
from app.models import Item
from app.schemas import ItemCreate, ItemOut, TTLOption, WallResponse
from app.services import items as items_service
from app.services import storage as storage_service
@@ -72,6 +75,16 @@ def wall(
)
def _burn_file_after_response(item_id: int) -> None:
db = db_module.SessionLocal()
try:
item = db.get(Item, item_id)
if item is not None and item.burn_after_read and not item.burned:
items_service.burn_file_item(db, item)
finally:
db.close()
@router.get("/{slug}/file")
def download_file(slug: str, db: Session = Depends(get_db)):
item = items_service.get_file_item(db, slug)
@@ -84,11 +97,15 @@ def download_file(slug: str, db: Session = Depends(get_db)):
media_type = item.mime or "application/octet-stream"
filename = item.file_name or path.name
inline = media_type.startswith(_INLINE_IMAGE_PREFIXES)
background = None
if item.burn_after_read:
background = BackgroundTask(_burn_file_after_response, item.id)
return FileResponse(
path,
media_type=media_type,
filename=filename,
content_disposition_type="inline" if inline else "attachment",
background=background,
)
+14 -7
View File
@@ -134,20 +134,27 @@ def get_file_item(db: Session, slug: str) -> Item | None:
return item
def burn_file_item(db: Session, item: Item) -> None:
"""Mark a file item burned and remove its bytes from disk."""
item.burned = True
path = resolve_file_path(item)
db.commit()
if path is not None:
try:
path.unlink(missing_ok=True)
except OSError:
pass
def get_item(db: Session, slug: str) -> Item | None:
item = db.scalar(select(Item).where(Item.slug == slug))
if item is None or _is_unavailable(item):
return None
item.view_count += 1
if item.burn_after_read:
# File items burn on download, not metadata fetch.
if item.burn_after_read and item.kind != "file":
item.burned = True
path = resolve_file_path(item)
if path is not None:
try:
path.unlink(missing_ok=True)
except OSError:
pass
db.commit()
db.refresh(item)
return item