- novo: wasabi-migracao-e-limpeza.md (fluxo executado 04/jul, v1.0.70) - wasabi-midias.md: ver-também cruzado Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.6 KiB
newwhats.clube67.com — Migração de mídia local → Wasabi & limpeza de disco
Runbook executado no DEV em 04/jul/2026 (release v1.0.70) para tirar TODA a mídia do disco do servidor e deixar só no Wasabi. Repetível em produção com os alvos de PROD. Complementa
wasabi-midias.md(design de index + delete em lote); aqui é o fluxo de migração one-shot + limpeza, a feature de thumbnails e o guard de recuperabilidade.
Contexto: onde a mídia vive
Campo mediaUrl na tabela messages (Postgres newwhats_dev / prod equivalente; colunas
camelCase, model Prisma @@map("messages")) tem 3 estados:
mediaUrl |
Significado | Arquivo em disco? |
|---|---|---|
whatsapp/<tenant>/<instance>/<ts>-<nome> |
já no Wasabi (servido remoto) | não |
/media/<instanceId>/<file> |
LOCAL — servido do disco | sim (/app/media/...) |
NULL |
lazy — re-baixável do WhatsApp via mediaPayload |
não |
Mídia local mora em newwhats.dev/data/media (montado como /app/media no container
newwhats-backend-dev). newwhats.dev é a cópia em execução (git-ignored); newwhats.local
é a fonte versionada. O container monta newwhats.dev/plugins.
Config do Wasabi (importante: NÃO está em env)
Vem do Dragonfly, chave plugin:config:UnifiedStorageProvider (DB /1 em
redis://…@10.99.0.3:6379/1; a senha está na DRAGONFLY_URL do container). Ler:
redis-cli -h 10.99.0.3 -p 6379 -a <senha> -n 1 GET plugin:config:UnifiedStorageProvider
Valores DEV: bucket whatsbasemidia, endpoint https://s3.wasabisys.com, region us-east-1.
Cliente S3 = forcePathStyle:true; upload com ACL:'public-read'.
Convenção de key (replicar EXATAMENTE)
Plugin ativo newwhats.dev/plugins/UnifiedStorageProvider/index.js (~L330):
whatsapp/<tenantId>/<instanceId>/<Date.now()>-<nomeSanitizado>
As mídias já no Wasabi usam isso — se a key divergir, o serving quebra.
Passo a passo da migração
Rodar DENTRO do container newwhats-backend-dev (tem @aws-sdk/client-s3, pg, os
arquivos em /app/media e DATABASE_URL): export NODE_PATH=/app/node_modules; cd /app.
Para cada linha WHERE "mediaUrl" LIKE '/media/%':
- ler o arquivo local (
/app/media/+mediaUrlsem o prefixo/media/); PutObjectno Wasabi com a key acima,ContentTypedo mime,ACL:'public-read';HeadObjectconferindoContentLength == tamanho local(rede de segurança);- só então
UPDATE messages SET "mediaUrl" = <key>; unlinkSyncdo arquivo local.
Idempotente e resumível (ao migrar, o mediaUrl deixa de casar /media/%). Rodar
DRY_RUN=1 primeiro (verifica existência/tamanho, sem enviar/apagar). Script de
referência: migrate_media.js.
Refs mortas
/media/… sem arquivo E sem mediaPayload = referência quebrada (no DEV eram 257).
UPDATE messages SET "mediaUrl" = NULL → aí o guard media_recoverable=false exibe
"indisponível" de forma limpa (em vez de tentar carregar path quebrado → 404).
Órfãos e esvaziamento final
Após migrar tudo, nenhum mediaUrl referencia arquivo local → qualquer arquivo que
sobrar em data/media é órfão. Confirmar erros=0 no resumo e então esvaziar
(find data/media -mindepth 1 -delete). No DEV liberou ~2,1 GB.
Feature acoplada (mesma release)
- Thumbnails: no re-download, além de subir a mídia, gera
sharp → 400px → webp q60e grava<mediaUrl>.thumb.webpno Wasabi. OMessageBubblepede a thumb na prévia e cai pro full viaonError. (commit2c8eb41/ submódulo09edeb9) mediaRecoverable:GET /inbox/:chatId/messagesdevolveBoolean(mediaUrl || mediaPath || mediaPayload); o frontend mapeia p/media_recoverablee o guard evita o400de/media/:id/downloadem mídia legada.
Resultado no DEV (04/jul/2026)
- 659 mídias migradas (~2,0 GB), 0 erros; 257 refs mortas → NULL; disco: 2,1 GB → 0.
- Banco final: 2.231 no Wasabi, 26.793 lazy (NULL+payload), 0 refs
/media/.
Cuidados
- Sempre
HeadObjectantes de apagar o local; nunca apagar sem upload confirmado. - O "janitor" do plugin (
setInterval120s) só varrelocalFallbackDir/uploadse não atualiza o banco — não serve para migrar a mídia do WhatsApp. - Limpar temporários com credenciais depois (
/tmp/wasabi.env, creds do Dragonfly). - Testar sempre no
dev.newwhatsantes; pedir aprovação antes de rodar em produção.
Para PRODUÇÃO
O newwhats de produção não roda na VPS4. Aplicar o mesmo fluxo contra o diretório de
mídia, o banco e a config Dragonfly de produção. Ver ambiente-dev.md e
infra/maquinas-e-rede.md.