Três correções independentes para mensagens enviadas/recebidas via celular:
1. WhatsAppConnectionManager: o listener CB:message agora captura também
recipient_pn (InfiniteAPI v7+) além de peer_recipient_pn (Baileys oficial),
garantindo que o PN correto do destinatário seja gravado no peerRecipientHints
antes do messages.upsert ser emitido.
2. MessageHandler.processMessage: adicionado fast path fromMe=false via
remoteJidAlt para contatos @lid (InfiniteAPI já resolve o PN do remetente).
Para fromMe=true onde InfiniteAPI resolveu @lid→nosso próprio JID incorretamente,
recupera o JID correto via peerRecipientHints antes de descartar como self-chat.
3. MessageHandler.processHistoryBatch: corrigido guard senderPn que não testava
!msg.key.fromMe; para mensagens enviadas, senderPn é o nosso próprio JID,
não o destinatário — o que criava self-chats corrompidos no DB.
4. whatsapp.send.routes: adicionado .catch P2002 no upsert da rota POST /send;
race condition com eco Baileys causava unhandled rejection → exit(1) no Node 20.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Solução 3 — Wasabi:
- Após download do CDN do WhatsApp, faz upload fire-and-forget para
Wasabi com path determinístico (avatars/{tenant}/{instance}/{jid}.jpg)
- Armazena o path relativo em contact.avatarUrl no banco
- Na próxima requisição, serve bytes direto do Wasabi sem tocar no CDN do WA
- Adiciona customPath em StorageUploadPayload e getBuffer() na interface
StorageProviderInterface/StorageProviderRegistry e plugin
Solução 4 — IndexedDB:
- Backend expõe avatarVersion (MD5 curto do avatarUrl) no response /api/chats
- Campo propagado por chatStore → chatToLegacy → NewWhatsChat
- Hook useAvatarCache (IndexedDB nativo, sem deps) armazena dataUrl do avatar
com chave jid:version — invalida automaticamente quando o avatar muda no banco
- Avatar.tsx serve do IndexedDB antes de disparar qualquer request HTTP;
converte via canvas e persiste no IndexedDB na primeira carga bem-sucedida
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>