feat: implement Extensions with real FreeSWITCH directory integration
- extensions table (tenant-scoped, RLS): number, sip_password_enc
(AES-256-GCM via packages/shared/src/crypto.ts), caller_id, context,
sofia_profile, codecs, max_registrations
- apps/api/src/extensions: CRUD (POST/GET/GET:id/DELETE), protected by a
new generic PermissionGuard (@RequirePermission decorator), tenant
resolved only from the JWT (never trusted from the client)
- SIP password is returned in plaintext only once, in the create response;
toPublicExtension() explicitly destructures the encrypted field out
(not a spread) so it can't leak by accident
- b2bcall-fs-config now resolves real directory data: Tenant.telephonyDomain
-> Extension.number, decrypts the password, builds proper directory XML
including a dial-string param (missing it caused originate to fail with
MANDATORY_IE_MISSING instead of the expected USER_NOT_REGISTERED)
- pinned FreeSWITCH's 357737{domain} to a stable value (b2bcall.local) via a
vars.xml patch in the Dockerfile -- it previously used the container's
dynamic IP, which could never match a stored telephony_domain
- added HTTP Basic auth between FreeSWITCH and fs-config
(gateway-credentials, timingSafeEqual comparison) now that the service
returns real secret data, closing the gap flagged as pending in the XML
Curl phase instead of leaving it open
- found and fixed: PermissionGuard's constructor-injected Reflector came
back undefined at runtime under tsx/esbuild (unreliable cross-file
decorator metadata emission) -- fixed with an explicit @Inject(Reflector);
worth watching for in future guards/services run via tsx
- verified end-to-end: create extension -> originate user/<ext> reports
USER_NOT_REGISTERED (found, not registered) -> delete -> back to
SUBSCRIBER_ABSENT (not found); password never reappears in any GET;
unauthenticated fs-config requests get 401
- docs/EXTENSIONS.md
This commit is contained in:
26
TODO.md
26
TODO.md
@@ -85,7 +85,31 @@
|
||||
— testado ponta a ponta com curl (login, refresh rotation, logout, RBAC, 401/403/429)
|
||||
- [ ] Password reset por e-mail — depende de SMTP configurado
|
||||
|
||||
## PHASE 08+ — ver `agente.md` seções 39 em diante (Extensions, Trunks, Dialplan, Call Center,
|
||||
## PHASE 08 — Extensions (agente.md secao 39-40, 178)
|
||||
- [x] Tabela `extensions` (tenant-scoped, RLS) — number, sip_password_enc,
|
||||
caller_id, context, sofia_profile, codecs, max_registrations
|
||||
- [x] `packages/shared/src/crypto.ts`: AES-256-GCM (senha SIP cifrada em
|
||||
repouso), `generateStrongPassword()`, `maskSecret()`
|
||||
- [x] `apps/api/src/extensions`: CRUD (POST/GET/GET:id/DELETE), RBAC via novo
|
||||
`PermissionGuard` genérico (`@RequirePermission`), tenant só do JWT
|
||||
- [x] Senha SIP só aparece em texto puro na resposta do POST, nunca depois
|
||||
(destructuring explícito, não spread — evita vazamento por acidente)
|
||||
- [x] `b2bcall-fs-config` resolve directory real: Tenant.telephonyDomain →
|
||||
Extension.number, decifra a senha, monta XML com dial-string
|
||||
- [x] `Tenant.telephonyDomain` fixo (`b2bcall.local`) via patch no `vars.xml`
|
||||
do FreeSWITCH — antes usava o IP dinâmico do container, instável
|
||||
- [x] HTTP Basic auth entre FreeSWITCH e fs-config (`gateway-credentials`,
|
||||
timingSafeEqual) — adicionada nesta mesma fase, não deixada pendente
|
||||
- [x] Testado ponta a ponta: criar ramal → `user/1500` dá USER_NOT_REGISTERED
|
||||
(achou, sem telefone) → deletar → volta a SUBSCRIBER_ABSENT
|
||||
- [x] Achado: `PermissionGuard` injetando `Reflector` via construtor dava
|
||||
`undefined` em runtime rodando via `tsx`/esbuild (emissão de metadata
|
||||
de tipo não é 100% confiável cross-file) — corrigido com `@Inject()`
|
||||
explícito; atenção pra isso em guards/services futuros
|
||||
- [ ] 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,
|
||||
Predictive Dialer, Recordings, AI, Billing, Frontend, Reports, Security, Tests)
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user