From 109402cdd65a61f63afc0be6117d3af30886d96b Mon Sep 17 00:00:00 2001 From: Myx Date: Mon, 30 Mar 2026 00:28:45 +0200 Subject: [PATCH] perf: Health snapshot changes --- electron/ipc/system.ts | 5 ++ electron/preload.ts | 8 +++ electron/update/desktop-updater.ts | 60 +++++++++++++++++++ .../platform/electron/electron-api.models.ts | 7 +++ .../services/desktop-app-update.service.ts | 54 +++++++++-------- 5 files changed, 109 insertions(+), 25 deletions(-) diff --git a/electron/ipc/system.ts b/electron/ipc/system.ts index 9350c7f..329ddac 100644 --- a/electron/ipc/system.ts +++ b/electron/ipc/system.ts @@ -28,6 +28,7 @@ import { getDesktopUpdateState, handleDesktopSettingsChanged, restartToApplyUpdate, + readDesktopUpdateServerHealth, type DesktopUpdateServerContext } from '../update/desktop-updater'; import { consumePendingDeepLink } from '../app/deep-links'; @@ -317,6 +318,10 @@ export function setupSystemHandlers(): void { ipcMain.handle('get-auto-update-state', () => getDesktopUpdateState()); + ipcMain.handle('get-auto-update-server-health', async (_event, serverUrl: string) => { + return await readDesktopUpdateServerHealth(serverUrl); + }); + ipcMain.handle('configure-auto-update-context', async (_event, context: Partial) => { return await configureDesktopUpdaterContext(context); }); diff --git a/electron/preload.ts b/electron/preload.ts index 6204189..a25ae9f 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -50,6 +50,12 @@ export interface DesktopUpdateServerContext { serverVersionStatus: DesktopUpdateServerVersionStatus; } +export interface DesktopUpdateServerHealthSnapshot { + manifestUrl: string | null; + serverVersion: string | null; + serverVersionStatus: DesktopUpdateServerVersionStatus; +} + export interface DesktopUpdateState { autoUpdateMode: 'auto' | 'off' | 'version'; availableVersions: string[]; @@ -127,6 +133,7 @@ export interface ElectronAPI { restartRequired: boolean; }>; getAutoUpdateState: () => Promise; + getAutoUpdateServerHealth: (serverUrl: string) => Promise; configureAutoUpdateContext: (context: Partial) => Promise; checkForAppUpdates: () => Promise; restartToApplyUpdate: () => Promise; @@ -207,6 +214,7 @@ const electronAPI: ElectronAPI = { consumePendingDeepLink: () => ipcRenderer.invoke('consume-pending-deep-link'), getDesktopSettings: () => ipcRenderer.invoke('get-desktop-settings'), getAutoUpdateState: () => ipcRenderer.invoke('get-auto-update-state'), + getAutoUpdateServerHealth: (serverUrl) => ipcRenderer.invoke('get-auto-update-server-health', serverUrl), configureAutoUpdateContext: (context) => ipcRenderer.invoke('configure-auto-update-context', context), checkForAppUpdates: () => ipcRenderer.invoke('check-for-app-updates'), restartToApplyUpdate: () => ipcRenderer.invoke('restart-to-apply-update'), diff --git a/electron/update/desktop-updater.ts b/electron/update/desktop-updater.ts index 1ee9303..58eccfe 100644 --- a/electron/update/desktop-updater.ts +++ b/electron/update/desktop-updater.ts @@ -18,6 +18,11 @@ interface ReleaseManifestEntry { version: string; } +interface ServerHealthResponse { + releaseManifestUrl?: string; + serverVersion?: string; +} + interface UpdateVersionInfo { version: string; } @@ -53,6 +58,12 @@ export interface DesktopUpdateServerContext { serverVersionStatus: DesktopUpdateServerVersionStatus; } +export interface DesktopUpdateServerHealthSnapshot { + manifestUrl: string | null; + serverVersion: string | null; + serverVersionStatus: DesktopUpdateServerVersionStatus; +} + export interface DesktopUpdateState { autoUpdateMode: AutoUpdateMode; availableVersions: string[]; @@ -78,6 +89,8 @@ export interface DesktopUpdateState { export const AUTO_UPDATE_STATE_CHANGED_CHANNEL = 'auto-update-state-changed'; +const SERVER_HEALTH_TIMEOUT_MS = 5_000; + let currentCheckPromise: Promise | null = null; let currentContext: DesktopUpdateServerContext = { manifestUrls: [], @@ -388,6 +401,47 @@ async function loadReleaseManifest(manifestUrl: string): Promise { + const sanitizedServerUrl = sanitizeHttpUrl(serverUrl); + + if (!sanitizedServerUrl) { + return createUnavailableServerHealthSnapshot(); + } + + try { + const response = await net.fetch(`${sanitizedServerUrl}/api/health`, { + method: 'GET', + headers: { + accept: 'application/json' + }, + signal: AbortSignal.timeout(SERVER_HEALTH_TIMEOUT_MS) + }); + + if (!response.ok) { + return createUnavailableServerHealthSnapshot(); + } + + const payload = await response.json() as ServerHealthResponse; + const serverVersion = normalizeSemanticVersion(payload.serverVersion); + + return { + manifestUrl: sanitizeHttpUrl(payload.releaseManifestUrl), + serverVersion, + serverVersionStatus: serverVersion ? 'reported' : 'missing' + }; + } catch { + return createUnavailableServerHealthSnapshot(); + } +} + function formatManifestLoadErrors(errors: string[]): string { if (errors.length === 0) { return 'No valid release manifest could be loaded.'; @@ -724,6 +778,12 @@ export async function checkForDesktopUpdates(): Promise { return desktopUpdateState; } +export async function readDesktopUpdateServerHealth( + serverUrl: string +): Promise { + return await loadServerHealth(serverUrl); +} + export function restartToApplyUpdate(): boolean { if (!desktopUpdateState.restartRequired) { return false; diff --git a/toju-app/src/app/core/platform/electron/electron-api.models.ts b/toju-app/src/app/core/platform/electron/electron-api.models.ts index a328c03..9b677d4 100644 --- a/toju-app/src/app/core/platform/electron/electron-api.models.ts +++ b/toju-app/src/app/core/platform/electron/electron-api.models.ts @@ -57,6 +57,12 @@ export interface DesktopUpdateServerContext { serverVersionStatus: DesktopUpdateServerVersionStatus; } +export interface DesktopUpdateServerHealthSnapshot { + manifestUrl: string | null; + serverVersion: string | null; + serverVersionStatus: DesktopUpdateServerVersionStatus; +} + export interface DesktopUpdateState { autoUpdateMode: AutoUpdateMode; availableVersions: string[]; @@ -127,6 +133,7 @@ export interface ElectronApi { consumePendingDeepLink: () => Promise; getDesktopSettings: () => Promise; getAutoUpdateState: () => Promise; + getAutoUpdateServerHealth: (serverUrl: string) => Promise; configureAutoUpdateContext: (context: Partial) => Promise; checkForAppUpdates: () => Promise; restartToApplyUpdate: () => Promise; diff --git a/toju-app/src/app/core/services/desktop-app-update.service.ts b/toju-app/src/app/core/services/desktop-app-update.service.ts index 806e922..1f05791 100644 --- a/toju-app/src/app/core/services/desktop-app-update.service.ts +++ b/toju-app/src/app/core/services/desktop-app-update.service.ts @@ -10,17 +10,13 @@ import { type ServerEndpoint, ServerDirectoryFacade } from '../../domains/server import { type AutoUpdateMode, type DesktopUpdateServerContext, + type DesktopUpdateServerHealthSnapshot, type DesktopUpdateServerVersionStatus, type DesktopUpdateState, type ElectronApi } from '../platform/electron/electron-api.models'; import { ElectronBridgeService } from '../platform/electron/electron-bridge.service'; -interface ServerHealthResponse { - releaseManifestUrl?: string; - serverVersion?: string; -} - interface ServerHealthSnapshot { endpointId: string; manifestUrl: string | null; @@ -29,7 +25,6 @@ interface ServerHealthSnapshot { } const SERVER_CONTEXT_REFRESH_INTERVAL_MS = 5 * 60_000; -const SERVER_CONTEXT_TIMEOUT_MS = 5_000; function createInitialState(): DesktopUpdateState { return { @@ -292,30 +287,23 @@ export class DesktopAppUpdateService { private async readServerHealth(endpoint: ServerEndpoint): Promise { const sanitizedServerUrl = endpoint.url.replace(/\/+$/, ''); + const api = this.getElectronApi(); + + if (!api?.getAutoUpdateServerHealth) { + return { + endpointId: endpoint.id, + manifestUrl: null, + serverVersion: null, + serverVersionStatus: 'unavailable' + }; + } try { - const response = await fetch(`${sanitizedServerUrl}/api/health`, { - method: 'GET', - signal: AbortSignal.timeout(SERVER_CONTEXT_TIMEOUT_MS) - }); - - if (!response.ok) { - return { - endpointId: endpoint.id, - manifestUrl: null, - serverVersion: null, - serverVersionStatus: 'unavailable' - }; - } - - const payload = await response.json() as ServerHealthResponse; - const serverVersion = normalizeOptionalString(payload.serverVersion); + const payload = await api.getAutoUpdateServerHealth(sanitizedServerUrl); return { endpointId: endpoint.id, - manifestUrl: normalizeOptionalHttpUrl(payload.releaseManifestUrl), - serverVersion, - serverVersionStatus: serverVersion ? 'reported' : 'missing' + ...this.normalizeHealthSnapshot(payload) }; } catch { return { @@ -327,6 +315,22 @@ export class DesktopAppUpdateService { } } + private normalizeHealthSnapshot( + snapshot: DesktopUpdateServerHealthSnapshot + ): Omit { + const serverVersion = normalizeOptionalString(snapshot.serverVersion); + + return { + manifestUrl: normalizeOptionalHttpUrl(snapshot.manifestUrl), + serverVersion, + serverVersionStatus: serverVersion + ? snapshot.serverVersionStatus + : snapshot.serverVersionStatus === 'reported' + ? 'missing' + : snapshot.serverVersionStatus + }; + } + private async pushContext(context: Partial): Promise { const api = this.getElectronApi();