somratpro commited on
Commit
96b5930
Β·
1 Parent(s): b0d5852

refactor: standardize Cloudflare proxy implementation, clean up configuration, and update documentation

Browse files
.env.example CHANGED
@@ -42,7 +42,7 @@ N8N_LOG_LEVEL=error
42
  # Your Cloudflare Worker URL (e.g. h8n-proxy.somrat.workers.dev)
43
  CLOUDFLARE_PROXY_URL=
44
  # Comma-separated list of domains to proxy. Use "*" to proxy everything.
45
- OUTBOUND_PROXY_DOMAINS=api.telegram.org,discord.com,discordapp.com
46
  # BUILD-TIME VARIABLE (HF Spaces: add as Variable, not Secret)
47
  # -----------------------------------------------------------------------------
48
 
 
42
  # Your Cloudflare Worker URL (e.g. h8n-proxy.somrat.workers.dev)
43
  CLOUDFLARE_PROXY_URL=
44
  # Comma-separated list of domains to proxy. Use "*" to proxy everything.
45
+ CLOUDFLARE_PROXY_DOMAINS=api.telegram.org,discord.com,discordapp.com
46
  # BUILD-TIME VARIABLE (HF Spaces: add as Variable, not Secret)
47
  # -----------------------------------------------------------------------------
48
 
CHANGELOG.md CHANGED
@@ -2,28 +2,32 @@
2
 
3
  All notable changes to this project will be documented in this file.
4
 
5
- ## [1.0.0] - 2026-04-22
6
 
7
  ### πŸŽ‰ Initial Release
8
 
9
  #### Features
10
 
11
- - **Self-hosted n8n** β€” runs the latest n8n on HuggingFace Spaces Docker with zero external database requirements (uses SQLite)
12
- - **Persistent backup** β€” automatically backs up `/home/node/.n8n` (workflows, credentials, SQLite DB, encryption key) to a private HF Dataset via `huggingface_hub`
13
- - **Safe SQLite backup** β€” uses `sqlite3 .backup` for a consistent hot-copy of the live database
14
- - **Auto-restore on startup** β€” restores the full n8n state from the dataset before starting n8n
15
- - **Graceful shutdown** β€” runs a final backup pass on `SIGTERM` / `SIGINT` before exiting
16
- - **Health endpoint** β€” `/health` on port 7861 returns sync status and service info
17
- - **Proxy server** β€” lightweight Node.js reverse proxy forwards HTTP and WebSocket traffic from port 7861 to n8n on port 5678
18
- - **UptimeRobot integration** β€” `setup-uptimerobot.sh` creates an external keep-alive monitor for the `/health` endpoint
19
- - **Basic auth** β€” n8n basic auth enabled by default; set `N8N_BASIC_AUTH_USER` and `N8N_BASIC_AUTH_PASSWORD` to secure your instance
20
- - **Timezone support** β€” set `GENERIC_TIMEZONE` for schedule trigger accuracy
21
- - **Optional n8n version pinning** β€” pass `N8N_VERSION` as a HF Space Variable to pin a specific n8n release
22
 
23
  #### Architecture
24
 
25
- - `Dockerfile` β€” builds on `node:22-slim`, installs n8n and Python sync dependencies
26
- - `start.sh` β€” validates config, restores backup, starts sync loop, proxy, and n8n
27
- - `n8n-sync.py` β€” manages backup/restore using `huggingface_hub`
28
- - `health-server.js` β€” lightweight HTTP + WebSocket reverse proxy with `/health` and `/status` endpoints
29
- - `setup-uptimerobot.sh` β€” optional one-shot script to create an UptimeRobot monitor
 
 
 
 
 
 
2
 
3
  All notable changes to this project will be documented in this file.
4
 
5
+ ## [1.0.0] - 2026-04-24
6
 
7
  ### πŸŽ‰ Initial Release
8
 
9
  #### Features
10
 
