Client:实现安全的在线检查、下载和下次启动更新 #93

Closed
opened 2026-08-10 12:05:47 +08:00 by ila · 4 comments
Owner

基本信息

  • 类型:需求
  • 父级大工单:#1
  • 所属 MVP / 版本:MVP 后续 / 在线更新
  • 阶段:发布与升级
  • 依赖:#92

要解决什么

#92 已建立 Launcher.exe + app/ + data/ 发布结构和 autobuy——manifest.json,但 Client 仍需人工取得并替换更新包。需要在不阻塞界面、不覆盖本地数据、不保存更新凭据的前提下,实现用户主动检查、下载并在下次启动安全应用更新。

做什么 / 不做什么

  • 做:
    • 设置页新增“软件更新”区域,展示当前版本、HTTPS 清单地址、状态和“检查更新”按钮。
    • 清单地址作为非敏感设置保存;地址为空时提示配置。
    • 使用 QObject + moveToThread 在后台检查和下载,防重复点击并处理窗口销毁后的迟到结果。
    • 严格解析版本和清单,只允许 HTTPS;重定向后仍须是 HTTPS,更新包须与清单同源。
    • 校验更新包文件名、大小和 SHA256,限制清单和更新包最大体积。
    • 安全解压到根目录 data/update/app.new,拒绝绝对路径、..、符号链接和 app/ 以外内容。
    • 下载完成后只提示“关闭并重新启动后生效”,不强制关闭正在运行的任务。
    • Launcher.exe 在主程序未运行时应用待更新目录,保留一份 app.old;移动失败立即恢复。
    • 主程序启动后写健康标记;新版本未健康启动时,Launcher 在安全时机恢复 app.old。
    • 增加版本、网络、清单、哈希、安全解压、Launcher 更新和 Qt Worker/UI 测试。
  • 不做:
    • 不实现强制更新、静默下载、定时自动检查、差分补丁或 Launcher 自更新。
    • 不保存或发送更新账号、密码、Token、Cookie。
    • 不允许 HTTP,不忽略 TLS 证书错误。
    • 本工单不增加数字签名;HTTPS 与 SHA256 的边界在文档中明确。
    • 不改变 Admin 四个 Client 业务接口,不改变数据库结构、采购流程和真实下单开关。

怎么做

  • 新增 client/src/update_service.py:清单模型、严格版本比较、HTTPS 下载、同源校验、大小与 SHA256 校验、安全解压和待更新状态文件。
  • 新增 client/src/update_ui_event.py:检查/下载 Worker 和生命周期管理,严格使用 QObject + moveToThread。
  • 在 client/src/settings_ui.py 增加软件更新表单,在 settings_ui_event.py 装配更新事件;地址使用显式保存或检查时保存,并保留输入错误。
  • 扩展 client/build_tools/launcher.py:识别仍运行的主程序、应用/恢复更新、维护 app.old 和健康状态。
  • 主程序窗口成功创建后写入健康标记;源码运行时不执行目录替换。
  • 扩展打包清单和构建验证,确保更新所需模块进入发布包。
  • 同步 docs/client/01-requirements.md、02-architecture.md、05-ui-specification.md、06-quality-security.md。

验收标准

  • 设置页可配置 HTTPS 清单地址并显示当前版本;HTTP、空值和非法 URL 有明确提示。
  • 检查与下载不阻塞 Qt 主线程,重复点击不会创建并行任务,关闭页面后迟到结果不会访问已销毁控件。
  • 当前版本为最新时显示稳定状态;发现新版本时展示版本并由用户确认是否下载。
  • 下载后大小与 SHA256 必须与 autobuy——manifest.json 一致,不一致时不生成待更新状态。
  • 只接受 HTTPS、重定向后 HTTPS、清单同源的更新包。
  • 安全解压拒绝路径穿越、绝对路径、符号链接和 app/ 之外内容。
  • 更新只写入 data/update/,不覆盖数据库、日志、任务和凭据。
  • Launcher 下次启动能把 app.new 应用为 app 并保留 app.old;移动失败或新版本健康检查失败时能恢复旧版本。
  • 程序已运行时再次启动 Launcher 不替换 app/,并给出可理解提示。
  • 更新成功只提示用户重新启动,不强制中断当前采集或采购任务。
  • 真实下单保持默认关闭;未新增 Admin 业务接口和敏感信息落盘。
  • 自动测试和打包验证通过;未验证的真实 HTTPS 发布服务器与干净 Windows 场景明确记录。

怎么验证

从 client/ 执行:

C:/Python310/python.exe -m py_compile src/update_service.py src/update_ui_event.py build_tools/launcher.py
$env:QT_QPA_PLATFORM="offscreen"
C:/Python310/python.exe -m pytest test -q
Remove-Item Env:QT_QPA_PLATFORM
.\build_client.bat

