- apps/api/src/telephony: TelephonyModule (conexao AMI da API para
comandos de controle) + PjsipRealtimeService (unico ponto de escrita nas
tabelas realtime do schema 'asterisk' via Prisma $executeRaw
parametrizado)
- apps/api/src/trunks: CRUD completo (tipos IP/AUTH/REGISTRATION), secret
cifrado em repouso (AES-256-GCM), endpoint de teste de status via
PJSIPShowContacts/pjsip show registration
- apps/api/src/extensions: CRUD completo, senha SIP gerada
automaticamente, endpoint de reset, nunca reexibe a senha apos a criacao
- apps/api/src/monitoring: GET /api/monitoring/extensions com prioridade
de cor (agente.md secao 15) a partir do ExtensionState alimentado por
apps/asterisk-events
- health check agora reporta status real do Asterisk/AMI via heartbeat do
asterisk-events no Redis, sem precisar de conexao AMI propria para isso
Testado ponta a ponta contra o Asterisk real: ramal e tronco criados via
API aparecem imediatamente via 'pjsip show endpoint' sem reload (realtime
funcionando), secret nunca retornado pela API, exclusao em cascata
confirmada (ps_endpoints/ps_auths/ps_aors/ps_contacts/ps_registrations).
- packages/telephony: cliente AMI proprio sobre TCP puro (sem dependencia
de terceiros pouco mantida) + interface TelephonyProvider +
AsteriskTelephonyProvider (Originate, Hangup, QueuePause/Add/Remove,
QueueStatus, ExtensionState, DeviceState, PJSIPShowEndpoints/Contacts,
Reload, runCommand, stream de eventos). Testado contra o Asterisk real —
o formato de resposta do Command mudou entre versoes do Asterisk
(headers 'Output:' repetidos em vez de 'Response: Follows'/'--END
COMMAND--'), corrigido apos inspecionar os bytes crus do protocolo
- apps/asterisk-events: worker dedicado a manter a conexao AMI viva,
normalizar eventos (Newchannel, DialBegin/End, Hangup, DeviceStateChange,
ContactStatus, eventos de fila/agente), persistir ExtensionState no
Postgres e publicar em Redis pub/sub para consumo em tempo real.
Containerizado, alcanca o Asterisk (host network) via
host.docker.internal a partir da rede bridge. Heartbeat no Redis para
health check
- packages/database: novos modelos Trunk, Extension, ExtensionState
(migration aplicada)
- packages/shared: secret-crypto.ts (AES-256-GCM para credenciais de trunk
e senha SIP em repouso, master key externa ao banco)
Testado ponta a ponta: chamada real originada -> eventos normalizados
recebidos via Redis SUBSCRIBE, heartbeat renovando no TTL correto.
- 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.
- 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.
- 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