feat: edição de rotas de entrada, fix de 2 bugs reais no ESL, diagnóstico de NAT/áudio e softphone WebRTC (PHASE 65/66)
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
This commit is contained in:
227
docs/QA_SETUP.md
Normal file
227
docs/QA_SETUP.md
Normal file
@@ -0,0 +1,227 @@
|
||||
# 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 em `infrastructure/systemd/` assumem isso)
|
||||
- Docker + Docker Compose plugin (`docker compose version` funcionando)
|
||||
- Node.js >= 22 (`node -v`) e `pnpm` (`corepack enable` costuma bastar —
|
||||
o `packageManager` no `package.json` da 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 `tsc` do `apps/api` consumirem
|
||||
- Este repositório **precisa ficar em `/opt/b2bcall`** — as units systemd em
|
||||
`infrastructure/systemd/*.service` tê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:
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
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):
|
||||
```bash
|
||||
echo "B2BCALL_API_URL=http://localhost:3000" > apps/frontend/.env.local
|
||||
```
|
||||
|
||||
## 3. Subir os containers Docker
|
||||
|
||||
```bash
|
||||
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:
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
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):
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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:
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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>`):
|
||||
|
||||
1. Acesse `http://<ip-da-maquina>:3001/login`
|
||||
2. Entre com `admin@b2bcall.local` e a senha de `FIRST_LOGIN.txt`
|
||||
3. A tela "Trocar senha" aparece automaticamente (senha temporária,
|
||||
`mustChangePassword=true`) — troque por uma senha sua
|
||||
4. Depois de trocar, você cai em `/platform` (Platform Super Admin)
|
||||
5. 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
|
||||
|
||||
- **`EADDRINUSE` na porta 3000/3001**: confira que `API_PORT=3000` está no
|
||||
`.env` e que a unit do frontend tem `Environment=PORT=3001` — sem os
|
||||
dois fixos, os dois processos competem pela mesma porta.
|
||||
- **Timeout conectando no ESL do FreeSWITCH** (telas de
|
||||
`Platform > Infraestrutura`): confira `ESL_HOST=127.0.0.1` no `.env`
|
||||
(nunca `freeswitch` — esse nome só resolve dentro da rede Docker) e que
|
||||
a porta 8021 está publicada em loopback (`docker compose ps` deve
|
||||
mostrar `127.0.0.1:8021->8021/tcp` no FreeSWITCH).
|
||||
- **RLS bloqueando tudo silenciosamente** (contagens sempre zero, listas
|
||||
sempre vazias mesmo com dado no banco): confirme que
|
||||
`scripts/db-setup-app-role.sh` rodou depois da migration — sem a senha
|
||||
do role `b2bcall_app` setada, `APP_DATABASE_URL` não autentica e
|
||||
`apps/api` provavelmente nem sobe.
|
||||
- **Container do FreeSWITCH não builda**: quase sempre `FREESWITCH_PAT`
|
||||
errado/expirado no `.env` — `docker compose logs freeswitch` mostra o
|
||||
erro de autenticação do `apt` contra o repositório do SignalWire.
|
||||
Reference in New Issue
Block a user