- 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.
199 lines
5.6 KiB
YAML
199 lines
5.6 KiB
YAML
name: b2bcall
|
|
|
|
networks:
|
|
b2bcall-net:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
postgres-data:
|
|
redis-data:
|
|
asterisk-lib:
|
|
asterisk-log:
|
|
asterisk-spool:
|
|
|
|
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
|
|
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
|
|
# 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
|