Cloudflare.py 7.2 KB

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