commit 8d522e93a33799a526f9812c1aab7b39b9fd8925 Author: B2BCall Bootstrap Date: Thu Aug 27 10:17:07 2026 -0300 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 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6d27fbe --- /dev/null +++ b/.env.example @@ -0,0 +1,73 @@ +# ============================================================================ +# B2BCall — variáveis de ambiente (exemplo) +# Copie para `.env` e preencha os valores reais. NUNCA commitar o `.env`. +# ============================================================================ + +# --- Geral ------------------------------------------------------------- +NODE_ENV=production +APP_NAME=B2BCall +APP_URL=http://10.10.32.142 +TZ=America/Sao_Paulo + +# Modo de simulação do discador: quando true, NENHUMA chamada externa real é +# originada (usado em dev/testes). Nunca deixar true em produção. +DIALER_SIMULATION=true + +# Popular dados de demonstração no seed (nunca true em produção) +SEED_DEMO_DATA=false + +# --- PostgreSQL ---------------------------------------------------------- +POSTGRES_HOST=postgres +POSTGRES_PORT=5432 +POSTGRES_DB=b2bcall +POSTGRES_USER=b2bcall +POSTGRES_PASSWORD=CHANGE_ME_STRONG_PASSWORD +DATABASE_URL=postgresql://b2bcall:CHANGE_ME_STRONG_PASSWORD@postgres:5432/b2bcall?schema=public + +# Schema separado usado pelo Asterisk Realtime (res_odbc) +ASTERISK_DB_SCHEMA=asterisk + +# --- Redis ----------------------------------------------------------------- +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_PASSWORD=CHANGE_ME_STRONG_PASSWORD +REDIS_URL=redis://:CHANGE_ME_STRONG_PASSWORD@redis:6379 + +# --- Autenticação / JWT ------------------------------------------------ +JWT_ACCESS_SECRET=CHANGE_ME_RANDOM_64_HEX +JWT_REFRESH_SECRET=CHANGE_ME_RANDOM_64_HEX +JWT_ACCESS_TTL=15m +JWT_REFRESH_TTL=7d +COOKIE_DOMAIN= +COOKIE_SECURE=false + +# Master key para criptografia de segredos (credenciais de trunk) — AES-256-GCM +SECRETS_MASTER_KEY=CHANGE_ME_RANDOM_32_BYTE_BASE64 + +# --- Rate limiting ----------------------------------------------------- +RATE_LIMIT_LOGIN_MAX=5 +RATE_LIMIT_LOGIN_WINDOW_SECONDS=60 + +# --- Asterisk / AMI / ARI ------------------------------------------------ +ASTERISK_HOST=127.0.0.1 +AMI_PORT=5038 +AMI_USERNAME=b2bcall_ami +AMI_SECRET=CHANGE_ME_STRONG_PASSWORD +ARI_PORT=8088 +ARI_USERNAME=b2bcall_ari +ARI_SECRET=CHANGE_ME_STRONG_PASSWORD + +# --- SMTP (recuperação de senha, notificações) -------------------------- +SMTP_HOST= +SMTP_PORT=587 +SMTP_USER= +SMTP_PASSWORD= +SMTP_FROM=no-reply@b2bcall.local + +# --- Observabilidade ----------------------------------------------------- +LOG_LEVEL=info +METRICS_ENABLED=true + +# --- Portas expostas pelo Nginx (host) ----------------------------------- +HTTP_PORT=80 +HTTPS_PORT=443 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e67aa61 --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +### Env / secrets ### +.env +.env.* +!.env.example +*.pem +*.key +!infrastructure/**/*.example.key +FIRST_LOGIN.txt + +### Node ### +node_modules/ +dist/ +build/ +.next/ +out/ +coverage/ +*.tsbuildinfo +npm-debug.log* +pnpm-debug.log* +yarn-error.log* + +### Docker / infra runtime data ### +infrastructure/postgres/data/ +infrastructure/asterisk/data/ +infrastructure/asterisk/sounds/custom/ +volumes/ + +### Logs ### +*.log +logs/ + +### OS / editor ### +.DS_Store +.vscode/ +.idea/ +*.swp + +### Claude Code session data ### +.claude/ + +### Backups ### +backups/*.sql +backups/*.sql.gz +backups/*.tar.gz diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..1429d96 --- /dev/null +++ b/TODO.md @@ -0,0 +1,126 @@ +# B2BCall — TODO Operacional + +Checklist vivo. Marcar `[x]` somente após testar. Não deixar item concluído sem +validação. Ver critérios de aceite completos em `docs/ARCHITECTURE.md` e no prompt +mestre original (`agente.md`, seções 90-93). + +## Fase 0 — Infraestrutura base +- [x] Levantamento do servidor (SO, recursos, rede) +- [x] docs/ARCHITECTURE.md +- [x] TODO.md +- [x] Git init + .gitignore + primeiro commit +- [x] Instalar Docker Engine + Compose plugin (29.7.2 / Compose v5.5.0) +- [x] Estrutura de diretórios do monorepo +- [x] .env.example (+ scripts/generate-secrets.sh, .env real gerado localmente) +- [ ] nftables (firewall base, default-deny administrativo, sem bloquear SSH) — + adiado para a Fase 9, quando houver serviços/portas reais a proteger + +## Fase 1 — Dados e cache +- [x] docker-compose: serviço postgres 17-alpine (schemas application/asterisk) — testado, healthy +- [x] docker-compose: serviço redis 7-alpine (auth, AOF, maxmemory 100mb) — testado, healthy +- [x] Health checks postgres/redis +- [ ] packages/database: schema inicial + migrations tool + +## Fase 2 — Asterisk +- [ ] Dockerfile Asterisk 22 LTS (host network) +- [ ] PJSIP (transports, sem chan_sip) +- [ ] AMI habilitado (bind local/rede interna apenas) +- [ ] ARI habilitado (idem) +- [ ] Realtime ODBC -> Postgres (schema asterisk) +- [ ] CDR, CEL, queue_log habilitados +- [ ] Dialplan inicial de teste + +## Fase 3 — Backend base +- [ ] apps/api (NestJS + Fastify) bootstrap +- [ ] Autenticação (Argon2id, access+refresh, cookies HttpOnly) +- [ ] Rate limiting (login e endpoints sensíveis) +- [ ] RBAC (users/roles/permissions/user_roles/role_permissions) + tela de perfis +- [ ] Auditoria (audit_logs) + interceptor genérico +- [ ] Health checks /api/health(/live|/ready) +- [ ] Swagger/OpenAPI (desabilitável em produção) + +## Fase 4 — Telefonia (camada de aplicação) +- [ ] packages/telephony: TelephonyProvider + AsteriskTelephonyProvider +- [ ] apps/asterisk-events (AMI listener, normalização, persistência, pub/sub) +- [ ] CRUD Troncos (com CPS máximo, ACL, teste de status) +- [ ] CRUD Ramais (senha SIP gerada, reset, status tempo real) +- [ ] Painel visual de Ramais (WebSocket, prioridade de cores) +- [ ] Dialplan estruturado (versionado, modo Advanced, validação+rollback) +- [ ] Administração do Asterisk (abas: geral, pjsip, rtp, filas, cdr, cel, logs, + ami, ari, modules, diagnóstico com allowlist de comandos) + +## Fase 5 — Call Center +- [ ] Agentes (agents, agent_sessions) separados de users +- [ ] Máquina de estados do agente (OFFLINE..PAUSED) +- [ ] Motivos de pausa (CRUD) +- [ ] Filas (CRUD + estratégias documentadas) +- [ ] Tela do agente (login dinâmico, pausa/retomada, disposição) +- [ ] Disposições de chamada (CRUD + ações: callback, DNC) +- [ ] Callback (agendamento + scheduler) +- [ ] Monitoramento de Filas (tempo real) + +## Fase 6 — Campanhas e discador preditivo +- [ ] CRUD Campanhas (todos os campos da seção 24) +- [ ] Import CSV streaming (preview, mapeamento, validação, duplicados, rejeitados) +- [ ] Normalização de telefone (serviço dedicado, BR inicialmente) +- [ ] Lista de supressão (DNC) + checagem obrigatória pré-originação +- [ ] CPS limiter (token bucket, coordenado via Redis, multi-worker) +- [ ] Reserva concorrente de leads (FOR UPDATE SKIP LOCKED + timeout) +- [ ] Idempotência de originação (attempt_id/call_id/uniqueid/linkedid, state machine) +- [ ] PredictiveDialerEngine (EWMA, pacing, previsão de liberação de agentes) +- [ ] Controle de abandono (pacing cai / suspende originação) +- [ ] AMD opcional por campanha +- [ ] Wrap-up time +- [ ] Retry engine (regras por causa, limite de tentativas) +- [ ] Horário de campanha (timezone, dias/horários, WAITING_SCHEDULE) +- [ ] Lock distribuído por campanha (Redis) +- [ ] docs/PREDICTIVE_DIALER.md + +## Fase 7 — CDR, métricas e relatórios +- [ ] Modelo consolidado de chamadas (CDR+CEL+AMI+queue_log) +- [ ] Reconciliação de estados órfãos após restart +- [ ] TME / TMA (definições documentadas, cálculo correto) +- [ ] Relatório de Chamadas (filtros, paginação server-side, export CSV) +- [ ] Relatório de Agentes (tempos, pausas detalhadas) +- [ ] Dashboard geral (cards + gráficos reais) +- [ ] Dashboard do discador (por campanha, tempo real) +- [ ] Compliance de Chamadas (parâmetros configuráveis, alertas, contadores) + +## Fase 8 — Frontend completo +- [ ] Bootstrap Next.js + Tailwind + shadcn/ui + TanStack Query + WS client +- [ ] Logo processada (b2bcall.png) + tema light/dark +- [ ] Menu completo (seção 52) +- [ ] Todas as telas do checklist de aceite (seção 90) + +## Fase 9 — Segurança e produção +- [ ] Criptografia de segredos de trunk (AES-256-GCM) +- [ ] HTTP security headers, CORS, CSRF, Helmet +- [ ] nftables final revisado +- [ ] Logs estruturados JSON (sem segredos) +- [ ] Correlation IDs (request_id/attempt_id/call_id) +- [ ] Métricas Prometheus (/metrics) +- [ ] Bootstrap super_admin (senha aleatória, FIRST_LOGIN.txt, forçar troca) +- [ ] Seed (permissões, perfis, pausas, disposições) sem dados fake em produção +- [ ] Backup/restore (postgres, asterisk config, .env seguro) +- [ ] scripts/install.sh, update.sh, backup.sh, restore.sh, healthcheck.sh +- [ ] Nginx reverse proxy (80/443, WS, HTTPS documentado) + +## Fase 10 — Testes e aceite +- [ ] Unit tests (predictive engine, CPS limiter, permissions, phone norm, retry, + state machines, TME/TMA, scheduling) +- [ ] Integration tests (postgres, redis, repositories, API, AMI mock) +- [ ] E2E (login → ... → RBAC, conforme seção 64) +- [ ] Modo simulação (DIALER_SIMULATION=true) + testes do predictive engine +- [ ] Prova de CPS respeitado / concorrência máxima / sem discagem dupla / + pausa efetiva / recuperação após restart / pacing reage a abandono / + supressão respeitada / horário respeitado +- [ ] Quality gate (lint, typecheck, unit, integration, e2e, compose config, + compose ps, health checks) — tudo verde +- [ ] Aceite de segurança (seção 92) +- [ ] Aceite Asterisk (seção 93, comandos documentados) +- [ ] README final completo +- [ ] Relatório final da implementação + +--- +**Nota de ambiente:** VM atual com 1.9 GiB RAM / 2 vCPU — adequada para dev e +simulação, não para carga real de produção. Ver docs/ARCHITECTURE.md seção 1. diff --git a/agente.md b/agente.md new file mode 100644 index 0000000..31821c3 --- /dev/null +++ b/agente.md @@ -0,0 +1,3534 @@ + +# PROMPT MESTRE — B2BCall + +Você é o arquiteto principal, engenheiro de software sênior, DevOps, DBA PostgreSQL e especialista em telefonia VoIP/Call Center responsável por projetar e implementar **do zero** uma plataforma profissional de discagem preditiva chamada: + +# B2BCall + +Você está trabalhando diretamente em um servidor: + +* Debian 13 +* Instalação limpa +* Servidor dedicado à aplicação +* Diretório principal: `/opt/b2bcall` +* Logo existente em: `/opt/b2bcall/b2bcall.png` +* Banco de dados: PostgreSQL +* Telefonia: Asterisk +* SIP: PJSIP +* Frontend e backend preferencialmente em TypeScript +* Toda a aplicação deve funcionar em containers Docker +* O Asterisk não possuirá IP público +* O Asterisk ficará atrás de um OpenSIPS +* Os troncos SIP serão acessados através de IPs privados +* Nenhuma porta SIP, AMI, ARI, PostgreSQL ou Redis deve ficar desnecessariamente exposta à Internet + +A aplicação deve ser moderna, segura, modular, escalável e adequada para ambiente de produção. + +--- + +# 1. REGRA PRINCIPAL DE EXECUÇÃO + +NÃO produza apenas documentação. + +NÃO faça apenas mockups. + +NÃO faça somente o frontend. + +NÃO pare depois de criar a estrutura inicial. + +NÃO deixe funções principais marcadas como TODO. + +Você deve efetivamente: + +1. analisar o servidor; +2. preparar o ambiente; +3. instalar Docker e dependências necessárias; +4. criar a estrutura do projeto; +5. criar os containers; +6. configurar PostgreSQL; +7. configurar Redis se necessário; +8. instalar/configurar Asterisk; +9. construir backend; +10. construir frontend; +11. construir o motor do discador; +12. integrar o backend ao Asterisk; +13. implementar autenticação; +14. implementar RBAC/permissões; +15. implementar todas as telas solicitadas; +16. implementar métricas; +17. implementar relatórios; +18. implementar monitoramento em tempo real; +19. implementar auditoria; +20. implementar segurança; +21. criar migrations; +22. criar testes; +23. executar os testes; +24. corrigir os problemas encontrados; +25. subir o ambiente; +26. validar os health checks; +27. documentar; +28. continuar até que o sistema esteja operacional. + +Trabalhe de maneira AUTÔNOMA. + +Quando houver mais de uma abordagem tecnicamente válida, escolha a solução que considerar mais adequada para produção e documente a decisão. + +Não interrompa o desenvolvimento a cada decisão pequena para perguntar ao usuário. + +Somente pare por algo realmente impossível de resolver sem informação externa, como uma senha de uma operadora que não foi fornecida. + +--- + +# 2. OBJETIVO DO PRODUTO + +O B2BCall será uma plataforma profissional de Call Center e discagem preditiva. + +Deve possuir: + +* Asterisk integrado; +* ramais; +* agentes; +* filas; +* pausas; +* troncos; +* dialplans; +* campanhas; +* leads; +* discador preditivo; +* controle CPS; +* monitoramento em tempo real; +* dashboards; +* TME; +* TMA; +* relatórios; +* RBAC; +* administração completa; +* configuração do Asterisk; +* auditoria; +* segurança; +* observabilidade. + +Não quero um simples sistema de “click to call”. + +Quero um verdadeiro: + +**Predictive Dialer / Automatic Call Distribution System.** + +--- + +# 3. ARQUITETURA TECNOLÓGICA + +Use preferencialmente um monorepo TypeScript. + +Uma arquitetura recomendada é: + +```text +/opt/b2bcall +├── apps +│ ├── frontend +│ ├── api +│ ├── dialer-worker +│ ├── asterisk-events +│ └── scheduler +├── packages +│ ├── database +│ ├── shared +│ ├── types +│ ├── ui +│ └── telephony +├── infrastructure +│ ├── asterisk +│ ├── nginx +│ ├── postgres +│ └── docker +├── scripts +├── docs +├── docker-compose.yml +├── .env.example +├── .gitignore +└── README.md +``` + +Pode adaptar a estrutura se houver justificativa técnica. + +## Stack sugerida + +### Frontend + +* Next.js +* React +* TypeScript +* Tailwind CSS +* shadcn/ui ou componentes equivalentes +* TanStack Query +* WebSocket para dados em tempo real + +### Backend + +Preferencialmente: + +* Node.js +* TypeScript +* NestJS + +Pode utilizar Fastify como HTTP adapter. + +### Banco + +* PostgreSQL 17 ou versão estável compatível +* migrations obrigatórias +* índices corretamente planejados +* transactions +* constraints +* foreign keys +* JSONB apenas quando fizer sentido + +### Cache/mensageria + +Redis pode ser utilizado para: + +* filas internas; +* locks; +* presença; +* sessões em tempo real; +* workers; +* campanhas; +* rate limit; +* eventos. + +Pode utilizar BullMQ. + +### Telefonia + +Use: + +**Asterisk 22 LTS** + +com: + +* PJSIP; +* AMI; +* ARI quando realmente necessário; +* CDR; +* CEL; +* queue_log; +* app_queue. + +Não utilize `chan_sip`. + +--- + +# 4. DOCKER + +Toda a solução deve ser containerizada. + +Criar no mínimo serviços equivalentes a: + +```text +b2bcall-frontend +b2bcall-api +b2bcall-worker +b2bcall-asterisk-events +b2bcall-scheduler +b2bcall-asterisk +b2bcall-postgres +b2bcall-redis +b2bcall-nginx +``` + +Avalie cuidadosamente a rede do Asterisk. + +Como RTP possui uma quantidade significativa de portas UDP, considere utilizar `network_mode: host` para o container do Asterisk caso isso simplifique e torne a operação mais confiável. + +Se utilizar host network, documente detalhadamente os impactos. + +Os outros containers devem permanecer em redes Docker privadas sempre que possível. + +PostgreSQL e Redis não devem ficar expostos publicamente. + +Adicionar: + +* restart policies; +* health checks; +* volumes persistentes; +* logging; +* dependency checks; +* resource considerations. + +--- + +# 5. TOPOLOGIA SIP + +A arquitetura é: + +```text + INTERNET + | + [ OpenSIPS ] + | + rede SIP privada + | + [ Asterisk ] + | + [ B2BCall ] +``` + +E para operadoras: + +```text +Asterisk + | +rede privada + | +Troncos SIP +``` + +O Asterisk NÃO possui IP público. + +Nunca assuma que ele precisa registrar troncos pela Internet. + +Permitir troncos: + +* IP Authentication; +* Username/password quando necessário; +* PJSIP registration quando necessário. + +Permitir configuração de: + +* IP; +* porta; +* transporte; +* codecs; +* DTMF; +* From User; +* From Domain; +* Contact User; +* Outbound Proxy; +* identificação por IP; +* autenticação; +* caller ID; +* limites; +* context; +* qualify; +* timers. + +Criar ACLs e configurações de segurança. + +AMI e ARI jamais deverão estar publicamente acessíveis. + +--- + +# 6. ASTERISK REALTIME + +Avalie utilizar Asterisk Realtime + ODBC/PostgreSQL para objetos PJSIP. + +Exemplos: + +* ps_endpoints +* ps_auths +* ps_aors +* ps_contacts +* ps_endpoint_id_ips +* ps_registrations + +O banco de aplicação deve continuar possuindo seu próprio modelo de domínio. + +Não misture indiscriminadamente tabelas da aplicação com tabelas internas do Asterisk. + +Uma boa estratégia é utilizar schemas separados: + +```text +public/application +asterisk +``` + +ou configuração equivalente. + +--- + +# 7. INTEGRAÇÃO ASTERISK + +O backend deve possuir uma camada: + +```text +TelephonyProvider +``` + +e implementação: + +```text +AsteriskTelephonyProvider +``` + +Não espalhe comandos AMI dentro de controllers da aplicação. + +Criar serviço central responsável por: + +* Originate; +* Hangup; +* QueuePause; +* QueueAdd; +* QueueRemove; +* ExtensionState; +* DeviceState; +* QueueStatus; +* PJSIPShowEndpoints; +* PJSIPShowContacts; +* reload; +* eventos; +* canais; +* bridges; +* chamadas. + +Utilize AMI principalmente para eventos administrativos e controle. + +Utilize ARI quando o controle de canais/bridges exigir ARI. + +--- + +# 8. EVENTOS EM TEMPO REAL + +Criar serviço dedicado que permaneça conectado ao AMI. + +Ele deve consumir eventos como: + +* Newchannel; +* DialBegin; +* DialEnd; +* BridgeEnter; +* BridgeLeave; +* Hangup; +* Newstate; +* DeviceStateChange; +* QueueMemberStatus; +* QueueMemberPause; +* AgentConnect; +* AgentComplete; +* QueueCallerJoin; +* QueueCallerLeave; +* QueueCallerAbandon; +* ContactStatus; +* PeerStatus/PJSIP equivalentes. + +Normalizar esses eventos. + +Persistir eventos importantes. + +Transmitir dados do estado atual para o frontend utilizando WebSocket. + +Nunca fazer o frontend executar polling a cada segundo no Asterisk. + +--- + +# 9. AUTENTICAÇÃO + +Criar autenticação profissional. + +Login: + +* e-mail; +* senha. + +Implementar: + +* Argon2id para hash; +* access token; +* refresh token; +* cookies HttpOnly quando aplicável; +* Secure; +* SameSite; +* rotação de refresh token; +* logout; +* revogação de sessão; +* recuperação de senha; +* token de recuperação com validade; +* auditoria; +* proteção contra enumeração de usuários. + +Nunca armazenar senha em texto puro. + +Nunca armazenar JWT em localStorage se puder ser evitado. + +--- + +# 10. RATE LIMIT + +É obrigatório implementar proteção contra força bruta. + +Exemplo: + +```text +5 tentativas de login / minuto / IP +``` + +com bloqueio progressivo. + +Implementar também limites por: + +* IP; +* usuário; +* endpoint sensível. + +Registrar tentativas excessivas no audit log. + +Redis poderá ser utilizado para rate limiting distribuído. + +--- + +# 11. RBAC — PERMISSÕES + +Criar RBAC completo. + +Não faça simplesmente: + +```text +if user.role == admin +``` + +Criar: + +```text +users +roles +permissions +user_roles +role_permissions +``` + +Perfis iniciais: + +```text +super_admin +admin +supervisor +agent +``` + +Porém os níveis NÃO devem ser fixos. + +O `super_admin` precisa possuir uma tela de: + +**Perfis e Permissões** + +onde possa definir exatamente o que cada perfil pode acessar. + +Exemplos: + +```text +dashboard.view + +trunks.view +trunks.create +trunks.update +trunks.delete + +extensions.view +extensions.create +extensions.update +extensions.delete + +dialplans.view +dialplans.create +dialplans.update +dialplans.delete + +queues.view +queues.create +queues.update +queues.delete + +agents.view +agents.create +agents.update +agents.delete + +campaigns.view +campaigns.create +campaigns.start +campaigns.pause +campaigns.stop +campaigns.update +campaigns.delete + +reports.view +reports.export + +monitoring.view + +asterisk.view +asterisk.configure +asterisk.reload + +users.view +users.create +users.update + +roles.manage + +audit.view + +settings.manage +``` + +O frontend deve esconder funcionalidades sem permissão. + +MAS: + +**a segurança sempre deve ser validada novamente pelo backend.** + +Nunca confie apenas no frontend. + +--- + +# 12. AUDITORIA + +Criar `audit_logs`. + +Registrar no mínimo: + +* login; +* logout; +* login inválido; +* criação; +* edição; +* exclusão; +* início de campanha; +* pausa de campanha; +* encerramento de campanha; +* alteração de trunk; +* alteração de ramal; +* alteração de fila; +* alteração de agente; +* alteração do Asterisk; +* reload; +* alteração de permissões. + +Guardar: + +```text +user_id +action +entity_type +entity_id +before +after +ip_address +user_agent +created_at +``` + +Para campos sensíveis, nunca salvar segredos abertos no `before/after`. + +--- + +# 13. TELA DE TRONCOS + +Criar menu: + +**Telefonia → Troncos** + +Permitir: + +* adicionar; +* editar; +* ativar; +* desativar; +* duplicar; +* excluir; +* testar. + +Campos: + +* Nome +* Tecnologia: PJSIP +* Tipo: + + * IP + * autenticação + * registration +* Host +* Porta +* Transporte +* Username +* Password +* From User +* From Domain +* Contact User +* Outbound Proxy +* Context +* Caller ID +* Codecs +* DTMF mode +* Qualify +* Max Channels +* CPS máximo +* ACL +* IPs permitidos +* Enabled + +Mostrar status: + +```text +ONLINE +OFFLINE +UNREACHABLE +UNKNOWN +``` + +Nunca mostrar a senha completa depois de salva. + +--- + +# 14. TELA DE RAMAIS + +Criar: + +**Telefonia → Ramais** + +Campos: + +* número; +* nome; +* senha SIP; +* caller ID; +* contexto; +* codecs; +* transporte; +* max contacts; +* qualify; +* enabled. + +Gerar senha SIP segura automaticamente. + +Permitir resetar a senha. + +Mostrar status em tempo real. + +--- + +# 15. PAINEL VISUAL DE RAMAIS + +Criar uma tela específica: + +**Monitoramento → Ramais** + +Cada ramal deverá aparecer em um card/quadrado. + +Exemplo: + +```text +┌──────────────────┐ +│ RAMAL 1001 │ +│ João Silva │ +│ DISPONÍVEL │ +│ 00:04:32 │ +└──────────────────┘ +``` + +Utilizar EXATAMENTE esta convenção principal: + +```text +CINZA = ramal offline +VERDE = ramal disponível +LARANJA = ramal ocupado +AZUL = agente logado +VERMELHO = agente em pausa +``` + +Como alguns estados podem coexistir, implementar a seguinte prioridade visual: + +```text +PAUSA + ↓ +EM CHAMADA + ↓ +AGENTE LOGADO + ↓ +REGISTRADO/DISPONÍVEL + ↓ +OFFLINE +``` + +Ou seja: + +```text +pausa -> vermelho +ocupado -> laranja +agente -> azul +disponível -> verde +offline -> cinza +``` + +Mostrar opcionalmente: + +* agente; +* ramal; +* fila; +* duração do estado; +* duração da chamada; +* campanha atual. + +Atualização via WebSocket. + +--- + +# 16. AGENTES + +Separar claramente: + +**usuário da aplicação** + +de: + +**agente de Call Center** + +Uma pessoa pode possuir usuário e agente associados. + +Criar: + +```text +agents +agent_sessions +``` + +Campos do agente: + +* código; +* nome; +* usuário associado; +* ativo; +* filas; +* campanhas permitidas; +* ramal atual. + +Permitir login dinâmico de agente. + +--- + +# 17. TELA DO AGENTE + +Após login, o agente deve possuir uma interface simplificada. + +Mostrar: + +```text +Agente +Ramal +Fila +Campanha +Estado +Tempo no estado +Chamada atual +Telefone +Nome do lead +``` + +Botões: + +```text +DISPONÍVEL +PAUSA +FINALIZAR PAUSA +``` + +Quando clicar em PAUSA, abrir os motivos cadastrados. + +Exemplo: + +```text +Banheiro +Almoço +Lanche +Reunião +Treinamento +Suporte +Particular +``` + +Registrar: + +* início; +* fim; +* duração; +* motivo; +* usuário; +* agente; +* ramal. + +O agente deve conseguir logar e deslogar das filas conforme suas permissões. + +--- + +# 18. CADASTRO DE PAUSAS + +Criar: + +**Call Center → Motivos de Pausa** + +Campos: + +```text +Nome +Código +Descrição +Tempo máximo opcional +Remunerada SIM/NÃO +Ativa SIM/NÃO +``` + +Permitir relatórios de pausas posteriormente. + +--- + +# 19. FILAS + +Criar: + +**Call Center → Filas** + +Configurar: + +* Nome +* Número +* Strategy +* Timeout +* Retry +* Wrap-up time +* Max length +* Music on Hold +* Announce +* Service Level +* Auto Fill +* Ring in use +* Weight +* Members + +Strategies: + +```text +ringall +leastrecent +fewestcalls +random +rrmemory +rrordered +linear +wrandom +``` + +Mostrar explicação amigável de cada strategy na interface. + +--- + +# 20. MONITORAMENTO DE FILAS + +Criar: + +**Monitoramento → Filas** + +Cada fila deve mostrar em tempo real: + +* chamadas aguardando; +* agentes logados; +* agentes disponíveis; +* agentes ocupados; +* agentes pausados; +* agentes offline; +* maior tempo de espera atual; +* TME; +* TMA; +* abandonos; +* chamadas atendidas; +* service level; +* taxa de abandono. + +Permitir expandir uma fila e visualizar agentes individualmente. + +--- + +# 21. DIALPLAN + +Criar: + +**Telefonia → Dialplan** + +Não criar somente um textarea. + +Criar editor estruturado permitindo: + +```text +Context +Extension +Priority +Application +Arguments +Enabled +Order +``` + +Por exemplo: + +```text +Context: outbound +Extension: _X. +Application: Dial +Arguments: PJSIP/${EXTEN}@trunk01 +``` + +Também disponibilizar um modo: + +**Advanced** + +somente para `super_admin` ou permissão equivalente. + +Antes de aplicar um dialplan: + +1. gerar nova configuração; +2. validar; +3. manter backup; +4. tentar reload seguro; +5. verificar resultado; +6. caso inválido, não ativar; +7. permitir rollback. + +Versionar alterações. + +Criar: + +```text +dialplan_versions +``` + +--- + +# 22. ADMINISTRAÇÃO DO ASTERISK + +Criar menu: + +**Sistema → Asterisk** + +Quero uma interface de administração suficientemente completa para gerenciar os parâmetros necessários ao B2BCall. + +Separar por abas. + +## Geral + +* system name; +* language; +* default context; +* timezone; +* sounds; +* MOH. + +## PJSIP + +* transports; +* UDP/TCP/TLS; +* bind; +* local_net; +* external signaling; +* external media; +* timers; +* codecs; +* endpoint defaults. + +## RTP + +* RTP start; +* RTP end; +* ICE se necessário; +* symmetric RTP; +* configurações relevantes. + +## Filas + +Parâmetros globais. + +## CDR + +Configuração. + +## CEL + +Configuração. + +## Logs + +Configuração e nível de log. + +## AMI + +Exibir STATUS da integração. + +Não mostrar senha AMI aberta. + +## ARI + +Exibir STATUS da integração. + +## Modules + +Listar módulos carregados. + +## Diagnóstico + +Permitir executar comandos seguros e previamente permitidos como: + +```text +core show uptime +core show channels +pjsip show endpoints +pjsip show contacts +queue show +module show +``` + +NÃO implemente uma shell Linux arbitrária pela interface web. + +Isso seria uma vulnerabilidade crítica. + +--- + +# 23. VERSIONAMENTO DAS CONFIGURAÇÕES DO ASTERISK + +Toda alteração gerada pela aplicação deve possuir versão. + +Criar algo como: + +```text +asterisk_config_versions +``` + +Guardar: + +* usuário; +* data; +* tipo; +* configuração anterior; +* nova configuração; +* status; +* resultado do reload. + +Disponibilizar: + +**Rollback** + +para versões anteriores. + +--- + +# 24. CAMPANHAS + +Criar: + +**Discador → Campanhas** + +Campos mínimos: + +```text +Nome +Descrição +Fila +Tronco +Caller ID +Dialplan/context +Status +Data inicial +Data final +Dias da semana +Horário inicial +Horário final +Timezone +CPS +Máximo de chamadas simultâneas +Pacing inicial +Pacing mínimo +Pacing máximo +Taxa alvo de abandono +Tempo máximo aguardando agente +Tempo de ring +Número máximo de tentativas +Intervalo entre tentativas +Retry BUSY +Retry NO ANSWER +Retry CONGESTION +AMD ligado/desligado +``` + +Status: + +```text +DRAFT +READY +RUNNING +PAUSED +DRAINING +STOPPED +COMPLETED +``` + +Botões: + +```text +INICIAR +PAUSAR +RETOMAR +PARAR +DRENAR +``` + +`DRAINING` significa: + +* não originar novas chamadas; +* deixar as existentes terminarem. + +--- + +# 25. CPS — CALLS PER SECOND + +CPS é requisito obrigatório. + +Cada campanha deve possuir: + +```text +campaign.max_cps +``` + +Cada trunk também: + +```text +trunk.max_cps +``` + +O limite real deve ser: + +```text +min(campaign.max_cps, trunk.available_cps) +``` + +Implementar token bucket ou algoritmo equivalente. + +Não pode simplesmente executar um loop com `sleep`. + +Tem que funcionar de maneira correta mesmo com múltiplos workers. + +Utilize Redis para coordenação distribuída se necessário. + +--- + +# 26. LEADS + +Cada campanha possuirá leads. + +Criar tabela `leads`. + +Campos mínimos: + +```text +id +campaign_id +name +phone +normalized_phone +status +attempt_count +last_attempt_at +next_attempt_at +last_result +created_at +updated_at +``` + +Permitir dados adicionais: + +```text +custom_fields JSONB +``` + +Status possíveis: + +```text +NEW +READY +RESERVED +DIALING +RINGING +ANSWERED +CONNECTED_AGENT +BUSY +NO_ANSWER +FAILED +INVALID +VOICEMAIL +CALLBACK +COMPLETED +DO_NOT_CALL +MAX_ATTEMPTS +``` + +--- + +# 27. IMPORTAÇÃO CSV + +Criar na campanha: + +**Importar Leads** + +Formato mínimo: + +```csv +nome,telefone +Joao Silva,48999999999 +Maria Souza,11999999999 +``` + +Interface deve permitir: + +1. upload; +2. detectar delimitador; +3. preview; +4. mapear colunas; +5. validar; +6. normalizar telefones; +7. detectar duplicados; +8. importar. + +Mostrar: + +```text +Total: 10.000 +Válidos: 9.850 +Inválidos: 100 +Duplicados: 50 +``` + +Permitir baixar CSV dos registros rejeitados. + +Não carregar arquivo gigantesco inteiro na memória. + +Faça processamento streaming/batch. +x +--- + +# 28. NORMALIZAÇÃO DE TELEFONE + +Criar serviço dedicado de normalização. + +Não espalhar regexes pelo código. + +Preparar inicialmente para números brasileiros. + +Guardar: + +```text +phone_original +phone_normalized +``` + +Arquitetura deve permitir suporte futuro a E.164 internacional. + +--- + +# 29. LISTA DE SUPRESSÃO + +Implementar: + +**Do Not Call / Lista de Bloqueio** + +Uma campanha não deve discar um número presente nessa lista. + +Criar: + +```text +suppression_list +``` + +Permitir: + +* adicionar manualmente; +* importar CSV; +* pesquisar; +* remover com permissão; +* informar motivo; +* auditoria. + +Antes de originar: + +```text +CHECK SUPPRESSION +``` + +obrigatoriamente. + +--- + +# 30. MOTOR DO DISCADOR PREDITIVO + +Este é um dos componentes MAIS IMPORTANTES do projeto. + +NÃO implemente simplesmente: + +```text +for lead: + originate() +``` + +Isso NÃO é discador preditivo. + +Criar um serviço: + +```text +PredictiveDialerEngine +``` + +responsável por decidir continuamente quantas chamadas devem ser originadas. + +Deve considerar: + +* agentes logados; +* agentes disponíveis; +* agentes ocupados; +* agentes em wrap-up; +* agentes pausados; +* chamadas atualmente discando; +* chamadas tocando; +* chamadas atendidas aguardando agente; +* answer rate histórico; +* taxa de chamadas ocupadas; +* taxa de não atendimento; +* tempo médio até atendimento; +* TMA; +* agentes que provavelmente ficarão disponíveis; +* abandono recente; +* CPS disponível; +* limite de concorrência; +* pacing configurado. + +--- + +# 31. ALGORITMO PREDITIVO + +Implementar inicialmente um algoritmo conservador e adaptativo. + +Mantenha estatísticas por campanha. + +Exemplo: + +```text +answer_probability +average_answer_delay +average_talk_time +abandon_rate +available_agents +busy_agents +dialing_calls +ringing_calls +``` + +Utilize EWMA ou método estatístico equivalente para evitar variações violentas. + +Calcular aproximadamente: + +```text +expected_agent_supply = + agentes_disponiveis + + + agentes_com_probabilidade_de_liberacao_no_horizonte +``` + +Depois estimar: + +```text +expected_answers = calls_to_dial * answer_probability +``` + +e ajustar para que: + +```text +expected_answers ~= capacidade prevista de agentes +``` + +Adicionar multiplicador: + +```text +pacing_factor +``` + +Porém sempre respeitando: + +```text +CPS +max_concurrent_calls +max_pacing +max_abandon_rate +available_agents +trunk capacity +campaign schedule +``` + +--- + +# 32. PREVISÃO DE LIBERAÇÃO DE AGENTES + +Para tornar o discador realmente preditivo, utilize: + +```text +TMA histórico +tempo atual da chamada +distribuição histórica de duração +``` + +para estimar quais agentes podem ficar disponíveis dentro de um horizonte curto. + +Não precisa construir machine learning. + +Uma abordagem estatística eficiente, explicável e determinística é preferível inicialmente. + +Documentar detalhadamente o algoritmo em: + +```text +docs/PREDICTIVE_DIALER.md +``` + +--- + +# 33. CONTROLE DE ABANDONO + +O motor deve possuir proteção contra abandono excessivo. + +Se: + +```text +abandon_rate > target_abandon_rate +``` + +o pacing deve cair automaticamente. + +Se subir muito: + +```text +pacing -> modo conservador +``` + +Se necessário: + +```text +suspender novas originações +``` + +Nunca desenhar o sistema para originar milhares de chamadas sem agentes com o objetivo de verificar se números atendem. + +A intenção do discador é estabelecer comunicação real entre lead e agente. + +--- + +# 34. PROTEÇÃO REGULATÓRIA / COMPLIANCE + +Criar uma área: + +**Sistema → Compliance de Chamadas** + +Não hardcode regras de uma única legislação dentro do motor. + +Criar parâmetros configuráveis. + +Registrar: + +```text +total_calls +answered_calls +short_calls +abandoned_calls +calls_per_number +calls_per_day +calls_per_month +``` + +Criar alertas relacionados a: + +* volume excessivo; +* chamadas curtas; +* abandono; +* excesso de tentativas; +* horário permitido; +* supressão; +* caller ID. + +Para operação brasileira, preparar indicadores considerando que chamadas de curta duração são particularmente relevantes para fiscalização. + +Adicionar suporte de configuração de identificação/caller ID apropriado para campanhas de telemarketing, inclusive numeração 0303 quando aplicável ao caso de uso. + +Adicionar contador mensal para identificar operações de alto volume que possam exigir mecanismos adicionais de autenticação de chamadas pela rede. + +O sistema deve AJUDAR a cumprir regras. + +Nunca implementar funcionalidades destinadas a burlar mecanismos antispam, autenticação, identificação de origem ou bloqueios das operadoras. + +--- + +# 35. RESERVA CONCORRENTE DE LEADS + +É obrigatório evitar que dois workers disquem para o mesmo lead. + +Utilizar mecanismo transacional como: + +```sql +SELECT ... +FOR UPDATE SKIP LOCKED +``` + +ou solução equivalente. + +Transição: + +```text +READY + ↓ +RESERVED + ↓ +DIALING +``` + +Tem que ser atômica. + +Implementar timeout de reservation para recuperar leads caso um worker morra. + +--- + +# 36. IDEMPOTÊNCIA + +Originação deve possuir: + +```text +attempt_id +call_id +uniqueid +linkedid +``` + +Nunca originar novamente uma tentativa já confirmada simplesmente porque houve timeout da API. + +Construir chamadas como state machine. + +Exemplo: + +```text +CREATED +RESERVED +ORIGINATING +RINGING +ANSWERED +QUEUED +AGENT_CONNECTED +COMPLETED +FAILED +``` + +Persistir as transições relevantes. + +--- + +# 37. CALL FLOW PREDITIVO + +Fluxo esperado: + +```text +Lead + ↓ +PredictiveDialer + ↓ +CPS limiter + ↓ +Asterisk Originate + ↓ +Trunk + ↓ +Destino +``` + +Quando a chamada atende: + +```text +ANSWER + ↓ +opcional AMD + ↓ +humano + ↓ +Fila Asterisk + ↓ +Agente disponível + ↓ +Bridge +``` + +Persistir todos os timestamps. + +--- + +# 38. AMD + +Adicionar Answering Machine Detection como recurso OPCIONAL. + +Nunca deixar AMD obrigatório. + +Configurar por campanha. + +Possíveis resultados: + +```text +HUMAN +MACHINE +NOT_SURE +HANGUP +``` + +Criar parâmetros avançados apenas para administrador. + +Registrar resultado para posterior otimização. + +--- + +# 39. AFTER CALL / WRAP-UP + +Implementar: + +```text +wrap_up_time +``` + +Durante wrap-up o agente não deve ser tratado pelo predictive engine como imediatamente disponível. + +Mostrar estado: + +```text +WRAP UP +``` + +e tempo restante. + +--- + +# 40. DISPOSIÇÃO DA CHAMADA + +Após atendimento, permitir ao agente classificar a ligação. + +Exemplos: + +```text +VENDA +SEM INTERESSE +CALLBACK +NÚMERO ERRADO +NÃO LIGAR NOVAMENTE +CAIXA POSTAL +OUTRO +``` + +Criar tela administrativa para configurar disposições. + +Uma disposição pode possuir ações. + +Exemplo: + +```text +CALLBACK +-> solicitar data/hora +``` + +```text +DO_NOT_CALL +-> adicionar automaticamente à suppression_list +``` + +--- + +# 41. CALLBACK + +Permitir agendar retorno. + +Campos: + +```text +lead +agent +campaign +scheduled_at +timezone +notes +``` + +O scheduler deve colocar o lead novamente em condição de discagem no horário correto. + +Permitir callback: + +```text +para qualquer agente +``` + +ou: + +```text +preferencialmente para o mesmo agente +``` + +--- + +# 42. CDR / HISTÓRICO DE CHAMADAS + +Criar modelo consolidado próprio. + +Capturar: + +```text +call_id +asterisk_uniqueid +linkedid +campaign +lead +queue +agent +extension +trunk +caller +called +start_at +ring_at +answer_at +queue_enter_at +agent_answer_at +end_at +duration +billsec +talk_time +wait_time +disposition +hangup_cause +amd_result +``` + +Nunca depender exclusivamente do CDR nativo para reconstruir toda a jornada do Call Center. + +Utilizar: + +* CDR; +* CEL; +* AMI Events; +* queue_log. + +--- + +# 43. RELATÓRIO DE CHAMADAS + +Criar: + +**Relatórios → Chamadas** + +Filtros: + +* data inicial; +* data final; +* ramal; +* agente; +* fila; +* campanha; +* tronco; +* telefone; +* status; +* disposição. + +Colunas: + +```text +Data +Origem +Destino +Campanha +Fila +Agente +Ramal +Tronco +Espera +Conversação +Duração +Resultado +Hangup Cause +``` + +Permitir exportar CSV. + +Paginação obrigatoriamente server-side. + +Não carregar milhões de registros de uma vez no browser. + +--- + +# 44. TME + +Implementar TME — Tempo Médio de Espera. + +Calcular principalmente: + +```text +agent_answer_at - queue_enter_at +``` + +para chamadas atendidas. + +Separadamente mostrar: + +```text +tempo médio antes do abandono +``` + +Não misturar as duas métricas silenciosamente. + +Documentar a definição utilizada. + +--- + +# 45. TMA + +Implementar: + +**TMA — Tempo Médio de Atendimento** + +Calcular principalmente: + +```text +SUM(talk_time) / answered_calls +``` + +Permitir futuramente visualizar: + +```text +Talk Time +Talk + Wrap-Up +``` + +como indicadores separados. + +--- + +# 46. DASHBOARD + +Criar um dashboard moderno. + +Cards principais: + +```text +Chamadas hoje +Chamadas atendidas +Chamadas em andamento +Chamadas aguardando +Agentes disponíveis +Agentes ocupados +Agentes pausados +TME +TMA +Taxa de atendimento +Taxa de abandono +``` + +Gráficos: + +### Chamadas por hora + +Line/area chart. + +### Atendidas x abandonadas + +### TME ao longo do dia + +### TMA ao longo do dia + +### Agentes por estado + +### Campanhas + +Mostrar: + +```text +Discando +Ringing +Atendidas +Conectadas +CPS atual +Pacing +Answer Rate +Abandono +``` + +Não criar gráficos falsos. + +Todos devem utilizar informações reais do banco/Asterisk. + +--- + +# 47. DASHBOARD DO DISCADOR + +Criar painel específico por campanha mostrando em tempo real: + +```text +CPS configurado +CPS atual +Chamadas discando +Chamadas tocando +Atendidas +Na fila +Conectadas em agente +Agentes disponíveis +Pacing atual +Answer rate +Abandon rate +TME +TMA +Leads restantes +Leads processados +``` + +Isso será extremamente importante para operação. + +--- + +# 48. STATUS DOS AGENTES + +Estados canônicos: + +```text +OFFLINE +LOGGED_IN +AVAILABLE +RINGING +IN_CALL +WRAP_UP +PAUSED +``` + +Não inferir tudo diretamente de uma única variável do Asterisk. + +Criar máquina de estados consistente. + +Registrar início/fim de cada estado. + +Isso permitirá calcular produtividade posteriormente. + +--- + +# 49. RELATÓRIO DE AGENTES + +Criar: + +**Relatórios → Agentes** + +Exibir: + +```text +Tempo logado +Tempo disponível +Tempo em chamada +Tempo pausado +Tempo wrap-up +Chamadas atendidas +TMA +``` + +Detalhar pausas: + +```text +motivo +início +fim +duração +``` + +--- + +# 50. QUEUE_LOG + +Utilizar os dados do `queue_log` porque ele contém eventos específicos de ACD que um CDR tradicional não representa corretamente. + +Capturar eventos relevantes como: + +* ENTERQUEUE; +* CONNECT; +* COMPLETEAGENT; +* COMPLETECALLER; +* ABANDON; +* RINGNOANSWER; +* TRANSFER; +* PAUSE; +* UNPAUSE. + +Normalizar no PostgreSQL. + +Não basear TME/TMA exclusivamente em parsing periódico de arquivo. + +Preferencialmente capturar eventos em tempo real e utilizar queue_log também para reconciliação/auditoria. + +--- + +# 51. RECONCILIAÇÃO + +Eventos podem eventualmente ser perdidos por restart de algum worker. + +Criar rotina de reconciliação. + +Ela deve comparar informações recentes com: + +* CDR; +* CEL; +* queue_log; +* estados persistidos. + +Corrigir chamadas que ficaram indevidamente em: + +```text +RINGING +IN_PROGRESS +CONNECTED +``` + +após falha/restart. + +--- + +# 52. INTERFACE + +Quero uma aplicação: + +* bonita; +* moderna; +* clean; +* corporativa; +* rápida; +* responsiva; +* com excelente UX. + +Utilizar a logo: + +```text +/opt/b2bcall/b2bcall.png +``` + +Copiar/processar para o diretório apropriado do frontend. + +Não modificar a logo original. + +Menu sugerido: + +```text +Dashboard + +Discador + ├─ Campanhas + ├─ Leads + ├─ Importações + ├─ Callbacks + └─ Lista de Bloqueio + +Call Center + ├─ Agentes + ├─ Filas + ├─ Motivos de Pausa + └─ Disposições + +Telefonia + ├─ Ramais + ├─ Troncos + └─ Dialplan + +Monitoramento + ├─ Filas + ├─ Agentes + ├─ Ramais + └─ Campanhas + +Relatórios + ├─ Chamadas + ├─ Agentes + ├─ Filas + └─ Campanhas + +Sistema + ├─ Usuários + ├─ Perfis e Permissões + ├─ Asterisk + ├─ Compliance + ├─ Auditoria + └─ Configurações +``` + +--- + +# 53. BANCO DE DADOS + +Planejar pelo menos entidades equivalentes a: + +```text +users +sessions + +roles +permissions +user_roles +role_permissions + +agents +agent_sessions +agent_state_events + +pause_reasons +agent_pause_events + +extensions + +trunks + +queues +queue_members + +dialplans +dialplan_versions + +campaigns +campaign_schedules +campaign_agents + +lead_imports +leads + +dial_attempts + +calls +call_events + +queue_events + +call_dispositions +call_callbacks + +suppression_list + +asterisk_config_versions + +audit_logs + +application_settings +``` + +Adicionar tabelas auxiliares necessárias. + +Criar índices para consultas de alto volume. + +Especial atenção para índices envolvendo: + +```text +campaign_id +status +next_attempt_at +created_at +phone_normalized +call_id +linkedid +queue_id +agent_id +extension_id +``` + +--- + +# 54. VOLUME DE DADOS + +Projetar desde o início considerando milhões de registros de chamadas. + +Não criar relatórios que façam: + +```sql +SELECT * FROM calls; +``` + +Implementar: + +* paginação; +* índices; +* filtros; +* agregações; +* query plans razoáveis. + +Avaliar particionamento temporal posteriormente para: + +```text +calls +call_events +queue_events +audit_logs +``` + +Não precisa particionar prematuramente se não houver necessidade, mas documentar estratégia de crescimento. + +--- + +# 55. SEGURANÇA DE SEGREDOS + +Criar: + +```text +.env +.env.example +``` + +`.env` obrigatoriamente em `.gitignore`. + +Segredos: + +* PostgreSQL; +* Redis; +* JWT; +* encryption key; +* AMI; +* ARI; +* SIP; +* SMTP. + +Nunca commitar. + +Nunca imprimir em logs. + +Credenciais de trunk devem ser criptografadas em repouso quando forem mantidas pelo aplicativo. + +Utilizar AES-256-GCM ou mecanismo equivalente com master key externa ao banco. + +--- + +# 56. FIREWALL + +Configurar firewall utilizando nftables ou abordagem adequada ao Debian 13. + +Princípio: + +**default deny para serviços administrativos/telefônicos sensíveis.** + +Permitir SIP no Asterisk somente de: + +* OpenSIPS autorizado; +* operadoras privadas; +* redes explicitamente configuradas. + +AMI: + +somente aplicação. + +ARI: + +somente aplicação. + +PostgreSQL: + +somente containers/localhost necessário. + +Redis: + +somente containers. + +Não bloquear SSH durante a instalação. + +--- + +# 57. HTTP SECURITY + +Implementar: + +* Content Security Policy; +* X-Content-Type-Options; +* Referrer-Policy; +* HSTS quando HTTPS; +* CORS restritivo; +* CSRF quando necessário; +* Helmet ou equivalente; +* input validation; +* DTO validation. + +Não confiar em entrada do usuário. + +--- + +# 58. SQL SECURITY + +Nunca concatenar SQL manualmente com dados fornecidos pelo usuário. + +Utilizar queries parametrizadas/ORM. + +Importação de CSV também deve ser tratada como entrada não confiável. + +--- + +# 59. ASTERISK CONFIG SECURITY + +O editor avançado de configuração é uma área sensível. + +Antes de gravar: + +* validar seção; +* validar formato; +* impedir path traversal; +* nunca aceitar caminho arbitrário; +* nunca permitir gravar fora de diretórios permitidos; +* criar backup; +* versionar; +* validar; +* reload controlado. + +Nunca transformar a interface do Asterisk em uma execução remota de comandos Linux. + +--- + +# 60. LOGS + +Utilizar logs estruturados JSON no backend. + +Campos úteis: + +```text +timestamp +level +service +request_id +user_id +campaign_id +call_id +attempt_id +message +``` + +Nunca registrar: + +* senha; +* SIP password; +* JWT; +* refresh token; +* credencial AMI; +* credencial ARI. + +--- + +# 61. CORRELATION ID + +Cada request HTTP deve possuir: + +```text +request_id +``` + +Cada tentativa: + +```text +attempt_id +``` + +Cada chamada: + +```text +call_id +``` + +Relacionar com: + +```text +Asterisk UNIQUEID +Asterisk LINKEDID +``` + +Isso será fundamental para troubleshooting. + +--- + +# 62. HEALTH CHECKS + +Criar: + +```text +/api/health +/api/health/live +/api/health/ready +``` + +Verificar: + +* API; +* PostgreSQL; +* Redis; +* AMI; +* Asterisk. + +Dashboard administrativo deve mostrar: + +```text +API OK +Postgres OK +Redis OK +Asterisk OK +AMI OK +``` + +--- + +# 63. BACKUP + +Criar scripts/documentação para backup de: + +* PostgreSQL; +* configurações Asterisk; +* `.env` de maneira segura; +* uploads relevantes. + +Adicionar procedimento de restore. + +--- + +# 64. TESTES + +Criar testes reais. + +## Unit tests + +Principalmente: + +* predictive engine; +* CPS limiter; +* permissions; +* phone normalization; +* retry strategy; +* state machines; +* TME/TMA; +* scheduling. + +## Integration + +* PostgreSQL; +* Redis; +* repositories; +* API; +* AMI adapter mockado. + +## E2E + +Pelo menos: + +```text +login +criar agente +criar ramal +criar fila +criar trunk +criar campanha +importar CSV +iniciar campanha em modo simulado +pausar campanha +relatórios +RBAC +``` + +--- + +# 65. MODO DE SIMULAÇÃO + +Criar um recurso muito importante: + +```text +DIALER_SIMULATION=true +``` + +Neste modo: + +**NÃO realizar chamadas externas.** + +Simular: + +```text +ANSWER +BUSY +NO ANSWER +ANSWER DELAY +TALK TIME +``` + +Isso permitirá testar o predictive engine sem gerar milhares de chamadas reais. + +Criar testes utilizando esse modo. + +--- + +# 66. TESTE DO PREDICTIVE ENGINE + +Criar simulador com, por exemplo: + +```text +20 agentes +10 CPS +30% answer rate +10 segundos answer delay +180 segundos TMA +``` + +Verificar: + +* pacing; +* disponibilidade; +* abandono; +* CPS; +* estabilização. + +Gerar logs/estatísticas. + +O motor não pode oscilar violentamente. + +--- + +# 67. GIT + +Inicializar Git em: + +```text +/opt/b2bcall +``` + +Criar `.gitignore`. + +Commits organizados por etapas. + +Exemplos: + +```text +feat: bootstrap b2bcall architecture + +feat: add authentication and RBAC + +feat: add asterisk pjsip integration + +feat: add call center queues + +feat: add campaign management + +feat: implement predictive dialing engine + +feat: add realtime monitoring + +feat: add call reports + +feat: add security hardening + +test: add predictive dialer simulation +``` + +Nunca versionar segredos. + +--- + +# 68. DOCUMENTAÇÃO + +Criar: + +```text +README.md +docs/ARCHITECTURE.md +docs/INSTALL.md +docs/ASTERISK.md +docs/OPENSIPS.md +docs/PREDICTIVE_DIALER.md +docs/DATABASE.md +docs/SECURITY.md +docs/OPERATIONS.md +docs/BACKUP_RESTORE.md +docs/API.md +docs/TROUBLESHOOTING.md +CHANGELOG.md +``` + +--- + +# 69. TODO OPERACIONAL + +Durante o desenvolvimento mantenha: + +```text +TODO.md +``` + +Exemplo: + +```text +[x] Docker +[x] PostgreSQL +[x] Redis +[x] Authentication +[x] RBAC +[x] Asterisk +[x] Extensions +[x] Trunks +[ ] Queues +[ ] Agents +[ ] Campaigns +[ ] Predictive Engine +``` + +Quando terminar um item: + +* testar; +* marcar `[x]`; +* continuar para o próximo. + +Não utilize o TODO como desculpa para deixar funcionalidades incompletas. + +No final, nenhum requisito principal deste documento deve permanecer pendente. + +--- + +# 70. BOOTSTRAP DO SUPER ADMIN + +Na primeira instalação criar um usuário `super_admin`. + +Nunca utilizar senha padrão como: + +```text +admin +123456 +password +``` + +Gerar senha aleatória forte. + +Mostrar apenas uma vez ao final da instalação. + +Idealmente criar: + +```text +/opt/b2bcall/FIRST_LOGIN.txt +``` + +com: + +```bash +chmod 600 +``` + +Orientar que o arquivo seja removido após primeiro acesso. + +Forçar troca da senha no primeiro login. + +--- + +# 71. SEED + +Criar seed inicial contendo: + +* permissões; +* perfil super_admin; +* admin; +* supervisor; +* agent; +* motivos básicos de pausa; +* disposições padrão. + +Mas tudo deve poder ser alterado posteriormente. + +--- + +# 72. NÃO CRIAR DADOS FAKE EM PRODUÇÃO + +Dados demonstrativos somente quando: + +```text +SEED_DEMO_DATA=true +``` + +Por padrão: + +```text +false +``` + +Dashboard de produção nunca deve apresentar números inventados. + +--- + +# 73. UX DE ERROS + +Não mostrar ao usuário: + +```text +500 Internal Server Error +``` + +sem contexto. + +Mostrar mensagem amigável e gerar: + +```text +request_id +``` + +para suporte técnico. + +No log técnico, manter detalhes. + +--- + +# 74. PERFORMANCE + +Evitar: + +* N+1 queries; +* polling desnecessário; +* carregamento completo de tabelas; +* processamento pesado dentro de HTTP request; +* bloqueio do event loop; +* originação síncrona de milhares de chamadas. + +Utilizar workers. + +--- + +# 75. CONTROLE DE CAMPANHA DISTRIBUÍDO + +O motor deve impedir dois workers de controlarem simultaneamente a mesma campanha. + +Criar distributed lock. + +Exemplo conceitual: + +```text +dialer:campaign:{id}:lock +``` + +com: + +* TTL; +* renewal; +* ownership token. + +Se worker morrer, outro poderá assumir posteriormente. + +--- + +# 76. PAUSA IMEDIATA + +Quando administrador clicar: + +```text +PAUSAR CAMPANHA +``` + +não originar nenhuma nova chamada. + +As chamadas já existentes podem continuar. + +Em: + +```text +STOP +``` + +definir comportamento de maneira clara e segura. + +Não derrubar ligações de clientes/agentes sem confirmação explícita. + +--- + +# 77. MONITORAMENTO DO TRONCO + +Monitorar: + +* status; +* chamadas atuais; +* CPS atual; +* CPS limite; +* channels limit; +* tentativas; +* falhas; +* congestion; +* SIP response codes quando disponíveis. + +Mostrar no dashboard. + +--- + +# 78. HANGUP CAUSE + +Guardar informações de encerramento. + +Normalizar resultados como: + +```text +ANSWERED +BUSY +NO_ANSWER +REJECTED +CONGESTION +INVALID_NUMBER +NETWORK_FAILURE +CANCELLED +``` + +Mas preservar também o código original recebido do Asterisk/rede. + +--- + +# 79. RETRY ENGINE + +Criar regras por campanha. + +Exemplo: + +```text +BUSY -> 15 minutos +NO ANSWER -> 60 minutos +CONGESTION -> 5 minutos +FAILED -> 30 minutos +``` + +Limitar quantidade máxima de tentativas. + +Permitir personalização. + +Nunca rediscagem infinita. + +--- + +# 80. HORÁRIO DE CAMPANHA + +Scheduler deve impedir discagem fora do período configurado. + +Considerar timezone. + +Por exemplo: + +```text +America/Sao_Paulo +``` + +Configurar: + +* segunda; +* terça; +* quarta; +* quinta; +* sexta; +* sábado; +* domingo. + +Campanha fora do horário: + +```text +WAITING_SCHEDULE +``` + +--- + +# 81. ARQUITETURA DE FRONTEND + +Criar componentes reutilizáveis. + +Evitar páginas gigantes com milhares de linhas. + +Separar: + +```text +components +features +hooks +services +schemas +types +``` + +Usar tabelas com: + +* busca; +* filtros; +* paginação; +* ordenação; +* loading; +* empty state; +* error state. + +--- + +# 82. DESIGN + +A aparência deve lembrar sistemas SaaS/Contact Center modernos. + +Utilizar: + +* sidebar; +* topbar; +* cards; +* tabelas profissionais; +* badges; +* tooltips; +* gráficos; +* skeleton loading; +* dialogs; +* toast notifications. + +Evitar aparência de: + +* sistema antigo; +* FreePBX clone; +* formulário HTML cru; +* bootstrap antigo. + +A identidade visual deve utilizar a logo do B2BCall. + +Criar light/dark mode se não prejudicar o desenvolvimento. + +--- + +# 83. API + +Utilizar REST bem estruturada inicialmente. + +Exemplos: + +```text +/api/auth +/api/users +/api/roles +/api/permissions + +/api/extensions +/api/trunks +/api/dialplans + +/api/agents +/api/queues +/api/pause-reasons + +/api/campaigns +/api/campaigns/:id/leads +/api/campaigns/:id/start +/api/campaigns/:id/pause +/api/campaigns/:id/stop + +/api/reports/calls +/api/reports/agents + +/api/monitoring/extensions +/api/monitoring/queues +/api/monitoring/campaigns + +/api/asterisk +/api/compliance +/api/audit +``` + +Gerar OpenAPI/Swagger. + +Swagger administrativo deve poder ser desabilitado em produção. + +--- + +# 84. VALIDATION + +Usar validação forte de schemas/DTO. + +Exemplo: + +CPS: + +```text +>= 1 +``` + +Telefone: + +validar e normalizar. + +Datas: + +validar timezone. + +UUID: + +validar. + +IDs fornecidos pelo usuário nunca devem ser confiados sem validação. + +--- + +# 85. MIGRATIONS + +Nenhuma alteração de banco manual sem migration. + +Criar comandos: + +```bash +npm run db:migrate +npm run db:seed +``` + +ou equivalentes. + +--- + +# 86. DEPLOYMENT + +Criar script: + +```text +scripts/install.sh +``` + +para servidor Debian 13 limpo. + +Ele deve: + +1. verificar permissões; +2. instalar dependências; +3. instalar Docker; +4. instalar Compose plugin; +5. criar diretórios; +6. gerar secrets; +7. preparar `.env`; +8. buildar containers; +9. iniciar PostgreSQL/Redis; +10. executar migrations; +11. iniciar Asterisk; +12. iniciar API/workers/frontend; +13. executar health checks; +14. mostrar resultado. + +Também criar: + +```text +scripts/update.sh +scripts/backup.sh +scripts/restore.sh +scripts/healthcheck.sh +``` + +--- + +# 87. NGINX / REVERSE PROXY + +Criar reverse proxy. + +Expor para a LAN preferencialmente apenas: + +```text +80 +443 +``` + +WebSocket precisa funcionar corretamente. + +Preparar suporte HTTPS. + +Se nenhum domínio/certificado estiver disponível inicialmente, documentar como ativar certificado posteriormente. + +Não inventar domínio. + +--- + +# 88. RESTART SEGURO + +Restart da API não deve destruir campanhas. + +Estado importante sempre no: + +* PostgreSQL; +* Redis quando apropriado. + +Ao reiniciar worker: + +* recuperar campanhas RUNNING; +* reconciliar chamadas; +* limpar reservations expiradas; +* reassumir locks; +* continuar operação corretamente. + +--- + +# 89. OBSERVABILIDADE + +Criar no mínimo: + +* logs estruturados; +* health checks; +* métricas internas. + +Preparar endpoint Prometheus: + +```text +/metrics +``` + +Métricas úteis: + +```text +b2bcall_calls_total +b2bcall_calls_answered_total +b2bcall_calls_abandoned_total + +b2bcall_campaign_cps + +b2bcall_agents_available +b2bcall_agents_busy +b2bcall_agents_paused + +b2bcall_queue_waiting + +b2bcall_dialer_active_calls +``` + +Pode adicionar outras. + +--- + +# 90. REQUISITOS DE ACEITE + +O projeto NÃO está terminado enquanto eu não conseguir realizar, pela interface: + +1. fazer login; +2. cadastrar usuário; +3. cadastrar perfil; +4. alterar permissões; +5. cadastrar ramal; +6. visualizar seu status; +7. cadastrar trunk; +8. cadastrar dialplan; +9. cadastrar fila; +10. cadastrar agente; +11. associar agente a fila; +12. cadastrar pausa; +13. logar como agente; +14. colocar agente em pausa; +15. retirar pausa; +16. cadastrar campanha; +17. configurar CPS; +18. importar CSV; +19. iniciar campanha; +20. pausar campanha; +21. visualizar discagem; +22. visualizar agentes; +23. visualizar fila; +24. visualizar TME; +25. visualizar TMA; +26. pesquisar chamadas; +27. filtrar por ramal; +28. filtrar por fila; +29. exportar relatório; +30. administrar configurações necessárias do Asterisk; +31. consultar audit log. + +--- + +# 91. ACEITE DO DIALER + +Além da interface, provar através de teste/simulação que: + +```text +configured CPS = 5 +``` + +não origina mais de 5 novas chamadas em qualquer janela apropriada de 1 segundo. + +Provar que: + +```text +campaign max concurrent = 20 +``` + +não ultrapassa 20 chamadas simultâneas. + +Provar que: + +* lead não é discado duas vezes simultaneamente; +* campanha pausada não gera novas chamadas; +* worker reiniciado recupera operação; +* pacing cai quando abandono sobe; +* lead bloqueado não é chamado; +* campanha fora do horário não chama. + +--- + +# 92. ACEITE DE SEGURANÇA + +Verificar: + +* nenhuma senha no Git; +* nenhum `.env` commitado; +* PostgreSQL não público; +* Redis não público; +* AMI não público; +* ARI não público; +* rate limit funcionando; +* RBAC funcionando no backend; +* usuário agent não acessa configuração Asterisk; +* agent não consegue elevar a própria permissão; +* SQL injection protegida; +* path traversal protegido; +* logs sem credenciais. + +--- + +# 93. ACEITE ASTERISK + +Executar e documentar resultado de: + +```text +asterisk -rx "core show version" +asterisk -rx "core show uptime" +asterisk -rx "pjsip show endpoints" +asterisk -rx "pjsip show contacts" +asterisk -rx "queue show" +``` + +Validar comunicação: + +```text +API -> AMI -> Asterisk +``` + +--- + +# 94. QUALITY GATE + +Antes de declarar terminado: + +Executar: + +```text +lint +typecheck +unit tests +integration tests +e2e tests +docker compose config +docker compose ps +health checks +``` + +Corrigir erros. + +Não simplesmente informar que existem erros conhecidos. + +--- + +# 95. README FINAL + +No final atualizar README com: + +## B2BCall + +### Architecture + +### Requirements + +### Installation + +### First Login + +### Configuration + +### OpenSIPS Integration + +### Trunks + +### Agents + +### Queues + +### Campaigns + +### Predictive Dialer + +### Reports + +### Backup + +### Update + +### Troubleshooting + +--- + +# 96. RELATÓRIO FINAL DA IMPLEMENTAÇÃO + +Somente depois de efetivamente terminar, apresentar um resumo contendo: + +```text +B2BCall version: +Asterisk version: +Node version: +PostgreSQL version: +Docker version: + +Containers: +... + +URLs: +... + +Super admin: +... + +Health: +... + +Tests: +... + +Pending: +... +``` + +Se existir algo pendente, explicar claramente. + +--- + +# 97. DECISÕES TÉCNICAS IMPORTANTES + +Tenha especialmente em mente: + +### Asterisk não é banco de negócio + +O estado de campanhas/leads/agentes deve estar no PostgreSQL. + +### Redis não é fonte permanente + +Informações importantes devem sobreviver à perda do Redis. + +### AMI events são assíncronos + +Não presuma que Originate HTTP concluído significa chamada completada. + +### LINKEDID é importante + +Uma chamada poderá possuir múltiplos canais Asterisk. + +### Calls devem possuir ID próprio + +Não utilize `UNIQUEID` do Asterisk como primary key de negócio. + +### WebSocket é para visualização + +O banco continua sendo fonte persistente. + +### O predictive dialer precisa ser controlável + +Sempre permitir: + +```text +PAUSE +DRAIN +STOP +``` + +### Segurança deve existir desde a arquitetura + +Não adicionar segurança apenas no final. + +--- + +# 98. PRIMEIRA TAREFA + +Comece AGORA. + +Primeiro: + +```bash +cd /opt/b2bcall +``` + +Analise: + +```bash +pwd +ls -lah +cat /etc/os-release +uname -a +ip addr +df -h +free -h +``` + +Verifique a existência: + +```text +/opt/b2bcall/b2bcall.png +``` + +Depois crie: + +```text +docs/ARCHITECTURE.md +TODO.md +``` + +Defina a arquitetura inicial. + +Em seguida inicialize o Git e comece efetivamente a implantação. + +NÃO pare após criar os documentos. + +Continue imediatamente para: + +```text +Docker +↓ +PostgreSQL +↓ +Redis +↓ +Asterisk +↓ +Backend +↓ +Authentication +↓ +RBAC +↓ +Telefonia +↓ +Call Center +↓ +Campanhas +↓ +Predictive Dialer +↓ +Frontend +↓ +Monitoring +↓ +Reports +↓ +Security +↓ +Tests +↓ +Production validation +``` + +Trabalhe até alcançar os critérios de aceite. + +--- + +# 99. PRINCÍPIO FINAL + +Este projeto será usado como uma plataforma real de Call Center. + +Portanto, sempre prefira: + +```text +correto > rápido +seguro > conveniente +transacional > improvisado +observável > caixa-preta +configurável > hardcoded +testado > presumido +``` + +Não entregue apenas uma demonstração. + +Construa o **B2BCall** como uma aplicação que possa realmente evoluir para produção. diff --git a/b2bcall.png b/b2bcall.png new file mode 100644 index 0000000..37ba6e8 Binary files /dev/null and b/b2bcall.png differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d504473 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,73 @@ +name: b2bcall + +networks: + b2bcall-net: + driver: bridge + +volumes: + postgres-data: + redis-data: + +services: + postgres: + image: postgres:17-alpine + container_name: b2bcall-postgres + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB:-b2bcall} + POSTGRES_USER: ${POSTGRES_USER:-b2bcall} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} + volumes: + - postgres-data:/var/lib/postgresql/data + - ./infrastructure/postgres/init:/docker-entrypoint-initdb.d:ro + networks: + - b2bcall-net + # Sem "ports": Postgres não deve ficar acessível fora da rede Docker interna. + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-b2bcall} -d ${POSTGRES_DB:-b2bcall}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + memory: 512M + + redis: + image: redis:7-alpine + container_name: b2bcall-redis + restart: unless-stopped + environment: + REDIS_PASSWORD: ${REDIS_PASSWORD:?REDIS_PASSWORD is required} + command: > + redis-server + --requirepass ${REDIS_PASSWORD:?REDIS_PASSWORD is required} + --maxmemory 100mb + --maxmemory-policy noeviction + --appendonly yes + volumes: + - redis-data:/data + networks: + - b2bcall-net + # Sem "ports": Redis não deve ficar acessível fora da rede Docker interna. + healthcheck: + test: ["CMD-SHELL", "redis-cli -a $$REDIS_PASSWORD --no-auth-warning ping | grep -q PONG"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 5s + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + deploy: + resources: + limits: + memory: 160M diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..7412917 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,142 @@ +# B2BCall — Arquitetura + +## 1. Contexto do servidor (levantado em 2026-08-27) + +| Item | Valor | +|---|---| +| SO | Debian 13 (trixie) | +| Kernel | 6.12.94+deb13-amd64 | +| CPU | 2 vCPU | +| RAM | 1.9 GiB (+ 1.6 GiB swap) | +| Disco | 29 GiB (26 GiB livres) | +| IP | 10.10.32.142/24 (privado, sem IP público) | +| Hostname | Lab-FSCore-API | + +**Alerta de capacidade:** 1.9 GiB de RAM é pouco para rodar simultaneamente Postgres + +Redis + Asterisk + API (NestJS) + worker + asterisk-events + scheduler + frontend +(Next.js) + Nginx. Decisões desta arquitetura foram tomadas para caber neste ambiente +de laboratório: + +- Build do frontend fora do container em produção real (ou build multi-stage com + `node --max-old-space-size` limitado) — builds Next.js podem estourar RAM em VMs de + 2 GiB. +- `docker-compose.yml` define `mem_limit`/`deploy.resources.limits` conservadores por + serviço. +- Antes de operação real de discagem em produção, **recomenda-se aumentar a VM para + pelo menos 4 vCPU / 8 GiB RAM**. Este ambiente serve para desenvolvimento e testes + funcionais (inclusive `DIALER_SIMULATION=true`), não para carga real de discador + preditivo com volume. + +## 2. Estrutura do monorepo + +```text +/opt/b2bcall +├── apps +│ ├── frontend # Next.js (App Router) + Tailwind + shadcn/ui +│ ├── api # NestJS (Fastify adapter) - REST + WebSocket gateway +│ ├── dialer-worker # PredictiveDialerEngine, CPS limiter, retry engine +│ ├── asterisk-events # Conector AMI dedicado -> normaliza eventos -> Postgres/Redis/WS +│ └── scheduler # Callbacks, horários de campanha, reconciliação, cron jobs +├── packages +│ ├── database # Prisma/Drizzle schema + migrations + client +│ ├── shared # utils comuns (logger, result types, error classes) +│ ├── types # tipos TS compartilhados (DTOs, enums de estado) +│ ├── ui # componentes de UI compartilhados (design system) +│ └── telephony # TelephonyProvider (interface) + AsteriskTelephonyProvider (AMI/ARI) +├── infrastructure +│ ├── asterisk # pjsip.conf, extensions, odbc, realtime, queues +│ ├── nginx # reverse proxy, websocket upgrade, TLS +│ ├── postgres # init scripts, schemas (application / asterisk) +│ └── docker # Dockerfiles por serviço +├── scripts # install.sh, update.sh, backup.sh, restore.sh, healthcheck.sh +├── docs +├── docker-compose.yml +├── .env.example +├── .gitignore +└── README.md +``` + +## 3. Decisões técnicas + +### 3.1 Rede Docker / Asterisk + +- Asterisk roda em `network_mode: host` — RTP usa uma faixa grande de portas UDP + (10000-20000 por padrão); mapear porta a porta em bridge network é inviável em + produção. **Impacto documentado:** o container do Asterisk enxerga a rede do host + diretamente (perde isolamento de rede do Docker); todas as portas do Asterisk + (SIP 5060/5061, RTP, AMI 5038, ARI 8088) ficam sujeitas apenas ao firewall do host + (nftables), não ao Docker networking. Por isso as regras de nftables (seção 8) são + a única linha de defesa e devem ser tratadas como obrigatórias, não opcionais. +- Todos os demais serviços (api, frontend, worker, asterisk-events, scheduler, + postgres, redis, nginx) ficam em uma rede Docker privada (`b2bcall-net`, bridge), + sem publicação de portas para o host exceto Nginx (80/443). +- Postgres e Redis não publicam portas no host; acessíveis somente dentro de + `b2bcall-net`. + +### 3.2 Topologia SIP + +```text +INTERNET → [OpenSIPS] → rede SIP privada → [Asterisk] → [B2BCall] +Asterisk → rede privada → Troncos SIP (operadoras) +``` + +O Asterisk nunca terá IP público. OpenSIPS e troncos são acessados via IP privado. +AMI/ARI nunca expostos além do host local (bind em 127.0.0.1 ou rede interna Docker +quando possível). + +### 3.3 Asterisk Realtime + +- PJSIP objects (`ps_endpoints`, `ps_auths`, `ps_aors`, `ps_contacts`, + `ps_endpoint_id_ips`, `ps_registrations`) via `res_odbc` + `res_config_odbc`, + schema dedicado `asterisk` no mesmo Postgres. +- Domínio de aplicação (campanhas, leads, agentes, filas, etc.) vive no schema + `public`/`application`. Nunca misturar as duas responsabilidades na mesma tabela. + +### 3.4 Camada de telefonia + +- `packages/telephony`: interface `TelephonyProvider` (Originate, Hangup, + QueuePause, QueueAdd/Remove, ExtensionState, DeviceState, QueueStatus, + PJSIPShowEndpoints/Contacts, reload) + implementação `AsteriskTelephonyProvider` + (AMI para controle/eventos administrativos, ARI apenas quando controle fino de + canais/bridges for necessário). Nenhum comando AMI é chamado diretamente de + controllers da API. + +### 3.5 Eventos em tempo real + +- `apps/asterisk-events` mantém conexão persistente ao AMI, normaliza eventos, + persiste os relevantes e publica no `apps/api` (WebSocket gateway) via Redis + pub/sub. Frontend nunca faz polling do Asterisk. + +### 3.6 Motor do discador preditivo + +- `apps/dialer-worker`: `PredictiveDialerEngine` — decide continuamente quantas + chamadas originar, usando EWMA de answer rate, TMA, abandono, CPS disponível + (token bucket coordenado via Redis) e capacidade de agentes. Detalhado em + `docs/PREDICTIVE_DIALER.md` (a ser criado na fase correspondente). +- Reserva de leads via `SELECT ... FOR UPDATE SKIP LOCKED` no Postgres — + transição `READY → RESERVED → DIALING` atômica, com timeout de reservation. +- Lock distribuído por campanha via Redis (`dialer:campaign:{id}:lock`, TTL + + renewal + ownership token) para impedir dois workers controlando a mesma + campanha. + +### 3.7 Banco de dados + +- PostgreSQL 17. +- Migrations obrigatórias (nenhuma alteração manual de schema). +- Particionamento temporal de `calls`, `call_events`, `queue_events`, `audit_logs` + avaliado quando o volume justificar — não implementado prematuramente, mas + índices e chaves são desenhados para permitir particionamento futuro sem + migração destrutiva. + +### 3.8 Segurança desde a arquitetura + +- Segredos apenas em `.env` (nunca commitado). Credenciais de trunk SIP + criptografadas em repouso (AES-256-GCM, master key fora do banco). +- RBAC completo (users/roles/permissions/user_roles/role_permissions), + reforçado no backend independentemente do frontend. +- nftables com política default-deny para serviços administrativos/telefônicos; + SSH nunca bloqueado durante instalação. + +## 4. Status + +Ver `TODO.md` para o checklist vivo de implementação. diff --git a/infrastructure/postgres/init/001-schemas.sql b/infrastructure/postgres/init/001-schemas.sql new file mode 100644 index 0000000..5d03829 --- /dev/null +++ b/infrastructure/postgres/init/001-schemas.sql @@ -0,0 +1,14 @@ +-- Executado automaticamente na primeira inicialização do container Postgres +-- (docker-entrypoint-initdb.d). Idempotente por natureza do entrypoint oficial +-- (só roda quando o data directory está vazio). + +-- Schema dedicado ao Asterisk Realtime (ps_endpoints, ps_auths, ps_aors, ...). +-- O domínio de aplicação (campanhas, leads, agentes, etc.) vive em "public". +CREATE SCHEMA IF NOT EXISTS asterisk; + +-- Extensões necessárias para o domínio da aplicação. +CREATE EXTENSION IF NOT EXISTS pgcrypto; -- gen_random_uuid(), criptografia auxiliar +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +GRANT ALL ON SCHEMA asterisk TO CURRENT_USER; +GRANT ALL ON SCHEMA public TO CURRENT_USER; diff --git a/scripts/generate-secrets.sh b/scripts/generate-secrets.sh new file mode 100755 index 0000000..cb9f59b --- /dev/null +++ b/scripts/generate-secrets.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Gera valores fortes para os segredos do .env. +# Uso: ./scripts/generate-secrets.sh (imprime pares KEY=VALUE para colar no .env) +set -euo pipefail + +rand_hex() { openssl rand -hex "${1:-32}"; } +rand_b64() { openssl rand -base64 "${1:-32}"; } + +echo "POSTGRES_PASSWORD=$(rand_hex 24)" +echo "REDIS_PASSWORD=$(rand_hex 24)" +echo "JWT_ACCESS_SECRET=$(rand_hex 64)" +echo "JWT_REFRESH_SECRET=$(rand_hex 64)" +echo "SECRETS_MASTER_KEY=$(rand_b64 32)" +echo "AMI_SECRET=$(rand_hex 24)" +echo "ARI_SECRET=$(rand_hex 24)"