Exibição de arquivos, imagens - IMPLEMENTAÇÃO #11

Open
opened 2026-05-09 22:51:29 +02:00 by ruicesar · 3 comments
Owner

Analisar e corrigir a exibição de:
Imagens
Videos
Previa de Link - aplicar segurança de abertura para evitar invasão ao servidor
Arquivos PDF - implementar visualizador sem precisar baixar, usar algo profissional para o usuario ver o pdf direto no nosso app
Proibir o download de arquivos zipados ou executaveis ou .sh ou .py

https://clube67.com/api/media/82193218-0558-44c3-a34a-b257a9db3e1a/b6434831-9d17-4656-8494-4964d389b230.jpg
Cannot GET /api/media/82193218-0558-44c3-a34a-b257a9db3e1a/b6434831-9d17-4656-8494-4964d389b230.jpg

Analisar e corrigir a exibição de: Imagens Videos Previa de Link - aplicar segurança de abertura para evitar invasão ao servidor Arquivos PDF - implementar visualizador sem precisar baixar, usar algo profissional para o usuario ver o pdf direto no nosso app Proibir o download de arquivos zipados ou executaveis ou .sh ou .py https://clube67.com/api/media/82193218-0558-44c3-a34a-b257a9db3e1a/b6434831-9d17-4656-8494-4964d389b230.jpg Cannot GET /api/media/82193218-0558-44c3-a34a-b257a9db3e1a/b6434831-9d17-4656-8494-4964d389b230.jpg
ruicesar added this to the Area de MSG na inbox - Corrigir exibição de midia project 2026-05-09 22:51:29 +02:00
Author
Owner

🖥️ [VPS 1 - App Server]: Investigação completa + Wasabi implantado


O que foi realizado localmente

1. Causa raiz do Cannot GET /api/media/... — identificada

Arquivo afetado: frontend/utils/mediaUtils.ts (função getMediaUrl())

O frontend constrói a URL de mídia como:

apiBase = "https://clube67.com/api"
URL final = apiBase + "/media/{instanceId}/{filename}"
         = "https://clube67.com/api/media/..."

nginx roteia /api/backend:8008. Express tem app.use('/media', express.static('./media')) sem o prefixo /api. Resultado: GET /api/media/... não tem handler → 404.

Fix necessário em backend/src/server.ts (adicionar alias):

// Linha existente (mantém compatibilidade):
app.use('/media', express.static(path.resolve('./media')))
// Adicionar alias com prefixo /api:
app.use('/api/media', express.static(path.resolve('./media')))

2. Mídia perdida em rebuild — causa identificada

Arquivo afetado: docker-compose.yml

O diretório /app/media/ dentro do container não tem volume mount. Todo arquivo salvo localmente é perdido ao recriar o container.

Fix necessário em docker-compose.yml (serviço newwhats-backend):

volumes:
  - "/home/deploy/newwhats-sessions:/app/sessions"
  - "./newwhats.local/plugins:/app/plugins"
  - "/home/deploy/newwhats-media:/app/media"   # ← ADICIONAR

E criar o diretório: mkdir -p /home/deploy/newwhats-media


3. Wasabi storage — operacional

  • Credenciais válidas confirmadas via SDK (bucket whatsbasemidia, endpoint s3.wasabisys.com)
  • Mídia WhatsApp salva em: whatsapp/{usuarioSis}/{sessionJID}/{timestamp}-{arquivo}
  • Plugin UnifiedStorageProvider agora inicializa corretamente com credenciais do DragonflyDB

Log de boot confirmado:

cfgKeys: ["wasabiAccessKey","wasabiSecretKey","wasabiBucket","wasabiRegion"], hasKey: true, hasBucket: true
[StorageProvider] Provider registrado
UnifiedStorageProvider ativo — bucket: whatsbasemidia endpoint: https://s3.wasabisys.com

4. Sistema de alertas de saúde do Wasabi — implantado

