Files

1612 lines
73 KiB
Markdown
Raw Permalink Normal View History

# 项目总览
本文档用于总览当前项目的目标、已实现能力、主要目录、交付物和后续注意事项。适合在提交代码、交接项目或给他人快速了解项目时阅读。
如果只想快速判断“现在能交付什么、还要注意什么”,先看根目录的 `项目交付状态一页纸.md`
## 一、项目整体是什么
本项目是一套面向 Windows/Linux 客户端软件的自动升级与版本控制系统,并补充实现了 SimCAE 崩溃报告后端接口。
整体分为两部分:
```text
update-client/ Windows/Linux 客户端升级运行时、SDK 打包脚本、接入说明文档
2026-07-14 01:39:30 +00:00
update-server/ FastAPI 服务端、后台页面、Docker 部署和离线交付脚本
```
文档放置约定:
```text
项目总览.md 项目整体说明,保留在总仓库根目录
update-client/Docs/ 客户端说明文档
update-server/Docs/ 服务端说明文档
```
客户端负责:
- 检查服务端是否有新版本。
- 校验 License、设备身份和版本策略。
- 下载 Manifest 和版本文件。
- 校验 SHA-256 哈希和 Manifest 签名。
- 安装新版本、健康检查、失败回滚。
- 作为 SDK 提供给业务软件接入。
服务端负责:
- 管理应用、渠道、License、版本发布。
- 生成和签名 Manifest。
- 提供文件下载地址。
- 记录升级日志、下载日志。
- 提供后台管理页面。
- 提供 SimCAE 崩溃报告和符号包上传接口。
- 支持 Docker 和离线 Docker 部署包。
## 二、客户端当前已实现
客户端核心程序:
```text
Launcher.exe / Launcher 用户入口,检查更新并启动业务主程序
Updater.exe / Updater 下载、校验、安装、提交或回滚
Bootstrap.exe / Bootstrap 辅助替换运行中的 EXE/DLL 或 Linux 可执行文件
MainApp.exe / MainApp 当前仓库内的示例业务程序
```
已经实现的能力:
- 首次启动时把 `config/app_config.json` 导入当前 Windows 用户注册表,后续普通运行配置优先从注册表读取;如果解析后的 JSON 内容变化,下一次启动会自动重新导入,并清理 `config/client_identity.dat``config/version_policy.dat`
- 服务端 API 地址 `api_base_url` 不再写入 `app_config.json` 或注册表,而是写入 `config/server_config.json` 并通过 `config/server_config.qrc` 编译进 Launcher / Updater。
- 首次设备登记和本地设备身份校验。
- License 校验。
2026-07-14 01:39:30 +00:00
- `license_key` 为空时,`Launcher.exe` 会弹窗让用户粘贴后台创建的 License,并写入注册表。当前实现会先检查配置项;即使本地已有 `config/client_identity.dat`,只要清空 `license_key` 仍会提示用户补填 License。
- License 过期、错误、禁用、设备数达到上限或设备身份凭证与授权不匹配时,`Launcher.exe` 会引导用户重新输入 License,而不是要求用户手动查找配置文件。
- 版本策略校验。
- 防止策略序号回退。
- 检测系统时间回拨。
- Manifest 下载、缓存和签名验签。
- 按 Manifest 校验安装目录文件完整性。
- 文件 SHA-256 校验。
- 启动票据 `--ticket-file` 校验,防止用户绕过 `Launcher.exe` 直接启动业务程序。
- 升级后健康检查 `--health-file`,业务程序启动成功后写入 `ok`
- 升级失败回滚。
- 升级日志和下载日志上报服务端。
2026-07-20 06:45:59 +00:00
- Git 标签清单生成:如果服务端策略开启,`Launcher` 会请求服务端生成 Git tags 文本,并写入 `Launcher` 同目录下的 `tags.txt`;客户端不会接触 Git token。
- Launcher / Updater / Bootstrap 已支持 Windows 和 Linux Qt 跨平台编译。
## 三、客户端 SDK 交付状态
已经提供 Windows 和 Linux SDK/客户端打包脚本:
```text
2026-07-14 01:39:30 +00:00
update-client/scripts/package-sdk.ps1
update-client/scripts/package-client.ps1
update-client/scripts/package-sdk.sh
update-client/scripts/package-client.sh
2026-07-14 01:39:30 +00:00
```
客户端文档现在统一放在:
```text
2026-07-14 09:28:13 +00:00
update-client/Docs/00-先读我-客户端文档入口.txt
update-client/Docs/01-客户端接入打包部署指南.md
update-client/Docs/02-编译环境和第三方依赖说明.md
```
`package-sdk.ps1` / `package-sdk.sh` 用于生成给业务开发者接入的 SDK 包。当前 SDK 包结构设计为:
```text
UpdateClientSDK/
sdk_manifest.json
Docs/
00-先读我-客户端文档入口.txt
01-客户端接入打包部署指南.md
02-编译环境和第三方依赖说明.md
bin/
config/
app_config.json
manifest_public_key.pem
Common/
ConfigHelper.h/.cpp
TicketHelper.h/.cpp
scripts/
SimCAE自动升级SDK接入说明_v0.1.docx 可选,本地存在时才会随包复制
```
为了降低接入方理解成本,SDK 包内默认带 `Docs/00-先读我-客户端文档入口.txt`。Word 接入说明是可选增强,`.docx` 不进 Gitfresh clone 没有 Word 时也能打包 SDK。
Linux 当前已完成 Qt 跨平台编译、Linux SDK 打包脚本、Linux 最终客户端打包脚本、Linux 配置模板和部署说明。真实 Linux 版 SimCAE 主程序还未由业务开发方交付,因此真实业务联调暂未完成。
客户端接入文档已经说明:
- 拿到 `UpdateClientSDK.zip` 后怎么操作。
- 如何复制 SDK 运行时到业务软件 Release 目录。
- `app_config.json` 每个字段怎么填写。
- 为什么用户入口必须改成 `Launcher.exe`
- 业务主程序如何解析 `--ticket-file``--health-file`
- 可复制的 Qt/C++ 接入代码。
- 如何联调升级、健康检查和回滚。
- 如何打最终客户端包。
## 四、服务端当前已实现
2026-07-14 10:34:00 +00:00
服务端采用的成熟框架和模板生态:
- 后端 HTTP 框架:`FastAPI`,运行服务使用 `Uvicorn`,请求模型和参数校验使用 FastAPI 自带的 `Pydantic` 体系。
- 后端工程结构:参考 FastAPI 官方 full-stack 模板、FastAPI Users 等成熟 FastAPI 后台项目的分层方式,拆成 `routes / services / repositories / schemas / core`
- 管理后台前端:使用 GitHub 上的 `pure-admin-thin` / `vue-pure-admin` 生态,技术栈是 `Vue3 + Element Plus + TypeScript + Vite`
- 存储组件:元数据使用 SQLite,版本文件和崩溃文件使用 MinIO 对象存储。
2026-07-20 06:45:59 +00:00
- Git 标签清单:服务端支持通过 `GITEA_BASE_URL` / `GITEA_TOKEN` 访问 Gitea API,管理后台策略页可以控制 Launcher 是否生成 `tags.txt`,并在后台预览当前仓库 tags。`GITEA_TOKEN` 只保存在服务端 `.env`,不会返回给客户端。
- 当前说明:后端已经完成工程化分层,并已加入用户名/密码登录、Argon2 密码哈希、JWT access/refresh token、RBAC 权限检查和管理员用户管理页面。当前实现是轻量用户体系,后续仍可继续接入 FastAPI Users 等更完整的成熟认证组件。
- 自动化测试:使用 pytest,测试会创建隔离的临时数据库、临时 Manifest 私钥和临时存储目录,覆盖管理员登录/JWT、License/设备登记、更新检查、发布事务和崩溃报告核心流程。
2026-07-14 10:34:00 +00:00
当前后端已经从早期单文件形态拆成分层结构,主要入口和目录:
2026-07-14 01:39:30 +00:00
```text
update-server/main.py 应用启动、数据库初始化、MinIO 初始化、全局客户端鉴权
update-server/app/api/routes/ 管理端、客户端更新、崩溃报告等 HTTP 路由
update-server/app/services/ 发布、签名、License、设备凭证、崩溃报告等业务逻辑
update-server/app/repositories/ SQLite 数据库读写层
update-server/app/schemas/ Pydantic 请求模型
update-server/db.py
update-server/tables.sql
update-server/minio_tool.py
update-server/admin-ui/ 新版管理后台源码
```
服务端文档现在统一放在:
```text
2026-07-14 09:28:13 +00:00
update-server/Docs/00-先读我-服务端文档入口.txt
update-server/Docs/01-服务端Docker打包部署指南.md
update-server/Docs/02-崩溃报告接口联调指南.md
update-server/Docs/03-后端工程化结构说明.md
```
已经实现的能力:
- 应用管理。
- 渠道管理。
- License 管理。
- 设备首次登记。
- 版本发布。
- 版本发布已支持后台任务模式:浏览器上传完成后,服务端继续在后台校验、入库和上传 MinIO,管理页面轮询任务状态。
- 版本发布支持两种方式:选择软件发布根目录,或上传压缩好的发布包。管理页面已经拆成“软件根目录/压缩发布包”两个发布方式,避免用户误以为只能选择文件夹。
- 压缩发布包支持 `zip``tar.gz``tgz``tar.bz2``tbz2``rar`;服务端会先解压,再按 `RELEASE_MAIN_EXECUTABLE` 校验主程序路径。
- Manifest 生成和私钥签名。
- MinIO 对象存储上传版本文件。
- 客户端检查更新接口。
- 客户端文件下载接口。
- 升级结果上报。
- 文件下载日志记录。
- 后台管理页面。
2026-07-14 01:39:30 +00:00
- 后端已经按 `routes / services / repositories / schemas` 拆分,`main.py` 不再堆业务接口。
- Docker 运行。
- Docker 离线部署包生成。
## 五、SimCAE Crash Report 后端已实现
根据 `SimCAE_Crash_Report后端接口规范.docx`,服务端已实现:
```text
GET /api/v1/health
POST /api/v1/crash-reports
GET /api/v1/crash-reports/{report_id}
GET /api/v1/crash-reports/{report_id}/files/{file_name}
POST /api/v1/symbols
```
已支持:
- Bearer Token 鉴权。
- 崩溃报告 multipart 上传。
- metadata JSON 校验。
- minidump 文件上传。
- attachments.zip 可选上传。
- SHA-256 校验。
- `Idempotency-Key` 幂等处理。
- 重复上传识别。
- 冲突上传返回 409。
- 崩溃文件受保护下载。
- 文件访问审计日志。
- 符号包上传。
- Docker 下持久化保存崩溃报告数据。
## 六、Docker 和离线部署状态
服务端 Docker 文件:
```text
2026-07-14 01:39:30 +00:00
update-server/Dockerfile
update-server/docker-compose.yml
update-server/docker-compose.image.yml
update-server/scripts/package-offline-server.sh
update-server/.env.example
update-server/.env.docker.example
```
Ubuntu 主流程使用:
```bash
2026-07-14 01:39:30 +00:00
cd update-server
2026-07-14 01:39:30 +00:00
bash ./scripts/package-offline-server.sh \
--version 0.1.0 \
--output-dir ./dist/SimCAEServerDockerPackage
```
生成:
```text
2026-07-14 01:39:30 +00:00
update-server/dist/SimCAEServerDockerPackage.tar.gz
```
该包中包含:
- 我们自己的 `simcae-update-server` 镜像。
- MinIO 镜像。
- MinIO init 镜像。
- `docker-compose.yml`
- `.env.example`
- `load-images.sh`
- `README.md`
- `keys/manifest_private_key.pem`
- `keys/manifest_public_key.pem`
当前 Docker 镜像内已安装 `libarchive-tools`,用于后台发布 `.rar` 压缩包时调用 `bsdtar` 解压。`zip``tar.*` 由 Python 标准库直接支持。
注意:Docker 镜像包只包含软件本体和部署配置,不包含已经运行出来的数据。当前服务器上的数据库、上传过的版本文件、崩溃报告等数据在:
```text
2026-07-14 01:39:30 +00:00
update-server/runtime/
update-server/minio_data/
```
如果要完整迁移现有服务数据,需要额外备份和恢复这两个目录。
## 七、重要安全概念
Manifest 是服务端生成的版本文件清单,里面记录每个文件的路径、大小和 SHA-256。
SHA-256 用来确认文件内容没有损坏或被替换。
服务端使用:
```text
manifest_private_key.pem
```
对 Manifest 签名。
客户端使用:
```text
manifest_public_key.pem
```
验证 Manifest 签名。
关系是:
```text
服务端私钥签名 Manifest
客户端公钥验证 Manifest
客户端按 Manifest 里的 SHA-256 校验每个文件
```
私钥必须保密,公钥可以放入客户端 SDK。
## 八、当前建议提交的内容
建议提交:
- 客户端源码修改。
- 服务端源码修改。
- SDK 打包脚本。
- Dockerfile 和 docker-compose 文件。
- `.env.example``.env.docker.example`
- Word 接入说明文档。
- 服务端 Docker 离线包 README 生成逻辑。
- `.gitignore`
默认值说明:服务端和客户端示例配置里已经放了可直接试跑的默认 token、MinIO 用户名和密码,目的是让接收方不用一上来就被配置卡住。它们可以直接用于内网联调;如果进入正式生产或公网环境,再按安全要求替换。
不建议提交:
2026-07-14 01:39:30 +00:00
- `update-client/App/`
- `update-client/out/`
- `update-client/dist/`
- `update-server/dist/`
- `update-server/runtime/`
- `update-server/minio_data/`
- `update-server/*.db`
- `update-server/.env`
- `update-server/keys/manifest_private_key.pem`
- 任何真实 token、License、私钥和运行日志。
## 九、SimCAE 安装根目录模式
SimCAE 的真实目录结构是安装根目录下包含 `bin/``Licenses/``installerResources/` 和维护工具,业务主程序位于 `bin/SimCAE.exe`。当前交付配置按“SDK 在 bin、发布整个安装根目录”工作:
```text
SimCAE/
bin/
Launcher.exe
Updater.exe
Bootstrap.exe
SimCAE.exe
config/
app_config.json
manifest_public_key.pem
Licenses/
installerResources/
maintenancetool.exe
```
客户端配置使用 `install_root=..``main_executable=SimCAE.exe`,服务端配置使用 `RELEASE_MAIN_EXECUTABLE=bin/SimCAE.exe`。后台发布版本时可以选择整个 `SimCAE/` 安装根目录,也可以上传压缩好的 `SimCAE.zip` / `SimCAE.tar.gz` 等发布包;不要只选择或只压缩 `bin/`。升级事务目录固定放在 `Launcher.exe` 所在目录下,也就是 `SimCAE/bin/update/`
## 十、运行目录权限说明
当前客户端 SDK 把部署配置源放在 `Launcher.exe` 所在目录下的 `config/app_config.json`,启动时同步到当前用户的 QSettings 配置区;Windows 下对应注册表路径 `HKEY_CURRENT_USER\Software\Marsco\UpdateClientSDK\installations\<安装目录SHA256>\config`Linux 下由 Qt QSettings 写入当前用户配置目录。服务端 API 地址 `api_base_url` 是例外:它位于源码 `config/server_config.json`,通过 `config/server_config.qrc` 编译进程序,不再写入 `app_config.json` 或注册表。如果检测到 `app_config.json` 变化,SDK 会删除当前用户数据目录里的 `client_identity.dat``version_policy.dat``local_state.json`,避免旧授权身份、旧策略或旧防回滚状态继续生效。设备身份、状态、策略、更新事务、备份、暂存文件、下载断点和 Manifest 缓存默认位于当前用户数据目录,例如 Windows:`%LOCALAPPDATA%\Marsco\UpdateClientSDK\installations\<安装目录SHA256>\`。当 SDK 放在 `SimCAE/bin` 时,不会再在 `SimCAE/` 根目录生成顶层 `update/`
2026-07-14 01:39:30 +00:00
如果把软件放在 `C:\Program Files\SimCAE\bin` 并用普通用户启动,普通配置值已经不需要写回 `app_config.json`;但设备身份、状态、策略、更新缓存等文件仍可能需要写安装目录。当前代码对小型状态文件写入已有管理员权限确认;正式要完整安装到 `Program Files` 并自动升级大文件时,后续仍建议补 Windows 服务,或将更多运行时状态迁移到 `ProgramData` / `AppData`
## 十一、后续待办
- 等业务开发者按 SDK 文档改造 SimCAE 源码。
- 用改造后的 SimCAE Release 目录做完整升级联调。
- 确认直接启动 `SimCAE.exe` 会被拒绝,只能通过 `Launcher.exe` 启动。
- 验证升级成功、升级失败回滚、Manifest 验签失败拦截。
- 如需迁移现有服务数据,补充 `runtime/``minio_data/` 的备份恢复流程。
- 交付配置已提供可直接试跑的默认 token 和 MinIO 账号密码;正式生产环境建议替换为客户自己的强随机值。
- 生产私钥和测试私钥建议分开管理。
---
## 附录:原项目进度记录
2026-07-14 01:39:30 +00:00
以下内容迁移自原 `update-client/项目进度.txt`,用于保留更详细的阶段性进度、功能清单和概念说明。
```text
软件自动升级与版本控制系统——项目进度
更新时间:2026-07-07
2026-07-14 01:39:30 +00:00
依据:《软件自动升级与版本控制系统开发设计文档 v0.1》、《SimCAE_Crash_Report后端接口规范》及当前 update-server/update-client 源码
============================================================
一、项目进度概述
============================================================
目前项目已经形成两个相关但职责不同的后端/客户端能力:
1. 软件自动升级与版本控制系统。
2. SimCAE 崩溃报告后端接口。
自动升级系统的 Windows 在线更新主链路已经基本打通,并且已经从“能升级”推进到“可控、安全、可回滚、可离线导入”的阶段。客户端具备 Launcher、Updater、MainApp、Bootstrap 四个程序;服务端具备 FastAPI、SQLite、MinIO、本地回退存储、版本发布、Manifest、下载授权、升级结果上报、版本策略、设备身份、License 授权、动态渠道、离线更新包、下载日志、管理员审计日志和管理网页。客户端可以检查版本、验证 RSA 签名、差异下载、断点续传、备份旧文件、通过 Bootstrap 替换被占用文件、删除废弃文件、启动新版本并等待健康确认;失败时可以进入自动回滚流程。
SimCAE 崩溃报告后端已经完成第一阶段核心接口:接收 SimCAECrashReporter.exe 上传的 metadata.json、crash.dmp 和可选 attachments.zip;根据 clientReportId 做幂等去重;校验 minidump SHA-256;保存原始文件;返回稳定 reportId;接收 CI/发布流程上传的 symbols.zip;并提供管理 token 查询和私有文件下载入口。管理后台已经新增基础“崩溃报告”页面,可查看报告列表并下载 metadata、dmp、attachments 和 server.json。自动符号化、聚合统计、告警和问题分派属于后续增强。
按两份需求文档综合判断:
1. 自动升级 Demo 主链路:约 90%~95%,剩余主要是系统性故障测试和插件真实接口加载。
2. 自动升级完整设计文档:约 75%~80%,剩余主要是正式管理员体系、限流、代码签名、灰度、多平台、插件接口版本准入和生产化测试。
3. Crash Report 第一阶段接口:约 90%~95%,已经按需求文档完成真实 HTTP 联调;剩余是 HTTPS 部署、限流和保留期清理。
4. Crash Report 完整平台:约 50%~60%,因为自动符号化、统计聚合、告警和问题分派还未做。
当前最主要的剩余工作不再是普通在线更新,而是把已完成能力做实机回归、生产化安全加固,并把 SimCAE 崩溃上报客户端接入进来。
============================================================
二、已经实现的功能
============================================================
2.1 客户端基础框架
已实现:
1. Launcher.exe。
2. Updater.exe。
3. MainApp.exe。
4. 独立原生 Bootstrap.exe。
5. Qt 图形提示和更新进度界面。
6. MainApp 显示当前版本。
7. MainApp 显示 Demo DLL 的简化 Hash 标识。
8. Launcher 启动 MainApp。
9. 直接双击 MainApp 时拒绝运行。
10. Windows x64 CMake 工程。
11. Qt 5.15、MSVC、OpenSSL 构建配置。
说明:MainApp 当前读取 Demo DLL 内容并显示 Hash 标识,但还没有通过 QLibrary 真正加载并调用插件接口,因此“插件实际加载”只算部分完成。
2026-07-14 01:39:30 +00:00
2.2 本地配置
已实现:
2026-07-14 01:39:30 +00:00
1. 使用 `config/app_config.json` 作为部署配置源文件。
2. Launcher / Updater / MainApp 启动时按解析后的 JSON 内容 SHA-256 判断配置是否变化,变化后自动导入当前 Windows 用户注册表。
3. 注册表路径为 `HKEY_CURRENT_USER\Software\Marsco\UpdateClientSDK\installations\<安装目录SHA256>\config`。
4. 后续运行配置优先从注册表读取,运行时动态值写入注册表。
5. 检测到 JSON 变化后自动清理 `client_identity.dat` 和 `version_policy.dat`。
6. 支持旧 client.ini 自动迁移。
7. 保存 App ID、当前版本、渠道、设备 ID、客户端 Token、启动 Token、平台和架构。
8. 使用 `config/server_config.json` + `config/server_config.qrc` 把 API 地址编译进程序,避免写入 `app_config.json` 或注册表。
2026-07-14 01:39:30 +00:00
8. 更新成功后写入 current_version。
9. 文件型本地状态仍使用 QSaveFile 原子写入。
10. 运行时配置不进入发布包。
11. 已配置项目 .gitignore。
2.3 服务端基础能力
已实现:
1. FastAPI HTTP 服务。
2. SQLite 数据库。
3. MinIO 对象存储。
4. 应用创建与查询。
5. 版本发布、查询、设置最新和删除。
6. stable、preview、dev 三个固定渠道。
7. 删除版本时同步删除 MinIO 文件。
8. 发布失败时清理 MinIO 和本地回退目录中的半成品。
9. 大文件上传前检查磁盘空间。
10. multipart 临时文件存放到 /dev/shm,避免与 MinIO 双重占用根分区。
11. MinIO 不可用时支持本地存储回退。
12. 管理员 Token 鉴权和令牌修改。
13. 管理页面和跨域配置。
当前主要接口:
自动升级客户端接口:
1. POST /api/v1/device/issue
2. POST /api/v1/update/check
3. POST /api/v1/update/manifest
4. POST /api/v1/update/download-url
5. POST /api/v1/update/download-report
6. POST /api/v1/update/report
管理后台接口:
1. POST /admin/publish
2. GET /admin/version/list
3. POST /admin/version/set-latest
4. POST /admin/version/set-protocol
5. POST /admin/version/delete
6. POST /admin/version/offline-package
7. GET /admin/policy
8. POST /admin/policy/save
9. GET/POST /admin/channel/*
10. GET/POST /admin/license/*
11. GET/POST /admin/device/*
12. GET /admin/report/list
13. GET /admin/download-log/list
14. GET /admin/audit-log/list
SimCAE 崩溃报告接口:
1. GET /api/v1/health
2. POST /api/v1/crash-reports
3. GET /api/v1/crash-reports/{reportId}
4. GET /api/v1/crash-reports/{reportId}/files/{fileName}
5. POST /api/v1/symbols
2.4 软件根目录和多层目录发布
已实现:
1. 浏览器一次选择软件根目录。
2. 保留所有文件的相对路径。
3. 支持多层 DLL、插件和资源目录。
4. 服务端阻止绝对路径、..、盘符路径和重复路径。
5. Manifest、MinIO 和客户端安装过程都保留相对路径。
6. 检查 MainApp.exe 是否位于发布根级。
7. 排除 Bootstrap、运行时配置、状态文件、策略缓存、PDB、ILK 和更新临时目录。
8. 支持上传压缩发布包,服务端解压后按同一套路径、主程序、文件数量和空间规则校验,并自动过滤 `update/`、`update_temp/`、构建目录、运行时配置和调试产物。
9. 压缩包可多一层顶层目录,例如 `SimCAE/bin/SimCAE.exe` 会自动归一为 `bin/SimCAE.exe`。
2.5 Manifest
已实现:
1. 服务端动态生成全量 Manifest。
2. 包含 App ID、版本、渠道、平台、架构、Manifest 序列和创建时间。
3. 包含文件相对路径、大小、SHA-256 和 executable 标记。
4. 使用稳定 JSON 序列化。
5. 使用 RSA-2048/SHA-256 签名。
6. 客户端使用内置公钥验签。
7. 签名失败时拒绝安装。
8. Manifest 本地缓存。
9. 新旧 Manifest 对比。
10. 根据新旧 Manifest 差集删除废弃文件。
2.6 在线升级完整流程
已实现流程:
1. Launcher 请求更新检查。
2. 服务端返回目标版本和签名策略。
3. Launcher 验证版本策略 RSA 签名。
4. Launcher 根据策略决定升级、降级、继续运行或禁止运行。
5. 启动 Updater。
6. Updater 获取并验证 Manifest。
7. 获取 MinIO 预签名下载地址。
8. 比较本地文件 SHA-256。
9. 只下载新增或变化的文件。
10. 校验文件大小和 SHA-256。
11. 备份旧文件。
12. Updater 退出并移交 Bootstrap。
13. Bootstrap 替换文件或删除废弃文件。
14. Bootstrap 重新启动 Updater 续办事务。
15. Updater 完整校验安装结果。
16. 保存新版本号。
17. 启动 MainApp 并等待健康确认。
18. 健康确认成功后提交事务。
19. 上报升级成功结果。
2.7 差异下载、断点续传和下载体验
已实现:
1. SHA 相同的文件跳过下载。
2. HTTP Range 断点续传。
3. 使用运行目录下的 `update/download_cache/<sha256>.part` 保存片段;SimCAE 场景中即 `SimCAE/bin/update/download_cache/`。
4. Updater 重启后仍可继续未完成文件。
5. 单文件最多自动重试四次。
6. 使用递增等待时间重试。
7. 服务端不支持 Range 时安全地完整重下。
8. 下载后验证大小和 SHA-256。
9. 显示当前文件、已下载量、总下载量、速度和总进度。
10. 清理不属于当前 Manifest 的旧片段。
2.8 客户端磁盘空间预检
下载前会计算:
1. 尚未下载的字节数。
2. 已存在的断点片段大小。
3. 被覆盖文件需要的备份空间。
4. 废弃文件需要的备份空间。
5. 至少 128MB 的安全余量。
空间不足时会在开始下载前阻止更新,并显示所需空间和当前可用空间。
2.9 升级事务状态机
已实现运行目录下的 `update/upgrade_state.json`SimCAE 场景中即 `SimCAE/bin/update/upgrade_state.json`,包含:
1. transaction_id。
2. from_version。
3. to_version。
4. status。
5. manifest_id。
6. staging_dir。
7. backup_dir。
8. changed_paths。
9. obsolete_paths。
10. error_code 和 message。
已经实现的主要状态:
prepared、verified、waiting_mainapp_exit、backed_up、awaiting_bootstrap、replacing、replaced、post_verify、rollback_required、rolling_back、rolled_back、committed、failed。
Updater 启动时会读取旧事务,并根据状态尝试恢复或回滚。
2.10 Bootstrap 自更新机制
已实现:
1. Bootstrap 不依赖 Qt。
2. Updater 退出后由 Bootstrap 接管安装。
3. 可以替换 Updater.exe、Launcher.exe、MainApp.exe、Qt DLL、OpenSSL DLL、Qt 插件和业务文件。
4. 完成后重新启动 Updater 续办事务。
5. 回滚也由 Bootstrap 执行,避免运行中的 Updater 锁住自己。
6. Bootstrap 自身属于不可由普通更新事务替换的根组件。
7. 旧 Manifest 即使包含 Bootstrap,也会由客户端作为受保护文件忽略。
2.11 自动回滚和健康检查
已实现:
1. 安装前备份旧文件。
2. 替换失败时回滚。
3. 安装后 Hash 校验失败时回滚。
4. MainApp 无法启动时回滚。
5. MainApp 15 秒内未写入健康标记时回滚。
6. 版本状态保存失败时回滚。
7. 回滚后恢复旧版本号。
8. 回滚后重新启动旧 MainApp。
9. 被删除的废弃文件也会在回滚时恢复。
10. Updater 自身发生变化时由 Bootstrap 执行回滚。
2.12 版本运行策略
已实现:
1. version_policies 数据表。
2. 每个应用和渠道分别保存策略。
3. 每次修改自动递增 policy_seq。
4. RSA 签名策略在线下发。
5. Launcher 验证策略签名。
6. 签名失败时拒绝使用。
7. 策略原子缓存到本地。
8. policy_seq 防回滚。
9. 强制升级。
10. 禁用指定版本。
11. 最低支持版本。
12. 允许或禁止降级。
13. 允许或禁止离线启动。
14. 策略有效期。
15. 自定义客户端提示。
16. 管理页面策略编辑区。
17. 上次在线验证时间和系统时间回拨检测。
2.13 受控降级和用户选择
已实现:
1. 管理员可以将历史版本设置为渠道最新。
2. 允许降级时服务端返回 rollback_allowed。
3. 禁止降级时返回 rollback_denied。
4. 用户可以选择是否执行降级。
5. 用户拒绝降级后继续运行当前版本。
6. 普通可选升级也允许用户选择稍后更新。
7. 强制升级不能跳过。
8. 降级复用完整事务、Bootstrap、校验、健康确认和失败回滚机制。
9. 每个发布版本记录 client_protocolLauncher 上报当前协议。
10. 降级目标协议低于当前客户端协议时,服务端在安装前返回 rollback_denied。
11. 管理后台支持查看和修正历史版本的协议标签。
这里的“客户端协议”不是 HTTP 协议,也不是软件版本号,而是 Launcher/Updater 支持的升级机制能力编号。当前默认是 `3`,代表支持一次性启动票据、健康检查、策略校验、受控降级等当前客户端机制;普通发布保持默认值即可,只有未来客户端升级机制发生不兼容变化时才需要调整。
2.14 离线运行基础
已实现:
1. 在线策略本地缓存。
2. 本地策略 RSA 验签。
3. offline_allowed。
4. valid_until。
5. 策略过期时拒绝启动。
6. policy_seq 防回滚。
7. 记录上次成功启动时间。
8. 记录上次在线验证时间。
9. 检测系统时间是否回拨。
说明:目前实现的是“离线运行”,不是“离线升级”。
2.15 管理页面
已实现:
1. 用户名和密码登录管理后台。
2. 登录成功后由服务端签发 JWT access token 和 refresh token,管理接口使用 `Authorization: Bearer ...` 调用。
3. 管理员密码修改。
4. RBAC 角色权限校验,目前内置超级管理员、发布管理员、授权管理员、审计人员和崩溃报告管理员。
5. 管理员用户管理:创建用户、编辑显示名称和角色、启用/禁用、重置密码,并防止禁用最后一个超级管理员。
6. 创建和选择应用。
7. 选择软件根目录发布。
8. 切换到“压缩发布包”模式上传压缩包发布,支持 zip、tar.gz、tgz、tar.bz2、tbz2、rar。
9. stable、preview、dev 渠道选择。
10. 版本列表。
11. 设置最新版本。
12. 删除版本和云端文件。
13. 版本策略读取和保存。
14. 升级日志。
15. 调试输出。
16. 发布文件预览、大小和状态提示。
17. 页面美化和响应式布局。
2.16 一次性短期启动票据
已实现:
1. Launcher 和 Updater 启动 MainApp 前生成临时 ticket 文件。
2. 票据包含 app_id、device_id、version、nonce、issued_at 和 expires_at。
3. 使用本机 launch_token 对票据执行 HMAC-SHA256 签名。
4. 票据有效期为 60 秒。
5. MainApp 先原子改名抢占票据,再读取验证并立即删除。
6. 同一票据路径只能被一个 MainApp 进程消费。
7. 票据绑定应用、设备和当前版本。
8. 固定 --launcher-token 启动方式已移除。
9. 该启动协议定义为客户端协议 P3,P3 不允许降级到 P2/P1。
2.17 MainApp 启动完整性准入
已实现:
1. MainApp 每次启动时加载当前版本的 Manifest 缓存。
2. 使用 config/manifest_public_key.pem 重新验证 Manifest RSA-SHA256 签名。
3. 仅信任签名覆盖的 manifest_text,并校验 app_id、channel 和 version。
4. 校验 Manifest 声明的所有受控文件是否存在且 SHA-256 一致。
5. 递归扫描安装目录,拒绝 Manifest 未声明的额外 EXE/DLL,包括未授权插件。
6. 配置、本地状态、策略、设备身份和 Bootstrap 等运行时受保护文件采用独立规则,不与版本 Hash 混用。
7. 完整性检查位于启动健康确认之前;更新后校验失败不会写入健康标记,Updater 可触发自动回滚。
2.18 身份、授权、渠道、离线包和日志能力
已实现:
1. 设备身份签发:/api/v1/device/issue 根据 license_key、installation_id 和 machine_hash 签发 device_id。
2. 客户端身份凭证:client_identity.dat 持久化,服务端使用 RSA-SHA256 签名,客户端请求携带 X-Device-Credential。
3. 服务端逐请求验证设备凭证、授权状态、设备禁用状态、app_id 和 channel。
4. License 授权:licenses 和 license_devices 表,支持有效期、启用/禁用、最大设备数和设备占用。
5. 授权密钥只保存 SHA-256,明文只在创建时返回一次。
2026-07-14 01:39:30 +00:00
6. Launcher 首次启动时如果没有 License,会弹窗要求用户输入并自动写入注册表;当前实现会先检查 `license_key` 配置项,即使本地已有 `client_identity.dat`,清空 `license_key` 仍会触发输入框。
7. License 错误、过期、禁用、设备数达到上限或设备身份与授权不匹配时,Launcher 会清理旧设备身份并引导用户重新输入 License。
8. 动态渠道:channels 表按 app_id 隔离,管理后台支持新增、编辑、启用和停用。
9. 发布、策略、授权、设备登记和更新检查统一校验渠道。
10. 离线更新包:管理后台可生成 MUPD0001/.upd 包,包内包含签名 Manifest、包头签名、文件偏移、大小和 SHA-256。
11. Launcher/Updater 支持导入离线包,离线安装复用现有事务、Bootstrap、校验、健康确认和回滚机制。
12. 下载日志:记录下载授权、客户端完成结果、文件大小、IP、User-Agent 和时间。
13. 管理员审计日志:记录 /admin 写操作、管理员账号、角色、鉴权方式、路径、结果、状态码、IP 和 User-Agent。
2.19 SimCAE Crash Report 后端第一阶段
已实现:
1. GET /api/v1/health,返回 simcae-crash-server 健康状态。
2. POST /api/v1/crash-reports,接收 multipart/form-data 崩溃报告。
3. Bearer Token 鉴权,崩溃上传 token、符号上传 token、管理 token 分权。
4. Idempotency-Key 与 metadata.clientReportId 绑定。
5. 同一 clientReportId 首次上传返回 201 和 duplicate=false。
6. 同一 clientReportId 重复上传且内容一致返回 200 和 duplicate=true。
7. 同一 clientReportId 但内容不一致返回 409 idempotency_conflict。
8. 校验 metadata JSON 必填字段、schemaVersion、product 和 clientReportId UUID。
9. 校验 metadata.files 中声明的 minidump SHA-256。
10. 保存 metadata.json、crash.dmp、可选 attachments.zip 和 server.json。
11. crash_reports 表记录 report_id、client_report_id、版本、gitCommit、异常码、收包时间、文件 Hash、文件大小和存储路径。
12. GET /api/v1/crash-reports/{reportId} 可用管理 token 查询报告状态。
13. GET /api/v1/crash-reports/{reportId}/files/{fileName} 可用管理 token 下载私有原始文件,并记录访问审计。
14. POST /api/v1/symbols 接收 CI/发布流程上传的 metadata.json 和 symbols.zip。
15. crash_symbol_uploads 表按 product、appVersion、gitCommit、buildType、platform 索引符号包。
16. 已配置 CRASH_STORAGE_ROOTDocker 部署时落到 /data/crash_storage 持久化目录。
17. 管理后台新增“崩溃报告”页面,登录后可查看 report_id、版本、gitCommit、构建类型、渠道、异常码、崩溃时间、接收时间、来源 IP 和文件大小。
18. 管理后台可下载单条崩溃报告的 metadata.json、crash.dmp、attachments.zip 和 server.json,并写入 crash_file_access_logs。
19. 已按需求文档的 curl 联调思路完成真实 HTTP 测试:上传成功、重复上传幂等、内容冲突 409、错误 token 401、缺 metadata 400、minidump SHA-256 错误 400、管理查询、原始文件下载、符号包上传和符号包重复上传均通过。
说明:第一阶段已经做到“可靠接收、校验、保存、索引、查询和后台查看/下载”。自动符号化、聚合统计、告警和问题分派属于后续增强。
============================================================
三、部分实现的功能
============================================================
3.1 启动票据
已完成并移入“2.16 一次性短期启动票据”。
3.2 MainApp 启动完整性检查
已完成并移入“2.17 MainApp 启动完整性准入”。当 MainApp.exe 位于 Manifest 内时,其自身也会参与 Hash 验证。
3.3 回滚完整性
文件恢复和版本号恢复已经实现,但仍缺少:
1. 回滚完成后加载旧 Manifest 并进行完整 Hash 校验。
2. 更详细的逐文件回滚错误。
3. 回滚失败后的修复安装入口。
4. 完整断电、杀进程、文件占用测试。
3.4 Manifest 安全字段
RSA 签名已经实现,但仍缺少:
1. signature_alg 字段。
2. key_id 字段。
3. 当前和上一公钥同时内置。
4. 公钥轮换流程。
5. 密钥吊销机制。
3.5 升级日志
当前只记录 device_id、旧版本、新版本、success/fail 和时间。
尚缺少:
1. app_id 和 channel。
2. transaction_id。
3. error_code。
4. 失败阶段和失败文件。
5. 下载字节数和耗时。
6. 回滚结果。
7. 操作系统、架构和客户端 IP。
3.6 REST API 契约
当前更新检查主要接收 app_id、current_version 和 channel。
需求文档中的以下字段尚未进入完整闭环:
1. client_id。
2. device_id。
3. license_id。
4. platform。
5. arch。
6. operation。
7. target_version。
3.7 管理员登录
当前管理后台已经从单管理员 Token 升级为用户名/密码登录、JWT access/refresh token 和 RBAC 权限检查。
已实现:
1. admin_users 表。
2. admin_refresh_tokens 表。
3. 初始管理员自动创建。
4. Argon2 密码哈希。
5. JWT access token 和 refresh token。
6. 角色权限映射,例如 super_admin、release_admin、license_admin、auditor、crash_admin。
7. 管理员用户管理接口和页面:列表、创建、编辑角色、启用/禁用、重置密码。
8. 管理后台写操作审计记录管理员用户名、角色、认证方式和操作结果。
`ADMIN_TOKEN` 仍保留为旧脚本兼容令牌、JWT_SECRET 未配置时的默认签名密钥,以及 CRASH_ADMIN_TOKEN 为空时的崩溃报告管理兜底令牌。
仍可继续加强:
1. 登录失败审计和频率限制。
2. 更细粒度的会话管理和 Token 轮换策略。
3. 接入 FastAPI Users 或同类成熟用户认证模块,替换当前轻量用户表。
3.8 数据库结构
已有 apps、versions、version_files、version_policies、upgrade_logs、update_report、devices、licenses、license_devices、channels、download_logs、admin_audit_logs、crash_reports、crash_symbol_uploads、crash_file_access_logs。
部分字段仍未达到完整产品化设计,例如:
1. versions 缺少 status、allow_rollback、rollback_targets、描述和发布时间等字段。
2. version_files 缺少 storage_key、file_type、is_required。
3. apps 字段较少。
4. 缺少较完整的外键、索引和约束。
5. Crash Report 还缺少符号化结果、聚合状态、负责人和处理备注等二阶段字段。
3.9 精确降级控制
已有允许/禁止降级,但仍缺少:
1. rollback_targets 目标白名单。
2. 数据格式兼容性规则。
3. 主动输入目标版本。
4. 不同版本间的允许降级关系(当前仅有协议版本兼容门槛)。
5. “允许用户降级”和“管理员强制回退”的独立策略。
3.10 Crash Report 第二阶段能力
第一阶段上传、保存、去重和符号包接收已经完成,但仍缺少:
1. 自动符号化,把 crash.dmp 转成可读调用栈。
2. 根据 appVersion、gitCommit、buildType 自动匹配 symbols.zip。
3. 崩溃报告高级搜索、筛选、统计和详情页面。
4. 按异常码、版本、GPU、命令、项目等维度聚合。
5. 保留期清理,例如 90 天或 180 天。
6. 上传限流和异常峰值告警。
7. 生产环境 HTTPS、对象存储私有权限和访问审计完善。
============================================================
四、核心缺口和当前状态
============================================================
4.3 设备身份和激活
已实现,待统一联调验收:
1. 服务端 devices 表及 installation_id 唯一登记。
2. /api/v1/device/issue 首次签发和凭证恢复。
3. client_identity.dat 持久化。
4. RSA-SHA256 设备凭证签名及客户端本地验签。
5. 客户端请求自动携带 X-Device-Credential。
6. 服务端逐请求验签、查设备记录、凭证序列和禁用状态。
7. app_id、device_id 与请求内容绑定。
8. 管理后台设备列表及禁用/恢复操作。
9. 机器特征只上传 SHA-256,不上传原始机器标识。
共享 client_token 目前仅作为客户端安装介质的首次登记门槛;正式更新接口还必须通过设备凭证验证。
4.4 License 授权系统
已实现,待统一联调验收:
1. licenses 和 license_devices 数据表。
2. 授权密钥只保存 SHA-256,明文仅在创建时返回一次。
3. 授权绑定 app_id 和 channel。
4. 授权有效期和 active/disabled 状态。
5. max_devices 最大设备数及原子名额占用。
6. 设备与 license_id 绑定,阻止安装实例静默切换授权。
7. 设备身份凭证包含 license_id、channel 和 valid_until,并由 RSA-SHA256 签名。
8. MainApp 离线启动时验证签名授权凭证及有效期。
9. 服务端每次在线请求实时检查授权状态、期限、应用和渠道。
10. HTTP 401/403 授权拒绝不再被客户端误判为离线模式。
11. 管理后台支持创建、列表、设备占用量和启用/禁用授权。
尚未实现 HTTPS;正式环境中 license_key 登记请求必须通过 HTTPS 传输。
4.5 动态渠道管理
已实现,待统一联调验收:
1. channels 表,渠道按 app_id 隔离。
2. 渠道代码、显示名称、启用状态和排序。
3. 管理后台新增、编辑、启用和停用渠道。
4. 发布、版本策略和 License 下拉框从渠道 API 动态加载。
5. 新应用自动创建兼容用 stable、preview、dev 初始渠道,之后可自行修改或停用。
6. 历史应用启动迁移时自动补建初始渠道。
7. 服务端发布、策略保存、License 创建、设备登记和更新检查统一验证渠道存在且启用。
8. 设备凭证与 app_id + channel 双重绑定,不能跨渠道请求 Manifest 或下载链接。
9. 客户端 channel 为普通渠道代码,不再限制为固定枚举。
渠道采用停用而非物理删除,避免破坏历史版本、授权、设备和日志的引用关系。
4.6 离线更新包
已实现,待统一联调验收:
1. 自描述 MUPD0001/.upd 二进制容器。
2. 管理后台按已发布版本流式生成和下载离线包。
3. 包内复用在线版本的全量 Manifest 和 RSA-SHA256 签名。
4. 包头包含应用、渠道、版本、Manifest 摘要、文件路径、偏移、大小和 Hash,并独立执行 RSA-SHA256 签名。
5. 服务端从 MinIO 或本地回退存储流式输出,不生成同体积临时副本。
6. Launcher 在服务器不可用时提供离线包选择,也支持 --import-offline 主动导入。
7. Updater 支持 --offline-package,先验包签名、Manifest 签名、身份、路径、边界和 Hash。
8. 文件逐块提取到现有事务 staging,不一次性加载整个包。
9. 离线安装复用 backup、Bootstrap、安装后全量校验、健康确认和自动回滚。
10. Bootstrap 恢复阶段读取本地签名 Manifest 缓存,不依赖网络。
11. 离线导入遵守本地签名策略的有效期、offline_allowed、禁用版本和降级许可。
12. 离线包生成属于可审计的后台 POST 操作。
当前容器不压缩文件,优先保证无需额外 Qt ZIP 依赖、可流式处理和格式可验证;后续可以在保持签名格式兼容的前提下增加逐文件压缩算法字段。
4.7 插件白名单
已通过签名 Manifest 实现 DLL/EXE Hash 白名单和未知 DLL/EXE 拒绝。尚未实现插件接口版本检查、插件独立签名和 Windows 发布者验证。
4.8 服务端限流
尚未实现更新检查限流、下载链接限流、完整包下载次数限制、每日字节额度、管理 API 限流以及 HTTP 429/RATE_LIMITED。
4.9 下载日志
已实现,待统一联调验收:
1. download_logs 表及设备、License、应用、渠道、版本、文件、字节数、结果、IP、User-Agent 和时间字段。
2. 下载链接签发时记录 authorized。
3. Updater 下载并完成 SHA-256 校验后逐文件批量上报 success。
4. 下载失败时逐文件批量上报 fail。
5. 下载日志查询 API 和管理后台列表。
由于文件由 MinIO 预签名 URL 直传,服务端授权记录与客户端完成记录分开保存,避免把“获得 URL”误判为“下载成功”。
4.10 管理员审计日志
已实现,待统一联调验收:
1. admin_audit_logs 表。
2. 统一中间件记录所有 /admin 写操作。
3. 记录管理员账号、角色、鉴权方式和兼容令牌截断 Hash 指纹,不保存令牌明文。
4. 记录操作路径、HTTP 方法、查询目标、成功/失败、状态码、IP、User-Agent 和时间。
5. 鉴权失败及业务失败同样进入审计。
6. 审计日志查询 API 和管理后台列表。
审计中间件不保存请求正文,避免 License 密钥、新密码、兼容令牌等敏感内容进入日志。
4.11 代码签名
尚未实现 Windows Authenticode、发布者验证和 EXE/DLL 代码签名检查。
4.12 灰度发布
尚未实现按设备、客户、地区、百分比或批次灰度,以及失败率自动停止。
4.13 多平台
2026-07-14 10:34:00 +00:00
Windows x64 已完成主要联调;Linux x64 已完成 Qt 跨平台编译、SDK 打包脚本、客户端打包脚本、配置模板和服务端发布配置支持。尚未完成 Windows ARM64、macOS、多平台 Manifest 分流,以及真实 Linux 版 SimCAE 主程序实机联调。
============================================================
五、需求文档 Demo 里程碑状态
============================================================
第一阶段:基础框架——基本完成。
缺口:Demo 插件尚未真正通过 QLibrary/接口协议加载。
第二阶段:服务端基础能力——基本完成。
已补齐 FastAPI、SQLite、MinIO、本地回退、管理页、动态渠道、设备身份和 License 基础能力。
第三阶段:在线更新——基本完成。
正常升级已实际测试成功,差异下载、断点续传、事务、Bootstrap 替换、健康确认和自动回滚都已进入代码闭环。
第四阶段:版本策略——大部分完成。
已实现签名策略、policy_seq、防回滚、强制升级、禁用版本、最低版本、离线有效期和受控降级;缺口是 rollback_targets、数据兼容性规则和强制回退独立策略。
第五阶段:离线能力——基本完成,待更多实机验收。
已经支持离线运行、策略有效期、系统时间回拨检测和离线更新包导入。
第六阶段:可靠性与安全——大部分完成,仍需生产化加固。
已经支持事务回滚、一次性启动票据、policy_seq、时间回拨检测、启动完整性准入、EXE/DLL Hash 白名单、下载日志和管理员审计;尚未实现服务端限流、Windows 代码签名、正式管理员角色体系和完整故障矩阵测试。
SimCAE Crash Report 第一阶段——已实现。
已支持崩溃包上传、幂等去重、Hash 校验、原始文件私有保存、报告查询、原始文件下载审计和符号包上传;缺口是自动符号化、统计聚合、专门后台页面、保留期清理和生产 HTTPS/限流。
============================================================
六、需求文档验收用例状态
============================================================
1. 通过 Launcher 正常启动 MainApp:已实现。
2. 直接启动 MainApp 被拒绝:已实现,并采用 60 秒一次性 HMAC 启动票据。
3. MainApp 显示主程序和 DLL 版本:已实现简化版。
4. 后台发布版本:已实现。
5. 客户端在线升级:已实现并实测。
6. 升级后 DLL 变化:支持。
7. 升级后 MainApp 自动重启:已实现。
8. 手动篡改 DLL 后启动失败:已实现,待 Windows 实机验收。
9. 手动篡改 version_policy 后启动失败:已实现 RSA 验签。
10. 强制升级:已实现,待最终客户端测试。
11. 禁用版本:已实现,待最终客户端测试。
12. preview 客户端获取 preview 更新:基础支持,待测试。
13. 禁止降级:已实现并进行过 API 测试。
14. 离线凭证未过期时启动:已实现,待测试。
15. 离线凭证过期后拒绝启动:已实现,待测试。
16. 模拟升级失败后成功回滚:代码已实现,待完整故障测试。
============================================================
七、推荐后续开发顺序
============================================================
建议依次推进:
1. 做一轮完整 Windows 实机回归:成功升级、强制升级、版本禁用、受控降级、离线启动、离线包导入、断点续传、文件占用、杀进程、断电模拟、回滚和策略篡改。
2. 接入 SimCAECrashReporter.exe,按文档上传 metadata.json、crash.dmp 和 attachments.zip。
3. 为 Crash Report 增加管理后台页面:列表、详情、下载 metadata/dmp/attachments、符号包状态。
4. 实现 Crash Report 自动符号化:按 product/appVersion/gitCommit/buildType/platform 匹配 symbols.zip,生成调用栈。
5. 补服务端限流:更新检查、下载授权、崩溃上传、符号上传、管理接口都需要 429 保护。
6. 补生产化安全:HTTPS、CORS 白名单、管理员用户/角色/会话、Token 轮换、私有对象存储权限。
7. 补 Windows 代码签名和发布者验证,避免只依赖文件 Hash。
8. 补插件接口版本准入:插件不仅要在 Manifest 白名单里,还要声明 ABI/API 版本并由 MainApp 校验。
9. 补 rollback_targets 精确降级目标和数据兼容性规则。
10. 再做灰度发布、多平台 Manifest 分流、失败率自动停止和告警。
当前下一项最适合做的主功能:SimCAE Crash Reporter 客户端联调 + Crash Report 管理后台列表。这样可以最快证明新文档接口真的能被客户端使用。
============================================================
八、这个项目到底是什么
============================================================
一句话说明:
这是一个给 Windows 桌面软件使用的“安全自动升级 + 版本发布管理 + 崩溃报告接收”系统。
它不是单纯下载一个新 exe 覆盖旧 exe,而是一整套发布、校验、授权、回滚和审计链路。目标是让客户端软件可以安全地在线升级、离线升级、禁止不合规版本运行,并在崩溃后把原始 dump 和环境信息传回服务器。
8.1 服务端是什么
2026-07-14 01:39:30 +00:00
服务端在 update-server/ 下,核心是 FastAPI 应用。它负责:
1. 管理应用、版本、渠道和策略。
2. 接收管理员发布的软件文件。
3. 把文件存到 MinIO 或本地回退目录。
4. 为每个版本生成 Manifest。
5. 对 Manifest、策略、设备身份、离线包做 RSA 签名。
6. 给客户端签发临时下载 URL。
7. 接收升级结果、下载结果、设备登记和 License 授权。
8. 提供新版 admin-ui 管理后台;旧版 admin.html 已在开发阶段移除,不再保留兼容入口。
9. 接收 SimCAE 崩溃报告和符号包。
服务端主要存储有三类:
1. SQLite:保存结构化数据,例如 apps、versions、version_files、devices、licenses、crash_reports。
2. MinIO/本地回退目录:保存自动升级发布文件。
3. crash_storage:保存崩溃报告原始文件,例如 metadata.json、crash.dmp、attachments.zip、symbols.zip。
8.2 客户端是什么
2026-07-14 01:39:30 +00:00
客户端在 update-client/ 下,主要由几个程序配合:
1. Launcher.exe:入口程序。负责检查更新、验证策略、决定是否启动 MainApp 或 Updater。
2. Updater.exe:真正下载和安装文件的程序。负责 Manifest 验证、差异下载、断点续传、备份、校验、提交或回滚。
3. Bootstrap.exe:原生小程序。负责替换正在被占用的 EXE/DLL,尤其是 Updater 自己无法替换自己时。
4. MainApp.exe:业务主程序。启动时验证一次性启动票据和当前安装目录完整性,健康后写标记给 Updater。
5. Common:客户端公共工具代码,例如配置、HTTP、文件 Hash、策略验签、设备身份、启动票据等。
未来 SimCAE 崩溃采集还有三个程序角色:
1. SimCAE.exe:业务主程序,初始化崩溃采集并写入元数据。
2. crashpad_handler.exe 或 SimCAECrashHandler.exe:崩溃后抓 minidump。
3. SimCAECrashReporter.exe:询问用户是否上传,并调用 /api/v1/crash-reports。
8.3 一次在线升级是怎么发生的
典型流程如下:
1. 用户启动 Launcher。
2. Launcher 读取 app_config.json、local_state.json 和设备身份凭证。
3. Launcher 请求 /api/v1/update/check。
4. 服务端根据 app_id、channel、current_version、设备身份、License 和策略判断是否有更新。
5. 服务端返回目标版本和签名策略。
6. Launcher 用内置公钥验证策略签名。
7. 如果需要更新,Launcher 启动 Updater。
8. Updater 请求 /api/v1/update/manifest。
9. 服务端返回签名 Manifest。
10. Updater 验证 Manifest 签名。
11. Updater 对比本地文件 SHA-256,只下载缺失或变化的文件。
12. Updater 请求 /api/v1/update/download-url 获取临时下载地址。
13. Updater 下载文件到 staging,并校验大小和 SHA-256。
14. Updater 备份旧文件。
15. Bootstrap 接管替换文件。
16. Updater 进行安装后完整校验。
17. Updater 启动 MainApp 并等待健康标记。
18. MainApp 验证启动票据和安装目录完整性,成功后写健康标记。
19. Updater 提交事务,写入 current_version。
20. Updater 上报升级结果。
任何关键步骤失败,都尽量回滚到旧版本。
8.4 一次崩溃上报是怎么发生的
典型流程如下:
1. SimCAE 崩溃后,本地崩溃处理进程生成 crash.dmp。
2. SimCAECrashReporter.exe 准备 metadata.json,里面有版本、gitCommit、系统、异常码、用户备注、文件 Hash 等。
3. Reporter 用 multipart/form-data 请求 POST /api/v1/crash-reports。
4. 请求头带 Authorization: Bearer <crash-token>。
5. 请求头带 Idempotency-Key,值等于 metadata.clientReportId。
6. 服务端校验 token、metadata、clientReportId、minidump Hash。
7. 服务端保存 metadata.json、crash.dmp、attachments.zip 和 server.json。
8. 服务端写入 crash_reports 表。
9. 服务端返回 reportId。
10. 如果客户端超时重试,同一个 clientReportId 且内容一致会返回原来的 reportId,不会重复保存。
CI 或发布脚本还可以调用 POST /api/v1/symbols 上传 PDB/EXE/DLL 符号包,为后续自动符号化做准备。
============================================================
九、核心概念解释:Manifest、Hash、签名等
============================================================
9.1 Manifest 是什么
Manifest 可以理解成“这个版本应该长什么样”的文件清单。
在本项目里,一个版本的 Manifest 通常包含:
1. app_id:哪个软件。
2. version:哪个版本。
3. channel:哪个渠道。
4. platform 和 arch:适用平台,例如 Windows x64。
5. manifest_seqManifest 序列。
6. created_at:生成时间。
7. files:文件列表。
8. 每个文件的相对路径、大小、SHA-256 和 executable 标记。
9. signature:服务端 RSA 签名。
它的作用:
1. Updater 用它知道要下载哪些文件。
2. Updater 用它知道哪些文件没变,可以跳过下载。
3. Updater 用它校验下载文件有没有损坏或被替换。
4. MainApp 启动时用它校验安装目录是否被篡改。
5. 离线包里也带 Manifest,保证离线安装和在线安装使用同一套可信清单。
9.2 Hash 是什么
Hash 是文件内容的指纹。本项目主要使用 SHA-256。
同一个文件内容算出来的 SHA-256 一定相同;只要文件改了一个字节,SHA-256 基本就会完全变掉。
在本项目里,Hash 的用法很多:
1. 发布版本时,服务端计算每个文件的 SHA-256,写入 version_files 和 Manifest。
2. Updater 下载文件后重新计算 SHA-256,和 Manifest 对比。
3. 如果本地文件 SHA-256 已经等于 Manifest 中的值,就跳过下载。
4. MainApp 启动时计算 EXE/DLL 的 SHA-256,发现不在 Manifest 或 Hash 不一致就拒绝启动。
5. 离线包提取文件时也按 SHA-256 校验。
6. Crash Report 上传时,metadata.files 里声明 crash.dmp 的 SHA-256,服务端收到后重新计算并比对。
7. License key 和管理员 token 不保存明文,只保存 SHA-256 或指纹。
注意:Hash 只能证明“内容是否一致”,不能证明“这个 Hash 是可信的”。如果攻击者能同时改文件和 Hash,单靠 Hash 就不够。所以还需要 RSA 签名。
9.3 RSA 签名是什么
RSA 签名可以理解成服务端给某段关键内容盖章。
本项目里,服务端持有私钥,客户端内置公钥。服务端用私钥签名,客户端用公钥验证。客户端没有私钥,所以伪造不了签名。
本项目签名覆盖的内容包括:
1. Manifest。
2. version_policy。
3. client_identity。
4. 离线更新包包头。
为什么需要签名:
1. 防止 Manifest 被篡改。
2. 防止策略被篡改,例如把“禁止离线启动”改成“允许”。
3. 防止旧策略被随便替换回来。
4. 防止伪造设备身份凭证。
5. 防止离线包被人重打包。
Hash 和签名的关系:
1. Hash 负责校验文件内容。
2. 签名负责校验“这份清单/策略/身份是不是服务端认可的”。
3. 两者组合起来,才能做到既完整又可信。
9.4 version_policy 是什么
version_policy 是服务端下发给 Launcher 的运行策略。
它包含:
1. 是否强制升级。
2. 是否允许降级。
3. 是否允许离线启动。
4. 策略有效期。
5. 最低支持版本。
6. 禁用版本列表。
7. policy_seq。
8. 用户提示信息。
policy_seq 是策略序列号。每次管理员保存策略,序列号递增。客户端会记录见过的最大 policy_seq,拒绝更旧的策略,防止有人把老策略文件放回来。
9.5 启动票据是什么
启动票据是 Launcher 或 Updater 启动 MainApp 前临时生成的一次性凭证。
它解决的问题是:不允许用户绕过 Launcher 直接双击 MainApp。
票据里有 app_id、device_id、version、nonce、issued_at、expires_at,并用本机 launch_token 做 HMAC-SHA256 签名。MainApp 启动后必须抢占、验证并删除票据,验证失败就退出。
9.6 事务、staging、backup 和 Bootstrap 是什么
更新不能简单地“边下载边覆盖”,否则中途断电或文件占用会把软件弄坏。
所以项目采用事务式更新:
1. staging:新文件先下载到临时安装区。
2. backup:替换前备份旧文件。
3. upgrade_state.json:记录当前更新走到哪一步。
4. Bootstrap:负责真正替换正在被占用的 EXE/DLL。
5. post_verify:替换后全量校验。
6. commitMainApp 健康后才提交新版本。
7. rollback:失败时按 backup 恢复旧版本。
这样即使 Updater 中途退出,下次启动也能根据 upgrade_state.json 继续完成或回滚。
9.7 预签名 URL 是什么
服务端不直接把所有大文件通过 FastAPI 返回给客户端,而是把文件放在 MinIO。客户端真正下载文件时,先向服务端请求 /api/v1/update/download-url。
服务端检查设备身份、License、版本和文件列表后,返回一组临时有效的下载 URL。
这样做的好处:
1. 大文件传输交给对象存储。
2. URL 有时效,不是永久公开链接。
3. 服务端可以在签发 URL 时记录下载授权日志。
4. 客户端下载完成后再上报 success/fail。
9.8 clientReportId 和 Idempotency-Key 是什么
这是崩溃报告上传里的防重复机制。
客户端每个崩溃报告生成一个 UUID,写入 metadata.clientReportId,同时放到 HTTP 头 Idempotency-Key。
服务端规则:
1. 第一次看到这个 ID:保存报告,返回新的 reportId。
2. 再次看到这个 ID 且内容一致:返回原来的 reportIdduplicate=true。
3. 再次看到这个 ID 但内容不一致:返回 409。
这样客户端上传超时后可以放心重试,不会制造一堆重复报告。
9.9 symbols.zip 是什么
Windows 崩溃 dump 只有地址还不够,人很难直接看懂。要把地址转成函数名、文件名、行号,就需要发布版本对应的 PDB/EXE/DLL 符号文件。
symbols.zip 就是 CI 或发布流程上传的符号包。它按 product、appVersion、gitCommit、buildType、platform 保存。后续自动符号化时,服务端会用 crash report 里的版本信息找到对应符号包,再把 crash.dmp 转成人能读的调用栈。
9.10 Token、License 和设备身份的区别
这几个东西容易混:
1. CLIENT_API_TOKEN:客户端安装包自带的公共门槛,用于首次设备登记等基础访问。
2. license_key:客户授权密钥,证明这个客户/项目有权使用某个 app/channel,并限制设备数和有效期。
3. device_id/client_identity.dat:服务端给某台安装实例签发的设备身份,之后每次更新请求都要带。
4. 管理后台 JWT:网页登录成功后使用 `Authorization: Bearer <accessToken>` 调用后台接口。
5. CRASH_REPORT_TOKENSimCAECrashReporter.exe 上传崩溃报告用的 Bearer Token。
6. CRASH_SYMBOL_TOKENCI 上传 symbols.zip 用的 Bearer Token。
7. CRASH_ADMIN_TOKEN:查询和下载崩溃原始文件用;如果不配置,就复用 ADMIN_TOKEN 作为崩溃报告管理兜底令牌。
简单理解:
1. Token 是“能不能调用某类接口”。
2. License 是“这个客户有没有授权”。
3. 设备身份是“这台机器是不是已经被服务端登记过”。
4. Manifest + Hash + RSA 签名是“这个版本的文件是不是完整且可信”。
============================================================
十、SDK 和 Docker:给其他软件接入时怎么理解
============================================================
10.1 什么是 SDK
SDK 是 Software Development Kit,中文通常叫“软件开发工具包”。
它不是单个 exe,也不只是源码,而是一套让别人能把你的能力接到自己软件里的交付包。一个合格 SDK 通常包含:
1. 可直接使用的二进制文件,例如 Launcher.exe、Updater.exe、Bootstrap.exe。
2. 必要的运行时文件。对 SimCAE 这种自身已带 Qt 的软件,SDK 默认不再携带 Qt DLL,避免覆盖业务软件原有运行库。
3. 配置模板,例如 app_config.example.json 和 server_config.json。
4. 接入文档,例如如何配置 app_id、channel、qrc 服务端地址、license_key。
5. 打包脚本,例如 package-client.ps1。
6. 示例项目或 Demo。
7. API/命令行约定,例如主程序必须由 Launcher 启动,MainApp 需要写健康标记。
8. 服务端接口说明,例如 /api/v1/update/check、/api/v1/crash-reports。
所以老板说“客户端打包成 SDK 给其他软件用”,在本项目里可以理解为:
把自动升级客户端能力整理成一个可复用接入包。其他软件只要按说明放入自己的主程序和配置,就可以复用我们的 Launcher、Updater、Bootstrap、Manifest 校验、差异更新、回滚、离线包和设备授权能力。
10.2 当前项目更接近哪种 SDK
目前客户端还不是传统意义上的“给别人 include 一个头文件、link 一个 lib”的 SDK。
当前更接近“独立更新器 SDK”或“外置更新壳 SDK”:
1. 其他软件仍然有自己的主程序,例如 SimCAE.exe 或 MyApp.exe。
2. 我们提供 Launcher.exe、Updater.exe、Bootstrap.exe 和配置模板。
3. 用户以后从 Launcher.exe 启动软件,而不是直接双击业务主程序。
4. Launcher 检查更新和策略,必要时启动 Updater。
5. Updater 下载并替换业务软件目录里的文件。
6. MainApp/业务主程序需要配合健康标记、启动票据和完整性检查规则。
也就是说,它不是“库 SDK”,而是“升级运行时 SDK”。
如果以后要做成更标准的 C++ SDK,可以再拆出:
1. UpdateClientCore.lib / dll:封装检查更新、下载、校验、上报。
2. CrashReporterSDK.lib / dll:封装崩溃上报、metadata 生成、附件打包。
3. include/*.h:给接入方调用的头文件。
4. samples/:最小接入例子。
这属于下一阶段产品化封装,不影响当前先交付“独立更新器 SDK”。
10.3 当前客户端 SDK 建议交付目录
建议最终交付类似下面的目录:
UpdateClientSDK/
SimCAE自动升级SDK接入说明_v0.1.docx
sdk_manifest.json
bin/
Launcher.exe
Updater.exe
Bootstrap.exe
config/
app_config.json
server_config.json
server_config.qrc
manifest_public_key.pem
scripts/
install-sdk.ps1
package-client.ps1
package-sdk.ps1
对接方真正拿到后,通常只需要做这些事:
1. 把 SDK 的 Launcher、Updater、Bootstrap 放到业务软件运行目录。
2. 设置 app_config.jsonapp_id、channel、当前版本、主程序名等。`license_key` 可以预先填入;如果为空,用户首次启动 Launcher 时会弹窗粘贴授权密钥。
3. 设置 config/server_config.json:服务端 API 地址。该文件必须在编译 Launcher/Updater 前写好,因为它会被 qrc 编进程序。
4. 以后让用户启动 Launcher.exe。
5. 在管理后台创建应用、License、渠道和发布版本。
6. 发布新版本时选择干净的 Release 输出目录,或切换到“压缩发布包”模式上传 zip/tar.gz/tar.bz2/rar 等压缩发布包。
10.4 当前已有 package-client.ps1 的作用
2026-07-14 01:39:30 +00:00
update-client/scripts/package-client.ps1 已经是 SDK/客户端包雏形。
它目前会做这些事:
1. 从 out/bin 收集已编译好的客户端文件。
2. 检查必填配置,例如 app_id、channel、current_version、client_token、launch_token、license_key、主程序名等。
3. 检查 Launcher、Updater、Bootstrap、MainApp 是否存在。
4. 拒绝 PDB、ILK、Debug Qt DLL 等调试产物进入发布包。
5. 检查主程序是否被嵌套放错目录。
6. 拷贝 app_config.json 到 config/。
7. 删除 client_identity.dat、local_state.json、version_policy.dat 等运行时状态,避免把某台机器的身份带给别人。
8. 拷贝当前版本签名 Manifest 缓存。
9. 输出 UpdateClient.zip。
它现在更像“打最终客户端安装包”的脚本。要升级成真正 SDK,可以在它外面再包一层目录结构和说明文档。
10.5 一个实际接入例子
假设有一个第三方软件叫 MyCAD.exe,想使用我们的自动升级能力。
接入方式可以是:
1. 在服务端管理后台创建 app_id=mycad。
2. 创建渠道 stable。
3. 创建 License,把 license_key 给 MyCAD 项目。
4. MyCAD 的发布目录里包含:
- MyCAD.exe
- MyCAD 需要的 DLL 和资源
- Launcher.exe
- Updater.exe
- Bootstrap.exe
- config/app_config.json
- config/manifest_public_key.pem
5. app_config.json 里写:
- app_id=mycad
- channel=stable
- main_executable=MyCAD.exe
- license_key=服务端生成的授权密钥
6. config/server_config.json 里写 `api_base_url=http://服务器IP:8000`,并重新编译 Launcher/Updater。
7. 用户桌面快捷方式指向 Launcher.exe。
8. 管理员以后在后台发布 MyCAD 的新版本。
9. 用户启动 Launcher.exe 时自动检查、下载、替换并启动 MyCAD.exe。
这就是“把客户端升级能力作为 SDK 给其他软件使用”。
10.6 什么是 Docker
Docker 可以理解为“把服务端运行环境打包成一个标准盒子”。
2026-07-14 01:39:30 +00:00
本机直接运行 update-server/venv/bin/python3 main.py 时,依赖的是当前机器上的 Python 虚拟环境、当前目录、当前配置文件。
Docker 运行时,依赖的是镜像里的 Python、镜像里的依赖、容器里的 /app 目录和 docker-compose.yml 映射进去的数据目录。
好处是:
1. 换一台服务器也能按同样方式启动。
2. 不怕目标机器 Python 包版本乱。
3. 服务端、MinIO、初始化 bucket 可以一起编排。
4. 数据目录可以明确挂载,方便备份和迁移。
5. 出问题时可以通过 docker compose logs 查看服务日志。
10.7 当前服务端 Docker 已经具备什么
2026-07-14 01:39:30 +00:00
当前 update-server/Dockerfile 已经具备:
1. 使用 python:3.12-slim 作为基础镜像。
2026-07-14 01:39:30 +00:00
2. 安装 update-server/requirements.txt 里的 FastAPI、MinIO、cryptography 等依赖。
3. 拷贝 main.py、db.py、minio_tool.py、tables.sql。
4. 拷贝 admin-ui/dist 管理后台静态文件到容器内 /app/admin-ui/dist。
5. 使用非 root 用户 updateapp 运行。
6. 暴露 8000 端口。
7. 提供 healthcheck。
2026-07-14 01:39:30 +00:00
当前 update-server/docker-compose.yml 已经具备:
1. minio:对象存储,保存升级文件。
2. minio-init:首次启动时创建 bucket。
3. apiFastAPI 服务。
4. /data 持久化目录,用来保存 mini.db、上传文件、crash_storage 等。
5. /run/secrets/update-keys 只读挂载签名私钥。
6. 8000 端口对外提供管理后台和 API。
7. 9000 端口对外提供 MinIO 文件下载。
所以可以说:服务端已经具备 Docker 运行条件。
10.8 Docker 部署基本步骤
首次部署建议:
1. 进入 server 目录。
2. 准备 .env。
3. 设置 ADMIN_USERNAME、ADMIN_PASSWORD、ADMIN_JWT_SECRET、ADMIN_TOKEN、CLIENT_API_TOKEN、MINIO_ACCESS_KEY、MINIO_SECRET_KEY、MINIO_BUCKET、MINIO_PUBLIC_ENDPOINT、CRASH_REPORT_TOKEN、CRASH_SYMBOL_TOKEN。
4. 准备签名私钥 keys/manifest_private_key.pem。
5. 准备 runtime、minio_data 等持久化目录。
6. 执行 docker compose build。
7. 执行 docker compose up -d。
8. 用 docker compose ps 查看状态。
9. 用 docker compose logs --tail=100 api 查看服务日志。
10. 浏览器访问 http://服务器IP:8000/。
示例命令:
2026-07-14 01:39:30 +00:00
cd update-server
cp -n .env.example .env
mkdir -p runtime minio_data keys
docker compose config
docker compose build
docker compose up -d
docker compose ps
docker compose logs --tail=100 api
停止服务:
docker compose down
注意:不要随便执行 docker compose down -v,因为 -v 会删除 volume,可能导致数据库或对象存储数据丢失。
10.9 SDK 和 Docker 的关系
SDK 是给“客户端软件接入方”用的。
Docker 是给“服务端部署人员”用的。
两者不是一类东西:
1. SDK 解决“别的软件怎么接入自动升级/崩溃上报”。
2. Docker 解决“服务端怎么稳定部署和运行”。
一个完整交付可以这样分:
1. server-deploy/:服务端 Docker 部署包,包含 Dockerfile、docker-compose.yml、.env.example、README。
2. UpdateClientSDK/:客户端接入包,包含 Launcher、Updater、Bootstrap、配置模板、脚本和接入说明。
3. CrashReporterSDK/:崩溃上报接入包,包含 Reporter、metadata 模板、上传协议说明和示例。
10.10 当前离“SDK 交付”还差什么
已经具备的条件:
1. 客户端核心程序已经跑通。
2. 服务端接口已经基本完整。
3. 管理后台可以发布版本和管理策略。
4. Docker 部署文件已经具备。
5. package-client.ps1 已经能打客户端分发包。
还建议补齐:
1. SDK README:接入方从零到跑起来的步骤。
2. app_config.example.json:填写项解释和示例值。
3. 最小 Demo:一个最小 MainApp 示例,证明 SDK 可以接入别的软件。
4. 错误码文档:常见 HTTP 错误、客户端错误、升级失败阶段说明。
5. SDK 打包脚本:输出 UpdateClientSDK.zip,而不只是 UpdateClient.zip。
6. 版本命名规范:SDK 自己也要有版本号,例如 UpdateSDK-0.1.0。
7. Docker 部署 README:生产环境如何配置 HTTPS、Token、MinIO 公网地址和备份。
8. Crash Reporter 接入 Demo:演示 metadata.json、crash.dmp 和 attachments.zip 上传。
建议下一步做两个包:
2026-07-14 01:39:30 +00:00
1. update-server/docker 部署包:给运维或服务器部署人员。
2. update-client/UpdateClientSDK.zip:给其他软件开发人员。
10.11 现在具体怎么做
建议按下面顺序落地:
第一步:确认服务端 Docker 包。
1. 进入 server 目录。
2. 复制 .env.example 为 .env。
3. 修改 ADMIN_USERNAME、ADMIN_PASSWORD、ADMIN_JWT_SECRET、ADMIN_TOKEN、CLIENT_API_TOKEN、MINIO_ACCESS_KEY、MINIO_SECRET_KEY、MINIO_BUCKET、MINIO_PUBLIC_ENDPOINT、CRASH_REPORT_TOKEN、CRASH_SYMBOL_TOKEN。
4. 准备 keys/manifest_private_key.pem。
5. 执行 docker compose config,确认配置无误。
6. 执行 docker compose build。
7. 执行 docker compose up -d。
8. 浏览器打开 http://服务器IP:8000/。
9. 在后台创建应用、渠道、License。
10. 发布一个初始版本。
第二步:准备客户端 SDK 包。
2026-07-14 01:39:30 +00:00
1. 确认 update-client/out/bin 是 Release 输出目录。
2. 确认 out/bin 里有 Launcher.exe、Updater.exe、Bootstrap.exe。SimCAE 场景下不要把 SDK 自带 Qt DLL 覆盖到 SimCAE 的 bin 目录。
3. 确认 config/manifest_public_key.pem 是服务端私钥对应的公钥。
2026-07-14 01:39:30 +00:00
4. 填好 update-client/config/app_config.example.json 里的示例字段。
5. 在 Windows PowerShell 执行:
2026-07-14 01:39:30 +00:00
cd update-client
.\scripts\package-sdk.ps1 -SourceDir .\out\bin -OutputDir .\dist\UpdateClientSDK -ZipFile .\dist\UpdateClientSDK.zip -SdkVersion 0.1.0
第三步:给接入方一个最小验证方式。
1. 解压 UpdateClientSDK.zip。
2. 把接入方自己的 YourApp.exe 放到 bin 目录同级的最终产品目录中。
3. 把 config/app_config.example.json 复制成 config/app_config.json。
4. 修改 app_id、channel、client_token、license_key、main_executable,并确认 SDK 二进制已用正确的 config/server_config.json 编译。
5. 从 Launcher.exe 启动。
6. 确认能登记设备、拉取策略、启动业务主程序。
第四步:生成具体产品包。
SDK 是给开发者看的;具体产品给客户安装时,使用 package-client.ps1
2026-07-14 01:39:30 +00:00
cd update-client
.\scripts\package-client.ps1 -SourceDir .\out\bin -ConfigFile .\config\app_config.json -OutputDir .\dist\UpdateClient -ZipFile .\dist\UpdateClient.zip
第五步:补接入方文档和 Demo。
1. SDK 包内提供 Docs/00-先读我-客户端文档入口.txt 作为默认入口;Word 说明仅作为可选增强。
2. 准备一个最小 MainApp 示例,演示 --ticket-file 和 --health-file。
3. 准备一份常见错误说明。
4. 准备一份服务端 Docker 部署说明。
5. 最后再考虑拆出 C++ include/lib 形式的更传统 SDK。
```