chore(ops): restore missing root files in newwhats.clube67.com
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)

This commit is contained in:
VPS 4 Deploy Agent
2026-05-18 03:27:25 +02:00
parent 5ec6bd6354
commit 0dc5eefa06
406 changed files with 237013 additions and 0 deletions
@@ -0,0 +1,5 @@
# Ignorar arquivos de configuração sensíveis locais se copiados acidentalmente
gcal_key.json
ssh_alerts_config.json
*.log
.DS_Store
@@ -0,0 +1,319 @@
# Baileys Engine Swap — Plano de Implementação
**Data:** 2026-04-29
**Contexto:** Mensagens ricas (botões, lista, carrossel) não renderizam no WhatsApp Web com `@whiskeysockets/baileys` padrão. A solução é usar o fork InfiniteAPI que implementa esses tipos nativamente dentro do `sendMessage()`.
---
## 1. Problema Raiz
O `@whiskeysockets/baileys` v6.7.18 (oficial) **não suporta** os campos:
- `nativeButtons`
- `nativeList`
- `nativeCarousel`
O WhatsApp Web exige nós binários adicionais (`<biz><interactive>`, `<bot biz_bot="1"/>`) injetados no stanza via `relayMessage(additionalNodes)`. O `rich-message.ts` atual tenta replicar isso manualmente, mas **ainda falha no WhatsApp Web**.
**Erro observado:** "Não foi possível carregar a mensagem. Use seu celular para acessá-la."
### Por que o InfiniteAPI funciona
O fork `github:rsalcara/InfiniteAPI` (v7.0.0-rc.9, package name: `baileys`) implementa toda a lógica de `additionalNodes` internamente no `generateWAMessageContent()` / `relayMessage()`. O dev só chama `sock.sendMessage(jid, { nativeButtons: [...] })` e o fork faz o resto.
### Por que whatsmeow (Go) NÃO resolve
Pesquisa confirmada (abril 2026):
- `InteractiveMessage` + `NativeFlowMessage` → struct existe, botões **não aparecem** (issue #711, sem solução)
- `ListMessage`**não exibe** na conversa do destinatário (issue #669)
- `CarouselMessage`**não existe** (exclusivo da Cloud API)
- Migração seria ~23 meses de reescrita para o mesmo resultado quebrado
---
## 2. Solução Escolhida: Engine Swap com Fallback
Instalar **os dois** packages (nomes diferentes, convivem no mesmo `node_modules`) e criar uma camada de abstração `engine/` que permite trocar via variável de ambiente + restart automático.
**Downtime por troca:** ~510 segundos (restart + reconexão automática, sem novo QR — auth state no disco é compatível entre os dois, InfiniteAPI é fork direto).
---
## 3. Estrutura de Arquivos
```
backend/
src/
modules/whatsapp/
engine/
official.ts ← re-exporta @whiskeysockets/baileys
infinite.ts ← re-exporta baileys (InfiniteAPI)
index.ts ← exporta conforme process.env.BAILEYS_ENGINE
connection/
WhatsAppConnectionManager.ts ← muda import para '../engine'
handlers/
MessageHandler.ts ← muda import para '../../engine' (ou relativo)
ContactHandler.ts ← idem
rich-message.ts ← simplificado + fallback comentado
modules/chatbot/
chatbot.service.ts ← muda import
shared/utils/
whatsapp.ts ← muda import
```
---
## 4. Implementação Passo a Passo
### 4.1 Instalar os dois packages
```bash
cd /home/deploy/projetos/newwhats.local/backend
# Manter o oficial
# @whiskeysockets/baileys já está em package.json
# Adicionar InfiniteAPI com alias para não conflitar
npm install baileys@github:rsalcara/InfiniteAPI
```
Ambos convivem no `node_modules` porque têm nomes diferentes:
- `node_modules/@whiskeysockets/baileys/`
- `node_modules/baileys/`
### 4.2 Criar `engine/official.ts`
```typescript
// Re-exporta tudo do Baileys oficial
export * from '@whiskeysockets/baileys'
export { proto } from '@whiskeysockets/baileys'
```
### 4.3 Criar `engine/infinite.ts`
```typescript
// Re-exporta tudo do InfiniteAPI
export * from 'baileys'
export { proto } from 'baileys'
```
### 4.4 Criar `engine/index.ts`
```typescript
// Seleciona engine conforme BAILEYS_ENGINE (padrão: infinite)
// IMPORTANTE: process.env lido em tempo de inicialização do módulo.
// Para trocar a engine: alterar BAILEYS_ENGINE + reiniciar o processo.
const engine = process.env.BAILEYS_ENGINE ?? 'infinite'
if (engine === 'infinite') {
module.exports = require('./infinite')
} else {
module.exports = require('./official')
}
```
> **Nota TypeScript:** usar `require()` condicional ou um `export *` com re-export dinâmico.
> Alternativa mais simples: dois arquivos de entrada separados e o `tsconfig` aponta para um deles via path alias.
### 4.5 Trocar imports nos 6 arquivos
| Arquivo | Import atual | Novo import |
|---|---|---|
| `WhatsAppConnectionManager.ts` | `@whiskeysockets/baileys` | `../engine` |
| `handlers/MessageHandler.ts` | `@whiskeysockets/baileys` | `../../engine` |
| `handlers/ContactHandler.ts` | `@whiskeysockets/baileys` | `../../engine` |
| `rich-message.ts` | `@whiskeysockets/baileys` (3 imports) | `./engine` |
| `modules/chatbot/chatbot.service.ts` | `@whiskeysockets/baileys` | `../whatsapp/engine` |
| `shared/utils/whatsapp.ts` | (verificar) | path relativo para engine |
### 4.6 Simplificar `rich-message.ts` para InfiniteAPI
Com InfiniteAPI, o `sendRichMessage()` fica:
```typescript
export async function sendRichMessage(
sock: WASocket,
jid: string,
payload: RichPayload,
quoted?: { key: proto.IMessageKey; message: proto.IMessage },
): Promise<string> {
const { text, footer, nativeButtons, nativeList, nativeCarousel, poll } = payload
if (poll) {
const sent = await sock.sendMessage(jid, {
poll: { name: poll.name, values: poll.values, selectableCount: poll.selectableCount ?? 1 },
}, quoted ? { quoted } : undefined)
return sent?.key?.id ?? `poll-${Date.now()}`
}
if (nativeButtons) {
const sent = await sock.sendMessage(jid, {
text: text ?? '',
footer,
nativeButtons, // InfiniteAPI trata tudo internamente
} as any, quoted ? { quoted } : undefined)
return sent?.key?.id ?? `btn-${Date.now()}`
}
if (nativeList) {
const sent = await sock.sendMessage(jid, {
text: text ?? '',
footer,
nativeList,
} as any, quoted ? { quoted } : undefined)
return sent?.key?.id ?? `list-${Date.now()}`
}
if (nativeCarousel) {
const sent = await sock.sendMessage(jid, {
text: text ?? '',
footer,
nativeCarousel,
} as any, quoted ? { quoted } : undefined)
return sent?.key?.id ?? `carousel-${Date.now()}`
}
// Texto simples
const sent = await sock.sendMessage(jid, { text: text ?? '' }, quoted ? { quoted } : undefined)
return sent?.key?.id ?? `text-${Date.now()}`
}
```
> **Fallback:** Manter o código atual do proto manual em `rich-message.official.ts` caso precise debugar com a engine oficial.
### 4.7 Adicionar BAILEYS_ENGINE ao .env
```bash
# /home/deploy/projetos/newwhats.local/backend/.env
BAILEYS_ENGINE=infinite # ou: official
```
### 4.8 Endpoint de toggle no painel admin
**Backend** — novo endpoint em `whatsapp.routes.ts` ou `admin.routes.ts`:
```typescript
// POST /api/admin/engine
// Body: { engine: 'infinite' | 'official' }
router.post('/engine', adminOnly, async (req, res) => {
const { engine } = req.body
if (!['infinite', 'official'].includes(engine)) return res.status(400).json({ error: 'invalid engine' })
// 1. Persiste no banco (tabela config ou settings)
await prisma.config.upsert({
where: { key: 'BAILEYS_ENGINE' },
update: { value: engine },
create: { key: 'BAILEYS_ENGINE', value: engine },
})
// 2. Reinicia o processo (pm2 ou systemctl)
// Usar exec() com o comando correto para o ambiente
exec('pm2 restart newwhats-backend', (err) => {
if (err) return res.status(500).json({ error: 'restart failed' })
res.json({ ok: true, engine })
})
})
// GET /api/admin/engine — retorna engine ativa
router.get('/engine', adminOnly, async (req, res) => {
const config = await prisma.config.findUnique({ where: { key: 'BAILEYS_ENGINE' } })
res.json({ engine: config?.value ?? process.env.BAILEYS_ENGINE ?? 'infinite' })
})
```
**Frontend** — componente simples na página de administração:
```tsx
// Toggle na página /admin/settings ou /admin/system
<div>
<span>Engine WhatsApp</span>
<select value={engine} onChange={handleSwitch}>
<option value="infinite">InfiniteAPI (botões/lista/carrossel)</option>
<option value="official">Baileys Oficial (estável, sem botões)</option>
</select>
<span className="text-yellow-500"> Troca reinicia o servidor (~10s)</span>
</div>
```
---
## 5. Tipos Suportados por Engine
| Tipo de Mensagem | Baileys Oficial | InfiniteAPI |
|---|---|---|
| Texto simples | ✅ | ✅ |
| Reply / Quote | ✅ | ✅ |
| Menções (@) | ✅ | ✅ |
| Mídia (foto/vídeo/doc/áudio) | ✅ | ✅ |
| Enquete (poll) | ✅ | ✅ |
| Botões (`nativeButtons`) — reply, url, copy, call | ❌ (quebrado no Web) | ✅ |
| Lista (`nativeList`) | ❌ (quebrado no Web) | ✅ |
| Carrossel (`nativeCarousel`) | ❌ (quebrado no Web) | ✅ |
| Botão com header imagem/vídeo | ❌ | ✅ (`headerImage`, `headerVideo`) |
### Tipos de botão suportados pelo InfiniteAPI
```typescript
type NativeButton =
| { type: 'reply'; id: string; text: string } // Quick reply
| { type: 'url'; text: string; url: string } // Abrir URL
| { type: 'copy'; text: string; copyText: string } // Copiar texto
| { type: 'call'; text: string; phoneNumber: string } // Ligar
```
---
## 6. Riscos e Mitigações
| Risco | Mitigação |
|---|---|
| InfiniteAPI é RC (não estável) | Engine swap — volta pro oficial em 10s |
| Repo `github:rsalcara/InfiniteAPI` pode sair do ar | Fazer fork próprio no GitHub interno |
| Breaking changes na API do fork | Wrapper `engine/` isola o impacto |
| Auth state incompatível após troca | Não é problema — InfiniteAPI usa o mesmo formato `useMultiFileAuthState` |
| TypeScript: tipos diferentes entre engines | Usar `as any` nos campos extras do InfiniteAPI |
---
## 7. Estimativa de Esforço
| Tarefa | Tempo |
|---|---|
| Instalar packages + criar `engine/` | 30 min |
| Trocar imports nos 6 arquivos | 30 min |
| Simplificar `rich-message.ts` | 30 min |
| Endpoint admin + frontend toggle | 2 horas |
| Teste real (celular + Web) | 1 hora |
| **Total** | **~45 horas** |
---
## 8. Arquivos-Chave para Referência
| Arquivo | Descrição |
|---|---|
| `backend/src/modules/whatsapp/rich-message.ts` | Lógica atual de envio rico (proto manual) |
| `backend/src/modules/whatsapp/connection/WhatsAppConnectionManager.ts` | Core da conexão Baileys (1265 linhas) |
| `backend/src/modules/whatsapp/handlers/MessageHandler.ts` | Processa mensagens recebidas (865 linhas) |
| `backend/src/modules/whatsapp/whatsapp.send.routes.ts` | Rotas de envio (757 linhas) |
| `/home/deploy/projetos/whatsbotoes/baileys_interactive/` | Projeto de referência usando InfiniteAPI |
| `/home/deploy/projetos/whatsbotoes/tipos_de_disparo.md` | Documentação dos tipos de disparo |
| `node_modules/baileys/lib/Types/Message.d.ts` | Tipos do InfiniteAPI (após instalar) |
---
## 9. Comandos Úteis
```bash
# Compilar backend
cd /home/deploy/projetos/newwhats.local/backend
npx tsc --skipLibCheck
# Compilar só rich-message
npx tsc src/modules/whatsapp/rich-message.ts --outDir dist/modules/whatsapp --skipLibCheck --esModuleInterop --module commonjs --moduleResolution node --target es2020
# Reiniciar backend
pm2 restart newwhats-backend # ou o nome do processo
# Ver engine ativa
grep BAILEYS_ENGINE .env
```
@@ -0,0 +1,211 @@
# 🚀 Guia de Desenvolvimento - NewWhats
Este documento descreve como configurar e rodar o ambiente de desenvolvimento com **hot-reload** para desenvolvimento rápido.
---
## 📋 Portas
| Serviço | Produção | Desenvolvimento |
|---------|----------|-----------------|
| Frontend (Next.js) | 3003 | 4004 |
| Backend (Express) | 8008 | 8009 |
---
## 🛠️ Setup Inicial
### 1. Requisitos
- Node.js (verifique com `node -v`)
- npm (verifique com `npm -v`)
- PostgreSQL rodando
- DragonflyDB (Redis-compatible) rodando
- NATS JetStream rodando
- Temporal.io rodando
### 2. Instalar Dependências
```bash
# Frontend
cd frontend
npm install
# Backend
cd ../backend
npm install
```
---
## 🏃 Executar em Desenvolvimento (Hot-Reload)
### Opção 1: Usar Serviços Systemd (Recomendado)
#### Iniciar Ambos os Serviços
```bash
# Iniciar dev services
sudo systemctl start newwhats-frontend-dev newwhats-backend-dev
# Verificar status
sudo systemctl status newwhats-frontend-dev newwhats-backend-dev
# Ver logs em tempo real
sudo journalctl -u newwhats-frontend-dev -f
sudo journalctl -u newwhats-backend-dev -f
```
#### Parar Serviços Dev
```bash
sudo systemctl stop newwhats-frontend-dev newwhats-backend-dev
```
#### Ativar Auto-start na Boot
```bash
sudo systemctl enable newwhats-frontend-dev newwhats-backend-dev
```
---
### Opção 2: Rodar Manualmente (Terminal)
#### Terminal 1 - Frontend (Hot-Reload)
```bash
cd /home/deploy/projetos/newwhats.local/frontend
npm run dev
# Output esperado:
# ▲ Next.js 14.2.35
# - Local: http://localhost:4004
# ✓ Ready in X.Xs
```
#### Terminal 2 - Backend (Hot-Reload)
```bash
cd /home/deploy/projetos/newwhats.local/backend
npm run dev
# Output esperado:
# Server running on port 8009
# Watching for file changes...
```
---
## 📝 Variáveis de Ambiente
### Frontend
- `NODE_ENV=development` → Ativa React DevTools e hot-reload
- Porta: `4004` (configurado em `package.json`)
### Backend
- `NODE_ENV=development` → Logs detalhados, sem compressão
- `PORT=8009` → Porta dev (não conflita com produção 8008)
- `.env` já está configurado com valores corretos
---
## 🔄 Hot-Reload em Ação
### Frontend
- Edite arquivos em `frontend/pages/`, `frontend/components/`, etc.
- Next.js recompila automaticamente e atualiza o navegador
- Para mudanças em `next.config.mjs`, reinicie o serviço
### Backend
- Edite arquivos em `backend/src/`
- `tsx watch` reinicia o servidor automaticamente
- Mudanças em `.env` requerem restart manual
---
## 🚨 Troubleshooting
### Porta já em uso
```bash
# Verificar qual processo usa a porta
lsof -i :4004 # Frontend
lsof -i :8009 # Backend
# Matar processo (último recurso)
sudo kill -9 <PID>
```
### Serviço não inicia
```bash
# Verificar logs detalhados
sudo systemctl status newwhats-frontend-dev
sudo journalctl -u newwhats-frontend-dev -n 50
# Recarregar systemd após editar .service
sudo systemctl daemon-reload
```
### Mudanças não aparecem
- **Frontend**: Limpar cache do navegador (Ctrl+Shift+Del)
- **Backend**: Aguarde a mensagem de restart no terminal
---
## ⚙️ Scripts Disponíveis
### Frontend
```bash
npm run dev # Desenvolvimento com hot-reload (porta 4004)
npm run build # Build para produção
npm run start # Rodar build (produção, porta 3003)
npm run lint # Linting
```
### Backend
```bash
npm run dev # Desenvolvimento com tsx watch (porta 8009)
npm run build # Compilar TypeScript
npm run start # Rodar compilado (produção, porta 8008)
npm run db:migrate # Migrar database
npm run db:push # Push schema para database
npm run db:studio # Prisma Studio
```
---
## 🔗 URLs de Desenvolvimento
- **Frontend**: http://localhost:4004
- **Backend API**: http://localhost:8009
- **Prisma Studio**: `npm run db:studio` (backend)
---
## 📌 Produção vs Desenvolvimento
| Aspecto | Produção | Desenvolvimento |
|---------|----------|-----------------|
| Hot-reload | ❌ | ✅ |
| Compilação | ✅ Build prévio | ❌ On-the-fly |
| Logs | Estruturados (JSON) | Pretty-printed |
| Otimização | ✅ | ❌ |
| Velocidade Dev | Lenta | ⚡ Rápida |
---
## 🤝 Boas Práticas
1. **Sempre use dev em desenvolvimento** — hot-reload economiza tempo
2. **Teste em produção antes de deploy** — verifique otimizações
3. **Mantenha `.env` sincronizado** — mudanças críticas requerem restart
4. **Limpe logs regularmente**`journalctl --vacuum=time=7d`
5. **Verifique portas antes de mudar** — evite conflitos
---
## 📞 Suporte
Para problemas ou dúvidas, verifique:
- Logs dos serviços: `journalctl -u newwhats-*-dev -f`
- Status das dependências: `systemctl status postgresql dragonfly nats`
- Conectividade: `curl http://localhost:8009/health`
---
**Última atualização**: 2026-04-10
**Ambiente**: Linux (Debian/Ubuntu)
@@ -0,0 +1,95 @@
# 📝 Relatório Técnico de Implementação — NewWhats SaaS
Este relatório descreve as ações executadas, decisões arquiteturais consolidadas, riscos mapeados e próximos passos recomendados para o ecossistema NewWhats.
---
## 🏗️ Decisões Arquiteturais e Infraestrutura
Para atender à premissa de manter o host limpo (sem dependências locais de Node/npm/PM2) e em conformidade com o design "V8 Bunker", a aplicação foi **100% containerizada**:
1. **Estrutura de Containerização (Multi-Stage) e Resolução do Erro do Prisma**:
* **newwhats-backend**: Inicialmente construído com `node:20-alpine`. Identificamos que a engine binária do Prisma requer `libssl.so.1.1` (Glibc/OpenSSL 1.1) que foi removido no Alpine moderno.
* **Solução Proativa**: Migramos a base do backend para `node:20-slim` (Debian-based) e instalamos nativamente as dependências `openssl` e `ca-certificates`.
* Adicionamos `"debian-openssl-3.0.x"` no bloco `binaryTargets` do `schema.prisma` para compilar o driver otimizado. O backend agora compila e inicializa instantaneamente em conformidade de segurança máxima.
2. **Reverse Proxy Unificado e Resiliente (Nginx + Traefik)**:
* Mantivemos o Traefik gerenciando os certificados SSL (`HTTPS`) de forma nativa e automática na borda (Portas `80/443`).
* **Correção no Nginx (`default.conf`)**: Corrigimos o typo da variável de cabeçalho (`$proxy_add_x_forwarded-for` para `$proxy_add_x_forwarded_for` com sublinha).
* **Configuração de Alta Resiliência**: Para evitar que o Nginx falhasse caso o backend estivesse em inicialização, adicionamos a dependência `depends_on` no Compose e configuramos o resolver DNS interno do Docker (`resolver 127.0.0.11 valid=30s`) com variáveis dinâmicas de proxy. O Nginx agora inicia de forma instantânea e tolerante a falhas.
3. **Persistência de Sessões**:
* O Baileys armazena os arquivos físicos de conexões das contas do WhatsApp. Criamos um volume compartilhado que mapeia o diretório interno do container para a pasta física do host `/home/deploy/newwhats-sessions`, garantindo que os usuários não precisem re-escanear o QR code quando os containers forem atualizados ou recriados.
---
## 📂 Arquivos Modificados e Criados
* 📝 [docker-compose.yml](file:///home/deploy/stack/clube67/docker-compose.yml): Registra os novos serviços, regras de dependências e volumes compartilhados.
* 📝 [default.conf](file:///home/deploy/stack/clube67/nginx/default.conf): Mapeamento de proxies Nginx resilientes e regras de conexões WebSockets.
* 📝 [Dockerfile (Backend)](file:///home/deploy/stack/clube67/newwhats.local/backend/Dockerfile): Definição de build modular baseada em Debian Slim com suporte total ao Prisma Engine.
* 📝 [schema.prisma](file:///home/deploy/stack/clube67/newwhats.local/backend/prisma/schema.prisma): Configuração de compilação multi-target para garantir compatibilidade OpenSSL 3.0.
* 📝 [TAREFAS.md](file:///home/deploy/stack/clube67/newwhats.local/TAREFAS.md): Planejamento detalhado por entregáveis.
---
## 🚨 Pontos de Atenção e Homologação da VPS 3
> [!WARNING]
> **Status das Dependências de Rede na VPS 3 (`10.99.0.3`)**:
> Ao inicializar o backend, verificamos nos logs do container que a conexão com o DragonflyDB (Redis local no compose) conectou perfeitamente (`{"msg":"DragonflyDB conectado"}`), mas o Prisma parou na barreira de inicialização:
>
> `Can't reach database server at 10.99.0.3:5432`
>
> * **Diagnóstico**: O IP da VPS 3 responde aos pings da VPN instantaneamente, mas a porta `5432` retorna `Connection Refused` do host de destino.
---
## 🛡️ Guia de Ativação do PostgreSQL na VPS 3
Para liberar o acesso do backend (VPS 1) ao PostgreSQL na VPS 3 de forma totalmente segura, siga os passos abaixo diretamente na VPS 3 (como usuário `deploy`):
### 1. Configurar escuta do PostgreSQL na interface VPN
Abra o arquivo de configuração principal (o caminho exato depende da versão instalada, ex: `14` ou `15`):
```bash
sudo nano /etc/postgresql/15/main/postgresql.conf
```
Procure pela linha `listen_addresses` e altere para:
```ini
listen_addresses = 'localhost,10.99.0.3'
```
*(Isso instrui o PostgreSQL a aceitar conexões locais e conexões vindas apenas da interface segura do WireGuard, mantendo a porta fechada para a internet pública).*
### 2. Autorizar o IP da VPS 1 no controle de acesso do PostgreSQL
Abra o arquivo de controle de conexões do cliente:
```bash
sudo nano /etc/postgresql/15/main/pg_hba.conf
```
Adicione a seguinte linha de liberação ao final do arquivo:
```text
# Liberar conexões seguras da VPS 1 (Produção) para o NewWhats
host newwhats newwhats 10.99.0.1/32 scram-sha-256
```
*(Substitua `scram-sha-256` por `md5` se o seu banco estiver configurado com criptografia legado).*
### 3. Reiniciar o serviço do PostgreSQL
Aplique as novas configurações recarregando o serviço:
```bash
sudo systemctl restart postgresql
```
### 4. Validar as regras de Firewall (nftables) na VPS 3
Se a VPS 3 usar `nftables` (como indicado no layout "V8 Bunker"), verifique se as regras atuais permitem o tráfego de entrada na porta `5432` vindo de `10.99.0.1`.
Para listar e validar as regras atuais na VPS 3:
```bash
sudo nft list ruleset
```
Certifique-se de que há uma regra de aceitação similar a esta na tabela de entrada da VPN:
```text
ip saddr 10.99.0.1 tcp dport 5432 accept
```
---
## ⏭️ Próximos Passos
1. **Liberar PostgreSQL na VPS 3**: Execute os passos acima na VPS 3 para abrir o canal seguro de conexão.
2. **Atualização Automática**: Assim que a porta `5432` estiver liberada, o container `clube67-newwhats-backend-1` (que está configurado para tentar se reconectar automaticamente) se conectará instantaneamente, executará as rotinas de inicialização e o painel em `clube67.com` estará 100% no ar!
@@ -0,0 +1,51 @@
# 📋 LISTA DE TAREFAS — DEPLOY NEWWHATS (FRENTE A: WASABI STORAGE)
Este documento registra o escopo de implementação física, build, deploy e validação das correções da **Frente A (Wasabi Storage)** realizadas no ambiente de produção da VPS 1.
---
## 🚀 FRENTE A: Correções Wasabi Storage
| ID | Tarefa | Arquivo(s) Afetado(s) | Status | Detalhes |
|---|---|---|---|---|
| **T1** | Evitar vazamento de mídia local | `MessageHandler.ts` | **CONCLUÍDO** | Adicionado `fs.unlink()` assíncrono para limpar arquivos locais após upload para Wasabi. |
| **T2** | Correção de `mediaPath` no DB | `MessageHandler.ts` | **CONCLUÍDO** | Salvamento de `mediaPath = null` para mídias salvas com sucesso em storage remoto, forçando o carregamento direto do remote link. |
| **T3** | Evitar duplicação de handlers Express | `UnifiedStorageProvider/index.ts` | **CONCLUÍDO** | Adicionada flag privada `routesRegistered` para evitar múltiplos registros de rotas HTTP no Express em reativações. |
| **T4** | Interface para deleção recursiva | `types.ts`, `StorageProvider.ts` | **CONCLUÍDO** | Adicionado o método `deletePrefix` no contrato `StorageProviderInterface` e wrapper no `StorageProviderRegistry`. |
| **T5** | Implementação de limpeza Wasabi + Fallback | `UnifiedStorageProvider/index.ts` | **CONCLUÍDO** | Implementado `deletePrefix` usando `ListObjectsV2Command` + `DeleteObjectsCommand` paginados e remoção recursiva local. |
| **T6** | Limpeza de mídias ao deletar instância | `whatsapp.routes.ts` | **CONCLUÍDO** | Integrada chamada assíncrona ao `storageProvider.deletePrefix` na rota `DELETE /:id` usando prefixo correto. |
| **T7** | Correção de carregamento de plugins (Container) | `Dockerfile`, `docker-compose.yml` | **CONCLUÍDO** | Corrigida a ausência da pasta de plugins no container montando-a como volume e criando symlinks para resolução de imports. |
---
## 📈 Resumo do Status
- **Fase de Planejamento:** 100% Concluído
- **Implementação do Código:** 100% Concluído
- **Compilação e Transpilação (Plugins):** 100% Concluído
- **Build da Imagem e Re-deploy (Docker):** 100% Concluído
- **Validação de Logs e Ativação de Plugins:** 100% Concluído (Todos os plugins ativos sem erros!)
---
## 🔒 Próximos Passos recomendados ao Usuário
1. **Reconexão de Instâncias:** Reconectar as instâncias no painel administrativo para reativar as conexões de mensagens e testar o envio de mídias direto ao Wasabi.
2. **Validação do Bucket:** Confirmar que os novos arquivos estão sendo gerados sob o prefixo correto `whatsapp/{tenantId}/{instanceId}/` dentro do bucket `whatsbasemidia`.
3. **Validação da Exclusão:** Excluir uma instância de teste e verificar a remoção automática das mídias correspondentes tanto localmente quanto no bucket Wasabi.
---
*Última atualização: 08 de Maio de 2026 às 02:00 (UTC).*
---
## 🚀 FRENTE B: Baileys Engine Swap (Mensagens Ricas no WA Web)
| ID | Tarefa | Arquivo(s) Afetado(s) | Status | Detalhes / Critérios de Aceite |
|---|---|---|---|---|
| **B1** | Instalar fork InfiniteAPI | `package.json` | **CONCLUÍDO** | Instalar `baileys@github:rsalcara/InfiniteAPI` sob alias/coexistência no backend. |
| **B2** | Camada de abstração `engine` | `engine/official.ts`, `engine/infinite.ts`, `engine/index.ts` | **CONCLUÍDO** | Criar arquivos de ponte de re-export dinâmico dependentes de `BAILEYS_ENGINE` (`infinite` ou `official`). |
| **B3** | Mapeamento de imports | `WhatsAppConnectionManager.ts`, `MessageHandler.ts`, `ContactHandler.ts`, `rich-message.ts`, etc. | **CONCLUÍDO** | Redirecionar todos os imports diretos de `@whiskeysockets/baileys` para a nova camada unificada `./engine`. |
| **B4** | Simplificação de envio de mídias ricas | `rich-message.ts` | **CONCLUÍDO** | Refatorar a lógica complexa de binary nodes para invocar os campos nativos do fork InfiniteAPI com fallback inteligente. |
| **B5** | Endpoints de Administração & Reinício | `admin.routes.ts`, `whatsapp.routes.ts` | **CONCLUÍDO** | Adicionar rotas `GET /engine` e `POST /engine` para persistir escolha no arquivo .env e auto-reiniciar o processo (PM2/Docker). |
| **B6** | Componente de Toggle no Painel Admin | Frontend (Páginas Admin) | **CONCLUÍDO** | Criar interface visual de seleção de engine com aviso de reinicialização (~10s) na aba Sistema de admin/settings.tsx. |
@@ -0,0 +1,247 @@
# 📐 Arquitetura de Rede e Topologia de Servidores
Este documento descreve detalhadamente a arquitetura de servidores e rede privada projetada para garantir **alta performance, isolamento de riscos, segurança de dados e facilidade de escalonamento**.
---
## 🗺️ Visão Geral da Topologia (Rede VPN WireGuard)
Toda a comunicação crítica entre os servidores trafega por dentro de uma rede privada criptografada baseada no protocolo **WireGuard** (`10.99.0.0/24`). Os servidores possuem seus firewalls públicos trancados, permitindo apenas acessos necessários e isolando os dados vitais.
```mermaid
graph TD
subgraph "Rede Pública (Internet)"
Internet((Internet Pública))
Client([Notebook Rui])
end
subgraph "Rede Privada VPN (10.99.0.0/24)"
VPS1["🖥️ VPS 1 (Produção) <br> IP: 10.99.0.1 <br> (Traefik / Apps)"]
VPS3["🗄️ VPS 3 (Banco de Dados) <br> IP: 10.99.0.3 <br> (PostgreSQL Dedicado)"]
VPS4["🧪 VPS 4 (Desenvolvimento) <br> IP: 10.99.0.4 <br> (Staging / Builds)"]
VPS5["💬 VPS 5 (CRM WhatsApp) <br> IP: 10.99.0.5 <br> (Bots / APIs)"]
end
%% Conexões Públicas
Internet == HTTP/HTTPS (80/443) ==> VPS1
Client == Conexão VPN Criptografada ==> VPS1
%% Conexões Privadas VPN
VPS1 <== Canal Seguro VPN ==> VPS3
VPS1 <== Canal Seguro VPN ==> VPS4
VPS1 <== Canal Seguro VPN ==> VPS5
Client <== Acesso SSH Seguro ==> VPS1
Client <== Acesso SSH Seguro ==> VPS3
Client <== Acesso SSH Seguro ==> VPS4
Client <== Acesso SSH Seguro ==> VPS5
%% Conexões de Banco de Dados Internas
VPS1 -- PostgreSQL (Porta 5432) --> VPS3
VPS4 -- PostgreSQL (Porta 5432) --> VPS3
VPS5 -- PostgreSQL (Porta 5432) --> VPS3
style VPS3 fill:#1E293B,stroke:#3B82F6,stroke-width:3px,color:#fff
style VPS1 fill:#1E293B,stroke:#10B981,stroke-width:2px,color:#fff
style VPS4 fill:#1E293B,stroke:#F59E0B,stroke-width:2px,color:#fff
style VPS5 fill:#1E293B,stroke:#EC4899,stroke-width:2px,color:#fff
```
---
## 📋 Especificação Detalhada dos Servidores
### 1. 🗄️ VPS 3: Servidor de Banco de Dados Dedicado (Appliance)
* **Função Principal**: Hospedar exclusivamente o banco de dados **PostgreSQL Bare-Metal (Nativo)** e serviços fundamentais de persistência.
* **IP Privado VPN**: `10.99.0.3`
* **Tecnologias**: PostgreSQL (Instalação Nativa via Host), WireGuard, nftables. (O Docker é **estritamente omitido** para reduzir superfície de ataque e otimizar I/O).
* **Políticas do Firewall (nftables)**:
* **Porta 22 (SSH)**: **Bloqueada publicamente**. Liberada **apenas** para o IP reservado do administrador via VPN (`10.99.0.10`).
* **Porta 5432 (PostgreSQL)**: **Bloqueada publicamente**. Liberada **apenas** para conexões originadas de IPs autorizados na VPN (`10.99.0.1` e `10.99.0.5`).
* **Vantagens de Segurança & Performance**:
* **Zero vazamento de dados**: O banco não pode ser acessado de fora da VPN sob hipótese alguma.
* **Conceito de Appliance**: Sem Docker daemon (sem socket de root adicional) ou compiladores, a superfície de invasão é reduzida ao menor nível matemático possível.
* **Performance Máxima (Tuning de Host)**: Como a máquina é nativa e dedicada, as configurações de cache e buffers de memória do PostgreSQL podem usufruir livremente do Page Cache nativo do Linux e Huge Pages do Kernel, garantindo o máximo rendimento físico do SSD e RAM.
---
### 🚀 2. VPS 1: Servidor de Sistemas em Produção
* **Função Principal**: Gateway de entrada do tráfego público e hospedagem das aplicações em produção.
* **IP Privado VPN**: `10.99.0.1` (Atua como Hub WireGuard)
* **Políticas do Firewall (UFW)**:
* **Portas 80 (HTTP) e 443 (HTTPS)**: **Abertas ao público** para permitir o acesso dos clientes aos sites e sistemas.
* **Porta 22 (SSH)**: **Bloqueada publicamente**. Apenas acessível via VPN.
* **Porta 3002 (Antigo Dev)**: **Bloqueada publicamente** no Docker (atrelada apenas ao IP privado `10.99.0.1` do container).
* **Vantagens de Segurança & Performance**:
* **Serviços sem Estado (Stateless)**: A VPS 1 apenas processa as requisições web e consome os dados da VPS 3. Caso a VPS 1 sofra um pico extremo de acessos, o banco de dados continua intacto e seguro.
* **Centralização com Traefik**: Proxy reverso centralizado gerenciando SSL e direcionamento interno seguro de containers.
---
### 🧪 3. VPS 4: Servidor de Desenvolvimento e Homologação (Staging)
* **Função Principal**: Testar novos recursos, hospedar containers de staging e realizar builds de desenvolvimento.
* **IP Privado VPN**: `10.99.0.4`
* **Políticas do Firewall (UFW)**:
* **Porta 22 (SSH)**: **Bloqueada publicamente**. Apenas acessível via VPN.
* **Portas de teste (ex: 3002, 8080)**: **Bloqueadas publicamente** ou atreladas apenas ao IP privado da VPN para acesso exclusivo dos desenvolvedores autorizados.
* **Tecnologias**: Docker, Docker Compose, PostgreSQL 18 (Standby Replica em Container).
* **Vantagens de Segurança & Performance**:
* **Replicação Streaming Real-Time (Staging)**: Hospeda um container PostgreSQL 18 configurado como réplica de leitura (Standby) do banco de dados nativo de produção da VPS 3. Isso permite testar queries pesadas e validar migrações com dados reais em tempo de desenvolvimento de forma 100% segura e com zero impacto no banco principal.
* **Isolamento de Riscos**: Compilações de código (`npm run build`, `docker build`) e testes pesados de carga rodam aqui e consomem CPU sem comprometer os servidores de produção e banco de dados.
* **Ambiente Espelho**: Homologação idêntica à produção para validações precisas antes de aplicar modificações em produção.
---
### 💬 4. VPS 5: Servidor de Aplicação Proprietária (newwhats)
* **Função Principal**: Hospedar o backend e workers da plataforma proprietária **newwhats** (Backend Express com Socket.IO, Workers do Temporal e broker NATS).
* **IP Privado VPN**: `10.99.0.5`
* **Políticas do Firewall (UFW)**:
* **Portas de Integração Web**: Liberadas apenas para os Webhooks e requisições públicas de Socket.IO mapeadas via gateway.
* **Porta 22 (SSH)**: **Bloqueada publicamente**. Apenas acessível via VPN.
* **Vantagens de Segurança & Performance**:
* **Isolamento de Estado**: Executar o backend Node.js (Prisma/Knex), os workers de workflows do Temporal e o broker de mensagens NATS na VPS 5 impede que oscilações no tráfego de mensagens do WhatsApp ou alto uso de CPU nos workers interfiram com o banco de dados principal de produção (VPS 3).
* **Separação de CPU**: O processamento pesado das sessões de WhatsApp e emulação de instâncias roda na VPS 5, consumindo sua CPU e RAM de forma isolada do Gateway (VPS 1) e do Banco (VPS 3).
---
## 🔒 Diretrizes de Segurança Corporativa (Resumo)
1. **Acesso Administrativo**: Realizado **exclusivamente via chave SSH criptográfica**. Autenticação por senhas desativada em todas as máquinas.
2. **Login de Root Desativado**: Bloqueado login direto de root via SSH. Todos conectam como o usuário `deploy` e elevam privilégios de forma segura via `sudo` após a conexão.
3. **Ponto Único de Falha**: Se a VPS 1 (Hub WireGuard) cair, a comunicação VPN é temporariamente pausada. O console VNC da Contabo é mantido ativo como canal redundante de suporte imediato.
4. **Logs Automatizados**: Logins SSH bem-sucedidos ou suspeitos geram alertas em segundo plano com marcações de 30 minutos na agenda central do Google, permitindo controle visual de acessos em tempo real.
---
## 🛡️ Fase 2: Hardening Avançado e Arquitetura Zero-Trust (Roadmap)
Esta seção documenta as decisões estratégicas e o plano de ação de segurança de curto e médio prazo projetados para elevar a maturidade da infraestrutura de rede e servidores ao nível **Enterprise / DevSecOps Sênior**.
### 🗺️ Visão Geral do Fluxo de Segurança na Borda
```mermaid
graph TD
subgraph "Camadas de Defesa (Defense in Depth)"
Internet((Internet Pública)) --> PM[1. Proteção de Borda do Provedor]
PM --> NFT[2. nftables + CrowdSec <br> Bloqueio Dinâmico / Comportamental]
NFT --> TF[3. Traefik Reverse Proxy <br> SSL/TLS & Roteamento]
TF --> DK[4. Containers Docker <br> Rootless / Hardened]
end
```
---
### 1. 🌐 Desativação Proativa de IPv6 (Curto/Médio Prazo)
Para eliminar vetores de bypass acidentais de firewall e reduzir a complexidade da superfície de ataque, o suporte a IPv6 é **totalmente desativado** em todas as VPSs.
* **Ações de Configuração**:
Para desativar em tempo de execução e garantir persistência pós-reboot, o arquivo `/etc/sysctl.d/99-disable-ipv6.conf` deve ser criado com as seguintes diretivas:
```text
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
```
*Comando para aplicar imediatamente:* `sudo sysctl --system`
* **Gatilhos para Reativação do IPv6**:
O IPv6 só será reabilitado caso surja uma necessidade real de negócio, tais como:
* Requisitos de conformidade corporativa (*Enterprise Compliance*).
* Integração obrigatória com CDNs IPv6-only ou arquiteturas de borda distribuída (*Anycast*).
* Casos em que a reativação exigirá a implementação completa de **nftables dual-stack, filtros de Router Advertisement (RA), NDP e SLAAC seguros**.
---
### 2. 🐋 Isolamento de Redes Docker (Prevenção de Movimentação Lateral)
O uso da rede de ponte padrão (`bridge`) do Docker é estritamente proibido em produção. Os containers são segmentados em **redes de domínios funcionais isolados**, limitando severamente o raio de colisão (*blast radius*) caso um container de aplicação (especialmente bots de CRM/WhatsApp ou navegadores invisíveis) seja comprometido.
#### 🔀 Matriz de Conectividade de Dados (newwhats):
```mermaid
graph TD
subgraph "VPS 1: Gateway de Borda pública"
Traefik[Traefik Router]
end
subgraph "VPS 5: Camada de Aplicação (newwhats)"
Express[Backend Express & Socket.IO]
TempWorker[Temporal Worker]
NATS[NATS Broker :4222]
Express <--> NATS
TempWorker <--> Express
end
subgraph "Túnel VPN Criptografado (wg0)"
WG0((Canal Privado WireGuard))
end
subgraph "VPS 3: Appliance de Dados Bare-Metal"
Postgres[(PostgreSQL :5432)]
Dragonfly[(DragonflyDB :6379)]
Temporal[(Temporal Server :7233)]
end
%% Roteamento Web e Sockets
Traefik == HTTPS Proxy / Sockets ==> WG0 ==> Express
%% Conexões de Dados da Aplicação via VPN
Express ==> WG0 ==> Postgres
Express ==> WG0 ==> Dragonfly
TempWorker ==> WG0 ==> Temporal
Temporal -. Persistência Interna .-> Postgres
style Postgres fill:#1E293B,stroke:#3B82F6,stroke-width:2px,color:#fff
style Dragonfly fill:#1E293B,stroke:#10B981,stroke-width:2px,color:#fff
style Temporal fill:#1E293B,stroke:#F59E0B,stroke-width:2px,color:#fff
```
* **Diretrizes de Conectividade**:
* **Tráfego de Borda**: O Traefik na VPS 1 encaminha o tráfego HTTP/HTTPS e conexões persistentes do Socket.IO diretamente para a VPS 5 (`10.99.0.5`) pela interface de túnel criptografado, mantendo as portas da VPS 5 públicas totalmente fechadas.
* **Persistência de Negócio (Prisma + Knex)**: Ambas as ORMs/Query Builders centralizadas na VPS 5 apontam diretamente para o IP interno `10.99.0.3:5432` da VPS 3.
* **Cache e Estados Rápidos**: O DragonflyDB na VPS 3 atende instantaneamente às solicitações de sessões e QR codes da VPS 5 via `10.99.0.3:6379`, provendo tempos de resposta na faixa de microssegundos sem concorrência local de CPU.
* **Workflows Assíncronos**: O Temporal Server processa o agendamento de tarefas e status de reputação na VPS 3, enquanto o **Temporal Worker** na VPS 5 consome e executa as atividades pesadas locais da aplicação.
---
### 3. 🔑 Zero-Trust Interno no WireGuard (ACLs no Firewall)
A rede VPN WireGuard (`10.99.0.0/24`) deixa de ser tratada como uma zona de confiança irrestrita. Implementa-se uma política de privilégio mínimo na comunicação entre os servidores da VPN.
* **O Problema de Movimentação Lateral**:
Se a máquina de desenvolvimento/staging (VPS 4) for comprometida por um script malicioso, o atacante não pode ter permissão para escaneá-la ou conectar-se ao banco de dados de produção (VPS 3), exceto para o fluxo estritamente necessário de replicação de dados.
* **Regras de Isolamento Lateral (Exemplo no Firewall da VPS 3)**:
```text
# Bloquear por padrão toda conexão vinda da VPN exceto as estritamente autorizadas:
- Permitir VPS 1 (Produção) -> Porta 5432 (PostgreSQL) [ACEITAR]
- Permitir VPS 5 (CRM) -> Porta 5432 (PostgreSQL) [ACEITAR]
- Permitir VPS 4 (Staging) -> Porta 5432 (PostgreSQL - Apenas 'replicator') [ACEITAR]
- Permitir VPS 4 (Staging) -> VPS 3 (Produção - Outras portas / serviços) [BLOQUEAR]
```
---
### 4. 🎛️ Migração de Borda: nftables + CrowdSec
Substitui-se a pilha clássica e reativa do `iptables` / `Fail2Ban` por uma arquitetura ativa, dinâmica e de alta performance na VPS pública 1.
* **Por que `nftables`?**
A sintaxe nativa do `nftables` permite a criação de conjuntos dinâmicos (*sets*) de IP de forma extremamente eficiente, processados diretamente no kernel com consumo mínimo de CPU durante ataques volumétricos.
* **Por que `CrowdSec`?**
Diferente do Fail2Ban (que analisa logs localmente e de forma reativa por expressões regulares), o CrowdSec utiliza detecção baseada em comportamentos locais de ataque e sincroniza uma base global de reputação de IPs (*inteligência coletiva*). Se um IP atacou outra infraestrutura na internet, ele é bloqueado na sua VPS antes mesmo de fazer a primeira requisição ao seu Traefik.
---
### 5. 💔 Mitigação do Ponto Único de Falha (SPOF) da VPS 1
Atualmente, a VPS 1 acumula as funções de **Gateway de Borda (Traefik)**, **Hub Central de VPN (WireGuard)** e **Hospedagem de Aplicações**.
* **Visão de Evolução Futura**:
Para eliminar o risco de interrupção total dos serviços em caso de queda da VPS 1, planeja-se a separação física de responsabilidades em médio prazo:
1. **VPS Edge Dedicated**: Uma máquina leve rodando exclusivamente o gateway de entrada (`Traefik`), `nftables` e o concentrador `WireGuard`.
2. **VPS App Dedicated**: Servidores internos que hospedam as aplicações (sem expor portas SSH ou HTTP à internet), consumindo tráfego encaminhado diretamente pelo Gateway da Edge.
---
### 6. 🔒 Hardening Avançado de Containers
Para garantir que a invasão de um container não resulte no comprometimento do sistema operacional host, aplicam-se as seguintes diretrizes de segurança no provisionamento de containers Docker:
* **No-New-Privileges**: Impede que processos dentro do container ganhem novos privilégios via binários `setuid` ou `setgid`.
* **Read-Only Root Filesystem**: Monta o sistema de arquivos do container como apenas leitura, forçando gravações temporárias apenas em volumes declarados ou `/tmp` na memória (tmpfs).
* **CAP Drop**: Remove capacidades de sistema desnecessárias ao container (ex: `NET_ADMIN`, `SYS_ADMIN`, `SYS_CHROOT`).
* **Rootless Containers**: Sempre que suportado pela aplicação, os containers devem rodar com IDs de usuário não privilegiados (non-root).
Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

@@ -0,0 +1,152 @@
# 📂 Guia de Compartilhamento da Pasta de Instruções entre VPSs
Este documento descreve as melhores estratégias e o passo a passo técnico para que **agentes (humanos, automatizados ou IA)** em outras VPSs da sua rede segura (`10.99.0.0/24`) tenham acesso de leitura e escrita à pasta `/home/deploy/instrucoes`.
Como você já possui uma infraestrutura de rede privada criptografada baseada em **WireGuard**, podemos aproveitar essa segurança para compartilhar os arquivos de forma extremamente protegida e eficiente.
---
## 🗺️ Tabela Comparativa de Abordagens
| Abordagem | Tipo | Sincronização | Segurança | Complexidade | Recomendação |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **1. Git Privado** | Repositório | Manual / CI-CD | 🟢 **Máxima** (Chaves SSH) | 🟡 Baixa | **Altamente Recomendado** para desenvolvimento e IA |
| **2. Syncthing** | P2P Ativo | Instantânea | 🟢 **Excelente** (TLS + VPN) | 🟡 Média | **Ideal** para espelhamento em tempo real bidirecional |
| **3. SSHFS** | Mount Direto | Sob demanda | 🟢 **Excelente** (SSH s/ VPN) | 🟢 Muito Baixa | **Excelente** para montagem rápida sem duplicar arquivos |
| **4. Rsync + Cron** | Script Push/Pull | Periódica | 🟢 **Excelente** (SSH) | 🟢 Muito Baixa | Para espelhamento simples de leitura apenas |
---
## 🕸️ Opção 1: Repositório Git Privado (Recomendado para Agentes & IA)
Esta é a abordagem padrão da indústria. Em vez de compartilhar a pasta fisicamente via rede (o que gera dependência de conexão direta), transformamos a pasta `instrucoes` em um **repositório Git privado** (ex: no GitHub ou GitLab).
### Como funciona:
Cada VPS possui um clone local em `/home/deploy/instrucoes`. Quando um agente ou você altera algo, faz um `git commit` e `git push`. As outras VPSs fazem `git pull`.
### Vantagens para Agentes de IA:
Os agentes de IA (como o Antigravity e outros) lidam nativamente de forma excelente com comandos Git. Isso garante controle de versão, histórico de quem alterou o quê, facilidade de rollback e elimina riscos de corrupção de arquivos por escrita simultânea.
### Passo a Passo de Configuração:
1. **Inicializar o Git localmente na máquina de origem (ex: VPS 1):**
```bash
cd /home/deploy/instrucoes
git init
# Adicionar um .gitignore para ignorar arquivos temporários ou chaves sensíveis que não devem subir
echo "gcal_key.json" >> .gitignore
echo "ssh_alerts_config.json" >> .gitignore
git add .
git commit -m "feat: commit inicial das instrucoes"
```
2. **Criar um Repositório Privado** no GitHub/GitLab.
3. **Gerar chaves de acesso SSH (Deploy Keys) nas outras VPSs:**
Em cada VPS que precisa de acesso, gere uma chave SSH para o usuário `deploy`:
```bash
ssh-keygen -t ed25519 -C "vps-instrucoes-deploy" -f ~/.ssh/id_ed25519_github
```
Adicione a chave pública (`.pub`) gerada como **Deploy Key** (com permissão de escrita, se necessário) nas configurações do repositório no GitHub.
4. **Clonar o repositório nas outras VPSs:**
```bash
cd /home/deploy
git clone git@github.com:seu-usuario/seu-repositorio.git instrucoes
```
5. **Automatizar o Pull (Opcional):**
Se quiser que as outras VPSs atualizem as instruções automaticamente sempre que houver mudanças no repositório remoto, configure um cronjob de 5 em 5 minutos para rodar o pull:
```bash
crontab -e
# Adicione a linha abaixo:
*/5 * * * * cd /home/deploy/instrucoes && git pull origin main > /dev/null 2>&1
```
---
## 🔄 Opção 2: Syncthing (Sincronização em Tempo Real Descentralizada)
O **Syncthing** é um serviço peer-to-peer de sincronização contínua de arquivos. Ele roda como um daemon leve nas VPSs e garante que qualquer arquivo criado ou modificado em uma pasta seja propagado instantaneamente para as outras em questão de segundos.
### Como configurar de forma segura via VPN WireGuard:
1. **Instalar o Syncthing em todas as VPSs:**
```bash
sudo apt-get update
sudo apt-get install -y syncthing
```
2. **Configurar para escutar apenas no IP da VPN (Zero-Trust):**
Edite o arquivo de configuração do Syncthing (gerado após a primeira execução em `~/.config/syncthing/config.xml`) e ajuste o endereço da interface gráfica e de sincronização para rodar apenas nos IPs locais ou de VPN, prevenindo exposição pública.
* Altere a linha do GUI para escutar localmente: `<address>127.0.0.1:8384</address>` (Você pode acessar usando um túnel SSH do seu notebook: `ssh -L 8384:127.0.0.1:8384 deploy@10.99.0.1`).
3. **Adicionar os Servidores como Peers:**
* No painel do Syncthing da VPS 1, copie o **Device ID**.
* Adicione esse Device ID no painel da VPS 4/5, especificando o endereço fixo da VPN (ex: `tcp://10.99.0.1:22000`).
* Compartilhe a pasta `/home/deploy/instrucoes` entre eles.
4. **Habilitar o Serviço na inicialização:**
```bash
sudo systemctl enable syncthing@deploy.service
sudo systemctl start syncthing@deploy.service
```
---
## 🔗 Opção 3: SSHFS (Montagem de Pasta sobre SSH na VPN)
O **SSHFS** permite montar um diretório remoto de um servidor em outro servidor de forma transparente, utilizando o próprio protocolo SSH de transporte. Como a sua porta SSH (22) está aberta e protegida dentro da rede privada da VPN (`10.99.0.0/24`), este método é extremamente simples e não exige serviços extras rodando em segundo plano além do próprio SSH.
### Como configurar:
Digamos que a pasta física está na **VPS 1 (IP `10.99.0.1`)** e você deseja acessá-la em tempo real na **VPS 4 (IP `10.99.0.4`)**:
1. **Instalar o SSHFS na VPS de destino (ex: VPS 4):**
```bash
sudo apt-get update
sudo apt-get install -y sshfs
```
2. **Garantir que o usuário `deploy` da VPS 4 tem acesso SSH sem senha à VPS 1:**
Adicione a chave SSH pública (`~/.ssh/id_ed25519.pub`) do usuário `deploy` da VPS 4 no arquivo `/home/deploy/.ssh/authorized_keys` da VPS 1.
3. **Criar a pasta de montagem na VPS de destino (VPS 4):**
```bash
mkdir -p /home/deploy/instrucoes
```
4. **Montar o diretório manualmente para testar:**
```bash
sshfs deploy@10.99.0.1:/home/deploy/instrucoes /home/deploy/instrucoes -o IdentityFile=/home/deploy/.ssh/id_ed25519 -o allow_other -o reconnect -o ServerAliveInterval=15
```
5. **Tornar a montagem permanente pós-reboot (fstab):**
Adicione a seguinte linha ao final do arquivo `/etc/fstab` na VPS de destino:
```text
deploy@10.99.0.1:/home/deploy/instrucoes /home/deploy/instrucoes fuse.sshfs x-systemd.automount,IdentityFile=/home/deploy/.ssh/id_ed25519,allow_other,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,_netdev 0 0
```
---
## 🚀 Opção 4: Rsync Automatizado via Cron (Espelhamento Simples)
Se os agentes nas outras VPSs precisam apenas ler as instruções ou executar instaladores (como o `install.sh`) e você quer uma solução extremamente leve e direta sem montagens de disco.
### Script de sincronização unidirecional (VPS 1 -> VPS de Destino):
Na **VPS 1**, crie um script simples ou configure uma tarefa recorrente no Cron para enviar as atualizações:
```bash
# Sincroniza a pasta de instruções para a VPS 4 através da VPN segura
rsync -avz --delete -e "ssh -i /home/deploy/.ssh/id_ed25519" /home/deploy/instrucoes/ deploy@10.99.0.4:/home/deploy/instrucoes/
```
Você pode programar isso para rodar a cada hora ou a cada 15 minutos de forma silenciosa e imperceptível.
---
## 🎯 Veredito: Qual escolher?
* **Escolha o Git Privado (Opção 1)** se você deseja que múltiplos agentes (incluindo IAs) façam alterações nos scripts e nos documentos de forma colaborativa, sem riscos de conflitos cegos de escrita e mantendo histórico de auditoria perfeito.
* **Escolha o SSHFS (Opção 3)** se você quer facilidade máxima de implementação imediata: a pasta fica unificada, consome armazenamento físico de apenas uma máquina e reflete edições instantaneamente sem sincronizações adicionais.
@@ -0,0 +1,86 @@
# Continuar — NewWhats como Motor Central (SaaS multi-tenant)
## Instrução inicial para a próxima sessão
> Faça uma **análise profissional** da ideia descrita abaixo. Antes de escrever qualquer código:
> 1. Gere uma **lista de tarefas detalhada** (fases, dependências, critérios de aceite) e salve em `TAREFAS.md`.
> 2. Mantenha um **relatório vivo** de todas as implementações em `RELATORIO.md` (o que foi feito, decisões técnicas, pontos de atenção, próximos passos).
> 3. **Regra-ouro:** toda funcionalidade nova deve ser pensada e entregue **como plugin** (em `/plugins/<nome>/` com `manifest.json`, hooks, config_schema). Nada vai direto ao core se couber como plugin.
---
## Contexto do projeto
- **Projeto motor:** `/home/deploy/projetos/newwhats.local` — já possui Baileys (WhatsApp), inbox, sessões, storage (Wasabi), sistema de plugins estilo WordPress, e API de pareamento OTP (`/api/pair/*`) com `integration_key`.
- **Projetos satélites:** múltiplos sistemas (ex.: Alemão Conveniências em `/home/deploy/projetos/alemaoconveniencias.local`) que **não devem instalar Baileys**. Devem consumir o motor via API autenticada por `integration_key`.
- **Já implementado:** pair system (OTP por e-mail), plugin `newwhats` no Alemão com `x-nw-key` auth, modal de pareamento em 3 passos no dashboard.
- **Falta:** expor inbox, sessões e secretária virtual como serviço consumível pelos satélites.
## Ideia a analisar
Transformar o NewWhats em **motor SaaS multi-tenant**, onde cada projeto satélite é um cliente leve que consome:
- **Inbox** (chats, mensagens, envio, mídias)
- **Sessões** (status WA, QR, pareamento de números)
- **Secretária virtual** (IA com contexto — roda 100% no motor, satélite só consome output)
## Arquitetura-alvo
```
NewWhats (motor)
├─ Baileys + Inbox + Storage + Secretária (IA)
└─ API /api/ext/* (auth: x-nw-key)
▼ HTTPS + WebSocket
┌───────┴────────┬──────────┐
Alemão Projeto B Projeto C
(plugin (plugin (plugin
nw-client) nw-client) nw-client)
```
## Decisões propostas (validar antes de implementar)
1. **Plugin `nw-client` portável** em `/plugins-sdk/newwhats-client/` — reutilizável em todos os satélites, zero dependência de Baileys.
2. **API `/api/ext/*` nova** (além de `/api/pair/*` já existente), autenticada por `x-nw-key`:
- `GET /api/ext/sessions`
- `GET /api/ext/inbox?session=X`
- `GET /api/ext/inbox/:chatId/messages`
- `POST /api/ext/inbox/:chatId/send`
- `POST /api/ext/secretaria/ask`
- `WS /api/ext/stream` (eventos realtime — fallback polling 5s)
3. **Secretária virtual sempre no motor** — centraliza custo LLM e contexto.
4. **Isolamento multi-tenant**`integration_key` resolve para `user_id` + `allowed_sessions[]`. Satélite nunca vê dados de outro tenant.
5. **Distribuição do client plugin** — opções:
- (a) `npm publish @newwhats/client` (registry privado/público)
- (b) git submodule
- (c) **CLI `nw plugin install`** que baixa tarball assinado do motor ← recomendado
6. **Tudo como plugin:**
- Motor: `plugins/ext-api/` (expõe `/api/ext/*`), `plugins/secretaria-virtual/` (IA), `plugins/realtime-bridge/` (WS).
- Satélite: `plugins/newwhats-client/` (consumidor único, com UI de inbox embutida).
## Tradeoffs a documentar no relatório
- **WS vs polling** — WS é melhor para UX, mas adiciona complexidade de reconexão/auth. V1 polling 5s, V2 WS.
- **Proxy de mídias** — satélite baixa mídia via motor (`/api/ext/media/:id`) ou recebe URL assinada Wasabi? URL assinada é mais leve mas expõe storage.
- **Rate limit por integration_key** — necessário desde o v1 para proteger motor.
- **Versionamento da API** — `/api/ext/v1/*` desde o início.
## Pendências herdadas (do `project_alemao_plugin_session.md`)
- `aria-hidden` warning no pairModal (Bootstrap 5 — usar `inert` ou Bootstrap 5.3+).
- Inbox no Alemão via plugin — **esta é a primeira entrega desta nova fase**.
- SMTP não configurado no NewWhats — dev_code exposto na resposta até configurar.
## Entregáveis esperados da próxima sessão
1. `TAREFAS.md` — backlog fase-a-fase (motor → SDK → satélite → secretária).
2. `RELATORIO.md` — iniciado com análise profissional da ideia, riscos, e decisões arquiteturais.
3. Esqueleto dos plugins (manifests + hooks declarados, sem lógica ainda) para validar a forma antes de implementar.
4. Pergunta ao usuário: **(A)** contrato detalhado da API primeiro, ou **(B)** PoC end-to-end de `/api/ext/inbox` no Alemão para validar fluxo.
## Referências rápidas
- Sistema de plugins: `backend/src/core/` (types, hook-bus, plugin-loader, plugin-registry)
- Pair system: `backend/src/modules/pair/pair.routes.ts` + `pair.service.ts`
- Plugin consumidor existente: `/home/deploy/projetos/alemaoconveniencias.local/sistema-nuvem/plugins/newwhats/`
- Build backend: `npm run build` em `/home/deploy/projetos/newwhats.local/backend`
- Serviços: `newwhats-backend.service` (8008), `alemaoconveniencias.service` (4001)
@@ -0,0 +1,272 @@
[
{
"version": "v1.0.66",
"buildCount": 66,
"timestamp": "2026-05-15T04:12:10.103Z",
"commitHash": "6534781",
"commitMessage": "docs: adicionar obrigatoriedade de git pull no Passo 1 do protocolo de agentes",
"author": "VPS 4 Builder",
"status": "Sucesso"
},
{
"version": "v1.0.65",
"buildCount": 65,
"timestamp": "2026-05-13T05:56:16.035Z",
"commitHash": "9efc2e7",
"commitMessage": "feat(self-healing): support dynamic repository based on alert label",
"author": "ruicesar",
"status": "Sucesso"
},
{
"version": "v1.0.64",
"buildCount": 64,
"timestamp": "2026-05-12T04:28:55.788Z",
"commitHash": "e6ef64e",
"commitMessage": "feat(soc): add secure external domain routing and fully local grafana dashboards provisioning",
"author": "VPS 1 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.63",
"buildCount": 63,
"timestamp": "2026-05-12T04:23:05.325Z",
"commitHash": "b7541eb",
"commitMessage": "docs(protocolo): adiciona governança de alteração estrutural e idioma português",
"author": "Rui",
"status": "Sucesso"
},
{
"version": "v1.0.62",
"buildCount": 62,
"timestamp": "2026-05-12T04:15:48.794Z",
"commitHash": "599d833",
"commitMessage": "docs: adiciona guia de auto-cura à wiki",
"author": "Rui",
"status": "Sucesso"
},
{
"version": "v1.0.61",
"buildCount": 61,
"timestamp": "2026-05-12T03:35:51.446Z",
"commitHash": "cabf918",
"commitMessage": "fix(secretaria): SEC-06/07/14 — provider OpenAI, telemetria e digitando...",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.60",
"buildCount": 60,
"timestamp": "2026-05-12T03:29:28.895Z",
"commitHash": "5f23c51",
"commitMessage": "chore(deploy): bump version",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.59",
"buildCount": 59,
"timestamp": "2026-05-12T03:16:39.861Z",
"commitHash": "e6a1ab9",
"commitMessage": "fix(soc): trazer configs do Grafana/Promtail para o git e corrigir volumes",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.58",
"buildCount": 58,
"timestamp": "2026-05-12T03:01:53.631Z",
"commitHash": "2f906b1",
"commitMessage": "chore(deploy): bump version",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.57",
"buildCount": 57,
"timestamp": "2026-05-12T01:58:22.906Z",
"commitHash": "12268c2",
"commitMessage": "fix(history): salva mediaPayload em processHistoryBatch + recupera msgs antigas",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.56",
"buildCount": 56,
"timestamp": "2026-05-12T01:32:09.606Z",
"commitHash": "4efa6fb",
"commitMessage": "fix(media): aplica estado 'não disponível' a imagem, vídeo, documento e sticker",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.55",
"buildCount": 55,
"timestamp": "2026-05-12T01:26:34.699Z",
"commitHash": "5fb9a8f",
"commitMessage": "fix(audio): trata erro 400/404 no redownload com estado 'Áudio não disponível'",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.54",
"buildCount": 54,
"timestamp": "2026-05-12T01:19:55.845Z",
"commitHash": "634426e",
"commitMessage": "fix(audio): corrige redownload de áudio + move botão de velocidade antes do waveform",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.53",
"buildCount": 53,
"timestamp": "2026-05-12T01:11:06.530Z",
"commitHash": "9d082a1",
"commitMessage": "fix(whatsapp): mensagens do celular perdidas para contatos @lid + crash P2002",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.52",
"buildCount": 52,
"timestamp": "2026-05-11T21:28:30.055Z",
"commitHash": "3f6b646",
"commitMessage": "feat(self-healing): adiciona o bridge server do Grafana para Gitea e guia de arquitetura",
"author": "ruicesar",
"status": "Sucesso"
},
{
"version": "v1.0.51",
"buildCount": 51,
"timestamp": "2026-05-11T20:50:10.267Z",
"commitHash": "bd6801a",
"commitMessage": "feat(webhook): integra listener com a API de Status do Gitea",
"author": "ruicesar",
"status": "Sucesso"
},
{
"version": "v1.0.50",
"buildCount": 50,
"timestamp": "2026-05-11T19:55:29.765Z",
"commitHash": "cdacfcd",
"commitMessage": "feat(preloader): replace WhatsApp icon with smooth circle SVG",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.49",
"buildCount": 49,
"timestamp": "2026-05-11T19:41:33.099Z",
"commitHash": "2233060",
"commitMessage": "fix(plugins): adiciona backend/src ao include do tsconfig para resolver erros de editor",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.48",
"buildCount": 48,
"timestamp": "2026-05-11T16:58:23.256Z",
"commitHash": "539b922",
"commitMessage": "feat(media): re-download de mídia não baixada ao clicar",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.47",
"buildCount": 47,
"timestamp": "2026-05-11T16:00:16.840Z",
"commitHash": "fc80367",
"commitMessage": "fix(storage): remove auth de /api/storage/view e try-catch nos sticker routes",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.46",
"buildCount": 46,
"timestamp": "2026-05-11T15:48:59.138Z",
"commitHash": "b97f7f9",
"commitMessage": "feat: sistema de figurinhas, emoji PT-BR e preloader inteligente",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.45",
"buildCount": 45,
"timestamp": "2026-05-11T07:42:48.317Z",
"commitHash": "22891e1",
"commitMessage": "fix(listener): skip version-bump commits to prevent deploy loop",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.44",
"buildCount": 44,
"timestamp": "2026-05-11T07:39:34.532Z",
"commitHash": "a7f4d6b",
"commitMessage": "feat(preview): fix button layout and add mouse-following zoom",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.43",
"buildCount": 43,
"timestamp": "2026-05-11T07:22:01.966Z",
"commitHash": "7b55354",
"commitMessage": "fix(send-media): use upsert to prevent P2002 duplicate messageId error",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.42",
"buildCount": 42,
"timestamp": "2026-05-11T07:13:59.009Z",
"commitHash": "4d0b895",
"commitMessage": "chore(deploy): bump version",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.41",
"buildCount": 41,
"timestamp": "2026-05-11T07:12:23.037Z",
"commitHash": "949274e",
"commitMessage": "chore(deploy): bump version",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.40",
"buildCount": 40,
"timestamp": "2026-05-11T07:10:34.610Z",
"commitHash": "7ec3d78",
"commitMessage": "chore(deploy): bump version",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.39",
"buildCount": 39,
"timestamp": "2026-05-11T07:08:47.779Z",
"commitHash": "b0ac06e",
"commitMessage": "chore(deploy): bump version",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.38",
"buildCount": 38,
"timestamp": "2026-05-11T07:07:06.786Z",
"commitHash": "bbbc010",
"commitMessage": "chore(deploy): bump version",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
},
{
"version": "v1.0.37",
"buildCount": 37,
"timestamp": "2026-05-11T07:05:13.069Z",
"commitHash": "706fcea",
"commitMessage": "chore(deploy): bump version",
"author": "VPS 4 Deploy Agent",
"status": "Sucesso"
}
]
@@ -0,0 +1,27 @@
module.exports = {
apps: [
{
name: 'newwhats-backend',
script: 'npm',
args: 'run dev',
cwd: '/home/deploy/projetos/newwhats.local/backend',
watch: false, // tsx watch já cuida do reload
env: {
NODE_ENV: 'development',
},
// Mostra logs coloridos no pm2 logs
time: true,
},
{
name: 'newwhats-frontend',
script: 'npm',
args: 'run dev',
cwd: '/home/deploy/projetos/newwhats.local/frontend',
watch: false, // next dev tem Fast Refresh nativo
env: {
NODE_ENV: 'development',
},
time: true,
},
],
}
+192
View File
@@ -0,0 +1,192 @@
#!/bin/bash
# ==============================================================================
# 🚀 SCRIPT DE INSTALAÇÃO UNIFICADA: ALERTAS SSH (GCAL + TELEGRAM + PAM)
# ==============================================================================
# Este script automatiza o deploy completo da arquitetura de monitoramento em
# qualquer VPS da rede. Ele configura o PAM, Python, Telegram e Banners.
# ==============================================================================
# Cores para formatação de saída
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # Sem Cor
echo -e "${YELLOW}========================================================================${NC}"
echo -e "${YELLOW} 🛡️ MONITORAMENTO SSH AUTOMÁTICO - INSTALADOR DE DEPLOY ${NC}"
echo -e "${YELLOW}========================================================================${NC}"
# 1. Verificar se é root
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Erro: Este script precisa ser executado como root (sudo ./install.sh).${NC}"
exit 1
fi
# 2. Captura de Parâmetro de VPS (IP ou Nome)
VPS_PREFIX=""
while [[ "$#" -gt 0 ]]; do
case $1 in
--vps) VPS_PREFIX="$2"; shift ;;
*) echo "Parâmetro desconhecido: $1"; exit 1 ;;
esac
shift
done
# Se não foi passado --vps, tenta auto-detectar o IP da interface wg0 (VPN) ou eth0
if [ -z "$VPS_PREFIX" ]; then
echo -e "${YELLOW}Auto-detectando o IP da VPS...${NC}"
# Tenta pegar o IP da interface wg0 primeiro
VPS_PREFIX=$(ip -o -4 addr show dev wg0 2>/dev/null | awk '{split($4,a,"/"); print a[1]}')
if [ -z "$VPS_PREFIX" ]; then
# Tenta pegar o IP da interface eth0
VPS_PREFIX=$(ip -o -4 addr show dev eth0 2>/dev/null | awk '{split($4,a,"/"); print a[1]}')
fi
fi
if [ -z "$VPS_PREFIX" ]; then
VPS_PREFIX="127.0.0.1"
fi
echo -e "${GREEN}✅ Prefixo/IP Identificado para esta VPS: ${YELLOW}$VPS_PREFIX${NC}"
# 3. Instalação de Dependências
echo -e "\n${YELLOW}[Passo 1/6] Instalando pacotes e dependências (Apt)...${NC}"
apt-get update -qq
apt-get install -y -qq python3-googleapi curl
echo -e "${GREEN}✅ Pacotes instalados com sucesso.${NC}"
# 4. Criar Chave de Conta de Serviço se não existir
echo -e "\n${YELLOW}[Passo 2/6] Verificando chave da Conta de Serviço Google Cloud...${NC}"
if [ ! -f "/etc/ssh/gcal_key.json" ]; then
echo -e "${YELLOW}⚠️ Aviso: O arquivo /etc/ssh/gcal_key.json não foi encontrado.${NC}"
echo -e "${YELLOW}Por favor, certifique-se de salvar a chave JSON da conta de serviço em /etc/ssh/gcal_key.json e aplicar as permissões corretas.${NC}"
else
echo -e "${GREEN}✅ Chave da Conta de Serviço detectada em /etc/ssh/gcal_key.json.${NC}"
chown root:deploy /etc/ssh/gcal_key.json 2>/dev/null || chown root:root /etc/ssh/gcal_key.json
chmod 640 /etc/ssh/gcal_key.json
fi
# 5. Criar arquivo de configurações se não existir
echo -e "\n${YELLOW}[Passo 3/6] Criando arquivo de configurações centralizadas (/etc/ssh/ssh_alerts_config.json)...${NC}"
if [ ! -f "/etc/ssh/ssh_alerts_config.json" ]; then
cat <<EOF > /etc/ssh/ssh_alerts_config.json
{
"telegram_token": "8641435108:AAGL3rOiVj1GpYAyUi4D-pdeDJ9K_JUdlgM",
"telegram_chat_id": "",
"calendar_id": "385857c79ef3aed0f4923b29007637a8b7764d6f8359959500bce27611833f5d@group.calendar.google.com"
}
EOF
echo -e "${GREEN}✅ Arquivo de configurações criado.${NC}"
else
echo -e "${GREEN}✅ Arquivo de configurações já existe. Mantendo configurações atuais.${NC}"
fi
chown root:deploy /etc/ssh/ssh_alerts_config.json 2>/dev/null || chown root:root /etc/ssh/ssh_alerts_config.json
chmod 640 /etc/ssh/ssh_alerts_config.json
# 6. Criar Scripts Integradores no Servidor
echo -e "\n${YELLOW}[Passo 4/6] Configurando scripts integradores em /usr/local/bin/...${NC}"
# Copia ou escreve o script principal Python
if [ -f "ssh_gcal_alert.py" ]; then
cp ssh_gcal_alert.py /usr/local/bin/ssh_gcal_alert.py
else
# Escreve o arquivo diretamente se rodando solto
curl -s https://raw.githubusercontent.com/sua-uri-ou-copia-local/ssh_gcal_alert.py -o /usr/local/bin/ssh_gcal_alert.py 2>/dev/null
fi
chown root:root /usr/local/bin/ssh_gcal_alert.py
chmod 755 /usr/local/bin/ssh_gcal_alert.py
# Copia ou escreve o script do instalador Telegram
if [ -f "ssh_alert_setup_telegram.py" ]; then
cp ssh_alert_setup_telegram.py /usr/local/bin/ssh_alert_setup_telegram.py
else
curl -s https://raw.githubusercontent.com/sua-uri-ou-copia-local/ssh_alert_setup_telegram.py -o /usr/local/bin/ssh_alert_setup_telegram.py 2>/dev/null
fi
chown root:root /usr/local/bin/ssh_alert_setup_telegram.py
chmod 755 /usr/local/bin/ssh_alert_setup_telegram.py
# Criar o gatilho do PAM dinamicamente com o VPS_PREFIX correto
cat <<EOF > /usr/local/bin/ssh_pam_trigger.sh
#!/bin/bash
# Executa apenas se o estágio do PAM for 'open_session' e o serviço for o 'sshd'
if [ "\$PAM_TYPE" = "open_session" ] && [ "\$PAM_SERVICE" = "sshd" ]; then
USER_LOGIN="\$PAM_USER"
IP_LOGIN="\$PAM_RHOST"
DATE_LOGIN=\$(date "+%Y-%m-%d %H:%M:%S")
# Se o IP vier vazio (comum em alguns túneis locais), assume localhost
if [ -z "\$IP_LOGIN" ]; then
IP_LOGIN="127.0.0.1"
fi
# Validação de IP seguro da VPN
if [[ ! "\$IP_LOGIN" =~ ^10\.99\.0\. ]]; then
AVISO_INDEVIDO="🚨 ALERTA: Acesso fora da rede VPN segura detectado!"
else
AVISO_INDEVIDO="✅ Acesso autorizado através da rede VPN segura."
fi
# Dispara o script Python de calendário e Telegram em segundo plano
/usr/local/bin/ssh_gcal_alert.py --vps "$VPS_PREFIX" --user "\$USER_LOGIN" --ip "\$IP_LOGIN" --status "\$AVISO_INDEVIDO" > /dev/null 2>&1 &
fi
EOF
chown root:root /usr/local/bin/ssh_pam_trigger.sh
chmod 755 /usr/local/bin/ssh_pam_trigger.sh
echo -e "${GREEN}✅ Scripts integradores configurados e autorizados.${NC}"
# 7. Configurar o PAM
echo -e "\n${YELLOW}[Passo 5/6] Configurando o subsistema PAM para SSH (/etc/pam.d/sshd)...${NC}"
PAM_LINE="session optional pam_exec.so type=open_session /usr/local/bin/ssh_pam_trigger.sh"
if grep -q "ssh_pam_trigger.sh" /etc/pam.d/sshd; then
echo -e "${GREEN}✅ PAM para SSH já está devidamente configurado (ignorado para evitar duplicidade).${NC}"
else
echo "$PAM_LINE" >> /etc/pam.d/sshd
echo -e "${GREEN}✅ Gatilho do PAM anexado com sucesso ao fim do arquivo /etc/pam.d/sshd.${NC}"
fi
# 8. Configurar o Banner Visual do Terminal (profile.d)
echo -e "\n${YELLOW}[Passo 6/6] Configurando banner visual de auditoria no terminal (/etc/profile.d/ssh_alert.sh)...${NC}"
cat <<EOF > /etc/profile.d/ssh_alert.sh
#!/bin/bash
# Script de Banner de Auditoria SSH
USER_LOGIN=\$USER
IP_LOGIN=\$(echo \$SSH_CONNECTION | awk '{print \$1}')
DATE_LOGIN=\$(date "+%Y-%m-%d %H:%M:%S")
# Validação de IP seguro da VPN (10.99.0.X)
if [[ ! "\$IP_LOGIN" =~ ^10\.99\.0\. ]]; then
AVISO_INDEVIDO="🚨 ALERTA: Acesso fora da rede VPN segura detectado!"
else
AVISO_INDEVIDO="✅ Acesso autorizado através da rede VPN segura."
fi
# Exibição do Banner de Auditoria ao Usuário no Terminal
echo -e "\033[1;33m"
echo "========================================================================="
echo " 🖥️ AUDITORIA DE ACESSO SISTÊMICO "
echo "========================================================================="
echo -e "\033[0m"
echo " Usuário Conectado: \$USER_LOGIN"
echo " IP de Origem: \$IP_LOGIN"
echo " Data do Login: \$DATE_LOGIN"
echo " Status do Canal: \$AVISO_INDEVIDO"
echo ""
echo " * Todas as operações estão sendo monitoradas e auditadas."
echo "========================================================================="
echo ""
EOF
chmod 755 /etc/profile.d/ssh_alert.sh
echo -e "${GREEN}✅ Banner visual de terminal ativado.${NC}"
# Finalização e Próximos Passos
echo -e "\n${GREEN}========================================================================${NC}"
echo -e "${GREEN}🎉 INSTALAÇÃO CONCLUÍDA COM SUCESSO!${NC}"
echo -e "${GREEN}========================================================================${NC}"
echo -e "\n${YELLOW}👉 PRÓXIMO PASSO (VINCULAR TELEGRAM):${NC}"
echo -e "Para receber notificações instantâneas no seu Telegram, execute o comando abaixo"
echo -e "como administrador e envie uma mensagem para o seu bot:"
echo -e "\n ${GREEN}sudo /usr/local/bin/ssh_alert_setup_telegram.py${NC}\n"
echo -e "========================================================================"
@@ -0,0 +1,121 @@
# 📅 Guia de Implementação: Alertas de SSH no Google Calendar & Telegram
Este guia fornece o instalador automatizado, scripts de configuração dinâmica e documentação técnica para implantar o sistema de monitoramento, auditoria de logins e alertas em tempo real em qualquer servidor da sua rede (ex: VPS 4, VPS 1, etc.).
---
## 🏗️ Como o Sistema Funciona
Para garantir que **100% de todos os acessos** sejam auditados (incluindo acessos de túneis de portas `-N`, SFTP, conexões VS Code e logins interativos) de forma robusta e assíncrona, o sistema é dividido em dois módulos integrados:
```text
[Login SSH Concluído]
├─► [Gatilho do PAM: /etc/pam.d/sshd] (Intercepta todas as sessões: túneis, VS Code, shell, etc.)
│ │
│ └─► [Executa em Background: /usr/local/bin/ssh_pam_trigger.sh]
│ │
│ └─► [Chama: /usr/local/bin/ssh_gcal_alert.py]
│ │
│ ├─► [Busca GeoIP & ISP] (Cidade, Estado, País e Operadora)
│ │
│ ├─► [Rate-Limiting / Debounce] (Evita enchentes de alertas em curto tempo)
│ │
│ ├─► [Local Syslog] (Salva logs localmente em caso de falha de rede)
│ │
│ ├─► [Telegram Push] ──► Mensagem instantânea no Celular
│ │
│ └─► [Google Calendar API] ──► Registra Bloco Visual na Agenda
└─► [Sourced: /etc/profile.d/ssh_alert.sh] (Apenas em shells de login interativos)
└─► Exibe Banner de Auditoria Amarelo no Terminal do Usuário
```
---
## ⚡ Método Rápido (Recomendado - Instalação em 10 segundos)
Se você já possui o repositório de instruções em seu servidor, a instalação é totalmente automatizada através do script `install.sh`.
### Passo 1: Executar o Instalador Automático
Acesse a pasta de instruções e execute o instalador como `root` especificando o IP/Prefixo desta nova VPS (ex: `10.99.0.4`):
```bash
sudo ./install.sh --vps "10.99.0.X"
```
*(O script irá baixar e configurar todas as dependências, permissões, scripts integradores, PAM e o banner visual do terminal automaticamente).*
### Passo 2: Vincular o seu Chat do Telegram ao Bot
Para receber as notificações instantâneas no seu celular através do bot `@AcessosVPSbot`, execute o assistente de emparelhamento automático:
```bash
sudo /usr/local/bin/ssh_alert_setup_telegram.py
```
1. Abra o Telegram no seu celular.
2. Pesquise por **`@AcessosVPSbot`** (ou acesse [t.me/AcessosVPSbot](https://t.me/AcessosVPSbot)) e clique em **"Começar"** ou envie `/start`.
3. O terminal detectará instantaneamente a sua mensagem, capturará o seu Chat ID, atualizará a configuração do servidor e enviará uma confirmação no seu chat!
---
## 🛠️ Detalhamento Técnico e Estrutura de Arquivos
Caso queira entender a estrutura ou realizar alterações manuais, o ecossistema é formado pelos seguintes componentes:
### 1. Arquivo de Configuração Centralizada (`/etc/ssh/ssh_alerts_config.json`)
Guarda as chaves e credenciais de forma segura, com permissões restritas apenas para leitura (`chmod 640` para `root:deploy`).
```json
{
"telegram_token": "8641435108:AAGL3rOiVj1GpYAyUi4D-pdeDJ9K_JUdlgM",
"telegram_chat_id": "SEU_CHAT_ID_CAPTURADO_AQUI",
"calendar_id": "385857c79ef3aed0f4923b29007637a8b7764d6f8359959500bce27611833f5d@group.calendar.google.com"
}
```
### 2. Script de Integração e Decisão (`/usr/local/bin/ssh_gcal_alert.py`)
Escrito em Python 3, realiza a chamada da API do Google Calendar e do Telegram, com tratamento de erros integrado no `syslog` local, controle de frequência (Rate-Limiting de 120s para o mesmo usuário/IP) e enriquecimento de GeoIP/ISP.
### 3. Script de Gatilho do PAM (`/usr/local/bin/ssh_pam_trigger.sh`)
Intercepta no nível de kernel/autenticação cada abertura de sessão SSH e envia os dados para o script Python em segundo plano.
```bash
#!/bin/bash
if [ "$PAM_TYPE" = "open_session" ] && [ "$PAM_SERVICE" = "sshd" ]; then
USER_LOGIN="$PAM_USER"
IP_LOGIN="$PAM_RHOST"
DATE_LOGIN=$(date "+%Y-%m-%d %H:%M:%S")
if [ -z "$IP_LOGIN" ]; then
IP_LOGIN="127.0.0.1"
fi
if [[ ! "$IP_LOGIN" =~ ^10\.99\.0\. ]]; then
AVISO_INDEVIDO="🚨 ALERTA: Acesso fora da rede VPN segura detectado!"
else
AVISO_INDEVIDO="✅ Acesso autorizado através da rede VPN segura."
fi
/usr/local/bin/ssh_gcal_alert.py --vps "10.99.0.X" --user "$USER_LOGIN" --ip "$IP_LOGIN" --status "$AVISO_INDEVIDO" > /dev/null 2>&1 &
fi
```
### 4. Configuração do PAM (`/etc/pam.d/sshd`)
Garante que o gatilho acima seja executado. É adicionada a seguinte linha ao final do arquivo:
```text
session optional pam_exec.so type=open_session /usr/local/bin/ssh_pam_trigger.sh
```
### 5. Banner de Terminal (`/etc/profile.d/ssh_alert.sh`)
Exibe o banner amarelo chamativo de auditoria apenas para usuários conectando de forma convencional via shell de terminal interativo.
---
## 🧪 Como Testar
Para certificar-se de que tudo está rodando adequadamente e simular o disparo direto do PAM com busca de GeoIP externa:
```bash
sudo PAM_TYPE=open_session PAM_SERVICE=sshd PAM_USER=deploy PAM_RHOST=8.8.8.8 /usr/local/bin/ssh_pam_trigger.sh
```
O evento correspondente deverá aparecer na sua agenda imediatamente (em vermelho intenso indicando fora da VPN), e uma mensagem push de alerta chegará instantaneamente no seu Telegram!
@@ -0,0 +1,72 @@
# 🤖 Protocolo Gitea Swarm (PGS): Coordenação de Multi-Agentes em Ambiente Multi-VPS
Este documento estabelece o protocolo de comunicação padrão, obrigatório e imutável para todos os Agentes de Inteligência Artificial (IAs) que atuarem no gerenciamento, desenvolvimento ou manutenção da infraestrutura do **Clube67** distribuída em múltiplos servidores.
Como a arquitetura adota segurança **Zero-Trust de movimentação lateral** (impedindo que agentes em uma VPS acessem outras máquinas via SSH automático), o **Gitea** atua como o **Orquestrador de Estado Assíncrono** do enxame de agentes.
---
## 📋 Regras de Engajamento para Agentes
Sempre que um agente for invocado para resolver uma Issue ou realizar uma implementação, ele deve obrigatoriamente seguir os 4 passos de ciclo de vida abaixo:
```mermaid
graph TD
A[1. Autocognição: Onde estou?] --> B[2. Leitura de Histórico na Issue]
B --> C[3. Execução Local na VPS Atual]
C --> D[4. Comentário de Hand-off e Status]
```
---
### 🔍 Passo 1: Autocognição (Em qual VPS estou?)
Toda IA, ao iniciar sua execução, deve identificar em qual máquina física ou container está rodando, executando comandos como `hostname` ou inspecionando o IP da VPN WireGuard local.
* *Exemplo:* "Identificado que estou rodando na **VPS 3 (Data Server - 10.99.0.3)**."
### 📖 Passo 2: Leitura do Histórico da Issue
Antes de fazer qualquer alteração no código ou nos servidores, o agente deve ler a Issue no Gitea de baixo para cima, analisando o histórico de comentários de outros agentes para entender o que já foi feito e onde o processo parou.
### 🛠️ Passo 3: Execução Local Estrita
O agente deve realizar apenas as alterações correspondentes à VPS onde ele está ativamente rodando. Ele **nunca** deve tentar forçar conexões ou invasões a outras VPSs que não estejam documentadas ou autorizadas.
### 💬 Passo 4: Comentário de Hand-off (O Passe de Bastão)
Ao concluir sua tarefa local, o agente **não deve simplesmente fechar a tarefa**, caso ela dependa de ações em outros servidores. Ele deve postar um comentário estruturado seguindo o modelo abaixo:
---
## 📝 Modelo Padrão de Comentário de Hand-off ( PGS )
O comentário de encerramento do agente na Issue deve conter obrigatoriamente os seguintes blocos:
```markdown
### 🖥️ [VPS ATUAL]: [Nome da VPS, ex: VPS 3 (Data Appliance)]
* **O que foi realizado localmente:** [Lista clara de arquivos modificados, bancos criados ou serviços configurados].
* **Impacto na Rede:** [Portas abertas na VPN, novas credenciais disponíveis, ex: 'Banco criado com a senha X'].
### ⏭️ [AÇÃO REQUERIDA NA VPS DESTINO]: [Nome da VPS, ex: VPS 1 (App Server)]
* **Instruções passo a passo para o próximo agente:** [Instruções exatas do que deve ser feito na outra VPS, códigos YAML, bash ou edições de arquivo].
* **Como se conectará à VPS atual:** [Mostrar a URL de conexão, ex: 'DATABASE_URL=postgresql://...'].
### 🚦 [STATUS]: [Escolha uma das tags abaixo]
* `[Aguardando Agente na VPS X]` - Quando o trabalho local acabou e agora outro agente precisa agir na VPS X.
* `[Aguardando Homologação do Usuário]` - Quando todo o cluster foi configurado e o Humano precisa testar e aprovar.
```
---
## 🎯 Exemplo de Aplicação Real (Issue #2 - newwhats)
1. **Agente A (na VPS 3 - Banco):**
* Cria o banco `newwhats` e configura DragonflyDB.
* Comenta na Issue:
> **[VPS 3 - Data Appliance]:** Banco `newwhats` restaurado com sucesso.
> **[REQUERIDO NA VPS 1]:** Próximo agente, edite o `docker-compose.yml` da aplicação apontando para `10.99.0.3` usando a senha `X`. Comente quando terminar.
> **[STATUS]:** `[Aguardando Agente na VPS 1]`
2. **Agente B (na VPS 1 - Frontend):**
* Inicia na VPS 1, lê o comentário acima, altera o `docker-compose.yml` local e sobe o container.
* Comenta na Issue:
> **[VPS 1 - App Server]:** docker-compose.yml atualizado e container reiniciado. Conexão com VPS 3 validada e healthy.
> **[STATUS]:** `[Aguardando Homologação do Usuário]`
---
*Este protocolo garante consistência, segurança máxima e clareza absoluta na comunicação humana e de inteligência artificial em nosso ecossistema.*
@@ -0,0 +1,106 @@
# 🛡️ Guia de Segurança e Liberdade para o Usuário `deploy`
Na cultura de DevOps moderna, a "lenda" do usuário `deploy` poder trabalhar com total liberdade sem ter acesso ao usuário `root` é, na verdade, uma **melhor prática de segurança chamada "Princípio do Menor Privilégio" (Least Privilege)**.
Se o usuário `deploy` pudesse dar `sudo su` e virar `root` a qualquer momento, qualquer vulnerabilidade na sua aplicação web (ex: um upload de arquivo malicioso ou injeção de código) daria controle total do servidor inteiro para um invasor.
Para dar ao usuário `deploy` toda a liberdade de desenvolvimento e publicação de aplicações com **segurança absoluta**, implementamos 4 pilares práticos:
---
## 🏗️ Pilar 1: Controle Total do Diretório Web (Sem `sudo`)
O usuário `deploy` não precisa de permissão de root para alterar os arquivos da aplicação. Basta torná-lo dono dos diretórios de publicação.
### Prática:
Geralmente usamos a pasta `/var/www/` ou criamos um diretório de aplicações na própria Home do usuário (`/home/deploy/apps/`).
Para dar controle total ao `deploy` sem precisar de `sudo` para editar arquivos:
```bash
# Criar a pasta do seu projeto
sudo mkdir -p /var/www/meu-app
# Definir o usuário deploy como proprietário da pasta
sudo chown -R deploy:deploy /var/www/meu-app
# Aplicar permissões recomendadas (pastas 755 e arquivos 644)
find /var/www/meu-app -type d -exec chmod 755 {} \;
find /var/www/meu-app -type f -exec chmod 644 {} \;
```
**Resultado**: O usuário `deploy` agora pode usar Git, SFTP, VSCode, criar arquivos, deletar e subir atualizações dentro de `/var/www/meu-app` com total liberdade e **zero** necessidade de usar `sudo`.
---
## 🐋 Pilar 2: Gerenciamento do Docker sem Root
Se as suas aplicações rodam em Docker (ou Docker Compose), o usuário `deploy` não precisa usar `sudo docker` para gerenciar os containers.
### Prática:
Adicionamos o usuário `deploy` ao grupo do Docker no Linux:
```bash
sudo usermod -aG docker deploy
```
**Resultado**: Após fazer logout e login novamente, o usuário `deploy` poderá rodar comandos como `docker ps`, `docker compose up -d`, `docker restart` e fazer builds de imagens diretamente, com total liberdade, sem nunca usar `sudo`.
---
## ⚙️ Pilar 3: Sudoers com Limitação Cirúrgica (Segurança Avançada)
Muitas vezes, a aplicação precisa apenas de tarefas muito específicas do sistema operacional, como por exemplo:
* Reiniciar o servidor web Nginx (`systemctl restart nginx`).
* Recarregar as configurações do Nginx (`systemctl reload nginx`).
* Reiniciar um serviço específico da sua aplicação em NodeJS/Python criado no Systemd (`systemctl restart api-app`).
### Prática:
Em vez de dar acesso total ao `sudo` para o `deploy`, nós liberamos **apenas estes comandos específicos**, sem requisição de senha!
1. Criamos um arquivo de configuração exclusivo no Sudoers para o `deploy`:
```bash
sudo nano /etc/sudoers.d/deploy
```
2. Adicionamos a regra cirúrgica:
```text
# Permite que o usuário deploy execute APENAS estes comandos listados como root, sem pedir senha:
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx, /usr/bin/systemctl status nginx
```
3. Aplicamos a permissão correta ao arquivo do sudoers:
```bash
sudo chmod 440 /etc/sudoers.d/deploy
```
**Resultado**: O usuário `deploy` agora pode rodar `sudo systemctl reload nginx` para aplicar uma mudança de domínio na web com total liberdade, mas se ele tentar rodar `sudo apt update` ou `sudo userdel`, o sistema **negará o acesso**.
---
## 🔄 Pilar 4: Processos em Espaço de Usuário (PM2 / Systemd User)
Se a sua aplicação web não roda em Docker e precisa ficar ativa em segundo plano (ex: aplicações Node.js, Python FastAPI, Go), o usuário `deploy` pode gerenciá-la de duas formas sem root:
### A. Utilizando o PM2 (NodeJS Process Manager)
O PM2 roda diretamente no espaço de memória do usuário `deploy`.
```bash
# Como usuário deploy, instalar e rodar apps:
pm2 start app.js --name "minha-api"
pm2 save
```
O `deploy` tem controle total para reiniciar (`pm2 restart minha-api`), parar, e ver logs sem precisar do root.
### B. Serviços Systemd do Usuário (`systemd --user`)
O Linux moderno permite que usuários criem seus próprios arquivos de serviço que rodam sob sua conta na pasta:
`~/.config/systemd/user/`
Você pode gerenciar o serviço rodando:
```bash
systemctl --user daemon-reload
systemctl --user restart meu-servico.service
```
---
## 📊 Tabela de Comparação de Segurança:
| Ação | Como o Root faria (Inseguro) | Como o `deploy` faz com segurança |
| :--- | :--- | :--- |
| **Subir arquivos Web** | Usa `sudo` ou loga como `root` | Altera livremente dentro de `/var/www/` (Dono da pasta) |
| **Gerenciar Containers** | `sudo docker compose restart` | `docker compose restart` (Membro do grupo `docker`) |
| **Reiniciar Nginx** | `sudo systemctl restart nginx` | `sudo systemctl restart nginx` (Autorizado via Sudoers Cirúrgico) |
| **Atualizar Código (Git)** | Roda `git pull` como root | Roda `git pull` usando suas chaves SSH do usuário |
@@ -0,0 +1,83 @@
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const versionFilePath = path.resolve(__dirname, 'version.json');
const deploysFilePath = path.resolve(__dirname, 'deploys.json');
// 1. Obter informações de commit do Git
let commitHash = 'unknown';
let commitMessage = 'unknown';
let author = 'unknown';
try {
commitHash = execSync('git rev-parse --short HEAD').toString().trim();
commitMessage = execSync('git log -1 --pretty=format:"%s"').toString().trim();
author = execSync('git log -1 --pretty=format:"%an"').toString().trim();
} catch (e) {
console.error('Falha ao obter detalhes do git:', e.message);
}
// 2. Carregar versão atual ou inicializar
let data = {
version: 'v1.0.0',
buildCount: 0,
lastDeploy: ''
};
if (fs.existsSync(versionFilePath)) {
try {
data = JSON.parse(fs.readFileSync(versionFilePath, 'utf8'));
} catch (e) {
console.error('Falha ao analisar version.json, reiniciando.');
}
}
// 3. Incrementar buildCount (+1) e atualizar versão v1.0.x
data.buildCount = (data.buildCount || 0) + 1;
data.version = `v1.0.${data.buildCount}`; // v1.+1
data.lastDeploy = new Date().toISOString();
data.lastCommit = {
hash: commitHash,
message: commitMessage,
author: author
};
// 4. Salvar version.json local
fs.writeFileSync(versionFilePath, JSON.stringify(data, null, 2), 'utf8');
// Copiar para a pasta pública do frontend para que fique exposto via HTTP
const publicVersionPath = path.resolve(__dirname, 'frontend/public/version.json');
fs.mkdirSync(path.dirname(publicVersionPath), { recursive: true });
fs.writeFileSync(publicVersionPath, JSON.stringify(data, null, 2), 'utf8');
// 5. Adicionar entrada de histórico no deploys.json
let deploys = [];
if (fs.existsSync(deploysFilePath)) {
try {
deploys = JSON.parse(fs.readFileSync(deploysFilePath, 'utf8'));
} catch (e) {
console.error('Falha ao analisar deploys.json');
}
}
deploys.unshift({
version: data.version,
buildCount: data.buildCount,
timestamp: data.lastDeploy,
commitHash,
commitMessage,
author,
status: 'Sucesso'
});
// Limitar o histórico de deploys a 30 registros
deploys = deploys.slice(0, 30);
fs.writeFileSync(deploysFilePath, JSON.stringify(deploys, null, 2), 'utf8');
// Copiar deploys.json histórico para a pasta pública do frontend
const publicDeploysPath = path.resolve(__dirname, 'frontend/public/deploys.json');
fs.writeFileSync(publicDeploysPath, JSON.stringify(deploys, null, 2), 'utf8');
console.log(`Versão atualizada para ${data.version} (build #${data.buildCount})`);
@@ -0,0 +1,10 @@
{
"version": "v1.0.66",
"buildCount": 66,
"lastDeploy": "2026-05-15T04:12:10.103Z",
"lastCommit": {
"hash": "6534781",
"message": "docs: adicionar obrigatoriedade de git pull no Passo 1 do protocolo de agentes",
"author": "VPS 4 Builder"
}
}
@@ -0,0 +1,111 @@
# 🛡️ Plano de Contingência e Alta Disponibilidade da VPN
Este documento descreve detalhadamente as soluções propostas para evitar o bloqueio de acesso e garantir a continuidade da comunicação entre os servidores (**VPS 3, 4 e 5**) caso o servidor central de VPN (**VPS 1**) sofra uma queda ou falha de sistema.
---
## 🚨 O Problema: O Ponto Único de Falha (SPOF)
Atualmente, a rede privada utiliza uma topologia em **Estrela (Hub-and-Spoke)**, onde a **VPS 1** é o Hub central. Se a VPS 1 cair:
1. A rede VPN criptografada (`10.99.0.0/24`) deixa de funcionar.
2. A comunicação interna entre os servidores (como o envio de dados do app para o banco de dados na VPS 3) é interrompida.
3. Como o SSH padrão (porta 22) está fechado na internet pública para as outras VPSs, você fica **impedido de acessar os servidores via terminal de forma convencional**, necessitando recorrer ao Console VNC no painel web da Contabo.
Abaixo estão as três soluções de contingência, ordenadas de forma prática.
---
## 🚪 Opção A: Estratégia Sênior Zero-Trust (Recomendado - 🛡️ Segurança Absoluta)
Esta abordagem elimina qualquer necessidade de expor portas SSH na internet pública (mesmo portas altas secundárias). Em vez de manter uma porta pública de "fallback" (vulnerável a ruído de scanners e bots), a administração e o tráfego de diagnóstico são restringidos a **100% de conexões internas criptografadas por VPN WireGuard**.
### Como mitigar o risco de lockout (Bloqueio de Acesso):
Para evitar o isolamento de acesso caso o servidor central de VPN caia, adota-se uma trilha tripla de contingência:
1. **Console VNC nativo do Provedor (Ex: Contabo)**:
Mantido ativo na console web do provedor como canal de recuperação direta de emergência e console físico absoluto em caso de desastre.
2. **Peers WireGuard Redundantes (Multi-Device)**:
Configuração de múltiplos dispositivos do administrador autorizados no Hub:
* **Peer 1**: Notebook Principal.
* **Peer 2**: Notebook Reserva, celular corporativo ou VM dedicada.
3. **Backups Offline de Chaves e Configurações**:
Armazenamento das configurações de VPN do Wireguard (`wg0.conf`) de forma cifrada offline (Ex: Pendrive criptografado, Cofre físico ou Vault de segurança como Bitwarden/1Password).
### Vantagens dessa Abordagem:
* **Zero Exposição Pública**: Nenhuma porta além de 80 e 443 (na VPS 1) e da porta UDP do WireGuard ficam abertas na internet.
* **Redução de Overhead**: Conectar de ferramentas administrativas (como DBeaver ou SSH direto) diretamente via IP da VPN (`10.99.0.3`) sem necessidade de fazer túneis SSH adicionais desnecessários sobre o canal VPN.
---
## 🕸️ Opção B: Comunicação Direta entre as VPSs (Full-Mesh - 🛠️ Intermediário)
Consiste em criar túneis diretos do WireGuard entre as VPSs 3, 4 e 5, dispensando a necessidade de a VPS 1 estar online para que elas conversem entre si.
### Como configurar as rotas diretas:
Para fazer isso, precisamos adicionar as chaves públicas e os IPs de endpoint físicos das outras VPSs diretamente no arquivo `/etc/wireguard/wg0.conf` de cada uma das máquinas.
**Exemplo de configuração na VPS 3 (`/etc/wireguard/wg0.conf`):**
```ini
[Interface]
Address = 10.99.0.3/24
PrivateKey = <Chave_Privada_da_VPS_3>
ListenPort = 52830
# Peer 1: VPS 1 (Hub Central)
[Peer]
PublicKey = <Chave_Publica_da_VPS_1>
AllowedIPs = 10.99.0.1/32
Endpoint = 158.220.109.237:51820
PersistentKeepalive = 25
# Peer 2: VPS 4 (Conexão Direta - Redundância)
[Peer]
PublicKey = <Chave_Publica_da_VPS_4>
AllowedIPs = 10.99.0.4/32
Endpoint = <IP_Publico_da_VPS_4>:51820
PersistentKeepalive = 25
# Peer 3: VPS 5 (Conexão Direta - Redundância)
[Peer]
PublicKey = <Chave_Publica_da_VPS_5>
AllowedIPs = 10.99.0.5/32
Endpoint = <IP_Publico_da_VPS_5>:51820
PersistentKeepalive = 25
```
### Vantagens:
* Se a VPS 1 quebrar, as conexões de banco de dados (`10.99.0.3`) das VPSs 4 e 5 continuam funcionando de forma 100% direta e automática.
---
## 🔄 Opção C: VPN Secundária de Backup (Failover Hub - 🛠️ Avançado)
Consiste em configurar a **VPS 3** (Banco de Dados) ou a **VPS 5** (CRM) como um **segundo servidor de VPN (redundante)** rodando sob uma interface separada (ex: `wg1`), criando um caminho de fuga independente.
### Estrutura de Rede da VPN de Backup:
* **VPN Principal (`wg0`)**: Hub na VPS 1, rede `10.99.0.0/24`.
* **VPN de Backup (`wg1`)**: Hub na VPS 3, rede `10.98.0.0/24`.
### Configuração da VPS 3 como Hub Redundante (`/etc/wireguard/wg1.conf`):
```ini
[Interface]
Address = 10.98.0.3/24 # IP da VPS 3 na nova rede de backup
PrivateKey = <Chave_Privada_de_Backup_da_VPS_3>
ListenPort = 51821 # Porta diferente para não colidir com o wg0
# Peer 1: Seu Notebook
[Peer]
PublicKey = <Chave_Publica_do_Seu_Notebook>
AllowedIPs = 10.98.0.10/32
# Peer 2: VPS 4
[Peer]
PublicKey = <Chave_Publica_de_Backup_da_VPS_4>
AllowedIPs = 10.98.0.4/32
# Peer 3: VPS 5
[Peer]
PublicKey = <Chave_Publica_de_Backup_da_VPS_5>
AllowedIPs = 10.98.0.5/32
```
### Vantagens:
* Se a VPS 1 desaparecer da internet por completo, basta que você e seus sistemas ativem a interface `wg1` do WireGuard. Toda a sua infraestrutura continuará conectada em questão de segundos através da nova rede segura comandada pela VPS 3!
@@ -0,0 +1,212 @@
# 🗄️ Guia de Provisionamento e Tuning de Dados: VPS 3 (newwhats)
Este documento centraliza as instruções práticas de configuração, tuning e provisionamento para o banco de dados principal de produção (**PostgreSQL**) e o cache de alta performance (**DragonflyDB**) hospedados na **VPS 3 dedicada** (especificações físicas: **8 GB de RAM e 4 núcleos de CPU**).
---
## 🏛️ Divisão e Partilha Fisiológica de Recursos (Capacidade: 8 GB RAM / 4 CPUs)
Para garantir estabilidade absoluta do sistema e impedir que oscilações no tráfego de mensagens do WhatsApp causem o encerramento forçado de serviços pelo OOM (*Out Of Memory Killer*) do Linux, o hardware é segmentado de forma cirúrgica:
| Componente | Memória Alocada | Threads / CPUs | Função / Justificativa |
| :--- | :--- | :--- | :--- |
| **PostgreSQL shared_buffers** | **2.0 GB** (25%) | Automático (Host) | Cache interno nativo de tabelas e índices de negócio do PostgreSQL. |
| **PostgreSQL work_mem & OS** | **2.2 GB** (~27%) | Automático (Host) | Ordenações locais de queries, conexões e buffers de escrita do banco. |
| **DragonflyDB `--memlimit_gb`** | **3.0 GB** (~38%) | **`--num_threads 2`** | Armazenamento seguro de sessões de WhatsApp, presenças e QR codes ativos. |
| **Sistema Operacional (SO) & Redes** | **800 MB** (~10%) | Reservado (2 CPUs) | Processamento da rede VPN WireGuard, kernel e deamon de firewall. |
> [!IMPORTANT]
> **Tuning de CPU**: O DragonflyDB é multi-threaded por natureza. Definir `--num_threads 2` confina a execução do cache a apenas 2 núcleos, deixando 2 núcleos de processamento físico totalmente livres para as consultas SQL do PostgreSQL e para a criptografia do túnel Wireguard.
---
## 🚪 Escolha Estratégica do Temporal: **Opção B (Temporal Server na VPS 5)**
O **Temporal Server** possui uma pegada de RAM ativa que consome entre **400 MB a 800 MB** em produção. Rodá-lo na VPS 3 deixaria a máquina no limite crítico de recursos físicos (apenas ~600 MB livres de folga).
Portanto, adota-se a **Opção B: Hospedar o Temporal Server na VPS 5 (Aplicação)**:
* **Ganho de Estabilidade**: Preserva a VPS 3 exclusivamente como um *Data Appliance* de alta performance (Postgres + DragonflyDB).
* **Latência Zero de API**: Como o **Temporal Worker** roda ao lado do backend Express na VPS 5, colocá-los na mesma máquina permite que a comunicação entre Worker e Server aconteça via `localhost`, eliminando overhead de rede VPN.
* **Persistência**: O Temporal Server na VPS 5 continuará salvando seus dados persistentes de workflows (agenda/reputação) de forma segura no database dedicado `temporal` na VPS 3 via túnel privado.
---
## 🗄️ 1. Separação de Databases e Privilégios no PostgreSQL
O banco de dados do negócio (`newwhats`) e a infraestrutura interna do orquestrador (`temporal`) são isolados fisicamente em databases independentes para evitar conflito de migrações (Prisma/Knex vs `temporal-sql-tool`), facilitar backups por tabela e prover auditoria de acessos.
Acesse o console do PostgreSQL (`sudo -u postgres psql`) e execute o script SQL abaixo:
```sql
-- ====================================================
-- 1. CRIAÇÃO DE DATABASES INDEPENDENTES
-- ====================================================
CREATE DATABASE newwhats;
CREATE DATABASE temporal;
-- ====================================================
-- 2. CRIAÇÃO DOS USUÁRIOS/ROLES ESTREITOS
-- ====================================================
CREATE USER newwhats_user WITH PASSWORD 'SuaSenhaForteNewwhatsAqui';
CREATE USER temporal_user WITH PASSWORD 'SuaSenhaForteTemporalAqui';
-- ====================================================
-- 3. REVOGAÇÃO DE ACESSOS PÚBLICOS PADRÃO (ZERO-TRUST)
-- ====================================================
REVOKE ALL ON DATABASE newwhats FROM PUBLIC;
REVOKE ALL ON DATABASE temporal FROM PUBLIC;
-- ====================================================
-- 4. ATRIBUIÇÃO DE CONEXÕES EXCLUSIVAS
-- ====================================================
GRANT CONNECT ON DATABASE newwhats TO newwhats_user;
GRANT CONNECT ON DATABASE temporal TO temporal_user;
-- ====================================================
-- 5. CONFIGURAÇÃO DE PRIVILÉGIOS GRANULARES
-- ====================================================
-- Conecte no banco 'newwhats' (\c newwhats) e execute:
GRANT USAGE ON SCHEMA public TO newwhats_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO newwhats_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO newwhats_user;
ALTER DATABASE newwhats OWNER TO newwhats_user;
-- Conecte no banco 'temporal' (\c temporal) e execute:
-- O temporal-sql-tool precisa de privilégios plenos dentro de seu banco para autogerenciar tabelas
GRANT ALL PRIVILEGES ON DATABASE temporal TO temporal_user;
ALTER DATABASE temporal OWNER TO temporal_user;
```
---
## 📊 2. Hardening da Configuração do PostgreSQL (`pg_hba.conf`)
Edite o arquivo `/etc/postgresql/15/main/pg_hba.conf` para garantir que o acesso ao banco de dados seja liberado apenas para os papéis designados originados da rede interna da VPN Wireguard:
```text
# TYPE DATABASE USER ADDRESS METHOD
# 1. Permissões locais do sistema operacional (Apenas Root local)
local all postgres peer
# 2. Permitir que o backend (VPS 5) conecte apenas no database de negócio
host newwhats newwhats_user 10.99.0.5/32 scram-sha-256
# 3. Permitir que o orquestrador Temporal (VPS 5) conecte apenas no database temporal
host temporal temporal_user 10.99.0.5/32 scram-sha-256
# 4. Bloqueio preventivo total para qualquer outro IP ou usuário
host all all 0.0.0.0/0 reject
```
*Para aplicar as alterações:*
```bash
sudo systemctl reload postgresql
```
---
## 🚀 3. Provisionamento do DragonflyDB (Systemd)
O **DragonflyDB** roda nativamente como um serviço leve do sistema, configurado com escuta restrita e controle estrito de memória física.
### 🛠️ Configuração do Serviço `/etc/systemd/system/dragonfly.service`:
Crie o arquivo de configuração de unidade do Systemd:
```ini
[Unit]
Description=DragonflyDB Cache Server
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=dragonfly
Group=dragonfly
# Inicialização endurecida e otimizada para 8 GB RAM / 4 CPUs
ExecStart=/usr/local/bin/dragonfly \
--bind 10.99.0.3 \
--port 6379 \
--memlimit_gb 3 \
--num_threads 2 \
--save_schedule "" \
--logtostdout \
--requirepass SENHA_DRAGONFLY_PRODUCAO
Restart=always
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
```
> [!NOTE]
> **Hardening de Escuta**: O parâmetro `--bind 10.99.0.3` substitui o padrão inseguro `0.0.0.0`. Isso força o DragonflyDB a escutar **exclusivamente** as requisições que transitam por dentro do túnel privado do Wireguard, impossibilitando tentativas de conexão direta a partir da internet pública.
### 🏁 Passos de ativação do serviço:
1. Certifique-se de criar o usuário dedicado do sistema:
```bash
sudo useradd -r -s /bin/false dragonfly
```
2. Recarregue os daemons e ative o DragonflyDB:
```bash
sudo systemctl daemon-reload
sudo systemctl enable dragonfly
sudo systemctl start dragonfly
```
---
## 🏛️ 4. Centralização de Múltiplos Projetos (Database Appliance)
Centralizar os bancos de dados de todos os seus projetos (ex: `google.com`, `facebook.com`, `newwhats`) na **VPS 3 dedicada**, mantendo as aplicações e frontends isolados na **VPS 1 (ou outras VPSs de app)**, é uma excelente decisão de design.
Isso separa a computação volátil das aplicações (onde acontecem picos de tráfego, deploys, memory leaks e builds pesados) da consistência estável da camada de persistência de dados.
### ⚠️ Regras de Segurança e Isolamento Mandatórias:
Para operar esse modelo compartilhado com segurança de nível corporativo e evitar que uma falha em um projeto comprometa ou degrade os outros (efeito *Noisy Neighbor*):
#### 1. Isolamento Lógico Absoluto (Databases & Roles Independentes)
**Nunca** use o mesmo banco de dados ou o mesmo usuário PostgreSQL para projetos distintos. Cada projeto deve ter seu banco de dados próprio e seu usuário com privilégios restritos ao seu respectivo banco.
```text
PostgreSQL (VPS 3)
├── database: google_db ──> OWNER: google_user (Apenas VPS 1 / 10.99.0.1)
├── database: facebook_db ──> OWNER: facebook_user (Apenas VPS 1 / 10.99.0.1)
└── database: newwhats ──> OWNER: newwhats_user (Apenas VPS 5 / 10.99.0.5)
```
#### 2. Configuração do `pg_hba.conf` para Múltiplos Projetos:
Edite o arquivo `/etc/postgresql/15/main/pg_hba.conf` para mapear de forma cirúrgica as origens permitidas para cada projeto:
```text
# TYPE DATABASE USER ADDRESS METHOD
# 1. Permissões de Administração local
local all postgres peer
# 2. Projeto GOOGLE (Frontend/Backend na VPS 1 -> Banco na VPS 3)
host google_db google_user 10.99.0.1/32 scram-sha-256
# 3. Projeto FACEBOOK (Frontend/Backend na VPS 1 -> Banco na VPS 3)
host facebook_db facebook_user 10.99.0.1/32 scram-sha-256
# 4. Projeto NEWWHATS (Backend na VPS 5 -> Banco na VPS 3)
host newwhats newwhats_user 10.99.0.5/32 scram-sha-256
# 5. BLOQUEIO PADRÃO TOTAL (Zero-Trust)
host all all 0.0.0.0/0 reject
```
#### 3. Gestão de Pools de Conexão no Node.js (Prisma/Knex)
Cada aplicação Node.js aberta na VPS 1 tentará criar um pool de conexões persistentes. Se muitos projetos rodarem simultaneamente, as conexões ativas podem saturar a memória RAM da VPS 3 (cada conexão ativa no Postgres é um processo físico consumindo ~10MB-20MB de RAM).
* **Solução**: Restrinja o tamanho máximo de pool nas variáveis de ambiente `.env` de cada projeto na VPS 1:
```env
# Exemplo de limite de conexões no Prisma
DATABASE_URL="postgresql://google_user:SENHA@10.99.0.3:5432/google_db?connection_limit=10"
```
* **PgBouncer (Avançado)**: Se o número de projetos escalonar para dezenas de aplicações na VPS 1, configure o **PgBouncer** na VPS 3 para multiplexar centenas de conexões virtuais em poucas conexões físicas reais do Postgres, reduzindo drasticamente o consumo de RAM.