2 Commits

Author SHA1 Message Date
c03c6d4eaa 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
2026-08-28 07:39:19 -03:00
d2ea83c06a feat: activate mod_xml_curl with b2bcall-fs-config (agente.md secao 26)
- apps/freeswitch-config (b2bcall-fs-config): Fastify service implementing
  the mod_xml_curl HTTP protocol (form-encoded POST -> XML response),
  containerized, no host port published
- reactivated mod_xml_curl in FreeSWITCH, binding restricted to
  directory|dialplan only (configuration was removed after testing showed
  it firing several unnecessary HTTP round-trips at boot for module
  configs we don't need dynamic — matches agente.md's own 'don't put every
  critical config through XML Curl' guidance)
- no extensions/dialplan tables exist yet (next phases), so the service
  always answers 'not found' for now — this phase only proves the wire
  protocol works without breaking the static vanilla config fallback
- verified end-to-end: user/8888 (nowhere) -> SUBSCRIBER_ABSENT via
  fs-config; user/1000 (static vanilla extension) -> USER_NOT_REGISTERED,
  proving FreeSWITCH correctly falls through to static XML when xml_curl
  says not found
- docs/XML_CURL.md, including the not-yet-authenticated endpoint note (fine
  while it only returns not-found; needs gateway-credentials before serving
  real directory/dialplan data)
2026-08-28 07:07:05 -03:00