""" 使用 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()