使用本地 HTTPS 测试替身覆盖清单、下载、重定向、哈希失败和恶意 ZIP;使用临时发布目录覆盖 Launcher 成功应用、移动失败、健康失败回退和主程序仍运行。最终还需对真实 HTTPS 静态发布地址及打包产物进行人工验证。

风险和回退

  • SHA256 只能发现文件与清单不一致,不能在发布服务器整体失陷时证明发布者身份;后续可独立增加签名清单。
  • Windows 文件占用或杀毒软件可能导致目录替换失败;Launcher 必须先恢复旧 app,不���在没有可启动版本时继续。
  • 更新状态全部在 data/update/,发生问题可关闭程序后移走该目录,并把 app.old 恢复为 app。
  • 改动不涉及数据库迁移;可按本工单提交整体回退,保留 #92 的人工更新方式。
## 基本信息 - 类型:需求 - 父级大工单:#1 - 所属 MVP / 版本:MVP 后续 / 在线更新 - 阶段:发布与升级 - 依赖:#92 ## 要解决什么 #92 已建立 `Launcher.exe + app/ + data/` 发布结构和 `autobuy——manifest.json`,但 Client 仍需人工取得并替换更新包。需要在不阻塞界面、不覆盖本地数据、不保存更新凭据的前提下,实现用户主动检查、下载并在下次启动安全应用更新。 ## 做什么 / 不做什么 - 做: - 设置页新增“软件更新”区域,展示当前版本、HTTPS 清单地址、状态和“检查更新”按钮。 - 清单地址作为非敏感设置保存;地址为空时提示配置。 - 使用 `QObject + moveToThread` 在后台检查和下载,防重复点击并处理窗口销毁后的迟到结果。 - 严格解析版本和清单,只允许 HTTPS;重定向后仍须是 HTTPS,更新包须与清单同源。 - 校验更新包文件名、大小和 SHA256,限制清单和更新包最大体积。 - 安全解压到根目录 `data/update/app.new`,拒绝绝对路径、`..`、符号链接和 `app/` 以外内容。 - 下载完成后只提示“关闭并重新启动后生效”,不强制关闭正在运行的任务。 - `Launcher.exe` 在主程序未运行时应用待更新目录,保留一份 `app.old`;移动失败立即恢复。 - 主程序启动后写健康标记;新版本未健康启动时,Launcher 在安全时机恢复 `app.old`。 - 增加版本、网络、清单、哈希、安全解压、Launcher 更新和 Qt Worker/UI 测试。 - 不做: - 不实现强制更新、静默下载、定时自动检查、差分补丁或 Launcher 自更新。 - 不保存或发送更新账号、密码、Token、Cookie。 - 不允许 HTTP,不忽略 TLS 证书错误。 - 本工单不增加数字签名;HTTPS 与 SHA256 的边界在文档中明确。 - 不改变 Admin 四个 Client 业务接口,不改变数据库结构、采购流程和真实下单开关。 ## 怎么做 - 新增 `client/src/update_service.py`:清单模型、严格版本比较、HTTPS 下载、同源校验、大小与 SHA256 校验、安全解压和待更新状态文件。 - 新增 `client/src/update_ui_event.py`:检查/下载 Worker 和生命周期管理,严格使用 `QObject + moveToThread`。 - 在 `client/src/settings_ui.py` 增加软件更新表单,在 `settings_ui_event.py` 装配更新事件;地址使用显式保存或检查时保存,并保留输入错误。 - 扩展 `client/build_tools/launcher.py`:识别仍运行的主程序、应用/恢复更新、维护 `app.old` 和健康状态。 - 主程序窗口成功创建后写入健康标记;源码运行时不执行目录替换。 - 扩展打包清单和构建验证,确保更新所需模块进入发布包。 - 同步 `docs/client/01-requirements.md`、`02-architecture.md`、`05-ui-specification.md`、`06-quality-security.md`。 ## 验收标准 - [x] 设置页可配置 HTTPS 清单地址并显示当前版本;HTTP、空值和非法 URL 有明确提示。 - [x] 检查与下载不阻塞 Qt 主线程,重复点击不会创建并行任务,关闭页面后迟到结果不会访问已销毁控件。 - [x] 当前版本为最新时显示稳定状态;发现新版本时展示版本并由用户确认是否下载。 - [x] 下载后大小与 SHA256 必须与 `autobuy——manifest.json` 一致,不一致时不生成待更新状态。 - [x] 只接受 HTTPS、重定向后 HTTPS、清单同源的更新包。 - [x] 安全解压拒绝路径穿越、绝对路径、符号链接和 `app/` 之外内容。 - [x] 更新只写入 `data/update/`,不覆盖数据库、日志、任务和凭据。 - [x] Launcher 下次启动能把 `app.new` 应用为 `app` 并保留 `app.old`;移动失败或新版本健康检查失败时能恢复旧版本。 - [x] 程序已运行时再次启动 Launcher 不替换 `app/`,并给出可理解提示。 - [x] 更新成功只提示用户重新启动,不强制中断当前采集或采购任务。 - [x] 真实下单保持默认关闭;未新增 Admin 业务接口和敏感信息落盘。 - [x] 自动测试和打包验证通过;未验证的真实 HTTPS 发布服务器与干净 Windows 场景明确记录。 ## 怎么验证 从 `client/` 执行: ```powershell C:/Python310/python.exe -m py_compile src/update_service.py src/update_ui_event.py build_tools/launcher.py $env:QT_QPA_PLATFORM="offscreen" C:/Python310/python.exe -m pytest test -q Remove-Item Env:QT_QPA_PLATFORM .\build_client.bat ``` 使用本地 HTTPS 测试替身覆盖清单、下载、重定向、哈希失败和恶意 ZIP;使用临时发布目录覆盖 Launcher 成功应用、移动失败、健康失败回退和主程序仍运行。最终还需对真实 HTTPS 静态发布地址及打包产物进行人工验证。 ## 风险和回退 - SHA256 只能发现文件与清单不一致,不能在发布服务器整体失陷时证明发布者身份;后续可独立增加签名清单。 - Windows 文件占用或杀毒软件可能导致目录替换失败;Launcher 必须先恢复旧 `app`,不���在没有可启动版本时继续。 - 更新状态全部在 `data/update/`,发生问题可关闭程序后移走该目录,并把 `app.old` 恢复为 `app`。 - 改动不涉及数据库迁移;可按本工单提交整体回退,保留 #92 的人工更新方式。
Author
Owner

