electron-updater-for-render 1.1.2-beta.7 → 2.0.0-beta.1
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/bin/cli.cjs +4 -2
- package/dist/bin/cli.js +4 -2
- package/dist/builder/index.cjs +4 -2
- package/dist/builder/index.js +4 -2
- package/dist/main/index.cjs +275 -174
- package/dist/main/index.d.ts +15 -5
- package/dist/main/index.js +280 -173
- package/dist/main/utils.d.ts +6 -0
- package/dist/preload/index.cjs +47 -0
- package/dist/preload/index.d.ts +13 -0
- package/dist/preload/index.js +22 -0
- package/dist/renderer/index.cjs +39 -0
- package/dist/renderer/index.d.ts +23 -0
- package/dist/renderer/index.js +14 -0
- package/dist/types.cjs +18 -0
- package/dist/types.d.ts +31 -0
- package/dist/types.js +0 -0
- package/package.json +21 -1
package/dist/bin/cli.cjs
CHANGED
|
@@ -50,7 +50,8 @@ async function createUpdatePackage(options) {
|
|
|
50
50
|
packageJsonPath,
|
|
51
51
|
privateKeyPath,
|
|
52
52
|
releaseNotesPath,
|
|
53
|
-
forceUpdate
|
|
53
|
+
forceUpdate,
|
|
54
|
+
rolloutRule
|
|
54
55
|
} = options;
|
|
55
56
|
if (!import_fs.default.existsSync(outDir)) {
|
|
56
57
|
throw new Error(`Output directory not found at ${outDir}`);
|
|
@@ -100,7 +101,8 @@ async function createUpdatePackage(options) {
|
|
|
100
101
|
...signature ? { signature } : {},
|
|
101
102
|
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
102
103
|
releaseNotes: releaseNotesContent,
|
|
103
|
-
...forceUpdate ? { forceUpdate } : {}
|
|
104
|
+
...forceUpdate ? { forceUpdate } : {},
|
|
105
|
+
...rolloutRule ? { rolloutRule } : {}
|
|
104
106
|
};
|
|
105
107
|
const jsonPath = import_path.default.join(updatesDir, "latest.json");
|
|
106
108
|
import_fs.default.writeFileSync(jsonPath, JSON.stringify(updateInfo, null, 2));
|
package/dist/bin/cli.js
CHANGED
|
@@ -23,7 +23,8 @@ async function createUpdatePackage(options) {
|
|
|
23
23
|
packageJsonPath,
|
|
24
24
|
privateKeyPath,
|
|
25
25
|
releaseNotesPath,
|
|
26
|
-
forceUpdate
|
|
26
|
+
forceUpdate,
|
|
27
|
+
rolloutRule
|
|
27
28
|
} = options;
|
|
28
29
|
if (!fs.existsSync(outDir)) {
|
|
29
30
|
throw new Error(`Output directory not found at ${outDir}`);
|
|
@@ -73,7 +74,8 @@ async function createUpdatePackage(options) {
|
|
|
73
74
|
...signature ? { signature } : {},
|
|
74
75
|
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
75
76
|
releaseNotes: releaseNotesContent,
|
|
76
|
-
...forceUpdate ? { forceUpdate } : {}
|
|
77
|
+
...forceUpdate ? { forceUpdate } : {},
|
|
78
|
+
...rolloutRule ? { rolloutRule } : {}
|
|
77
79
|
};
|
|
78
80
|
const jsonPath = path.join(updatesDir, "latest.json");
|
|
79
81
|
fs.writeFileSync(jsonPath, JSON.stringify(updateInfo, null, 2));
|
package/dist/builder/index.cjs
CHANGED
|
@@ -52,7 +52,8 @@ async function createUpdatePackage(options) {
|
|
|
52
52
|
packageJsonPath,
|
|
53
53
|
privateKeyPath,
|
|
54
54
|
releaseNotesPath,
|
|
55
|
-
forceUpdate
|
|
55
|
+
forceUpdate,
|
|
56
|
+
rolloutRule
|
|
56
57
|
} = options;
|
|
57
58
|
if (!import_fs.default.existsSync(outDir)) {
|
|
58
59
|
throw new Error(`Output directory not found at ${outDir}`);
|
|
@@ -102,7 +103,8 @@ async function createUpdatePackage(options) {
|
|
|
102
103
|
...signature ? { signature } : {},
|
|
103
104
|
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
104
105
|
releaseNotes: releaseNotesContent,
|
|
105
|
-
...forceUpdate ? { forceUpdate } : {}
|
|
106
|
+
...forceUpdate ? { forceUpdate } : {},
|
|
107
|
+
...rolloutRule ? { rolloutRule } : {}
|
|
106
108
|
};
|
|
107
109
|
const jsonPath = import_path.default.join(updatesDir, "latest.json");
|
|
108
110
|
import_fs.default.writeFileSync(jsonPath, JSON.stringify(updateInfo, null, 2));
|
package/dist/builder/index.js
CHANGED
|
@@ -17,7 +17,8 @@ async function createUpdatePackage(options) {
|
|
|
17
17
|
packageJsonPath,
|
|
18
18
|
privateKeyPath,
|
|
19
19
|
releaseNotesPath,
|
|
20
|
-
forceUpdate
|
|
20
|
+
forceUpdate,
|
|
21
|
+
rolloutRule
|
|
21
22
|
} = options;
|
|
22
23
|
if (!fs.existsSync(outDir)) {
|
|
23
24
|
throw new Error(`Output directory not found at ${outDir}`);
|
|
@@ -67,7 +68,8 @@ async function createUpdatePackage(options) {
|
|
|
67
68
|
...signature ? { signature } : {},
|
|
68
69
|
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
69
70
|
releaseNotes: releaseNotesContent,
|
|
70
|
-
...forceUpdate ? { forceUpdate } : {}
|
|
71
|
+
...forceUpdate ? { forceUpdate } : {},
|
|
72
|
+
...rolloutRule ? { rolloutRule } : {}
|
|
71
73
|
};
|
|
72
74
|
const jsonPath = path.join(updatesDir, "latest.json");
|
|
73
75
|
fs.writeFileSync(jsonPath, JSON.stringify(updateInfo, null, 2));
|
package/dist/main/index.cjs
CHANGED
|
@@ -30,12 +30,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/main/index.ts
|
|
31
31
|
var main_exports = {};
|
|
32
32
|
__export(main_exports, {
|
|
33
|
-
RenderUpdater: () => RenderUpdater
|
|
33
|
+
RenderUpdater: () => RenderUpdater,
|
|
34
|
+
setupUpdaterIPC: () => setupUpdaterIPC
|
|
34
35
|
});
|
|
35
36
|
module.exports = __toCommonJS(main_exports);
|
|
36
37
|
var import_path2 = __toESM(require("path"), 1);
|
|
37
38
|
var import_original_fs = __toESM(require("original-fs"), 1);
|
|
38
|
-
var import_crypto = __toESM(require("crypto"), 1);
|
|
39
39
|
var import_semver = __toESM(require("semver"), 1);
|
|
40
40
|
var import_electron2 = require("electron");
|
|
41
41
|
var import_promises = require("stream/promises");
|
|
@@ -158,7 +158,46 @@ var RouterHandler = class _RouterHandler {
|
|
|
158
158
|
}
|
|
159
159
|
};
|
|
160
160
|
|
|
161
|
+
// src/main/utils.ts
|
|
162
|
+
var import_fs2 = require("fs");
|
|
163
|
+
var import_crypto = __toESM(require("crypto"), 1);
|
|
164
|
+
function verifyFileStream(filePath, info, publicKey) {
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
const hash = import_crypto.default.createHash("sha256");
|
|
167
|
+
const verify = publicKey && info.signature ? import_crypto.default.createVerify("RSA-SHA256") : null;
|
|
168
|
+
const stream = (0, import_fs2.createReadStream)(filePath);
|
|
169
|
+
stream.on("data", (chunk) => {
|
|
170
|
+
hash.update(chunk);
|
|
171
|
+
if (verify) {
|
|
172
|
+
verify.update(chunk);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
stream.on("end", () => {
|
|
176
|
+
const sha256 = hash.digest("hex");
|
|
177
|
+
if (info.sha256 && sha256 !== info.sha256) {
|
|
178
|
+
console.error(`[RenderUpdater] SHA256 mismatch! Expected ${info.sha256}, got ${sha256}`);
|
|
179
|
+
return resolve(false);
|
|
180
|
+
}
|
|
181
|
+
if (verify && publicKey && info.signature) {
|
|
182
|
+
verify.end();
|
|
183
|
+
const isValid = verify.verify(publicKey, info.signature, "base64");
|
|
184
|
+
if (!isValid) {
|
|
185
|
+
console.error("[RenderUpdater] RSA Signature verification failed.");
|
|
186
|
+
return resolve(false);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
resolve(true);
|
|
190
|
+
});
|
|
191
|
+
stream.on("error", (err) => {
|
|
192
|
+
console.error("[RenderUpdater] Stream error during file verification:", err);
|
|
193
|
+
reject(err);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
161
198
|
// src/main/index.ts
|
|
199
|
+
var import_electron3 = require("electron");
|
|
200
|
+
var fsPromises = import_original_fs.default.promises;
|
|
162
201
|
var RenderUpdater = class {
|
|
163
202
|
versionsDir;
|
|
164
203
|
currentVersionFile;
|
|
@@ -180,6 +219,9 @@ var RenderUpdater = class {
|
|
|
180
219
|
onStatusChanged;
|
|
181
220
|
onError;
|
|
182
221
|
onBeforeRestart;
|
|
222
|
+
identity;
|
|
223
|
+
allowPrerelease;
|
|
224
|
+
requestOptions;
|
|
183
225
|
constructor(options) {
|
|
184
226
|
console.log("[RenderUpdater] Initializing with versionsDir:", options.versionsDir);
|
|
185
227
|
this.versionsDir = options.versionsDir;
|
|
@@ -197,6 +239,9 @@ var RenderUpdater = class {
|
|
|
197
239
|
this.onBeforeRestart = options.onBeforeRestart;
|
|
198
240
|
this.routerMode = options.routerMode ?? "hash";
|
|
199
241
|
this.protocolName = options.protocol ?? "app";
|
|
242
|
+
this.identity = options.identity;
|
|
243
|
+
this.allowPrerelease = options.allowPrerelease ?? false;
|
|
244
|
+
this.requestOptions = options.requestOptions;
|
|
200
245
|
this.download = this.download.bind(this);
|
|
201
246
|
this.check = this.check.bind(this);
|
|
202
247
|
this.checkForUpdatesAndNotify = this.checkForUpdatesAndNotify.bind(this);
|
|
@@ -206,7 +251,7 @@ var RenderUpdater = class {
|
|
|
206
251
|
if (!import_original_fs.default.existsSync(this.versionsDir)) {
|
|
207
252
|
import_original_fs.default.mkdirSync(this.versionsDir, { recursive: true });
|
|
208
253
|
}
|
|
209
|
-
const savedVersion = this.
|
|
254
|
+
const savedVersion = this.readCurrentVersionFromFileSync();
|
|
210
255
|
this._activeVersion = savedVersion || "0.0.0";
|
|
211
256
|
this._diskVersion = savedVersion || "0.0.0";
|
|
212
257
|
console.log("[RenderUpdater] Initialized. Active version:", this._activeVersion);
|
|
@@ -221,7 +266,8 @@ var RenderUpdater = class {
|
|
|
221
266
|
});
|
|
222
267
|
}
|
|
223
268
|
}
|
|
224
|
-
|
|
269
|
+
// 初始化使用一次的同步方法(可接受)
|
|
270
|
+
readCurrentVersionFromFileSync() {
|
|
225
271
|
if (import_original_fs.default.existsSync(this.currentVersionFile)) {
|
|
226
272
|
try {
|
|
227
273
|
const data = JSON.parse(import_original_fs.default.readFileSync(this.currentVersionFile, "utf-8"));
|
|
@@ -234,35 +280,33 @@ var RenderUpdater = class {
|
|
|
234
280
|
}
|
|
235
281
|
return "0.0.0";
|
|
236
282
|
}
|
|
237
|
-
|
|
283
|
+
// 全面异步替换原同步清理操作
|
|
284
|
+
async cleanOldVersions() {
|
|
238
285
|
try {
|
|
239
286
|
if (!this.versionsDir) return;
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
});
|
|
287
|
+
const files = await fsPromises.readdir(this.versionsDir, { withFileTypes: true });
|
|
288
|
+
const versionDirs = files.filter((dirent) => dirent.isDirectory() && import_semver.default.valid(dirent.name)).map((dirent) => dirent.name);
|
|
243
289
|
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
290
|
const safeVersions = /* @__PURE__ */ new Set();
|
|
247
|
-
|
|
248
|
-
|
|
291
|
+
safeVersions.add(this._activeVersion);
|
|
292
|
+
safeVersions.add(this._diskVersion);
|
|
249
293
|
let kept = 0;
|
|
250
294
|
for (const v of versionDirs) {
|
|
251
295
|
if (!safeVersions.has(v)) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
296
|
+
if (kept >= this.maxVersionsToKeep) {
|
|
297
|
+
console.info(`[RenderUpdater] Cleaning up old version: ${v}`);
|
|
298
|
+
try {
|
|
299
|
+
await fsPromises.rm(import_path2.default.join(this.versionsDir, v), { recursive: true, force: true });
|
|
300
|
+
} catch (e) {
|
|
301
|
+
if (e.code === "EBUSY") {
|
|
302
|
+
console.warn(`[RenderUpdater] Version ${v} is locked, skipping.`);
|
|
303
|
+
} else {
|
|
304
|
+
console.error(`[RenderUpdater] Failed to remove version ${v}:`, e);
|
|
305
|
+
}
|
|
260
306
|
}
|
|
307
|
+
} else {
|
|
308
|
+
kept++;
|
|
261
309
|
}
|
|
262
|
-
} else {
|
|
263
|
-
kept++;
|
|
264
|
-
}
|
|
265
|
-
if (kept >= this.maxVersionsToKeep && !safeVersions.has(v)) {
|
|
266
310
|
}
|
|
267
311
|
}
|
|
268
312
|
} catch (e) {
|
|
@@ -297,7 +341,18 @@ var RenderUpdater = class {
|
|
|
297
341
|
async check() {
|
|
298
342
|
let remoteInfo = null;
|
|
299
343
|
try {
|
|
300
|
-
const
|
|
344
|
+
const fetchUrl = new URL(`${this.baseUrl}/latest.json`);
|
|
345
|
+
fetchUrl.searchParams.append("t", Date.now().toString());
|
|
346
|
+
if (this.requestOptions?.query) {
|
|
347
|
+
for (const [k, v] of Object.entries(this.requestOptions.query)) {
|
|
348
|
+
fetchUrl.searchParams.append(k, v);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const fetchOpts = {};
|
|
352
|
+
if (this.requestOptions?.headers) {
|
|
353
|
+
fetchOpts.headers = this.requestOptions.headers;
|
|
354
|
+
}
|
|
355
|
+
const response = await fetch(fetchUrl.toString(), fetchOpts);
|
|
301
356
|
if (response.ok) {
|
|
302
357
|
remoteInfo = await response.json();
|
|
303
358
|
}
|
|
@@ -307,7 +362,16 @@ var RenderUpdater = class {
|
|
|
307
362
|
const runningVersion = this._activeVersion || "0.0.0";
|
|
308
363
|
const pendingVersion = this._diskVersion || "0.0.0";
|
|
309
364
|
if (remoteInfo && remoteInfo.version && import_semver.default.valid(remoteInfo.version)) {
|
|
310
|
-
|
|
365
|
+
let allowUpdate = true;
|
|
366
|
+
if (remoteInfo.rolloutRule?.deviceIds) {
|
|
367
|
+
if (!this.identity?.deviceId || !remoteInfo.rolloutRule.deviceIds.includes(this.identity.deviceId)) {
|
|
368
|
+
allowUpdate = false;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (allowUpdate && !this.allowPrerelease && import_semver.default.prerelease(remoteInfo.version)) {
|
|
372
|
+
allowUpdate = false;
|
|
373
|
+
}
|
|
374
|
+
if (allowUpdate && import_semver.default.gt(remoteInfo.version, pendingVersion, { includePrerelease: this.allowPrerelease })) {
|
|
311
375
|
return { updateAvailable: true, version: remoteInfo.version, info: remoteInfo, status: "available" };
|
|
312
376
|
}
|
|
313
377
|
}
|
|
@@ -332,32 +396,46 @@ var RenderUpdater = class {
|
|
|
332
396
|
this.isDownloading = true;
|
|
333
397
|
try {
|
|
334
398
|
console.log("[RenderUpdater] download() entry. current this.versionsDir:", this.versionsDir);
|
|
335
|
-
|
|
336
|
-
if (
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
|
|
399
|
+
let infoToUse;
|
|
400
|
+
if (providedInfo) {
|
|
401
|
+
infoToUse = providedInfo;
|
|
402
|
+
} else {
|
|
403
|
+
const fetchUrl = new URL(`${this.baseUrl}/latest.json`);
|
|
404
|
+
fetchUrl.searchParams.append("t", Date.now().toString());
|
|
405
|
+
if (this.requestOptions?.query) {
|
|
406
|
+
for (const [k, v] of Object.entries(this.requestOptions.query)) {
|
|
407
|
+
fetchUrl.searchParams.append(k, v);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
const fetchOpts = {};
|
|
411
|
+
if (this.requestOptions?.headers) {
|
|
412
|
+
fetchOpts.headers = this.requestOptions.headers;
|
|
413
|
+
}
|
|
414
|
+
const res = await fetch(fetchUrl.toString(), fetchOpts);
|
|
415
|
+
infoToUse = await res.json();
|
|
341
416
|
}
|
|
417
|
+
if (!this.versionsDir) throw new Error("[RenderUpdater] CRITICAL: this.versionsDir lost in download.");
|
|
418
|
+
if (!infoToUse || !infoToUse.version) throw new Error(`[RenderUpdater] CRITICAL: infoToUse or version missing. info: ${JSON.stringify(infoToUse)}`);
|
|
342
419
|
const versionDir = import_path2.default.join(this.versionsDir, infoToUse.version);
|
|
343
|
-
const
|
|
344
|
-
|
|
420
|
+
const finalAsarPath = import_path2.default.join(versionDir, "renderer.asar");
|
|
421
|
+
const tmpAsarPath = import_path2.default.join(versionDir, "renderer.asar.tmp");
|
|
422
|
+
if (import_original_fs.default.existsSync(finalAsarPath) && await verifyFileStream(finalAsarPath, infoToUse, this.publicKey)) {
|
|
345
423
|
console.log("[RenderUpdater] File already exists and verified. Skipping download.");
|
|
346
424
|
onProgress?.(100);
|
|
347
425
|
return;
|
|
348
426
|
}
|
|
349
|
-
|
|
350
|
-
import_original_fs.default.mkdirSync(versionDir, { recursive: true });
|
|
351
|
-
}
|
|
427
|
+
await fsPromises.mkdir(versionDir, { recursive: true });
|
|
352
428
|
let downloadedBytes = 0;
|
|
353
|
-
if (import_original_fs.default.existsSync(
|
|
354
|
-
|
|
429
|
+
if (import_original_fs.default.existsSync(tmpAsarPath)) {
|
|
430
|
+
const stats = await fsPromises.stat(tmpAsarPath);
|
|
431
|
+
downloadedBytes = stats.size;
|
|
432
|
+
}
|
|
433
|
+
const fetchOptions = { headers: {} };
|
|
434
|
+
if (this.requestOptions?.headers) {
|
|
435
|
+
Object.assign(fetchOptions.headers, this.requestOptions.headers);
|
|
355
436
|
}
|
|
356
|
-
const fetchOptions = {};
|
|
357
437
|
if (downloadedBytes > 0) {
|
|
358
|
-
fetchOptions.headers = {
|
|
359
|
-
Range: `bytes=${downloadedBytes}-`
|
|
360
|
-
};
|
|
438
|
+
fetchOptions.headers["Range"] = `bytes=${downloadedBytes}-`;
|
|
361
439
|
}
|
|
362
440
|
const downloadUrl = infoToUse.path ? `${this.baseUrl.replace(/\/$/, "")}/${infoToUse.path}` : infoToUse.url;
|
|
363
441
|
if (!downloadUrl) throw new Error("[RenderUpdater] Cannot determine download URL: info.path and info.url are both missing.");
|
|
@@ -372,7 +450,7 @@ var RenderUpdater = class {
|
|
|
372
450
|
const incomingTotal = contentLength ? parseInt(contentLength, 10) : 0;
|
|
373
451
|
const total = downloadedBytes + incomingTotal;
|
|
374
452
|
if (!downloadResponse.body) throw new Error("Response body is empty");
|
|
375
|
-
const fileStream = import_original_fs.default.createWriteStream(
|
|
453
|
+
const fileStream = import_original_fs.default.createWriteStream(tmpAsarPath, { flags: downloadResponse.status === 206 ? "a" : "w" });
|
|
376
454
|
const progressTransform = new import_stream.Transform({
|
|
377
455
|
transform(chunk, _encoding, callback) {
|
|
378
456
|
downloadedBytes += chunk.length;
|
|
@@ -387,62 +465,34 @@ var RenderUpdater = class {
|
|
|
387
465
|
progressTransform,
|
|
388
466
|
fileStream
|
|
389
467
|
);
|
|
390
|
-
if (!
|
|
391
|
-
|
|
468
|
+
if (!await verifyFileStream(tmpAsarPath, infoToUse, this.publicKey)) {
|
|
469
|
+
await fsPromises.rm(tmpAsarPath, { force: true });
|
|
392
470
|
throw new Error("[RenderUpdater] Verification failed after download (SHA256 or RSA Mismatch)");
|
|
393
471
|
}
|
|
394
|
-
|
|
395
|
-
this.
|
|
472
|
+
await fsPromises.rename(tmpAsarPath, finalAsarPath);
|
|
473
|
+
await this.setUpdatePending(infoToUse.version);
|
|
474
|
+
await this.cleanOldVersions();
|
|
396
475
|
} finally {
|
|
397
476
|
this.isDownloading = false;
|
|
398
477
|
}
|
|
399
478
|
}
|
|
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);
|
|
479
|
+
async useVersion(version) {
|
|
480
|
+
await this.updateDiskVersion(version);
|
|
481
|
+
this._activeVersion = version;
|
|
429
482
|
}
|
|
430
|
-
setUpdatePending(version) {
|
|
431
|
-
this.updateDiskVersion(version);
|
|
483
|
+
async setUpdatePending(version) {
|
|
484
|
+
await this.updateDiskVersion(version);
|
|
432
485
|
if (this.onStatusChanged) {
|
|
433
486
|
this.onStatusChanged({ status: "ready", version });
|
|
434
487
|
}
|
|
435
488
|
}
|
|
436
|
-
updateDiskVersion(version) {
|
|
437
|
-
|
|
489
|
+
async updateDiskVersion(version) {
|
|
490
|
+
await fsPromises.writeFile(
|
|
438
491
|
this.currentVersionFile,
|
|
439
492
|
JSON.stringify({ version, date: (/* @__PURE__ */ new Date()).toISOString() })
|
|
440
493
|
);
|
|
441
494
|
this._diskVersion = version;
|
|
442
495
|
}
|
|
443
|
-
updateMemoryVersion(version) {
|
|
444
|
-
this._activeVersion = version;
|
|
445
|
-
}
|
|
446
496
|
async installAndRestart() {
|
|
447
497
|
if (this.onBeforeRestart) {
|
|
448
498
|
await this.onBeforeRestart();
|
|
@@ -450,107 +500,158 @@ var RenderUpdater = class {
|
|
|
450
500
|
import_electron2.app.relaunch();
|
|
451
501
|
import_electron2.app.quit();
|
|
452
502
|
}
|
|
503
|
+
// --- 改版后切分的上帝函数 ---
|
|
453
504
|
async checkForUpdatesAndNotify() {
|
|
454
505
|
try {
|
|
455
506
|
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);
|
|
507
|
+
if (!checkRes.updateAvailable || !checkRes.info) return;
|
|
508
|
+
const info = checkRes.info;
|
|
509
|
+
if (checkRes.status === "ready") {
|
|
510
|
+
await this.handleReadyUpdate(info);
|
|
511
|
+
} else {
|
|
512
|
+
await this.handleNewUpdate(info);
|
|
513
|
+
}
|
|
514
|
+
} catch (e) {
|
|
515
|
+
this.handleError(e);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
async handleReadyUpdate(info) {
|
|
519
|
+
if (this.onUpdateAvailable) {
|
|
520
|
+
this.onUpdateAvailable(info, async () => {
|
|
521
|
+
if (this.onDownloadComplete) {
|
|
522
|
+
this.onDownloadComplete(info, () => this.performInstall(info, true).catch(this.handleError.bind(this)));
|
|
522
523
|
} 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
|
-
}
|
|
524
|
+
await this.performInstall(info, true);
|
|
542
525
|
}
|
|
526
|
+
});
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
if (info.forceUpdate || !this.autoPrompt) {
|
|
530
|
+
await this.performInstall(info, true);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const response = await this.showReadyDialog(info.version);
|
|
534
|
+
await this.performInstall(info, response === 0);
|
|
535
|
+
}
|
|
536
|
+
async handleNewUpdate(info) {
|
|
537
|
+
if (this.onUpdateAvailable) {
|
|
538
|
+
this.onUpdateAvailable(info, () => this.executeAutoDownload(info));
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
if (info.forceUpdate || this.autoDownload) {
|
|
542
|
+
await this.executeAutoDownload(info);
|
|
543
|
+
} else if (this.autoPrompt) {
|
|
544
|
+
const response = await this.showNewUpdateDialog(info);
|
|
545
|
+
if (response === 0) {
|
|
546
|
+
await this.executeAutoDownload(info);
|
|
543
547
|
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
async executeAutoDownload(info) {
|
|
551
|
+
try {
|
|
552
|
+
await this.download(info, this.onDownloadProgress);
|
|
553
|
+
const doInstall = (shouldRestart) => this.performInstall(info, shouldRestart).catch(this.handleError.bind(this));
|
|
554
|
+
if (this.onDownloadComplete) {
|
|
555
|
+
this.onDownloadComplete(info, () => doInstall(true));
|
|
547
556
|
} else {
|
|
548
|
-
|
|
557
|
+
const isSilent = info.forceUpdate === "silent";
|
|
558
|
+
const shouldRestart = isSilent || await this.showDownloadCompleteDialog(info.version, info.forceUpdate === "prompt") === 0;
|
|
559
|
+
await doInstall(shouldRestart);
|
|
549
560
|
}
|
|
561
|
+
} catch (e) {
|
|
562
|
+
this.handleError(e);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
async performInstall(info, shouldRestart) {
|
|
566
|
+
if (shouldRestart) {
|
|
567
|
+
await this.useVersion(info.version);
|
|
568
|
+
await this.installAndRestart();
|
|
569
|
+
} else {
|
|
570
|
+
await this.setUpdatePending(info.version);
|
|
571
|
+
console.info(`[RenderUpdater] Update v${info.version} ready, will apply on next launch.`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
handleError(error) {
|
|
575
|
+
if (this.onError) {
|
|
576
|
+
this.onError(error);
|
|
577
|
+
} else {
|
|
578
|
+
console.error("[RenderUpdater] Error:", error);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
// UI 分发抽离
|
|
582
|
+
async showReadyDialog(version) {
|
|
583
|
+
const { response } = await import_electron2.dialog.showMessageBox({
|
|
584
|
+
type: "info",
|
|
585
|
+
title: "\u66F4\u65B0\u5C31\u7EEA",
|
|
586
|
+
message: `\u65B0\u7248\u672C v${version} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u5E94\u7528\uFF1F`,
|
|
587
|
+
buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
|
|
588
|
+
defaultId: 0,
|
|
589
|
+
cancelId: 1
|
|
590
|
+
});
|
|
591
|
+
return response;
|
|
592
|
+
}
|
|
593
|
+
async showNewUpdateDialog(info) {
|
|
594
|
+
const { response } = await import_electron2.dialog.showMessageBox({
|
|
595
|
+
type: "info",
|
|
596
|
+
title: "\u53D1\u73B0\u65B0\u7248\u672C",
|
|
597
|
+
message: `\u53D1\u73B0\u53EF\u7528\u66F4\u65B0 (v${info.version})\u3002\u662F\u5426\u7ACB\u5373\u4E0B\u8F7D\u66F4\u65B0\uFF1F
|
|
598
|
+
|
|
599
|
+
\u66F4\u65B0\u65E5\u5FD7\uFF1A
|
|
600
|
+
${info.releaseNotes || "\u65E0\u8BE6\u7EC6\u8BB0\u5F55\u3002"}`,
|
|
601
|
+
buttons: ["\u7ACB\u5373\u66F4\u65B0", "\u7A0D\u540E\u518D\u8BF4"],
|
|
602
|
+
cancelId: 1
|
|
603
|
+
});
|
|
604
|
+
return response;
|
|
605
|
+
}
|
|
606
|
+
async showDownloadCompleteDialog(version, forcePrompt) {
|
|
607
|
+
if (forcePrompt) {
|
|
608
|
+
import_electron2.dialog.showMessageBoxSync({
|
|
609
|
+
type: "warning",
|
|
610
|
+
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
611
|
+
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`,
|
|
612
|
+
buttons: ["\u6211\u77E5\u9053\u4E86"]
|
|
613
|
+
});
|
|
614
|
+
return 0;
|
|
615
|
+
} else if (this.autoPrompt) {
|
|
616
|
+
const { response } = await import_electron2.dialog.showMessageBox({
|
|
617
|
+
type: "info",
|
|
618
|
+
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
619
|
+
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`,
|
|
620
|
+
buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
|
|
621
|
+
defaultId: 0,
|
|
622
|
+
cancelId: 1
|
|
623
|
+
});
|
|
624
|
+
return response;
|
|
550
625
|
}
|
|
626
|
+
return 0;
|
|
551
627
|
}
|
|
552
628
|
};
|
|
629
|
+
function setupUpdaterIPC(updater) {
|
|
630
|
+
import_electron3.ipcMain.handle("updater:check", () => updater.check());
|
|
631
|
+
import_electron3.ipcMain.handle("updater:download", () => updater.download());
|
|
632
|
+
import_electron3.ipcMain.handle("updater:installAndRestart", () => updater.installAndRestart());
|
|
633
|
+
import_electron3.ipcMain.on("updater:useVersion", (_, version) => updater.useVersion(version));
|
|
634
|
+
updater.onDownloadProgress = (percent) => {
|
|
635
|
+
const { webContents } = require("electron");
|
|
636
|
+
webContents.getAllWebContents().forEach((wc) => {
|
|
637
|
+
wc.send("updater:onDownloadProgress", percent);
|
|
638
|
+
});
|
|
639
|
+
};
|
|
640
|
+
updater.onStatusChanged = (status) => {
|
|
641
|
+
const { webContents } = require("electron");
|
|
642
|
+
webContents.getAllWebContents().forEach((wc) => {
|
|
643
|
+
wc.send("updater:onStatusChanged", status);
|
|
644
|
+
});
|
|
645
|
+
};
|
|
646
|
+
updater.onBeforeRestart = () => {
|
|
647
|
+
const { webContents } = require("electron");
|
|
648
|
+
webContents.getAllWebContents().forEach((wc) => {
|
|
649
|
+
wc.send("updater:onBeforeRestart");
|
|
650
|
+
});
|
|
651
|
+
};
|
|
652
|
+
}
|
|
553
653
|
// Annotate the CommonJS export names for ESM import in node:
|
|
554
654
|
0 && (module.exports = {
|
|
555
|
-
RenderUpdater
|
|
655
|
+
RenderUpdater,
|
|
656
|
+
setupUpdaterIPC
|
|
556
657
|
});
|