11
+ - **Self-hosted n8n** β€” Runs the latest n8n on HuggingFace Spaces Docker using SQLite (no external DB required).
12
+ - **Persistent Backup** β€” Automatically syncs the entire n8n workspace (workflows, credentials, database) to a private HF Dataset.
13
+ - **Cloudflare Transparent Proxy** β€” Built-in fix to bypass platform network blocks for services like Telegram and Discord.
14
+ - **DNS-over-HTTPS (DoH)** β€” Automatic fallback resolution for domains blocked at the DNS level (e.g., WhatsApp, Telegram).
15
+ - **Premium Dashboard** β€” Beautiful web interface at `/` for real-time uptime monitoring and sync health tracking.
16
+ - **Built-in Keep-Alive** β€” Integrated UptimeRobot setup tool directly from the dashboard to prevent free HF Spaces from sleeping.
17
+ - **Native Security** β€” Optimized for n8n v2 native user management with hardened file permissions (`umask 0077`).
18
+ - **Safe Persistence** β€” Uses atomic SQLite backups to ensure data integrity during periodic syncs.
19
+ - **Auto-Restore** β€” Seamlessly pulls the latest state from your HF Dataset on every startup.
20
+ - **Graceful Shutdown** β€” Ensures a final backup pass is completed on `SIGTERM` / `SIGINT` before the container exits.
 
21
 
22
  #### Architecture
23
 
