250e47b06e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3.2 KiB
3.2 KiB
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 nomessages). - 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 (
messagescommediaPath != null) — evitalistObjectscaro. - 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 enviardryRun:false. - Coordenado DB ↔ Wasabi:
storageProvider.deleteObjects(paths)(chunks de 1000, S3DeleteObjectsCommand) +updateManyzeramediaPath/mediaUrl(sem link quebrado). Filtra pathshttp*(CDN externo) — só apaga objetos do bucket. StorageProvider.deleteObjects(paths)adicionado à interface, ao registry e ao provider concreto (UnifiedStorageProvider), além dodeletePrefixjá 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
Messagenão guardamediaSize— agregado de bytes exigiriaHeadObject/listObjects. Avaliar adicionar colunamediaSize. - Local fallback (dev sem Wasabi):
mediaPathé caminho absoluto local; odeleteObjectssó 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.transcriptionsalva, 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.