feat: verify updater package and harden client reliability in 0.1.25
This commit is contained in:
@@ -153,12 +153,14 @@ const syncState = reactive({
|
||||
nextRunAt: "",
|
||||
});
|
||||
const updateState = reactive({
|
||||
currentVersion: "0.1.24",
|
||||
currentVersion: "0.1.25",
|
||||
latestVersion: "",
|
||||
available: false,
|
||||
mandatory: false,
|
||||
checking: false,
|
||||
downloadUrl: "",
|
||||
packageSha256: "",
|
||||
packageSize: 0,
|
||||
releaseNotes: "",
|
||||
lastCheckedAt: "",
|
||||
message: "",
|
||||
@@ -884,6 +886,17 @@ function normalizeReleaseNotesText(raw: string | undefined) {
|
||||
.trim();
|
||||
}
|
||||
|
||||
function normalizeSha256(raw: string | undefined) {
|
||||
const digest = String(raw || "").trim().toLowerCase();
|
||||
return /^[a-f0-9]{64}$/.test(digest) ? digest : "";
|
||||
}
|
||||
|
||||
function normalizePackageSize(raw: unknown) {
|
||||
const value = Number(raw);
|
||||
if (!Number.isFinite(value) || value <= 0) return 0;
|
||||
return Math.floor(value);
|
||||
}
|
||||
|
||||
async function checkClientUpdate(showResultToast = true): Promise<boolean> {
|
||||
if (updateState.checking) {
|
||||
return false;
|
||||
@@ -904,6 +917,8 @@ async function checkClientUpdate(showResultToast = true): Promise<boolean> {
|
||||
updateState.latestVersion = String(response.data.latestVersion || updateState.currentVersion);
|
||||
updateState.available = Boolean(response.data.updateAvailable);
|
||||
updateState.downloadUrl = String(response.data.downloadUrl || "");
|
||||
updateState.packageSha256 = normalizeSha256(String(response.data.sha256 || ""));
|
||||
updateState.packageSize = normalizePackageSize(response.data.packageSize);
|
||||
updateState.releaseNotes = normalizeReleaseNotesText(String(response.data.releaseNotes || ""));
|
||||
updateState.mandatory = Boolean(response.data.mandatory);
|
||||
updateState.message = String(response.data.message || "");
|
||||
@@ -919,6 +934,8 @@ async function checkClientUpdate(showResultToast = true): Promise<boolean> {
|
||||
|
||||
updateState.available = false;
|
||||
updateState.downloadUrl = "";
|
||||
updateState.packageSha256 = "";
|
||||
updateState.packageSize = 0;
|
||||
updateState.message = String(response.data?.message || "检查更新失败");
|
||||
if (showResultToast) {
|
||||
showToast(updateState.message, "error");
|
||||
@@ -984,7 +1001,7 @@ async function installLatestUpdate(): Promise<boolean> {
|
||||
resetUpdateRuntime();
|
||||
updateRuntime.downloading = true;
|
||||
const taskId = `UPD-${Date.now()}`;
|
||||
const installerName = `玩玩云_v${updateState.latestVersion || updateState.currentVersion}.exe`;
|
||||
const installerName = `wanwan-cloud-desktop_v${updateState.latestVersion || updateState.currentVersion}.exe`;
|
||||
updateRuntime.taskId = taskId;
|
||||
updateRuntime.progress = 1;
|
||||
updateRuntime.speed = "准备下载";
|
||||
@@ -998,12 +1015,40 @@ async function installLatestUpdate(): Promise<boolean> {
|
||||
try {
|
||||
if (response.ok && response.data?.success) {
|
||||
updateRuntime.downloading = false;
|
||||
updateRuntime.installing = true;
|
||||
updateRuntime.progress = 100;
|
||||
updateRuntime.speed = "-";
|
||||
updateRuntime.speed = "校验中";
|
||||
const savePath = String(response.data?.savePath || "").trim();
|
||||
updateRuntime.installerPath = savePath;
|
||||
if (savePath) {
|
||||
const expectedSha = normalizeSha256(updateState.packageSha256);
|
||||
const expectedSize = normalizePackageSize(updateState.packageSize);
|
||||
if (expectedSha || expectedSize > 0) {
|
||||
const verifyResponse = await invokeBridge("api_compute_file_sha256", {
|
||||
filePath: savePath,
|
||||
});
|
||||
if (!(verifyResponse.ok && verifyResponse.data?.success)) {
|
||||
const message = String(verifyResponse.data?.message || "校验更新包失败");
|
||||
resetUpdateRuntime();
|
||||
showToast(message, "error");
|
||||
return false;
|
||||
}
|
||||
const actualSha = normalizeSha256(String(verifyResponse.data?.sha256 || ""));
|
||||
const actualSize = normalizePackageSize(verifyResponse.data?.fileSize);
|
||||
if (expectedSize > 0 && actualSize > 0 && expectedSize !== actualSize) {
|
||||
resetUpdateRuntime();
|
||||
showToast(`更新包大小校验失败(期望 ${formatBytes(expectedSize)},实际 ${formatBytes(actualSize)})`, "error");
|
||||
return false;
|
||||
}
|
||||
if (expectedSha && actualSha !== expectedSha) {
|
||||
resetUpdateRuntime();
|
||||
showToast("更新包完整性校验失败,请重试下载", "error");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
updateRuntime.installing = true;
|
||||
updateRuntime.progress = 100;
|
||||
updateRuntime.speed = "-";
|
||||
const launchResponse = await invokeBridge("api_silent_install_and_restart", {
|
||||
installerPath: savePath,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user