Commit Graph

5 Commits

Author SHA1 Message Date
0a8b830e2c Fase 7: CDR/métricas/relatórios, reconciliação, dashboard e compliance
- Reconciliação de tentativas órfãs após restart (reconciliation.ts),
  rodando a cada 60s.
- Vínculo real agente<->chamada (agent-call-binding.ts): claim atômico de
  agente disponível via FOR UPDATE SKIP LOCKED, DialAttempt.agentId
  populado no connect, ciclo AVAILABLE -> IN_CALL -> WRAP_UP -> AVAILABLE.
- Corrige abandonRate (EWMA) nunca atualizado pelo campaign-worker real —
  agora o fluxo QUEUED -> connect-or-abandon atualiza as estatísticas de
  fato usadas pelo predictive engine.
- Novo módulo de relatórios: /api/reports/calls (+export CSV), /metrics
  (TME/TMA/abandono), /agents/:id.
- Novo módulo de dashboard: /api/dashboard, /calls-by-hour,
  /campaigns/:id (Postgres + snapshot EWMA do Redis).
- Novo módulo de compliance: ComplianceSettings configurável +
  /api/compliance/settings e /indicators com contadores reais.
- Validado end-to-end contra containers reais (campanha de teste em modo
  simulação): agentId no connect, ciclo de estado do agente e abandonRate
  todos confirmados corrigidos com dados reais, não só no harness isolado.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoVkLx1KsvtT1C88dRS3QW
2026-08-27 16:09:22 -03:00
66cc2058fd feat: implement predictive dialing engine
- apps/dialer-worker: motor do discador preditivo completo
  - predictive-engine.ts: EWMA de answerProbability/avgTalkTimeSeconds/
    abandonRate, previsao de liberacao de agentes, calculo de quantas
    chamadas originar. Logica pura, 14 testes unitarios
  - cps-limiter.ts: token bucket via script Lua atomico no Redis (dois
    buckets independentes campanha+tronco, min() dos dois, seguro com
    multiplos workers)
  - lead-repository.ts: reserva atomica via FOR UPDATE SKIP LOCKED,
    recuperacao de reservas orfas apos queda de worker
  - campaign-lock.ts: lock distribuido por campanha (Redis SET NX PX +
    token de posse, renovacao/liberacao seguras via Lua)
  - schedule.ts: janela de horario da campanha (timezone real via
    Intl.DateTimeFormat, dias da semana), 6 testes unitarios
  - retry-rules.ts: motor de retentativa por causa de encerramento,
    configuravel por campanha, nunca infinito
  - simulation.ts + campaign-worker.ts (modo DIALER_SIMULATION): permite
    testar o motor inteiro sem tronco de operadora real
  - simulation-harness.ts: reproduz em tempo discreto e deterministico o
    cenario exato de aceite da secao 66 (20 agentes/10 CPS/30% atendimento/
    TMA 180s) — 6 testes validando CPS nunca excedido, concorrencia nunca
    excedida, pacing nao diverge
  - campaign-worker.ts: orquestra tudo contra Postgres/Redis/Asterisk reais

- docs/PREDICTIVE_DIALER.md: algoritmo documentado, incluindo dois bugs
  reais encontrados e corrigidos durante o teste do cenario de aceite
  (concorrencia nao contava chamadas em atendimento; pacing subia sem
  limite durante periodos ociosos, causando rajada maxima assim que um
  agente ficava livre) e limitacoes conhecidas (AMD e wrap-up automatico
  via eventos reais ainda pendentes, documentados sem esconder)

