Files
update-system/client/项目进度.txt
T

539 lines
18 KiB
Plaintext
Raw Normal View History

2026-07-01 03:32:41 +00:00
软件自动升级与版本控制系统——项目进度
更新时间:2026-07-01
依据:《软件自动升级与版本控制系统开发设计文档 v0.1》及当前 server/client 源码
============================================================
一、项目进度概述
============================================================
目前项目已经完成一套可实际运行的 Windows 在线自动更新主链路。
客户端已经具备 Launcher、Updater、MainApp、Bootstrap 四个程序;服务端已经具备 FastAPI、SQLite、MinIO、版本发布、Manifest、下载授权、升级结果上报、版本策略管理和管理网页。客户端可以检查版本、验证 RSA 签名、差异下载、断点续传、备份旧文件、通过 Bootstrap 替换被占用文件、删除废弃文件、启动新版本并等待健康确认;失败时可以进入自动回滚流程。
目前 1.1.3 的正常升级流程已经实际运行成功,说明在线更新的成功路径已经基本打通。
按需求文档的 Demo 里程碑判断:
1. 第一阶段“基础框架”:基本完成。
2. 第二阶段“服务端基础能力”:基本完成。
3. 第三阶段“在线更新”:基本完成,正常升级已实测。
4. 第四阶段“版本策略”:大部分完成。
5. 第五阶段“离线能力”:完成离线运行基础,离线更新包尚未实现。
6. 第六阶段“可靠性与安全”:完成事务、回滚、策略防回退和时间检测;插件白名单、限流、完整日志等尚未实现。
如果只计算“在线更新 Demo”,当前完成度约为 80%。
如果按照整份设计文档计算,当前完成度约为 60%~65%。
当前最主要的剩余工作不是普通在线更新,而是进一步补齐安全准入和管理能力,包括:一次性启动票据、MainApp 启动时核心文件完整性检查、设备身份、插件白名单、下载与审计日志、动态渠道、限流、离线更新包等。
============================================================
二、已经实现的功能
============================================================
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 真正加载并调用插件接口,因此“插件实际加载”只算部分完成。
2.2 本地 JSON 配置
已实现:
1. 使用 config/app_config.json 保存客户端配置。
2. 支持旧 client.ini 自动迁移。
3. 保存 API 地址、App ID、当前版本、渠道、设备 ID、客户端 Token、启动 Token、平台和架构。
4. 更新成功后写入 current_version。
5. 使用 QSaveFile 原子写入主要配置。
6. 运行时配置不进入发布包。
7. 已配置项目 .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. /api/v1/update/check
2. /api/v1/update/manifest
3. /api/v1/update/download-url
4. /api/v1/update/report
5. /admin/publish
6. /admin/version/list
7. /admin/version/set-latest
8. /admin/version/delete
9. /admin/policy
10. /admin/policy/save
11. /admin/report/list
2.4 软件根目录和多层目录发布
已实现:
1. 浏览器一次选择软件根目录。
2. 保留所有文件的相对路径。
3. 支持多层 DLL、插件和资源目录。
4. 服务端阻止绝对路径、..、盘符路径和重复路径。
5. Manifest、MinIO 和客户端安装过程都保留相对路径。
6. 检查 MainApp.exe 是否位于发布根级。
7. 排除 Bootstrap、运行时配置、状态文件、策略缓存、PDB、ILK 和更新临时目录。
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 保存片段。
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,包含:
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、校验、健康确认和失败回滚机制。
2.14 离线运行基础
已实现:
1. 在线策略本地缓存。
2. 本地策略 RSA 验签。
3. offline_allowed。
4. valid_until。
5. 策略过期时拒绝启动。
6. policy_seq 防回滚。
7. 记录上次成功启动时间。
8. 记录上次在线验证时间。
9. 检测系统时间是否回拨。
说明:目前实现的是“离线运行”,不是“离线升级”。
2.15 管理页面
已实现:
1. 管理员令牌输入、隐藏、显示和保存。
2. 修改管理员令牌。
3. 创建和选择应用。
4. 选择软件根目录发布。
5. stable、preview、dev 渠道选择。
6. 版本列表。
7. 设置最新版本。
8. 删除版本和云端文件。
9. 版本策略读取和保存。
10. 升级日志。
11. 调试输出。
12. 发布文件预览、大小和状态提示。
13. 页面美化和响应式布局。
============================================================
三、部分实现的功能
============================================================
3.1 启动票据
当前实现:Launcher 或 Updater 通过 --launcher-token 参数启动 MainApp,直接双击 MainApp 会被拒绝。
与需求差距:
1. 不是临时 ticket 文件。
2. 没有 app_id、client_id、device_id、version、nonce、issued_at、expires_at。
3. 没有一次性使用后删除。
4. 没有防重放。
5. 使用的是配置中的长期固定 Token。
结论:只能防止普通用户直接双击,不能算完整安全启动票据。
3.2 MainApp 启动完整性检查
当前 MainApp 会检查启动 Token、版本策略签名、策略有效期、policy_seq 和时间回拨。
尚缺少:
1. 启动时重新验证当前 Manifest。
2. 核心 EXE/DLL 全量 Hash。
3. 检测受控目录中未被 Manifest 声明的额外 EXE/DLL。
4. 插件白名单。
5. MainApp 自身 Hash 验证。
因此需求中的“手动篡改 DLL 后启动失败”目前不能保证通过。
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,服务端只保存 Token Hash。
尚缺少:
1. admin_users 表。
2. 正式登录接口。
3. Token 过期时间。
4. 多管理员和角色权限。
5. 登录失败审计。
6. 管理员操作审计。
3.8 数据库结构
已有 apps、versions、version_files、version_policies、upgrade_logs、update_report。
部分字段仍未达到文档设计,例如:
1. versions 缺少 status、allow_rollback、rollback_targets、描述和发布时间等字段。
2. version_files 缺少 storage_key、file_type、is_required。
3. apps 字段较少。
4. 缺少较完整的外键、索引和约束。
3.9 精确降级控制
已有允许/禁止降级,但仍缺少:
1. rollback_targets 目标白名单。
2. 数据格式兼容性规则。
3. 主动输入目标版本。
4. 不同版本间的允许降级关系。
5. “允许用户降级”和“管理员强制回退”的独立策略。
============================================================
四、尚未实现的核心功能
============================================================
4.1 一次性短期启动票据
需要实现临时 ticket 文件、有效期、nonce、签名、版本绑定、设备绑定、一次使用后删除和防重放。
4.2 MainApp 核心文件完整性准入
需要在每次启动时验证当前已安装 Manifest、MainApp、核心 DLL、插件目录和额外可执行文件。
4.3 设备身份和激活
尚未实现:
1. client_identity.dat 正式签发。
2. 设备身份 RSA 签名。
3. /api/v1/device/issue。
4. 首次设备激活和刷新。
5. 设备禁用。
6. devices 表。
7. 每次请求验证设备身份。
当前 device_id 只是普通配置字符串,客户端请求使用共享 client_token。
4.4 License 授权系统
尚未实现 licenses 表、授权有效期、设备绑定、最大设备数、授权禁用、许可证签名和授权渠道控制。
4.5 动态渠道管理
目前 stable、preview、dev 写死在客户端和后台。
尚未实现 channels 表、渠道新增、启用、禁用、渠道名称和按应用配置渠道。
4.6 离线更新包
尚未实现:
1. .upd 离线包。
2. 离线包生成。
3. 离线包 Manifest 和 RSA 签名。
4. 本地导入和校验。
5. 解包到 staging。
6. 离线事务安装。
4.7 插件白名单
尚未实现插件目录扫描、未知 DLL 检测、插件签名/Hash 准入和插件接口版本检查。
4.8 服务端限流
尚未实现更新检查限流、下载链接限流、完整包下载次数限制、每日字节额度、管理 API 限流以及 HTTP 429/RATE_LIMITED。
4.9 下载日志
尚未实现 download_logs 表,以及设备、版本、文件、下载字节、结果、IP 和时间等业务下载记录。
4.10 管理员审计日志
尚未实现 admin_audit_logs,包括发布、删除、设置最新、修改策略、修改令牌、登录失败等操作记录。
4.11 代码签名
尚未实现 Windows Authenticode、发布者验证和 EXE/DLL 代码签名检查。
4.12 灰度发布
尚未实现按设备、客户、地区、百分比或批次灰度,以及失败率自动停止。
4.13 多平台
目前只支持 Windows x64,尚未支持 Windows ARM64、Linux、macOS 和多平台 Manifest 分流。
============================================================
五、需求文档 Demo 里程碑状态
============================================================
第一阶段:基础框架——基本完成。
缺口:Demo 插件尚未真正通过接口加载。
第二阶段:服务端基础能力——基本完成。
缺口:动态 channels 表和设备身份未完成。
第三阶段:在线更新——基本完成。
正常升级 1.1.3 已实际测试成功。
第四阶段:版本策略——大部分完成。
缺口:动态渠道、rollback_targets 和强制回退独立策略。
第五阶段:离线能力——部分完成。
已经支持离线运行和策略有效期;尚未实现离线更新包。
第六阶段:可靠性与安全——部分完成。
已经支持事务回滚、policy_seq 和时间回拨检测;尚未实现插件白名单、限流、完整下载日志和启动完整性准入。
============================================================
六、需求文档验收用例状态
============================================================
1. 通过 Launcher 正常启动 MainApp:已实现。
2. 直接启动 MainApp 被拒绝:已实现,但启动票据较弱。
3. MainApp 显示主程序和 DLL 版本:已实现简化版。
4. 后台发布版本:已实现。
5. 客户端在线升级:已实现并实测。
6. 升级后 DLL 变化:支持。
7. 升级后 MainApp 自动重启:已实现。
8. 手动篡改 DLL 后启动失败:未完整实现。
9. 手动篡改 version_policy 后启动失败:已实现 RSA 验签。
10. 强制升级:已实现,待最终客户端测试。
11. 禁用版本:已实现,待最终客户端测试。
12. preview 客户端获取 preview 更新:基础支持,待测试。
13. 禁止降级:已实现并进行过 API 测试。
14. 离线凭证未过期时启动:已实现,待测试。
15. 离线凭证过期后拒绝启动:已实现,待测试。
16. 模拟升级失败后成功回滚:代码已实现,待完整故障测试。
============================================================
七、推荐后续开发顺序
============================================================
建议依次实现:
1. 一次性、短期、防重放的启动票据。
2. MainApp 启动时核心文件完整性检查和插件白名单。
3. 设备身份签发与服务端验证。
4. 升级日志错误码、失败阶段、回滚结果和下载日志。
5. 管理员审计日志。
6. 动态渠道管理。
7. rollback_targets 精确降级目标。
8. 服务端限流。
9. 离线更新包。
10. 最后统一进行成功升级、强制升级、版本禁用、离线、断点续传、文件占用、进程中断、回滚和策略篡改测试。
当前下一项最值得实现的是:一次性启动票据,以及 MainApp 启动时的核心文件完整性检查。这两项是目前客户端启动准入中最大的安全缺口。