evOo-local/apps/web/src/screen-proxy.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

83 lines
2.2 KiB
TypeScript

import type { IncomingHttpHeaders } from "node:http";
import type { ScreenProxyTarget } from "@rakazo/core/node/screen-capability";
import {
isScreenProxyTarget,
SCREEN_RECHECK_MS,
SCREEN_TARGET_ENDPOINT,
} from "@rakazo/core/node/screen-capability";
const SENSITIVE_FORWARD_HEADERS = new Set([
"authorization",
"cookie",
"host",
"proxy-authenticate",
"proxy-authorization",
]);
/** Fail closed when the authoritative lifecycle check is unavailable. */
export async function resolveNovncTarget(
url: string | undefined,
secret: string,
api: string,
): Promise<ScreenProxyTarget | null> {
if (!url?.startsWith("/novnc/session/")) return null;
try {
const response = await fetch(new URL(SCREEN_TARGET_ENDPOINT, api), {
method: "POST",
redirect: "error",
signal: AbortSignal.timeout(2_000),
headers: { authorization: `Bearer ${secret}`, "content-type": "application/json" },
body: JSON.stringify({ path: url }),
});
if (!response.ok) return null;
const target: unknown = await response.json();
return isScreenProxyTarget(target) ? target : null;
} catch {
return null;
}
}
function isHttp2PseudoHeader(key: string) {
return key.startsWith(":");
}
export function safeProxyHeaders(headers: IncomingHttpHeaders) {
return Object.fromEntries(
Object.entries(headers).filter(([key, value]) => {
return (
value != null &&
!isHttp2PseudoHeader(key) &&
!SENSITIVE_FORWARD_HEADERS.has(key.toLowerCase())
);
}),
);
}
/** Recheck streams as well as new requests; no positive authorization cache. */
export function watchScreenAuthorization(check: () => Promise<boolean>, revoke: () => void) {
let stopped = false;
let timer: ReturnType<typeof setTimeout>;
const tick = async () => {
let allowed = false;
try {
allowed = Boolean(await check());
} catch {
/* Fail closed. */
}
if (stopped) return;
if (!allowed) {
stopped = true;
revoke();
return;
}
timer = setTimeout(tick, SCREEN_RECHECK_MS);
timer.unref?.();
};
timer = setTimeout(tick, SCREEN_RECHECK_MS);
timer.unref?.();
return () => {
stopped = true;
clearTimeout(timer);
};
}