|
|
@@ -1,74 +1,147 @@
|
|
|
"""
|
|
|
-使用 Cloudflare Tunnel 替代 ngrok
|
|
|
+使用 Cloudflare Tunnel 替代 ngrok - 改进版
|
|
|
"""
|
|
|
import subprocess
|
|
|
import time
|
|
|
-import json
|
|
|
-import requests
|
|
|
+import os
|
|
|
+import signal
|
|
|
+import sys
|
|
|
+import re
|
|
|
+from pathlib import Path
|
|
|
|
|
|
-def setup_cloudflare_tunnel(port=8101):
|
|
|
- """设置 Cloudflare Tunnel"""
|
|
|
+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...")
|
|
|
|
|
|
- print("🔄 Setting up Cloudflare Tunnel...")
|
|
|
+ # 确保目录存在
|
|
|
+ bin_dir = Path.home() / "zhch" / "bin"
|
|
|
+ bin_dir.mkdir(exist_ok=True)
|
|
|
|
|
|
- # 1. 下载 cloudflared
|
|
|
- download_cmd = [
|
|
|
- 'wget', '-O', '/tmp/cloudflared',
|
|
|
- 'https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64'
|
|
|
- ]
|
|
|
+ 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]
|
|
|
|
|
|
try:
|
|
|
- subprocess.run(download_cmd, check=True, env={
|
|
|
- 'https_proxy': 'http://172.16.40.16:7280',
|
|
|
- 'http_proxy': 'http://172.16.40.16:7280'
|
|
|
- })
|
|
|
- subprocess.run(['chmod', '+x', '/tmp/cloudflared'], check=True)
|
|
|
- print("✅ Cloudflared downloaded")
|
|
|
+ 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
|
|
|
|
|
|
- # 2. 启动临时隧道
|
|
|
+ # 准备命令
|
|
|
tunnel_cmd = [
|
|
|
- '/tmp/cloudflared', 'tunnel',
|
|
|
+ cloudflared_path, 'tunnel',
|
|
|
'--url', f'http://localhost:{port}',
|
|
|
- '--no-autoupdate'
|
|
|
+ '--no-autoupdate',
|
|
|
+ '--logfile', f'{Path.home()}/zhch/logs/cloudflared.log',
|
|
|
+ '--loglevel', 'info'
|
|
|
]
|
|
|
|
|
|
+ print(f"🚀 Starting tunnel: {' '.join(tunnel_cmd)}")
|
|
|
+
|
|
|
try:
|
|
|
- print(f"🚀 Starting tunnel for port {port}...")
|
|
|
+ env = setup_environment()
|
|
|
process = subprocess.Popen(
|
|
|
tunnel_cmd,
|
|
|
stdout=subprocess.PIPE,
|
|
|
- stderr=subprocess.PIPE,
|
|
|
+ stderr=subprocess.STDOUT, # 合并输出
|
|
|
text=True,
|
|
|
- env={
|
|
|
- 'https_proxy': 'http://172.16.40.16:7280',
|
|
|
- 'http_proxy': 'http://172.16.40.16:7280'
|
|
|
- }
|
|
|
+ env=env,
|
|
|
+ bufsize=1, # 行缓冲
|
|
|
+ universal_newlines=True
|
|
|
)
|
|
|
|
|
|
+ print("⏳ Waiting for tunnel to start...")
|
|
|
+
|
|
|
# 等待隧道启动并获取 URL
|
|
|
- for _ in range(30): # 等待最多30秒
|
|
|
+ start_time = time.time()
|
|
|
+ url_found = False
|
|
|
+
|
|
|
+ while time.time() - start_time < timeout:
|
|
|
if process.poll() is not None:
|
|
|
- stdout, stderr = process.communicate()
|
|
|
- print(f"❌ Process failed: {stderr}")
|
|
|
+ # 进程已终止
|
|
|
+ stdout, _ = process.communicate()
|
|
|
+ print(f"❌ Process terminated early:")
|
|
|
+ print(stdout)
|
|
|
return None
|
|
|
|
|
|
- time.sleep(1)
|
|
|
-
|
|
|
- # 尝试从输出中提取 URL
|
|
|
try:
|
|
|
- # 读取一些输出
|
|
|
- output = process.stdout.readline()
|
|
|
- if 'trycloudflare.com' in output:
|
|
|
- url = output.split()[-1]
|
|
|
- print(f"✅ Tunnel URL: {url}")
|
|
|
- return url, process
|
|
|
- except:
|
|
|
- continue
|
|
|
+ # 非阻塞读取
|
|
|
+ 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("⏰ Timeout waiting for tunnel URL")
|
|
|
+ print(f"⏰ Timeout after {timeout} seconds")
|
|
|
process.terminate()
|
|
|
return None
|
|
|
|
|
|
@@ -76,14 +149,83 @@ def setup_cloudflare_tunnel(port=8101):
|
|
|
print(f"❌ Failed to start tunnel: {e}")
|
|
|
return None
|
|
|
|
|
|
-if __name__ == "__main__":
|
|
|
- result = setup_cloudflare_tunnel(8101)
|
|
|
+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("Press Ctrl+C to stop the tunnel")
|
|
|
- try:
|
|
|
- process.wait()
|
|
|
- except KeyboardInterrupt:
|
|
|
+ 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()
|
|
|
+ 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()
|