Skip to content

WebSocket 事件清单

管理端实时通道入口为 /api/ws(access token 经 Sec-WebSocket-Protocol 子协议传递)。消息契约以 packages/shared/src/platform/types.ts 中的 WsMessage 为准,服务端管理器位于 packages/server/src/lib/ws-manager.ts

连接认证

浏览器 WebSocket 无法自定义请求头,access token 经子协议头传递(@zenith/shared/platformwsAuthProtocols(token) 生成),不再放进 URL 查询串,避免落入代理 / 访问日志:

text
GET /api/ws
Sec-WebSocket-Protocol: zenith-auth, eyJ...

服务端只回显 zenith-authWebSocketServer.handleProtocols),绝不把 token 写回握手响应;?token= 查询串已不再接受。升级鉴权(lib/ws-auth.ts)与 HTTP authMiddleware 同一口径:拒绝会员 / refresh token、实时校验用户与租户状态(checkAdminJwtSubject)、检查吊销黑名单;三个 WebSocket 端点(/api/ws/api/ws/terminal/api/ws/terminal-monitor)共用。

认证失败关闭连接:4001 Unauthorized。Redis 检查异常时 fail-open。连接成功后按用户维度建立本地连接集合,并通过 Redis pub/sub 在 api 进程之间扇出;worker 产生的任务进度、站内信、工作流事件与 IoT 送达帧也经同一链路到达持有浏览器或设备连接的节点。扇出语义为 at-most-once,Redis 不可用时实时增量可能丢弃,客户端重连后应回源补齐。

监控集群视图

GET /api/monitor/ws 返回的是集群合并视图:各 api 节点按 30 秒节拍发布本地 WS 快照(wsStats 信封,含连接明细、计数与消息采样),接收方按发送方归档成镜像后合并——计数器按节点求和、在线用户去重、明细按时间截断(断开 50 / 消息 200)。镜像 90 秒未刷新视为节点失联并移出视图;单进程部署时退化为本进程快照。

入站帧约束

  • 单帧上限 64 KiB(WebSocketServer.maxPayload),超限直接断开;每连接令牌桶限速(60 帧 / 秒,突发 120),超额帧丢弃。
  • 入站帧经 zod 校验,只接受 pingchat:typingrtc:* 有限类型,其余静默丢弃。
  • 身份字段由服务端覆写:chat:typinguserId / nicknamertc:*from 一律取连接的认证主体,客户端声明无效。
  • chat:typingrtc:* 要求发送者是目标会话成员;callIdrtc:invite / rtc:join 时绑定到所属会话,后续信令只在该会话成员之间中继,定向目标 to 也必须是该会话成员。

事件类型

类型payload说明
announcement:newAnnouncement@zenith/shared/messaging新公告。壳层把它作为未读头插进「最近公告」缓存并未读 +1;updated / deleted / read / read-all 同样按 id 局部更新缓存,均不触发请求
announcement:updatedAnnouncement公告更新
announcement:deleted{ id: number }公告删除
announcement:read{ id: number }公告已读
announcement:read-all{}公告全部已读
in-app-message:newInAppMessage@zenith/shared/messaging新站内信。载荷即收件人自己的真实行(id / createdAt 来自 insert … returning),客户端直接写入铃铛缓存并未读 +1,不回源;群发按收件人各推一份(scheduleSendPerUser,跨进程仍是一封 perUser 信封)。断线重连后客户端一次性重拉铃铛列表 / 未读数 / 最近公告
in-app-message:read{ id: number }站内信已读
in-app-message:read-all{}站内信全部已读
in-app-message:deleted{ id: number }站内信删除
session:force-logout{ reason: string; code?: SessionRevokeReason; by?: { client, ip, location, browser, os, at } }强制下线;code = concurrent-login(会话并发挤下线)时 by 携带新登录信息,前端据此提示「已在其他设备登录」并落到登录页横幅
chat:messageunknown聊天消息
chat:recall{ messageId: number; conversationId: number }撤回消息
chat:read{ conversationId: number; userId: number }会话已读
chat:member-join{ conversationId: number; userId: number }成员加入
chat:member-leave{ conversationId: number; userId: number }成员离开
chat:group-update{ conversationId: number }群信息更新
chat:member-update{ conversationId: number }群成员更新
chat:join-requestunknown入群申请
chat:conversation-removed{ conversationId: number }会话被移除
chat:typing{ conversationId: number; userId: number; typing: boolean }输入状态
chat:reactionunknown表情回应
chat:editunknown编辑消息
chat:vote-updateunknown投票更新
chat:presenceChatPresence[]@zenith/shared/chat在线状态变更;服务端按 1 秒窗口合并后批量推送。在线判定是集群合并视图:用户在任一 api 进程有连接即在线,各进程经扇出交换本地增量与 30 秒全量快照,远端镜像 90 秒无刷新视为该进程离线
channel:messageChannelMessage@zenith/shared/messaging频道消息
channel:message-retractunknown频道消息撤回
channel:cs-messageunknown客服消息
rtc:inviteunknown音视频邀请
rtc:acceptunknown接听
rtc:rejectunknown拒绝
rtc:busyunknown忙线
rtc:cancelunknown取消
rtc:joinunknown加入通话房间
rtc:room-participantsunknown房间成员列表
rtc:leaveunknown离开通话
rtc:offerunknownWebRTC offer
rtc:answerunknownWebRTC answer
rtc:iceunknownICE candidate
workflow:taskCreatedunknown工作流待办创建
workflow:taskFinishedunknown工作流任务完成
workflow:instanceFinishedunknown流程实例完成
payment:successunknown支付成功
payment:closedunknown支付关闭
payment:failedunknown支付失败
payment:refundedunknown退款成功
payment:refund-failedunknown退款失败
task:progressunknown任务进度
mp-kf:session-newunknown公众号客服新会话
mp-kf:session-updateunknown公众号客服会话更新
mp-kf:session-messageunknown公众号客服消息
analytics:ingestunknown埋点摄取通知
analytics:config-updatedunknown埋点配置更新

WebRTC 信令

rtc:* 事件由 rtc-manager.ts 和聊天路由协作。rtc:invite / rtc:join 登记房间并把 callId 绑定到会话(同一 callId 换会话加入会被拒绝),rtc:join 向加入者下发 rtc:room-participantsrtc:accept 把被叫加入房间;其它信令必须引用已登记的 callId,按 to(须为该会话成员)定向或向会话成员广播,from 由服务端按连接主体写入。rtc:reject / rtc:busy / rtc:cancel / rtc:leave 与断线都会清理房间成员,空房间与 6 小时无活动的房间自动回收。

独立 WebSocket 端点

以下端点是专用协议,不属于 WsMessage 清单:

  • /api/ws/terminal
  • /api/ws/terminal-monitor

客户端处理建议

  • 使用 type 做分发,payload 按事件类型收窄。
  • 未识别事件应忽略并保留日志,避免客户端因服务端扩展中断。
  • 断线后重新拉取公告、站内信、聊天会话等可缓存状态,WebSocket 只作为实时增量通知。

Built with VitePress for local documentation preview.