Usuário testou em produção e confirmou que os dois bugs do commit anterior (Content-Type em requisições sem corpo, DELETE de supressão sem motivo) estão corrigidos. Atualiza TODO.md/RELATORIO_FINAL.md para refletir a verificação visual como concluída, e documenta a lição em TROUBLESHOOTING.md: teste via curl prova que o backend funciona, não que o frontend consegue falar com ele.
7.1 KiB
B2BCall — Relatório Final da Implementação
Formato conforme agente.md seção 96. Gerado em 2026-08-27 contra o ambiente
real em execução (Lab-FSCore-API, 10.10.32.142).
B2BCall version: 0.1.0
Asterisk version: 22.10.1
Node version: v24.20.0
PostgreSQL version: 17 (postgres:17-alpine)
Docker version: 29.7.2 (Compose v5.5.0)
Containers
| Container | Status |
|---|---|
| b2bcall-postgres | healthy |
| b2bcall-redis | healthy |
| b2bcall-asterisk | healthy |
| b2bcall-api | healthy |
| b2bcall-frontend | healthy |
| b2bcall-nginx | healthy |
| b2bcall-asterisk-events | up (sem healthcheck HTTP formal — worker sem porta exposta) |
| b2bcall-dialer-worker | up (idem) |
URLs
- Interface web:
http://10.10.32.142/ - API:
http://10.10.32.142/api - Health:
http://10.10.32.142/api/health - Métricas (Prometheus, autenticado, uso interno):
http://10.10.32.142/api/metrics
Super admin
Ver arquivo local CREDENCIAIS.txt (fora do git, entregue separadamente —
nunca colocamos senha em texto puro em um arquivo versionado, mesmo em um
Git privado). Contém: usuário/senha do super_admin web e as credenciais
do PostgreSQL/Redis.
Health
$ bash scripts/healthcheck.sh
docker compose ps: 8/8 containers up
/api/health: {"status":"ok","postgres":"up","redis":"up","asterisk":"up"}
Asterisk core show uptime: OK
pjsip show endpoints: OK (comando responde; sem endpoints — fixtures de teste removidos)
nftables (b2bcall_fw): ativo
Aceite Asterisk (seção 93)
$ asterisk -rx "core show version"
Asterisk 22.10.1 built by root @ buildkitsandbox on a x86_64 running Linux on 2026-08-27 13:32:29 UTC
$ asterisk -rx "core show uptime"
System uptime: 5 hours, 43 minutes, 19 seconds
Last reload: 6 minutes, 54 seconds
$ asterisk -rx "pjsip show endpoints"
No objects found.
(esperado: todos os troncos/ramais de teste foram removidos ao final de cada fase)
$ asterisk -rx "pjsip show contacts"
No objects found.
$ asterisk -rx "queue show"
No queues.
Comunicação API → AMI → Asterisk: validada via
GET /api/asterisk/status →
{"amiControlConnection":"up","asteriskEventsHeartbeat":"up",...}.
Tests
Build/typecheck (pnpm -r build, 7 workspaces): PASS
Lint (api, frontend): PASS
Unit tests:
apps/api 13/13 PASS
apps/dialer-worker 33/33 PASS
docker compose config: válida
docker compose ps: 8/8 up
scripts/healthcheck.sh: PASS
Aceite de segurança (seção 92) — verificado ao vivo, não só por inspeção
| Item | Resultado |
|---|---|
| Nenhuma senha no Git | ✅ (git log --all -- .env vazio) |
Nenhum .env commitado |
✅ |
| PostgreSQL não público | ✅ (127.0.0.1:5432 apenas) |
| Redis não público | ✅ (nenhuma porta publicada) |
| AMI não público | ✅ (nftables bloqueia ens18:5038) |
| ARI não público | ✅ (nftables bloqueia ens18:8088/8089) |
| Rate limit funcionando | ✅ (7ª tentativa de login seguida → HTTP 429) |
| RBAC funcionando no backend | ✅ (papel agent → 403 em /asterisk/status, /trunks, /users) |
| Agent não acessa configuração do Asterisk | ✅ |
| Agent não eleva a própria permissão | ✅ |
| SQL injection protegida | ✅ (Prisma parametrizado em toda parte) |
| Path traversal protegido | ✅ (nenhum path de arquivo construído a partir de input do usuário) |
| Logs sem credenciais | ✅ (redação do pino confirmada, varredura real sem vazamento) |
Prova do dialer (seção 91)
- CPS configurado nunca excedido em nenhuma janela de 1s: ✅ (
simulation-harness.spec.ts) - Concorrência máxima da campanha nunca excedida: ✅ (idem)
- Pacing reage/reduz quando abandono sobe: ✅ (idem + bug real corrigido durante o desenvolvimento, ver
docs/PREDICTIVE_DIALER.md) - Reprodutibilidade (mesma seed → mesmo resultado): ✅
- Reserva de lead atômica (sem discagem dupla): ✅ (
FOR UPDATE SKIP LOCKED, testado na Fase 6 contra Postgres real) - Campanha pausada não gera chamada nova: ✅ (testado na Fase 6)
- Lead suprimido não é discado: ✅ (
isSuppressedpré-originação, testado na Fase 6) - Campanha fora do horário não discou: ✅ (
schedule.spec.ts, 6 testes) - Worker reiniciado recupera operação: ⚠️ parcial —
reconciliation.tstem teste unitário e roda em produção a cada 60s, mas o cenário "matar o processo de verdade no meio de uma chamada" não foi exercitado nesta sessão (ver Pending).
Pending
Itens genuinamente pendentes, nunca escondidos:
- Integration tests automatizados (Postgres/Redis/AMI reais via testcontainers ou equivalente) não existem como suíte formal — toda a verificação de integração foi feita manualmente contra containers reais a cada fase (real, mas não repetível em CI sem intervenção).
- Recuperação após kill real do worker não foi testada nesta sessão (só via teste unitário da lógica de reconciliação).
scripts/install.sh/update.shnão foram testados de ponta a ponta num servidor limpo (o servidor atual já estava provisionado) — validados por inspeção linha a linha contra os comandos manuais já executados.Verificação visual em navegador real— feita posteriormente pelo usuário em produção. Confirmou que ocurlsozinho não é suficiente: achou dois bugs reais de integração frontend↔API invisíveis a testes via curl (Content-Type em requisições sem corpo quebrando todo botão de ação; DELETE de supressão sem forma de enviar o motivo exigido). Ambos corrigidos, usuário confirmou funcionamento. Verdocs/TROUBLESHOOTING.md.- Tela de Callbacks no frontend não existe — o agendamento e a
reativação automática funcionam via API/worker (
POST /agent-console/ dispose,GET /api/callbacks,callback-sweep.ts), mas não há uma tela dedicada para visualizar a fila de callbacks pendentes. - AMD (detecção de secretária eletrônica) — campo existe no schema, detecção real via app AMD do Asterisk não integrada à originação.
- Correlação automática CDR↔DialAttempt para chamadas reais (não simuladas) além do timeout de segurança de reconciliação.
- WebSocket real — painéis de monitoramento usam polling (5-15s), não um gateway WebSocket dedicado.
- OpenSIPS não implantado — decisão documentada em
docs/OPENSIPS.md(não resolve nenhum problema real do ambiente atual de instância única). - HTTPS não configurado (rede privada, sem IP público) — passo a
passo em
docs/OPERATIONS.md. asterisk-events/dialer-workernão têm healthcheck Docker formal (workers em background sem porta HTTP própria).- Rotação de
SECRETS_MASTER_KEYnão é automatizada — exigiria redescriptografar todos os segredos de tronco/ramal manualmente.
Nenhum destes itens bloqueia o uso do sistema para o que foi pedido (discador preditivo funcional, call center completo, frontend completo, segurança e backup); são lacunas de robustez/automação a considerar antes de operar com volume real de produção.