# Rust 后端(src-tauri) ## 模块结构 ``` src-tauri/src/ ├── lib.rs # 应用入口:注册插件、SessionManager/MonitorService/SftpService、18 个 invoke 命令 ├── main.rs # 启动入口 ├── commands/ # Tauri 命令层(薄封装,只委托,无业务逻辑) │ ├── host.rs # list_hosts / save_host / delete_host │ ├── session.rs # open_session / close_session / write_terminal / │ │ # resize_terminal / list_sessions │ ├── monitor.rs # start_monitoring / stop_monitoring / get_monitor_status │ ├── sftp.rs # sftp_list_dir / sftp_download / sftp_upload / sftp_cancel_task │ └── logging.rs # set_log_level / get_recent_logs / export_logs ├── core/ # 核心服务层 │ ├── session_manager.rs # 会话协调器(纯协调,不实现 IO) │ ├── terminal_service.rs # 终端服务(连接 + PTY + 双向 IO 工作线程) │ ├── ssh_transport.rs # 深层 SSH transport(Terminal/SFTP/Exec opaque capability) │ ├── host_service.rs # 深层 HostConfig 持久化(校验 + 凭据 + 补偿) │ ├── monitor_service.rs # 监控任务生命周期管理(单实例 + 任务状态单一事实) │ ├── monitor_worker.rs # 采集循环(注入 connect_fn + /proc 解析 + 网络速率) │ ├── logging.rs # 日志基础设施(LogStore + 全局 Logger:stderr + 日志文件) │ └── sftp_service.rs # SFTP 目录 + 传输任务队列(任务状态单一事实) ├── models/ # 数据模型(serde camelCase) │ ├── host.rs # HostConfig / SaveHostRequest / AuthType(含 group 分组) │ ├── session.rs # SessionInfo / SessionStatus / 事件 payload │ ├── monitor.rs # MonitorSnapshot / NetworkInterface / NetworkSnapshot / TaskInfo / TaskStatus │ └── sftp.rs # RemoteEntry / TransferTask / TransferType / SftpTaskStatus / 事件 payload ├── storage/ # 持久化 adapter │ ├── host_store.rs # 主机配置 JSON 文件存取(app config dir) │ └── secure_store.rs # OS 安全存储(keyring,密码/私钥口令) └── errors/ └── app_error.rs # AppError(thiserror)+ AppErrorInfo(稳定错误码) ``` ## 核心服务说明 ### HostConfigService(host_service.rs,约 960 行) - 深层持久化 module:校验 → 加载列表 → 写新凭据并解析引用 → 构造 HostConfig → upsert → 落盘(commit 点)→ 切换 auth type 时清理陈旧凭据 - `HostConfig.group` 分组字段:空串表示"未分组",保存/删除/迁移由 service 统一处理 - `CredentialStore` adapter seam(set/delete):真实实现包装 OS secure storage,测试用内存实现可注入失败 - 失败补偿:写新凭据或落盘失败时,删除本次已写入的凭据,保持安全存储与 hosts.json 一致;删除时先落盘再清理凭据,凭据删除失败不阻断 - command adapter 只构造并委托;`open_session` 也经 `service.get_host` 查询主机 ### SessionManager(session_manager.rs,约 375 行) - `SessionHandle`:meta + runtime_status + command_tx + shutdown + host,组成活跃会话 - `HashMap` 索引所有真实会话;`runtime_status` 为后端权威状态 - 锁只用于短暂注册/查找;open / close 在锁外启动或清理其他 module(MonitorService / SftpService 分别注册为独立 Tauri State) ### TerminalService(terminal_service.rs,约 1030 行) - `start_terminal_session`:完整连接流程,按 `ConnectionPhase` 分阶段 - 每个阶段带超时(`run_phase_with_timeout`),失败映射到具体 `SessionStatus` 并携带 `AppErrorInfo` - 状态变更先更新 `runtime_status` 再 emit `session:status`(权威路径) - 工作线程:非阻塞读取 stdout(4KB 缓冲)→ UTF-8 解码 → emit `terminal:data` - 命令队列:`TerminalCommand::{Input, Resize, Shutdown}` 通过 mpsc 接收 ### SshTransport(ssh_transport.rs,约 975 行) - 对外只提供 Terminal / SFTP / Exec 三种 opaque capability;`ssh2::Session`、Channel、SFTP handle、blocking mode 与第三方错误转换均留在 implementation 内 - Terminal、SFTP、Monitoring 各自独立底层 SSH 连接;SFTP blocking IO 不影响 Terminal 连接级状态 - 会话打开并行建立 Terminal 与 SFTP 连接;SFTP 连接失败只交付一次,后续操作按需重连;Session 关闭丢弃迟到连接结果 - 真实 SSH 并发 E2E 由环境变量 `TITAN_SSH_E2E_*` 驱动(未配置时 ignored) ### MonitorService + MonitorWorker(约 590 + 700 行) - `MonitorService`:start/stop/get_status,维护 taskId 与 session 映射;`transition_task_status` 为单一迁移入口(先更新 registry 校验合法性,再 emit `task:status`) - `MonitorWorker`:独立 SSH 连接,每 2 秒执行采集脚本一次 - 采集脚本解析 `/proc/meminfo`、`/proc/stat`(CPU 增量计算,累计不含 guest 以避免双重计数)、`df -B1 /`、`/proc/net/dev`(网络接口 RX/TX 速率,不含 lo,相邻采样差) - CPU/内存/磁盘任一字段缺失时为 null(未知),不伪造 0%;网络缺失时 `available: false` - 连接/采集失败走 transition 为 Failed(终态),不自动重连;stop 后迟到的迁移被拒绝且不发事件 ### SftpService(sftp_service.rs,约 1600 行) - 每条会话复用一条独立 SFTP 连接并串行远程操作;registry 锁只用于短暂查找 - `enqueue_download` / `enqueue_upload`:任务入队(Pending),后台线程执行 - `transition_task`:单一迁移入口,先更新 registry(含 error)再 emit `sftp:task_status`;终态迁移同步移除取消令牌 - `CancelToken`(Arc)支持任务取消;`cleanup_session` 以 registry 为权威,终态任务不重复取消,清理后整会话任务移除 - 进度约每 500ms emit 一次 `sftp:progress`;测试使用内存 SFTP adapter(`memory_sftp`)覆盖真实 worker 全链路 ### 错误模型(errors/app_error.rs) - `AppError`:应用层错误枚举(SSH 连接/认证、会话、存储、SFTP 等 13 类),`code()` 返回稳定英文错误码(如 `SshConnectionError`、`CredentialNotFound`) - `AppErrorInfo`:跨 Tauri 边界的 IPC 错误 payload(`{ code, detail }`),语言无关;detail 保留底层诊断供前端本地化展示 - 命令层统一 `map_err(AppErrorInfo::from)`,不再向前端泄漏底层库错误类型 ### Storage - `HostStore`:hosts.json 存储于 Tauri app config dir,增删改查 + 排序 - `SecureStore`:`password_key(host_id)` / `passphrase_key(host_id)` 作为 keyring 条目键 - 日志文件:`app_log_dir()/titanssh.log`,追加写、启动时超 10MB 截断;`LogStore`(core/logging.rs)提供最近行读取与导出复制 ## 技术依赖 | 依赖 | 用途 | | --- | --- | | tauri 2.10.3 | 应用框架(无额外 feature) | | ssh2 0.9 | SSH 协议(libssh2 绑定) | | tokio 1 | 异步运行时(仅 sync / rt / rt-multi-thread / macros,按需 feature) | | keyring 3.6 | OS 凭据存储(apple-native / windows-native / sync-secret-service) | | uuid / chrono | 任务 ID 与毫秒时间戳 | | thiserror / anyhow | 错误处理 | | log + env_logger | 后端日志:自定义 Logger 同时写 stderr(env_logger 格式化)与应用日志目录文件(等级由 `set_log_level` 运行时调整) | | tauri-plugin-shell / tauri-plugin-dialog | shell 与文件对话框 | ## 开发依赖 - proptest 1.4(属性测试)、mockall 0.12(mock)、tauri test feature - macOS 附加 security-framework 3.7 ## 测试覆盖 `#[cfg(test)]` 存在于:host_service(save/delete 组合与补偿、无明文 proptest)、session_manager、terminal_service、ssh_transport、monitor_worker(含网络速率解析)、monitor_service(迁移合法性)、sftp_service(worker 全链路 + transition)、models(proptest)、storage(host_store、secure_store)、errors(错误码稳定)、commands(logging 等级解析)、core/logging(格式、最近行、导出、截断、安装幂等)。运行:`cd src-tauri && cargo test`