import { ProxyError } from "./policy"; import { forwardBareHttp, isDiscordRelayHost } from "./render"; export type JimmyConfig = { JIMMY_RELAY_ENABLED?: string; JIMMY_RELAY_URL?: string; JIMMY_RELAY_SECRET?: string; }; // Jimmy is explicitly opt-in. A missing URL or secret leaves Render unchanged. export function jimmyRelayEndpoint(env: JimmyConfig): URL | null { if (env.JIMMY_RELAY_ENABLED !== "true" || !env.JIMMY_RELAY_URL || !env.JIMMY_RELAY_SECRET || env.JIMMY_RELAY_SECRET.length < 32) return null; let url: URL; try { url = new URL(env.JIMMY_RELAY_URL); } catch { throw new ProxyError(503, "JIMMY_CONFIG", "Invalid Jimmy relay URL."); } if (url.protocol !== "https:" || url.username || url.password || url.port || url.search || url.hash || (url.pathname !== "/" && url.pathname !== "")) { throw new ProxyError(503, "JIMMY_CONFIG", "Jimmy relay must use a plain HTTPS origin."); } return new URL("/bare/v3/", url); } export function useJimmyRelay(target: URL, env: JimmyConfig): boolean { return isDiscordRelayHost(target) && jimmyRelayEndpoint(env) !== null; } export async function jimmyRelayHttp( request: Request, target: URL, env: JimmyConfig, ): Promise { const endpoint = jimmyRelayEndpoint(env); if (!endpoint) throw new ProxyError(503, "JIMMY_CONFIG", "Jimmy relay is not configured."); // Shorter deadline lets safe GET/HEAD requests fall back to Render promptly. return forwardBareHttp(request, target, endpoint, env.JIMMY_RELAY_SECRET!, 8_000, "JIMMY_UNAVAILABLE"); }