BangSo/apps/mobile/lib/computer.ts

85 lines
2.7 KiB
TypeScript
Raw Permalink Normal View History

2026-09-01 16:51:19 +00:00
import type { ComputerMode, ComputerStatus as ContractComputerStatus } from "@rakazo/contracts";
export const COMPUTER_HEARTBEAT_MS = 60_000;
export const SCREEN_URL_OPEN_ATTEMPTS = 5;
export const SCREEN_URL_RETRY_DELAY_MS = 400;
export type ComputerStatus = ContractComputerStatus;
function isLocalHostname(hostname: string) {
return (
hostname === "127.0.0.1" ||
hostname === "localhost" ||
hostname === "[::1]" ||
hostname === "::1"
);
}
export async function readScreenUrl(
request: () => Promise<{ url: string | null }>,
options: {
attempts?: number;
delayMs?: number;
sleep?: (ms: number) => Promise<void>;
} = {},
): Promise<string | null> {
const attempts = Math.max(1, options.attempts ?? 1);
const delayMs = options.delayMs ?? SCREEN_URL_RETRY_DELAY_MS;
const sleep =
options.sleep ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));
let lastError: unknown;
for (let attempt = 1; attempt <= attempts; attempt += 1) {
try {
const screen = await request();
if (screen.url) return screen.url;
lastError = undefined;
} catch (error) {
lastError = error;
}
if (attempt < attempts) await sleep(delayMs);
}
if (lastError) throw lastError;
return null;
}
/** Point a loopback noVNC URL at the same host the app uses for the API. */
export function embeddableScreenUrl(url: string | null, apiBase: string): string | null {
if (!url) return null;
try {
const parsed = new URL(url);
const api = new URL(apiBase);
if (isLocalHostname(parsed.hostname) && !isLocalHostname(api.hostname)) {
parsed.hostname = api.hostname;
}
return parsed.toString();
} catch {
return url;
}
}
export function previewPlaceholder(
state: string | undefined,
booting: boolean,
name: string,
mode?: ComputerMode,
): string {
if (state === "booting" || booting) return "Booting live desktop…";
if (state === "running") return computerLabel(mode, name);
if (state === "suspended") return "Computer is asleep — take control to wake it";
if (state === "error") return "Computer failed to boot";
return "Computer is stopped";
}
export function controlLabel(computer: ComputerStatus | null, name: string, botId?: string) {
if (computer?.busyBotName) return `${computer.busyBotName} is using it`;
if (computer?.controlHolder === "user" && computer.controlBotId === botId) {
return "You have control";
}
if (computer?.state === "suspended") return "Asleep";
return computerLabel(computer?.mode, name);
}
export function computerLabel(mode: ComputerMode | undefined, name: string) {
return mode === "dedicated" ? `${name}s computer` : "Team Computer";
}