- packages/telephony: TelephonyProvider interface (agente.md secao 25) and
FreeSwitchTelephonyProvider implementation over the 'esl' library
(actively maintained, TypeScript-native, built-in reconnect-with-backoff
satisfying secao 195); normalizeEslEvent() translates raw ESL events into
the internal vocabulary (secao 24)
- apps/freeswitch-events (b2bcall-fs-events): permanent ESL connection,
resubscribes on every reconnect, publishes normalized events to the
'b2bcall:events' Redis pub/sub channel; containerized (Dockerfile +
docker-compose service) since its whole job is reaching the freeswitch
container by internal hostname
- packages/shared: reusable createLogger() (structured JSON per secao 189),
fixed a BigInt serialization crash surfaced by the esl library's error
stats
- found and fixed a real FreeSWITCH 1.11 default: without an explicit
apply-inbound-acl, mod_event_socket silently rejects any non-loopback
connection ('Access Denied, go away.') even with the correct password —
added a dedicated ACL (loopback + the Docker Compose network range, never
0.0.0.0/0) in infrastructure/freeswitch/overrides/autoload_configs/
- verified end-to-end with a local loopback test call: CALL_CREATED ->
CALL_ANSWERED -> CALL_ENDED observed on the Redis channel with the
correct callUuid and hangup cause
- docs/EVENT_SOCKET.md
79 lines
2.2 KiB
YAML
79 lines
2.2 KiB
YAML
services:
|
|
postgres:
|
|
image: postgres:18-alpine
|
|
container_name: b2bcall-postgres
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_USER: ${POSTGRES_USER}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
POSTGRES_DB: ${POSTGRES_DB}
|
|
ports:
|
|
- "127.0.0.1:5432:5432"
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 10
|
|
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: b2bcall-redis
|
|
restart: unless-stopped
|
|
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
|
|
ports:
|
|
- "127.0.0.1:6379:6379"
|
|
volumes:
|
|
- redis_data:/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "redis-cli -a ${REDIS_PASSWORD} ping | grep -q PONG"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 10
|
|
|
|
freeswitch:
|
|
build:
|
|
context: ./infrastructure/freeswitch
|
|
secrets:
|
|
- freeswitch_pat
|
|
container_name: b2bcall-freeswitch
|
|
restart: unless-stopped
|
|
environment:
|
|
ESL_PASSWORD: ${ESL_PASSWORD}
|
|
# Nenhuma porta publicada no host: SIP/RTP ainda não têm troncos reais
|
|
# configurados, e o Event Socket (8021) só deve ser alcançável por outros
|
|
# containers na rede interna do compose (agente.md secao 22).
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "fs_cli -p \"$$ESL_PASSWORD\" -x status | grep -q 'is ready'"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
start_period: 20s
|
|
|
|
fs-events:
|
|
build:
|
|
context: .
|
|
dockerfile: apps/freeswitch-events/Dockerfile
|
|
container_name: b2bcall-fs-events
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- freeswitch
|
|
- redis
|
|
environment:
|
|
ESL_HOST: freeswitch
|
|
ESL_PORT: "8021"
|
|
ESL_PASSWORD: ${ESL_PASSWORD}
|
|
# Hostnames internos do compose (freeswitch/redis), diferente do
|
|
# REDIS_URL do .env que aponta pra localhost (uso pelo apps/api, que
|
|
# ainda roda no host) — ver docs/NETWORK_ARCHITECTURE.md.
|
|
REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379
|
|
|
|
secrets:
|
|
freeswitch_pat:
|
|
environment: FREESWITCH_PAT
|
|
|
|
volumes:
|
|
postgres_data:
|
|
redis_data:
|