mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 12:55:16 -07:00
173 lines
6.3 KiB
JavaScript
173 lines
6.3 KiB
JavaScript
'use strict';
|
|
var __importDefault =
|
|
(this && this.__importDefault) ||
|
|
function (mod) {
|
|
return mod && mod.__esModule ? mod : { default: mod };
|
|
};
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
exports.createUpdateService = createUpdateService;
|
|
exports.createFileUpdateStateStore = createFileUpdateStateStore;
|
|
const node_fs_1 = __importDefault(require('node:fs'));
|
|
const node_path_1 = __importDefault(require('node:path'));
|
|
const release_assets_1 = require('./release-assets');
|
|
function getBestLatestVersion(currentVersion, appUpdate, release) {
|
|
const releaseVersion = (0, release_assets_1.parseReleaseVersion)(release);
|
|
const candidates = [appUpdate.version, releaseVersion].filter(
|
|
(value) => typeof value === 'string' && value.length > 0,
|
|
);
|
|
const latest = candidates.reduce(
|
|
(best, candidate) =>
|
|
(0, release_assets_1.compareSemverLike)(candidate, best) > 0 ? candidate : best,
|
|
currentVersion,
|
|
);
|
|
return {
|
|
available:
|
|
appUpdate.available || (0, release_assets_1.compareSemverLike)(latest, currentVersion) > 0,
|
|
version: latest,
|
|
};
|
|
}
|
|
function shouldSkipAutomaticCheck(config, state, now) {
|
|
if (!config.enabled) return true;
|
|
if (!state.lastAutomaticCheckAt) return false;
|
|
const intervalMs = Math.max(1, config.checkIntervalHours) * 60 * 60 * 1000;
|
|
return now - state.lastAutomaticCheckAt < intervalMs;
|
|
}
|
|
function summarizeError(error) {
|
|
const raw = error instanceof Error ? error.message : String(error);
|
|
const firstLine = raw
|
|
.split('\n')
|
|
.map((line) => line.trim())
|
|
.find((line) => line.length > 0);
|
|
return firstLine ?? 'unknown error';
|
|
}
|
|
function createUpdateService(deps) {
|
|
const inFlightBySource = new Map();
|
|
async function runCheck(request) {
|
|
const now = deps.now();
|
|
const config = deps.getConfig();
|
|
const channel = config.channel;
|
|
const state = await deps.readState();
|
|
const isAutomatic = request.source === 'automatic';
|
|
if (isAutomatic && !request.force && shouldSkipAutomaticCheck(config, state, now)) {
|
|
return { status: 'skipped' };
|
|
}
|
|
try {
|
|
const appUpdate = await deps.checkAppUpdate(channel).catch((error) => {
|
|
if (isAutomatic) {
|
|
deps.log(`App update metadata check failed: ${summarizeError(error)}`);
|
|
}
|
|
return {
|
|
available: false,
|
|
version: deps.getCurrentVersion(),
|
|
};
|
|
});
|
|
const shouldFetchReleaseMetadata =
|
|
deps.shouldFetchReleaseMetadata?.({ request, channel, appUpdate }) ?? true;
|
|
const release = shouldFetchReleaseMetadata
|
|
? await deps.fetchLatestStableRelease(channel).catch((error) => {
|
|
deps.log(`GitHub release update check failed: ${summarizeError(error)}`);
|
|
return null;
|
|
})
|
|
: null;
|
|
const currentVersion = deps.getCurrentVersion();
|
|
const latest = getBestLatestVersion(currentVersion, appUpdate, release);
|
|
if (isAutomatic) {
|
|
const nextState = {
|
|
...state,
|
|
lastAutomaticCheckAt: now,
|
|
};
|
|
if (latest.available && state.lastNotifiedVersion !== latest.version) {
|
|
await deps.notifyUpdateAvailable(latest.version);
|
|
nextState.lastNotifiedVersion = latest.version;
|
|
}
|
|
await deps.writeState(nextState);
|
|
}
|
|
if (!latest.available) {
|
|
if (!isAutomatic) {
|
|
await deps.showNoUpdateDialog(currentVersion);
|
|
}
|
|
return { status: 'up-to-date', version: currentVersion };
|
|
}
|
|
if (isAutomatic) {
|
|
return { status: 'update-available', version: latest.version };
|
|
}
|
|
const choice = await deps.showUpdateAvailableDialog(latest.version);
|
|
if (choice === 'close') {
|
|
return { status: 'update-available', version: latest.version };
|
|
}
|
|
const canInstallAppUpdate = appUpdate.available && appUpdate.canUpdate !== false;
|
|
let appUpdateApplied = false;
|
|
if (canInstallAppUpdate) {
|
|
await deps.downloadAppUpdate();
|
|
appUpdateApplied = true;
|
|
}
|
|
const launcherResult = await deps.updateLauncher(request.launcherPath, channel, release);
|
|
if (launcherResult.status === 'protected' && launcherResult.command) {
|
|
deps.log(`Launcher update requires manual command: ${launcherResult.command}`);
|
|
}
|
|
if (!appUpdateApplied) {
|
|
await deps.showManualUpdateRequiredDialog(latest.version);
|
|
return { status: 'update-available', version: latest.version };
|
|
}
|
|
const restartChoice = await deps.showRestartDialog();
|
|
if (restartChoice === 'restart') {
|
|
await deps.quitAndInstall();
|
|
}
|
|
return { status: 'updated', version: latest.version };
|
|
} catch (error) {
|
|
const message = summarizeError(error);
|
|
if (isAutomatic) {
|
|
deps.log(`Automatic update check failed: ${message}`);
|
|
} else {
|
|
await deps.showUpdateFailedDialog(message);
|
|
}
|
|
return { status: 'failed', error: message };
|
|
}
|
|
}
|
|
return {
|
|
checkForUpdates(request) {
|
|
const inFlight = inFlightBySource.get(request.source);
|
|
if (inFlight) return inFlight;
|
|
const nextInFlight = runCheck(request).finally(() => {
|
|
inFlightBySource.delete(request.source);
|
|
});
|
|
inFlightBySource.set(request.source, nextInFlight);
|
|
return nextInFlight;
|
|
},
|
|
startAutomaticChecks(options = {}) {
|
|
const setTimeoutFn = deps.setTimeout ?? setTimeout;
|
|
const setIntervalFn = deps.setInterval ?? setInterval;
|
|
const startupDelayMs = options.startupDelayMs ?? 15_000;
|
|
const pollIntervalMs = options.pollIntervalMs ?? 60 * 60 * 1000;
|
|
setTimeoutFn(() => {
|
|
void this.checkForUpdates({ source: 'automatic' });
|
|
}, startupDelayMs);
|
|
setIntervalFn(() => {
|
|
void this.checkForUpdates({ source: 'automatic' });
|
|
}, pollIntervalMs);
|
|
},
|
|
};
|
|
}
|
|
function createFileUpdateStateStore(statePath) {
|
|
return {
|
|
async readState() {
|
|
try {
|
|
return JSON.parse(await node_fs_1.default.promises.readFile(statePath, 'utf8'));
|
|
} catch {
|
|
return {};
|
|
}
|
|
},
|
|
async writeState(state) {
|
|
await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(statePath), {
|
|
recursive: true,
|
|
});
|
|
await node_fs_1.default.promises.writeFile(
|
|
statePath,
|
|
`${JSON.stringify(state, null, 2)}\n`,
|
|
'utf8',
|
|
);
|
|
},
|
|
};
|
|
}
|
|
//# sourceMappingURL=update-service.js.map
|