File size: 3,349 Bytes
be8c7bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/**
 * 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 });
    }
  },
};