diff --git a/DEPLOY-MULTIDEVICE-FIX.md b/DEPLOY-MULTIDEVICE-FIX.md new file mode 100644 index 000000000..a9d16076a --- /dev/null +++ b/DEPLOY-MULTIDEVICE-FIX.md @@ -0,0 +1,274 @@ +# 🚀 Evolution API - Multi-Device Fix + +## 📋 Resumo da Alteração + +**Problema:** A Evolution API estava caindo/desconectando quando o WhatsApp Android estava ativo, porque se identificava como "WebClient" (WhatsApp Web), ocupando o slot de sessão web. + +**Solução:** Remover a identificação de browser para usar o modo Multi-Device nativo do Baileys 7.x, que não conflita com outras sessões. + +--- + +## 🔧 Alteração no Código + +### Arquivo: `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts` + +**ANTES (WebClient - CAI):** +```typescript +const session = this.configService.get('CONFIG_SESSION_PHONE'); + +let browserOptions = {}; + +if (number || this.phoneNumber) { + this.phoneNumber = number; + this.logger.info(`Phone number: ${number}`); +} else { + const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()]; + browserOptions = { browser }; + this.logger.info(`Browser: ${browser}`); +} + +// ... no socketConfig: +...browserOptions, +``` + +**DEPOIS (Multi-Device nativo - NÃO CAI):** +```typescript +if (number || this.phoneNumber) { + this.phoneNumber = number; + this.logger.info(`Phone number: ${number}`); +} + +// Multi-Device mode: não definimos browser para evitar ser tratado como WebClient +// Isso faz o Baileys usar o modo MD nativo, que não conflita com outras sessões +this.logger.info('Using Multi-Device native mode (no browser identification)'); + +// ... no socketConfig: +// Removido browserOptions para usar Multi-Device nativo (não WebClient) +``` + +### Imports removidos: +- `ConfigSessionPhone` do `@config/env.config` +- `WABrowserDescription` do `baileys` +- `release` do `os` + +--- + +## 📦 Repositório Fork + +**URL:** https://github.com/joinads/evolution-api + +**Commit:** `5dbf3e93` - "fix: usar Multi-Device nativo para evitar desconexões" + +--- + +## 🐳 Deploy na VPS com Docker Compose + +### Pré-requisitos +- Docker e Docker Compose instalados +- Acesso SSH à VPS +- Volumes existentes com dados (PostgreSQL, Redis, Instances) + +### Volumes Utilizados (externos) +``` +evolution-clean_evolution_instances # Dados das instâncias WhatsApp +evolution-clean_evolution_redis # Cache Redis +evolution-clean_postgres_data # Banco de dados PostgreSQL +``` + +--- + +## 📝 Comandos de Deploy + +### 1. Clone o repositório +```bash +cd ~ +git clone https://github.com/joinads/evolution-api.git evolution-api-custom +cd evolution-api-custom +``` + +### 2. Copie o .env existente +```bash +cp ~/evolution-clean/.env . +``` + +### 3. Crie o docker-compose.prod.yaml +```bash +cat > docker-compose.prod.yaml << 'EOF' +services: + api: + container_name: evolution_api + build: + context: . + dockerfile: Dockerfile + image: evolution-api:v2.3.4-multidevice + restart: always + depends_on: + - redis + - postgres + ports: + - 8080:8080 + volumes: + - evolution-clean_evolution_instances:/evolution/instances + networks: + - evolution-net + env_file: + - .env + expose: + - 8080 + + redis: + image: redis:latest + networks: + - evolution-net + container_name: redis + command: > + redis-server --port 6379 --appendonly yes + volumes: + - evolution-clean_evolution_redis:/data + ports: + - 6379:6379 + + postgres: + container_name: postgres + image: postgres:15 + networks: + - evolution-net + command: ["postgres", "-c", "max_connections=1000", "-c", "listen_addresses=*"] + restart: always + ports: + - 5432:5432 + environment: + - POSTGRES_USER=caio + - POSTGRES_PASSWORD=caio123 + - POSTGRES_DB=evolution + - POSTGRES_HOST_AUTH_METHOD=trust + volumes: + - evolution-clean_postgres_data:/var/lib/postgresql/data + expose: + - 5432 + +volumes: + evolution-clean_evolution_instances: + external: true + evolution-clean_evolution_redis: + external: true + evolution-clean_postgres_data: + external: true + +networks: + evolution-net: + name: evolution-net + driver: bridge +EOF +``` + +### 4. Pare a Evolution antiga (se estiver rodando) +```bash +cd ~/evolution-clean +docker-compose down +``` + +### 5. Build da nova imagem +```bash +cd ~/evolution-api-custom +docker-compose -f docker-compose.prod.yaml build --no-cache +``` + +### 6. Suba os containers +```bash +docker-compose -f docker-compose.prod.yaml up -d +``` + +### 7. Verifique os logs +```bash +docker-compose -f docker-compose.prod.yaml logs -f api +``` + +--- + +## 🔄 Comandos Úteis + +### Ver status dos containers +```bash +docker-compose -f docker-compose.prod.yaml ps +``` + +### Reiniciar a API +```bash +docker-compose -f docker-compose.prod.yaml restart api +``` + +### Ver logs em tempo real +```bash +docker-compose -f docker-compose.prod.yaml logs -f api +``` + +### Parar todos os containers +```bash +docker-compose -f docker-compose.prod.yaml down +``` + +### Rebuild após alterações no código +```bash +git pull origin main +docker-compose -f docker-compose.prod.yaml build --no-cache +docker-compose -f docker-compose.prod.yaml up -d +``` + +--- + +## 🔍 Verificar se o Fix está Funcionando + +Nos logs da API, você deve ver: +``` +Using Multi-Device native mode (no browser identification) +``` + +**NÃO deve mais aparecer:** +``` +Browser: ['Evolution API', 'Chrome', ...] +``` + +--- + +## ⚠️ Notas Importantes + +1. **Instâncias existentes:** Continuam funcionando normalmente. As credenciais salvas não dependem do parâmetro `browser`. + +2. **Novas conexões:** Usarão o modo Multi-Device nativo automaticamente. + +3. **Se uma sessão expirar:** Ao reconectar via QR Code, já usará o novo modo. + +4. **Volumes externos:** O docker-compose usa `external: true` para apontar para os volumes existentes, preservando todos os dados. + +--- + +## 📊 Comparação: Antes vs Depois + +| Aspecto | Antes (v2.3.4 oficial) | Depois (com fix) | +|---------|------------------------|------------------| +| Identificação | `['Evolution API', 'Chrome', OS]` | Nenhuma (MD nativo) | +| Tipo de sessão | WebClient | Multi-Device | +| Aparece como | "WhatsApp Web" | Dispositivo vinculado | +| Conflita com Android | ✅ SIM | ❌ NÃO | +| Cai quando Android ativo | ✅ SIM | ❌ NÃO | + +--- + +## 🆘 Rollback (Voltar para versão oficial) + +Se precisar voltar para a versão oficial: + +```bash +cd ~/evolution-api-custom +docker-compose -f docker-compose.prod.yaml down + +cd ~/evolution-clean +docker-compose up -d +``` + +## 🔗 Links +- Fork: https://github.com/joinads/evolution-api +- Original: https://github.com/EvolutionAPI/evolution-api +- Baileys: https://github.com/WhiskeySockets/Baileys + diff --git a/deploy-vps.sh b/deploy-vps.sh new file mode 100644 index 000000000..5c5ee61cf --- /dev/null +++ b/deploy-vps.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# =========================================== +# SCRIPT DE DEPLOY - Evolution API Multi-Device +# =========================================== + +set -e + +echo "🚀 Iniciando deploy da Evolution API com Multi-Device fix..." + +# Cores para output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Verificar se está no diretório correto +if [ ! -f "docker-compose.prod.yaml" ]; then + echo -e "${RED}❌ Erro: Execute este script no diretório da Evolution API${NC}" + exit 1 +fi + +# Backup do docker-compose atual (se existir) +if [ -f "docker-compose.yaml" ]; then + echo -e "${YELLOW}📦 Fazendo backup do docker-compose.yaml atual...${NC}" + cp docker-compose.yaml docker-compose.yaml.backup.$(date +%Y%m%d_%H%M%S) +fi + +# Parar containers existentes (mantém volumes) +echo -e "${YELLOW}⏹️ Parando containers existentes...${NC}" +docker compose -f docker-compose.prod.yaml down 2>/dev/null || docker-compose -f docker-compose.prod.yaml down 2>/dev/null || true + +# Build da nova imagem +echo -e "${YELLOW}🔨 Buildando imagem com Multi-Device fix...${NC}" +docker compose -f docker-compose.prod.yaml build --no-cache api + +# Subir containers +echo -e "${YELLOW}🚀 Iniciando containers...${NC}" +docker compose -f docker-compose.prod.yaml up -d + +# Aguardar API iniciar +echo -e "${YELLOW}⏳ Aguardando API iniciar...${NC}" +sleep 10 + +# Verificar status +echo -e "${GREEN}✅ Deploy concluído!${NC}" +echo "" +echo "📊 Status dos containers:" +docker compose -f docker-compose.prod.yaml ps + +echo "" +echo "📋 Últimos logs da API:" +docker compose -f docker-compose.prod.yaml logs api --tail 20 + +echo "" +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN}🎉 Evolution API Multi-Device está rodando!${NC}" +echo -e "${GREEN}========================================${NC}" +echo "" +echo "🔗 Acesse: http://SEU_IP:8080" +echo "📚 Docs: http://SEU_IP:8080/docs" +echo "🖥️ Manager: http://SEU_IP:8080/manager" +echo "" +echo "💡 Para ver logs em tempo real:" +echo " docker compose -f docker-compose.prod.yaml logs -f api" + diff --git a/docker-compose.local.yaml b/docker-compose.local.yaml new file mode 100644 index 000000000..1c68ee407 --- /dev/null +++ b/docker-compose.local.yaml @@ -0,0 +1,120 @@ +version: "3.8" + +services: + api: + container_name: evolution_api_local + build: + context: . + dockerfile: Dockerfile + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_started + ports: + - "8080:8080" + volumes: + - evolution_instances:/evolution/instances + networks: + - evolution-local + environment: + # Servidor + - SERVER_NAME=evolution + - SERVER_TYPE=http + - SERVER_PORT=8080 + - SERVER_URL=http://localhost:8080 + - SERVER_DISABLE_DOCS=false + - SERVER_DISABLE_MANAGER=false + + # Banco de dados + - DATABASE_PROVIDER=postgresql + - DATABASE_CONNECTION_URI=postgresql://evolution:evolution123@postgres:5432/evolution + - DATABASE_CONNECTION_CLIENT_NAME=evolution + - DATABASE_SAVE_DATA_INSTANCE=true + - DATABASE_SAVE_DATA_NEW_MESSAGE=true + - DATABASE_SAVE_MESSAGE_UPDATE=true + - DATABASE_SAVE_DATA_CONTACTS=true + - DATABASE_SAVE_DATA_CHATS=true + - DATABASE_SAVE_DATA_HISTORIC=true + - DATABASE_SAVE_DATA_LABELS=true + - DATABASE_SAVE_IS_ON_WHATSAPP=true + - DATABASE_SAVE_IS_ON_WHATSAPP_DAYS=7 + + # Redis + - CACHE_REDIS_ENABLED=true + - CACHE_REDIS_URI=redis://redis:6379 + - CACHE_REDIS_PREFIX_KEY=evolution-cache + - CACHE_REDIS_TTL=604800 + - CACHE_REDIS_SAVE_INSTANCES=true + - CACHE_LOCAL_ENABLED=true + + # Autenticação + - AUTHENTICATION_API_KEY=sua-api-key-aqui + - AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=false + + # Logs + - LOG_LEVEL=ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,WEBHOOKS,WEBSOCKET + - LOG_COLOR=true + - LOG_BAILEYS=error + + # Instâncias + - DEL_INSTANCE=false + - DEL_TEMP_INSTANCES=true + + # Idioma + - LANGUAGE=pt-BR + + # WebSocket + - WEBSOCKET_ENABLED=true + - WEBSOCKET_GLOBAL_EVENTS=true + + # QR Code + - QRCODE_LIMIT=30 + - QRCODE_COLOR=#198754 + + # Telemetria + - TELEMETRY_ENABLED=false + + postgres: + container_name: evolution_postgres_local + image: postgres:15-alpine + restart: unless-stopped + environment: + - POSTGRES_DB=evolution + - POSTGRES_USER=evolution + - POSTGRES_PASSWORD=evolution123 + volumes: + - postgres_data:/var/lib/postgresql/data + networks: + - evolution-local + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U evolution -d evolution"] + interval: 5s + timeout: 5s + retries: 5 + + redis: + container_name: evolution_redis_local + image: redis:7-alpine + restart: unless-stopped + command: redis-server --appendonly yes + volumes: + - redis_data:/data + networks: + - evolution-local + ports: + - "6379:6379" + +volumes: + evolution_instances: + postgres_data: + redis_data: + +networks: + evolution-local: + name: evolution-local + driver: bridge + diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml new file mode 100644 index 000000000..7f05d11ca --- /dev/null +++ b/docker-compose.prod.yaml @@ -0,0 +1,64 @@ +services: + api: + container_name: evolution_api + # Build local com as alterações do Multi-Device + build: + context: . + dockerfile: Dockerfile + image: evolution-api:v2.3.4-multidevice + restart: always + depends_on: + - redis + - postgres + ports: + - 8080:8080 + volumes: + - evolution_instances:/evolution/instances + networks: + - evolution-net + env_file: + - .env + expose: + - 8080 + + redis: + image: redis:latest + networks: + - evolution-net + container_name: redis + command: > + redis-server --port 6379 --appendonly yes + volumes: + - evolution_redis:/data + ports: + - 6379:6379 + + postgres: + container_name: postgres + image: postgres:15 + networks: + - evolution-net + command: ["postgres", "-c", "max_connections=1000", "-c", "listen_addresses=*"] + restart: always + ports: + - 5432:5432 + environment: + - POSTGRES_USER=caio + - POSTGRES_PASSWORD=caio123 + - POSTGRES_DB=evolution + - POSTGRES_HOST_AUTH_METHOD=trust + volumes: + - postgres_data:/var/lib/postgresql/data + expose: + - 5432 + +volumes: + evolution_instances: + evolution_redis: + postgres_data: + +networks: + evolution-net: + name: evolution-net + driver: bridge + diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 2636adbda..d54772e7e 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -67,7 +67,6 @@ import { Chatwoot, ConfigService, configService, - ConfigSessionPhone, Database, Log, Openai, @@ -122,7 +121,6 @@ import makeWASocket, { Product, proto, UserFacingSocketConfig, - WABrowserDescription, WAMediaUpload, WAMessage, WAMessageKey, @@ -140,7 +138,6 @@ import Long from 'long'; import mimeTypes from 'mime-types'; import NodeCache from 'node-cache'; import cron from 'node-cron'; -import { release } from 'os'; import { join } from 'path'; import P from 'pino'; import qrcode, { QRCodeToDataURLOptions } from 'qrcode'; @@ -550,21 +547,16 @@ export class BaileysStartupService extends ChannelStartupService { private async createClient(number?: string): Promise { this.instance.authState = await this.defineAuthState(); - const session = this.configService.get('CONFIG_SESSION_PHONE'); - - let browserOptions = {}; - if (number || this.phoneNumber) { this.phoneNumber = number; this.logger.info(`Phone number: ${number}`); - } else { - const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()]; - browserOptions = { browser }; - - this.logger.info(`Browser: ${browser}`); } + // Multi-Device mode: não definimos browser para evitar ser tratado como WebClient + // Isso faz o Baileys usar o modo MD nativo, que não conflita com outras sessões + this.logger.info('Using Multi-Device native mode (no browser identification)'); + const baileysVersion = await fetchLatestWaWebVersion({}); const version = baileysVersion.version; const log = `Baileys version: ${version.join('.')}`; @@ -630,7 +622,7 @@ export class BaileysStartupService extends ChannelStartupService { msgRetryCounterCache: this.msgRetryCounterCache, generateHighQualityLinkPreview: true, getMessage: async (key) => (await this.getMessage(key)) as Promise, - ...browserOptions, + // Removido browserOptions para usar Multi-Device nativo (não WebClient) markOnlineOnConnect: this.localSettings.alwaysOnline, retryRequestDelayMs: 350, maxMsgRetryCount: 4,