electron-updater-for-render 1.1.2-beta.7 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main/index.cjs +196 -166
- package/dist/main/index.d.ts +11 -5
- package/dist/main/index.js +196 -166
- package/dist/main/utils.d.ts +6 -0
- package/package.json +1 -1
package/dist/main/index.cjs
CHANGED
|
@@ -35,7 +35,6 @@ __export(main_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(main_exports);
|
|
36
36
|
var import_path2 = __toESM(require("path"), 1);
|
|
37
37
|
var import_original_fs = __toESM(require("original-fs"), 1);
|
|
38
|
-
var import_crypto = __toESM(require("crypto"), 1);
|
|
39
38
|
var import_semver = __toESM(require("semver"), 1);
|
|
40
39
|
var import_electron2 = require("electron");
|
|
41
40
|
var import_promises = require("stream/promises");
|
|
@@ -158,7 +157,45 @@ var RouterHandler = class _RouterHandler {
|
|
|
158
157
|
}
|
|
159
158
|
};
|
|
160
159
|
|
|
160
|
+
// src/main/utils.ts
|
|
161
|
+
var import_fs2 = require("fs");
|
|
162
|
+
var import_crypto = __toESM(require("crypto"), 1);
|
|
163
|
+
function verifyFileStream(filePath, info, publicKey) {
|
|
164
|
+
return new Promise((resolve, reject) => {
|
|
165
|
+
const hash = import_crypto.default.createHash("sha256");
|
|
166
|
+
const verify = publicKey && info.signature ? import_crypto.default.createVerify("RSA-SHA256") : null;
|
|
167
|
+
const stream = (0, import_fs2.createReadStream)(filePath);
|
|
168
|
+
stream.on("data", (chunk) => {
|
|
169
|
+
hash.update(chunk);
|
|
170
|
+
if (verify) {
|
|
171
|
+
verify.update(chunk);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
stream.on("end", () => {
|
|
175
|
+
const sha256 = hash.digest("hex");
|
|
176
|
+
if (info.sha256 && sha256 !== info.sha256) {
|
|
177
|
+
console.error(`[RenderUpdater] SHA256 mismatch! Expected ${info.sha256}, got ${sha256}`);
|
|
178
|
+
return resolve(false);
|
|
179
|
+
}
|
|
180
|
+
if (verify && publicKey && info.signature) {
|
|
181
|
+
verify.end();
|
|
182
|
+
const isValid = verify.verify(publicKey, info.signature, "base64");
|
|
183
|
+
if (!isValid) {
|
|
184
|
+
console.error("[RenderUpdater] RSA Signature verification failed.");
|
|
185
|
+
return resolve(false);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
resolve(true);
|
|
189
|
+
});
|
|
190
|
+
stream.on("error", (err) => {
|
|
191
|
+
console.error("[RenderUpdater] Stream error during file verification:", err);
|
|
192
|
+
reject(err);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
161
197
|
// src/main/index.ts
|
|
198
|
+
var fsPromises = import_original_fs.default.promises;
|
|
162
199
|
var RenderUpdater = class {
|
|
163
200
|
versionsDir;
|
|
164
201
|
currentVersionFile;
|
|
@@ -206,7 +243,7 @@ var RenderUpdater = class {
|
|
|
206
243
|
if (!import_original_fs.default.existsSync(this.versionsDir)) {
|
|
207
244
|
import_original_fs.default.mkdirSync(this.versionsDir, { recursive: true });
|
|
208
245
|
}
|
|
209
|
-
const savedVersion = this.
|
|
246
|
+
const savedVersion = this.readCurrentVersionFromFileSync();
|
|
210
247
|
this._activeVersion = savedVersion || "0.0.0";
|
|
211
248
|
this._diskVersion = savedVersion || "0.0.0";
|
|
212
249
|
console.log("[RenderUpdater] Initialized. Active version:", this._activeVersion);
|
|
@@ -221,7 +258,8 @@ var RenderUpdater = class {
|
|
|
221
258
|
});
|
|
222
259
|
}
|
|
223
260
|
}
|
|
224
|
-
|
|
261
|
+
// 初始化使用一次的同步方法(可接受)
|
|
262
|
+
readCurrentVersionFromFileSync() {
|
|
225
263
|
if (import_original_fs.default.existsSync(this.currentVersionFile)) {
|
|
226
264
|
try {
|
|
227
265
|
const data = JSON.parse(import_original_fs.default.readFileSync(this.currentVersionFile, "utf-8"));
|
|
@@ -234,35 +272,33 @@ var RenderUpdater = class {
|
|
|
234
272
|
}
|
|
235
273
|
return "0.0.0";
|
|
236
274
|
}
|
|
237
|
-
|
|
275
|
+
// 全面异步替换原同步清理操作
|
|
276
|
+
async cleanOldVersions() {
|
|
238
277
|
try {
|
|
239
278
|
if (!this.versionsDir) return;
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
});
|
|
279
|
+
const files = await fsPromises.readdir(this.versionsDir, { withFileTypes: true });
|
|
280
|
+
const versionDirs = files.filter((dirent) => dirent.isDirectory() && import_semver.default.valid(dirent.name)).map((dirent) => dirent.name);
|
|
243
281
|
versionDirs.sort((a, b) => import_semver.default.compare(import_semver.default.coerce(b) ?? b, import_semver.default.coerce(a) ?? a));
|
|
244
|
-
const activeIdx = versionDirs.indexOf(this._activeVersion);
|
|
245
|
-
const diskIdx = versionDirs.indexOf(this._diskVersion);
|
|
246
282
|
const safeVersions = /* @__PURE__ */ new Set();
|
|
247
|
-
|
|
248
|
-
|
|
283
|
+
safeVersions.add(this._activeVersion);
|
|
284
|
+
safeVersions.add(this._diskVersion);
|
|
249
285
|
let kept = 0;
|
|
250
286
|
for (const v of versionDirs) {
|
|
251
287
|
if (!safeVersions.has(v)) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
288
|
+
if (kept >= this.maxVersionsToKeep) {
|
|
289
|
+
console.info(`[RenderUpdater] Cleaning up old version: ${v}`);
|
|
290
|
+
try {
|
|
291
|
+
await fsPromises.rm(import_path2.default.join(this.versionsDir, v), { recursive: true, force: true });
|
|
292
|
+
} catch (e) {
|
|
293
|
+
if (e.code === "EBUSY") {
|
|
294
|
+
console.warn(`[RenderUpdater] Version ${v} is locked, skipping.`);
|
|
295
|
+
} else {
|
|
296
|
+
console.error(`[RenderUpdater] Failed to remove version ${v}:`, e);
|
|
297
|
+
}
|
|
260
298
|
}
|
|
299
|
+
} else {
|
|
300
|
+
kept++;
|
|
261
301
|
}
|
|
262
|
-
} else {
|
|
263
|
-
kept++;
|
|
264
|
-
}
|
|
265
|
-
if (kept >= this.maxVersionsToKeep && !safeVersions.has(v)) {
|
|
266
302
|
}
|
|
267
303
|
}
|
|
268
304
|
} catch (e) {
|
|
@@ -333,25 +369,21 @@ var RenderUpdater = class {
|
|
|
333
369
|
try {
|
|
334
370
|
console.log("[RenderUpdater] download() entry. current this.versionsDir:", this.versionsDir);
|
|
335
371
|
const infoToUse = providedInfo || await (await fetch(`${this.baseUrl}/latest.json?t=${Date.now()}`)).json();
|
|
336
|
-
if (!this.versionsDir)
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
if (!infoToUse || !infoToUse.version) {
|
|
340
|
-
throw new Error(`[RenderUpdater] CRITICAL: infoToUse or version missing. info: ${JSON.stringify(infoToUse)}`);
|
|
341
|
-
}
|
|
372
|
+
if (!this.versionsDir) throw new Error("[RenderUpdater] CRITICAL: this.versionsDir lost in download.");
|
|
373
|
+
if (!infoToUse || !infoToUse.version) throw new Error(`[RenderUpdater] CRITICAL: infoToUse or version missing. info: ${JSON.stringify(infoToUse)}`);
|
|
342
374
|
const versionDir = import_path2.default.join(this.versionsDir, infoToUse.version);
|
|
343
|
-
const
|
|
344
|
-
|
|
375
|
+
const finalAsarPath = import_path2.default.join(versionDir, "renderer.asar");
|
|
376
|
+
const tmpAsarPath = import_path2.default.join(versionDir, "renderer.asar.tmp");
|
|
377
|
+
if (import_original_fs.default.existsSync(finalAsarPath) && await verifyFileStream(finalAsarPath, infoToUse, this.publicKey)) {
|
|
345
378
|
console.log("[RenderUpdater] File already exists and verified. Skipping download.");
|
|
346
379
|
onProgress?.(100);
|
|
347
380
|
return;
|
|
348
381
|
}
|
|
349
|
-
|
|
350
|
-
import_original_fs.default.mkdirSync(versionDir, { recursive: true });
|
|
351
|
-
}
|
|
382
|
+
await fsPromises.mkdir(versionDir, { recursive: true });
|
|
352
383
|
let downloadedBytes = 0;
|
|
353
|
-
if (import_original_fs.default.existsSync(
|
|
354
|
-
|
|
384
|
+
if (import_original_fs.default.existsSync(tmpAsarPath)) {
|
|
385
|
+
const stats = await fsPromises.stat(tmpAsarPath);
|
|
386
|
+
downloadedBytes = stats.size;
|
|
355
387
|
}
|
|
356
388
|
const fetchOptions = {};
|
|
357
389
|
if (downloadedBytes > 0) {
|
|
@@ -372,7 +404,7 @@ var RenderUpdater = class {
|
|
|
372
404
|
const incomingTotal = contentLength ? parseInt(contentLength, 10) : 0;
|
|
373
405
|
const total = downloadedBytes + incomingTotal;
|
|
374
406
|
if (!downloadResponse.body) throw new Error("Response body is empty");
|
|
375
|
-
const fileStream = import_original_fs.default.createWriteStream(
|
|
407
|
+
const fileStream = import_original_fs.default.createWriteStream(tmpAsarPath, { flags: downloadResponse.status === 206 ? "a" : "w" });
|
|
376
408
|
const progressTransform = new import_stream.Transform({
|
|
377
409
|
transform(chunk, _encoding, callback) {
|
|
378
410
|
downloadedBytes += chunk.length;
|
|
@@ -387,62 +419,34 @@ var RenderUpdater = class {
|
|
|
387
419
|
progressTransform,
|
|
388
420
|
fileStream
|
|
389
421
|
);
|
|
390
|
-
if (!
|
|
391
|
-
|
|
422
|
+
if (!await verifyFileStream(tmpAsarPath, infoToUse, this.publicKey)) {
|
|
423
|
+
await fsPromises.rm(tmpAsarPath, { force: true });
|
|
392
424
|
throw new Error("[RenderUpdater] Verification failed after download (SHA256 or RSA Mismatch)");
|
|
393
425
|
}
|
|
394
|
-
|
|
395
|
-
this.
|
|
426
|
+
await fsPromises.rename(tmpAsarPath, finalAsarPath);
|
|
427
|
+
await this.setUpdatePending(infoToUse.version);
|
|
428
|
+
await this.cleanOldVersions();
|
|
396
429
|
} finally {
|
|
397
430
|
this.isDownloading = false;
|
|
398
431
|
}
|
|
399
432
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
hashSum.update(fileBuffer);
|
|
404
|
-
const sha256 = hashSum.digest("hex");
|
|
405
|
-
if (sha256 !== info.sha256 && info.sha256 !== "") {
|
|
406
|
-
console.error(`[RenderUpdater] SHA256 mismatch! Expected ${info.sha256}, got ${sha256}`);
|
|
407
|
-
return false;
|
|
408
|
-
}
|
|
409
|
-
if (this.publicKey && info.signature) {
|
|
410
|
-
try {
|
|
411
|
-
const verify = import_crypto.default.createVerify("RSA-SHA256");
|
|
412
|
-
verify.update(fileBuffer);
|
|
413
|
-
verify.end();
|
|
414
|
-
const isValid = verify.verify(this.publicKey, info.signature, "base64");
|
|
415
|
-
if (!isValid) {
|
|
416
|
-
console.error("[RenderUpdater] RSA Signature verification failed.");
|
|
417
|
-
return false;
|
|
418
|
-
}
|
|
419
|
-
} catch (err) {
|
|
420
|
-
console.error("[RenderUpdater] RSA verification error:", err);
|
|
421
|
-
return false;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return true;
|
|
425
|
-
}
|
|
426
|
-
useVersion(version) {
|
|
427
|
-
this.updateDiskVersion(version);
|
|
428
|
-
this.updateMemoryVersion(version);
|
|
433
|
+
async useVersion(version) {
|
|
434
|
+
await this.updateDiskVersion(version);
|
|
435
|
+
this._activeVersion = version;
|
|
429
436
|
}
|
|
430
|
-
setUpdatePending(version) {
|
|
431
|
-
this.updateDiskVersion(version);
|
|
437
|
+
async setUpdatePending(version) {
|
|
438
|
+
await this.updateDiskVersion(version);
|
|
432
439
|
if (this.onStatusChanged) {
|
|
433
440
|
this.onStatusChanged({ status: "ready", version });
|
|
434
441
|
}
|
|
435
442
|
}
|
|
436
|
-
updateDiskVersion(version) {
|
|
437
|
-
|
|
443
|
+
async updateDiskVersion(version) {
|
|
444
|
+
await fsPromises.writeFile(
|
|
438
445
|
this.currentVersionFile,
|
|
439
446
|
JSON.stringify({ version, date: (/* @__PURE__ */ new Date()).toISOString() })
|
|
440
447
|
);
|
|
441
448
|
this._diskVersion = version;
|
|
442
449
|
}
|
|
443
|
-
updateMemoryVersion(version) {
|
|
444
|
-
this._activeVersion = version;
|
|
445
|
-
}
|
|
446
450
|
async installAndRestart() {
|
|
447
451
|
if (this.onBeforeRestart) {
|
|
448
452
|
await this.onBeforeRestart();
|
|
@@ -450,104 +454,130 @@ var RenderUpdater = class {
|
|
|
450
454
|
import_electron2.app.relaunch();
|
|
451
455
|
import_electron2.app.quit();
|
|
452
456
|
}
|
|
457
|
+
// --- 改版后切分的上帝函数 ---
|
|
453
458
|
async checkForUpdatesAndNotify() {
|
|
454
459
|
try {
|
|
455
460
|
const checkRes = await this.check();
|
|
456
|
-
if (checkRes.updateAvailable
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
};
|
|
473
|
-
if (this.onDownloadComplete) {
|
|
474
|
-
this.onDownloadComplete(info, () => {
|
|
475
|
-
performInstall(true).catch(console.error);
|
|
476
|
-
});
|
|
477
|
-
} else {
|
|
478
|
-
if (info.forceUpdate || isReady) {
|
|
479
|
-
if (isReady) {
|
|
480
|
-
const { response } = await import_electron2.dialog.showMessageBox({
|
|
481
|
-
type: "info",
|
|
482
|
-
title: "\u66F4\u65B0\u5C31\u7EEA",
|
|
483
|
-
message: `\u65B0\u7248\u672C v${info.version} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u5E94\u7528\uFF1F`,
|
|
484
|
-
buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
|
|
485
|
-
defaultId: 0,
|
|
486
|
-
cancelId: 1
|
|
487
|
-
});
|
|
488
|
-
await performInstall(response === 0);
|
|
489
|
-
} else {
|
|
490
|
-
if (info.forceUpdate === "prompt") {
|
|
491
|
-
import_electron2.dialog.showMessageBoxSync({
|
|
492
|
-
type: "warning",
|
|
493
|
-
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
494
|
-
message: `\u6700\u65B0\u7248\u672C v${info.version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u5373\u5C06\u91CD\u542F\u7A0B\u5E8F\u4EE5\u5E94\u7528\u66F4\u65B0\u3002`,
|
|
495
|
-
buttons: ["\u6211\u77E5\u9053\u4E86"]
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
await performInstall(true);
|
|
499
|
-
}
|
|
500
|
-
} else if (this.autoPrompt) {
|
|
501
|
-
const { response } = await import_electron2.dialog.showMessageBox({
|
|
502
|
-
type: "info",
|
|
503
|
-
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
504
|
-
message: `\u6700\u65B0\u7248\u672C v${info.version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u4EE5\u5E94\u7528\u66F4\u65B0\uFF1F`,
|
|
505
|
-
buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
|
|
506
|
-
defaultId: 0,
|
|
507
|
-
cancelId: 1
|
|
508
|
-
});
|
|
509
|
-
await performInstall(response === 0);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
} catch (e) {
|
|
513
|
-
if (this.onError) {
|
|
514
|
-
this.onError(e);
|
|
515
|
-
} else {
|
|
516
|
-
console.error("[RenderUpdater] Download/Install error:", e);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
};
|
|
520
|
-
if (this.onUpdateAvailable) {
|
|
521
|
-
this.onUpdateAvailable(info, doDownload);
|
|
461
|
+
if (!checkRes.updateAvailable || !checkRes.info) return;
|
|
462
|
+
const info = checkRes.info;
|
|
463
|
+
if (checkRes.status === "ready") {
|
|
464
|
+
await this.handleReadyUpdate(info);
|
|
465
|
+
} else {
|
|
466
|
+
await this.handleNewUpdate(info);
|
|
467
|
+
}
|
|
468
|
+
} catch (e) {
|
|
469
|
+
this.handleError(e);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async handleReadyUpdate(info) {
|
|
473
|
+
if (this.onUpdateAvailable) {
|
|
474
|
+
this.onUpdateAvailable(info, async () => {
|
|
475
|
+
if (this.onDownloadComplete) {
|
|
476
|
+
this.onDownloadComplete(info, () => this.performInstall(info, true).catch(this.handleError.bind(this)));
|
|
522
477
|
} else {
|
|
523
|
-
|
|
524
|
-
await doDownload();
|
|
525
|
-
} else if (this.autoDownload) {
|
|
526
|
-
await doDownload();
|
|
527
|
-
} else if (this.autoPrompt) {
|
|
528
|
-
const { response } = await import_electron2.dialog.showMessageBox({
|
|
529
|
-
type: "info",
|
|
530
|
-
title: "\u53D1\u73B0\u65B0\u7248\u672C",
|
|
531
|
-
message: `\u53D1\u73B0\u53EF\u7528\u66F4\u65B0 (v${info.version})\u3002\u662F\u5426\u7ACB\u5373\u4E0B\u8F7D\u66F4\u65B0\uFF1F
|
|
532
|
-
|
|
533
|
-
\u66F4\u65B0\u65E5\u5FD7\uFF1A
|
|
534
|
-
${info.releaseNotes || "\u65E0\u8BE6\u7EC6\u8BB0\u5F55\u3002"}`,
|
|
535
|
-
buttons: ["\u7ACB\u5373\u66F4\u65B0", "\u7A0D\u540E\u518D\u8BF4"],
|
|
536
|
-
cancelId: 1
|
|
537
|
-
});
|
|
538
|
-
if (response === 0) {
|
|
539
|
-
await doDownload();
|
|
540
|
-
}
|
|
541
|
-
}
|
|
478
|
+
await this.performInstall(info, true);
|
|
542
479
|
}
|
|
480
|
+
});
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (info.forceUpdate || !this.autoPrompt) {
|
|
484
|
+
await this.performInstall(info, true);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const response = await this.showReadyDialog(info.version);
|
|
488
|
+
await this.performInstall(info, response === 0);
|
|
489
|
+
}
|
|
490
|
+
async handleNewUpdate(info) {
|
|
491
|
+
if (this.onUpdateAvailable) {
|
|
492
|
+
this.onUpdateAvailable(info, () => this.executeAutoDownload(info));
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
if (info.forceUpdate || this.autoDownload) {
|
|
496
|
+
await this.executeAutoDownload(info);
|
|
497
|
+
} else if (this.autoPrompt) {
|
|
498
|
+
const response = await this.showNewUpdateDialog(info);
|
|
499
|
+
if (response === 0) {
|
|
500
|
+
await this.executeAutoDownload(info);
|
|
543
501
|
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
async executeAutoDownload(info) {
|
|
505
|
+
try {
|
|
506
|
+
await this.download(info, this.onDownloadProgress);
|
|
507
|
+
const doInstall = (shouldRestart) => this.performInstall(info, shouldRestart).catch(this.handleError.bind(this));
|
|
508
|
+
if (this.onDownloadComplete) {
|
|
509
|
+
this.onDownloadComplete(info, () => doInstall(true));
|
|
547
510
|
} else {
|
|
548
|
-
|
|
511
|
+
const isSilent = info.forceUpdate === "silent";
|
|
512
|
+
const shouldRestart = isSilent || await this.showDownloadCompleteDialog(info.version, info.forceUpdate === "prompt") === 0;
|
|
513
|
+
await doInstall(shouldRestart);
|
|
549
514
|
}
|
|
515
|
+
} catch (e) {
|
|
516
|
+
this.handleError(e);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
async performInstall(info, shouldRestart) {
|
|
520
|
+
if (shouldRestart) {
|
|
521
|
+
await this.useVersion(info.version);
|
|
522
|
+
await this.installAndRestart();
|
|
523
|
+
} else {
|
|
524
|
+
await this.setUpdatePending(info.version);
|
|
525
|
+
console.info(`[RenderUpdater] Update v${info.version} ready, will apply on next launch.`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
handleError(error) {
|
|
529
|
+
if (this.onError) {
|
|
530
|
+
this.onError(error);
|
|
531
|
+
} else {
|
|
532
|
+
console.error("[RenderUpdater] Error:", error);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
// UI 分发抽离
|
|
536
|
+
async showReadyDialog(version) {
|
|
537
|
+
const { response } = await import_electron2.dialog.showMessageBox({
|
|
538
|
+
type: "info",
|
|
539
|
+
title: "\u66F4\u65B0\u5C31\u7EEA",
|
|
540
|
+
message: `\u65B0\u7248\u672C v${version} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u5E94\u7528\uFF1F`,
|
|
541
|
+
buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
|
|
542
|
+
defaultId: 0,
|
|
543
|
+
cancelId: 1
|
|
544
|
+
});
|
|
545
|
+
return response;
|
|
546
|
+
}
|
|
547
|
+
async showNewUpdateDialog(info) {
|
|
548
|
+
const { response } = await import_electron2.dialog.showMessageBox({
|
|
549
|
+
type: "info",
|
|
550
|
+
title: "\u53D1\u73B0\u65B0\u7248\u672C",
|
|
551
|
+
message: `\u53D1\u73B0\u53EF\u7528\u66F4\u65B0 (v${info.version})\u3002\u662F\u5426\u7ACB\u5373\u4E0B\u8F7D\u66F4\u65B0\uFF1F
|
|
552
|
+
|
|
553
|
+
\u66F4\u65B0\u65E5\u5FD7\uFF1A
|
|
554
|
+
${info.releaseNotes || "\u65E0\u8BE6\u7EC6\u8BB0\u5F55\u3002"}`,
|
|
555
|
+
buttons: ["\u7ACB\u5373\u66F4\u65B0", "\u7A0D\u540E\u518D\u8BF4"],
|
|
556
|
+
cancelId: 1
|
|
557
|
+
});
|
|
558
|
+
return response;
|
|
559
|
+
}
|
|
560
|
+
async showDownloadCompleteDialog(version, forcePrompt) {
|
|
561
|
+
if (forcePrompt) {
|
|
562
|
+
import_electron2.dialog.showMessageBoxSync({
|
|
563
|
+
type: "warning",
|
|
564
|
+
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
565
|
+
message: `\u6700\u65B0\u7248\u672C v${version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u5373\u5C06\u91CD\u542F\u7A0B\u5E8F\u4EE5\u5E94\u7528\u66F4\u65B0\u3002`,
|
|
566
|
+
buttons: ["\u6211\u77E5\u9053\u4E86"]
|
|
567
|
+
});
|
|
568
|
+
return 0;
|
|
569
|
+
} else if (this.autoPrompt) {
|
|
570
|
+
const { response } = await import_electron2.dialog.showMessageBox({
|
|
571
|
+
type: "info",
|
|
572
|
+
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
573
|
+
message: `\u6700\u65B0\u7248\u672C v${version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u4EE5\u5E94\u7528\u66F4\u65B0\uFF1F`,
|
|
574
|
+
buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
|
|
575
|
+
defaultId: 0,
|
|
576
|
+
cancelId: 1
|
|
577
|
+
});
|
|
578
|
+
return response;
|
|
550
579
|
}
|
|
580
|
+
return 0;
|
|
551
581
|
}
|
|
552
582
|
};
|
|
553
583
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/main/index.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare class RenderUpdater {
|
|
|
22
22
|
onError?: (error: Error) => void;
|
|
23
23
|
onBeforeRestart?: () => void | Promise<void>;
|
|
24
24
|
constructor(options: UpdaterOptions);
|
|
25
|
-
private
|
|
25
|
+
private readCurrentVersionFromFileSync;
|
|
26
26
|
private cleanOldVersions;
|
|
27
27
|
get activeVersion(): string;
|
|
28
28
|
get pendingVersion(): string;
|
|
@@ -35,11 +35,17 @@ export declare class RenderUpdater {
|
|
|
35
35
|
status: 'idle' | 'available' | 'ready';
|
|
36
36
|
}>;
|
|
37
37
|
download(providedInfo?: UpdateInfo, onProgress?: (percent: number) => void): Promise<void>;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
setUpdatePending(version: string): void;
|
|
38
|
+
useVersion(version: string): Promise<void>;
|
|
39
|
+
setUpdatePending(version: string): Promise<void>;
|
|
41
40
|
private updateDiskVersion;
|
|
42
|
-
private updateMemoryVersion;
|
|
43
41
|
installAndRestart(): Promise<void>;
|
|
44
42
|
checkForUpdatesAndNotify(): Promise<void>;
|
|
43
|
+
private handleReadyUpdate;
|
|
44
|
+
private handleNewUpdate;
|
|
45
|
+
private executeAutoDownload;
|
|
46
|
+
private performInstall;
|
|
47
|
+
private handleError;
|
|
48
|
+
private showReadyDialog;
|
|
49
|
+
private showNewUpdateDialog;
|
|
50
|
+
private showDownloadCompleteDialog;
|
|
45
51
|
}
|
package/dist/main/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// src/main/index.ts
|
|
2
2
|
import path2 from "path";
|
|
3
3
|
import fs2 from "original-fs";
|
|
4
|
-
import crypto from "crypto";
|
|
5
4
|
import semver from "semver";
|
|
6
5
|
import { app as app2, dialog } from "electron";
|
|
7
6
|
import { pipeline } from "stream/promises";
|
|
@@ -124,7 +123,45 @@ var RouterHandler = class _RouterHandler {
|
|
|
124
123
|
}
|
|
125
124
|
};
|
|
126
125
|
|
|
126
|
+
// src/main/utils.ts
|
|
127
|
+
import { createReadStream } from "fs";
|
|
128
|
+
import crypto from "crypto";
|
|
129
|
+
function verifyFileStream(filePath, info, publicKey) {
|
|
130
|
+
return new Promise((resolve, reject) => {
|
|
131
|
+
const hash = crypto.createHash("sha256");
|
|
132
|
+
const verify = publicKey && info.signature ? crypto.createVerify("RSA-SHA256") : null;
|
|
133
|
+
const stream = createReadStream(filePath);
|
|
134
|
+
stream.on("data", (chunk) => {
|
|
135
|
+
hash.update(chunk);
|
|
136
|
+
if (verify) {
|
|
137
|
+
verify.update(chunk);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
stream.on("end", () => {
|
|
141
|
+
const sha256 = hash.digest("hex");
|
|
142
|
+
if (info.sha256 && sha256 !== info.sha256) {
|
|
143
|
+
console.error(`[RenderUpdater] SHA256 mismatch! Expected ${info.sha256}, got ${sha256}`);
|
|
144
|
+
return resolve(false);
|
|
145
|
+
}
|
|
146
|
+
if (verify && publicKey && info.signature) {
|
|
147
|
+
verify.end();
|
|
148
|
+
const isValid = verify.verify(publicKey, info.signature, "base64");
|
|
149
|
+
if (!isValid) {
|
|
150
|
+
console.error("[RenderUpdater] RSA Signature verification failed.");
|
|
151
|
+
return resolve(false);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
resolve(true);
|
|
155
|
+
});
|
|
156
|
+
stream.on("error", (err) => {
|
|
157
|
+
console.error("[RenderUpdater] Stream error during file verification:", err);
|
|
158
|
+
reject(err);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
127
163
|
// src/main/index.ts
|
|
164
|
+
var fsPromises = fs2.promises;
|
|
128
165
|
var RenderUpdater = class {
|
|
129
166
|
versionsDir;
|
|
130
167
|
currentVersionFile;
|
|
@@ -172,7 +209,7 @@ var RenderUpdater = class {
|
|
|
172
209
|
if (!fs2.existsSync(this.versionsDir)) {
|
|
173
210
|
fs2.mkdirSync(this.versionsDir, { recursive: true });
|
|
174
211
|
}
|
|
175
|
-
const savedVersion = this.
|
|
212
|
+
const savedVersion = this.readCurrentVersionFromFileSync();
|
|
176
213
|
this._activeVersion = savedVersion || "0.0.0";
|
|
177
214
|
this._diskVersion = savedVersion || "0.0.0";
|
|
178
215
|
console.log("[RenderUpdater] Initialized. Active version:", this._activeVersion);
|
|
@@ -187,7 +224,8 @@ var RenderUpdater = class {
|
|
|
187
224
|
});
|
|
188
225
|
}
|
|
189
226
|
}
|
|
190
|
-
|
|
227
|
+
// 初始化使用一次的同步方法(可接受)
|
|
228
|
+
readCurrentVersionFromFileSync() {
|
|
191
229
|
if (fs2.existsSync(this.currentVersionFile)) {
|
|
192
230
|
try {
|
|
193
231
|
const data = JSON.parse(fs2.readFileSync(this.currentVersionFile, "utf-8"));
|
|
@@ -200,35 +238,33 @@ var RenderUpdater = class {
|
|
|
200
238
|
}
|
|
201
239
|
return "0.0.0";
|
|
202
240
|
}
|
|
203
|
-
|
|
241
|
+
// 全面异步替换原同步清理操作
|
|
242
|
+
async cleanOldVersions() {
|
|
204
243
|
try {
|
|
205
244
|
if (!this.versionsDir) return;
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
});
|
|
245
|
+
const files = await fsPromises.readdir(this.versionsDir, { withFileTypes: true });
|
|
246
|
+
const versionDirs = files.filter((dirent) => dirent.isDirectory() && semver.valid(dirent.name)).map((dirent) => dirent.name);
|
|
209
247
|
versionDirs.sort((a, b) => semver.compare(semver.coerce(b) ?? b, semver.coerce(a) ?? a));
|
|
210
|
-
const activeIdx = versionDirs.indexOf(this._activeVersion);
|
|
211
|
-
const diskIdx = versionDirs.indexOf(this._diskVersion);
|
|
212
248
|
const safeVersions = /* @__PURE__ */ new Set();
|
|
213
|
-
|
|
214
|
-
|
|
249
|
+
safeVersions.add(this._activeVersion);
|
|
250
|
+
safeVersions.add(this._diskVersion);
|
|
215
251
|
let kept = 0;
|
|
216
252
|
for (const v of versionDirs) {
|
|
217
253
|
if (!safeVersions.has(v)) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
254
|
+
if (kept >= this.maxVersionsToKeep) {
|
|
255
|
+
console.info(`[RenderUpdater] Cleaning up old version: ${v}`);
|
|
256
|
+
try {
|
|
257
|
+
await fsPromises.rm(path2.join(this.versionsDir, v), { recursive: true, force: true });
|
|
258
|
+
} catch (e) {
|
|
259
|
+
if (e.code === "EBUSY") {
|
|
260
|
+
console.warn(`[RenderUpdater] Version ${v} is locked, skipping.`);
|
|
261
|
+
} else {
|
|
262
|
+
console.error(`[RenderUpdater] Failed to remove version ${v}:`, e);
|
|
263
|
+
}
|
|
226
264
|
}
|
|
265
|
+
} else {
|
|
266
|
+
kept++;
|
|
227
267
|
}
|
|
228
|
-
} else {
|
|
229
|
-
kept++;
|
|
230
|
-
}
|
|
231
|
-
if (kept >= this.maxVersionsToKeep && !safeVersions.has(v)) {
|
|
232
268
|
}
|
|
233
269
|
}
|
|
234
270
|
} catch (e) {
|
|
@@ -299,25 +335,21 @@ var RenderUpdater = class {
|
|
|
299
335
|
try {
|
|
300
336
|
console.log("[RenderUpdater] download() entry. current this.versionsDir:", this.versionsDir);
|
|
301
337
|
const infoToUse = providedInfo || await (await fetch(`${this.baseUrl}/latest.json?t=${Date.now()}`)).json();
|
|
302
|
-
if (!this.versionsDir)
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
if (!infoToUse || !infoToUse.version) {
|
|
306
|
-
throw new Error(`[RenderUpdater] CRITICAL: infoToUse or version missing. info: ${JSON.stringify(infoToUse)}`);
|
|
307
|
-
}
|
|
338
|
+
if (!this.versionsDir) throw new Error("[RenderUpdater] CRITICAL: this.versionsDir lost in download.");
|
|
339
|
+
if (!infoToUse || !infoToUse.version) throw new Error(`[RenderUpdater] CRITICAL: infoToUse or version missing. info: ${JSON.stringify(infoToUse)}`);
|
|
308
340
|
const versionDir = path2.join(this.versionsDir, infoToUse.version);
|
|
309
|
-
const
|
|
310
|
-
|
|
341
|
+
const finalAsarPath = path2.join(versionDir, "renderer.asar");
|
|
342
|
+
const tmpAsarPath = path2.join(versionDir, "renderer.asar.tmp");
|
|
343
|
+
if (fs2.existsSync(finalAsarPath) && await verifyFileStream(finalAsarPath, infoToUse, this.publicKey)) {
|
|
311
344
|
console.log("[RenderUpdater] File already exists and verified. Skipping download.");
|
|
312
345
|
onProgress?.(100);
|
|
313
346
|
return;
|
|
314
347
|
}
|
|
315
|
-
|
|
316
|
-
fs2.mkdirSync(versionDir, { recursive: true });
|
|
317
|
-
}
|
|
348
|
+
await fsPromises.mkdir(versionDir, { recursive: true });
|
|
318
349
|
let downloadedBytes = 0;
|
|
319
|
-
if (fs2.existsSync(
|
|
320
|
-
|
|
350
|
+
if (fs2.existsSync(tmpAsarPath)) {
|
|
351
|
+
const stats = await fsPromises.stat(tmpAsarPath);
|
|
352
|
+
downloadedBytes = stats.size;
|
|
321
353
|
}
|
|
322
354
|
const fetchOptions = {};
|
|
323
355
|
if (downloadedBytes > 0) {
|
|
@@ -338,7 +370,7 @@ var RenderUpdater = class {
|
|
|
338
370
|
const incomingTotal = contentLength ? parseInt(contentLength, 10) : 0;
|
|
339
371
|
const total = downloadedBytes + incomingTotal;
|
|
340
372
|
if (!downloadResponse.body) throw new Error("Response body is empty");
|
|
341
|
-
const fileStream = fs2.createWriteStream(
|
|
373
|
+
const fileStream = fs2.createWriteStream(tmpAsarPath, { flags: downloadResponse.status === 206 ? "a" : "w" });
|
|
342
374
|
const progressTransform = new Transform({
|
|
343
375
|
transform(chunk, _encoding, callback) {
|
|
344
376
|
downloadedBytes += chunk.length;
|
|
@@ -353,62 +385,34 @@ var RenderUpdater = class {
|
|
|
353
385
|
progressTransform,
|
|
354
386
|
fileStream
|
|
355
387
|
);
|
|
356
|
-
if (!
|
|
357
|
-
|
|
388
|
+
if (!await verifyFileStream(tmpAsarPath, infoToUse, this.publicKey)) {
|
|
389
|
+
await fsPromises.rm(tmpAsarPath, { force: true });
|
|
358
390
|
throw new Error("[RenderUpdater] Verification failed after download (SHA256 or RSA Mismatch)");
|
|
359
391
|
}
|
|
360
|
-
|
|
361
|
-
this.
|
|
392
|
+
await fsPromises.rename(tmpAsarPath, finalAsarPath);
|
|
393
|
+
await this.setUpdatePending(infoToUse.version);
|
|
394
|
+
await this.cleanOldVersions();
|
|
362
395
|
} finally {
|
|
363
396
|
this.isDownloading = false;
|
|
364
397
|
}
|
|
365
398
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
hashSum.update(fileBuffer);
|
|
370
|
-
const sha256 = hashSum.digest("hex");
|
|
371
|
-
if (sha256 !== info.sha256 && info.sha256 !== "") {
|
|
372
|
-
console.error(`[RenderUpdater] SHA256 mismatch! Expected ${info.sha256}, got ${sha256}`);
|
|
373
|
-
return false;
|
|
374
|
-
}
|
|
375
|
-
if (this.publicKey && info.signature) {
|
|
376
|
-
try {
|
|
377
|
-
const verify = crypto.createVerify("RSA-SHA256");
|
|
378
|
-
verify.update(fileBuffer);
|
|
379
|
-
verify.end();
|
|
380
|
-
const isValid = verify.verify(this.publicKey, info.signature, "base64");
|
|
381
|
-
if (!isValid) {
|
|
382
|
-
console.error("[RenderUpdater] RSA Signature verification failed.");
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
385
|
-
} catch (err) {
|
|
386
|
-
console.error("[RenderUpdater] RSA verification error:", err);
|
|
387
|
-
return false;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
return true;
|
|
391
|
-
}
|
|
392
|
-
useVersion(version) {
|
|
393
|
-
this.updateDiskVersion(version);
|
|
394
|
-
this.updateMemoryVersion(version);
|
|
399
|
+
async useVersion(version) {
|
|
400
|
+
await this.updateDiskVersion(version);
|
|
401
|
+
this._activeVersion = version;
|
|
395
402
|
}
|
|
396
|
-
setUpdatePending(version) {
|
|
397
|
-
this.updateDiskVersion(version);
|
|
403
|
+
async setUpdatePending(version) {
|
|
404
|
+
await this.updateDiskVersion(version);
|
|
398
405
|
if (this.onStatusChanged) {
|
|
399
406
|
this.onStatusChanged({ status: "ready", version });
|
|
400
407
|
}
|
|
401
408
|
}
|
|
402
|
-
updateDiskVersion(version) {
|
|
403
|
-
|
|
409
|
+
async updateDiskVersion(version) {
|
|
410
|
+
await fsPromises.writeFile(
|
|
404
411
|
this.currentVersionFile,
|
|
405
412
|
JSON.stringify({ version, date: (/* @__PURE__ */ new Date()).toISOString() })
|
|
406
413
|
);
|
|
407
414
|
this._diskVersion = version;
|
|
408
415
|
}
|
|
409
|
-
updateMemoryVersion(version) {
|
|
410
|
-
this._activeVersion = version;
|
|
411
|
-
}
|
|
412
416
|
async installAndRestart() {
|
|
413
417
|
if (this.onBeforeRestart) {
|
|
414
418
|
await this.onBeforeRestart();
|
|
@@ -416,104 +420,130 @@ var RenderUpdater = class {
|
|
|
416
420
|
app2.relaunch();
|
|
417
421
|
app2.quit();
|
|
418
422
|
}
|
|
423
|
+
// --- 改版后切分的上帝函数 ---
|
|
419
424
|
async checkForUpdatesAndNotify() {
|
|
420
425
|
try {
|
|
421
426
|
const checkRes = await this.check();
|
|
422
|
-
if (checkRes.updateAvailable
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
};
|
|
439
|
-
if (this.onDownloadComplete) {
|
|
440
|
-
this.onDownloadComplete(info, () => {
|
|
441
|
-
performInstall(true).catch(console.error);
|
|
442
|
-
});
|
|
443
|
-
} else {
|
|
444
|
-
if (info.forceUpdate || isReady) {
|
|
445
|
-
if (isReady) {
|
|
446
|
-
const { response } = await dialog.showMessageBox({
|
|
447
|
-
type: "info",
|
|
448
|
-
title: "\u66F4\u65B0\u5C31\u7EEA",
|
|
449
|
-
message: `\u65B0\u7248\u672C v${info.version} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u5E94\u7528\uFF1F`,
|
|
450
|
-
buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
|
|
451
|
-
defaultId: 0,
|
|
452
|
-
cancelId: 1
|
|
453
|
-
});
|
|
454
|
-
await performInstall(response === 0);
|
|
455
|
-
} else {
|
|
456
|
-
if (info.forceUpdate === "prompt") {
|
|
457
|
-
dialog.showMessageBoxSync({
|
|
458
|
-
type: "warning",
|
|
459
|
-
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
460
|
-
message: `\u6700\u65B0\u7248\u672C v${info.version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u5373\u5C06\u91CD\u542F\u7A0B\u5E8F\u4EE5\u5E94\u7528\u66F4\u65B0\u3002`,
|
|
461
|
-
buttons: ["\u6211\u77E5\u9053\u4E86"]
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
await performInstall(true);
|
|
465
|
-
}
|
|
466
|
-
} else if (this.autoPrompt) {
|
|
467
|
-
const { response } = await dialog.showMessageBox({
|
|
468
|
-
type: "info",
|
|
469
|
-
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
470
|
-
message: `\u6700\u65B0\u7248\u672C v${info.version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u4EE5\u5E94\u7528\u66F4\u65B0\uFF1F`,
|
|
471
|
-
buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
|
|
472
|
-
defaultId: 0,
|
|
473
|
-
cancelId: 1
|
|
474
|
-
});
|
|
475
|
-
await performInstall(response === 0);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
} catch (e) {
|
|
479
|
-
if (this.onError) {
|
|
480
|
-
this.onError(e);
|
|
481
|
-
} else {
|
|
482
|
-
console.error("[RenderUpdater] Download/Install error:", e);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
if (this.onUpdateAvailable) {
|
|
487
|
-
this.onUpdateAvailable(info, doDownload);
|
|
427
|
+
if (!checkRes.updateAvailable || !checkRes.info) return;
|
|
428
|
+
const info = checkRes.info;
|
|
429
|
+
if (checkRes.status === "ready") {
|
|
430
|
+
await this.handleReadyUpdate(info);
|
|
431
|
+
} else {
|
|
432
|
+
await this.handleNewUpdate(info);
|
|
433
|
+
}
|
|
434
|
+
} catch (e) {
|
|
435
|
+
this.handleError(e);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async handleReadyUpdate(info) {
|
|
439
|
+
if (this.onUpdateAvailable) {
|
|
440
|
+
this.onUpdateAvailable(info, async () => {
|
|
441
|
+
if (this.onDownloadComplete) {
|
|
442
|
+
this.onDownloadComplete(info, () => this.performInstall(info, true).catch(this.handleError.bind(this)));
|
|
488
443
|
} else {
|
|
489
|
-
|
|
490
|
-
await doDownload();
|
|
491
|
-
} else if (this.autoDownload) {
|
|
492
|
-
await doDownload();
|
|
493
|
-
} else if (this.autoPrompt) {
|
|
494
|
-
const { response } = await dialog.showMessageBox({
|
|
495
|
-
type: "info",
|
|
496
|
-
title: "\u53D1\u73B0\u65B0\u7248\u672C",
|
|
497
|
-
message: `\u53D1\u73B0\u53EF\u7528\u66F4\u65B0 (v${info.version})\u3002\u662F\u5426\u7ACB\u5373\u4E0B\u8F7D\u66F4\u65B0\uFF1F
|
|
498
|
-
|
|
499
|
-
\u66F4\u65B0\u65E5\u5FD7\uFF1A
|
|
500
|
-
${info.releaseNotes || "\u65E0\u8BE6\u7EC6\u8BB0\u5F55\u3002"}`,
|
|
501
|
-
buttons: ["\u7ACB\u5373\u66F4\u65B0", "\u7A0D\u540E\u518D\u8BF4"],
|
|
502
|
-
cancelId: 1
|
|
503
|
-
});
|
|
504
|
-
if (response === 0) {
|
|
505
|
-
await doDownload();
|
|
506
|
-
}
|
|
507
|
-
}
|
|
444
|
+
await this.performInstall(info, true);
|
|
508
445
|
}
|
|
446
|
+
});
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (info.forceUpdate || !this.autoPrompt) {
|
|
450
|
+
await this.performInstall(info, true);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
const response = await this.showReadyDialog(info.version);
|
|
454
|
+
await this.performInstall(info, response === 0);
|
|
455
|
+
}
|
|
456
|
+
async handleNewUpdate(info) {
|
|
457
|
+
if (this.onUpdateAvailable) {
|
|
458
|
+
this.onUpdateAvailable(info, () => this.executeAutoDownload(info));
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (info.forceUpdate || this.autoDownload) {
|
|
462
|
+
await this.executeAutoDownload(info);
|
|
463
|
+
} else if (this.autoPrompt) {
|
|
464
|
+
const response = await this.showNewUpdateDialog(info);
|
|
465
|
+
if (response === 0) {
|
|
466
|
+
await this.executeAutoDownload(info);
|
|
509
467
|
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
async executeAutoDownload(info) {
|
|
471
|
+
try {
|
|
472
|
+
await this.download(info, this.onDownloadProgress);
|
|
473
|
+
const doInstall = (shouldRestart) => this.performInstall(info, shouldRestart).catch(this.handleError.bind(this));
|
|
474
|
+
if (this.onDownloadComplete) {
|
|
475
|
+
this.onDownloadComplete(info, () => doInstall(true));
|
|
513
476
|
} else {
|
|
514
|
-
|
|
477
|
+
const isSilent = info.forceUpdate === "silent";
|
|
478
|
+
const shouldRestart = isSilent || await this.showDownloadCompleteDialog(info.version, info.forceUpdate === "prompt") === 0;
|
|
479
|
+
await doInstall(shouldRestart);
|
|
515
480
|
}
|
|
481
|
+
} catch (e) {
|
|
482
|
+
this.handleError(e);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
async performInstall(info, shouldRestart) {
|
|
486
|
+
if (shouldRestart) {
|
|
487
|
+
await this.useVersion(info.version);
|
|
488
|
+
await this.installAndRestart();
|
|
489
|
+
} else {
|
|
490
|
+
await this.setUpdatePending(info.version);
|
|
491
|
+
console.info(`[RenderUpdater] Update v${info.version} ready, will apply on next launch.`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
handleError(error) {
|
|
495
|
+
if (this.onError) {
|
|
496
|
+
this.onError(error);
|
|
497
|
+
} else {
|
|
498
|
+
console.error("[RenderUpdater] Error:", error);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// UI 分发抽离
|
|
502
|
+
async showReadyDialog(version) {
|
|
503
|
+
const { response } = await dialog.showMessageBox({
|
|
504
|
+
type: "info",
|
|
505
|
+
title: "\u66F4\u65B0\u5C31\u7EEA",
|
|
506
|
+
message: `\u65B0\u7248\u672C v${version} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u5E94\u7528\uFF1F`,
|
|
507
|
+
buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
|
|
508
|
+
defaultId: 0,
|
|
509
|
+
cancelId: 1
|
|
510
|
+
});
|
|
511
|
+
return response;
|
|
512
|
+
}
|
|
513
|
+
async showNewUpdateDialog(info) {
|
|
514
|
+
const { response } = await dialog.showMessageBox({
|
|
515
|
+
type: "info",
|
|
516
|
+
title: "\u53D1\u73B0\u65B0\u7248\u672C",
|
|
517
|
+
message: `\u53D1\u73B0\u53EF\u7528\u66F4\u65B0 (v${info.version})\u3002\u662F\u5426\u7ACB\u5373\u4E0B\u8F7D\u66F4\u65B0\uFF1F
|
|
518
|
+
|
|
519
|
+
\u66F4\u65B0\u65E5\u5FD7\uFF1A
|
|
520
|
+
${info.releaseNotes || "\u65E0\u8BE6\u7EC6\u8BB0\u5F55\u3002"}`,
|
|
521
|
+
buttons: ["\u7ACB\u5373\u66F4\u65B0", "\u7A0D\u540E\u518D\u8BF4"],
|
|
522
|
+
cancelId: 1
|
|
523
|
+
});
|
|
524
|
+
return response;
|
|
525
|
+
}
|
|
526
|
+
async showDownloadCompleteDialog(version, forcePrompt) {
|
|
527
|
+
if (forcePrompt) {
|
|
528
|
+
dialog.showMessageBoxSync({
|
|
529
|
+
type: "warning",
|
|
530
|
+
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
531
|
+
message: `\u6700\u65B0\u7248\u672C v${version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u5373\u5C06\u91CD\u542F\u7A0B\u5E8F\u4EE5\u5E94\u7528\u66F4\u65B0\u3002`,
|
|
532
|
+
buttons: ["\u6211\u77E5\u9053\u4E86"]
|
|
533
|
+
});
|
|
534
|
+
return 0;
|
|
535
|
+
} else if (this.autoPrompt) {
|
|
536
|
+
const { response } = await dialog.showMessageBox({
|
|
537
|
+
type: "info",
|
|
538
|
+
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
539
|
+
message: `\u6700\u65B0\u7248\u672C v${version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u4EE5\u5E94\u7528\u66F4\u65B0\uFF1F`,
|
|
540
|
+
buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
|
|
541
|
+
defaultId: 0,
|
|
542
|
+
cancelId: 1
|
|
543
|
+
});
|
|
544
|
+
return response;
|
|
516
545
|
}
|
|
546
|
+
return 0;
|
|
517
547
|
}
|
|
518
548
|
};
|
|
519
549
|
export {
|