状态:进行中。属于 MVP 后续在线更新能力,依赖 #92;不纳入 #2 清单。

状态:进行中。属于 MVP 后续在线更新能力,依赖 #92;不纳入 #2 清单。
Author
Owner

????(?????)

  • ????:6d6829e feat: ?? Client ????????? (#93)
  • ???????????,??????? Qt Worker ???;????????,??????
  • ????????????????????? HTTPS URL;?????,??????SHA256?????? ZIP ???
  • Launcher ??????? app,?? app.old;????????????????????????
  • ????? 0.2.0?0.1.0 ?? Launcher ???????,???????????????;? 0.2.0 ????????

????

  • C:/Python310/python.exe -m pytest test -q --disable-warnings:256 passed?
  • .\build_client.bat:???? 0.2.0 ??????? ZIP??? ZIP ? autobuy--manifest.json?
  • ??????:0.1.0 ? 0.2.0 ??;app.old ?? 0.1.0,data ????,??????,pending.json ???
  • ??? SHA256:e485457589490e5b4724f53a7370a0d10655e16461edaccd40d88d68bc92149f?

???

  • ???? HTTPS ??????,????????????
  • ????????? Python ? Windows ????????????
  • ?? Launcher ?????????????,???????????
## ????(?????) - ????:`6d6829e feat: ?? Client ????????? (#93)` - ???????????,??????? Qt Worker ???;????????,?????? - ????????????????????? HTTPS URL;?????,??????SHA256?????? ZIP ??? - Launcher ??????? `app`,?? `app.old`;???????????????????????? - ????? `0.2.0`?`0.1.0` ?? Launcher ???????,???????????????;? `0.2.0` ???????? ### ???? - `C:/Python310/python.exe -m pytest test -q --disable-warnings`:256 passed? - `.\build_client.bat`:???? 0.2.0 ??????? ZIP??? ZIP ? `autobuy--manifest.json`? - ??????:0.1.0 ? 0.2.0 ??;`app.old` ?? 0.1.0,`data` ????,??????,`pending.json` ??? - ??? SHA256:`e485457589490e5b4724f53a7370a0d10655e16461edaccd40d88d68bc92149f`? ### ??? - ???? HTTPS ??????,???????????? - ????????? Python ? Windows ???????????? - ?? Launcher ?????????????,???????????
Author
Owner

本地归档

  • 归档文档:docs/task/93-client在线更新与安全回退.md
  • 归档提交:5abe81e docs: 归档任务 #93
  • 当前状态:实现与开发验证已完成,等待用户验收;验收通过后关闭工单并更新父工单 #1。
## 本地归档 - 归档文档:`docs/task/93-client在线更新与安全回退.md` - 归档提交:`5abe81e docs: 归档任务 #93` - 当前状态:实现与开发验证已完成,等待用户验收;验收通过后关闭工单并更新父工单 #1。
Author
Owner

用户已于 2026-08-11 明确验收通过。

  • 本地归档状态已更新为“已完成,用户验收通过”
  • 验收记录提交:eca9289
  • 本工单按项目流程关闭
用户已于 2026-08-11 明确验收通过。 - 本地归档状态已更新为“已完成,用户验收通过” - 验收记录提交:`eca9289` - 本工单按项目流程关闭
ila closed this issue 2026-08-11 10:58:21 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chengma/cmautobuy#93