Testado ponta a ponta contra containers reais (Postgres/Redis/Asterisk):
campanha completa criada -> agente disponivel via API -> leads importados
-> campanha iniciada -> reserva atomica -> CPS respeitado -> simulacao de
NO_ANSWER (retry agendado) e ANSWERED (AGENT_CONNECTED, EWMA atualizada ao
vivo) -> parada sem derrubar chamadas em andamento.
2026-08-27 15:24:31 -03:00
a2898fa566 feat: add authentication and RBAC
- packages/database: schema Prisma (users/sessions/roles/permissions/
  user_roles/role_permissions/audit_logs/password_reset_tokens), migration
  inicial e seed (permissoes+perfis+bootstrap super_admin com senha
  aleatoria em FIRST_LOGIN.txt). Decisao de ORM (Prisma) documentada em
  docs/ARCHITECTURE.md
- packages/shared: catalogo de permissoes (fonte unica usada por seed e API)
- apps/api: NestJS 11 + Fastify
  - autenticacao: Argon2id, access JWT + refresh token opaco com rotacao,
    cookies HttpOnly/SameSite=Lax, change/forgot/reset password
  - rate limiting progressivo de login via Redis (bloqueio crescente por IP)
  - RBAC reforcado no backend (PermissionsGuard), protecao contra
    auto-elevacao de privilegio
  - auditoria (audit_logs) nas acoes sensiveis, com redacao de segredos
  - health checks reais (postgres+redis), swagger desabilitavel, logs
    estruturados JSON com request_id de correlacao, filtro global de
    excecoes sem vazar erro cru
- infrastructure/docker/api.Dockerfile: build multi-stage do monorepo pnpm
- docker-compose.yml: servico api na rede interna, sem porta publicada

Testado via containers reais: login, /me, refresh, change-password,
rate limit (7 tentativas -> 429), RBAC (nega/permite), bloqueio de
auto-elevacao (403), audit log populado, health checks, lint e testes
unitarios passando.
2026-08-27 12:23:02 -03:00
eee6d7aece feat: add asterisk pjsip integration
- infrastructure/docker/asterisk.Dockerfile: Asterisk 22.10.1 LTS compilado
  do fonte oficial (Debian trixie nao distribui mais o pacote asterisk),
  com PJSIP, AMI, ARI, res_odbc/res_config_odbc, cdr_adaptive_odbc,
  cel_odbc, app_queue
- infrastructure/asterisk/config: templates renderizados no entrypoint
  (secrets via envsubst, nunca versionados) + sorcery.conf/extconfig.conf
  para PJSIP realtime, dialplan de teste (extensao 600)
- infrastructure/postgres/init/002-asterisk-realtime.sql: tabelas
  ps_endpoints/ps_auths/ps_aors/ps_contacts/ps_endpoint_id_ips/
  ps_registrations/cdr/cel no schema 'asterisk'
- infrastructure/nftables: protege AMI/ARI/SIP contra acesso pela LAN
  (interface ens18), trazido da Fase 9 do TODO para ja, sem afetar SSH
  nem a rede interna do Docker
- docker-compose.yml: servico asterisk em network_mode: host (RTP);
  Postgres publicado em 127.0.0.1:5432 (nao 0.0.0.0) para o Asterisk
  alcanca-lo, ja que host network nao enxerga a rede Docker interna

Testado: AMI login, ARI com auth (401 sem auth), pjsip realtime
consultando Postgres sem erro, dialplan originate -> CDR e CEL gravados
corretamente, healthcheck do compose passando.
2026-08-27 10:47:28 -03:00
8d522e93a3 feat: bootstrap b2bcall architecture
- docs/ARCHITECTURE.md: decisoes de arquitetura (monorepo, rede Asterisk host
  mode, realtime PJSIP, camada de telefonia, seguranca desde o design)
- TODO.md: checklist vivo de implementacao por fases
- estrutura inicial do monorepo (apps/, packages/, infrastructure/, scripts/)
- docker-compose.yml: postgres 17 + redis 7 com healthchecks, sem portas
  publicadas no host, schema 'asterisk' dedicado no Postgres
- .env.example + scripts/generate-secrets.sh
2026-08-27 10:17:07 -03:00