Commit Graph

3 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
60e9f6838e feat: add Event Socket integration (b2bcall-fs-events)
- 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
2026-08-28 06:47:28 -03:00
d1addb0a31 feat: add typescript monorepo with pnpm workspaces and prisma database package
- root package.json + pnpm-workspace.yaml + tsconfig.base.json
- packages/types: canonical enums (TenantStatus, AgentState)
- packages/shared: re-exports types, base for future utilities
- packages/database: Prisma 7 schema with driver adapter (pg), initial
  tenants table matching agente.md section 29, first migration applied
2026-08-27 23:22:38 -03:00