|
|
|
|
@@ -42,6 +42,9 @@ class SelfUpdater extends EventEmitter {
|
|
|
|
|
updateUrl: options.updateUrl || DEFAULTS.UPDATE_URL,
|
|
|
|
|
mirrorUrl: options.mirrorUrl || DEFAULTS.MIRROR_URL,
|
|
|
|
|
updatesDir: options.updatesDir || DEFAULTS.UPDATES_DIR,
|
|
|
|
|
// hostUpdatesDir is the HOST path that maps to updatesDir inside the container.
|
|
|
|
|
// Used when writing trigger.json so the host-side script can find staging files.
|
|
|
|
|
hostUpdatesDir: options.hostUpdatesDir || (isWindows ? options.updatesDir || DEFAULTS.UPDATES_DIR : '/opt/dashcaddy/updates'),
|
|
|
|
|
apiSourceDir: options.apiSourceDir || DEFAULTS.API_SOURCE_DIR,
|
|
|
|
|
frontendDir: options.frontendDir || DEFAULTS.FRONTEND_DIR,
|
|
|
|
|
maxBackups: parseInt(options.maxBackups || DEFAULTS.MAX_BACKUPS, 10),
|
|
|
|
|
@@ -102,12 +105,14 @@ class SelfUpdater extends EventEmitter {
|
|
|
|
|
this.status = 'checking';
|
|
|
|
|
try {
|
|
|
|
|
let remote;
|
|
|
|
|
let sourceUrl = this.config.updateUrl;
|
|
|
|
|
try {
|
|
|
|
|
remote = await this._fetchJson(`${this.config.updateUrl}/version.json`);
|
|
|
|
|
} catch (primaryErr) {
|
|
|
|
|
console.warn('[SelfUpdater] Primary server failed:', primaryErr.message, '— trying mirror');
|
|
|
|
|
try {
|
|
|
|
|
remote = await this._fetchJson(`${this.config.mirrorUrl}/version.json`);
|
|
|
|
|
sourceUrl = this.config.mirrorUrl;
|
|
|
|
|
} catch (mirrorErr) {
|
|
|
|
|
this.status = 'idle';
|
|
|
|
|
this.lastCheckTime = Date.now();
|
|
|
|
|
@@ -120,7 +125,7 @@ class SelfUpdater extends EventEmitter {
|
|
|
|
|
const available = this._isNewer(local, remote);
|
|
|
|
|
|
|
|
|
|
this.lastCheckTime = Date.now();
|
|
|
|
|
this.lastCheckResult = { available, local, remote };
|
|
|
|
|
this.lastCheckResult = { available, local, remote, sourceUrl };
|
|
|
|
|
this.status = 'idle';
|
|
|
|
|
|
|
|
|
|
if (available) {
|
|
|
|
|
@@ -147,13 +152,21 @@ class SelfUpdater extends EventEmitter {
|
|
|
|
|
const stagingDir = path.join(this.config.updatesDir, 'staging');
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 1. Download
|
|
|
|
|
// 1. Download (try primary, fallback to mirror)
|
|
|
|
|
this.status = 'downloading';
|
|
|
|
|
this.emit('update-progress', { step: 'downloading', version: remoteInfo.version });
|
|
|
|
|
|
|
|
|
|
const tarballUrl = `${this.config.updateUrl}/${remoteInfo.tarball}`;
|
|
|
|
|
const tarballPath = path.join(this.config.updatesDir, remoteInfo.tarball);
|
|
|
|
|
await this._downloadFile(tarballUrl, tarballPath);
|
|
|
|
|
const primaryUrl = `${this.config.updateUrl}/${remoteInfo.tarball}`;
|
|
|
|
|
const mirrorUrl = `${this.config.mirrorUrl}/${remoteInfo.tarball}`;
|
|
|
|
|
try {
|
|
|
|
|
await this._downloadFile(primaryUrl, tarballPath);
|
|
|
|
|
} catch (dlErr) {
|
|
|
|
|
console.warn('[SelfUpdater] Primary download failed:', dlErr.message, '— trying mirror');
|
|
|
|
|
// Ensure file is fully cleaned up before mirror attempt
|
|
|
|
|
try { fs.unlinkSync(tarballPath); } catch (_) {}
|
|
|
|
|
await this._downloadFile(mirrorUrl, tarballPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Verify SHA-256
|
|
|
|
|
const hash = await this._computeSha256(tarballPath);
|
|
|
|
|
@@ -184,12 +197,14 @@ class SelfUpdater extends EventEmitter {
|
|
|
|
|
this.status = 'waiting';
|
|
|
|
|
this.emit('update-progress', { step: 'triggering-rebuild', version: remoteInfo.version });
|
|
|
|
|
|
|
|
|
|
// Convert container path to host path for trigger.json
|
|
|
|
|
const hostApiSrc = apiSrc.replace(this.config.updatesDir, this.config.hostUpdatesDir);
|
|
|
|
|
const trigger = {
|
|
|
|
|
action: 'update',
|
|
|
|
|
version: remoteInfo.version,
|
|
|
|
|
commit: remoteInfo.commit,
|
|
|
|
|
fromVersion: local.version,
|
|
|
|
|
stagingDir: apiSrc,
|
|
|
|
|
stagingDir: hostApiSrc,
|
|
|
|
|
apiSourceDir: this.config.apiSourceDir,
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
@@ -285,11 +300,12 @@ class SelfUpdater extends EventEmitter {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const local = this.getLocalVersion();
|
|
|
|
|
const hostBackupDir = backupDir.replace(this.config.updatesDir, this.config.hostUpdatesDir);
|
|
|
|
|
const trigger = {
|
|
|
|
|
action: 'rollback',
|
|
|
|
|
version: version,
|
|
|
|
|
fromVersion: local.version,
|
|
|
|
|
stagingDir: backupDir,
|
|
|
|
|
stagingDir: hostBackupDir,
|
|
|
|
|
apiSourceDir: this.config.apiSourceDir,
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
};
|
|
|
|
|
@@ -508,6 +524,7 @@ const selfUpdater = new SelfUpdater({
|
|
|
|
|
updateUrl: process.env.DASHCADDY_UPDATE_URL,
|
|
|
|
|
mirrorUrl: process.env.DASHCADDY_MIRROR_URL,
|
|
|
|
|
updatesDir: process.env.DASHCADDY_UPDATES_DIR,
|
|
|
|
|
hostUpdatesDir: process.env.DASHCADDY_HOST_UPDATES_DIR,
|
|
|
|
|
apiSourceDir: process.env.DASHCADDY_API_SOURCE_DIR,
|
|
|
|
|
frontendDir: process.env.DASHCADDY_FRONTEND_DIR,
|
|
|
|
|
});
|
|
|
|
|
|