- 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.
205 lines
5.8 KiB
YAML
205 lines
5.8 KiB
YAML
name: b2bcall
|
|
|
|
networks:
|
|
b2bcall-net:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
postgres-data:
|
|
redis-data:
|
|
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:
|
|
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
|
|
ports:
|
|
# Publicado SOMENTE em loopback do host: o container do Asterisk usa
|
|
# network_mode: host (necessário para RTP) e por isso não enxerga a
|
|
# rede Docker interna por nome — precisa alcançar o Postgres via
|
|
# 127.0.0.1. Isso nunca fica acessível pela LAN/Internet (não é 0.0.0.0).
|
|
- "127.0.0.1:${POSTGRES_PORT:-5432}:5432"
|
|
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
|
|
|
|
asterisk:
|
|
build:
|
|
context: .
|
|
dockerfile: infrastructure/docker/asterisk.Dockerfile
|
|
image: b2bcall-asterisk:22.10.1
|
|
container_name: b2bcall-asterisk
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
# RTP exige uma faixa larga de portas UDP — inviável de mapear uma a uma
|
|
# em bridge network, por isso host mode (ver docs/ARCHITECTURE.md 3.1).
|
|
# AMI (5038), ARI (8088) e SIP (5060) ficam protegidos pelo nftables
|
|
# (infrastructure/nftables/b2bcall.nft), que bloqueia acesso pela LAN.
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
# O Asterisk (host network) alcança o Postgres via loopback do host,
|
|
# não pelo nome do container (ver publicação de porta do serviço postgres).
|
|
ASTERISK_DB_HOST: ${ASTERISK_DB_HOST:-127.0.0.1}
|
|
volumes:
|
|
- ./infrastructure/asterisk/config:/etc/asterisk-templates:ro
|
|
- 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
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 20s
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 400M
|
|
|
|
api:
|
|
build:
|
|
context: .
|
|
dockerfile: infrastructure/docker/api.Dockerfile
|
|
image: b2bcall-api:0.1.0
|
|
container_name: b2bcall-api
|
|
restart: unless-stopped
|
|
networks:
|
|
- b2bcall-net
|
|
# Permite alcançar o Asterisk (network_mode: host) via AMI/ARI a partir
|
|
# da rede bridge — ver docs/ARCHITECTURE.md 3.2.1.
|
|
extra_hosts:
|
|
- "host.docker.internal:host-gateway"
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
env_file:
|
|
- .env
|
|
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:
|
|
test: ["CMD", "node", "-e", "require('http').get('http://127.0.0.1:3000/api/health', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
|
|
interval: 15s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 15s
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 300M
|
|
|
|
asterisk-events:
|
|
build:
|
|
context: .
|
|
dockerfile: infrastructure/docker/asterisk-events.Dockerfile
|
|
image: b2bcall-asterisk-events:0.1.0
|
|
container_name: b2bcall-asterisk-events
|
|
restart: unless-stopped
|
|
networks:
|
|
- b2bcall-net
|
|
extra_hosts:
|
|
- "host.docker.internal:host-gateway"
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
asterisk:
|
|
condition: service_healthy
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
ASTERISK_HOST: host.docker.internal
|
|
POSTGRES_HOST: postgres
|
|
REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 150M
|