forked from boos/BoosAPI
87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
type DesktopServerEnvInput = {
|
|
inheritedEnv?: NodeJS.ProcessEnv;
|
|
userDataDir: string;
|
|
logsDir: string;
|
|
port: number;
|
|
};
|
|
|
|
type WaitForServerReadyInput = {
|
|
url: string;
|
|
fetcher?: (input: string, init?: RequestInit) => Promise<{ ok: boolean }>;
|
|
timeoutMs?: number;
|
|
intervalMs?: number;
|
|
};
|
|
|
|
type ServerExitState = {
|
|
code: number | null;
|
|
signal: NodeJS.Signals | null;
|
|
};
|
|
|
|
type DesktopServerWorkingDirInput = {
|
|
appPath: string;
|
|
resourcesPath: string;
|
|
isPackaged: boolean;
|
|
};
|
|
|
|
const DEFAULT_DESKTOP_SERVER_PORT = 4000;
|
|
const DEFAULT_READY_TIMEOUT_MS = 30_000;
|
|
const DEFAULT_READY_INTERVAL_MS = 250;
|
|
|
|
function delay(ms: number) {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
export function buildDesktopServerEnv(input: DesktopServerEnvInput): NodeJS.ProcessEnv {
|
|
const host = (input.inheritedEnv?.HOST || '0.0.0.0').trim() || '0.0.0.0';
|
|
|
|
return {
|
|
...(input.inheritedEnv || {}),
|
|
HOST: host,
|
|
PORT: String(input.port),
|
|
DATA_DIR: input.userDataDir,
|
|
METAPI_DESKTOP: '1',
|
|
METAPI_LOG_DIR: input.logsDir,
|
|
};
|
|
}
|
|
|
|
export function createDesktopServerUrl(port: number): string {
|
|
return `http://127.0.0.1:${port}`;
|
|
}
|
|
|
|
export function createDesktopHealthUrl(port: number): string {
|
|
return `${createDesktopServerUrl(port)}/api/desktop/health`;
|
|
}
|
|
|
|
export function resolveDesktopServerPort(env?: NodeJS.ProcessEnv): number {
|
|
const forcedPort = Number.parseInt(env?.METAPI_DESKTOP_SERVER_PORT || '', 10);
|
|
if (Number.isFinite(forcedPort) && forcedPort > 0) return forcedPort;
|
|
return DEFAULT_DESKTOP_SERVER_PORT;
|
|
}
|
|
|
|
export function resolveDesktopServerWorkingDir(input: DesktopServerWorkingDirInput): string {
|
|
return input.isPackaged ? input.resourcesPath : input.appPath;
|
|
}
|
|
|
|
export async function waitForServerReady(input: WaitForServerReadyInput): Promise<void> {
|
|
const fetcher = input.fetcher || ((url: string, init?: RequestInit) => fetch(url, init));
|
|
const timeoutMs = input.timeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
|
|
const intervalMs = input.intervalMs ?? DEFAULT_READY_INTERVAL_MS;
|
|
const startedAt = Date.now();
|
|
|
|
while (Date.now() - startedAt <= timeoutMs) {
|
|
try {
|
|
const response = await fetcher(input.url, { method: 'GET' });
|
|
if (response.ok) return;
|
|
} catch {
|
|
// Retry until timeout.
|
|
}
|
|
await delay(intervalMs);
|
|
}
|
|
|
|
throw new Error('Timed out waiting for metapi desktop server');
|
|
}
|
|
|
|
export function isFatalServerExit(exitState: ServerExitState): boolean {
|
|
return exitState.code !== null && exitState.code !== 0 && !exitState.signal;
|
|
}
|