新增: 跨端WS隧道脚手架(df-tunnel客户端+df-relay云中继)
df-tunnel:桌面端出站WS客户端(TunnelClient trait+骨架,穿NAT连云后端) df-relay:axum WS中继服务(转发小程序↔桌面端,广播骨架) 双crate独立消息骨架,不依赖src-tauri/df-types避跨crate强耦合 workspace crates/* glob自动注册,Phase2填充WS握手/鉴权/重连/路由
This commit is contained in:
85
crates/df-relay/src/broadcast.rs
Normal file
85
crates/df-relay/src/broadcast.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
//! df-relay 广播消息骨架
|
||||
//!
|
||||
//! 设计依据:设计文档「Layer2」—— 云后端纯转发,无业务逻辑。
|
||||
//! 中继消息骨架定义「谁发给谁」的路由语义:
|
||||
//! - `DeviceEvent`:桌面端事件 → 广播给该 device 绑定的小程序
|
||||
//! - `MiniappCommand`:小程序指令 → 转发给该 device 绑定的桌面端
|
||||
//! - `Control`:控制面消息(配对/心跳/在线状态)
|
||||
//!
|
||||
//! 与 df-tunnel::TunnelMessage 区分:
|
||||
//! - TunnelMessage 是隧道两端(桌面↔云)的传输单元
|
||||
//! - BroadcastMessage 是云后端内部的路由单元(标识来源/去向/广播范围)
|
||||
//!
|
||||
//! 脚手架阶段仅定义 enum,Phase2 填路由表 + 广播实现。
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// 客户端类型(用于路由判断)
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ClientKind {
|
||||
/// 桌面端(出站长连接,事件真相源)
|
||||
Device,
|
||||
/// 小程序(远程操作发起方)
|
||||
Miniapp,
|
||||
}
|
||||
|
||||
/// 中继路由消息骨架
|
||||
///
|
||||
/// 每条消息携带 `device_id`(配对绑定键),云后端按 device_id
|
||||
/// 找到绑定的对端连接进行转发/广播。
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BroadcastMessage {
|
||||
/// 配对绑定的设备 ID(桌面端首次配置时生成)
|
||||
pub device_id: String,
|
||||
/// 消息来源(用于鉴权校验:device 不能发 MiniappCommand)
|
||||
pub from: ClientKind,
|
||||
/// 消息载荷(原始 JSON,转发时不解析业务字段,保持轻量)
|
||||
pub payload: serde_json::Value,
|
||||
/// 时间戳(毫秒,用于冲突处理 —— 桌面端为真相源,但保留时间戳辅助判断)
|
||||
pub ts: i64,
|
||||
}
|
||||
|
||||
impl BroadcastMessage {
|
||||
/// 便捷构造:device 发出的事件
|
||||
pub fn from_device(device_id: impl Into<String>, payload: serde_json::Value, ts: i64) -> Self {
|
||||
Self {
|
||||
device_id: device_id.into(),
|
||||
from: ClientKind::Device,
|
||||
payload,
|
||||
ts,
|
||||
}
|
||||
}
|
||||
|
||||
/// 便捷构造:小程序发出的指令
|
||||
pub fn from_miniapp(device_id: impl Into<String>, payload: serde_json::Value, ts: i64) -> Self {
|
||||
Self {
|
||||
device_id: device_id.into(),
|
||||
from: ClientKind::Miniapp,
|
||||
payload,
|
||||
ts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 客户端在线状态(用于离线降级提示)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PresenceState {
|
||||
/// 在线
|
||||
Online,
|
||||
/// 离线(对端可缓存指令待重连)
|
||||
Offline,
|
||||
}
|
||||
|
||||
/// 控制面消息(配对绑定 / 心跳 / 在线状态广播)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind", rename_all = "snake_case")]
|
||||
pub enum ControlMessage {
|
||||
/// 配对绑定请求(小程序持 token 申请绑定 device_id)
|
||||
Pair { device_id: String, token: String },
|
||||
/// 心跳(保活 + 在线确认)
|
||||
Heartbeat { device_id: String },
|
||||
/// 在线状态变更通知
|
||||
Presence { device_id: String, state: PresenceState },
|
||||
}
|
||||
Reference in New Issue
Block a user