feat: add structured versioned dialplan and asterisk admin

- packages/database: DialplanEntry + DialplanVersion (schema/config
  gerado, nunca dialplan cru vindo do usuario)
- apps/api/src/dialplan: DialplanService gera extensions a partir de
  entradas estruturadas (campos com allowlist estrita de caracteres),
  escreve em arquivo compartilhado via volume Docker 'dialplan-generated'
  entre api e asterisk, recarrega via AMI ('dialplan reload') e valida
  checando 'dialplan show <contexto>' por marcadores de erro. Falha na
  validacao dispara rollback automatico para a versao anterior; rollback
  manual tambem disponivel para qualquer versao no historico
- infrastructure/asterisk: extensions.conf agora inclui o arquivo gerado
  pela aplicacao; entrypoint garante que ele existe (vazio) no primeiro
  boot antes da primeira publicacao
- apps/api/src/asterisk-admin: status (AMI + heartbeat), module show, e
  diagnostico com ALLOWLIST ESTRITA de comandos exatos (nunca shell
  arbitraria) — agente.md secao 22

Testado ponta a ponta contra o Asterisk real: dialplan publicado fica
ativo imediatamente sem restart (confirmado via 'dialplan show'),
comando de diagnostico fora da allowlist rejeitado com 400.
This commit is contained in:
2026-08-27 13:14:50 -03:00
parent 8a41b5d2b2
commit cf3fc4b3ef
18 changed files with 789 additions and 10 deletions

View File

@@ -10,6 +10,9 @@ volumes:
asterisk-lib:
asterisk-log:
asterisk-spool:
# Compartilhado entre api e asterisk: a API gera o dialplan estruturado
# aqui (agente.md seção 21) e o Asterisk inclui via extensions.conf.
dialplan-generated:
services:
postgres:
@@ -106,6 +109,7 @@ services:
- asterisk-lib:/var/lib/asterisk
- asterisk-log:/var/log/asterisk
- asterisk-spool:/var/spool/asterisk
- dialplan-generated:/etc/asterisk-generated
healthcheck:
test: ["CMD-SHELL", "asterisk -rx 'core show uptime' | grep -q 'System uptime'"]
interval: 15s
@@ -145,6 +149,8 @@ services:
environment:
POSTGRES_HOST: postgres
REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379
volumes:
- dialplan-generated:/etc/asterisk-generated
# Sem "ports": só o Nginx (Fase 9) expõe HTTP ao mundo externo. Em dev,
# acessar via `docker compose exec` ou publicar temporariamente.
healthcheck: