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
228 lines
10 KiB
Markdown
228 lines
10 KiB
Markdown
# 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.
|