feat: implement upload pipeline with compression and SSE

Backend:
- POST /api/v1/upload: multipart file upload with caption + hashtags
  - Validates file size against DB config limits (image/video separate)
  - Checks user ban status and event upload lock
  - Saves original to disk under {media_path}/originals/{slug}/
  - Tracks user total_upload_bytes for quota enforcement
  - Extracts hashtags from caption text and explicit CSV field
  - Upserts hashtags and links them to uploads
- PATCH /api/v1/upload/{id}: edit caption and hashtags (owner only)
- DELETE /api/v1/upload/{id}: soft-delete (owner only)
- GET /api/v1/stream: SSE endpoint with 30s keepalive
  - Broadcasts new-upload events to all connected clients
  - Uses tokio broadcast channel for fan-out

Services:
- CompressionWorker: Tokio semaphore-bounded (concurrency=2) background processor
  - Images: resize to 800px wide JPEG preview via image crate
  - PNG originals: lossless compression via oxipng
  - Videos: ffmpeg thumbnail extraction (1 frame at 1s, scaled to 800px)
  - Updates upload record with preview_path/thumbnail_path on completion

Models:
- Upload with full CRUD (create, find, update caption, soft delete, set paths)
- Hashtag with upsert, link/unlink, extract_hashtags() text parser
- UploadDto for API serialization with like/comment counts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
fabi
2026-03-31 21:48:59 +02:00
parent 8b9d916265
commit 3f052a4f91
12 changed files with 635 additions and 3 deletions

3
backend/Cargo.lock generated
View File

@@ -894,6 +894,7 @@ dependencies = [
"bcrypt",
"chrono",
"dotenvy",
"futures",
"image",
"jsonwebtoken",
"minijinja",
@@ -905,6 +906,7 @@ dependencies = [
"sqlx",
"sysinfo",
"tokio",
"tokio-stream",
"tower",
"tower-http",
"tower_governor",
@@ -3253,6 +3255,7 @@ dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
"tokio-util",
]
[[package]]