feat: implement Trunks with real FreeSWITCH gateway sync
- trunks table (tenant-scoped, RLS): host/proxy/realm, register, username/password_enc (AES-256-GCM), dtmf_mode, ping, transport, and a status/status_updated_at pair meant to be driven by FreeSWITCH events - apps/api/src/trunks: CRUD (POST/GET/GET:id/DELETE), same RBAC/tenant pattern as Extensions, password never exposed in any GET - packages/telephony: buildGatewayXml() generates a Sofia gateway XML file - b2bcall-fs-config now writes sip_profiles/external/<trunk_id>.xml (shared Docker volume with FreeSWITCH -- the vanilla external profile already includes external/*.xml) and runs 'sofia profile external rescan' over ESL; syncs on boot and on demand via Redis pub/sub (b2bcall:trunks:sync), since apps/api runs on the host and fs-config has no port published to reach directly - added FreeSwitchTelephonyProvider.waitUntilConnected() to fix a startup race: the first sync ran before the ESL connection had settled, logging a harmless but noisy error - verified end-to-end with a fake host: create trunk -> gateway file written -> FreeSWITCH shows the real gateway (FAIL_WAIT, expected) -> delete -> file removed (cleanup also correctly swept the stale 'example.com' gateway that had been copied into the volume from the vanilla image) - apps/freeswitch-events/src/trunk-status.ts: written to update Trunk.status from sofia::gateway_state events, using the same normalizeEslEvent path already proven for CHANNEL_* events - KNOWN GAP, documented rather than glossed over: monitored fs-events for ~90s while the gateway visibly transitioned states in FreeSWITCH (FAIL_WAIT/DOWN) and no sofia::gateway_state event was observed arriving. CUSTOM/sofia::* events have not actually been proven working end-to-end in this session -- only CHANNEL_* events have been. Needs verification against a real SIP target before the status auto-update can be trusted in production. See docs/TRUNKS.md and TODO.md. - docs/TRUNKS.md
This commit is contained in:
33
TODO.md
33
TODO.md
@@ -109,7 +109,38 @@
|
||||
- [ ] Quota de ramais — depende de Plans/Entitlements (não existe ainda)
|
||||
- [ ] Multi-domínio real por tenant — hoje só um domínio fixo pra todos
|
||||
|
||||
## PHASE 09+ — ver `agente.md` seções 41 em diante (Trunks, Dialplan, Call Center,
|
||||
## PHASE 09 — Trunks (agente.md secao 41-42)
|
||||
- [x] Tabela `trunks` (tenant-scoped, RLS) — host/proxy/realm, register,
|
||||
username/password_enc (AES-256-GCM), dtmf_mode, ping, transport,
|
||||
status/status_updated_at
|
||||
- [x] `apps/api/src/trunks`: CRUD (POST/GET/GET:id/DELETE), mesmo padrão de
|
||||
RBAC/tenant de Extensions, senha nunca exposta em nenhum GET
|
||||
- [x] `packages/telephony`: `buildGatewayXml()` (XML de gateway Sofia)
|
||||
- [x] `b2bcall-fs-config`: gera `sip_profiles/external/<trunk_id>.xml` (volume
|
||||
Docker compartilhado com o FreeSWITCH) e roda `sofia profile external
|
||||
rescan` via ESL — sincroniza no boot e sob demanda via Redis pub/sub
|
||||
(`b2bcall:trunks:sync`, publicado pela API a cada create/delete)
|
||||
- [x] Achado: 1º sync no boot corria antes da conexão ESL terminar de se
|
||||
estabelecer (erro cosmético) — corrigido com
|
||||
`FreeSwitchTelephonyProvider.waitUntilConnected()`
|
||||
- [x] Testado ponta a ponta com host fake: criar trunk → arquivo gerado →
|
||||
`sofia status gateway` mostra o gateway real (FAIL_WAIT, esperado) →
|
||||
deletar → arquivo removido (limpeza também tirou o `example.com` da
|
||||
vanilla que tinha sido copiado pro volume — comportamento correto)
|
||||
- [ ] **Lacuna real, não resolvida**: `Trunk.status` deveria ser atualizado
|
||||
via eventos `sofia::gateway_state` (código escrito em
|
||||
`apps/freeswitch-events/src/trunk-status.ts`, baseado no mesmo
|
||||
`normalizeEslEvent` já testado pra eventos CHANNEL_*), mas o evento
|
||||
**não foi observado chegando** em ~90s de monitoramento mesmo com o
|
||||
gateway mudando de estado de verdade no FreeSWITCH (FAIL_WAIT/DOWN).
|
||||
Os eventos `sofia::*` (CUSTOM) nunca foram provados funcionando nesta
|
||||
sessão — só CHANNEL_* foi verificado de ponta a ponta até agora.
|
||||
Precisa de investigação com um alvo SIP real (outro FreeSWITCH, por
|
||||
exemplo) antes de confiar em atualização automática de status em
|
||||
produção. Ver docs/TRUNKS.md.
|
||||
- [ ] Quota de troncos — depende de Plans/Entitlements (não existe ainda)
|
||||
|
||||
## PHASE 10+ — ver `agente.md` seções 43 em diante (Dialplan, Call Center,
|
||||
Predictive Dialer, Recordings, AI, Billing, Frontend, Reports, Security, Tests)
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user