Skip to content

SDD-009 — Media & Image Management

Một Media Service dùng chung toàn Nemo12 (horizontal capability như Interaction/Notification). Bài học legacy (sutucon): ảnh base64 lưu thẳng trong D1 (learner_product_submission_images, product_feedback_request_images, ≤200KB, hidden_at moderation) — không scale, phình DB; IMAGES (Cloudflare Images) và R2 khai báo nhưng chưa dùng. Chuyenchon: 5 R2 buckets rời rạc theo app. Nemo12 làm đúng ngay từ đầu.

1. Kiến trúc

text
Client → api.nemo12.com /v1/media
   ├─ D1: media registry (metadata, ownership, moderation, versions)
   ├─ R2: nemo12-content — bytes gốc, key media/<yyyy>/<id>/<filename>
   └─ Cloudflare Images (transformations): variants (thumb, card, full) qua CDN
Event: MediaUploaded / MediaModerated → Queue → moderation + AI analysis

2. Media Registry (REQ-MED-01)

text
media: id · owner_user_id · learner_id? · target_type · target_id · kind(image|audio|video|document)
· file_name(≤160) · mime_type · byte_size · width/height · checksum_sha256 · r2_key
· visibility(private|learner_parent|mentor_team|school|community|public)
· moderation_status(pending|approved|hidden|blocked) · hidden_at · created_at · version

target_type + target_id giống Interaction (SDD-005): ảnh gắn vào submission, evidence, comment, learner report, content asset, school page… Một ảnh tái sử dụng nhiều target qua bảng media_links.

3. Upload flow (REQ-MED-02)

  1. Client POST /v1/media/uploads (metadata + intent) → validate type/size/quota → trả direct upload URL (R2 presigned hoặc Images direct upload) — bytes không đi qua Worker (tránh giới hạn CPU/body).
  2. Client PUT bytes → confirm POST /v1/media/:id/complete → verify checksum + head object → status pending.
  3. Event MediaUploaded → Queue → moderation pipeline.

Giới hạn mặc định: image ≤10MB (PNG/JPG/WebP/HEIC), document ≤25MB (PDF), audio/video theo nhu cầu sau; quota per user/ngày. Fallback nhỏ: ảnh ≤200KB được phép inline base64 một chiều để tương thích import legacy, nhưng luôn được chuyển sang R2 bởi worker nền (không lưu vĩnh viễn trong D1).

4. Delivery (REQ-MED-03)

  • Variants qua Cloudflare Images: thumb 160px · card 640px · full 1600px, auto-format (AVIF/WebP).
  • URL ký (signed) cho visibility ≠ public; authorization backend theo relationship (parent↔family, mentor↔assignment) trước khi cấp URL.
  • Cache CDN; content_ref trong content system (SDD-004) trỏ media id.

5. Moderation & Safety — K12 (REQ-MED-04)

Mọi media learner/parent upload: pending → AI classify (nudity/violence/PII/inappropriate — qua AI Gateway) → auto-approve ngưỡng an toàn hoặc vào moderation queue (admin) → approve/hide/block + audit log. Soft-hide bằng hidden_at (pattern sutucon). EXIF strip mặc định; cấm serve ảnh chưa approved cho visibility community/public.

6. Lifecycle & Versioning (REQ-MED-05)

Immutable theo version; thay ảnh = version mới (giữ provenance). Retention: media mồ côi (không link) dọn sau 30 ngày; xóa theo yêu cầu chủ thể dữ liệu (child privacy — REQ-SEC-04) = hide ngay + purge R2 theo lịch, audit giữ metadata tối thiểu.

7. AI illustrations (REQ-MED-06 ⏳ scope)

Pipeline sinh ảnh minh họa content (kế thừa mori: flux-1-schnell + llava review) ghi vào cùng registry với source=ai_generated, provenance model/prompt — bật sau khi content pipeline chạy (Q-051).

Trace

REQ-MED-01→§2 · 02→§3 · 03→§4 · 04→§5 · 05→§6 · 06→§7.