| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- const https = require('node:https');
- const LATEST_RELEASE_API = 'https://api.github.com/repos/FB208/OpenBidKit_Yibiao/releases/latest';
- let autoUpdaterInstance = null;
- let downloadedUpdateVersion = '';
- let activeUpdateCheckPromise = null;
- function compareVersions(a, b) {
- const pa = String(a || '').replace(/^v/, '').split('.').map(Number);
- const pb = String(b || '').replace(/^v/, '').split('.').map(Number);
- for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
- const na = Number.isFinite(pa[i]) ? pa[i] : 0;
- const nb = Number.isFinite(pb[i]) ? pb[i] : 0;
- if (na > nb) return 1;
- if (na < nb) return -1;
- }
- return 0;
- }
- function fetchLatestRelease() {
- return new Promise((resolve, reject) => {
- const request = https.get(LATEST_RELEASE_API, { headers: { 'User-Agent': 'yibiao-client' } }, (response) => {
- let data = '';
- response.on('data', (chunk) => { data += chunk; });
- response.on('end', () => {
- if (response.statusCode < 200 || response.statusCode >= 300) {
- reject(new Error(`GitHub API 请求失败:${response.statusCode}`));
- return;
- }
- try {
- const release = JSON.parse(data);
- resolve({
- version: release.tag_name?.replace(/^v/, '') || '',
- body: release.body || '',
- });
- } catch {
- reject(new Error('解析 GitHub API 响应失败'));
- }
- });
- });
- request.on('error', (error) => reject(error));
- request.setTimeout(10000, () => {
- request.destroy();
- reject(new Error('请求超时'));
- });
- });
- }
- function formatErrorMessage(error) {
- return error instanceof Error ? error.message : String(error || '未知错误');
- }
- function setProgressBar(mainWindow, progress) {
- if (!mainWindow || mainWindow.isDestroyed()) {
- return;
- }
- mainWindow.setProgressBar(progress);
- }
- function getDisabledResult() {
- return { enabled: false, updateAvailable: false };
- }
- async function runUpdateCheck({ app, mainWindow, onProgress, onDownloaded, onError }) {
- const release = await fetchLatestRelease();
- if (!release.version || compareVersions(release.version, app.getVersion()) <= 0) {
- return { enabled: true, updateAvailable: false };
- }
- let downloadedVersion = release.version;
- let downloadedNotified = false;
- let errorNotified = false;
- const notifyError = (message) => {
- if (errorNotified) {
- return;
- }
- errorNotified = true;
- onError?.(message);
- };
- const handleProgress = (progress) => {
- const percent = Number(progress?.percent || 0);
- setProgressBar(mainWindow, Math.max(0, Math.min(1, percent / 100)));
- onProgress?.(percent);
- };
- const handleDownloaded = (info) => {
- downloadedVersion = info?.version || release.version;
- downloadedUpdateVersion = downloadedVersion;
- downloadedNotified = true;
- setProgressBar(mainWindow, -1);
- onDownloaded?.(downloadedVersion);
- };
- const handleError = (error) => {
- setProgressBar(mainWindow, -1);
- notifyError(formatErrorMessage(error));
- };
- autoUpdaterInstance.on('download-progress', handleProgress);
- autoUpdaterInstance.on('update-downloaded', handleDownloaded);
- autoUpdaterInstance.on('error', handleError);
- try {
- const result = await autoUpdaterInstance.checkForUpdates();
- if (!result) {
- throw new Error('未找到可下载的更新包');
- }
- await autoUpdaterInstance.downloadUpdate();
- downloadedUpdateVersion = downloadedVersion;
- setProgressBar(mainWindow, -1);
- if (!downloadedNotified) {
- onDownloaded?.(downloadedVersion);
- }
- return { enabled: true, updateAvailable: true, version: downloadedVersion, downloaded: true };
- } catch (error) {
- const message = formatErrorMessage(error);
- notifyError(message);
- return { enabled: true, updateAvailable: true, version: release.version, failed: true, message };
- } finally {
- autoUpdaterInstance.removeListener('download-progress', handleProgress);
- autoUpdaterInstance.removeListener('update-downloaded', handleDownloaded);
- autoUpdaterInstance.removeListener('error', handleError);
- setProgressBar(mainWindow, -1);
- }
- }
- async function checkAndDownloadUpdate(options = {}) {
- const { app } = options;
- if (!app?.isPackaged) {
- return getDisabledResult();
- }
- if (!autoUpdaterInstance) {
- return { enabled: true, updateAvailable: false, failed: true, message: '自动更新未初始化' };
- }
- if (downloadedUpdateVersion) {
- return { enabled: true, updateAvailable: true, version: downloadedUpdateVersion, downloaded: true };
- }
- if (activeUpdateCheckPromise) {
- return activeUpdateCheckPromise;
- }
- activeUpdateCheckPromise = runUpdateCheck(options)
- .catch((error) => {
- const message = formatErrorMessage(error);
- options.onError?.(message);
- return { enabled: true, updateAvailable: false, failed: true, message };
- })
- .finally(() => {
- activeUpdateCheckPromise = null;
- });
- return activeUpdateCheckPromise;
- }
- function triggerUpdateDownload(options) {
- return checkAndDownloadUpdate(options);
- }
- function quitAndInstall() {
- if (autoUpdaterInstance && downloadedUpdateVersion) {
- autoUpdaterInstance.quitAndInstall(false, true);
- }
- }
- function setupAutoUpdate({ app, mainWindow }) {
- if (!app.isPackaged) {
- return;
- }
- const { autoUpdater } = require('electron-updater');
- autoUpdaterInstance = autoUpdater;
- autoUpdater.autoDownload = false;
- autoUpdater.autoInstallOnAppQuit = false;
- autoUpdater.on('download-progress', (progress) => {
- const percent = Number(progress?.percent || 0);
- setProgressBar(mainWindow, Math.max(0, Math.min(1, percent / 100)));
- });
- autoUpdater.on('update-downloaded', (info) => {
- downloadedUpdateVersion = info?.version || downloadedUpdateVersion;
- setProgressBar(mainWindow, -1);
- });
- autoUpdater.on('error', (error) => {
- setProgressBar(mainWindow, -1);
- console.warn('自动更新检查失败', error);
- });
- }
- module.exports = {
- setupAutoUpdate,
- checkAndDownloadUpdate,
- triggerUpdateDownload,
- quitAndInstall,
- };
|