Três achados reportados pelo usuário numa mensagem só: (1) Rotas de Entrada não tinha edição depois de criada — implementada no mesmo padrão de Filas; (2) telas de Platform > Infraestrutura sempre davam "Timeout no ESL" nesta VM — não era limitação permanente como o comentário antigo dizia, e sim ESL_HOST=freeswitch (nome DNS que só existe dentro da rede do Docker) mais um segundo bug independente (`show gateways as json` não é comando válido nesta versão do FreeSWITCH); (3) ramal externo registrava mas sem áudio — diagnosticado com contadores de pacote do iptables: a VM está atrás de um roteador sem port-forward pra faixa de RTP, achado de infraestrutura de rede, não bug de código. Também integra o softphone WebRTC (handphone.js/OpenSIPS, já em produção): código-fonte encontrado em git.falehandix.com.br/Handix/handphone-2.0, patch mínimo pra aceitar o endereço do proxy em runtime (era build-time), nova config global (Platform > Infraestrutura > Softphone WebRTC) e widget na topbar do tenant que pega usuário/domínio/senha do ramal vinculado ao agente logado. Adiciona docs/QA_SETUP.md — runbook completo pra subir o ambiente do zero numa máquina nova (Docker, migrations, seed, systemd), e completa o .env.example que estava faltando a maioria das variáveis reais. Testado ponta a ponta com Playwright: edição de rota (criar/editar/F5), as 3 telas de Infraestrutura com dado real, e um tenant/ramal/agente de teste criados na hora confirmando que o script do softphone recebe as credenciais certas. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
10 KiB
Subindo o ambiente do zero numa máquina nova (QA)
Runbook pra clonar este repositório numa máquina nova e deixar tudo rodando
— Postgres/Redis/FreeSWITCH em Docker, apps/api/apps/frontend como
services systemd, banco migrado e semeado. Escrito pra outra sessão do
Claude Code seguir sozinha, na ordem, verificando cada passo antes do
próximo. Nada aqui precisa de intervenção manual além do que está marcado
explicitamente como "precisa de um humano".
Este documento descreve como levantar um ambiente igual ao de
desenvolvimento/laboratório (ACESSO_TESTE.md), não produção: sem HTTPS,
sem domínio próprio, DIALER_SIMULATION=true (nenhuma chamada PSTN real
sai). Rodar em produção de verdade é fora de escopo deste guia.
0. Pré-requisitos da máquina
- Linux com
systemd(as units eminfrastructure/systemd/assumem isso) - Docker + Docker Compose plugin (
docker compose versionfuncionando) - Node.js >= 22 (
node -v) epnpm(corepack enablecostuma bastar — opackageManagernopackage.jsonda raiz fixa a versão exata) - Pelo menos ~2GB de RAM livres pro Postgres+Redis+FreeSWITCH juntos, mais o
que o Next.js dev server e o
tscdoapps/apiconsumirem - Este repositório precisa ficar em
/opt/b2bcall— as units systemd eminfrastructure/systemd/*.servicetêm esse caminho fixo (WorkingDirectory=,EnvironmentFile=). Cloná-lo em outro lugar exige editar essas duas linhas nas duas units antes de instalar.
Verifique antes de prosseguir:
docker compose version
node -v # >= 22
corepack enable && corepack prepare pnpm@$(node -e "console.log(require('/opt/b2bcall/package.json').packageManager.split('@')[1])") --activate
pnpm -v
1. Clonar e instalar dependências
cd /opt
git clone <url-do-repositorio> b2bcall # ou `git pull` se já existe
cd /opt/b2bcall
pnpm install
2. Criar o .env
.env nunca é commitado (segredos reais). Copie o template e preencha:
cp .env.example .env
Abra .env.example pra ver o comentário de cada variável — ele já explica
o que cada uma faz e como gerar as que são segredos aleatórios
(openssl rand -hex 32 / openssl rand -base64 24). Resumo do que precisa
de valor manual:
| Variável | Como preencher |
|---|---|
FREESWITCH_PAT |
Precisa de um humano — token de acesso ao repositório de pacotes do FreeSWITCH (freeswitch.signalwire.com), não é gerável localmente. Peça ao dono do projeto. |
POSTGRES_PASSWORD, REDIS_PASSWORD, POSTGRES_APP_PASSWORD |
openssl rand -base64 24 (gerar um valor diferente pra cada) |
JWT_SECRET, JWT_REFRESH_SECRET, ENCRYPTION_KEY |
openssl rand -hex 32 (gerar um valor diferente pra cada — ENCRYPTION_KEY cifra segredos reversíveis como senha SIP de ramal, nunca reaproveitar entre ambientes) |
ESL_PASSWORD |
openssl rand -base64 24 |
FS_CONFIG_PASSWORD |
openssl rand -base64 24 |
DATABASE_URL |
postgresql://b2bcall:<POSTGRES_PASSWORD>@localhost:5432/b2bcall?schema=public |
REDIS_URL |
redis://:<REDIS_PASSWORD>@localhost:6379 |
APP_DATABASE_URL |
postgresql://b2bcall_app:<POSTGRES_APP_PASSWORD>@localhost:5432/b2bcall?schema=public |
FS_CONFIG_USER |
Qualquer string curta sem espaço (ex.: fsconfig) |
Todas as outras variáveis do .env.example já têm um valor padrão
sensato pra este tipo de ambiente (deixe como está).
Crie também apps/frontend/.env.local (não existe ainda num clone novo,
também gitignored):
echo "B2BCALL_API_URL=http://localhost:3000" > apps/frontend/.env.local
3. Subir os containers Docker
cd /opt/b2bcall
docker compose up -d --build
O docker compose lê FREESWITCH_PAT automaticamente do .env na raiz
pro build da imagem do FreeSWITCH (BuildKit secret — não fica em nenhuma
camada da imagem final). O build do FreeSWITCH baixa pacotes .deb
externos, pode levar alguns minutos na primeira vez.
Verifique que todos os 7 containers subiram saudáveis:
docker compose ps
Espera-se b2bcall-postgres, b2bcall-redis, b2bcall-freeswitch,
b2bcall-fs-config, b2bcall-fs-events, b2bcall-predictive-dialer,
b2bcall-ai-worker todos Up (os 3 primeiros com healthcheck healthy).
Se algum não subir, docker compose logs <serviço> quase sempre mostra a
causa na hora (comum: .env com uma variável faltando/typo).
4. Migrar e semear o banco
cd /opt/b2bcall/packages/database
set -a && source /opt/b2bcall/.env && set +a
npx prisma migrate deploy
npx prisma generate
Isso cria todas as tabelas, incluindo a role restrita b2bcall_app (não
tem senha ainda — a migration não pode embutir um segredo). Defina a senha
dela agora:
cd /opt/b2bcall
bash scripts/db-setup-app-role.sh
Depois, rode o seed (idempotente — cria o catálogo de permissions/roles do sistema e o usuário Platform Super Admin):
pnpm --filter @b2bcall/auth run seed
A senha temporária do Platform Super Admin (admin@b2bcall.local) é
gravada em /opt/b2bcall/FIRST_LOGIN.txt (permissão 600, gitignored) —
leia esse arquivo pra saber a senha do primeiro login, ele não aparece
no terminal.
5. Instalar os services systemd
cd /opt/b2bcall
sudo cp infrastructure/systemd/b2bcall-api.service /etc/systemd/system/
sudo cp infrastructure/systemd/b2bcall-frontend.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now b2bcall-api.service
sudo systemctl enable --now b2bcall-frontend.service
Verifique:
systemctl status b2bcall-api.service b2bcall-frontend.service
journalctl -u b2bcall-api.service -n 50 # esperado: "Nest application successfully started" (ou similar)
apps/api recompila (tsc) toda vez que a unit sobe — leva uns 15-20s.
Detalhes/troubleshooting em infrastructure/systemd/README.md.
6. Verificação de ponta a ponta
curl -s http://localhost:3000/health/ready # apps/api + Postgres + Redis, tudo respondendo
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3001/login # apps/frontend respondendo (espera 200)
Depois, pelo navegador (ou um túnel SSH se a máquina não for acessível
direto: ssh -L 3001:localhost:3001 <usuario>@<ip-da-maquina>):
- Acesse
http://<ip-da-maquina>:3001/login - Entre com
admin@b2bcall.locale a senha deFIRST_LOGIN.txt - A tela "Trocar senha" aparece automaticamente (senha temporária,
mustChangePassword=true) — troque por uma senha sua - Depois de trocar, você cai em
/platform(Platform Super Admin) - Crie um tenant de teste em Clientes > Tenants pra ter algo pra testar (a senha do admin desse tenant também é mostrada uma única vez na tela — anote ou copie na hora)
7. Se for testar telefonia real (SIP/RTP de fora da rede Docker)
Só necessário se for registrar um softphone/telefone físico de verdade contra este ambiente — não é preciso pra testar só a aplicação web.
docker-compose.yml já publica 5060/udp, 5060/tcp e a faixa de RTP
16384-16584/udp pra rede (não só loopback). Se esta máquina estiver
atrás de um roteador/NAT (IP da interface de rede é privado, tipo
10.x.x.x/192.168.x.x), é preciso configurar port forward no
roteador pra essas mesmas portas apontando pro IP privado desta máquina
— sem isso, o registro SIP até funciona (a resposta volta pelo mesmo
"buraco" que o cliente abriu), mas o áudio não passa (RTP é um fluxo
novo, em portas diferentes, e chega bloqueado). Isto foi um achado real
diagnosticado numa sessão anterior — detalhes completos e como confirmar
com contadores de pacote do iptables em docs/NETWORK_ARCHITECTURE.md
("Achado real (PHASE 65)").
8. Softphone WebRTC (widget embutido, PHASE 66)
O app do tenant tem um softphone WebRTC embutido (apps/frontend/public/ handphone.js) que conecta via WebRTC num proxy OpenSIPS externo (já em
produção, fora deste repositório) — o FreeSWITCH desta instalação nunca
fala WebRTC diretamente, cada ramal continua um registro SIP puro.
Pra funcionar, um Platform Super Admin precisa configurar o endereço WSS
desse proxy em Platform > Infraestrutura > Softphone WebRTC
(PUT /platform/webrtc-proxy, guardado na tabela platform_settings —
não tem nada a configurar via .env ou variável de ambiente pra isso).
Sem essa configuração, o widget simplesmente não conecta (fica sem
aparecer/logar um aviso no console do navegador) — não é um erro de
deploy, só falta esse passo manual pós-subida.
Referência rápida — o que roda onde
| Componente | Como sobe | Restart depois de mudar código |
|---|---|---|
| Postgres, Redis, FreeSWITCH, fs-config, fs-events, predictive-dialer, ai-worker | Docker (restart: unless-stopped) |
docker compose build <serviço> && docker compose up -d <serviço> |
apps/api |
systemd (b2bcall-api.service), pnpm dev (modo dev, não build de produção — decisão deliberada, ver infrastructure/systemd/README.md) |
systemctl restart b2bcall-api.service |
apps/frontend |
systemd (b2bcall-frontend.service), pnpm dev |
Hot-reload sozinho (Next.js dev server); systemctl restart só depois de mudar dependências |
Problemas comuns
EADDRINUSEna porta 3000/3001: confira queAPI_PORT=3000está no.enve que a unit do frontend temEnvironment=PORT=3001— sem os dois fixos, os dois processos competem pela mesma porta.- Timeout conectando no ESL do FreeSWITCH (telas de
Platform > Infraestrutura): confiraESL_HOST=127.0.0.1no.env(nuncafreeswitch— esse nome só resolve dentro da rede Docker) e que a porta 8021 está publicada em loopback (docker compose psdeve mostrar127.0.0.1:8021->8021/tcpno FreeSWITCH). - RLS bloqueando tudo silenciosamente (contagens sempre zero, listas
sempre vazias mesmo com dado no banco): confirme que
scripts/db-setup-app-role.shrodou depois da migration — sem a senha do roleb2bcall_appsetada,APP_DATABASE_URLnão autentica eapps/apiprovavelmente nem sobe. - Container do FreeSWITCH não builda: quase sempre
FREESWITCH_PATerrado/expirado no.env—docker compose logs freeswitchmostra o erro de autenticação doaptcontra o repositório do SignalWire.