File size: 4,030 Bytes
af119c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dad583a
af119c2
 
 
dad583a
af119c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6f931c4
 
 
af119c2
 
 
 
 
d6633c7
6f931c4
 
d6633c7
6f931c4
 
 
 
 
 
 
d6633c7
af119c2
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import subprocess
import time
import sys
import os
import threading

def stream_reader(pipe, prefix):
    """实时读取并打印子进程输出"""
    try:
        with pipe:
            for line in iter(pipe.readline, ''):
                print(f"[{prefix}] {line.strip()}")
                # 检查是否有启动成功的标志
                if "Uvicorn running on" in line:
                    print("✅ 检测到服务器启动成功标志!")
    except Exception:
        pass

def run_all_in_one():
    print("🚀 开始集成运行流程...")
    
    # 1. 启动服务器 (使用 threading 而不是 shell &)
    print("   正在启动 FastAPI 服务器...")
    server_process = subprocess.Popen(
        [sys.executable, "server.py"],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True,
        bufsize=1
    )
    
    # 启动一个线程来实时打印服务器日志,防止缓冲区满卡死
    t = threading.Thread(target=stream_reader, args=(server_process.stdout, "Server"))
    t.daemon = True
    t.start()
    
    # 2. 等待一段时间让服务器初始化
    print("⏳ 等待服务器初始化 (15秒)...")
    time.sleep(15)
    
    # 3. 检查服务器进程是否还活着
    if server_process.poll() is not None:
        print(f"❌ 服务器进程已退出 (Exit Code: {server_process.returncode})")
        print("   请检查上方的 [Server] 日志")
        return

    # 4. 启动 Cloudflare 隧道
    if not os.path.exists("./cloudflared"):
        print("⚠️ 未找到 cloudflared,正在下载...")
        subprocess.run("wget -q -O cloudflared https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 && chmod +x cloudflared", shell=True)

    print("\n🌐 启动 Cloudflare 隧道...")
    # 注意:cloudflared 默认把日志输出到 stderr,而不是 stdout
    tunnel_process = subprocess.Popen(
        ["./cloudflared", "tunnel", "--url", "http://localhost:8000", "--no-autoupdate"],
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT, # 将 stderr 合并到 stdout
        text=True,
        bufsize=1
    )

    # 5. 实时读取隧道输出,提取链接
    print("🔍 寻找公网链接中...")
    try:
        while True:
            line = tunnel_process.stdout.readline()
            if not line:
                break
            
            # 打印隧道日志
            # print(f"[Tunnel] {line.strip()}")
            
            if "trycloudflare.com" in line:
                import re
                url_match = re.search(r"https?://[\w\.-]+trycloudflare\.com", line)
                if url_match:
                    print("\n" + "="*60)
                    print("🎉 成功建立隧道!")
                    print(f"👉 公网访问地址: {url_match.group(0)}")
                    print("="*60 + "\n")
                    # 找到链接后,我们不仅不退出,还要跳出读取循环进入纯等待模式
                    # 否则继续读取可能会阻塞或读到 EOF 导致退出
                    break
            
            # 检查服务器是否还在运行
            if server_process.poll() is not None:
                print("❌ 警告:服务器进程意外退出!")
                break
        
        # 循环结束后,保持主线程存活
        print("ℹ️ 服务已就绪,主线程进入保活模式 (按 Stop 停止)...")
        while True:
            # 持续监控子进程状态
            if server_process.poll() is not None:
                print("❌ 警告:服务器进程意外退出!")
                break
            if tunnel_process.poll() is not None:
                print("❌ 警告:隧道进程意外退出!")
                break
            time.sleep(1)
                
    except KeyboardInterrupt:
        print("正在停止服务...")
        server_process.terminate()
        tunnel_process.terminate()

if __name__ == "__main__":
    run_all_in_one()