# 架构设计 ## 1. 分层模型 ``` ┌─────────────────────────────────────────────┐ │ React 前端 (src/) │ │ Stores (Zustand) · Components · Pages │ │ 只负责视图渲染与状态流转,不解析 shell 输出 │ └──────────────────┬──────────────────────────┘ │ invoke(请求/响应) · event(流式) ┌──────────────────▼──────────────────────────┐ │ Tauri 命令层 (commands/) │ │ host · session · monitor · sftp · logging │ │ 薄封装:参数校验 → 调用 core → 返回模型 │ └──────────────────┬──────────────────────────┘ ┌──────────────────▼──────────────────────────┐ │ Core 服务层 (core/) │ │ session_manager · terminal_service │ │ monitor_service · sftp_service │ │ ssh_transport · host_service · monitor_worker│ └──────────────────┬──────────────────────────┘ ┌──────────────────▼──────────────────────────┐ │ 存储 (storage/) · 模型 (models/) · 错误 (errors/)│ │ host_store · secure_store · app_error │ └─────────────────────────────────────────────┘ ``` ## 2. 核心原则 ### Session ≠ UI - `SessionManager` 持有真实 SSH 会话(`SessionHandle`),键为 `sessionId` - 前端标签页(`TerminalTabs`)只是视图,`activeView` 指向 sessionId 或 `'home'` - 关闭标签页时只调 `close_session`,会话生命周期由后端统一管理 ### 通信模型 - **invoke**:请求/响应。如 `open_session`、`sftp_list_dir`、`write_terminal`、`set_log_level` - **event**:流式推送。如 `terminal:data`、`session:status`、`monitor:snapshot` - 所有 payload 均为结构化 JSON(camelCase),前后端类型定义一一对应(Rust serde `rename_all = "camelCase"` + TS types) - 错误统一为语言无关的 `AppErrorInfo { code, detail }`:code 为稳定英文标识,detail 保留底层诊断,由前端按当前语言本地化展示 ### 服务隔离 - `terminal_service`:会话建立、PTY、终端双向 IO(约 1030 行,唯一持有终端工作线程逻辑) - `ssh_transport`:深层 SSH transport module(约 975 行),对外只暴露 Terminal / SFTP / Exec 三种 opaque capability;`ssh2::Session`、Channel、blocking mode 与第三方错误转换全部留在内部,不向上层泄漏 - `sftp_service`:SFTP 目录浏览、传输任务队列(上传/下载/取消),每条会话复用一条独立 SFTP 连接并串行操作(约 1600 行) - `monitor_service` + `monitor_worker`:独立 SSH 长连接周期采集(CPU / 内存 / 磁盘 + 网络接口速率),单实例管理(共约 1300 行) - `host_service`:深层 HostConfig 持久化 module(约 960 行),拥有校验、凭据写入与引用解析、落盘与失败补偿的完整 save/delete 流程,含分组字段 - `session_manager`:纯协调层,注册/索引/生命周期,不实现 IO 或采集(约 375 行) ### 连接模型(ssh_transport 深化) - Terminal、SFTP、Monitoring 各自使用**独立底层 SSH 连接**;SFTP 的 blocking IO 不再影响 Terminal 的连接级状态 - 会话打开时并行启动 Terminal 与 SFTP 连接;首个 SFTP 请求等待正在进行的连接,后台失败交付一次后由下一次操作触发按需重连 - Session 关闭立即移除 SFTP registry、唤醒等待者并丢弃迟到的连接结果 ## 3. 会话生命周期 ``` open_session (invoke) → session_manager 生成 sessionId + SessionInfo → terminal_service 启动工作线程 LoadingCredentials → ConnectingTcp → SshHandshake → Authenticating → OpeningChannel → RequestingPty → StartingShell → 成功: Connected(先更新后端 runtime_status,再 emit session:status) → 失败: AuthFailed | Timeout | Error(emit session:status + error) → 建立后 emit terminal:data 流式输出 前端 openSession → startMonitoring(独立监控任务) close_session → 后端统一 teardown:停止 Terminal → 清理该会话的 Monitoring task/snapshot → 清理 SFTP 任务与共享连接 → 移除会话 前端只清理本地投影(不主动 stop 监控) ``` ### 权威性约定 - 会话状态以后端 `runtime_status` 为权威:Terminal 先更新后端元数据再发事件,`list_sessions` 直接读取;前端只消费 `session:status`,不再回写 - 任务状态以所属 module 的 registry 为权威:先更新 registry(校验迁移合法性)再发事件,迟到的迁移被拒绝且不发事件 ## 4. 事件流 | 事件 | 方向 | 触发时机 | | --- | --- | --- | | `terminal:data` | 后端 → 前端 | 终端输出(UTF-8,4KB 缓冲非阻塞读) | | `session:status` | 后端 → 前端 | 会话状态变更(Connecting/Connected/AuthFailed/Timeout/Error/Disconnected),携带 `error: AppErrorInfo` | | `session:progress` | 后端 → 前端 | 连接阶段诊断(连接中的卡点提示) | | `monitor:snapshot` | 后端 → 前端 | 每 2 秒采集一次服务器指标(含网络接口速率;缺失指标为 null) | | `task:status` | 后端 → 前端 | 监控长任务状态变更(Pending/Running/Done/Failed),携带 `error: AppErrorInfo` | | `sftp:progress` | 后端 → 前端 | 传输进度(约每 500ms) | | `sftp:task_status` | 后端 → 前端 | 任务状态变更(Pending/Running/Done/Failed/Cancelled),携带 `error: AppErrorInfo` | ## 5. 并发模型 - **Tokio** 异步运行时承载 Tauri 命令与事件发射 - **std::sync::mpsc**:前端写操作通过 channel 发送 `TerminalCommand`(Input/Resize/Shutdown)给终端工作线程 - **Arc shutdown**:会话关闭信号,线程轮询退出 - **opaque capability**:Terminal / SFTP / Exec 各自持独立 `ssh2::Session`,transport 内部持有 channel 所有权与 blocking mode 调度;registry 锁只用于短暂查找,不跨远程 IO,不同 Session 可并发 - **监控采集**:monitor_worker 每 2 秒开新 channel 执行远端脚本一次,CPU 用相邻 `/proc/stat` 采样增量计算(累计不含 guest,避免双重计数),网络速率用相邻 `/proc/net/dev` 计数差计算(首次采样为 null);CPU/内存/磁盘字段缺失时为 null(未知),不伪造 0% ## 6. 安全设计 - 主机配置只存 `password_ref` / `passphrase_ref` 引用键,不存明文 - 明文凭据只存在于 `SaveHostRequest` 请求中,落盘前清除 - 保存顺序:写新凭据 → 落盘(commit 点)→ 清理陈旧凭据;任一失败补偿删除本次已写入的凭据 - 删除顺序:先落盘移除条目(commit 点),成功后再尽力清理两个凭据 key(幂等) - `secure_store` 基于 keyring(macOS Keychain / Windows Credential Manager / Linux secret-service) - macOS 额外启用 hardened runtime + entitlements - 跨边界错误不携带已本地化文案,只传稳定英文 code + 诊断 detail,避免语言泄漏到后端