跳到正文

callflow-webpage

callflow 平台的管理台前端(Quasar 2.21.x + @quasar/app-vite 3.x + Vue 3 + TypeScript)。它通过 HTTP 调用 apps/callflow-server

当前版本已内置登录页与路由守卫,使用 localStorage 保存 Bearer token。 未登录访问受保护页面时,会跳到 /login?redirect=...&reason=required;本地 token 过期或后续接口收到 401 时,会清理本地会话并跳到 /login?redirect=...&reason=expired

页面

路由导航用途
/总览仪表盘:FreeSWITCH 概览(活跃通道、当前通话、SIP 注册)以及近期 channels / registrations
/runtime-configs运行配置编辑 tts 与录音落盘目录 recording.directory,触发 callflow-esl 重载
/number-mappings业务与号码映射业务定义、业务级 JSON 配置与被叫号码 → 业务映射配置的管理(增删改)
/freeswitch/channels通道监控当前通话与通道状态专用页
/freeswitch/registrationsSIP 注册SIP 注册与在线状态专用页
/softphone软电话浏览器 SIP over WebSocket / WebRTC 软电话:本地配置 SIP 注册、呼出、来电、接听、挂断、静音、保持与 DTMF
/outbound-call外呼测试选择或输入业务编码,填写完整外呼参数,经 callflow-server 代理提交 originate,并展示最近一次同步结果
/tables/:name通用 FreeSWITCH 表浏览:选择任意表、搜索、分页、点击单元格查看完整内容(/tables/channels/tables/sip_registrations 会重定向到上面的专用页)
/tts-auditionTTS 调试TTS 健康检查、端点切换、speaker 试听与缓存命中观察
/asr-testASR 调试上传音频、浏览器录音或实时检测,转为 16kHz mono PCM16 WAV 后提交到 ASR 服务并展示 partial / final 结果
/login管理员登录页;未登录访问业务页会自动跳转到这里

TTS 试听页从 TTS 服务的 /health 读取 model.numSpeakers,为每个可用音色渲染一个卡片; 因此多说话人模型(例如 Bert-VITS2 vits-zh-hf-fanchen-C 或 Kokoro)会自动暴露全部说话人。

“业务与号码映射”页在业务列表显示配置状态,并可用 JSON 对象弹窗编辑 businessConfig;保存 {} 会清空业务层覆盖。号码映射弹窗维护更高优先级的 mappingConfig。callflow-esl 的最终优先级为“配置文件 < 业务配置 < 号码映射配置”, 但显式 business_code 外呼不会读取号码映射层。两个配置字段都直接填写业务字段, 例如 { "model": "qwen3.5:2b" },不再额外嵌套 ollamaChat

外呼测试页的调用链为 callflow-webpage -> callflow-server -> callflow-esl -> FreeSWITCH。业务列表加载失败时仍可手工填写 businessCode;页面不会暴露或覆盖 服务端配置的 ESL 地址和 token。结果中的“已接受”只代表 FreeSWITCH 接受 originate, 不代表被叫已经接通,页面也不追踪后续挂机或通话状态。

软电话页首版只实现 MicroSIP 替代能力,不接入 callflow-esl 软电话会话、转写、TTS 或外呼坐席流程。当前 MicroSIP 常见配置 1000@192.168.2.184:5060 / transport=udp 不能被浏览器直接使用;浏览器必须连接 FreeSWITCH Sofia profile 暴露的 SIP WebSocket / WebRTC 地址,例如 ws://192.168.2.184:5066。如果管理台 通过 HTTPS 访问,浏览器侧 SIP WebSocket 也必须使用 wss://。页面可选择把 SIP 配置保存到本机 localStorage,不会写入后端。

主布局右下角的悬浮软电话与 /softphone 页面共用同一个全局 SIP 实例 (use-global-sip-softphone),注册与通话状态在路由切换间保持。进入 /softphone 页面时悬浮窗自动隐藏,来电由页面 UI 承接;离开页面后悬浮窗恢复,若来电仍在等待 会重新弹出提醒。远端音频由全局实例自持的 audio 元素播放,不依赖悬浮窗展开或任何 页面挂载。两处「注销」都只注销 SIP 注册,不清除已保存的本机配置。

/softphone 注册配置区提供「自动注册」开关,偏好使用独立的 localStorage 键保存在当前浏览器,未设置或存储值无效时默认开启,以保持原有 行为。关闭开关会立即注销当前 SIP UA 并停止 JsSIP 后续重连,但不会删除已保存的 SIP 配置,也不影响页面和悬浮窗中的手动「注册」。重新开启时,若当前表单的必填 配置完整会立即注册,并继续遵循「保存到本机浏览器」选项;来电或通话期间不能切换 此开关。

