WIP checkpoint: Address review feedback: Address review feedback: Scaffold FastAPI backend project structure

This commit is contained in:
2026-08-17 23:42:56 +00:00
parent 48a43c44a5
commit 241fa9d0e0
2 changed files with 37 additions and 19 deletions
+27 -10
View File
@@ -1,5 +1,7 @@
"""Asset management router."""
import os
from pathlib import Path
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Query
@@ -51,6 +53,17 @@ class AssetUpdate(BaseModel):
is_public: Optional[bool] = None
# --- Helpers ---
def _sanitize_filename(filename: str) -> str:
"""Strip path components and unsafe characters from a filename."""
# Remove any directory components (path traversal protection).
base = os.path.basename(filename.replace("\\", "/"))
# Keep only safe characters.
safe = "".join(c for c in base if c.isalnum() or c in "._-")
return safe or "file"
# --- Endpoints ---
@router.post("/", response_model=AssetResponse, status_code=201)
@@ -155,11 +168,11 @@ async def delete_asset(
await db.commit()
@router.post("/{asset_id}/upload")
@router.post("/{asset_id}/upload", response_model=AssetResponse)
async def upload_asset_file(
asset_id: int,
file: UploadFile = File(...),
current_user: CurrentUser = None,
current_user: CurrentUser = Depends(),
db: AsyncSession = Depends(get_db),
):
"""Upload a file for an existing asset."""
@@ -167,11 +180,10 @@ async def upload_asset_file(
if asset is None or asset.owner_id != current_user.id:
raise HTTPException(status_code=404, detail="Asset not found")
# Sanitize filename to prevent path traversal
safe_filename = file.filename.replace("/", "_").replace("\\", "_") if file.filename else f"asset_{asset_id}"
file_path = f"uploads/{asset_id}_{safe_filename}"
# Sanitize filename to prevent path traversal.
safe_filename = _sanitize_filename(file.filename or f"asset_{asset_id}")
# Validate file size against configured maximum
# Validate file size against configured maximum.
max_bytes = settings.MAX_UPLOAD_SIZE_MB * 1024 * 1024
content = await file.read()
file_size = len(content)
@@ -181,12 +193,17 @@ async def upload_asset_file(
detail=f"File size {file_size} exceeds maximum allowed size {max_bytes} bytes",
)
# TODO: Implement actual file write logic (S3, local filesystem, etc.)
asset.file_path = file_path
# Write the file to the upload directory.
upload_dir = Path(settings.UPLOAD_DIR)
upload_dir.mkdir(parents=True, exist_ok=True)
stored_name = f"{asset_id}_{safe_filename}"
file_path = upload_dir / stored_name
file_path.write_bytes(content)
asset.file_path = str(file_path)
asset.mime_type = file.content_type
asset.file_size = file_size
await db.commit()
await db.refresh(asset)
return {"message": "File upload endpoint", "asset_id": asset_id, "file_path": file_path}
return asset