perf: Health snapshot changes
This commit is contained in:
@@ -28,6 +28,7 @@ import {
|
|||||||
getDesktopUpdateState,
|
getDesktopUpdateState,
|
||||||
handleDesktopSettingsChanged,
|
handleDesktopSettingsChanged,
|
||||||
restartToApplyUpdate,
|
restartToApplyUpdate,
|
||||||
|
readDesktopUpdateServerHealth,
|
||||||
type DesktopUpdateServerContext
|
type DesktopUpdateServerContext
|
||||||
} from '../update/desktop-updater';
|
} from '../update/desktop-updater';
|
||||||
import { consumePendingDeepLink } from '../app/deep-links';
|
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-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<DesktopUpdateServerContext>) => {
|
ipcMain.handle('configure-auto-update-context', async (_event, context: Partial<DesktopUpdateServerContext>) => {
|
||||||
return await configureDesktopUpdaterContext(context);
|
return await configureDesktopUpdaterContext(context);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -50,6 +50,12 @@ export interface DesktopUpdateServerContext {
|
|||||||
serverVersionStatus: DesktopUpdateServerVersionStatus;
|
serverVersionStatus: DesktopUpdateServerVersionStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DesktopUpdateServerHealthSnapshot {
|
||||||
|
manifestUrl: string | null;
|
||||||
|
serverVersion: string | null;
|
||||||
|
serverVersionStatus: DesktopUpdateServerVersionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DesktopUpdateState {
|
export interface DesktopUpdateState {
|
||||||
autoUpdateMode: 'auto' | 'off' | 'version';
|
autoUpdateMode: 'auto' | 'off' | 'version';
|
||||||
availableVersions: string[];
|
availableVersions: string[];
|
||||||
@@ -127,6 +133,7 @@ export interface ElectronAPI {
|
|||||||
restartRequired: boolean;
|
restartRequired: boolean;
|
||||||
}>;
|
}>;
|
||||||
getAutoUpdateState: () => Promise<DesktopUpdateState>;
|
getAutoUpdateState: () => Promise<DesktopUpdateState>;
|
||||||
|
getAutoUpdateServerHealth: (serverUrl: string) => Promise<DesktopUpdateServerHealthSnapshot>;
|
||||||
configureAutoUpdateContext: (context: Partial<DesktopUpdateServerContext>) => Promise<DesktopUpdateState>;
|
configureAutoUpdateContext: (context: Partial<DesktopUpdateServerContext>) => Promise<DesktopUpdateState>;
|
||||||
checkForAppUpdates: () => Promise<DesktopUpdateState>;
|
checkForAppUpdates: () => Promise<DesktopUpdateState>;
|
||||||
restartToApplyUpdate: () => Promise<boolean>;
|
restartToApplyUpdate: () => Promise<boolean>;
|
||||||
@@ -207,6 +214,7 @@ const electronAPI: ElectronAPI = {
|
|||||||
consumePendingDeepLink: () => ipcRenderer.invoke('consume-pending-deep-link'),
|
consumePendingDeepLink: () => ipcRenderer.invoke('consume-pending-deep-link'),
|
||||||
getDesktopSettings: () => ipcRenderer.invoke('get-desktop-settings'),
|
getDesktopSettings: () => ipcRenderer.invoke('get-desktop-settings'),
|
||||||
getAutoUpdateState: () => ipcRenderer.invoke('get-auto-update-state'),
|
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),
|
configureAutoUpdateContext: (context) => ipcRenderer.invoke('configure-auto-update-context', context),
|
||||||
checkForAppUpdates: () => ipcRenderer.invoke('check-for-app-updates'),
|
checkForAppUpdates: () => ipcRenderer.invoke('check-for-app-updates'),
|
||||||
restartToApplyUpdate: () => ipcRenderer.invoke('restart-to-apply-update'),
|
restartToApplyUpdate: () => ipcRenderer.invoke('restart-to-apply-update'),
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ interface ReleaseManifestEntry {
|
|||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ServerHealthResponse {
|
||||||
|
releaseManifestUrl?: string;
|
||||||
|
serverVersion?: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface UpdateVersionInfo {
|
interface UpdateVersionInfo {
|
||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
@@ -53,6 +58,12 @@ export interface DesktopUpdateServerContext {
|
|||||||
serverVersionStatus: DesktopUpdateServerVersionStatus;
|
serverVersionStatus: DesktopUpdateServerVersionStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DesktopUpdateServerHealthSnapshot {
|
||||||
|
manifestUrl: string | null;
|
||||||
|
serverVersion: string | null;
|
||||||
|
serverVersionStatus: DesktopUpdateServerVersionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DesktopUpdateState {
|
export interface DesktopUpdateState {
|
||||||
autoUpdateMode: AutoUpdateMode;
|
autoUpdateMode: AutoUpdateMode;
|
||||||
availableVersions: string[];
|
availableVersions: string[];
|
||||||
@@ -78,6 +89,8 @@ export interface DesktopUpdateState {
|
|||||||
|
|
||||||
export const AUTO_UPDATE_STATE_CHANGED_CHANNEL = 'auto-update-state-changed';
|
export const AUTO_UPDATE_STATE_CHANGED_CHANNEL = 'auto-update-state-changed';
|
||||||
|
|
||||||
|
const SERVER_HEALTH_TIMEOUT_MS = 5_000;
|
||||||
|
|
||||||
let currentCheckPromise: Promise<void> | null = null;
|
let currentCheckPromise: Promise<void> | null = null;
|
||||||
let currentContext: DesktopUpdateServerContext = {
|
let currentContext: DesktopUpdateServerContext = {
|
||||||
manifestUrls: [],
|
manifestUrls: [],
|
||||||
@@ -388,6 +401,47 @@ async function loadReleaseManifest(manifestUrl: string): Promise<ReleaseManifest
|
|||||||
return parseReleaseManifest(payload);
|
return parseReleaseManifest(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createUnavailableServerHealthSnapshot(): DesktopUpdateServerHealthSnapshot {
|
||||||
|
return {
|
||||||
|
manifestUrl: null,
|
||||||
|
serverVersion: null,
|
||||||
|
serverVersionStatus: 'unavailable'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadServerHealth(serverUrl: string): Promise<DesktopUpdateServerHealthSnapshot> {
|
||||||
|
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 {
|
function formatManifestLoadErrors(errors: string[]): string {
|
||||||
if (errors.length === 0) {
|
if (errors.length === 0) {
|
||||||
return 'No valid release manifest could be loaded.';
|
return 'No valid release manifest could be loaded.';
|
||||||
@@ -724,6 +778,12 @@ export async function checkForDesktopUpdates(): Promise<DesktopUpdateState> {
|
|||||||
return desktopUpdateState;
|
return desktopUpdateState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function readDesktopUpdateServerHealth(
|
||||||
|
serverUrl: string
|
||||||
|
): Promise<DesktopUpdateServerHealthSnapshot> {
|
||||||
|
return await loadServerHealth(serverUrl);
|
||||||
|
}
|
||||||
|
|
||||||
export function restartToApplyUpdate(): boolean {
|
export function restartToApplyUpdate(): boolean {
|
||||||
if (!desktopUpdateState.restartRequired) {
|
if (!desktopUpdateState.restartRequired) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ export interface DesktopUpdateServerContext {
|
|||||||
serverVersionStatus: DesktopUpdateServerVersionStatus;
|
serverVersionStatus: DesktopUpdateServerVersionStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DesktopUpdateServerHealthSnapshot {
|
||||||
|
manifestUrl: string | null;
|
||||||
|
serverVersion: string | null;
|
||||||
|
serverVersionStatus: DesktopUpdateServerVersionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DesktopUpdateState {
|
export interface DesktopUpdateState {
|
||||||
autoUpdateMode: AutoUpdateMode;
|
autoUpdateMode: AutoUpdateMode;
|
||||||
availableVersions: string[];
|
availableVersions: string[];
|
||||||
@@ -127,6 +133,7 @@ export interface ElectronApi {
|
|||||||
consumePendingDeepLink: () => Promise<string | null>;
|
consumePendingDeepLink: () => Promise<string | null>;
|
||||||
getDesktopSettings: () => Promise<DesktopSettingsSnapshot>;
|
getDesktopSettings: () => Promise<DesktopSettingsSnapshot>;
|
||||||
getAutoUpdateState: () => Promise<DesktopUpdateState>;
|
getAutoUpdateState: () => Promise<DesktopUpdateState>;
|
||||||
|
getAutoUpdateServerHealth: (serverUrl: string) => Promise<DesktopUpdateServerHealthSnapshot>;
|
||||||
configureAutoUpdateContext: (context: Partial<DesktopUpdateServerContext>) => Promise<DesktopUpdateState>;
|
configureAutoUpdateContext: (context: Partial<DesktopUpdateServerContext>) => Promise<DesktopUpdateState>;
|
||||||
checkForAppUpdates: () => Promise<DesktopUpdateState>;
|
checkForAppUpdates: () => Promise<DesktopUpdateState>;
|
||||||
restartToApplyUpdate: () => Promise<boolean>;
|
restartToApplyUpdate: () => Promise<boolean>;
|
||||||
|
|||||||
@@ -10,17 +10,13 @@ import { type ServerEndpoint, ServerDirectoryFacade } from '../../domains/server
|
|||||||
import {
|
import {
|
||||||
type AutoUpdateMode,
|
type AutoUpdateMode,
|
||||||
type DesktopUpdateServerContext,
|
type DesktopUpdateServerContext,
|
||||||
|
type DesktopUpdateServerHealthSnapshot,
|
||||||
type DesktopUpdateServerVersionStatus,
|
type DesktopUpdateServerVersionStatus,
|
||||||
type DesktopUpdateState,
|
type DesktopUpdateState,
|
||||||
type ElectronApi
|
type ElectronApi
|
||||||
} from '../platform/electron/electron-api.models';
|
} from '../platform/electron/electron-api.models';
|
||||||
import { ElectronBridgeService } from '../platform/electron/electron-bridge.service';
|
import { ElectronBridgeService } from '../platform/electron/electron-bridge.service';
|
||||||
|
|
||||||
interface ServerHealthResponse {
|
|
||||||
releaseManifestUrl?: string;
|
|
||||||
serverVersion?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ServerHealthSnapshot {
|
interface ServerHealthSnapshot {
|
||||||
endpointId: string;
|
endpointId: string;
|
||||||
manifestUrl: string | null;
|
manifestUrl: string | null;
|
||||||
@@ -29,7 +25,6 @@ interface ServerHealthSnapshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SERVER_CONTEXT_REFRESH_INTERVAL_MS = 5 * 60_000;
|
const SERVER_CONTEXT_REFRESH_INTERVAL_MS = 5 * 60_000;
|
||||||
const SERVER_CONTEXT_TIMEOUT_MS = 5_000;
|
|
||||||
|
|
||||||
function createInitialState(): DesktopUpdateState {
|
function createInitialState(): DesktopUpdateState {
|
||||||
return {
|
return {
|
||||||
@@ -292,14 +287,9 @@ export class DesktopAppUpdateService {
|
|||||||
|
|
||||||
private async readServerHealth(endpoint: ServerEndpoint): Promise<ServerHealthSnapshot> {
|
private async readServerHealth(endpoint: ServerEndpoint): Promise<ServerHealthSnapshot> {
|
||||||
const sanitizedServerUrl = endpoint.url.replace(/\/+$/, '');
|
const sanitizedServerUrl = endpoint.url.replace(/\/+$/, '');
|
||||||
|
const api = this.getElectronApi();
|
||||||
|
|
||||||
try {
|
if (!api?.getAutoUpdateServerHealth) {
|
||||||
const response = await fetch(`${sanitizedServerUrl}/api/health`, {
|
|
||||||
method: 'GET',
|
|
||||||
signal: AbortSignal.timeout(SERVER_CONTEXT_TIMEOUT_MS)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
return {
|
return {
|
||||||
endpointId: endpoint.id,
|
endpointId: endpoint.id,
|
||||||
manifestUrl: null,
|
manifestUrl: null,
|
||||||
@@ -308,14 +298,12 @@ export class DesktopAppUpdateService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = await response.json() as ServerHealthResponse;
|
try {
|
||||||
const serverVersion = normalizeOptionalString(payload.serverVersion);
|
const payload = await api.getAutoUpdateServerHealth(sanitizedServerUrl);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
endpointId: endpoint.id,
|
endpointId: endpoint.id,
|
||||||
manifestUrl: normalizeOptionalHttpUrl(payload.releaseManifestUrl),
|
...this.normalizeHealthSnapshot(payload)
|
||||||
serverVersion,
|
|
||||||
serverVersionStatus: serverVersion ? 'reported' : 'missing'
|
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return {
|
return {
|
||||||
@@ -327,6 +315,22 @@ export class DesktopAppUpdateService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private normalizeHealthSnapshot(
|
||||||
|
snapshot: DesktopUpdateServerHealthSnapshot
|
||||||
|
): Omit<ServerHealthSnapshot, 'endpointId'> {
|
||||||
|
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<DesktopUpdateServerContext>): Promise<void> {
|
private async pushContext(context: Partial<DesktopUpdateServerContext>): Promise<void> {
|
||||||
const api = this.getElectronApi();
|
const api = this.getElectronApi();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user