| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- """
- 使用 Cloudflare Tunnel 替代 ngrok - 改进版
- """
- import subprocess
- import time
- import os
- import signal
- import sys
- import re
- from pathlib import Path
- def setup_environment():
- """设置环境变量"""
- proxy_url = "http://172.16.40.16:7280"
- env = os.environ.copy()
- env.update({
- 'HTTP_PROXY': proxy_url,
- 'HTTPS_PROXY': proxy_url,
- 'http_proxy': proxy_url,
- 'https_proxy': proxy_url,
- # 设置 DNS
- 'GODEBUG': 'netdns=go+1',
- })
- return env
- def download_cloudflared():
- """下载 cloudflared"""
- print("🔄 Downloading cloudflared...")
-
- # 确保目录存在
- bin_dir = Path.home() / "zhch" / "bin"
- bin_dir.mkdir(exist_ok=True)
-
- cloudflared_path = bin_dir / "cloudflared"
-
- # 如果已存在,跳过下载
- if cloudflared_path.exists():
- print("✅ Cloudflared already exists")
- return str(cloudflared_path)
-
- # 下载命令
- download_url = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64"
- download_cmd = ['curl', '-L', '-o', str(cloudflared_path), download_url]
- # 1. 下载 cloudflared
- download_cmd = ['wget', '-O', str(cloudflared_path), download_url]
- try:
- env = setup_environment()
- result = subprocess.run(
- download_cmd,
- check=True,
- env=env,
- capture_output=True,
- text=True,
- timeout=300 # 5分钟超时
- )
-
- # 设置执行权限
- subprocess.run(['chmod', '+x', str(cloudflared_path)], check=True)
- print("✅ Cloudflared downloaded successfully")
- return str(cloudflared_path)
-
- except subprocess.TimeoutExpired:
- print("❌ Download timeout - network may be slow")
- return None
- except subprocess.CalledProcessError as e:
- print(f"❌ Download failed: {e}")
- print(f"STDOUT: {e.stdout}")
- print(f"STDERR: {e.stderr}")
- return None
- def setup_cloudflare_tunnel(port=8101, timeout=60):
- """设置 Cloudflare Tunnel"""
-
- print(f"🔄 Setting up Cloudflare Tunnel for port {port}...")
-
- # 下载 cloudflared
- cloudflared_path = download_cloudflared()
- if not cloudflared_path:
- return None
-
- # 准备命令
- tunnel_cmd = [
- cloudflared_path, 'tunnel',
- '--url', f'http://localhost:{port}',
- '--no-autoupdate',
- '--logfile', f'{Path.home()}/zhch/logs/cloudflared.log',
- '--loglevel', 'info'
- ]
-
- print(f"🚀 Starting tunnel: {' '.join(tunnel_cmd)}")
-
- try:
- env = setup_environment()
- process = subprocess.Popen(
- tunnel_cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT, # 合并输出
- text=True,
- env=env,
- bufsize=1, # 行缓冲
- universal_newlines=True
- )
-
- print("⏳ Waiting for tunnel to start...")
-
- # 等待隧道启动并获取 URL
- start_time = time.time()
- url_found = False
-
- while time.time() - start_time < timeout:
- if process.poll() is not None:
- # 进程已终止
- stdout, _ = process.communicate()
- print(f"❌ Process terminated early:")
- print(stdout)
- return None
-
- try:
- # 非阻塞读取
- line = process.stdout.readline()
- if line:
- print(f"LOG: {line.strip()}")
-
- # 查找 trycloudflare.com URL
- if 'trycloudflare.com' in line:
- # 使用正则表达式提取 URL
- url_match = re.search(r'https://[a-zA-Z0-9-]+\.trycloudflare\.com', line)
- if url_match:
- url = url_match.group(0)
- print(f"✅ Tunnel URL found: {url}")
- return url, process
-
- # 检查错误信息
- if 'failed to request quick Tunnel' in line:
- print("❌ Failed to create tunnel - network connectivity issue")
- process.terminate()
- return None
-
- except Exception as e:
- print(f"Error reading output: {e}")
- break
-
- time.sleep(0.5)
-
- print(f"⏰ Timeout after {timeout} seconds")
- process.terminate()
- return None
-
- except Exception as e:
- print(f"❌ Failed to start tunnel: {e}")
- return None
- def test_local_service(port=8101):
- """测试本地服务是否运行"""
- import socket
- try:
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.settimeout(5)
- result = sock.connect_ex(('localhost', port))
- sock.close()
-
- if result == 0:
- print(f"✅ Local service is running on port {port}")
- return True
- else:
- print(f"❌ No service found on port {port}")
- return False
- except Exception as e:
- print(f"❌ Error testing local service: {e}")
- return False
- def main():
- port = 8101
-
- print("🌐 Cloudflare Tunnel Setup")
- print("=" * 50)
-
- # 检查本地服务
- if not test_local_service(port):
- print(f"⚠️ Warning: No service detected on port {port}")
- response = input("Continue anyway? (y/N): ")
- if response.lower() != 'y':
- return
-
- # 设置隧道
- result = setup_cloudflare_tunnel(port, timeout=120)
-
- if result:
- url, process = result
- print(f"\n🎉 Success!")
- print(f"🌐 Your service is now accessible at: {url}")
- print(f"🔗 Test URL: {url}/docs (if it's a FastAPI service)")
- print("\nPress Ctrl+C to stop the tunnel")
-
- def signal_handler(sig, frame):
- print("\n🛑 Stopping tunnel...")
- process.terminate()
- try:
- process.wait(timeout=5)
- except subprocess.TimeoutExpired:
- process.kill()
- print("✅ Tunnel stopped")
- sys.exit(0)
-
- signal.signal(signal.SIGINT, signal_handler)
-
- try:
- # 保持运行并显示日志
- while process.poll() is None:
- try:
- line = process.stdout.readline()
- if line:
- print(f"TUNNEL: {line.strip()}")
- except KeyboardInterrupt:
- break
- except Exception as e:
- print(f"Error: {e}")
- break
- except Exception as e:
- print(f"Error: {e}")
- finally:
- process.terminate()
- else:
- print("\n❌ Failed to setup tunnel")
- print("\n🔧 Troubleshooting tips:")
- print("1. Check if your proxy allows connections to api.trycloudflare.com")
- print("2. Try using a different tunnel service (localtunnel, bore.pub)")
- print("3. Contact your network administrator about tunnel access")
- print("4. Use VS Code port forwarding if available")
- if __name__ == "__main__":
- main()
|