HuggingClaw / cloudflare-worker.js
somratpro's picture
feat: add query parameter support for proxy target/key and ensure unbuffered gateway logging
aea0c64
raw
history blame
3.35 kB
/**
* 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 });
}
},
};