软电话在注册、拨号和接听前会请求受登录鉴权保护的 GET /api/softphone/ice-config。仓库默认 turn.enabled=false,后端返回 { enabled: false, iceServers: [], expiresAt: null },浏览器据此使用 host candidate 完成本地直连;此状态不缓存,也不安排刷新。跨网络生产部署应同时设置 turn.enabled=true 和真实 shared secret,成功响应会包含 STUN、TURN/UDP、TURN/TCP 及短期凭据过期时间。接口失败、启用响应为空或凭据过期时,新注册、拨号和接听会明确 失败,不会静默回退到空服务器列表。

开发

bash
bun install          # 或:npm install
bun run dev          # quasar dev — http://localhost:19913
bun run build        # quasar build → dist/spa
bun run test         # Bun 单元测试
bun run lint         # oxfmt + oxlint --fix
bun run lint:check   # oxfmt --check + oxlint
bun run typecheck    # vue-tsc --noEmit

源码结构

  • src/pages/:文件路由页面,只负责页面级编排和数据加载。
  • src/components/:可复用展示组件;例如 IncomingCallDialog 只接收来电数据并抛出操作事件,不持有 SIP 状态。
  • src/composables/:浏览器能力与有状态业务逻辑。SIP 生命周期、来电音、悬浮面板定位和离页保护均在此收口。
  • src/services/:HTTP、鉴权会话和路由鉴权边界;页面不直接维护 token 或拼装认证 Header。
  • src/router/:文件路由集成、历史地址重定向和全局鉴权守卫。

软电话相关代码遵循“协议状态与展示分离”:use-sip-softphone 管理 JsSIP 和 WebRTC 会话,SoftphoneWidget 负责编排注册与路由状态,IncomingCallDialog 仅负责 来电交互展示。刷新/关闭保护统一通过 use-before-unload-guard 注册,避免多个软电话 视图同时挂载时重复添加全局监听器。

开发服务器端口是 19913quasar.config.tsdevServer.port)。路由使用 hash 模式,并已启用 filenameBasedRouting

  • 路由文件入口保留在 src/router/index.ts,守卫和运行时重定向仍在这里维护。
  • 实际页面路由来自 src/pages/ 目录结构,不再维护 src/router/routes.ts
  • 源码 import 统一使用 @/,不再使用 src/components/stores/ 等旧别名。
  • 旧地址 /callflow/callflow/number-mappings 会分别重定向到 /runtime-configs/number-mappings

配置

环境变量默认值用途
VITE_CALLFLOW_API_BASE_URLhttp://127.0.0.1:9913(由 quasar.config.ts 注入)callflow-server 管理 API 基址;空串表示继续走当前站点下的相对路径 /api/...

自定义 env 类型定义在项目根目录 env.d.ts。所有 API 调用都经过 @/api/index.ts 统一注入鉴权并解包 { code, data, msg },具体端点按领域拆分在 @/api/ 目录; 页面代码继续使用 /api/... 路径。

VITE_CALLFLOW_API_BASE_URL 在构建期注入,地址必须能被最终用户的浏览器访问, 不能填写只有构建主机可访问的地址。在本项目目录中使用 Bash 构建:

bash
VITE_CALLFLOW_API_BASE_URL=http://<callflow-api-host>:<port> bun run build

在 PowerShell 中构建:

powershell
$env:VITE_CALLFLOW_API_BASE_URL = 'http://<callflow-api-host>:<port>'
bun run build
Remove-Item Env:VITE_CALLFLOW_API_BASE_URL

构建产物位于 dist/spa/。未设置变量时使用默认值 http://127.0.0.1:9913;API 地址变化后必须重新构建,修改静态文件托管服务的 运行时环境变量不会改变已经生成的前端产物。

开发模式下:

  • 显式设置 VITE_CALLFLOW_API_BASE_URL=http://127.0.0.1:9913 之类的完整 http(s) 地址时,前端会直连该地址。
  • 如果把 VITE_CALLFLOW_API_BASE_URL 留空或设为相对路径(例如 /api), 前端仍然按相对路径访问,请确保当前站点同域下真的有 /api 可用。

推荐本地联调方式:

  • 前端:http://localhost:19913
  • 后端:http://127.0.0.1:9913
  • 启动命令:VITE_CALLFLOW_API_BASE_URL=http://127.0.0.1:9913 bun run dev

默认值集中定义在 quasar.config.ts,生产环境仍可将 VITE_CALLFLOW_API_BASE_URL 留空,配合同域部署或上游反向代理。管理台自身健康检查也统一走 /api/health。所有请求都会统一附带 Authorization: Bearer ...,并在收到 401 时清理 localStorage 登录态、带上原目标地址和 reason=expired 跳回登录页。

登录页行为说明:

  • 登录成功后,会优先跳回 redirect 指向的站内路径;外部 URL、协议串、异常值会统一回退到 /
  • reason=required 时显示“请先登录后继续访问管理台”。
  • reason=expired 时显示“登录已过期,请重新登录”。
  • 如果后端不可达,页面仍会回到登录页,但提示会收敛为“无法连接登录服务”,避免与用户名/密码错误混淆。

文档与代码在同一仓库维护,现有 Markdown 是唯一内容源。