CH-J Toolspengurusan pelayan melalui SSH
Menu
Sumber yang diterbitkan

CH-J Tools

Layari direktori dan fail untuk keluaran aplikasi tertentu.

Muat turun ZIP sumber
CH-J / Server Manager 406 fail
core-20261024T135205Z-7c487d719de2 Ditambah: · Dibina:
updateService.js Ditambah: Tarikh tidak direkodkan 7.8 KB · 204 baris
Kod Muat turun fail
1"use strict";
3const { compareVersions } = require("../../shared/version");
5class UpdateService {
6 constructor(options) {
7 this.appVersion = options.appVersion;
8 this.buildId = options.buildId || null;
9 this.platform = options.platform;
10 this.arch = options.arch;
11 this.configStore = options.configStore;
12 this.providerFactory = options.providerFactory;
13 this.logger = options.logger;
14 this.lastCheck = null;
15 this.releases = [];
16 this.selectedReleaseId = null;
17 this.lastVerifiedDownload = null;
18 this.installerLaunchAuthorized = false;
19 this.operation = null;
20 }
22 getState() {
23 const release = this.lastCheck?.release || null;
24 const releaseRelation = release ? this._relation(release) : null;
25 return {
26 status: this.operation || "idle",
27 checked: Boolean(this.lastCheck),
28 currentVersion: this.appVersion,
29 currentBuildId: this.buildId,
30 updateAvailable: Boolean(release && releaseRelation !== "sameVersionCurrentRelease"),
31 release,
32 releases: this.releases,
33 selectedReleaseId: this.selectedReleaseId,
34 releaseRelation,
35 downloaded: this.lastVerifiedDownload ? {
36 releaseId: this.lastVerifiedDownload.releaseId,
37 filename: this.lastVerifiedDownload.filename,
38 size: this.lastVerifiedDownload.size,
39 sha512: this.lastVerifiedDownload.sha512,
40 signatureVerified: this.lastVerifiedDownload.signatureVerified === true,
41 primaryFingerprint: this.lastVerifiedDownload.primaryFingerprint,
42 signingFingerprints: this.lastVerifiedDownload.signingFingerprints
43 } : null
44 };
45 }
47 async check() {
48 if (this.operation) throw new Error("Another update operation is already running.");
49 this.operation = "checking";
50 try {
51 const config = this.configStore.get();
52 const provider = this.providerFactory(config.updates);
53 const catalog = await provider.listReleases({
54 currentVersion: this.appVersion,
55 platform: this.platform,
56 arch: this.arch,
57 channel: config.updates.channel
58 });
59 this.releases = catalog.releases;
60 const preferred = this.releases.find((release) => compareVersions(release.version, this.appVersion) > 0)
61 || this.releases[0]
62 || null;
63 this.selectedReleaseId = preferred?.id || null;
64 this.lastCheck = {
65 updateAvailable: Boolean(preferred),
66 currentVersion: this.appVersion,
67 release: preferred,
68 sourceBaseUrl: catalog.sourceBaseUrl
69 };
70 this.lastVerifiedDownload = null;
71 this.installerLaunchAuthorized = false;
72 this.logger?.info("Update check completed.", {
73 channel: config.updates.channel,
74 updateAvailable: this.lastCheck.updateAvailable,
75 version: this.lastCheck.release?.version || null,
76 sourceBaseUrl: this.lastCheck.sourceBaseUrl
77 });
78 } finally {
79 this.operation = null;
80 }
81 // Reported after the operation is cleared, so the result never claims "checking".
82 return this.getState();
83 }
85 select(releaseId) {
86 if (this.operation) throw new Error("Another update operation is already running.");
87 const release = this.releases.find((item) => item.id === String(releaseId || ""));
88 if (!release) throw new Error("Selected release is not available in the current catalog.");
89 this.selectedReleaseId = release.id;
90 this.lastCheck = {
91 ...(this.lastCheck || { currentVersion: this.appVersion }),
92 updateAvailable: true,
93 release
94 };
95 this.lastVerifiedDownload = null;
96 this.installerLaunchAuthorized = false;
97 return this.getState();
98 }
100 async download() {
101 if (this.operation) throw new Error("Another update operation is already running.");
102 if (!this.lastCheck?.release) throw new Error("No update release is selected.");
103 this.operation = "downloading";
104 this.lastVerifiedDownload = null;
105 this.installerLaunchAuthorized = false;
106 try {
107 const provider = this.providerFactory(this.configStore.get().updates);
108 this.lastVerifiedDownload = await provider.downloadAndVerify(this.lastCheck.release);
109 if (this.lastVerifiedDownload.signatureVerified !== true) {
110 this.lastVerifiedDownload = null;
111 throw new Error("The update did not pass mandatory OpenPGP verification.");
112 }
113 this.lastVerifiedDownload.releaseId = this.lastCheck.release.id;
114 this.installerLaunchAuthorized = false;
115 this.logger?.info("Update downloaded and verified with SHA-512 and OpenPGP.", {
116 filename: this.lastVerifiedDownload.filename,
117 size: this.lastVerifiedDownload.size,
118 reused: this.lastVerifiedDownload.reused,
119 primaryFingerprint: this.lastVerifiedDownload.primaryFingerprint,
120 signingFingerprints: this.lastVerifiedDownload.signingFingerprints
121 });
122 } finally {
123 this.operation = null;
124 }
125 return this.getState();
126 }
128 getVerifiedDownloadPath() {
129 return this.lastVerifiedDownload?.signatureVerified === true
130 ? this.lastVerifiedDownload.path
131 : null;
132 }
134 async prepareInstallerLaunch() {
135 if (this.operation) throw new Error("Another update operation is already running.");
136 const release = this.lastCheck?.release;
137 if (!release || !this.lastVerifiedDownload || this.lastVerifiedDownload.releaseId !== release.id) {
138 throw new Error("No verified update release is ready to install.");
139 }
140 this.operation = "verifying-signature";
141 this.installerLaunchAuthorized = false;
142 try {
143 const provider = this.providerFactory(this.configStore.get().updates);
144 const filePath = await provider.reverifyForInstall(this.lastVerifiedDownload, release);
145 this.installerLaunchAuthorized = true;
146 return filePath;
147 } catch (error) {
148 this.lastVerifiedDownload = null;
149 throw error;
150 } finally {
151 this.operation = null;
152 }
153 }
155 cleanupDownloads() {
156 const provider = this.providerFactory(this.configStore.get().updates);
157 const result = provider.cleanupDownloads?.() || { cleaned: false };
158 this.lastVerifiedDownload = null;
159 this.installerLaunchAuthorized = false;
160 return result;
161 }
163 markInstallerLaunched() {
164 const release = this.lastCheck?.release;
165 if (!release || !this.lastVerifiedDownload || this.lastVerifiedDownload.releaseId !== release.id
166 || this.lastVerifiedDownload.signatureVerified !== true || !this.installerLaunchAuthorized) {
167 throw new Error("No verified update release is ready to install.");
168 }
169 this.configStore.recordLaunchedRelease({
170 id: release.id,
171 version: release.version,
172 publishedAt: release.publishedAt,
173 sha512: release.sha512
174 });
175 this.installerLaunchAuthorized = false;
176 return this.getState();
177 }
179 _relation(release) {
180 const comparison = compareVersions(release.version, this.appVersion);
181 if (comparison > 0) return "newerVersion";
182 if (comparison < 0) return "olderVersion";
183 if (this.buildId && release.buildId === this.buildId) return "sameVersionCurrentRelease";
185 const lastLaunched = this.configStore.get().updates.lastLaunchedRelease;
186 if (lastLaunched && compareVersions(lastLaunched.version, release.version) === 0) {
187 if (lastLaunched.id === release.id || lastLaunched.sha512 === release.sha512) {
188 return "sameVersionCurrentRelease";
189 }
190 const releaseTime = Date.parse(release.publishedAt);
191 const launchedTime = Date.parse(lastLaunched.publishedAt);
192 if (Number.isFinite(releaseTime) && Number.isFinite(launchedTime)) {
193 return releaseTime > launchedTime ? "sameVersionNewerRelease" : "sameVersionOlderRelease";
194 }
195 }
197 const newestSameVersion = this.releases.find((item) => compareVersions(item.version, this.appVersion) === 0);
198 return newestSameVersion?.id === release.id
199 ? "sameVersionNewerRelease"
200 : "sameVersionOlderRelease";
201 }
204module.exports = { UpdateService };
SHA-256bd86b28272c1e13cf989401c767c9126efe689f556a4b7b098f545b34b7dd825

CH-J Proprietary Software License 1.14 · Kod sumber disediakan di bawah CH-J Proprietary Software License 1.14. Ketersediaannya tidak mengubah terma lesen atau memberikan hak tambahan.

SHA-256 arkib476ed8aef0f1e96a5acdf223127b6692acdf4492eeaf5d0816b6e2a634fbbda6
Fail