24
+ - `Dockerfile` β€” Optimized build on `node:22-slim` including all n8n and sync dependencies.
25
+ - `start.sh` β€” Orchestrates startup, validates environment, and manages service lifecycle.
26
+ - `health-server.js` β€” High-performance namespace proxy and dashboard server.
27
+ - `cloudflare-proxy.js` β€” Transparently intercepts and routes blocked traffic via Cloudflare Workers.
28
+ - `dns-fix.js` β€” Monkey-patches Node.js DNS for reliable DoH fallback.
29
+ - `n8n-sync.py` β€” Robust background sync engine using the `huggingface_hub` API.
30
+ - `start.sh` β€” Configures environment, restores backup, and launches background sync loop.
31
+
32
+ ---
33
+ *Made with ❀️ by [@somratpro](https://github.com/somratpro)*
CLOUDFLARE_PROXY_GUIDE.md DELETED
@@ -1,44 +0,0 @@
1
- # 🌐 Cloudflare Proxy Guide
2
-
3
- Hugging Face Spaces officially blocks outgoing connections to specific services like **Telegram**, **WhatsApp**, and **Discord** on the free tier.
4
-
5
- Hugging8n includes a built-in **Transparent Cloudflare Proxy** that allows you to bypass these restrictions using a single Cloudflare Worker.
6
-
7
- ---
8
-
9
- ## πŸš€ Setup in 2 Minutes
10
-
11
- ### Step 1: Deploy your Cloudflare Worker
12
-
13
- 1. Log in to [dash.cloudflare.com](https://dash.cloudflare.com/).
14
- 2. Go to **Workers & Pages** -> **Create Worker**.
15
- 3. Name it (e.g., `h8n-proxy`).
16
- 4. Paste the code from [cloudflare-worker.js](./cloudflare-worker.js) into the editor.
17
- 5. Click **Deploy**.
18
- 6. Copy your Worker URL (e.g., `h8n-proxy.yourname.workers.dev`).
19
-
20
- ### Step 2: Configure Hugging8n
21
-
22
- Go to your Space **Settings** -> **Variables** (or Secrets) and add:
23
-
24
- 1. **`CLOUDFLARE_PROXY_URL`** (Required):
25
- - Value: `h8n-proxy.yourname.workers.dev` (You can omit the `https://`).
26
-
27
- 2. **`CLOUDFLARE_PROXY_DOMAINS`** (Optional):
28
- - **Default**: Proxies Telegram and Discord only.
29
- - **Wildcard Mode**: Set this to `*` to proxy **every single request** n8n makes to the outside world.
30
-
31
- ### Step 3: Restart Space
32
-
33
- Hugging8n will now automatically intercept all requests to the blocked domains and route them through your worker. **You do not need to change any URLs inside your n8n workflows.**
34
-
35
- ---
36
-
37
- ## πŸ› οΈ How it Works
38
-
39
- 1. **DNS Fix**: Hugging8n uses a built-in DNS-over-HTTPS (DoH) resolver to get the real IPs of blocked domains, bypassing HF's sinkholed DNS.
40
- 2. **SNI Bypass**: The transparent proxy changes the "label" (SNI) of your traffic to match your Cloudflare domain. Hugging Face sees you connecting to Cloudflare (allowed) instead of Telegram/Discord (blocked).
41
- 3. **Universal Routing**: Using the `x-target-host` header, one single worker can handle multiple different services dynamically.
42
-
43
- ---
44
- *If you upgrade to a paid Space, these firewalls are removed and you no longer need this proxy.*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CODE_OF_CONDUCT.md CHANGED
@@ -7,12 +7,14 @@ We are committed to making participation in this project a harassment-free exper
7
  ## Our Standards
8
 
9
  **Positive behavior includes:**
 
10
  - Using welcoming and inclusive language
11
  - Being respectful of differing viewpoints
12
  - Gracefully accepting constructive criticism
13
  - Focusing on what is best for the community
14
 
15
  **Unacceptable behavior includes:**
 
16
  - Trolling, insulting, or derogatory comments
17
  - Public or private harassment
18
  - Publishing others' private information without permission
 
7
  ## Our Standards
8
 
9
  **Positive behavior includes:**
10
+
11
  - Using welcoming and inclusive language
12
  - Being respectful of differing viewpoints
13
  - Gracefully accepting constructive criticism
14
  - Focusing on what is best for the community
15
 
16
  **Unacceptable behavior includes:**
17
+
18
  - Trolling, insulting, or derogatory comments
19
  - Public or private harassment
20
  - Publishing others' private information without permission
README.md CHANGED
@@ -58,11 +58,12 @@ secrets:
58
  Navigate to your new Space's **Settings**, scroll down to **Variables and secrets**, and add:
59
 
60
  - `HF_TOKEN` – Your HuggingFace token with **Write** access (to enable automatic backup).
61
- - `CLOUDFLARE_PROXY_URL` – *(Optional but Recommended)* Your Cloudflare Worker URL to bypass platform blocks.
62
 
63
  ### Step 3: Deploy & Initialize
64
 
65
  The Space will build and start automatically. Once ready:
 
66
  1. Visit the Space URL.
67
  2. Click **Open n8n Editor**.
68
  3. Create your **Owner account** (this is your primary login).
@@ -70,6 +71,7 @@ The Space will build and start automatically. Once ready:
70
  ### Step 4: Monitor & Manage
71
 
72
  Use the built-in dashboard at the root URL (`/`) to track:
 
73
  - **Uptime:** Real-time uptime monitoring.
74
  - **Sync Status:** Visual indicators for your workflow backups.
75
  - **Keep-Alive:** Setup tool for external monitors.
@@ -79,16 +81,16 @@ Use the built-in dashboard at the root URL (`/`) to track:
79
  Hugging Face Free Tier blocks outgoing connections to some services (Telegram, Discord, etc.). Hugging8n includes a transparent proxy system to bypass this.
80
 
81
  1. Go to [Cloudflare Workers](https://dash.cloudflare.com/?to=/:account/workers-and-pages).
82
- 2. Create a new Worker and paste the code from [cloudflare-worker.js](./cloudflare-worker.js).
83
- 3. Deploy and copy the Worker URL.
84
- 4. Add this URL as the `CLOUDFLARE_PROXY_URL` secret in your Space settings.
85
-
86
- > [!TIP]
87
- > Check the [Cloudflare Proxy Guide](./CLOUDFLARE_PROXY_GUIDE.md) for detailed step-by-step instructions.
88
 
89
  ## πŸ’Ύ Persistent Backup
90
 
91
- Hugging8n automatically creates a private dataset named `hugging8n-backup` in your Hugging Face account.
92
 
93
  - **Restore:** On startup, it pulls the latest state from your dataset.
94
  - **Sync:** Periodically (every 3 minutes by default), it pushes updates to the dataset.
@@ -130,6 +132,7 @@ cp .env.example .env
130
  ```
131
 
132
  **With Docker:**
 
133
  ```bash
134
  docker build -t hugging8n .
135
  docker run -p 7861:7861 --env-file .env hugging8n
 
58
  Navigate to your new Space's **Settings**, scroll down to **Variables and secrets**, and add:
59
 
60
  - `HF_TOKEN` – Your HuggingFace token with **Write** access (to enable automatic backup).
61
+ - `CLOUDFLARE_PROXY_URL` – *(Optional but Recommended)* Your Cloudflare Worker URL to bypass platform blocks. check [Setup Guide](#-cloudflare-proxy-setup).
62
 
63
  ### Step 3: Deploy & Initialize
64
 
65
  The Space will build and start automatically. Once ready:
66
+
67
  1. Visit the Space URL.
68
  2. Click **Open n8n Editor**.
69
  3. Create your **Owner account** (this is your primary login).
 
71
  ### Step 4: Monitor & Manage
72
 
73
  Use the built-in dashboard at the root URL (`/`) to track:
74
+
75
  - **Uptime:** Real-time uptime monitoring.
76
  - **Sync Status:** Visual indicators for your workflow backups.
77
  - **Keep-Alive:** Setup tool for external monitors.
 
81
  Hugging Face Free Tier blocks outgoing connections to some services (Telegram, Discord, etc.). Hugging8n includes a transparent proxy system to bypass this.
82
 
83
  1. Go to [Cloudflare Workers](https://dash.cloudflare.com/?to=/:account/workers-and-pages).
84
+ 2. Create a new Worker using "Start with Hello World!" template
85
+ 3. choose worker name (e.g. h8n-proxy) and deploy.
86
+ 4. Click on "Edit Code" button, paste the code from [cloudflare-worker.js](./cloudflare-worker.js).
87
+ 5. Click on "Deploy" button.
88
+ 6. Copy the Worker URL (e.g., `https://h8n-proxy.yourname.workers.dev`).
89
+ 7. Add this URL as the `CLOUDFLARE_PROXY_URL` secret in your Hugging8n Space settings.
90
 
91
  ## πŸ’Ύ Persistent Backup
92
 
93
+ Hugging8n automatically creates a private dataset named `hugging8n-backup` in your Hugging Face account.
94
 
95
  - **Restore:** On startup, it pulls the latest state from your dataset.
96
  - **Sync:** Periodically (every 3 minutes by default), it pushes updates to the dataset.
 
132
  ```
133
 
134
  **With Docker:**
135
+
136
  ```bash
137
  docker build -t hugging8n .
138
  docker run -p 7861:7861 --env-file .env hugging8n
cloudflare-proxy.js CHANGED
@@ -1,6 +1,6 @@
1
  /**
2
  * Cloudflare Proxy: Transparent Fix for Blocked Domains
3
- *
4
  * Patches https.request to redirect traffic for Telegram/Discord
5
  * through a Cloudflare Worker proxy.
6
  */
@@ -9,16 +9,22 @@
9
  const https = require("https");
10
  const http = require("http");
11
 
12
- let PROXY_URL = process.env.CLOUDFLARE_PROXY_URL || process.env.OUTBOUND_PROXY_URL;
13
- if (PROXY_URL && !PROXY_URL.startsWith("http://") && !PROXY_URL.startsWith("https://")) {
 
 
 
 
14
  PROXY_URL = `https://${PROXY_URL}`;
15
  }
16
 
17
- const DEBUG = process.env.CLOUDFLARE_PROXY_DEBUG === "true" || process.env.OUTBOUND_PROXY_DEBUG === "true";
18
 
19
  // Allow user to define what to proxy. Use "*" to proxy everything except internal HF traffic.
20
- const PROXY_DOMAINS = process.env.CLOUDFLARE_PROXY_DOMAINS || process.env.OUTBOUND_PROXY_DOMAINS || "api.telegram.org,discord.com,discordapp.com,gateway.discord.gg,status.discord.com";
21
- const BLOCKED_DOMAINS = PROXY_DOMAINS.split(",").map(d => d.trim());
 
 
22
  const PROXY_ALL = PROXY_DOMAINS === "*";
23
 
24
  if (PROXY_URL) {
@@ -43,53 +49,64 @@ if (PROXY_URL) {
43
  path = options.pathname + options.search;
44
  headers = options.headers || {};
45
  } else {
46
- hostname = options.hostname || (options.host ? options.host.split(":")[0] : "");
 
 
47
  path = options.path || "/";
48
  headers = options.headers || {};
49
  }
50
 
51
  // 2. Check if we should intercept (and prevent recursion)
52
- const isInternal = hostname === "localhost" ||
53
- hostname === "127.0.0.1" ||
54
- hostname.endsWith(".hf.space") ||
55
- hostname.endsWith(".huggingface.co") ||
56
- hostname === "huggingface.co";
 
57
 
58
  let shouldProxy = false;
59
  if (PROXY_ALL) {
60
  shouldProxy = !isInternal;
61
  } else {
62
- shouldProxy = BLOCKED_DOMAINS.some(domain => hostname === domain || hostname.endsWith("." + domain));
 
 
63
  }
64
 
65
- const alreadyProxied = options._proxied || (headers && headers["x-target-host"]);
 
66
 
67
  if (shouldProxy && !alreadyProxied) {
68
- if (DEBUG) console.log(`[cloudflare-proxy] Redirecting ${hostname}${path} -> ${proxy.hostname}`);
 
 
 
69
 
70
  // 3. Create fresh options for the proxied request
71
- const newOptions = (typeof options === "string" || options instanceof URL)
72
- ? { protocol: "https:", path: path }
73
- : { ...options };
 
74
 
75
  // Ensure it's an object we can modify
76
- if (typeof newOptions !== "object") return original.apply(this, arguments);
 
77
 
78
  newOptions._proxied = true;
79
  newOptions.protocol = "https:";
80
  newOptions.hostname = proxy.hostname;
81
  newOptions.port = proxy.port || 443;
82
-
83
  // CRITICAL: Force fresh TLS handshake for the new domain
84
  newOptions.servername = proxy.hostname;
85
- delete newOptions.host; // Prefer hostname
86
  delete newOptions.agent; // Force a new agent to prevent connection reuse issues
87
 
88
  // Merge and update headers
89
  newOptions.headers = {
90
  ...(newOptions.headers || {}),
91
- "host": proxy.host,
92
- "x-target-host": hostname
93
  };
94
 
95
  // Always use HTTPS for the proxy connection
@@ -105,13 +122,18 @@ if (PROXY_URL) {
105
 
106
  if (DEBUG) {
107
  if (PROXY_ALL) {
108
- console.log(`[cloudflare-proxy] Transparent proxy active in WILDCARD mode (Proxying ALL except HF internal)`);
 
 
109
  } else {
110
- console.log(`[cloudflare-proxy] Transparent proxy active for: ${BLOCKED_DOMAINS.join(", ")}`);
 
 
111
  }
112
  console.log(`[cloudflare-proxy] Target proxy: ${proxy.hostname}`);
113
  }
114
  } catch (e) {
115
- if (DEBUG) console.error(`[cloudflare-proxy] Failed to initialize: ${e.message}`);
 
116
  }
117
  }
 
1
  /**
2
  * Cloudflare Proxy: Transparent Fix for Blocked Domains
3
+ *
4
  * Patches https.request to redirect traffic for Telegram/Discord
5
  * through a Cloudflare Worker proxy.
6
  */
 
9
  const https = require("https");
10
  const http = require("http");
11
 
12
+ let PROXY_URL = process.env.CLOUDFLARE_PROXY_URL;
13
+ if (
14
+ PROXY_URL &&
15
+ !PROXY_URL.startsWith("http://") &&
16
+ !PROXY_URL.startsWith("https://")
17
+ ) {
18
  PROXY_URL = `https://${PROXY_URL}`;
19
  }
20
 
21
+ const DEBUG = process.env.CLOUDFLARE_PROXY_DEBUG === "true";
22
 
23
  // Allow user to define what to proxy. Use "*" to proxy everything except internal HF traffic.
24
+ const PROXY_DOMAINS =
25
+ process.env.CLOUDFLARE_PROXY_DOMAINS ||
26
+ "api.telegram.org,discord.com,discordapp.com,gateway.discord.gg,status.discord.com";
27
+ const BLOCKED_DOMAINS = PROXY_DOMAINS.split(",").map((d) => d.trim());
28
  const PROXY_ALL = PROXY_DOMAINS === "*";
29
 
30
  if (PROXY_URL) {
 
49
  path = options.pathname + options.search;
50
  headers = options.headers || {};
51
  } else {
52
+ hostname =
53
+ options.hostname ||
54
+ (options.host ? options.host.split(":")[0] : "");
55
  path = options.path || "/";
56
  headers = options.headers || {};
57
  }
58
 
59
  // 2. Check if we should intercept (and prevent recursion)
60
+ const isInternal =
61
+ hostname === "localhost" ||
62
+ hostname === "127.0.0.1" ||
63
+ hostname.endsWith(".hf.space") ||
64
+ hostname.endsWith(".huggingface.co") ||
65
+ hostname === "huggingface.co";
66
 
67
  let shouldProxy = false;
68
  if (PROXY_ALL) {
69
  shouldProxy = !isInternal;
70
  } else {
71
+ shouldProxy = BLOCKED_DOMAINS.some(
72
+ (domain) => hostname === domain || hostname.endsWith("." + domain),
73
+ );
74
  }
75
 
76
+ const alreadyProxied =
77
+ options._proxied || (headers && headers["x-target-host"]);
78
 
79
  if (shouldProxy && !alreadyProxied) {
80
+ if (DEBUG)
81
+ console.log(
82
+ `[cloudflare-proxy] Redirecting ${hostname}${path} -> ${proxy.hostname}`,
83
+ );
84
 
85
  // 3. Create fresh options for the proxied request
86
+ const newOptions =
87
+ typeof options === "string" || options instanceof URL
88
+ ? { protocol: "https:", path: path }
89
+ : { ...options };
90
 
91
  // Ensure it's an object we can modify
92
+ if (typeof newOptions !== "object")
93
+ return original.apply(this, arguments);
94
 
95
  newOptions._proxied = true;
96
  newOptions.protocol = "https:";
97
  newOptions.hostname = proxy.hostname;
98
  newOptions.port = proxy.port || 443;
99
+
100
  // CRITICAL: Force fresh TLS handshake for the new domain
101
  newOptions.servername = proxy.hostname;
102
+ delete newOptions.host; // Prefer hostname
103
  delete newOptions.agent; // Force a new agent to prevent connection reuse issues
104
 
105
  // Merge and update headers
106
  newOptions.headers = {
107
  ...(newOptions.headers || {}),
108
+ host: proxy.host,
109
+ "x-target-host": hostname,
110
  };
111
 
112
  // Always use HTTPS for the proxy connection
 
122
 
123
  if (DEBUG) {
124
  if (PROXY_ALL) {
125
+ console.log(
126
+ `[cloudflare-proxy] Transparent proxy active in WILDCARD mode (Proxying ALL except HF internal)`,
127
+ );
128
  } else {
129
+ console.log(
130
+ `[cloudflare-proxy] Transparent proxy active for: ${BLOCKED_DOMAINS.join(", ")}`,
131
+ );
132
  }
133
  console.log(`[cloudflare-proxy] Target proxy: ${proxy.hostname}`);
134
  }
135
  } catch (e) {
136
+ if (DEBUG)
137
+ console.error(`[cloudflare-proxy] Failed to initialize: ${e.message}`);
138
  }
139
  }
cloudflare-worker.js CHANGED
@@ -1,11 +1,11 @@
1
  /**
2
  * Cloudflare Worker: Universal Outbound Proxy
3
- *
4
  * Deployment:
5
  * 1. Go to dash.cloudflare.com -> Workers & Pages -> Create Worker.
6
  * 2. Paste this code and deploy.
7
  * 3. Use your worker URL (e.g., https://my-proxy.workers.dev) as CLOUDFLARE_PROXY_URL.
8
- *
9
  * This worker reads the 'x-target-host' header to determine where to forward the request.
10
  */
11
 
@@ -13,7 +13,7 @@ export default {
13
  async fetch(request, env, ctx) {
14
  const url = new URL(request.url);
15
  const targetHost = request.headers.get("x-target-host");
16
-
17
  let targetBase = "";
18
 
19
  if (targetHost) {
@@ -23,15 +23,21 @@ export default {
23
  // Fallback: Guess based on path (legacy support)
24
  if (url.pathname.startsWith("/bot")) {
25
  targetBase = "https://api.telegram.org";
26
- } else if (url.pathname.startsWith("/api/webhooks") || url.pathname.startsWith("/api/v")) {
 
 
 
27
  targetBase = "https://discord.com";
28
  } else {
29
- return new Response("Invalid request. 'x-target-host' header missing and target not recognized via path.", { status: 400 });
 
 
 
30
  }
31
  }
32
 
33
  const targetUrl = targetBase + url.pathname + url.search;
34
-
35
  // Copy headers and remove internal/Cloudflare-specific ones
36
  const headers = new Headers(request.headers);
37
  headers.delete("cf-connecting-ip");
@@ -49,10 +55,10 @@ export default {
49
 
50
  try {
51
  const response = await fetch(modifiedRequest);
52
-
53
  // Special handling for Discord/Telegram which might return 403 on some CF IPs
54
  // If needed, you can add retry logic here.
55
-
56
  return response;
57
  } catch (e) {
58
  return new Response(`Proxy Error: ${e.message}`, { status: 502 });
 
1
  /**
2
  * Cloudflare Worker: Universal Outbound Proxy
3
+ *
4
  * Deployment:
5
  * 1. Go to dash.cloudflare.com -> Workers & Pages -> Create Worker.
6
  * 2. Paste this code and deploy.
7
  * 3. Use your worker URL (e.g., https://my-proxy.workers.dev) as CLOUDFLARE_PROXY_URL.
8
+ *
9
  * This worker reads the 'x-target-host' header to determine where to forward the request.
10
  */
11
 
 
13
  async fetch(request, env, ctx) {
14
  const url = new URL(request.url);
15
  const targetHost = request.headers.get("x-target-host");
16
+
17
  let targetBase = "";
18
 
19
  if (targetHost) {
 
23
  // Fallback: Guess based on path (legacy support)
24
  if (url.pathname.startsWith("/bot")) {
25
  targetBase = "https://api.telegram.org";
26
+ } else if (
27
+ url.pathname.startsWith("/api/webhooks") ||
28
+ url.pathname.startsWith("/api/v")
29
+ ) {
30
  targetBase = "https://discord.com";
31
  } else {
32
+ return new Response(
33
+ "Invalid request. 'x-target-host' header missing and target not recognized via path.",
34
+ { status: 400 },
35
+ );
36
  }
37
  }
38
 
39
  const targetUrl = targetBase + url.pathname + url.search;
40
+
41
  // Copy headers and remove internal/Cloudflare-specific ones
42
  const headers = new Headers(request.headers);
43
  headers.delete("cf-connecting-ip");
 
55
 
56
  try {
57
  const response = await fetch(modifiedRequest);
58
+
59
  // Special handling for Discord/Telegram which might return 403 on some CF IPs
60
  // If needed, you can add retry logic here.
61
+
62
  return response;
63
  } catch (e) {
64
  return new Response(`Proxy Error: ${e.message}`, { status: 502 });
dns-fix.js CHANGED
@@ -42,7 +42,9 @@ function dohResolve(hostname, callback) {
42
  res.on("end", () => {
43
  try {
44
  if (res.statusCode !== 200) {
45
- return callback(new Error(`DoH: server returned status ${res.statusCode}`));
 
 
46
  }
47
  const data = JSON.parse(body);
48
  const aRecords = (data.Answer || []).filter((a) => a.type === 1);
@@ -58,7 +60,9 @@ function dohResolve(hostname, callback) {
58
  }
59
  });
60
  });
61
- req.on("error", (e) => callback(new Error(`DoH request failed: ${e.message}`)));
 
 
62
  req.on("timeout", () => {
63
  req.destroy();
64
  callback(new Error("DoH request timed out"));
 
42
  res.on("end", () => {
43
  try {
44
  if (res.statusCode !== 200) {
45
+ return callback(
46
+ new Error(`DoH: server returned status ${res.statusCode}`),
47
+ );
48
  }
49
  const data = JSON.parse(body);
50
  const aRecords = (data.Answer || []).filter((a) => a.type === 1);
 
60
  }
61
  });
62
  });
63
+ req.on("error", (e) =>
64
+ callback(new Error(`DoH request failed: ${e.message}`)),
65
+ );
66
  req.on("timeout", () => {
67
  req.destroy();
68
  callback(new Error("DoH request timed out"));