CodeX - Upgraded API Tunnel to Cloudflare

This commit is contained in:
RayExo
2026-07-16 20:32:59 +05:30
parent d5985aa102
commit 7e4c4d9100
5 changed files with 139 additions and 107 deletions

View File

@@ -2,10 +2,11 @@ TOKEN = TOKEN_HERE
brand_name='ZyroX' brand_name='ZyroX'
NEXT_PUBLIC_BRAND_NAME='ZyroX' NEXT_PUBLIC_BRAND_NAME='ZyroX'
# Owner # ── Owner / Staff IDs (comma-separated Discord user IDs) ──────────────────────
OWNER_IDS=870179991462236170,767979794411028491 # Edit this list — no code changes needed anywhere else
OWNER_IDS=870179991462236170,767979794411028491,1432771000629596225,1382744437049790495,1263404140965396555
# LAVALINK CONFIG # ── Lavalink ──────────────────────────────────────────────────────────────────
LAVALINK_HOST="lavalink.jirayu.net" LAVALINK_HOST="lavalink.jirayu.net"
LAVALINK_PASSWORD="youshallnotpass" LAVALINK_PASSWORD="youshallnotpass"
@@ -15,28 +16,38 @@ LAVALINK_SECURE="false"
# Only needed when LAVALINK_SECURE is false (e.g. 2333) # Only needed when LAVALINK_SECURE is false (e.g. 2333)
LAVALINK_PORT="13592" LAVALINK_PORT="13592"
# ── Emoji Sync ────────────────────────────────────────────────────────────────
# EMOJI SYNC CONFIG
# Set to true to enable automatic application emoji sync on startup # Set to true to enable automatic application emoji sync on startup
EMOJI_SYNC="false" EMOJI_SYNC="false"
# API SERVER CONFIG # ── API / Dashboard Backend ───────────────────────────────────────────────────
# Set to true to enable the dashboard API server # Set to true to enable the dashboard API server
API_ENABLED="false" API_ENABLED="false"
# Port the API server listens on # Port the API server listens on
API_PORT="8000" API_PORT="8000"
# ON CMD RUN
WEBHOOK_URL="https://discord.com/api/webhooks/"
DASHBOARD_API_KEY="ZYROX_SECURE_API_KEY_12345_CHANGE_THIS_ASAP_BY_CODEX_DEVS" DASHBOARD_API_KEY="ZYROX_SECURE_API_KEY_12345_CHANGE_THIS_ASAP_BY_CODEX_DEVS"
# Extra CORS-allowed origins, comma-separated (optional)
CORS_ORIGINS=""
# CODEX DEVS # CODEX DEVS
# HTTPS TUNNEL CONFIG # ── Webhooks ──────────────────────────────────────────────────────────────────
WEBHOOK_URL="https://discord.com/api/webhooks/"
CMD_WEBHOOK_URL="https://discord.com/api/webhooks/"
# ── Cloudflare Tunnel For API Server ─────────────────────────────────────────────────────────
#
# How to get CF_TUNNEL_TOKEN (browser only, no CLI):
# 1. Go to https://one.dash.cloudflare.com
# 2. Networks → Tunnels → Create a tunnel → Choose "Cloudflared"
# 3. Give it a name (e.g. zyrox-api) and save
# 4. On the "Install connector" step, copy the token from the shown command:
# cloudflared tunnel run --token <COPY_THIS_TOKEN>
# 5. Go to "Published Application Routes" tab → Add a Published Application Routes:
# Subdomain: zyrox-api Domain: yourdomain.com Service: http://localhost:8000
# 6. Paste the token below — that's it. Permanent URL, unlimited traffic.
#
TUNNEL_ENABLED="true" TUNNEL_ENABLED="true"
# Get your authtoken from https://dashboard.ngrok.com/get-started/your-authtoken CF_TUNNEL_TOKEN="xxx_xxxx"
NGROK_AUTHTOKEN="xxx_xxxx" CF_TUNNEL_URL="https://zyrox-api.yourdomain.com"
# Reserve a free static domain at https://dashboard.ngrok.com/domains
# e.g. xxxx-xxxx-xxxx.ngrok-free.app — leave blank for a random URL each restart
NGROK_DOMAIN=""

