Cloudflare.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. """
  2. 使用 Cloudflare Tunnel 替代 ngrok - 改进版
  3. """
  4. import subprocess
  5. import time
  6. import os
  7. import signal
  8. import sys
  9. import re
  10. from pathlib import Path
  11. def setup_environment():
  12. """设置环境变量"""
  13. proxy_url = "http://172.16.40.16:7280"
  14. env = os.environ.copy()
  15. env.update({
  16. 'HTTP_PROXY': proxy_url,
  17. 'HTTPS_PROXY': proxy_url,
  18. 'http_proxy': proxy_url,
  19. 'https_proxy': proxy_url,
  20. # 设置 DNS
  21. 'GODEBUG': 'netdns=go+1',
  22. })
  23. return env
  24. def download_cloudflared():
  25. """下载 cloudflared"""
  26. print("🔄 Downloading cloudflared...")
  27. # 确保目录存在
  28. bin_dir = Path.home() / "zhch" / "bin"
  29. bin_dir.mkdir(exist_ok=True)
  30. cloudflared_path = bin_dir / "cloudflared"
  31. # 如果已存在,跳过下载
  32. if cloudflared_path.exists():
  33. print("✅ Cloudflared already exists")
  34. return str(cloudflared_path)
  35. # 下载命令
  36. download_url = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64"
  37. download_cmd = ['curl', '-L', '-o', str(cloudflared_path), download_url]
  38. # 1. 下载 cloudflared
  39. download_cmd = ['wget', '-O', str(cloudflared_path), download_url]
  40. try:
  41. env = setup_environment()
  42. result = subprocess.run(
  43. download_cmd,
  44. check=True,
  45. env=env,
  46. capture_output=True,
  47. text=True,
  48. timeout=300 # 5分钟超时
  49. )
  50. # 设置执行权限
  51. subprocess.run(['chmod', '+x', str(cloudflared_path)], check=True)
  52. print("✅ Cloudflared downloaded successfully")
  53. return str(cloudflared_path)
  54. except subprocess.TimeoutExpired:
  55. print("❌ Download timeout - network may be slow")
  56. return None
  57. except subprocess.CalledProcessError as e:
  58. print(f"❌ Download failed: {e}")
  59. print(f"STDOUT: {e.stdout}")
  60. print(f"STDERR: {e.stderr}")
  61. return None
  62. def setup_cloudflare_tunnel(port=8101, timeout=60):
  63. """设置 Cloudflare Tunnel"""
  64. print(f"🔄 Setting up Cloudflare Tunnel for port {port}...")
  65. # 下载 cloudflared
  66. cloudflared_path = download_cloudflared()
  67. if not cloudflared_path:
  68. return None
  69. # 准备命令
  70. tunnel_cmd = [
  71. cloudflared_path, 'tunnel',
  72. '--url', f'http://localhost:{port}',
  73. '--no-autoupdate',
  74. '--logfile', f'{Path.home()}/zhch/logs/cloudflared.log',
  75. '--loglevel', 'info'
  76. ]
  77. print(f"🚀 Starting tunnel: {' '.join(tunnel_cmd)}")
  78. try:
  79. env = setup_environment()
  80. process = subprocess.Popen(
  81. tunnel_cmd,
  82. stdout=subprocess.PIPE,
  83. stderr=subprocess.STDOUT, # 合并输出
  84. text=True,
  85. env=env,
  86. bufsize=1, # 行缓冲
  87. universal_newlines=True
  88. )
  89. print("⏳ Waiting for tunnel to start...")
  90. # 等待隧道启动并获取 URL
  91. start_time = time.time()
  92. url_found = False
  93. while time.time() - start_time < timeout:
  94. if process.poll() is not None:
  95. # 进程已终止
  96. stdout, _ = process.communicate()
  97. print(f"❌ Process terminated early:")
  98. print(stdout)
  99. return None
  100. try:
  101. # 非阻塞读取
  102. line = process.stdout.readline()
  103. if line:
  104. print(f"LOG: {line.strip()}")
  105. # 查找 trycloudflare.com URL
  106. if 'trycloudflare.com' in line:
  107. # 使用正则表达式提取 URL
  108. url_match = re.search(r'https://[a-zA-Z0-9-]+\.trycloudflare\.com', line)
  109. if url_match:
  110. url = url_match.group(0)
  111. print(f"✅ Tunnel URL found: {url}")
  112. return url, process
  113. # 检查错误信息
  114. if 'failed to request quick Tunnel' in line:
  115. print("❌ Failed to create tunnel - network connectivity issue")
  116. process.terminate()
  117. return None
  118. except Exception as e:
  119. print(f"Error reading output: {e}")
  120. break
  121. time.sleep(0.5)
  122. print(f"⏰ Timeout after {timeout} seconds")
  123. process.terminate()
  124. return None
  125. except Exception as e:
  126. print(f"❌ Failed to start tunnel: {e}")
  127. return None
  128. def test_local_service(port=8101):
  129. """测试本地服务是否运行"""
  130. import socket
  131. try:
  132. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  133. sock.settimeout(5)
  134. result = sock.connect_ex(('localhost', port))
  135. sock.close()
  136. if result == 0:
  137. print(f"✅ Local service is running on port {port}")
  138. return True
  139. else:
  140. print(f"❌ No service found on port {port}")
  141. return False
  142. except Exception as e:
  143. print(f"❌ Error testing local service: {e}")
  144. return False
  145. def main():
  146. port = 8101
  147. print("🌐 Cloudflare Tunnel Setup")
  148. print("=" * 50)
  149. # 检查本地服务
  150. if not test_local_service(port):
  151. print(f"⚠️ Warning: No service detected on port {port}")
  152. response = input("Continue anyway? (y/N): ")
  153. if response.lower() != 'y':
  154. return
  155. # 设置隧道
  156. result = setup_cloudflare_tunnel(port, timeout=120)
  157. if result:
  158. url, process = result
  159. print(f"\n🎉 Success!")
  160. print(f"🌐 Your service is now accessible at: {url}")
  161. print(f"🔗 Test URL: {url}/docs (if it's a FastAPI service)")
  162. print("\nPress Ctrl+C to stop the tunnel")
  163. def signal_handler(sig, frame):
  164. print("\n🛑 Stopping tunnel...")
  165. process.terminate()
  166. try:
  167. process.wait(timeout=5)
  168. except subprocess.TimeoutExpired:
  169. process.kill()
  170. print("✅ Tunnel stopped")
  171. sys.exit(0)
  172. signal.signal(signal.SIGINT, signal_handler)
  173. try:
  174. # 保持运行并显示日志
  175. while process.poll() is None:
  176. try:
  177. line = process.stdout.readline()
  178. if line:
  179. print(f"TUNNEL: {line.strip()}")
  180. except KeyboardInterrupt:
  181. break
  182. except Exception as e:
  183. print(f"Error: {e}")
  184. break
  185. except Exception as e:
  186. print(f"Error: {e}")
  187. finally:
  188. process.terminate()
  189. else:
  190. print("\n❌ Failed to setup tunnel")
  191. print("\n🔧 Troubleshooting tips:")
  192. print("1. Check if your proxy allows connections to api.trycloudflare.com")
  193. print("2. Try using a different tunnel service (localtunnel, bore.pub)")
  194. print("3. Contact your network administrator about tunnel access")
  195. print("4. Use VS Code port forwarding if available")
  196. if __name__ == "__main__":
  197. main()