Files
b2bcall/docs/DATABASE.md
B2BCall Bootstrap 80e72881b2 feat: Fase 9/10 — métricas, scripts operacionais, callback/wrap-up e aceite final
Fase 9 (segurança e produção):
- GET /api/metrics: endpoint Prometheus com métricas reais (chamadas,
  agentes, filas, CPS por campanha), protegido por permissão
- scripts/backup.sh, restore.sh, healthcheck.sh, install.sh, update.sh —
  testados contra o ambiente real (backup.sh e healthcheck.sh rodados de
  verdade; install.sh/update.sh validados por inspeção, ambiente atual já
  provisionado)
- POST /api/agent-console/dispose: aplica disposição de chamada de verdade
  (lacuna deixada aberta desde a Fase 6), com ações CALLBACK (agenda
  retorno) e DO_NOT_CALL (suprime automaticamente)
- apps/dialer-worker/src/callback-sweep.ts: reativa leads com callback
  vencido; GET /api/callbacks para consulta
- apps/dialer-worker/src/wrap-up-sweep.ts: transição automática
  WRAP_UP -> AVAILABLE + despausa real na fila do Asterisk. Exigiu corrigir
  main.ts para conectar ao AMI mesmo em DIALER_SIMULATION=true
  (DIALER_SIMULATION deve impedir só originação de chamada, não ações
  administrativas de fila)
- nftables revisado (sem alterações necessárias)
- Documentação completa: INSTALL, OPERATIONS, BACKUP_RESTORE, SECURITY,
  DATABASE, API, ASTERISK, OPENSIPS (não implementado, motivo
  documentado), TROUBLESHOOTING
- README.md e CHANGELOG.md reescritos

Fase 10 (testes e aceite):
- Quality gate completo executado: build/typecheck (7 workspaces), lint,
  46 testes unitários, docker compose config/ps, healthcheck — tudo verde
- Aceite de segurança (seção 92): 13 itens verificados ao vivo contra o
  sistema real, não só por inspeção de código
- Aceite Asterisk (seção 93): os 5 comandos executados e documentados,
  comunicação API->AMI->Asterisk validada
- docs/RELATORIO_FINAL.md: relatório final no formato da seção 96

Todos os fixtures de teste desta fase foram removidos/desativados ao
final. Credenciais de acesso entregues separadamente em CREDENCIAIS.txt
(fora do git, nunca versionado).
2026-08-27 19:16:14 -03:00

3.6 KiB

B2BCall — Banco de Dados

PostgreSQL 17, um único cluster/instância com dois schemas que nunca se misturam (ver docs/ARCHITECTURE.md §3.3):

  • public — domínio da aplicação (este documento). Gerenciado 100% por Prisma (packages/database/prisma/schema.prisma + prisma/migrations/).
  • asterisk — objetos de Realtime do Asterisk (ps_endpoints, ps_auths, ps_aors, ps_contacts, ps_endpoint_id_ips, ps_registrations, cdr, cel). Criado por infrastructure/postgres/init/002-asterisk-realtime.sql e escrito exclusivamente por apps/api/src/telephony/pjsip-realtime.service.ts (nunca por SQL solto em outro lugar da aplicação).

Modelos (schema public)

Modelo Propósito
User, Session, PasswordResetToken Autenticação (Fase 3)
Role, Permission, UserRole, RolePermission RBAC
AuditLog Auditoria de todas as ações sensíveis
Trunk, Extension, ExtensionState Telefonia (Fase 4) — Trunk/Extension são o CRUD da aplicação; os objetos PJSIP reais ficam no schema asterisk
DialplanEntry, DialplanVersion Dialplan estruturado versionado
Queue, QueueMember, PauseReason Call Center (Fase 5)
Agent, AgentSession, AgentStateEvent, AgentPauseEvent Máquina de estados do agente — sempre exatamente um AgentStateEvent aberto (ended_at IS NULL) por agente
Campaign, LeadImport, Lead, DialAttempt Discador preditivo (Fase 6). DialAttempt é a "chamada" como state machine (seção 36) — id próprio, nunca o UNIQUEID do Asterisk como PK de negócio
CallDisposition, Callback Disposição de chamada e agendamento de retorno
SuppressionEntry Lista de bloqueio (DNC)
ComplianceSettings Parâmetros de compliance (singleton)

Decisões importantes

  • Prisma como ORM (não Drizzle/TypeORM) — ver justificativa em docs/ARCHITECTURE.md §3.6.1.
  • Índices de alto volume: Lead(campaignId, status, nextAttemptAt), DialAttempt por campaignId/state/asteriskUniqueId, AuditLog(createdAt), AuditLog(entityType, entityId). Consultas de relatório usam paginação server-side sempre (nunca SELECT * sem filtro — seção 54).
  • Reserva concorrente de leads: SELECT ... FOR UPDATE SKIP LOCKED via $queryRaw em apps/dialer-worker/src/lead-repository.ts — não modelado via Prisma de alto nível porque a transição atômica READY→RESERVED precisa de controle fino de lock que o ORM não expõe com segurança suficiente sob concorrência real.
  • Particionamento: avaliado, não implementado (volume atual não justifica). DialAttempt/AuditLog são os candidatos naturais quando o volume crescer — desenhados para permitir particionamento por started_at/created_at sem migração destrutiva.
  • Segredos: Trunk.secretEncrypted e Extension.sipPasswordEncrypted são cifrados com AES-256-GCM (packages/shared/src/secret-crypto.ts), chave mestra em SECRETS_MASTER_KEY (.env, nunca no banco).

Migrations

cd packages/database
pnpm exec prisma migrate dev --name <descrição>   # desenvolvimento
pnpm exec prisma migrate deploy                     # produção (usado por scripts/update.sh)
pnpm run seed                                        # permissões, perfis, bootstrap super_admin

Nenhuma alteração de schema é feita manualmente em produção — sempre via migration versionada e commitada (seção 85).

Backup / Restore

Ver docs/BACKUP_RESTORE.md. Resumo: pg_dump -Fc do banco inteiro cobre public e asterisk num único arquivo, já que ambos vivem na mesma instância Postgres.