feat(iios): media sharing — presigned storage port + attachments on messages
Deploy iios-service / build-deploy (push) Failing after 3m35s
CI / build (push) Successful in 3m47s

Media the industry way: the DB stores a reference, bytes live behind a storage port.
- StoragePort + LocalDiskStorage (dev). MediaService presigns short-lived signed
  upload/download URLs (HS256 tokens) pointing at IIOS's own endpoints; the bytes
  never touch the kernel. Prod swaps STORAGE_PORT to S3/Supabase — same as auth.
- MediaController: presign-upload / PUT upload/:token (raw stream) / GET blob/:token /
  presign-download. Uploads are OPA-governed (iios.media.upload: 25 MB cap +
  image/video/audio/pdf/office allowlist); downloads are tenant-fenced by object key.
- send() + MessageDto carry an attachment (contentRef/mimeType/sizeBytes → generic
  MEDIA_REF/VOICE_REF/FILE_REF part; DTO exposes kind image|video|audio|file).

Tests: media.service.spec (6) — round-trip, oversize/type denied, oversized PUT
refused, tampered token rejected, tenant fence. Full suite 192 green. Verified live:
presign→upload→send→history→signed download round-trips the exact bytes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-09 01:15:03 +05:30
parent 1256664361
commit 24a87f6fb6
13 changed files with 362 additions and 7 deletions
+2
View File
@@ -9,6 +9,7 @@ import { OutboxModule } from './outbox/outbox.module';
import { ThreadsModule } from './threads/threads.module';
import { MessageModule } from './messaging/message.module';
import { InboxModule } from './inbox/inbox.module';
import { MediaModule } from './media/media.module';
import { SupportModule } from './support/support.module';
import { AdaptersModule } from './adapters/adapters.module';
import { RoutingModule } from './routing/routing.module';
@@ -33,6 +34,7 @@ import { DevController } from './dev/dev.controller';
ThreadsModule,
MessageModule,
InboxModule,
MediaModule,
SupportModule,
AdaptersModule,
RoutingModule,