Task: Hardened Post & Banner Media Upload Pipeline (Hard)
Overview
Uploads cover avatars only. Extend storage-adapter uploads into a hardened media pipeline for post media + creator banners: validation, scan hook, replace/delete prior objects, ownership, and multi-MIME policy.
Scope (must all ship)
Endpoints
POST /api/v1/creators/me/posts/:id/media — multipart; owner only
POST /api/v1/creators/me/banner — multipart
- Optional
DELETE to clear banner/media
Validation
- MIME allowlists distinct for image vs video (config)
- Size caps: avatar vs banner vs post media (env: document in
.env.example)
- Randomize object keys; never use user filename path segments
- Virus-scan hook stub invoked pre-persist (
ClamAvPort no-op)
Storage
- Reuse
StorageAdapter
- Replace media deletes prior object (best-effort; DB URL update transactional with compensations documented)
- Persist URL on
Post.mediaUrl / CreatorProfile.bannerUrl (or media json array if multi — if multi, define max count)
Authz
- Only post owner; banner only profile owner
- Auth required
Errors
- Structured
400 with stable code (UNSUPPORTED_MEDIA_TYPE, PAYLOAD_TOO_LARGE, etc.)
Testing (≥10)
- Owner success paths
- Non-owner 403
- Bad MIME / oversized
- Replace deletes old (mock adapter assert)
- Unauth 401
- Scan hook invoked
- Randomized key unit test
Deliverables
- Controllers/services, env example, Swagger multipart,
docs/uploads-media.md, ≥10 tests
Acceptance Criteria
Out of scope
- HLS transcoding
- Direct-to-S3 multipart from browser (server-mediated OK)
Task: Hardened Post & Banner Media Upload Pipeline (Hard)
Overview
Uploads cover avatars only. Extend storage-adapter uploads into a hardened media pipeline for post media + creator banners: validation, scan hook, replace/delete prior objects, ownership, and multi-MIME policy.
Scope (must all ship)
Endpoints
POST /api/v1/creators/me/posts/:id/media— multipart; owner onlyPOST /api/v1/creators/me/banner— multipartDELETEto clear banner/mediaValidation
.env.example)ClamAvPortno-op)Storage
StorageAdapterPost.mediaUrl/CreatorProfile.bannerUrl(ormediajson array if multi — if multi, define max count)Authz
Errors
400with stablecode(UNSUPPORTED_MEDIA_TYPE,PAYLOAD_TOO_LARGE, etc.)Testing (≥10)
Deliverables
docs/uploads-media.md, ≥10 testsAcceptance Criteria
Out of scope