diff --git a/.clinerules/01-generalupdate-init.md b/.clinerules/01-generalupdate-init.md index e0ef229..a3307c4 100644 --- a/.clinerules/01-generalupdate-init.md +++ b/.clinerules/01-generalupdate-init.md @@ -13,7 +13,7 @@ SetSource(url, key) -> SetOption(AppType.Client) -> LaunchAsync() ## 4 大场景: None/UpgradeOnly/MainOnly/Both -## manifest.json 字段 +## generalupdate.manifest.json 字段 MainAppName, UpdateAppName, ProductId, InstallPath, UpdatePath, ClientVersion, UpgradeClientVersion ## 关键: UpgradeApp须随首个版本发布, 双进程AppSecretKey一致, 版本4段式 diff --git a/.clinerules/05-generalupdate-troubleshoot.md b/.clinerules/05-generalupdate-troubleshoot.md index beff63c..26ec99a 100644 --- a/.clinerules/05-generalupdate-troubleshoot.md +++ b/.clinerules/05-generalupdate-troubleshoot.md @@ -17,7 +17,7 @@ tags: ["dotnet", "generalupdate"] ## 诊断流程 (5步) -1. **版本号** — `manifest.json` 的 `ClientVersion` / `UpgradeClientVersion` 是否与服务端返回的版本一致? 4段式版本(string)严格匹配。 +1. **版本号** — `generalupdate.manifest.json` 的 `ClientVersion` / `UpgradeClientVersion` 是否与服务端返回的版本一致? 4段式版本(string)严格匹配。 2. **manifest 字段** — `MainAppName`、`UpdateAppName`、`ProductId`、`InstallPath`、`UpdatePath` 是否填写正确且与服务端一致? 3. **UpgradeApp 存在** — 主应用目录下是否存在 `Upgrade` 子目录及 `Upgrade.exe` (或对应平台可执行文件)? 随首个版本一同发布的。 4. **策略可访问** — `SetSource(url, key)` 配置的 API/OSS 端点能否正常响应? OSS 策略需要 `SetOSSInfo` 配置正确。 @@ -29,7 +29,7 @@ tags: ["dotnet", "generalupdate"] |------|------|-----------| | **升级没启动** | `LaunchAsync()` 未调用 / 调用顺序错误 / 主进程未检测到新版本 | 确认 `Bootstrap.LaunchAsync()` 在 `Main()` 中调用, 且 `SetOption(AppType.Client)` 正确 | | **Method not found** | 引用的 GeneralUpdate 版本与服务端不匹配 / 混用不同大版本 | 统一 NuGet 版本, 清理 bin/obj 后重新生成 | -| **路径超长 (>260)** | Windows 路径限制, `InstallPath` / `UpdatePath` 过深 | 缩短安装路径; 或启用 `SetOption` 中的长路径支持 (`LongPathSupport = true`) | +| **路径超长 (>260)** | Windows 路径限制, `InstallPath` / `UpdatePath` 过深 | 缩短安装路径; 或在项目配置中启用长路径支持 (`app.manifest` / `runtimeconfig`) | | **IPC 暴露** | EncryptedFile IPC 写入明文 / NamedPipe 未鉴权 | 启用加密传输; NamedPipe 设置 `AccessControl` 限制连接用户; 不使用 `AutoFallback` 回退到明文 | | **跨租户泄露** | 多租户部署共用 `ProductId` / `AppSecretKey` | 每个租户分配独立 `ProductId` 和 `AppSecretKey`; 服务端鉴权时校验租户标识 | @@ -38,7 +38,7 @@ tags: ["dotnet", "generalupdate"] | 问题 | 原因 | 排查/解决 | |------|------|-----------| | **静默更新不生效** | `UpdateMode.Silent` 配置缺失 / 权限不足无法后台启动 | 确认 `SetOption(UpdateMode.Silent)` 且应用有后台运行权限; 检查 `LaunchCommand` 参数是否正确传递 | -| **无限循环更新** | `ClientVersion` 与服务端最新版本不一致导致不断检测到"新版本" | 验证更新后是否更新了本地 `manifest.json` 的版本号; 确认服务端已标记该版本为"已发布"状态 | +| **无限循环更新** | `ClientVersion` 与服务端最新版本不一致导致不断检测到"新版本" | 验证更新后是否更新了本地 `generalupdate.manifest.json` 的版本号; 确认服务端已标记该版本为"已发布"状态 | | **OSS 无更新** | Bucket 配置错误 / 文件命名不匹配 / 签名过期 | `curl` 测试 OSS URL 是否直接可下载; 确认 `SetOSSInfo` 的 endpoint/bucket/region 正确 | | **文件占用** | 更新时目标文件正在被使用 (主进程/杀软/搜索索引) | 关闭主进程后更新; 使用 `Bowl` 守护进程管理进程生命周期; 排除杀软扫描目录 | @@ -50,9 +50,9 @@ tags: ["dotnet", "generalupdate"] | **差分更新失败** | BSDIFF 补丁应用时数据不完整 / 旧版本基准文件不匹配 | 校验原始文件哈希; 确认差分策略仅在同源版本间使用 | | **升级后配置丢失** | `UpdatePath` 未包含配置文件 / 更新后未合并 `appsettings.json` | 在更新流程中添加配置文件合并步骤; 或使用外部配置中心 | | **信号量泄露** | IPC 信号量未正确释放, 重启后端口/资源被占用 | 重启系统; 检查 `Bowl` 是否正确清理资源; 监控信号量计数 | -| **ZIP 遍历写入** | 恶意更新的 ZIP 包包含 `../` 路径覆盖系统文件 | 使用 `ZipSecurity` 校验项, 拒绝包含 `../` 或绝对路径的条目 | +| **ZIP 遍历写入** | 恶意更新的 ZIP 包包含 `../` 路径覆盖系统文件 | 解压时校验 ZIP 条目路径, 拒绝包含 `../` 或绝对路径的条目 | | **AOT 编译失败** | 动态加载 / 反射代码未适配 NativeAOT | 添加 `[DynamicDependency]` 特性; 避免 `Assembly.Load` 等动态加载 | -| **SignalR 重连慢** | WebSocket 长连接断开后 `RetryDelay` 配置过长 | 调整 `SignalRRetryPolicy` 的 `RetryDelay` 和 `MaxRetryCount` | +| **SignalR 重连慢** | WebSocket 长连接断开后 `RetryDelay` 配置过长 | 降低重试延迟并限制最大重试次数, 或自定义重连策略 | | **SSL 证书错误** | 自签名证书未受信 / 服务器 SNI 不匹配 | 配置 `SslValidationPolicy`; 或添加自定义证书验证委托 | ## L级 (Low) — 非关键 @@ -69,7 +69,7 @@ tags: ["dotnet", "generalupdate"] ```bash # 测试 API 是否可访问 -curl -v "http://your-server/api/update/check?appName=MyApp&clientVersion=1.0.0.1" +curl -v -X POST "http://your-server/Upgrade/Verification" -H "Content-Type: application/json" -d '{"appKey":"xxx","appType":1,"clientVersion":"1.0.0.1","productId":"xxx","platform":"windows","tenantId":"default"}' # 验证 manifest.json 内容 cat /path/to/app/manifest.json | jq . @@ -98,6 +98,6 @@ powershell "Get-CimInstance Win32_Semaphore | Select-Object Name, Count" ## 安全注意事项 - IPC 传输 — 不使用明文 `AutoFallback`, 始终启用加密 -- ZIP 包校验 — 启用 `ZipSecurity` 拒绝路径穿越条目 +- ZIP 包校验 — 解压时校验 ZIP 条目路径, 拒绝 `../` 和绝对路径条目 - 签名验证 — 程序集签名: 在 `SetSource` 中提供公钥, 拒绝未签名更新 - AppSecretKey 管理 — 硬编码在客户端是最后手段; 优先从启动参数或环境变量注入 diff --git a/.cursor/rules/generalupdate-init.mdc b/.cursor/rules/generalupdate-init.mdc index 085bffd..c3cdf28 100644 --- a/.cursor/rules/generalupdate-init.mdc +++ b/.cursor/rules/generalupdate-init.mdc @@ -17,17 +17,17 @@ SetSource(url, key) -> SetOption(AppType.Client) -> LaunchAsync() ## 4 大更新场景 - None, UpgradeOnly, MainOnly, Both -## manifest.json +## generalupdate.manifest.json MainAppName, UpdateAppName, ProductId, InstallPath, UpdatePath, ClientVersion, UpgradeClientVersion ## 关键警告 - UpgradeApp.exe 必须随首个版本发布(不能自己下载自己) - Client 和 Upgrade 必须相同 AppSecretKey - 版本号必须是 4 段式 -- manifest ClientVersion 不能为空 +- generalupdate.manifest.json ClientVersion 不能为空 ## 3 种配置方式 -- Minimal: SetSource + manifest.json +- Minimal: SetSource + generalupdate.manifest.json - Standard: SetConfig(UpdateRequest) + Option + events - appsettings: LoadFromConfiguration(config) diff --git a/.github/instructions/backend-api.instructions.md b/.github/instructions/backend-api.instructions.md index 11d780f..ced4397 100644 --- a/.github/instructions/backend-api.instructions.md +++ b/.github/instructions/backend-api.instructions.md @@ -9,7 +9,7 @@ applyTo: "**/*.cs" ## Verification Endpoint POST /Upgrade/Verification Request: appKey, appType, clientVersion, productId, platform, tenantId -Response: body[{ id, version, url, hash, size, name, appType, isCrossVersion }] +Response: { code, message, body: [{ id, version, url, hash, size, name, appType, isCrossVersion }] } ## Report Endpoint POST /Upgrade/Report diff --git a/RULES.md b/RULES.md index 26c8386..538cf3e 100644 --- a/RULES.md +++ b/RULES.md @@ -6,7 +6,7 @@ - IPC: Encrypted file(AES, default), NamedPipe, SharedMemory, AutoFallback ## Bootstrap -- Minimal: SetSource(url, key) -> SetOption(Client) -> LaunchAsync() +- Minimal: SetSource(url, key) -> SetOption(AppType.Client) -> LaunchAsync() - Standard: SetConfig(UpdateRequest) -> SetOption() -> AddListener*() -> LaunchAsync() - From config: LoadFromConfiguration(config.GetSection("GeneralUpdate")) @@ -46,7 +46,7 @@ Hooks, Strategy, UpdateReporter, DownloadSource, DownloadOrchestrator, DownloadP ## Diagnostics 1. NuGet versions match -2. manifest.json valid +2. generalupdate.manifest.json valid 3. UpgradeApp.exe exists 4. Server API reachable 5. Logs in Logs/generalupdate-trace *.log