Files
Documentos-Unicos/projetos/newwhats.clube67.com/wasabi-midias.md
T
2026-06-28 14:53:07 +02:00

60 lines
3.2 KiB
Markdown

# newwhats.clube67.com — Wasabi & mídias (index + delete em lote)
> Estado atual + desenho do que falta. Storage = **Wasabi (S3-compatível)** via
> `StorageProvider` (`backend/src/core/StorageProvider.ts`). Última revisão: 28/jun/2026.
## Como funciona hoje
- **Upload** (`uploadFile`): fire-and-forget após receber a mídia/avatar; grava no
bucket e salva no DB **apenas o path relativo** (ex.: avatar →
`contact.avatarUrl`; mídia de mensagem → caminho no `messages`).
- **Leitura**: proxy público no `server.ts` (sem authMiddleware, pois `<img>/<audio>/
<video>` não mandam header) → busca bytes no Wasabi; se não houver, faz fallback
ao CDN do WhatsApp e re-persiste.
- **Delete**: só `StorageProvider.deletePrefix(prefix)` → `{ deleted }`. Apaga por
**prefixo** (ex.: tudo de uma instância). Não há listagem nem delete seletivo.
### Convenção de path (prefixos)
Os paths são hierárquicos por `tenant/instância/...`, o que permite delete em lote
por prefixo. Confirmar o layout exato no `StorageProvider` antes de implementar
(avatar usa `avatarWasabiPath(tenantId, instanceId, jid)`).
## Implementado (28/jun/2026)
### 1. Index de mídias ✅
`GET /api/instances/:instanceId/media-index` (auth + escopo tenant/instância).
- Baseado no **DB** (`messages` com `mediaPath != null`) — evita `listObjects` caro.
- Query: `chatId?`, `type?` (IMAGE|VIDEO|AUDIO|DOCUMENT|STICKER), `from?`/`to?` (ISO),
`page`, `pageSize` (≤200).
- Retorna `{ total, page, pageSize, byType, items[] }`.
### 2. Delete em lote ✅
`POST /api/instances/:instanceId/media-batch-delete`. Body:
`{ chatId?, type?, from?, to?, dryRun? }`.
- **dryRun=true é o padrão** — só conta (`matched`, `willDeleteObjects`). Para apagar
de fato é preciso enviar `dryRun:false`.
- Coordenado **DB ↔ Wasabi**: `storageProvider.deleteObjects(paths)` (chunks de 1000,
S3 `DeleteObjectsCommand`) + `updateMany` zera `mediaPath`/`mediaUrl` (sem link
quebrado). Filtra paths `http*` (CDN externo) — só apaga objetos do bucket.
- `StorageProvider.deleteObjects(paths)` adicionado à interface, ao registry e ao
provider concreto (UnifiedStorageProvider), além do `deletePrefix` já existente.
> Validado em dev: index retornou 289 mídias (byType ok); dry-run de AUDIO casou 23.
### Pendência restante
- Index por **tamanho em bytes**: o `Message` não guarda `mediaSize` — agregado de
bytes exigiria `HeadObject`/`listObjects`. Avaliar adicionar coluna `mediaSize`.
- **Local fallback** (dev sem Wasabi): `mediaPath` é caminho absoluto local; o
`deleteObjects` só roda quando o storage provider está registrado. Em produção
(Wasabi ativo) o path é a key relativa do bucket — fluxo correto.
- Reconciliação DB ↔ bucket para achar **órfãos** (objeto sem registro).
### 3. Retenção
- Após `messages.transcription` salva, o **áudio pode ser arquivado/descartado**
(a Secretária já "entende" pelo texto). Job de retenção por idade/tamanho.
## Cuidados
- Delete é destrutivo e externo (Wasabi) — **sempre dry-run + confirmação**.
- Não apagar avatar/mídia ainda referenciada por outra mensagem (dedup por path).
- Operações em lote: throttle para não estourar limites do S3.
</content>