2026-07-07 09:48:01 +00:00
|
|
|
|
# 项目总览
|
|
|
|
|
|
|
|
|
|
|
|
本文档用于总览当前项目的目标、已实现能力、主要目录、交付物和后续注意事项。适合在提交代码、交接项目或给他人快速了解项目时阅读。
|
|
|
|
|
|
|
|
|
|
|
|
## 一、项目整体是什么
|
|
|
|
|
|
|
|
|
|
|
|
本项目是一套面向 Windows 客户端软件的自动升级与版本控制系统,并补充实现了 SimCAE 崩溃报告后端接口。
|
|
|
|
|
|
|
|
|
|
|
|
整体分为两部分:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
2026-07-14 01:39:30 +00:00
|
|
|
|
update-client/ Windows 客户端升级运行时、SDK 打包脚本、接入说明文档
|
|
|
|
|
|
update-server/ FastAPI 服务端、后台页面、Docker 部署和离线交付脚本
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
文档放置约定:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
项目总览.md 项目整体说明,保留在总仓库根目录
|
|
|
|
|
|
update-client/Docs/ 客户端说明文档
|
|
|
|
|
|
update-server/Docs/ 服务端说明文档
|
2026-07-07 09:48:01 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
客户端负责:
|
|
|
|
|
|
|
|
|
|
|
|
- 检查服务端是否有新版本。
|
|
|
|
|
|
- 校验 License、设备身份和版本策略。
|
|
|
|
|
|
- 下载 Manifest 和版本文件。
|
|
|
|
|
|
- 校验 SHA-256 哈希和 Manifest 签名。
|
|
|
|
|
|
- 安装新版本、健康检查、失败回滚。
|
|
|
|
|
|
- 作为 SDK 提供给业务软件接入。
|
|
|
|
|
|
|
|
|
|
|
|
服务端负责:
|
|
|
|
|
|
|
|
|
|
|
|
- 管理应用、渠道、License、版本发布。
|
|
|
|
|
|
- 生成和签名 Manifest。
|
|
|
|
|
|
- 提供文件下载地址。
|
|
|
|
|
|
- 记录升级日志、下载日志。
|
|
|
|
|
|
- 提供后台管理页面。
|
|
|
|
|
|
- 提供 SimCAE 崩溃报告和符号包上传接口。
|
|
|
|
|
|
- 支持 Docker 和离线 Docker 部署包。
|
|
|
|
|
|
|
|
|
|
|
|
## 二、客户端当前已实现
|
|
|
|
|
|
|
|
|
|
|
|
客户端核心程序:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
Launcher.exe 用户入口,检查更新并启动业务主程序
|
|
|
|
|
|
Updater.exe 下载、校验、安装、提交或回滚
|
|
|
|
|
|
Bootstrap.exe 辅助替换运行中的 EXE/DLL
|
|
|
|
|
|
MainApp.exe 当前仓库内的示例业务程序
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
已经实现的能力:
|
|
|
|
|
|
|
2026-07-14 01:39:30 +00:00
|
|
|
|
- 首次启动时把 `config/app_config.json` 导入当前 Windows 用户注册表,后续运行配置优先从注册表读取;如果解析后的 JSON 内容变化,下一次启动会自动重新导入,并清理 `config/client_identity.dat` 和 `config/version_policy.dat`。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
- 首次设备登记和本地设备身份校验。
|
|
|
|
|
|
- License 校验。
|
2026-07-14 01:39:30 +00:00
|
|
|
|
- `license_key` 为空时,`Launcher.exe` 会弹窗让用户粘贴后台创建的 License,并写入注册表。当前实现会先检查配置项;即使本地已有 `config/client_identity.dat`,只要清空 `license_key` 仍会提示用户补填 License。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
- License 过期、错误、禁用、设备数达到上限或设备身份凭证与授权不匹配时,`Launcher.exe` 会引导用户重新输入 License,而不是要求用户手动查找配置文件。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
- 版本策略校验。
|
|
|
|
|
|
- 防止策略序号回退。
|
|
|
|
|
|
- 检测系统时间回拨。
|
|
|
|
|
|
- Manifest 下载、缓存和签名验签。
|
|
|
|
|
|
- 按 Manifest 校验安装目录文件完整性。
|
|
|
|
|
|
- 文件 SHA-256 校验。
|
|
|
|
|
|
- 启动票据 `--ticket-file` 校验,防止用户绕过 `Launcher.exe` 直接启动业务程序。
|
|
|
|
|
|
- 升级后健康检查 `--health-file`,业务程序启动成功后写入 `ok`。
|
|
|
|
|
|
- 升级失败回滚。
|
|
|
|
|
|
- 升级日志和下载日志上报服务端。
|
2026-07-14 01:39:30 +00:00
|
|
|
|
- `update-server/legacy/admin.html` 管理后台布局已优化,升级日志和下载日志默认折叠。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
## 三、客户端 SDK 交付状态
|
|
|
|
|
|
|
|
|
|
|
|
已经提供 SDK 打包脚本:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
2026-07-14 01:39:30 +00:00
|
|
|
|
update-client/scripts/package-sdk.ps1
|
|
|
|
|
|
update-client/scripts/package-client.ps1
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
客户端文档现在统一放在:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
update-client/Docs/ReadMe.txt
|
|
|
|
|
|
update-client/Docs/客户端部署说明.md
|
|
|
|
|
|
update-client/Docs/第三方依赖说明.md
|
2026-07-07 09:48:01 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`package-sdk.ps1` 用于生成给业务开发者接入的 SDK 包。当前 SDK 包结构设计为:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
UpdateClientSDK/
|
|
|
|
|
|
SimCAE自动升级SDK接入说明_v0.1.docx
|
|
|
|
|
|
sdk_manifest.json
|
|
|
|
|
|
bin/
|
|
|
|
|
|
config/
|
|
|
|
|
|
app_config.json
|
|
|
|
|
|
manifest_public_key.pem
|
|
|
|
|
|
scripts/
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
为了降低接入方理解成本,SDK 根目录只保留一个 Word 说明文档作为入口。
|
|
|
|
|
|
|
|
|
|
|
|
Word 文档已经说明:
|
|
|
|
|
|
|
|
|
|
|
|
- 拿到 `UpdateClientSDK.zip` 后怎么操作。
|
|
|
|
|
|
- 如何复制 SDK 运行时到业务软件 Release 目录。
|
|
|
|
|
|
- `app_config.json` 每个字段怎么填写。
|
|
|
|
|
|
- 为什么用户入口必须改成 `Launcher.exe`。
|
|
|
|
|
|
- 业务主程序如何解析 `--ticket-file` 和 `--health-file`。
|
|
|
|
|
|
- 可复制的 Qt/C++ 接入代码。
|
|
|
|
|
|
- 如何联调升级、健康检查和回滚。
|
|
|
|
|
|
- 如何打最终客户端包。
|
|
|
|
|
|
|
|
|
|
|
|
## 四、服务端当前已实现
|
|
|
|
|
|
|
2026-07-14 01:39:30 +00:00
|
|
|
|
服务端使用 FastAPI。当前后端已经从早期单文件形态拆成分层结构,主要入口和目录:
|
|
|
|
|
|
|
|
|
|
|
|
```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/ 新版管理后台源码
|
|
|
|
|
|
update-server/legacy/admin.html 旧版管理后台页面,保留兼容
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
服务端文档现在统一放在:
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
```text
|
2026-07-14 01:39:30 +00:00
|
|
|
|
update-server/Docs/ReadMe.txt
|
|
|
|
|
|
update-server/Docs/服务端部署说明.md
|
|
|
|
|
|
update-server/Docs/后端模板化改造说明.md
|
2026-07-07 09:48:01 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
已经实现的能力:
|
|
|
|
|
|
|
|
|
|
|
|
- 应用管理。
|
|
|
|
|
|
- 渠道管理。
|
|
|
|
|
|
- License 管理。
|
|
|
|
|
|
- 设备首次登记。
|
|
|
|
|
|
- 版本发布。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
- 版本发布支持两种方式:选择软件发布根目录,或上传压缩好的发布包。管理页面已经拆成“软件根目录/压缩发布包”两个发布方式,避免用户误以为只能选择文件夹。
|
|
|
|
|
|
- 压缩发布包支持 `zip`、`tar.gz`、`tgz`、`tar.bz2`、`tbz2`、`rar`;服务端会先解压,再按 `RELEASE_MAIN_EXECUTABLE` 校验主程序路径。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
- Manifest 生成和私钥签名。
|
|
|
|
|
|
- MinIO 对象存储上传版本文件。
|
|
|
|
|
|
- 客户端检查更新接口。
|
|
|
|
|
|
- 客户端文件下载接口。
|
|
|
|
|
|
- 升级结果上报。
|
|
|
|
|
|
- 文件下载日志记录。
|
|
|
|
|
|
- 后台管理页面。
|
2026-07-14 01:39:30 +00:00
|
|
|
|
- 后端已经按 `routes / services / repositories / schemas` 拆分,`main.py` 不再堆业务接口。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
- 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
|
2026-07-07 09:48:01 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Ubuntu 主流程使用:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-07-14 01:39:30 +00:00
|
|
|
|
cd update-server
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
2026-07-14 01:39:30 +00:00
|
|
|
|
bash ./scripts/package-offline-server.sh \
|
2026-07-07 09:48:01 +00:00
|
|
|
|
--version 0.1.0 \
|
|
|
|
|
|
--output-dir ./dist/SimCAEServerDockerPackage
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
生成:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
2026-07-14 01:39:30 +00:00
|
|
|
|
update-server/dist/SimCAEServerDockerPackage.tar.gz
|
2026-07-07 09:48:01 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
该包中包含:
|
|
|
|
|
|
|
|
|
|
|
|
- 我们自己的 `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`。
|
|
|
|
|
|
|
2026-07-09 09:00:51 +00:00
|
|
|
|
当前 Docker 镜像内已安装 `libarchive-tools`,用于后台发布 `.rar` 压缩包时调用 `bsdtar` 解压。`zip` 和 `tar.*` 由 Python 标准库直接支持。
|
|
|
|
|
|
|
2026-07-07 09:48:01 +00:00
|
|
|
|
注意:Docker 镜像包只包含软件本体和部署配置,不包含已经运行出来的数据。当前服务器上的数据库、上传过的版本文件、崩溃报告等数据在:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
2026-07-14 01:39:30 +00:00
|
|
|
|
update-server/runtime/
|
|
|
|
|
|
update-server/minio_data/
|
2026-07-07 09:48:01 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
如果要完整迁移现有服务数据,需要额外备份和恢复这两个目录。
|
|
|
|
|
|
|
|
|
|
|
|
## 七、重要安全概念
|
|
|
|
|
|
|
|
|
|
|
|
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 接入说明文档。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
- 服务端 Docker 离线包 README 生成逻辑。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
- `.gitignore`。
|
|
|
|
|
|
|
2026-07-09 09:00:51 +00:00
|
|
|
|
默认值说明:服务端和客户端示例配置里已经放了可直接试跑的默认 token、MinIO 用户名和密码,目的是让接收方不用一上来就被配置卡住。它们可以直接用于内网联调;如果进入正式生产或公网环境,再按安全要求替换。
|
|
|
|
|
|
|
2026-07-07 09:48:01 +00:00
|
|
|
|
不建议提交:
|
|
|
|
|
|
|
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`
|
2026-07-07 09:48:01 +00:00
|
|
|
|
- 任何真实 token、License、私钥和运行日志。
|
|
|
|
|
|
|
2026-07-09 09:00:51 +00:00
|
|
|
|
## 九、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/`。
|
|
|
|
|
|
|
|
|
|
|
|
## 十、运行目录权限说明
|
|
|
|
|
|
|
2026-07-14 01:39:30 +00:00
|
|
|
|
当前客户端 SDK 把部署配置源放在 `Launcher.exe` 所在目录下的 `config/app_config.json`,启动时同步到当前 Windows 用户注册表;运行时配置值优先读写注册表,路径为 `HKEY_CURRENT_USER\Software\Marsco\UpdateClientSDK\installations\<安装目录SHA256>\config`。如果检测到 `app_config.json` 变化,SDK 会删除 `config/client_identity.dat` 和 `config/version_policy.dat`,避免旧授权身份或旧策略继续生效;安装目录不可写时会弹出管理员权限确认框。设备身份、状态、策略、更新事务、备份、暂存文件、下载断点和 Manifest 缓存仍位于 `Launcher.exe` 所在目录下,例如 `config/client_identity.dat`、`config/local_state.json`、`config/version_policy.dat`、`update/` 和 `update_temp/`。当 SDK 放在 `SimCAE/bin` 时,这些运行时文件都在 `SimCAE/bin` 下,不会再在 `SimCAE/` 根目录生成顶层 `update/`。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
|
2026-07-14 01:39:30 +00:00
|
|
|
|
如果把软件放在 `C:\Program Files\SimCAE\bin` 并用普通用户启动,普通配置值已经不需要写回 `app_config.json`;但设备身份、状态、策略、更新缓存等文件仍可能需要写安装目录。当前代码对小型状态文件写入已有管理员权限确认;正式要完整安装到 `Program Files` 并自动升级大文件时,后续仍建议补 Windows 服务,或将更多运行时状态迁移到 `ProgramData` / `AppData`。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
|
|
|
|
|
|
## 十一、后续待办
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
- 等业务开发者按 SDK 文档改造 SimCAE 源码。
|
|
|
|
|
|
- 用改造后的 SimCAE Release 目录做完整升级联调。
|
|
|
|
|
|
- 确认直接启动 `SimCAE.exe` 会被拒绝,只能通过 `Launcher.exe` 启动。
|
|
|
|
|
|
- 验证升级成功、升级失败回滚、Manifest 验签失败拦截。
|
|
|
|
|
|
- 如需迁移现有服务数据,补充 `runtime/` 和 `minio_data/` 的备份恢复流程。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
- 交付配置已提供可直接试跑的默认 token 和 MinIO 账号密码;正式生产环境建议替换为客户自己的强随机值。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
- 生产私钥和测试私钥建议分开管理。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 附录:原项目进度记录
|
|
|
|
|
|
|
2026-07-14 01:39:30 +00:00
|
|
|
|
以下内容迁移自原 `update-client/项目进度.txt`,用于保留更详细的阶段性进度、功能清单和概念说明。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
软件自动升级与版本控制系统——项目进度
|
|
|
|
|
|
更新时间:2026-07-07
|
2026-07-14 01:39:30 +00:00
|
|
|
|
依据:《软件自动升级与版本控制系统开发设计文档 v0.1》、《SimCAE_Crash_Report后端接口规范》及当前 update-server/update-client 源码
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
============================================================
|
|
|
|
|
|
一、项目进度概述
|
|
|
|
|
|
============================================================
|
|
|
|
|
|
|
|
|
|
|
|
目前项目已经形成两个相关但职责不同的后端/客户端能力:
|
|
|
|
|
|
|
|
|
|
|
|
1. 软件自动升级与版本控制系统。
|
|
|
|
|
|
2. SimCAE 崩溃报告后端接口。
|
|
|
|
|
|
|
|
|
|
|
|
自动升级系统的 Windows 在线更新主链路已经基本打通,并且已经从“能升级”推进到“可控、安全、可回滚、可离线导入”的阶段。客户端具备 Launcher、Updater、MainApp、Bootstrap 四个程序;服务端具备 FastAPI、SQLite、MinIO、本地回退存储、版本发布、Manifest、下载授权、升级结果上报、版本策略、设备身份、License 授权、动态渠道、离线更新包、下载日志、管理员审计日志和管理网页。客户端可以检查版本、验证 RSA 签名、差异下载、断点续传、备份旧文件、通过 Bootstrap 替换被占用文件、删除废弃文件、启动新版本并等待健康确认;失败时可以进入自动回滚流程。
|
|
|
|
|
|
|
2026-07-09 09:00:51 +00:00
|
|
|
|
SimCAE 崩溃报告后端已经完成第一阶段核心接口:接收 SimCAECrashReporter.exe 上传的 metadata.json、crash.dmp 和可选 attachments.zip;根据 clientReportId 做幂等去重;校验 minidump SHA-256;保存原始文件;返回稳定 reportId;接收 CI/发布流程上传的 symbols.zip;并提供管理 token 查询和私有文件下载入口。管理后台已经新增基础“崩溃报告”页面,可查看报告列表并下载 metadata、dmp、attachments 和 server.json。自动符号化、聚合统计、告警和问题分派属于后续增强。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
按两份需求文档综合判断:
|
|
|
|
|
|
|
|
|
|
|
|
1. 自动升级 Demo 主链路:约 90%~95%,剩余主要是系统性故障测试和插件真实接口加载。
|
|
|
|
|
|
2. 自动升级完整设计文档:约 75%~80%,剩余主要是正式管理员体系、限流、代码签名、灰度、多平台、插件接口版本准入和生产化测试。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
3. Crash Report 第一阶段接口:约 90%~95%,已经按需求文档完成真实 HTTP 联调;剩余是 HTTPS 部署、限流和保留期清理。
|
|
|
|
|
|
4. Crash Report 完整平台:约 50%~60%,因为自动符号化、统计聚合、告警和问题分派还未做。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
当前最主要的剩余工作不再是普通在线更新,而是把已完成能力做实机回归、生产化安全加固,并把 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-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
已实现:
|
|
|
|
|
|
|
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. 保存 API 地址、App ID、当前版本、渠道、设备 ID、客户端 Token、启动 Token、平台和架构。
|
|
|
|
|
|
8. 更新成功后写入 current_version。
|
|
|
|
|
|
9. 文件型本地状态仍使用 QSaveFile 原子写入。
|
|
|
|
|
|
10. 运行时配置不进入发布包。
|
|
|
|
|
|
11. 已配置项目 .gitignore。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
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 和更新临时目录。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
8. 支持上传压缩发布包,服务端解压后按同一套路径、主程序、文件数量和空间规则校验,并自动过滤 `update/`、`update_temp/`、构建目录、运行时配置和调试产物。
|
|
|
|
|
|
9. 压缩包可多一层顶层目录,例如 `SimCAE/bin/SimCAE.exe` 会自动归一为 `bin/SimCAE.exe`。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
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 断点续传。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
3. 使用运行目录下的 `update/download_cache/<sha256>.part` 保存片段;SimCAE 场景中即 `SimCAE/bin/update/download_cache/`。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
4. Updater 重启后仍可继续未完成文件。
|
|
|
|
|
|
5. 单文件最多自动重试四次。
|
|
|
|
|
|
6. 使用递增等待时间重试。
|
|
|
|
|
|
7. 服务端不支持 Range 时安全地完整重下。
|
|
|
|
|
|
8. 下载后验证大小和 SHA-256。
|
|
|
|
|
|
9. 显示当前文件、已下载量、总下载量、速度和总进度。
|
|
|
|
|
|
10. 清理不属于当前 Manifest 的旧片段。
|
|
|
|
|
|
|
|
|
|
|
|
2.8 客户端磁盘空间预检
|
|
|
|
|
|
|
|
|
|
|
|
下载前会计算:
|
|
|
|
|
|
|
|
|
|
|
|
1. 尚未下载的字节数。
|
|
|
|
|
|
2. 已存在的断点片段大小。
|
|
|
|
|
|
3. 被覆盖文件需要的备份空间。
|
|
|
|
|
|
4. 废弃文件需要的备份空间。
|
|
|
|
|
|
5. 至少 128MB 的安全余量。
|
|
|
|
|
|
|
|
|
|
|
|
空间不足时会在开始下载前阻止更新,并显示所需空间和当前可用空间。
|
|
|
|
|
|
|
|
|
|
|
|
2.9 升级事务状态机
|
|
|
|
|
|
|
2026-07-09 09:00:51 +00:00
|
|
|
|
已实现运行目录下的 `update/upgrade_state.json`,SimCAE 场景中即 `SimCAE/bin/update/upgrade_state.json`,包含:
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
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_protocol,Launcher 上报当前协议。
|
|
|
|
|
|
10. 降级目标协议低于当前客户端协议时,服务端在安装前返回 rollback_denied。
|
|
|
|
|
|
11. 管理后台支持查看和修正历史版本的协议标签。
|
|
|
|
|
|
|
2026-07-09 09:00:51 +00:00
|
|
|
|
这里的“客户端协议”不是 HTTP 协议,也不是软件版本号,而是 Launcher/Updater 支持的升级机制能力编号。当前默认是 `3`,代表支持一次性启动票据、健康检查、策略校验、受控降级等当前客户端机制;普通发布保持默认值即可,只有未来客户端升级机制发生不兼容变化时才需要调整。
|
|
|
|
|
|
|
2026-07-07 09:48:01 +00:00
|
|
|
|
2.14 离线运行基础
|
|
|
|
|
|
|
|
|
|
|
|
已实现:
|
|
|
|
|
|
|
|
|
|
|
|
1. 在线策略本地缓存。
|
|
|
|
|
|
2. 本地策略 RSA 验签。
|
|
|
|
|
|
3. offline_allowed。
|
|
|
|
|
|
4. valid_until。
|
|
|
|
|
|
5. 策略过期时拒绝启动。
|
|
|
|
|
|
6. policy_seq 防回滚。
|
|
|
|
|
|
7. 记录上次成功启动时间。
|
|
|
|
|
|
8. 记录上次在线验证时间。
|
|
|
|
|
|
9. 检测系统时间是否回拨。
|
|
|
|
|
|
|
|
|
|
|
|
说明:目前实现的是“离线运行”,不是“离线升级”。
|
|
|
|
|
|
|
|
|
|
|
|
2.15 管理页面
|
|
|
|
|
|
|
|
|
|
|
|
已实现:
|
|
|
|
|
|
|
|
|
|
|
|
1. 管理员令牌输入、隐藏、显示和保存。
|
|
|
|
|
|
2. 修改管理员令牌。
|
|
|
|
|
|
3. 创建和选择应用。
|
|
|
|
|
|
4. 选择软件根目录发布。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
5. 切换到“压缩发布包”模式上传压缩包发布,支持 zip、tar.gz、tgz、tar.bz2、tbz2、rar。
|
|
|
|
|
|
6. stable、preview、dev 渠道选择。
|
|
|
|
|
|
7. 版本列表。
|
|
|
|
|
|
8. 设置最新版本。
|
|
|
|
|
|
9. 删除版本和云端文件。
|
|
|
|
|
|
10. 版本策略读取和保存。
|
|
|
|
|
|
11. 升级日志。
|
|
|
|
|
|
12. 调试输出。
|
|
|
|
|
|
13. 发布文件预览、大小和状态提示。
|
|
|
|
|
|
14. 页面美化和响应式布局。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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` 仍会触发输入框。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
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。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
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_ROOT,Docker 部署时落到 /data/crash_storage 持久化目录。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
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、管理查询、原始文件下载、符号包上传和符号包重复上传均通过。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
2026-07-09 09:00:51 +00:00
|
|
|
|
说明:第一阶段已经做到“可靠接收、校验、保存、索引、查询和后台查看/下载”。自动符号化、聚合统计、告警和问题分派属于后续增强。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
============================================================
|
|
|
|
|
|
三、部分实现的功能
|
|
|
|
|
|
============================================================
|
|
|
|
|
|
|
|
|
|
|
|
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,服务端用 .env 中的 ADMIN_TOKEN 校验,并且已经记录 /admin 写操作审计。
|
|
|
|
|
|
|
|
|
|
|
|
尚缺少:
|
|
|
|
|
|
|
|
|
|
|
|
1. admin_users 表。
|
|
|
|
|
|
2. 正式登录接口。
|
|
|
|
|
|
3. Token 过期时间。
|
|
|
|
|
|
4. 多管理员和角色权限。
|
|
|
|
|
|
5. 登录失败审计。
|
|
|
|
|
|
6. 会话吊销和 Token 轮换流程。
|
|
|
|
|
|
|
|
|
|
|
|
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。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
3. 崩溃报告高级搜索、筛选、统计和详情页面。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
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 多平台
|
|
|
|
|
|
|
|
|
|
|
|
目前只支持 Windows x64,尚未支持 Windows ARM64、Linux、macOS 和多平台 Manifest 分流。
|
|
|
|
|
|
|
|
|
|
|
|
============================================================
|
|
|
|
|
|
五、需求文档 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 应用。它负责:
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
1. 管理应用、版本、渠道和策略。
|
|
|
|
|
|
2. 接收管理员发布的软件文件。
|
|
|
|
|
|
3. 把文件存到 MinIO 或本地回退目录。
|
|
|
|
|
|
4. 为每个版本生成 Manifest。
|
|
|
|
|
|
5. 对 Manifest、策略、设备身份、离线包做 RSA 签名。
|
|
|
|
|
|
6. 给客户端签发临时下载 URL。
|
|
|
|
|
|
7. 接收升级结果、下载结果、设备登记和 License 授权。
|
2026-07-14 01:39:30 +00:00
|
|
|
|
8. 提供新版 admin-ui 管理后台,并保留 legacy/admin.html 作为旧版兼容入口。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
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/ 下,主要由几个程序配合:
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
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_seq:Manifest 序列。
|
|
|
|
|
|
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. commit:MainApp 健康后才提交新版本。
|
|
|
|
|
|
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 且内容一致:返回原来的 reportId,duplicate=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. X-Admin-Token:管理后台请求头里传的管理员令牌;当前服务端用 .env 里的 ADMIN_TOKEN 校验。
|
|
|
|
|
|
5. CRASH_REPORT_TOKEN:SimCAECrashReporter.exe 上传崩溃报告用的 Bearer Token。
|
|
|
|
|
|
6. CRASH_SYMBOL_TOKEN:CI 上传 symbols.zip 用的 Bearer Token。
|
|
|
|
|
|
7. CRASH_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。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
2. 必要的运行时文件。对 SimCAE 这种自身已带 Qt 的软件,SDK 默认不再携带 Qt DLL,避免覆盖业务软件原有运行库。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
3. 配置模板,例如 app_config.example.json。
|
|
|
|
|
|
4. 接入文档,例如如何配置 app_id、channel、api_base_url、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/
|
2026-07-09 09:00:51 +00:00
|
|
|
|
SimCAE自动升级SDK接入说明_v0.1.docx
|
|
|
|
|
|
sdk_manifest.json
|
2026-07-07 09:48:01 +00:00
|
|
|
|
bin/
|
|
|
|
|
|
Launcher.exe
|
|
|
|
|
|
Updater.exe
|
|
|
|
|
|
Bootstrap.exe
|
|
|
|
|
|
config/
|
2026-07-09 09:00:51 +00:00
|
|
|
|
app_config.json
|
2026-07-07 09:48:01 +00:00
|
|
|
|
manifest_public_key.pem
|
|
|
|
|
|
scripts/
|
2026-07-09 09:00:51 +00:00
|
|
|
|
install-sdk.ps1
|
2026-07-07 09:48:01 +00:00
|
|
|
|
package-client.ps1
|
2026-07-09 09:00:51 +00:00
|
|
|
|
package-sdk.ps1
|
2026-07-07 09:48:01 +00:00
|
|
|
|
对接方真正拿到后,通常只需要做这些事:
|
|
|
|
|
|
|
2026-07-09 09:00:51 +00:00
|
|
|
|
1. 把 SDK 的 Launcher、Updater、Bootstrap 放到业务软件运行目录。
|
|
|
|
|
|
2. 设置 app_config.json:server 地址、app_id、channel、当前版本、主程序名等。`license_key` 可以预先填入;如果为空,用户首次启动 Launcher 时会弹窗粘贴授权密钥。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
3. 以后让用户启动 Launcher.exe。
|
|
|
|
|
|
4. 在管理后台创建应用、License、渠道和发布版本。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
5. 发布新版本时选择干净的 Release 输出目录,或切换到“压缩发布包”模式上传 zip/tar.gz/tar.bz2/rar 等压缩发布包。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
10.4 当前已有 package-client.ps1 的作用
|
|
|
|
|
|
|
2026-07-14 01:39:30 +00:00
|
|
|
|
update-client/scripts/package-client.ps1 已经是 SDK/客户端包雏形。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
它目前会做这些事:
|
|
|
|
|
|
|
|
|
|
|
|
1. 从 out/bin 收集已编译好的客户端文件。
|
|
|
|
|
|
2. 检查必填配置,例如 app_id、channel、api_base_url、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
|
|
|
|
|
|
- api_base_url=http://服务器IP:8000
|
|
|
|
|
|
- license_key=服务端生成的授权密钥
|
|
|
|
|
|
6. 用户桌面快捷方式指向 Launcher.exe。
|
|
|
|
|
|
7. 管理员以后在后台发布 MyCAD 的新版本。
|
|
|
|
|
|
8. 用户启动 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 虚拟环境、当前目录、当前配置文件。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
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 已经具备:
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
1. 使用 python:3.12-slim 作为基础镜像。
|
2026-07-14 01:39:30 +00:00
|
|
|
|
2. 安装 update-server/requirements.txt 里的 FastAPI、MinIO、cryptography 等依赖。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
3. 拷贝 main.py、db.py、minio_tool.py、tables.sql。
|
2026-07-14 01:39:30 +00:00
|
|
|
|
4. 拷贝 update-server/legacy/admin.html 到容器内 /app/legacy/admin.html。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
5. 使用非 root 用户 updateapp 运行。
|
|
|
|
|
|
6. 暴露 8000 端口。
|
|
|
|
|
|
7. 提供 healthcheck。
|
|
|
|
|
|
|
2026-07-14 01:39:30 +00:00
|
|
|
|
当前 update-server/docker-compose.yml 已经具备:
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
1. minio:对象存储,保存升级文件。
|
|
|
|
|
|
2. minio-init:首次启动时创建 bucket。
|
|
|
|
|
|
3. api:FastAPI 服务。
|
|
|
|
|
|
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_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
|
2026-07-07 09:48:01 +00:00
|
|
|
|
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:给其他软件开发人员。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
10.11 现在具体怎么做
|
|
|
|
|
|
|
|
|
|
|
|
建议按下面顺序落地:
|
|
|
|
|
|
|
|
|
|
|
|
第一步:确认服务端 Docker 包。
|
|
|
|
|
|
|
|
|
|
|
|
1. 进入 server 目录。
|
|
|
|
|
|
2. 复制 .env.example 为 .env。
|
|
|
|
|
|
3. 修改 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 输出目录。
|
2026-07-09 09:00:51 +00:00
|
|
|
|
2. 确认 out/bin 里有 Launcher.exe、Updater.exe、Bootstrap.exe。SimCAE 场景下不要把 SDK 自带 Qt DLL 覆盖到 SimCAE 的 bin 目录。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
3. 确认 config/manifest_public_key.pem 是服务端私钥对应的公钥。
|
2026-07-14 01:39:30 +00:00
|
|
|
|
4. 填好 update-client/config/app_config.example.json 里的示例字段。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
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
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
第三步:给接入方一个最小验证方式。
|
|
|
|
|
|
|
|
|
|
|
|
1. 解压 UpdateClientSDK.zip。
|
|
|
|
|
|
2. 把接入方自己的 YourApp.exe 放到 bin 目录同级的最终产品目录中。
|
|
|
|
|
|
3. 把 config/app_config.example.json 复制成 config/app_config.json。
|
|
|
|
|
|
4. 修改 app_id、channel、api_base_url、client_token、license_key、main_executable。
|
|
|
|
|
|
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
|
2026-07-07 09:48:01 +00:00
|
|
|
|
|
|
|
|
|
|
第五步:补接入方文档和 Demo。
|
|
|
|
|
|
|
2026-07-09 09:00:51 +00:00
|
|
|
|
1. SDK 根目录只保留一个 Word 接入说明作为入口,打开包后一眼能看到。
|
2026-07-07 09:48:01 +00:00
|
|
|
|
2. 准备一个最小 MainApp 示例,演示 --ticket-file 和 --health-file。
|
|
|
|
|
|
3. 准备一份常见错误说明。
|
|
|
|
|
|
4. 准备一份服务端 Docker 部署说明。
|
|
|
|
|
|
5. 最后再考虑拆出 C++ include/lib 形式的更传统 SDK。
|
|
|
|
|
|
```
|