# SimCAE Hub 客户端接入、打包和部署指南 本文说明 `update-client` 的当前实现。它保留 Launcher / Updater / Bootstrap 的桌面客户端机制,服务端协议使用 SimCAE Hub 当前 Go API。 ## 1. 适用范围 当前客户端只覆盖项目已有页面和接口对应的能力: 1. 产品版本、软件发布、发布包和 Manifest。 2. 客户授权、在线命名用户席位和门户受控下载。 3. 在线检查更新、Manifest 拉取、受控下载、SHA-256 校验。 4. Manifest 签名验签、临时文件、断点重试、安装前后完整性校验。 邮箱、支付、灰度、告警等页面上没有的能力不属于当前范围。崩溃报告是 SimCAE Hub 保留的旧系统兼容后端接口,不属于 Launcher / Updater / Bootstrap 的更新链路;接入方需要崩溃上报时,按项目根目录 `项目细节.md` 里的崩溃报告接口说明调用。 ## 2. 客户端程序组成 | 程序 | 作用 | | --- | --- | | `Launcher` | 客户日常启动入口,负责导入配置、使用 `client_token` 检查更新、启动 Updater 或主程序 | | `Updater` | 负责拉取 Manifest、下载发布包、校验文件、准备安装事务 | | `Bootstrap` | 负责在需要替换运行中文件时接管安装,并把结果交回 Updater | | `MainApp` | 示例主程序,用来验证 launch ticket 和安装后完整性校验 | | `Common` | 配置、HTTP、票据、完整性校验等公共代码 | ## 3. 当前在线更新链路 1. `Launcher` 启动后读取服务端生成的 `config/app_config.json`,并把静态配置导入当前用户的运行配置。 2. 如果配置里没有 `api_base_url`,客户端会回退到编译进 EXE 资源中的 `server_config.json`。 3. `Launcher` 确保存在 `device_id`,并检查 `client_token` 是否存在。 4. `Launcher` 调用 `GET /api/v1/client/update/authorized-check`,请求头带 `X-Client-Token`。 5. 如果服务端返回可用发布,`Launcher` 启动 `Updater`,并传入产品编码、渠道、目标版本和发布 ID。 6. `Updater` 调用 `GET /api/v1/client/update/manifest`,请求头继续带 `X-Client-Token`。 7. `Updater` 先校验服务端返回的 `manifestSha256`,再按配置决定是否强制要求 RSA-SHA256 签名。 8. `Updater` 从 Manifest 中读取每个文件的 `downloadUrl`、`sizeBytes` 和 `sha256`。 9. 下载请求统一带 `X-Client-Token`。 10. 下载使用 `.part` 临时文件保存进度,请求失败后按网络重试策略处理。 11. 文件下载完成后,客户端按 Manifest 校验文件大小和 SHA-256。 12. 安装前校验 staging 目录,安装完成后保存 Manifest 缓存,并可在主程序启动时再次校验已安装文件。 ## 4. 关键配置字段 正式客户安装包里的 `config/app_config.json` 由服务端在上传发布包 ZIP 时自动生成。常用字段如下: | 字段 | 说明 | | --- | --- | | `product_code` | SimCAE Hub 后台产品目录中的产品编码,例如 `stage2-dap` | | `app_id` | 本地应用标识,默认和产品编码一致 | | `channel` | 发布渠道,例如 `stable` | | `current_version` | 当前本地安装版本,例如 `1.0.0` | | `api_base_url` | 后端 API 地址,例如 `http://192.168.1.158:18000` | | `client_token` | Launcher/Updater 调更新接口使用的部署级令牌,不绑定某一个客户 | | `install_root` | 相对 Launcher/Updater 所在运行目录解析的更新根目录,决定 Updater、Bootstrap 和启动校验作用在哪棵目录 | | `main_executable` | 相对运行目录解析的业务入口程序,通常是 `MainApp.exe` 或真实软件入口 | | `launcher_executable` | 相对运行目录解析的 Launcher 文件名,主要用于提示和保持启动链路配置一致 | | `updater_executable` | 相对运行目录解析的 Updater 文件名,Launcher 检查到更新后会启动它 | | `bootstrap_executable` | 相对运行目录解析的 Bootstrap 文件名,Updater 需要替换文件时会启动它 | | `platform` | 操作系统,例如 `windows` 或 `linux` | | `arch` | 架构,例如 `x86_64` | | `abi` | ABI,例如 `msvc`;没有时可留空 | | `launch_token` | Launcher 和 MainApp 之间生成一次性启动票据的本地密钥 | | `require_manifest_signature` | 是否强制要求 Manifest 必须带签名 | | `verify_installed_on_start` | 主程序启动时是否按 Manifest 缓存校验已安装文件 | `config/server_config.json` 会编译进客户端资源,作为 `api_base_url` 缺失时的兜底地址,当前测试服务器地址为: `http://192.168.1.158:18000` 如果换服务器,可以改完该文件后重新编译客户端;正式客户包通常由服务端写入 `api_base_url`,不需要把 `server_config.json` 暴露给客户。 ## 5. 编译和打包 SDK 客户端编译、带 Qt 和不带 Qt 两种 SDK 打包方式、输出目录、输出 ZIP 文件名,统一维护在 [../打包成SDK.md](../打包成SDK.md)。 本文件只说明 SDK 在客户软件里的接入位置和运行逻辑,不重复维护打包命令。 SDK 包不会包含最终 `app_config.json`、`server_config.json`、`server_config.qrc` 或 `manifest_public_key.pem`。这些最终配置在完整客户软件包或 Qt IFW 交付包上传到 SimCAE Hub 后由服务端生成。 ## 6. 客户安装包配置 接入方应把以下文件放到客户软件目录的根目录或 `bin/` 目录: 1. `Launcher` 2. `Updater` 3. `Bootstrap` 4. `MainApp` 或真实业务主程序 5. `config/` 目录,可以先为空 ### 6.1 标准目录结构和路径口径 更新系统不要求必须放在客户软件根目录。它可以放在 `SimCAE/` 根目录,也可以放在 `SimCAE/bin/` 目录。关键是让客户端配置里的 `install_root` 和服务端 Manifest 文件路径使用同一套口径。 先区分三个目录概念: | 概念 | 说明 | | --- | --- | | 运行目录 | Launcher、Updater、Bootstrap 所在目录,由客户端自动识别 | | `install_root` | 相对运行目录解析的更新根目录,Updater 下载、校验、备份、回滚和 Bootstrap 替换文件都以它为范围 | | Manifest `files[].path` | 服务端生成的安装相对路径,客户端会把它拼到 `install_root` 下面 | 目录结构一:更新系统放在软件根目录。 ```text SimCAE/ Launcher.exe Updater.exe Bootstrap.exe MainApp.exe config/ app_config.json App/ ... ``` 对应配置: ```json { "install_root": ".", "main_executable": "MainApp.exe", "launcher_executable": "Launcher.exe", "updater_executable": "Updater.exe", "bootstrap_executable": "Bootstrap.exe" } ``` 目录结构二:更新系统和启动入口放在 `bin/`。 ```text SimCAE/ bin/ Launcher.exe Updater.exe Bootstrap.exe MainApp.exe config/ app_config.json App/ ... ``` 如果希望整个 `SimCAE/` 都属于更新范围,对应配置: ```json { "install_root": "..", "main_executable": "MainApp.exe", "launcher_executable": "Launcher.exe", "updater_executable": "Updater.exe", "bootstrap_executable": "Bootstrap.exe" } ``` 这表示 Launcher 从 `bin/` 启动 `MainApp.exe`,Updater 和 Bootstrap 更新的是 `bin/` 的上一级,也就是整个 `SimCAE/`。 服务端发布包路径要和客户端 `install_root` 对应: | 客户端配置 | 服务端 Manifest 路径口径 | | --- | --- | | 更新系统在根目录,`install_root` 是 `.` | `MainApp.exe`、`App/xxx.dll` 相对 `SimCAE/` | | 更新系统在 `bin/`,`install_root` 是 `..` | `bin/MainApp.exe`、`App/xxx.dll` 相对 `SimCAE/` | 当前管理后台“发布包”上传接口会使用上传文件名作为 `artifactName`,后端按安全文件名校验。当前稳定支持的是发布一个完整安装包或压缩包文件,或者把文件放在 `install_root` 根层级;还不是“自动解析压缩包并生成 App/bin 多文件 Manifest”的完整安装器。后续如果要让在线 Updater 直接把多个文件铺到 `App/`、`bin/` 等子目录,需要在现有发布包页面和 Go 后端上继续增强安全相对路径或 Manifest 文件清单生成能力。 如果后续要支持“更新系统在 `bin/`,但只校验和更新 `App/`”这类更窄的安装根目录,需要在管理后台和 Go 后端增加对应配置项,让服务端生成 `../App` 这类定制 `install_root`。当前服务端自动生成配置时只使用标准的 `.` 或 `..`。 如果开启 `verify_installed_on_start`,客户端会扫描 `install_root` 下的 EXE 和 DLL。整包 Manifest 中 `required=true` 的核心文件必须存在且 SHA-256 匹配;`required=false` 的可选组件文件可以由 MaintenanceTool 管理,缺失时不会阻止启动。可选组件建议放在独立目录中,例如 `plugins/dap/`,不要和核心程序 DLL 混放。 上传完整客户软件 ZIP 时,服务端会根据发布包记录自动写入这些关键值: ```json { "product_code": "stage2-dap", "channel": "stable", "current_version": "1.1.0", "api_base_url": "http://192.168.1.158:18000", "client_token": "<由服务器 .env 配置>", "install_root": ". 或 ..", "platform": "windows", "arch": "x86_64", "abi": "msvc" } ``` `install_root` 由服务端根据 Launcher 所在位置自动判断:更新系统在软件根目录时写 `.`,在 `bin/` 目录时写 `..`。 ## 7. 运行数据位置 Windows 运行数据目录: `%LOCALAPPDATA%\SimCAE\HubUpdateClient\installations\<安装目录SHA256>\` Linux 运行数据目录: `$XDG_DATA_HOME/SimCAE/HubUpdateClient/installations/<安装目录SHA256>/` 未设置 `XDG_DATA_HOME` 时通常是: `~/.local/share/SimCAE/HubUpdateClient/installations/<安装目录SHA256>/` Manifest 缓存保存在运行数据目录下的 `update/manifest_cache`。 ## 8. 常见问题 1. 客户门户登录失败:检查客户门户账号是否已激活、客户是否生效、密码是否正确。 2. 检查更新没有结果:检查后台发布是否已发布、发布包是否可用、产品编码、渠道和平台参数是否一致。 3. 下载 401:检查发布包中的 `config/app_config.json` 是否由服务端生成,`client_token` 是否和服务器 `.env` 中的 `SIMCAE_CLIENT_TOKEN` 一致。 4. Manifest 校验失败:检查服务端 Manifest 是否被篡改、发布包 SHA-256 是否和实际文件一致。 5. 强制签名失败:确认 `manifest_public_key.pem` 与服务端私钥匹配;如果服务端暂未启用签名,测试环境可先把 `require_manifest_signature` 设为 `false`。 6. 启动主程序失败:检查 `main_executable` 和 `install_root` 是否指向真实文件。