feat(update-center): 支持被系统锁定(hold)包的强制安装与更新中心体验优化

- 标记 apt-mark hold 的 spark(aptss) 源包,默认禁用勾选并提示开启强制安装
- 更新中心发起更新时透传 forceHeld,后端 Spark 本地 deb 走 shell-caller
  force-ssinstall 分支(单次免密 pkexec:unhold→ssinstall→hold),避免二次提权
- 无可选项时禁用全选并提示"无可更新项";更新选中后从列表中移除已启动项
- DownloadDetail 紧凑布局避免整页滚动;Toolbar 全选与更新选中按钮同行
- 继承 PKGNAME_PATTERN 与 metalinkUrl 白名单(https+*.spark-app.store+拒..)纵深防御
- apm 源无 hold 概念,forceHeld 在 apm 分支忽略(正确行为)
This commit is contained in:
xiyidaiwa
2026-08-13 16:42:21 +08:00
parent b5c5ab04b5
commit 0f62a7e36a
17 changed files with 893 additions and 105 deletions
+11 -2
View File
@@ -219,9 +219,15 @@ window.ipcRenderer.on("install-log", (_event, log: InstallLog) => {
window.ipcRenderer.on("install-complete", (_event, log: DownloadResult) => {
const downloadObj = downloads.value.find((d) => d.id === log.id);
if (downloadObj) {
// 更新中心来源的任务用「更新」语义,其余(普通安装)用「安装」语义,
// 避免把安装结果误报为「下载完成」。
const isUpdate = (downloadObj.source ?? "").toLowerCase().includes("update");
const doneLabel = isUpdate ? "更新完成" : "安装完成";
const failVerb = isUpdate ? "更新失败" : "安装失败";
if (log.success) {
downloadObj.status = "completed";
downloadObj.logs.push({ time: Date.now(), message: "下载完成" });
downloadObj.logs.push({ time: Date.now(), message: doneLabel });
} else {
downloadObj.status = "failed";
// 将失败原因写入日志,避免 UI 停在"正在获取 Metalink"等中间日志后突兀结束
@@ -232,7 +238,10 @@ window.ipcRenderer.on("install-complete", (_event, log: DownloadResult) => {
} catch {
reason = log.message || reason;
}
downloadObj.logs.push({ time: Date.now(), message: `下载失败: ${reason}` });
downloadObj.logs.push({
time: Date.now(),
message: `${failVerb}: ${reason}`,
});
}
}
});