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:
VPS 4 Builder
2026-06-28 14:53:07 +02:00
parent 9bbd170626
commit 250e47b06e
2 changed files with 33 additions and 16 deletions
+6 -3
View File
@@ -29,9 +29,12 @@
## Wasabi / mídias ## Wasabi / mídias
- [x] Upload fire-and-forget + proxy público de leitura (avatares e mídias). - [x] Upload fire-and-forget + proxy público de leitura (avatares e mídias).
- [x] `StorageProvider.deletePrefix(prefix)` (delete por prefixo). - [x] `StorageProvider.deletePrefix(prefix)` (delete por prefixo).
- [ ] **Index de mídias**listagem por tenant/instância/chat/período (auditoria + UI). - [x] **Index de mídias**`GET /api/instances/:id/media-index` (chat/tipo/período,
- [ ] **Delete em lote** — por instância, período ou chat, coordenado **DB ↔ Wasabi** byType, paginado). Ver [wasabi-midias.md](wasabi-midias.md).
(sem órfãos). Endpoint admin seguro. 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). - [ ] Política de retenção (arquivar/descartar áudio após transcrição salva).
## Ativação do WhatsApp (trajeto inteligente) ## Ativação do WhatsApp (trajeto inteligente)
+27 -13
View File
@@ -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 por prefixo. Confirmar o layout exato no `StorageProvider` antes de implementar
(avatar usa `avatarWasabiPath(tenantId, instanceId, jid)`). (avatar usa `avatarWasabiPath(tenantId, instanceId, jid)`).
## O que falta (pendência) ## Implementado (28/jun/2026)
### 1. Index de mídias ### 1. Index de mídias
Listagem/auditoria das mídias por **tenant / instância / chat / período**: `GET /api/instances/:instanceId/media-index` (auth + escopo tenant/instância).
- Fonte primária: tabela `messages` (path + metadados) — evita `listObjects` caro. - Baseado no **DB** (`messages` com `mediaPath != null`) — evita `listObjects` caro.
- Endpoint admin: `GET /admin/media?instanceId=&chatId=&from=&to=` → paginação, - Query: `chatId?`, `type?` (IMAGE|VIDEO|AUDIO|DOCUMENT|STICKER), `from?`/`to?` (ISO),
tamanho total, contagem por tipo. `page`, `pageSize` (≤200).
- Reconciliação opcional DB ↔ bucket para achar **órfãos** (no bucket sem registro). - Retorna `{ total, page, pageSize, byType, items[] }`.
### 2. Delete em lote ### 2. Delete em lote
- Por **instância** (offboarding), por **período** (retenção), por **chat** (LGPD). `POST /api/instances/:instanceId/media-batch-delete`. Body:
- Coordenado **DB ↔ Wasabi**: apagar registro e objeto na mesma operação lógica; `{ chatId?, type?, from?, to?, dryRun? }`.
nunca deixar órfão nem link quebrado. - **dryRun=true é o padrão** — só conta (`matched`, `willDeleteObjects`). Para apagar
- Implementar `deleteObjects` em lote (S3 aceita até 1000/req) além do `deletePrefix`. de fato é preciso enviar `dryRun:false`.
- Endpoint admin protegido + dry-run (retorna contagem antes de apagar). - 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 ### 3. Retenção
- Após `messages.transcription` salva, o **áudio pode ser arquivado/descartado** - Após `messages.transcription` salva, o **áudio pode ser arquivado/descartado**