163 lines
6.6 KiB
Markdown
163 lines
6.6 KiB
Markdown
# CTMS 桌面端第二阶段执行设计
|
||
|
||
## 范围与边界
|
||
|
||
第二阶段基于已合入 `dev` 的 Tauri 基线推进。每个评审单元从最新 `dev` 创建短期 `codex/*` 分支,合入后删除,不建立长期桌面主线。
|
||
|
||
本阶段只增强在线桌面客户端能力:
|
||
|
||
- 安全运行时与诊断。
|
||
- 原生文件选择、保存、打开。
|
||
- 服务端持久系统通知。
|
||
- 签名更新、发布流水线与 Windows 打包准备。
|
||
|
||
本阶段明确不实现离线登录、离线缓存、本地业务数据库、本地业务队列、后台业务同步或本地权限裁决。所有业务数据、权限、审计和认证判定仍以 FastAPI 后端为准。
|
||
|
||
## 运行时边界
|
||
|
||
共享业务模块只能通过 `frontend/src/runtime/` 使用平台能力。非 runtime 模块不得直接导入 Tauri API。
|
||
|
||
`ClientRuntime` 第二阶段能力包括:
|
||
|
||
- `secureSessionStorage`
|
||
- `files`
|
||
- `notifications`
|
||
- `updates`
|
||
- `capabilities.secureSessionStorage/nativeFiles/systemNotifications/automaticUpdates`
|
||
|
||
能力标志只在对应运行时可用或初始化成功后开启。Web 端继续使用浏览器能力,不申请系统通知权限,不启动桌面通知轮询,也不触发 updater。
|
||
|
||
## 安全会话存储
|
||
|
||
桌面端 token 存入系统凭据库:
|
||
|
||
- macOS:Keychain。
|
||
- Windows:Credential Manager。
|
||
|
||
桌面端登录使用后端签发的在线会话 token,可在系统凭据库中保存最长 30 天,以支持重启 App 后免输入密码。启动恢复后仍必须使用后端 token 校验和 `/auth/me` 用户状态校验;服务端不可达或会话被后端拒绝时不得进入离线模式。
|
||
|
||
Rust 仅暴露固定 service 下的读取、写入、删除命令。凭据 account 使用规范化服务端 origin 的 SHA-256,避免明文服务端地址散落在系统凭据项名称中。
|
||
|
||
应用挂载前异步初始化 token:
|
||
|
||
1. Web 端继续读取 `localStorage.ctms_token`。
|
||
2. 桌面端先删除 legacy `localStorage.ctms_token`。
|
||
3. 若 legacy token 仍有效,则迁移为带 30 天本机到期时间的系统凭据库会话记录。
|
||
4. 若迁移或读取凭据失败,则内存 token 置空并要求重新登录,不回退明文存储。
|
||
|
||
登出、服务器切换、认证失效时必须同步清除内存 token 和当前服务端 origin 对应的系统凭据。
|
||
|
||
## 原生文件能力
|
||
|
||
公共接口固定为:
|
||
|
||
```ts
|
||
pickFiles(options): Promise<File[]>
|
||
saveFile({ suggestedName, mimeType, data }): Promise<"saved" | "cancelled">
|
||
openFile({ suggestedName, mimeType, data }): Promise<void>
|
||
```
|
||
|
||
桌面端使用 Tauri dialog、fs、opener 插件。权限范围只覆盖用户当次选择的文件路径和 `$TEMP/ctms-desktop/**`,不启用 persisted-scope,不开放目录遍历或 shell。外部打开文件时写入随机临时目录,并使用净化后的文件名;启动和退出时清理临时目录,用户主动保存的文件不自动删除。
|
||
|
||
下载与预览统一先通过 Axios Bearer 请求取得 Blob,再交给文件适配器。附件全局下载接口只接受 `Authorization` header,不再接受 `?token=`。
|
||
|
||
## 系统通知
|
||
|
||
通知订阅由用户在个人设置中主动开启。开启时才请求 OS 权限并创建服务端订阅;`enabled_at` 设为当前时间,不补发历史分发记录。关闭后客户端停止领取通知,重新开启时重新设定起点。
|
||
|
||
后端新增:
|
||
|
||
- `desktop_notification_subscriptions`
|
||
- `desktop_notification_deliveries`
|
||
|
||
API:
|
||
|
||
- `GET/PUT /api/v1/desktop-notifications/subscription`
|
||
- `POST /api/v1/desktop-notifications/claim`
|
||
- `POST /api/v1/desktop-notifications/ack`
|
||
- `POST /api/v1/desktop-notifications/{distribution_id}/read`
|
||
|
||
claim 跨有效项目查询匹配当前用户或角色的活动文件分发,使用五分钟租约、唯一约束和事务防重。客户端显示系统通知后 ack;显示失败则等待租约到期后重试。
|
||
|
||
系统通知正文只显示通用内容:
|
||
|
||
- 标题:`CTMS 文件更新`
|
||
- 正文:`有新的文件版本待查看`
|
||
|
||
项目、文件、版本等详细信息仅在应用内列表显示,避免锁屏泄露。
|
||
|
||
## 诊断元数据与 CORS
|
||
|
||
Axios 请求附加:
|
||
|
||
- `X-CTMS-Client-Type`
|
||
- `X-CTMS-Client-Version`
|
||
- `X-CTMS-Client-Platform`
|
||
- `X-CTMS-Build-Channel`
|
||
- `X-CTMS-Build-Commit`
|
||
|
||
后端安全访问日志保存这些 nullable 字段,并支持按客户端类型/版本筛选。这些字段只用于诊断与排障,不参与授权。
|
||
|
||
CORS origin 由环境变量白名单控制,并显式允许桌面 origin、开发 origin 和上述请求头。
|
||
|
||
Tauri CSP 禁止远程脚本和 shell 入口,仅允许 HTTPS API、本地开发地址、blob 预览与必要资源。
|
||
|
||
## 单实例
|
||
|
||
单实例插件必须最先注册。重复启动时只恢复、显示并聚焦主窗口,不处理命令行参数、深链或业务动作。
|
||
|
||
## 自动更新与发布
|
||
|
||
正式 release 构建启用 updater。客户端从当前 CTMS origin 派生固定清单路径:
|
||
|
||
```text
|
||
/desktop-updates/stable/latest.json
|
||
```
|
||
|
||
生产只允许 HTTPS;本地测试只允许 localhost HTTP。
|
||
|
||
更新检查策略:
|
||
|
||
- 启动延迟 30 秒检查。
|
||
- 之后每 6 小时检查。
|
||
- 发现更新时展示版本和发布说明。
|
||
- 用户确认后下载、验签、安装并重启。
|
||
- 用户选择“稍后”后,同版本 24 小时内不再提示。
|
||
- 不做静默安装,不中断正在录入的业务流程。
|
||
|
||
Tauri 配置必须嵌入 updater 公钥并生成 updater artifacts。生产私钥只能存放在组织密钥库或 CI secret,不进仓库。macOS 更新制品为 `.app.tar.gz` 与 `.sig`。
|
||
|
||
release tag 流水线应从同一提交构建 Web 与桌面端:
|
||
|
||
1. 校验 `frontend/package.json`、Tauri 配置、Cargo manifest/lock 版本一致。
|
||
2. 构建 macOS Universal。
|
||
3. 完成 Apple 签名和公证。
|
||
4. 使用 updater 私钥签名更新包。
|
||
5. 生成 DMG、更新包、签名、`latest.json` 与校验清单。
|
||
6. 先上传不可变制品,最后原子替换 `latest.json`。
|
||
|
||
`latest.json` 同时提供 `darwin-aarch64` 和 `darwin-x86_64`,指向同一个 Universal 更新制品。
|
||
|
||
## Windows 准备
|
||
|
||
第二阶段只验证 Windows x64 NSIS 构建兼容,不发布正式 Windows 安装包。
|
||
|
||
验证范围:
|
||
|
||
- Windows Credential Manager。
|
||
- 路径净化与临时目录限制。
|
||
- 系统通知编译兼容。
|
||
- updater 编译兼容。
|
||
- WebView2 前置条件。
|
||
- 用户级安装假设。
|
||
- 后续代码签名要求。
|
||
|
||
## 验收重点
|
||
|
||
- `localStorage`、URL、日志和系统通知正文不包含 token。
|
||
- 附件下载不再接受 query-token。
|
||
- 业务模块不直接导入 Tauri API。
|
||
- Web 构建、类型检查和单测不回归。
|
||
- macOS `.app` 构建可重复。
|
||
- Keychain 会话、文件入口、通知权限、重复启动聚焦和签名更新链路完成端到端验证。
|