修复客户端更新诊断与错误展示
This commit is contained in:
@@ -14,6 +14,18 @@ import { setQuitting } from './app-state';
|
||||
/** Base CDN URL (without trailing channel path) */
|
||||
const OSS_BASE_URL = 'https://oss.intelli-spectrum.com';
|
||||
const WORKS_SQUARE_UPDATE_BASE_URL = 'https://square.nianxx.cn/api/app-updates';
|
||||
const STABLE_UPDATE_NOT_PUBLISHED_ERROR = '当前平台的正式更新包尚未发布,请稍后重试或从官网下载最新版';
|
||||
|
||||
export function normalizeUpdateError(error: unknown): string {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
const isMissingStableChannel = message.includes('Cannot find channel "latest.yml" update info')
|
||||
|| message.includes('Cannot find channel "latest-mac.yml" update info');
|
||||
const isMissingStableManifest = isMissingStableChannel
|
||||
&& message.includes('HttpError: 404')
|
||||
&& message.includes(WORKS_SQUARE_UPDATE_BASE_URL);
|
||||
|
||||
return isMissingStableManifest ? STABLE_UPDATE_NOT_PUBLISHED_ERROR : message;
|
||||
}
|
||||
|
||||
export interface UpdateStatus {
|
||||
status: 'idle' | 'checking' | 'available' | 'not-available' | 'downloading' | 'downloaded' | 'error';
|
||||
@@ -69,6 +81,8 @@ export class AppUpdater extends EventEmitter {
|
||||
private autoInstallTimer: NodeJS.Timeout | null = null;
|
||||
private autoInstallCountdown = 0;
|
||||
private readonly updateFeedError: string | null;
|
||||
private readonly activeUpdateChecks = new Set<symbol>();
|
||||
private readonly updaterEventErrorChecks = new Map<Error, Set<symbol>>();
|
||||
|
||||
/** Delay (in seconds) before auto-installing a downloaded update. */
|
||||
private static readonly AUTO_INSTALL_DELAY_SECONDS = 5;
|
||||
@@ -162,7 +176,10 @@ export class AppUpdater extends EventEmitter {
|
||||
});
|
||||
|
||||
autoUpdater.on('error', (error: Error) => {
|
||||
this.updateStatus({ status: 'error', error: error.message });
|
||||
if (this.activeUpdateChecks.size > 0) {
|
||||
this.updaterEventErrorChecks.set(error, new Set(this.activeUpdateChecks));
|
||||
}
|
||||
this.updateStatus({ status: 'error', error: normalizeUpdateError(error) });
|
||||
this.emit('error', error);
|
||||
});
|
||||
}
|
||||
@@ -198,6 +215,8 @@ export class AppUpdater extends EventEmitter {
|
||||
* final status so the UI never gets stuck in 'checking'.
|
||||
*/
|
||||
async checkForUpdates(): Promise<UpdateInfo | null> {
|
||||
const checkToken = Symbol('update-check');
|
||||
this.activeUpdateChecks.add(checkToken);
|
||||
try {
|
||||
this.updateStatus({ status: 'checking' });
|
||||
|
||||
@@ -230,8 +249,22 @@ export class AppUpdater extends EventEmitter {
|
||||
return result.updateInfo || null;
|
||||
} catch (error) {
|
||||
logger.error('[Updater] Check for updates failed:', error);
|
||||
this.updateStatus({ status: 'error', error: (error as Error).message || String(error) });
|
||||
const reportedChecks = error instanceof Error
|
||||
? this.updaterEventErrorChecks.get(error)
|
||||
: undefined;
|
||||
const alreadyReported = reportedChecks?.has(checkToken) ?? false;
|
||||
if (!alreadyReported) {
|
||||
this.updateStatus({ status: 'error', error: normalizeUpdateError(error) });
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
this.activeUpdateChecks.delete(checkToken);
|
||||
for (const [error, reportedChecks] of this.updaterEventErrorChecks) {
|
||||
reportedChecks.delete(checkToken);
|
||||
if (reportedChecks.size === 0) {
|
||||
this.updaterEventErrorChecks.delete(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user