# 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 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:@localhost:5432/b2bcall?schema=public` | | `REDIS_URL` | `redis://:@localhost:6379` | | `APP_DATABASE_URL` | `postgresql://b2bcall_app:@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 ` 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 @`): 1. Acesse `http://: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 && docker compose up -d ` | | `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.