chore(ops): restore missing root files in newwhats.clube67.com
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
This commit is contained in:
@@ -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 ~2–3 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:** ~5–10 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** | **~4–5 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
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user