Files
AyronSantos ae629d1dc2 Migração para PostgreSQL multi-driver + correções de segurança
- Camada de banco unificada (src/database.js): drivers Postgres/Firebird,
  tradutor de SQL, suporte a schema e pool de conexões
- Conexões: novo_local (Postgres externo) e firebird_local (legado)
- Tela de rotas da API redesenhada (auth, params, exemplos de body)
- Correções de segurança (críticos/altos/médios/baixos): XSS no chat,
  escalonamento de privilégio, mídia autenticada, SQL restrito a gerente,
  JWT sem fallback + issuer, IDOR em conversas, CORS por allowlist,
  rate-limit no login, limites de corpo por rota
- Deploy alinhado: install.sh grava .env com PG_*, migracoes.js driver-aware

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 10:02:59 -03:00

483 lines
23 KiB
Bash

#!/bin/bash
# =============================================================
# Chatc2 - Instalacao automatizada para Ubuntu 22.04 LTS
# Uso: curl -sL https://bit.ly/chatc2-install | bash
# ou: wget -qO- https://bit.ly/chatc2-install | bash
# =============================================================
set -euo pipefail
# ████████████████████████████████████████████████████
# VARIAVEIS
# ████████████████████████████████████████████████████
CHATC2_USER="chatc2"
CHATC2_DIR="/home/$CHATC2_USER/chatc2"
CHATC2_REPO="https://github.com/seu-usuario/chatc2/archive/refs/heads/main.tar.gz"
NODE_VERSION="22"
FIREBIRD_VERSION="3.0"
WHISPER_VERSION="1.5.4"
WHISPER_MODEL="ggml-tiny.bin" # 75MB - troque para ggml-base.bin (142MB) para mais precisao
SERVER_DOMAIN="${SERVER_DOMAIN:-}" # Opcional: seu-dominio.com.br
JWT_SECRET=""
NGROK_URL=""
# PostgreSQL EXTERNO (banco principal "novo_local").
# Informe via variaveis de ambiente antes de rodar, ou edite o .env depois.
PG_HOST="${PG_HOST:-}"
PG_PORT="${PG_PORT:-5432}"
PG_USER="${PG_USER:-postgres}"
PG_PASSWORD="${PG_PASSWORD:-}"
PG_DATABASE="${PG_DATABASE:-postgres}"
PG_SCHEMA="${PG_SCHEMA:-public}"
# Cores
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
log() { echo -e "${GREEN}[✓]${NC} $1"; }
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
err() { echo -e "${RED}[✘]${NC} $1"; exit 1; }
info() { echo -e "${CYAN}[i]${NC} $1"; }
# ████████████████████████████████████████████████████
# VERIFICACAO INICIAL
# ████████████████████████████████████████████████████
if [ "$EUID" -ne 0 ]; then
err "Execute como root: sudo bash install.sh"
fi
echo ""
echo "╔══════════════════════════════════════════════════╗"
echo "║ Chatc2 - Instalacao Automatica ║"
echo "║ Ubuntu 22.04 LTS ║"
echo "╚══════════════════════════════════════════════════╝"
echo ""
# ████████████████████████████████████████████████████
# PASSO 1: SYSTEMA BASE
# ████████████████████████████████████████████████████
info "Passo 1/12: Atualizando sistema..."
apt-get update -y
apt-get upgrade -y
apt-get install -y curl wget git unzip tar gzip \
build-essential python3 python3-pip \
ffmpeg nginx ufw certbot python3-certbot-nginx \
firebird3.0-server firebird3.0-utils
# ████████████████████████████████████████████████████
# PASSO 2: NODE.JS
# ████████████████████████████████████████████████████
info "Passo 2/12: Instalando Node.js $NODE_VERSION..."
curl -fsSL "https://deb.nodesource.com/setup_${NODE_VERSION}.x" | bash -
apt-get install -y nodejs
npm install -g pm2
log "Node.js $(node -v) instalado"
# ████████████████████████████████████████████████████
# PASSO 3: FIREBIRD
# ████████████████████████████████████████████████████
info "Passo 3/12: Configurando Firebird..."
systemctl enable firebird3.0
systemctl start firebird3.0 2>/dev/null || true
sleep 2
sed -i 's/^isc_password =.*/isc_password = masterkey/' /etc/firebird/3.0/firebird.conf 2>/dev/null || true
sed -i 's/^RemoteBindAddress = localhost/RemoteBindAddress = 0.0.0.0/' /etc/firebird/3.0/firebird.conf 2>/dev/null || true
# ── Correção de compatibilidade node-firebird + Firebird 3.0 ──
# Usa Legacy_Auth (mais compatível) em vez de Srp256 (não suportado pelo node-firebird)
sed -i 's/^#AuthServer = Srp$/AuthServer = Legacy_Auth/' /etc/firebird/3.0/firebird.conf 2>/dev/null || true
sed -i 's/^#AuthClient = Srp, Srp256, Legacy_Auth #Non Windows clients$/AuthClient = Legacy_Auth/' /etc/firebird/3.0/firebird.conf 2>/dev/null || true
# WireCrypt = Enabled (compatível com Legacy_Auth — não exige criptografia)
sed -i 's/^#WireCrypt = Enabled (for client) \/ Required (for server)$/WireCrypt = Enabled/' /etc/firebird/3.0/firebird.conf 2>/dev/null || true
systemctl restart firebird3.0
log "Firebird 3.0 configurado (senha: masterkey, auth: Legacy_Auth, WireCrypt: Enabled)"
# Garante que o Firebird esta rodando antes de continuar
systemctl restart firebird3.0 2>/dev/null || systemctl start firebird3.0 2>/dev/null || true
sleep 1
# ████████████████████████████████████████████████████
# PASSO 4: USUARIO E DIRETORIOS
# ████████████████████████████████████████████████████
info "Passo 4/12: Criando usuario e diretorios..."
id -u "$CHATC2_USER" &>/dev/null || useradd -m -s /bin/bash "$CHATC2_USER"
mkdir -p "$CHATC2_DIR"/{db,logs,tmp,uploads/audio,public,whisper/models,scripts,app/config,app/controllers,app/routes,app/middlewares}
chown -R "$CHATC2_USER:$CHATC2_USER" "$CHATC2_DIR"
# ████████████████████████████████████████████████████
# PASSO 5: WHISPER.CPP (Transcricao de Audio)
# ████████████████████████████████████████████████████
info "Passo 5/12: Instalando Whisper.cpp (compilacao nativa Linux)..."
mkdir -p "$CHATC2_DIR/whisper/models"
# Compila whisper.cpp do source (Linux) - binario nativo
if [ ! -f "$CHATC2_DIR/whisper/main" ]; then
info "Compilando whisper.cpp v${WHISPER_VERSION}..."
cd /tmp
rm -rf whisper-build 2>/dev/null || true
git clone --depth 1 --branch v${WHISPER_VERSION} https://github.com/ggerganov/whisper.cpp.git whisper-build 2>&1 | tail -1
cd whisper-build
make -j$(nproc) main 2>&1 | tail -3
cp -f main "$CHATC2_DIR/whisper/main"
chmod +x "$CHATC2_DIR/whisper/main"
cd /tmp && rm -rf whisper-build
log "Whisper.cpp compilado e instalado em $CHATC2_DIR/whisper/main"
else
log "Whisper.cpp binario Linux ja existe, pulando compilacao"
fi
info "Baixando modelo $WHISPER_MODEL (pode levar alguns minutos)..."
if [ ! -f "$CHATC2_DIR/whisper/models/$WHISPER_MODEL" ]; then
su - "$CHATC2_USER" -c "cd $CHATC2_DIR/whisper && \
curl -sL -o models/$WHISPER_MODEL \
https://huggingface.co/ggerganov/whisper.cpp/resolve/main/$WHISPER_MODEL"
log "Modelo $WHISPER_MODEL baixado"
else
log "Modelo $WHISPER_MODEL ja existe"
fi
log "Whisper.cpp instalado em $CHATC2_DIR/whisper/"
# ████████████████████████████████████████████████████
# PASSO 6: ARQUIVOS DO PROJETO
# ████████████████████████████████████████████████████
info "Passo 6/12: Preparando para receber arquivos..."
info "Transfira os arquivos do projeto para $CHATC2_DIR via SCP/SFTP"
info "Depois execute manualmente: cd $CHATC2_DIR && npm install"
echo ""
echo " ╔══════════════════════════════════════════════════════════╗"
echo " ║ Comando para enviar os arquivos (do seu computador): ║"
echo " ║ ║"
echo " ║ cd C:/projects/Chatc2 ║"
echo " ║ scp -r src/ scripts/ package.json package-lock.json ║"
echo " ║ .env.example deploy-linux/ ║"
echo " ║ root@<IP>:$CHATC2_DIR/ ║"
echo " ║ ║"
echo " ║ E depois rode novamente este script: ║"
echo " ║ bash $0 --continue ║"
echo " ╚══════════════════════════════════════════════════════════╝"
echo ""
# Se o script foi chamado com --continue, pula a espera
if [ "${1:-}" != "--continue" ]; then
info "Aguardando transferencia dos arquivos..."
info "Apos transferir, execute: bash $0 --continue"
exit 0
fi
# ████████████████████████████████████████████████████
# PASSO 7: NPM INSTALL
# ████████████████████████████████████████████████████
if [ ! -f "$CHATC2_DIR/package.json" ]; then
err "Arquivos do projeto nao encontrados em $CHATC2_DIR. Transfira via SCP primeiro."
fi
info "Passo 7/12: Instalando dependencias Node..."
chown -R "$CHATC2_USER:$CHATC2_USER" "$CHATC2_DIR"
# Remove node_modules anterior (se existir) para garantir instalação limpa
if [ -d "$CHATC2_DIR/node_modules" ]; then
info "Removendo node_modules anterior para instalacao limpa..."
rm -rf "$CHATC2_DIR/node_modules"
fi
# Executa npm install com saida completa (sem suprimir erros)
if ! su - "$CHATC2_USER" -c "cd $CHATC2_DIR && npm install"; then
err "Falha ao instalar dependencias Node. Verifique a conexao e o package.json"
fi
# Verifica se modulos criticos foram instalados
if [ ! -d "$CHATC2_DIR/node_modules/express" ]; then
err "Modulo 'express' nao encontrado apos npm install. Verifique o package.json"
fi
if [ ! -d "$CHATC2_DIR/node_modules/dotenv" ]; then
info "Instalando dotenv (carregamento de .env)..."
su - "$CHATC2_USER" -c "cd $CHATC2_DIR && npm install dotenv" || warn "Falha ao instalar dotenv"
fi
# Garante que o server.js carrega dotenv (variaveis do .env)
SERVER_JS="$CHATC2_DIR/src/server.js"
if [ -f "$SERVER_JS" ] && ! grep -q "require('dotenv').config()" "$SERVER_JS" 2>/dev/null; then
sed -i "1s/^/require('dotenv').config();\n/" "$SERVER_JS"
log "dotenv configurado no server.js"
fi
log "Dependencias instaladas e verificadas"
# ── Patch node-firebird: compatibilidade com Firebird 3.0 + sintaxe Node 22 ──
info "Passo 7b/12: Aplicando patches node-firebird..."
if [ -f "$(dirname "$0")/patch_node_firebird.py" ]; then
python3 "$(dirname "$0")/patch_node_firebird.py" "$CHATC2_DIR" 2>&1
log "Patches node-firebird aplicados"
else
warn "patch_node_firebird.py nao encontrado"
fi
# ── wireCrypt (Firebird 3.0) ──
# A partir da v2.0 a camada de banco foi unificada em src/database.js e o
# wireCrypt já vem embutido nos defaults do driver Firebird — nada a patchar.
info "Passo 7c/12: wireCrypt (Firebird) já embutido em src/database.js"
log "Nenhum patch de wireCrypt necessário"
# ████████████████████████████████████████████████████
# PASSO 8: ARQUIVO .ENV
# ████████████████████████████████████████████████████
info "Passo 8/12: Configurando .env..."
JWT_SECRET="CHATc2_$(date +%s)_$(openssl rand -hex 16)"
# Detecta IPs da maquina
LOCAL_IP=$(ip -4 addr show scope global | grep -oP 'inet \K[\d.]+' | head -1)
[ -z "$LOCAL_IP" ] && LOCAL_IP="127.0.0.1"
PUBLIC_IP=$(curl -s ifconfig.me 2>/dev/null || echo "")
# Monta URLs
LOCAL_URL="http://${LOCAL_IP}:3000"
if [ -n "$SERVER_DOMAIN" ]; then
EXTERNAL_URL="https://${SERVER_DOMAIN}"
elif [ -n "$PUBLIC_IP" ]; then
EXTERNAL_URL="http://${PUBLIC_IP}"
else
EXTERNAL_URL="http://${LOCAL_IP}:3000"
fi
# Sempre gera um .env limpo (remove paths Windows, comentarios invalidos, etc.)
if [ -f "$CHATC2_DIR/.env" ]; then
info ".env existente encontrado — fazendo backup e recriando..."
cp "$CHATC2_DIR/.env" "$CHATC2_DIR/.env.bak.$(date +%Y%m%d%H%M%S)" 2>/dev/null || true
fi
cat > "$CHATC2_DIR/.env" << EOF
# ============================================================
# Configuracao do Chatc2
# ============================================================
# Driver padrao do banco principal
DB_DRIVER=postgres
# ------------------------------------------------------------
# PostgreSQL (EXTERNO) — banco principal "novo_local"
# ------------------------------------------------------------
PG_HOST=${PG_HOST}
PG_PORT=${PG_PORT}
PG_USER=${PG_USER}
PG_PASSWORD=${PG_PASSWORD}
PG_DATABASE=${PG_DATABASE}
PG_SCHEMA=${PG_SCHEMA}
# ------------------------------------------------------------
# Firebird (legado / alias "firebird_local")
# ------------------------------------------------------------
DB_HOST=localhost
DB_PORT=3050
DB_USER=SYSDBA
DB_PASSWORD=masterkey
DB_ENCODING=UTF-8
# Caminho do .FDB (vazio = usa ../NOVO.FDB na raiz do projeto)
DB_DATABASE=
# Servidor
PORT=3000
JWT_SECRET=$JWT_SECRET
JWT_EXPIRES_IN=1h
# URLs de acesso
LOCAL_URL=${LOCAL_URL}
EXTERNAL_URL=${EXTERNAL_URL}
# ------------------------------------------------------------
# Seguranca (opcionais)
# ------------------------------------------------------------
# Origens permitidas no CORS (vazio = usa LOCAL_URL + EXTERNAL_URL)
# CORS_ORIGINS=${EXTERNAL_URL}
# Token de verificacao do webhook Evolution (header apikey)
# WEBHOOK_TOKEN=
EOF
chown "$CHATC2_USER:$CHATC2_USER" "$CHATC2_DIR/.env"
log "Arquivo .env criado/atualizado com JWT_SECRET seguro"
log " PG_HOST/DB = ${PG_HOST:-(vazio)} / ${PG_DATABASE} (schema: ${PG_SCHEMA})"
log " LOCAL_URL = ${LOCAL_URL}"
log " EXTERNAL_URL = ${EXTERNAL_URL}"
if [ -z "$PG_HOST" ] || [ -z "$PG_PASSWORD" ]; then
warn "PostgreSQL externo nao configurado: edite $CHATC2_DIR/.env (PG_HOST, PG_PASSWORD, PG_DATABASE, PG_SCHEMA) antes de iniciar."
fi
# ████████████████████████████████████████████████████
# PASSO 9: MIGRACOES DO BANCO
# ████████████████████████████████████████████████████
# ── Permissões dos arquivos .FDB ──
info "Passo 8b/12: Ajustando permissoes dos arquivos .FDB..."
# O Firebird roda como usuario 'firebird'. Os arquivos .FDB precisam
# pertencer ao grupo firebird com permissao de leitura+escrita (660).
if ls "$CHATC2_DIR/db/"*.FDB 2>/dev/null; then
chown "$CHATC2_USER:firebird" "$CHATC2_DIR/db/"*.FDB 2>/dev/null || true
chmod 660 "$CHATC2_DIR/db/"*.FDB 2>/dev/null || true
# Garante que o diretório db/ também seja acessível
chmod 750 "$CHATC2_DIR/db/" 2>/dev/null || true
log "Permissoes ajustadas: $CHATC2_USER:firebird 660"
else
warn "Nenhum arquivo .FDB encontrado em $CHATC2_DIR/db/ — copie os bancos manualmente"
warn "Apos copiar, execute: chown $CHATC2_USER:firebird $CHATC2_DIR/db/*.FDB && chmod 660 $CHATC2_DIR/db/*.FDB"
fi
info "Passo 9/12: Migracoes (Firebird legado)..."
# O schema do PostgreSQL e gerenciado no banco EXTERNO — nao migramos aqui.
# Migracoes Firebird so rodam se houver um .FDB local (alias firebird_local).
if ls "$CHATC2_DIR/db/"*.FDB "$CHATC2_DIR/"*.FDB >/dev/null 2>&1; then
if [ -f "$CHATC2_DIR/scripts/migracoes.js" ]; then
su - "$CHATC2_USER" -c "cd $CHATC2_DIR && node scripts/migracoes.js firebird_local 2>&1 | tail -10" || warn "Migracoes Firebird podem ter falhado"
log "Migracoes Firebird executadas"
fi
else
info "Sem .FDB local — pulando migracoes Firebird (schema do Postgres e externo)."
fi
# ████████████████████████████████████████████████████
# PASSO 9b: HABILITAR USUARIOS ADMIN PARA WEB
# ████████████████████████████████████████████████████
info "Passo 9b/12: Habilitando usuarios admin para acesso web..."
# Habilita acesso web para admins (Postgres principal e Firebird se presente)
cat > /tmp/chatc2-habilitar-web.js << 'SCRIPTJS'
const db = require('DATABASE_PATH');
(async () => {
const alias = process.argv[2];
if (!alias) { console.log('Uso: node script <alias>'); process.exit(1); }
try {
// Habilita USU_ACESSO_WEB = 1 para usuarios ativos com perfil admin (USU_TIPO = A)
const result = await db.query(alias,
"SELECT USU_CODIGO_ID, USU_NOME, USU_LOGIN FROM USUARIOS WHERE USU_STATUS = 'A' AND USU_TIPO = 'A' AND COALESCE(USU_ACESSO_WEB, 0) = 0");
for (const u of result) {
await db.execute(alias, 'UPDATE USUARIOS SET USU_ACESSO_WEB = 1 WHERE USU_CODIGO_ID = ?', [u.USU_CODIGO_ID]);
console.log(' ✅ ' + u.USU_NOME.trim() + ' (' + u.USU_LOGIN.trim() + ') — acesso web habilitado');
}
if (result.length === 0) console.log(' Nenhum usuario admin pendente.');
process.exit(0);
} catch(e) { console.error('Erro:', e.message); process.exit(1); }
})();
SCRIPTJS
sed -i "s|DATABASE_PATH|$CHATC2_DIR/src/database|" /tmp/chatc2-habilitar-web.js
# Postgres externo (alias principal)
su - "$CHATC2_USER" -c "cd $CHATC2_DIR && node /tmp/chatc2-habilitar-web.js novo_local" 2>&1 || true
# Firebird local (apenas se houver .FDB)
if ls "$CHATC2_DIR/db/"*.FDB "$CHATC2_DIR/"*.FDB >/dev/null 2>&1; then
su - "$CHATC2_USER" -c "cd $CHATC2_DIR && node /tmp/chatc2-habilitar-web.js firebird_local" 2>&1 || true
fi
rm -f /tmp/chatc2-habilitar-web.js
log "Usuarios admin verificados (Postgres + Firebird se presente)"
# ████████████████████████████████████████████████████
# PASSO 10: NGINX
# ████████████████████████████████████████████████████
info "Passo 10/12: Configurando Nginx..."
cat > /etc/nginx/sites-available/chatc2 << 'NGINX'
server {
listen 80;
server_name _;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
NGINX
# Remove sites padrao que podem conflitar
rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true
rm -f /etc/nginx/sites-enabled/example.com 2>/dev/null || true
# Habilita o site do chatc2
if [ ! -L /etc/nginx/sites-enabled/chatc2 ]; then
ln -sf /etc/nginx/sites-available/chatc2 /etc/nginx/sites-enabled/
fi
# Testa configuracao antes de aplicar
if nginx -t 2>&1; then
systemctl enable nginx 2>/dev/null || true
systemctl restart nginx 2>/dev/null || systemctl start nginx 2>/dev/null || true
log "Nginx configurado e iniciado"
else
warn "Erro na configuracao do nginx. Verifique manualmente: nginx -t"
fi
# SSL com Let's Encrypt (se dominio foi informado)
if [ -n "${SERVER_DOMAIN:-}" ]; then
info "Configurando SSL para $SERVER_DOMAIN..."
certbot --nginx -d "$SERVER_DOMAIN" --non-interactive --agree-tos \
--email "admin@${SERVER_DOMAIN}" || warn "SSL falhou (verifique DNS)"
log "SSL configurado para $SERVER_DOMAIN"
fi
# ████████████████████████████████████████████████████
# PASSO 11: PM2 (INICIO AUTOMATICO)
# ████████████████████████████████████████████████████
info "Passo 11/12: Configurando PM2..."
# Para processos anteriores (se existirem)
pm2 delete chatc2 2>/dev/null || true
# Inicia a aplicacao com PM2 como root (script ja exige root)
cd "$CHATC2_DIR"
if ! pm2 start src/server.js --name chatc2 --max-memory-restart 512M --time 2>&1; then
err "Falha ao iniciar a aplicacao com PM2. Verifique os logs."
fi
# Aguarda o servidor iniciar e verifica se esta ouvindo na porta 3000
sleep 2
if ss -tlnp | grep -q ':3000'; then
log "Servidor Node.js ouvindo na porta 3000"
else
warn "Servidor pode nao estar ouvindo na porta 3000. Verifique: pm2 logs chatc2"
fi
# Salva a lista de processos e configura inicio automatico no boot
pm2 save 2>/dev/null || true
pm2 startup systemd -u root --hp /root 2>/dev/null || true
log "PM2 configurado - aplicacao iniciara automaticamente no boot"
# ████████████████████████████████████████████████████
# PASSO 12: FIREWALL
# ████████████████████████████████████████████████████
info "Passo 12/12: Configurando firewall..."
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 3000/tcp
ufw --force enable
log "Firewall configurado (portas: 22, 80, 443, 3000)"
# ████████████████████████████████████████████████████
# FINAL
# ████████████████████████████████████████████████████
PUBLIC_IP=$(curl -s ifconfig.me 2>/dev/null || echo "<IP>")
echo ""
echo "╔══════════════════════════════════════════════════╗"
echo "║ Chatc2 - Instalacao Concluida! ║"
echo "╚══════════════════════════════════════════════════╝"
echo ""
echo " 📍 Acessar: http://$PUBLIC_IP"
echo " 📁 Diretorio: $CHATC2_DIR"
echo " 👤 Usuario: $CHATC2_USER"
echo " 🗄️ Firebird: senha: masterkey"
echo " 🎤 Whisper: modelo $WHISPER_MODEL"
echo ""
echo " 📋 Proximos passos:"
echo " 1. Copie seus bancos .FDB para $CHATC2_DIR/db/"
echo " 2. Edite $CHATC2_DIR/.env se necessario"
echo " 3. Crie usuario admin: node $CHATC2_DIR/scripts/gerar-token-usuario.js"
echo " 4. Configure o webhook na Evolution API:"
echo " POST http://$PUBLIC_IP/api/webhook/evolution"
echo " 5. Logs: pm2 logs chatc2"
echo " 6. Parar: pm2 stop chatc2"
echo " 7. Reset: pm2 restart chatc2"
echo ""