123 lines
3.1 KiB
TypeScript
123 lines
3.1 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
|
|
import type { SystemPresence } from "../infra/system-presence.js";
|
|
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
|
|
import { GatewayClient } from "./client.js";
|
|
|
|
export type GatewayProbeAuth = {
|
|
token?: string;
|
|
password?: string;
|
|
};
|
|
|
|
export type GatewayProbeClose = {
|
|
code: number;
|
|
reason: string;
|
|
hint?: string;
|
|
};
|
|
|
|
export type GatewayProbeResult = {
|
|
ok: boolean;
|
|
url: string;
|
|
connectLatencyMs: number | null;
|
|
error: string | null;
|
|
close: GatewayProbeClose | null;
|
|
health: unknown;
|
|
status: unknown;
|
|
presence: SystemPresence[] | null;
|
|
configSnapshot: unknown;
|
|
};
|
|
|
|
function formatError(err: unknown): string {
|
|
if (err instanceof Error) return err.message;
|
|
return String(err);
|
|
}
|
|
|
|
export async function probeGateway(opts: {
|
|
url: string;
|
|
auth?: GatewayProbeAuth;
|
|
timeoutMs: number;
|
|
}): Promise<GatewayProbeResult> {
|
|
const startedAt = Date.now();
|
|
const instanceId = randomUUID();
|
|
let connectLatencyMs: number | null = null;
|
|
let connectError: string | null = null;
|
|
let close: GatewayProbeClose | null = null;
|
|
|
|
return await new Promise<GatewayProbeResult>((resolve) => {
|
|
let settled = false;
|
|
const settle = (result: Omit<GatewayProbeResult, "url">) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
clearTimeout(timer);
|
|
client.stop();
|
|
resolve({ url: opts.url, ...result });
|
|
};
|
|
|
|
const client = new GatewayClient({
|
|
url: opts.url,
|
|
token: opts.auth?.token,
|
|
password: opts.auth?.password,
|
|
clientName: GATEWAY_CLIENT_NAMES.CLI,
|
|
clientVersion: "dev",
|
|
mode: GATEWAY_CLIENT_MODES.PROBE,
|
|
instanceId,
|
|
onConnectError: (err) => {
|
|
connectError = formatError(err);
|
|
},
|
|
onClose: (code, reason) => {
|
|
close = { code, reason };
|
|
},
|
|
onHelloOk: async () => {
|
|
connectLatencyMs = Date.now() - startedAt;
|
|
try {
|
|
const [health, status, presence, configSnapshot] = await Promise.all([
|
|
client.request("health"),
|
|
client.request("status"),
|
|
client.request("system-presence"),
|
|
client.request("config.get", {}),
|
|
]);
|
|
settle({
|
|
ok: true,
|
|
connectLatencyMs,
|
|
error: null,
|
|
close,
|
|
health,
|
|
status,
|
|
presence: Array.isArray(presence) ? (presence as SystemPresence[]) : null,
|
|
configSnapshot,
|
|
});
|
|
} catch (err) {
|
|
settle({
|
|
ok: false,
|
|
connectLatencyMs,
|
|
error: formatError(err),
|
|
close,
|
|
health: null,
|
|
status: null,
|
|
presence: null,
|
|
configSnapshot: null,
|
|
});
|
|
}
|
|
},
|
|
});
|
|
|
|
const timer = setTimeout(
|
|
() => {
|
|
settle({
|
|
ok: false,
|
|
connectLatencyMs,
|
|
error: connectError ? `connect failed: ${connectError}` : "timeout",
|
|
close,
|
|
health: null,
|
|
status: null,
|
|
presence: null,
|
|
configSnapshot: null,
|
|
});
|
|
},
|
|
Math.max(250, opts.timeoutMs),
|
|
);
|
|
|
|
client.start();
|
|
});
|
|
}
|