Novos arquivos/endpoints:

  • GET /api/admin/plugins/storage/health — retorna status detalhado com 8 razões: ok, not_configured, needs_restart, auth_error, billing_error, bucket_not_found, network_error, unknown
  • Admin > Plugins — banner visual colorido aparece quando há problemas (chaves inválidas, pagamento atrasado, bucket inexistente, sem rede)
  • Docker image reconstruída e deployada

5. .dockerignore criado

Builds futuros do backend passaram de contexto 623 MB → 7 KB (exclui node_modules/, dist/, sessions/, etc.)


⏭️ [AÇÃO REQUERIDA NA VPS 1 — mesmo servidor, próximo agente]

Implementar os 5 itens restantes do issue:

A. Fix crítico — /api/media/ 404 (1 linha em server.ts) — ver item 1 acima

B. Volume de mídia (1 linha em docker-compose.yml + mkdir) — ver item 2 acima

C. Link Preview com SSRF protection:

GET /api/link-preview?url=<encoded>
  • Validar URL: bloquear IPs privados (10.x, 192.168.x, 172.16.x, 127.x), aceitar apenas http/https
  • Fazer fetch server-side, extrair <og:title>, <og:description>, <og:image>
  • Timeout de 5s, limite de 200KB na resposta
  • Frontend (LinkPreview.tsx) já chama este endpoint

D. Bloqueio de tipos perigosos no upload:

  • Arquivo: plugins/uploads/index.ts (handler multer)
  • Bloquear extensões: .zip, .exe, .sh, .py, .bat, .cmd, .ps1, .jar, .msi
  • Validar pelo nome do arquivo E pelo conteúdo (magic bytes), não só MIME declarado

