From b40434018a138c271b6d187d9c8bf75d0b6db416 Mon Sep 17 00:00:00 2001 From: liangxu Date: Thu, 4 Jun 2026 16:39:30 +0800 Subject: [PATCH] feat(desktop): enhance error handling and logging for session synchronization and window mode transitions --- .../src/main/adapters/sohuhao.ts | 3 +- apps/desktop-client/src/main/bootstrap.ts | 59 ++++++++++++++----- apps/desktop-client/src/renderer/App.vue | 15 ++++- .../renderer/composables/useDesktopSession.ts | 19 +++++- .../src/renderer/views/LoginView.vue | 8 ++- .../src/shared/publisher-errors.test.ts | 3 + 6 files changed, 85 insertions(+), 22 deletions(-) diff --git a/apps/desktop-client/src/main/adapters/sohuhao.ts b/apps/desktop-client/src/main/adapters/sohuhao.ts index 447d6d4..f0abce8 100644 --- a/apps/desktop-client/src/main/adapters/sohuhao.ts +++ b/apps/desktop-client/src/main/adapters/sohuhao.ts @@ -62,6 +62,7 @@ const SOHU_ORIGIN = 'https://mp.sohu.com' const SOHU_MANAGE_URL = `${SOHU_ORIGIN}/mpfe/v4/contentManagement/first/page?newsType=1` const SOHU_PUBLISH_URL = `${SOHU_ORIGIN}/mpbp/bp/news/v4/news/publish/v2` const SOHU_DRAFT_URL = `${SOHU_ORIGIN}/mpbp/bp/news/v4/news/draft/v2` +const SOHU_NOT_LOGGED_IN_MESSAGE = '搜狐号账号登录态已失效,请重新登录搜狐号后再重试。' function stringId(value: unknown): string { if (typeof value === 'number') { @@ -296,7 +297,7 @@ function failureResult( summary: '搜狐号登录态失效,无法执行发布。', error: { code: 'sohuhao_not_logged_in', - message, + message: SOHU_NOT_LOGGED_IN_MESSAGE, }, } } diff --git a/apps/desktop-client/src/main/bootstrap.ts b/apps/desktop-client/src/main/bootstrap.ts index d8a2444..18cb8bf 100644 --- a/apps/desktop-client/src/main/bootstrap.ts +++ b/apps/desktop-client/src/main/bootstrap.ts @@ -757,13 +757,39 @@ async function createSettingsWindow(): Promise { return window } -function flattenError(error: unknown): Error { +function ipcErrorMessage(error: unknown): string { if (error instanceof Error) { - const copy = new Error(error.message || 'ipc_handler_failed') + return error.message || 'ipc_handler_failed' + } + if (typeof error === 'string') { + return error || 'ipc_handler_failed' + } + if ( + error && + typeof error === 'object' && + 'message' in error && + typeof (error as { message?: unknown }).message === 'string' + ) { + return (error as { message: string }).message || 'ipc_handler_failed' + } + return 'ipc_handler_failed' +} + +function flattenError(channel: string, error: unknown): Error { + const message = ipcErrorMessage(error) + console.error('[desktop-main] ipc handler failed', { + channel, + name: error instanceof Error ? error.name : typeof error, + message, + stack: error instanceof Error ? error.stack : undefined, + }) + + if (error instanceof Error) { + const copy = new Error(message) copy.name = error.name return copy } - return new Error(typeof error === 'string' ? error : 'ipc_handler_failed') + return new Error(message) } function safeHandle( @@ -774,7 +800,7 @@ function safeHandle( try { return await handler(...(args as unknown as Args)) } catch (error) { - throw flattenError(error) + throw flattenError(channel, error) } }) } @@ -1065,7 +1091,7 @@ function registerBridgeHandlers(): void { return setDesktopAppSetting(key, value) }, ) - ipcMain.handle( + safeHandle( 'desktop:runtime-session-sync', (_event, session: DesktopRuntimeSessionSyncRequest | null) => { syncRuntimeSession(session) @@ -1105,16 +1131,19 @@ function registerBridgeHandlers(): void { await releaseRuntimeSession({ revoke: Boolean(revoke) }) return null }) - ipcMain.handle('desktop:set-window-mode', async (event, mode: WindowMode, request?: unknown) => { - if (mode === 'login' || mode === 'main') { - await queueWindowModeSwitch( - mode, - normalizeWindowModeRequest(request), - windowFromIpcEvent(event), - ) - } - return null - }) + safeHandle( + 'desktop:set-window-mode', + async (event: IpcMainInvokeEvent, mode: WindowMode, request?: unknown) => { + if (mode === 'login' || mode === 'main') { + await queueWindowModeSwitch( + mode, + normalizeWindowModeRequest(request), + windowFromIpcEvent(event), + ) + } + return null + }, + ) } const hasSingleInstanceLock = initSingleInstance((argv) => { diff --git a/apps/desktop-client/src/renderer/App.vue b/apps/desktop-client/src/renderer/App.vue index 669a747..984f02b 100644 --- a/apps/desktop-client/src/renderer/App.vue +++ b/apps/desktop-client/src/renderer/App.vue @@ -30,7 +30,14 @@ function getModeBridge(): WindowModeBridge | undefined { } function syncWindowMode(authenticated: boolean): void { - void getModeBridge()?.setWindowMode?.(authenticated ? 'main' : 'login', { source: 'auth-state' }) + void getModeBridge() + ?.setWindowMode?.(authenticated ? 'main' : 'login', { source: 'auth-state' }) + .catch((err) => { + console.warn('[desktop-app] window mode sync failed', { + authenticated, + message: err instanceof Error ? err.message : String(err), + }) + }) } const rendererWindowMode = resolveRendererWindowMode() @@ -62,7 +69,11 @@ if (rendererWindowMode === 'main') { onMounted(() => { if (rendererWindowMode !== 'login') { - void syncRuntimeSession() + void syncRuntimeSession().catch((err) => { + console.warn('[desktop-app] runtime session sync failed', { + message: err instanceof Error ? err.message : String(err), + }) + }) } if (usesAutomaticWindowMode) { syncWindowMode(isAuthenticated.value) diff --git a/apps/desktop-client/src/renderer/composables/useDesktopSession.ts b/apps/desktop-client/src/renderer/composables/useDesktopSession.ts index ae34ffb..00a3f76 100644 --- a/apps/desktop-client/src/renderer/composables/useDesktopSession.ts +++ b/apps/desktop-client/src/renderer/composables/useDesktopSession.ts @@ -55,6 +55,13 @@ let logoutPromise: Promise | null = null let renewPromise: Promise | null = null let tokenRenewTimer: ReturnType | null = null +function logRuntimeSessionSyncFailure(reason: string, err: unknown): void { + console.warn('[desktop-session] runtime session sync failed', { + reason, + message: err instanceof Error ? err.message : String(err), + }) +} + function readStoredSession(): DesktopSession | null { if (typeof window === 'undefined') { return null @@ -117,7 +124,9 @@ function persistSession(next: DesktopSession | null): void { window.localStorage.setItem(sessionStorageKey, JSON.stringify(next)) } - void syncRuntimeSessionToMain(next) + void syncRuntimeSessionToMain(next).catch((err) => { + logRuntimeSessionSyncFailure('persist-session', err) + }) } function readStoredApiBaseURL(): string { @@ -141,7 +150,9 @@ function persistApiBaseURL(value: string): void { window.localStorage.setItem(apiBaseURLStorageKey, normalized) } - void syncRuntimeSessionToMain() + void syncRuntimeSessionToMain().catch((err) => { + logRuntimeSessionSyncFailure('api-base-url-change', err) + }) } function createPublicClient() { @@ -753,7 +764,9 @@ if (typeof window !== 'undefined') { window.addEventListener('storage', (event) => { if (event.key === apiBaseURLStorageKey) { apiBaseURL.value = normalizeDesktopApiBaseURL(event.newValue, normalizedDefaultApiBaseURL) - void syncRuntimeSessionToMain() + void syncRuntimeSessionToMain().catch((err) => { + logRuntimeSessionSyncFailure('storage-api-base-url-change', err) + }) } }) void syncStoredDesktopClientDeviceInfo() diff --git a/apps/desktop-client/src/renderer/views/LoginView.vue b/apps/desktop-client/src/renderer/views/LoginView.vue index a9657cc..2f0b485 100644 --- a/apps/desktop-client/src/renderer/views/LoginView.vue +++ b/apps/desktop-client/src/renderer/views/LoginView.vue @@ -62,7 +62,13 @@ async function submitLogin() { password: form.password, }) await persistCredentialsPreference() - await window.desktopBridge?.app?.setWindowMode?.('main', { source: 'login' }) + try { + await window.desktopBridge?.app?.setWindowMode?.('main', { source: 'login' }) + } catch (err) { + console.warn('[login-view] switch to main window failed', { + message: err instanceof Error ? err.message : String(err), + }) + } } async function hydrateSavedCredentials() { diff --git a/apps/desktop-client/src/shared/publisher-errors.test.ts b/apps/desktop-client/src/shared/publisher-errors.test.ts index b272ff1..130d211 100644 --- a/apps/desktop-client/src/shared/publisher-errors.test.ts +++ b/apps/desktop-client/src/shared/publisher-errors.test.ts @@ -47,6 +47,9 @@ describe('publisher error normalization', () => { expect(normalizePublisherErrorMessage('qiehao_cookie_missing')).toBe( '企鹅号账号登录态已失效,请重新登录该平台后再重试。', ) + expect(normalizePublisherErrorMessage('sohuhao_not_logged_in')).toBe( + '搜狐号账号登录态已失效,请重新登录该平台后再重试。', + ) }) it('uses a generic Chinese message for unknown publisher login-state failures', () => {