View File

@@ -334,7 +334,7 @@ def keep_alive():
keep_alive() keep_alive()
# --- ngrok HTTPS Tunnel (for Dashboard API) --- # --- Cloudflare Tunnel (HTTPS for API) ---
from utils.tunnel import start_tunnel from utils.tunnel import start_tunnel
start_tunnel() start_tunnel()

View File

@@ -103,14 +103,6 @@ def create_app() -> FastAPI:
allow_headers=["*"], allow_headers=["*"],
) )
# Middleware: add ngrok-skip-browser-warning header to every response
# (prevents ngrok's interstitial warning page from breaking CORS preflight)
@app.middleware("http")
async def add_ngrok_header(request: Request, call_next):
response = await call_next(request)
response.headers["ngrok-skip-browser-warning"] = "true"
return response
# Register Routers # Register Routers
app.include_router(bot.router, prefix="/api/v1/bot", tags=["Bot"]) app.include_router(bot.router, prefix="/api/v1/bot", tags=["Bot"])
app.include_router(guilds.router, prefix="/api/v1/guilds", tags=["Guilds"]) app.include_router(guilds.router, prefix="/api/v1/guilds", tags=["Guilds"])

View File

@@ -39,4 +39,4 @@ deep-translator==1.11.4
uvicorn==0.46.0 uvicorn==0.46.0
fastapi fastapi
slowapi slowapi
pyngrok pycloudflared

View File

