外观
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/registrations | SIP 注册 | SIP 注册与在线状态专用页 |
/softphone | 软电话 | 浏览器 SIP over WebSocket / WebRTC 软电话:本地配置 SIP 注册、呼出、来电、接听、挂断、静音、保持与 DTMF |
/verto-videophone | 视频电话 | 浏览器 FreeSWITCH Verto 视频电话与会议:支持 1080P/720P 视频呼叫、前后置摄像头切换、画中画小窗、实时信令追踪与 WebRTC 统计 |
/outbound-call | 外呼测试 | 选择或输入业务编码,填写完整外呼参数,经 callflow-server 代理提交 originate,并展示最近一次同步结果 |
/tables/:name | — | 通用 FreeSWITCH 表浏览:选择任意表、搜索、分页、点击单元格查看完整内容(/tables/channels、/tables/sip_registrations 会重定向到上面的专用页) |
/tts-audition | TTS 调试 | TTS 健康检查、端点切换、speaker 试听与缓存命中观察 |
/asr-test | ASR 调试 | 上传音频、浏览器录音或实时检测,转为 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" },不再额外嵌套 llmChat。
外呼测试页的调用链为 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 及短期凭据过期时间。接口失败、启用响应为空或凭据过期时,新注册、拨号和接听会明确 失败,不会静默回退到空服务器列表。
Verto 视频通话与 WebRTC 会议(/verto-videophone)
视频电话页面基于 FreeSWITCH 原生 mod_verto 协议与 WebRTC 原生 API 构建(核心封装位于 @/composables/use-verto-client):
- 协议机制:采用纯标准的
JSON-RPC 2.0 over WebSocket协议与 FreeSWITCHverto.conf.xml交互(默认明文ws://<host>:8081或加密wss://<host>:8082)。支持verto.login鉴权、verto.invite呼叫、verto.answer应答、verto.bye挂断与verto.infoDTMF 发送。 - 自适应视讯交互:
- 沉浸式视频区:远端大屏视频自动适应比例,未接通或纯音频通话时显示优雅的音频波形占位;
- 画中画小窗:本地预览视频以右上角悬浮画中画(PiP)呈现,支持双击切换主副画面;
- 移动端与响应式优化:针对手机/平板竖屏场景进行了全屏沉浸式适配与安全区域贴合;
- 前后置摄像头切换:移动端提供
switchCamera快捷翻转,支持动态无缝重协商视频轨; - 全功能控制栏:麦克风静音、摄像头开关、前后摄翻转、屏幕共享、拨号盘(DTMF 发送)与一键挂断。
- 运维与排障支持:
- 信令日志抽屉:实时记录并高亮展示双方发送与接收的完整 JSON-RPC 2.0 报文;
- WebRTC 实时统计:实时采样并展示当前音视频的分辨率、帧率(FPS)、发送/接收码率(kbps)、丢包率与往返延迟(RTT)。
开发
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 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 注册,避免多个软电话 视图同时挂载时重复添加全局监听器。
开发服务器端口是 19913(quasar.config.ts → devServer.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_URL | /api | callflow-server API 根地址;值会在 Quasar/Vite 构建期固化 |
@/api/request.ts 只负责统一注入鉴权、 解包 { code, data, msg } 和处理请求错误;调用方直接从 @/api/auth、 @/api/callflow、@/api/freeswitch、@/api/speech 或 @/api/softphone 引入对应领域接口,公共契约从 @/api/types 引入。领域接口只传入相对于 API 根的 路径,例如 /auth/login。
API 根地址由 VITE_CALLFLOW_API_BASE_URL 在构建期注入,未设置或为空时使用同域 /api。 配置值推荐明确包含 /api;未包含时请求层会自动补齐,并清理首尾空白和末尾斜杠。 普通请求、文件上传和健康检查均通过该 API 根地址访问。
默认部署方式是在托管该前端的站点上将同域 /api 反向代理到 callflow-server (默认 http://127.0.0.1:9913),并保留上游请求中的 /api 路径。使用自定义代理前缀 或浏览器可访问的后端地址时,在构建前设置变量,例如:
bash
VITE_CALLFLOW_API_BASE_URL=/gateway/callflow/api bun run build执行构建后,产物位于 dist/spa/。API 根地址变化后必须重新构建前端。
开发模式下:
- 使用默认
/api时,quasar.config.ts会将其代理到http://localhost:9913。 - 设置完整
http(s)API 地址时,浏览器会直接访问该地址。
推荐本地联调方式:
- 前端:
http://localhost:19913 - 后端:
http://127.0.0.1:9913 - 启动命令:
bun run dev
管理台自身健康检查也统一走 /api/health。所有请求都会统一附带 Authorization: Bearer ...,并在收到 401 时清理 localStorage 登录态、带上原目标地址和 reason=expired 跳回登录页。
登录页行为说明:
- 登录成功后,会优先跳回
redirect指向的站内路径;外部 URL、协议串、异常值会统一回退到/。 reason=required时显示“请先登录后继续访问管理台”。reason=expired时显示“登录已过期,请重新登录”。- 如果后端不可达,页面仍会回到登录页,但提示会收敛为“无法连接登录服务”,避免与用户名/密码错误混淆。