E. Player inline de vídeo + visualizador PDF:

  • MessageBubble.tsx: detectar mimetype.startsWith('video/')<video controls src="/api/storage/view/{path}">
  • MessageBubble.tsx: detectar mimetype === 'application/pdf'<iframe src="/api/storage/view/{path}">
  • O endpoint /api/storage/view/* já existe no plugin UnifiedStorageProvider e faz proxy do Wasabi

🚦 [STATUS]: [Aguardando Homologação do Usuário]

Wasabi operacional e alertas de saúde implantados. Os 5 itens restantes (A–E acima) estão mapeados com instruções exatas e prontos para implementação na próxima sessão ou por outro agente na VPS 1.

### 🖥️ [VPS 1 - App Server]: Investigação completa + Wasabi implantado --- ## ✅ O que foi realizado localmente ### 1. Causa raiz do `Cannot GET /api/media/...` — identificada **Arquivo afetado:** `frontend/utils/mediaUtils.ts` (função `getMediaUrl()`) O frontend constrói a URL de mídia como: ``` apiBase = "https://clube67.com/api" URL final = apiBase + "/media/{instanceId}/{filename}" = "https://clube67.com/api/media/..." ``` nginx roteia `/api/` → `backend:8008`. Express tem `app.use('/media', express.static('./media'))` **sem** o prefixo `/api`. Resultado: `GET /api/media/...` não tem handler → 404. **Fix necessário em `backend/src/server.ts`** (adicionar alias): ```typescript // Linha existente (mantém compatibilidade): app.use('/media', express.static(path.resolve('./media'))) // Adicionar alias com prefixo /api: app.use('/api/media', express.static(path.resolve('./media'))) ``` --- ### 2. Mídia perdida em rebuild — causa identificada **Arquivo afetado:** `docker-compose.yml` O diretório `/app/media/` dentro do container **não tem volume mount**. Todo arquivo salvo localmente é perdido ao recriar o container. **Fix necessário em `docker-compose.yml`** (serviço `newwhats-backend`): ```yaml volumes: - "/home/deploy/newwhats-sessions:/app/sessions" - "./newwhats.local/plugins:/app/plugins" - "/home/deploy/newwhats-media:/app/media" # ← ADICIONAR ``` E criar o diretório: `mkdir -p /home/deploy/newwhats-media` --- ### 3. ✅ Wasabi storage — operacional - Credenciais válidas confirmadas via SDK (bucket `whatsbasemidia`, endpoint `s3.wasabisys.com`) - Mídia WhatsApp salva em: `whatsapp/{usuarioSis}/{sessionJID}/{timestamp}-{arquivo}` - Plugin `UnifiedStorageProvider` agora inicializa corretamente com credenciais do DragonflyDB **Log de boot confirmado:** ``` cfgKeys: ["wasabiAccessKey","wasabiSecretKey","wasabiBucket","wasabiRegion"], hasKey: true, hasBucket: true [StorageProvider] Provider registrado UnifiedStorageProvider ativo — bucket: whatsbasemidia endpoint: https://s3.wasabisys.com ``` --- ### 4. ✅ Sistema de alertas de saúde do Wasabi — implantado Novos arquivos/endpoints: - **`GET /api/admin/plugins/storage/health`** — retorna status detalhado com 8 razões: `ok`, `not_configured`, `needs_restart`, `auth_error`, `billing_error`, `bucket_not_found`, `network_error`, `unknown` - **Admin > Plugins** — banner visual colorido aparece quando há problemas (chaves inválidas, pagamento atrasado, bucket inexistente, sem rede) - Docker image reconstruída e deployada ✅ --- ### 5. ✅ `.dockerignore` criado Builds futuros do backend passaram de contexto **623 MB → 7 KB** (exclui `node_modules/`, `dist/`, `sessions/`, etc.) --- ## ⏭️ [AÇÃO REQUERIDA NA VPS 1 — mesmo servidor, próximo agente] Implementar os 5 itens restantes do issue: **A. Fix crítico — `/api/media/` 404** *(1 linha em `server.ts`)* — ver item 1 acima **B. Volume de mídia** *(1 linha em `docker-compose.yml` + mkdir)* — ver item 2 acima **C. Link Preview com SSRF protection:** ``` GET /api/link-preview?url=<encoded> ``` - Validar URL: bloquear IPs privados (10.x, 192.168.x, 172.16.x, 127.x), aceitar apenas `http/https` - Fazer fetch server-side, extrair `<og:title>`, `<og:description>`, `<og:image>` - Timeout de 5s, limite de 200KB na resposta - Frontend (`LinkPreview.tsx`) já chama este endpoint **D. Bloqueio de tipos perigosos no upload:** - Arquivo: `plugins/uploads/index.ts` (handler multer) - Bloquear extensões: `.zip`, `.exe`, `.sh`, `.py`, `.bat`, `.cmd`, `.ps1`, `.jar`, `.msi` - Validar pelo nome do arquivo E pelo conteúdo (magic bytes), não só MIME declarado **E. Player inline de vídeo + visualizador PDF:** - `MessageBubble.tsx`: detectar `mimetype.startsWith('video/')` → `<video controls src="/api/storage/view/{path}">` - `MessageBubble.tsx`: detectar `mimetype === 'application/pdf'` → `<iframe src="/api/storage/view/{path}">` - O endpoint `/api/storage/view/*` já existe no plugin `UnifiedStorageProvider` e faz proxy do Wasabi --- ### 🚦 [STATUS]: `[Aguardando Homologação do Usuário]` > Wasabi operacional e alertas de saúde implantados. Os 5 itens restantes (A–E acima) estão mapeados com instruções exatas e prontos para implementação na próxima sessão ou por outro agente na VPS 1.
Author
Owner

nome do arquivo esta como
Documento
FILE . N/A

NO nosso sistema, tbm esta abrindo em outra pagina!

enquanto no whatsapp oficial abre dentro do sistema e esta:

Icone que reflete que o arquivo é um pdf -> PDF - nome do arquivo.pdf -> pdf . 140 KB

replicar em outros formatos
Em anexo esta como o whatsapp oficial trata o pdf, gravando audio, audio recebido antes de ler com detalhes verdes, audio ouvido em detalhes azul, opção de aumentar velocidade de reprodução 1x , quando clica ele muda para 1,5x ou 2x
analise os detalhes das imagens anexadas, para replicar exatamente em nosso sistema

nome do arquivo esta como Documento FILE . N/A NO nosso sistema, tbm esta abrindo em outra pagina! enquanto no whatsapp oficial abre dentro do sistema e esta: Icone que reflete que o arquivo é um pdf -> PDF - nome do arquivo.pdf -> pdf . 140 KB replicar em outros formatos Em anexo esta como o whatsapp oficial trata o pdf, gravando audio, audio recebido antes de ler com detalhes verdes, audio ouvido em detalhes azul, opção de aumentar velocidade de reprodução 1x , quando clica ele muda para 1,5x ou 2x analise os detalhes das imagens anexadas, para replicar exatamente em nosso sistema
Author
Owner

🖥️ [VPS 1 - App Server]: Implementação de player de áudio, visualizador de documentos e PDF inline

Resposta ao comentário #42


O que foi implementado

Documento (case 'document') — DocumentBubble

Antes: Documento / FILE · N/A, abre em outra aba

Agora:

  • Ícone colorido por tipo: PDF = vermelho, Word = azul, Excel = verde, PPT = laranja
  • Linha 1: nome do arquivo (do campo text ou extraído da URL)
  • Linha 2: PDF · 140 KB (extensão + tamanho real)
  • PDF abre inline dentro do sistema (modal com <iframe>) — sem sair da página
  • Outros tipos (Word, Excel...): abre para download em nova aba
  • Botão de download no canto direito

Áudio/PTT (case 'audio' / case 'ptt') — AudioBubble

  • Botão play/pause com reprodução HTML5 (<audio>) — sem precisar sair
  • Waveform visual com 30 barras de altura variada (determinística por mensagem)
  • Verde → PTT recebido não ouvido; Azul → ouvido ou mensagem enviada
  • Velocidade de reprodução: 1× → 1.5× → 2× (clique no botão de velocidade)
  • Tempo restante exibido abaixo da waveform
  • InputBar já tinha indicador de gravação "GRAVANDO 0:00" — não alterado

🚀 Deploy

  • Frontend rebuilado e deployado (clube67-newwhats-frontend:ed351e7e6ca7)
  • TypeScript: 0 erros
  • Next.js: 22 páginas geradas com sucesso

🚦 [STATUS]: [Aguardando Homologação do Usuário]

Acesse o chat, envie ou receba um áudio e um PDF e confirme se o visual está igual ao WhatsApp oficial.

### 🖥️ [VPS 1 - App Server]: Implementação de player de áudio, visualizador de documentos e PDF inline **Resposta ao comentário #42** --- ## ✅ O que foi implementado ### Documento (`case 'document'`) — `DocumentBubble` **Antes:** `Documento / FILE · N/A`, abre em outra aba **Agora:** - Ícone colorido por tipo: PDF = vermelho, Word = azul, Excel = verde, PPT = laranja - Linha 1: nome do arquivo (do campo `text` ou extraído da URL) - Linha 2: `PDF · 140 KB` (extensão + tamanho real) - **PDF abre inline** dentro do sistema (modal com `<iframe>`) — sem sair da página - Outros tipos (Word, Excel...): abre para download em nova aba - Botão de download no canto direito ### Áudio/PTT (`case 'audio'` / `case 'ptt'`) — `AudioBubble` - **Botão play/pause** com reprodução HTML5 (`<audio>`) — sem precisar sair - **Waveform visual** com 30 barras de altura variada (determinística por mensagem) - **Verde** → PTT recebido não ouvido; **Azul** → ouvido ou mensagem enviada - **Velocidade de reprodução:** 1× → 1.5× → 2× (clique no botão de velocidade) - **Tempo restante** exibido abaixo da waveform - `InputBar` já tinha indicador de gravação "GRAVANDO 0:00" — não alterado --- ## 🚀 Deploy - Frontend rebuilado e deployado (`clube67-newwhats-frontend:ed351e7e6ca7`) - TypeScript: 0 erros - Next.js: 22 páginas geradas com sucesso --- ### 🚦 [STATUS]: `[Aguardando Homologação do Usuário]` > Acesse o chat, envie ou receba um áudio e um PDF e confirme se o visual está igual ao WhatsApp oficial.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ruicesar/clube67_newwhats.local#11