@@ -13,38 +13,46 @@
# ╚══════════════════════════════════════════════════════════════════╝ # ╚══════════════════════════════════════════════════════════════════╝
""" """
HTTPS Tunnel for the ZyroX API using pyngrok. HTTPS Tunnel for the ZyroX API — Cloudflare Tunnel via pycloudflared.
pyngrok is a pure Python package that automatically downloads the ngrok binary Zero manual installs. Just:
on first run — no system installs, works on Pterodactyl out of the box. 1. pip install pycloudflared (already in requirements.txt)
2. Get your tunnel token from the Cloudflare dashboard (browser only, no CLI)
3. Set CF_TUNNEL_TOKEN in .env
With a free ngrok account + a reserved static domain you get the SAME URL on That's it. The cloudflared binary is downloaded automatically on first run.
every restart (free tier gives you 1 static domain).
────────────────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────────────────
One-time setup (do this once) How to get your CF_TUNNEL_TOKEN (browser only, no CLI needed)
────────────────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────────────────
1. Sign up for a free account at https://ngrok.com 1. Go to https://one.dash.cloudflare.com
2. Copy your authtoken from https://dashboard.ngrok.com/get-started/your-authtoken 2. Networks → Tunnels → Create a tunnel
3. Reserve a free static domain at https://dashboard.ngrok.com/domains 3. Choose "Cloudflared" as connector type
(looks like: xxxx-xxxx-xxxx.ngrok-free.app) 4. Give it a name (e.g. zyrox-api) and click Save
4. Add these two vars to your .env: 5. On the "Install connector" step, find the token in the command shown:
NGROK_AUTHTOKEN = your_token_here cloudflared tunnel run --token <YOUR_TOKEN_HERE>
NGROK_DOMAIN = xxxx-xxxx-xxxx.ngrok-free.app Copy just the token string.
5. Set TUNNEL_ENABLED = "true" 6. Go to "Published Application Routes" tab → Add a Published Application Routes:
Subdomain: zyrox-api Domain: yourdomain.com Service: http://localhost:8000
(Or use any domain you have on Cloudflare)
7. Paste the token into your .env:
CF_TUNNEL_TOKEN = "eyJhIjoiX..."
CF_TUNNEL_URL = "https://zyrox-api.yourdomain.com"
That's it — pyngrok handles the binary download automatically on first start. That's the permanent URL — never changes between restarts.
Unlimited bandwidth, unlimited requests, free.
────────────────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────────────────
""" """
import os import os
import time import time
import threading import threading
import subprocess
# ── env vars ────────────────────────────────────────────────────────────────── # ── env vars ──────────────────────────────────────────────────────────────────
TUNNEL_ENABLED = os.getenv("TUNNEL_ENABLED", "true").strip().lower() == "true" TUNNEL_ENABLED = os.getenv("TUNNEL_ENABLED", "true").strip().lower() == "true"
NGROK_AUTHTOKEN = os.getenv("NGROK_AUTHTOKEN", "").strip() CF_TUNNEL_TOKEN = os.getenv("CF_TUNNEL_TOKEN", "").strip() # token from Cloudflare dashboard
NGROK_DOMAIN = os.getenv("NGROK_DOMAIN", "").strip() # e.g. xxxx.ngrok-free.app CF_TUNNEL_URL = os.getenv("CF_TUNNEL_URL", "").strip() # e.g. https://api.yourdomain.com
API_PORT = int(os.getenv("API_PORT", "8000")) API_PORT = int(os.getenv("API_PORT", "8000"))
# ── colours ─────────────────────────────────────────────────────────────────── # ── colours ───────────────────────────────────────────────────────────────────
@@ -55,99 +63,120 @@ _RED = "\033[31m"
_RESET = "\033[0m" _RESET = "\033[0m"
def _run_ngrok(port: int, domain: str) -> None: def _get_binary() -> str | None:
""" """
Blocking loop: opens an ngrok tunnel and keeps it alive. Return path to cloudflared binary.
Restarts automatically if the connection drops. pycloudflared downloads it automatically on first call.
""" """
try: try:
from pyngrok import ngrok, conf, exception as ngrok_exc from pycloudflared import try_cloudflare # noqa: F401 — triggers download
import pycloudflared as _pcf
# pycloudflared stores the binary path in this attribute after download
path = getattr(_pcf, "cloudflared_path", None)
if path and os.path.isfile(path):
return path
# Some versions expose it differently — walk the package dir
pkg_dir = os.path.dirname(_pcf.__file__)
for fname in os.listdir(pkg_dir):
if "cloudflared" in fname.lower() and os.access(os.path.join(pkg_dir, fname), os.X_OK):
return os.path.join(pkg_dir, fname)
except ImportError: except ImportError:
print( pass
f"{_RED}◈ Tunnel: pyngrok is not installed.\n"
f" Run: pip install pyngrok{_RESET}"
)
return
# Set the authtoken (pyngrok persists it for future runs) # Last resort: system PATH
if NGROK_AUTHTOKEN: import shutil
conf.get_default().auth_token = NGROK_AUTHTOKEN return shutil.which("cloudflared")
def _run_tunnel(binary: str, token: str, port: int, public_url: str) -> None:
"""
Blocking loop — runs:
cloudflared tunnel --no-autoupdate run --token <token>
Restarts automatically if the process exits.
"""
cmd = [
binary,
"tunnel",
"--no-autoupdate",
"--url", f"http://localhost:{port}",
"run",
"--token", token,
]
first_run = True
while True: while True:
tunnel = None if first_run:
print(f"{_CYAN}◈ Tunnel: connecting to Cloudflare…{_RESET}")
first_run = False
else:
print(f"{_YELLOW}◈ Tunnel: reconnecting…{_RESET}")
try: try:
options = {"addr": port, "proto": "http"} proc = subprocess.Popen(
if domain: cmd,
options["hostname"] = domain # use reserved static domain stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
# NOTE: request_header_add is NOT supported via the pyngrok API. text=True,
# Instead the FastAPI middleware injects the ngrok-skip-browser-warning
# header on every response, which achieves the same result.
tunnel = ngrok.connect(**options)
public_url = tunnel.public_url.replace("http://", "https://")
print(f"{_GREEN}◈ Tunnel: API is live at {public_url}{_RESET}")
print(f"{_CYAN} ↳ set NEXT_PUBLIC_API_URL={public_url}/api/v1 in your dashboard .env{_RESET}")
# Block until the tunnel process exits
ngrok.get_ngrok_process().proc.wait()
except ngrok_exc.PyngrokNgrokHTTPError as e:
# Catches auth errors (401) and other ngrok HTTP-level errors
msg = str(e).lower()
if "401" in msg or "auth" in msg or "token" in msg:
print(
f"{_RED}◈ Tunnel: invalid NGROK_AUTHTOKEN.\n"
f" Get your token from https://dashboard.ngrok.com/get-started/your-authtoken{_RESET}"
) )
return # no point retrying with a bad token
print(f"{_RED}◈ Tunnel: ngrok HTTP error — {e}{_RESET}")
except ngrok_exc.PyngrokNgrokError as e: announced = False
print(f"{_RED}◈ Tunnel: ngrok error — {e}{_RESET}") for line in proc.stdout:
line = line.rstrip()
if not line:
continue
except Exception as e: # Only surface important lines — suppress the noisy info spam
print(f"{_RED}◈ Tunnel: unexpected error — {e}{_RESET}") low = line.lower()
if any(k in low for k in ("error", "warn", "registered", "connected", "failed", "unable")):
print(f"{_CYAN} [cloudflared] {line}{_RESET}")
finally: # Announce the live URL once the tunnel is up
# Clean up before retrying if not announced and ("registered tunnel connection" in low or "connection registered" in low):
try: announced = True
if tunnel: if public_url:
ngrok.disconnect(tunnel.public_url) print(f"{_GREEN}◈ Tunnel: API is live at {public_url}{_RESET}")
except Exception: print(f"{_CYAN} ↳ NEXT_PUBLIC_API_URL = {public_url}/api/v1{_RESET}")
pass else:
try: print(f"{_GREEN}◈ Tunnel: connected — check CF_TUNNEL_URL in .env for your public URL{_RESET}")
ngrok.kill()
except Exception:
pass
print(f"{_YELLOW}◈ Tunnel: connection lost, restarting in 5 s…{_RESET}") proc.wait()
code = proc.returncode
except FileNotFoundError:
print(f"{_RED}◈ Tunnel: binary not found at '{binary}' — try: pip install pycloudflared{_RESET}")
return
except Exception as exc:
print(f"{_RED}◈ Tunnel: unexpected error — {exc}{_RESET}")
code = -1
print(f"{_YELLOW}◈ Tunnel: exited (code {code}), restarting in 5 s…{_RESET}")
time.sleep(5) time.sleep(5)
def start_tunnel() -> None: def start_tunnel() -> None:
""" """
Start the ngrok HTTPS tunnel in a background daemon thread. Start the Cloudflare Tunnel in a background daemon thread.
Called from CodeX.py after keep_alive(). Called from CodeX.py after keep_alive().
""" """
if not TUNNEL_ENABLED: if not TUNNEL_ENABLED:
print(f"{_YELLOW}◈ Tunnel: disabled via TUNNEL_ENABLED=false{_RESET}") print(f"{_YELLOW}◈ Tunnel: disabled via TUNNEL_ENABLED=false{_RESET}")
return return
if not NGROK_AUTHTOKEN: if not CF_TUNNEL_TOKEN:
print( print(
f"{_YELLOW}◈ Tunnel: NGROK_AUTHTOKEN is not set — tunnel skipped.\n" f"{_YELLOW}◈ Tunnel: CF_TUNNEL_TOKEN is not set — tunnel skipped.\n"
f" Get your token from https://dashboard.ngrok.com/get-started/your-authtoken{_RESET}" f" Get your token from https://one.dash.cloudflare.com → Networks → Tunnels{_RESET}"
) )
return return
if not NGROK_DOMAIN: binary = _get_binary()
if not binary:
print( print(
f"{_YELLOW}◈ Tunnel: NGROK_DOMAIN is not set — a random URL will be used (changes each restart).\n" f"{_RED}◈ Tunnel: cloudflared binary not found.\n"
f" Reserve a free static domain at https://dashboard.ngrok.com/domains{_RESET}" f" Make sure pycloudflared is installed: pip install pycloudflared{_RESET}"
) )
return
print(f"{_CYAN}◈ Tunnel: starting ngrok tunnel on port {API_PORT}{_RESET}") print(f"{_CYAN}◈ Tunnel: cloudflared binary ready — starting tunnel on port {API_PORT}{_RESET}")
t = threading.Thread(target=_run_ngrok, args=(API_PORT, NGROK_DOMAIN), daemon=True) t = threading.Thread(target=_run_tunnel, args=(binary, CF_TUNNEL_TOKEN, API_PORT, CF_TUNNEL_URL), daemon=True)
t.start() t.start()