import { wakeRender } from './relay-wake'; import { renderWispUpgrade } from './wisp'; import { assertSameOrigin, ProxyError, validateTarget } from "./policy"; import { authenticated, unlock } from "./session"; import { relayHttp, relayWebSocket } from "./relay"; import { renderRelayHttp, useRenderRelay, type RenderConfig } from "./render"; import { jimmyRelayHttp, useJimmyRelay, type JimmyConfig } from "./jimmy"; export default { async fetch(request, env, ctx): Promise { const path = new URL(request.url).pathname; // Public test fixture is opt-in on loopback only; production's allowed hosts do not change. const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(new URL(request.url).hostname); const allowedHosts = loopback ? env.ALLOWED_HOSTS + ",httpbingo.org" : env.ALLOWED_HOSTS; try { if (path === "/api/relay/wake") { assertSameOrigin(request); if(!await authenticated(request,env.PROXY_PASSWORD))throw new ProxyError(401,"AUTH_REQUIRED","Unlock the relay first."); if(request.method!=="POST")return new Response("Use POST",{status:405}); return wakeRender(env as Env & RenderConfig); } if (path === "/wisp/") { assertSameOrigin(request); if(!await authenticated(request,env.PROXY_PASSWORD))throw new ProxyError(401,"AUTH_REQUIRED","Unlock the relay first."); return renderWispUpgrade(request,env as Env & RenderConfig); } if (path === "/api/target") { assertSameOrigin(request); if(!await authenticated(request,env.PROXY_PASSWORD))throw new ProxyError(401,"AUTH_REQUIRED","Unlock the relay first."); const target=validateTarget(new URL(request.url).searchParams.get("url")||"",allowedHosts); return Response.json({allowed:true,host:target.hostname},{headers:{"cache-control":"no-store"}}); } if (path === "/api/session") { assertSameOrigin(request); if (request.method === "POST") return await unlock(request, env.PROXY_PASSWORD); return Response.json({authenticated: await authenticated(request, env.PROXY_PASSWORD)}, { headers: {"cache-control": "no-store"}, }); } if (path === "/bare/" || path === "/bare/v3/") { assertSameOrigin(request); if (!await authenticated(request, env.PROXY_PASSWORD)) { return Response.json({code: "AUTH_REQUIRED", message: "Unlock your proxy from its launch page first."}, { status: 401, headers: {"cache-control": "no-store"}, }); } if (path === "/bare/") return Response.json({ versions: ["v3"], language: "Cloudflare Workers", project: {name: "ZeyadsStuffV2", description: "Private website relay", version: "2.3.0"}, }, {headers: {"cache-control": "no-store"}}); if (request.headers.get("upgrade")?.toLowerCase() === "websocket") return relayWebSocket(request, allowedHosts, ctx); if (!["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"].includes(request.method)) { throw new ProxyError(405, "METHOD_NOT_ALLOWED", "Unsupported request method."); } const target = validateTarget(request.headers.get("x-bare-url") ?? "", allowedHosts); const renderConfig = env as Env & RenderConfig; const jimmyConfig = env as Env & JimmyConfig; const render = useRenderRelay(target, renderConfig); const jimmy = useJimmyRelay(target, jimmyConfig); let response: Response; let backend: "jimmy" | "render" | "local" | "cloudflare"; if (jimmy) { try { response = await jimmyRelayHttp(request, target, jimmyConfig); backend = "jimmy"; } catch (error) { // Retrying writes could duplicate effects if Jimmy received the request. // Only fail over read-only methods, and never hide client validation errors. const readOnly = request.method === "GET" || request.method === "HEAD"; if (!render || !readOnly || request.signal.aborted || (error instanceof ProxyError && error.status < 500)) throw error; console.warn(JSON.stringify({ event: "jimmy_relay_fallback", code: error instanceof ProxyError ? error.code : "UPSTREAM_ERROR", })); response = await renderRelayHttp(request, target, renderConfig); backend = "render"; } } else if (render) { response = await renderRelayHttp(request, target, renderConfig); backend = "render"; } else { response = await relayHttp(request, allowedHosts); const hostname = new URL(request.url).hostname; backend = ["localhost", "127.0.0.1", "[::1]"].includes(hostname) ? "local" : "cloudflare"; } // Diagnostic route only; tokens and upstream credentials never leave the Worker. // WebSocket upgrades continue using the Workers relay. response.headers.set("x-relay-backend", backend); return response; } if (path.startsWith("/bare/")) return new Response("Unknown relay route", {status: 404}); const assetRequest=path==="/embed"?new Request(new URL("/",request.url),request):request; const response = await env.ASSETS.fetch(assetRequest); const headers = new Headers(response.headers); headers.set("referrer-policy", "no-referrer"); headers.set("x-content-type-options", "nosniff"); if(path!=="/embed")headers.set("x-frame-options", "SAMEORIGIN"); else {headers.delete("x-frame-options");headers.set("cache-control","no-store");} if (path === "/sw.js") { headers.set("service-worker-allowed", "/"); headers.set("cache-control", "no-cache"); } return new Response(response.body, {status: response.status, headers}); } catch (error) { const known = error instanceof ProxyError; if(known)console.warn(JSON.stringify({event:"relay_rejected",code:error.code,status:error.status,message:error.message})); return Response.json({ code: known ? error.code : "UPSTREAM_ERROR", message: known ? error.message : "The upstream connection failed. Retry in a moment.", }, {status: known ? error.status : 502, headers: {"cache-control": "no-store"}}); } }, } satisfies ExportedHandler;