Spaces:
Running
Running
| /** | |
| * Cloudflare Worker: Universal Outbound Proxy | |
| * | |
| * Manual setup: | |
| * 1. Create a Cloudflare Worker. | |
| * 2. Paste this file and deploy it. | |
| * 3. Use the worker URL as CLOUDFLARE_PROXY_URL. | |
| * | |
| * Optional worker vars: | |
| * - PROXY_SHARED_SECRET | |
| * - ALLOWED_TARGETS | |
| * - ALLOW_PROXY_ALL | |
| */ | |
| function normalizeList(raw) { | |
| return String(raw || "") | |
| .split(",") | |
| .map((value) => value.trim().toLowerCase()) | |
| .filter(Boolean); | |
| } | |
| export default { | |
| async fetch(request, env) { | |
| const url = new URL(request.url); | |
| const queryTarget = url.searchParams.get("proxy_target"); | |
| const targetHost = request.headers.get("x-target-host") || queryTarget; | |
| const proxySecret = ( | |
| env.PROXY_SHARED_SECRET || | |
| env.CLOUDFLARE_PROXY_SECRET || | |
| "" | |
| ).trim(); | |
| if (proxySecret) { | |
| const providedSecret = request.headers.get("x-proxy-key") || url.searchParams.get("proxy_key") || ""; | |
| if (providedSecret !== proxySecret) { | |
| // Fallback: allow Telegram requests via path without secret if it looks like a bot API call. | |
| // This is safe because it only proxies to api.telegram.org. | |
| if (url.pathname.startsWith("/bot") && !targetHost) { | |
| // Allowed | |
| } else { | |
| return new Response("Unauthorized: Invalid proxy key", { status: 401 }); | |
| } | |
| } | |
| } | |
| const allowProxyAll = | |
| String(env.ALLOW_PROXY_ALL || "true").toLowerCase() === "true"; | |
| const allowedTargets = normalizeList( | |
| env.ALLOWED_TARGETS || "api.telegram.org,discord.com,discordapp.com,gateway.discord.gg,status.discord.com,web.whatsapp.com,graph.facebook.com,googleapis.com,google.com,googleusercontent.com,gstatic.com", | |
| ); | |
| const isAllowedHost = (hostname) => { | |
| const normalized = String(hostname || "") | |
| .trim() | |
| .toLowerCase(); | |
| if (!normalized) return false; | |
| if (allowProxyAll) return true; | |
| return allowedTargets.some( | |
| (domain) => normalized === domain || normalized.endsWith(`.${domain}`), | |
| ); | |
| }; | |
| let targetBase = ""; | |
| if (targetHost) { | |
| if (!isAllowedHost(targetHost)) { | |
| return new Response(`Forbidden: Host ${targetHost} is not allowed.`, { status: 403 }); | |
| } | |
| targetBase = `https://${targetHost}`; | |
| } else if (url.pathname.startsWith("/bot")) { | |
| targetBase = "https://api.telegram.org"; | |
| } else { | |
| return new Response("Invalid request: No target host provided.", { status: 400 }); | |
| } | |
| const cleanSearch = new URLSearchParams(url.search); | |
| cleanSearch.delete("proxy_target"); | |
| cleanSearch.delete("proxy_key"); | |
| const searchStr = cleanSearch.toString(); | |
| const targetUrl = targetBase + url.pathname + (searchStr ? `?${searchStr}` : ""); | |
| const headers = new Headers(request.headers); | |
| headers.delete("cf-connecting-ip"); | |
| headers.delete("cf-ray"); | |
| headers.delete("cf-visitor"); | |
| headers.delete("host"); | |
| headers.delete("x-real-ip"); | |
| headers.delete("x-target-host"); | |
| headers.delete("x-proxy-key"); | |
| const proxiedRequest = new Request(targetUrl, { | |
| method: request.method, | |
| headers, | |
| body: request.body, | |
| redirect: "follow", | |
| }); | |
| try { | |
| return await fetch(proxiedRequest); | |
| } catch (error) { | |
| return new Response(`Proxy Error: ${error.message}`, { status: 502 }); | |
| } | |
| }, | |
| }; | |