docs(wasabi): index e delete em lote implementados (endpoints + deleteObjects)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -29,9 +29,12 @@
|
||||
## Wasabi / mídias
|
||||
- [x] Upload fire-and-forget + proxy público de leitura (avatares e mídias).
|
||||
- [x] `StorageProvider.deletePrefix(prefix)` (delete por prefixo).
|
||||
- [ ] **Index de mídias** — listagem por tenant/instância/chat/período (auditoria + UI).
|
||||
- [ ] **Delete em lote** — por instância, período ou chat, coordenado **DB ↔ Wasabi**
|
||||
(sem órfãos). Endpoint admin seguro. Ver [wasabi-midias.md](wasabi-midias.md).
|
||||
- [x] **Index de mídias** — `GET /api/instances/:id/media-index` (chat/tipo/período,
|
||||
byType, paginado). Ver [wasabi-midias.md](wasabi-midias.md).
|
||||
- [x] **Delete em lote** — `POST /api/instances/:id/media-batch-delete`, coordenado
|
||||
**DB ↔ Wasabi**, dryRun por padrão + `deleteObjects` em lote.
|
||||
- [ ] Index por tamanho (bytes) — falta coluna `mediaSize`.
|
||||
- [ ] Reconciliação DB ↔ bucket (achar órfãos).
|
||||
- [ ] Política de retenção (arquivar/descartar áudio após transcrição salva).
|
||||
|
||||
## Ativação do WhatsApp (trajeto inteligente)
|
||||
|
||||
@@ -18,21 +18,35 @@ Os paths são hierárquicos por `tenant/instância/...`, o que permite delete em
|
||||
por prefixo. Confirmar o layout exato no `StorageProvider` antes de implementar
|
||||
(avatar usa `avatarWasabiPath(tenantId, instanceId, jid)`).
|
||||
|
||||
## O que falta (pendência)
|
||||
## Implementado (28/jun/2026)
|
||||
|
||||
### 1. Index de mídias
|
||||
Listagem/auditoria das mídias por **tenant / instância / chat / período**:
|
||||
- Fonte primária: tabela `messages` (path + metadados) — evita `listObjects` caro.
|
||||
- Endpoint admin: `GET /admin/media?instanceId=&chatId=&from=&to=` → paginação,
|
||||
tamanho total, contagem por tipo.
|
||||
- Reconciliação opcional DB ↔ bucket para achar **órfãos** (no bucket sem registro).
|
||||
### 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
|
||||
- Por **instância** (offboarding), por **período** (retenção), por **chat** (LGPD).
|
||||
- Coordenado **DB ↔ Wasabi**: apagar registro e objeto na mesma operação lógica;
|
||||
nunca deixar órfão nem link quebrado.
|
||||
- Implementar `deleteObjects` em lote (S3 aceita até 1000/req) além do `deletePrefix`.
|
||||
- Endpoint admin protegido + dry-run (retorna contagem antes de apagar).
|
||||
### 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**
|
||||
|
||||
Reference in New Issue
Block a user