2025-11-17 17:13:22 +08:00
|
|
|
|
import asyncio
|
2025-11-25 18:13:02 +08:00
|
|
|
|
import ctypes
|
2025-10-23 18:53:22 +08:00
|
|
|
|
# ===== Main.py 顶部放置(所有 import 之前)=====
|
2025-08-15 20:04:59 +08:00
|
|
|
|
import os
|
|
|
|
|
|
import sys
|
|
|
|
|
|
from pathlib import Path
|
2025-11-17 17:13:22 +08:00
|
|
|
|
from hypercorn.asyncio import serve
|
|
|
|
|
|
from hypercorn.config import Config
|
2025-11-19 17:23:41 +08:00
|
|
|
|
|
2025-11-17 17:13:22 +08:00
|
|
|
|
from Module.DeviceInfo import DeviceInfo
|
|
|
|
|
|
from Module.FlaskSubprocessManager import FlaskSubprocessManager
|
2025-11-19 17:23:41 +08:00
|
|
|
|
from Utils.AiUtils import AiUtils
|
2025-11-17 17:13:22 +08:00
|
|
|
|
from Utils.DevDiskImageDeployer import DevDiskImageDeployer
|
2025-11-19 17:23:41 +08:00
|
|
|
|
from Utils.LogManager import LogManager
|
2025-10-29 16:56:34 +08:00
|
|
|
|
|
2025-08-15 20:04:59 +08:00
|
|
|
|
# 确定 exe 或 py 文件所在目录
|
|
|
|
|
|
BASE = Path(getattr(sys, 'frozen', False) and sys.executable or __file__).resolve().parent
|
|
|
|
|
|
LOG_DIR = BASE / "log"
|
|
|
|
|
|
LOG_DIR.mkdir(exist_ok=True) # 确保 log 目录存在
|
|
|
|
|
|
|
|
|
|
|
|
print(f"日志目录: {LOG_DIR}")
|
|
|
|
|
|
|
|
|
|
|
|
def _run_flask_role():
|
2025-11-07 16:31:18 +08:00
|
|
|
|
from Module.FlaskService import get_app, bootstrap_server_side_effects
|
2025-11-06 21:50:28 +08:00
|
|
|
|
print("Flask Pid:", os.getpid())
|
2025-10-29 16:56:34 +08:00
|
|
|
|
port = int(os.getenv("FLASK_COMM_PORT", "34566")) # 固定端口的兜底仍是 34567
|
2025-11-07 16:31:18 +08:00
|
|
|
|
app = get_app()
|
2025-11-17 17:13:22 +08:00
|
|
|
|
flaskPort = port + 1
|
|
|
|
|
|
AiUtils.flask_port_free(flaskPort)
|
2025-11-07 16:31:18 +08:00
|
|
|
|
bootstrap_server_side_effects()
|
2025-11-17 17:13:22 +08:00
|
|
|
|
|
2025-11-17 19:42:27 +08:00
|
|
|
|
# ==== 关键:统一获取 resources 目录 ====
|
|
|
|
|
|
if "__compiled__" in globals():
|
|
|
|
|
|
# 被 Nuitka 编译后的 exe 运行时
|
|
|
|
|
|
base_dir = os.path.dirname(sys.executable) # exe 所在目录
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 开发环境,直接跑 .py
|
|
|
|
|
|
cur_file = os.path.abspath(__file__) # Module/Main.py 所在目录
|
|
|
|
|
|
base_dir = os.path.dirname(os.path.dirname(cur_file)) # 回到项目根 iOSAi
|
|
|
|
|
|
|
|
|
|
|
|
resource_dir = os.path.join(base_dir, "resources")
|
|
|
|
|
|
|
2025-11-18 22:09:19 +08:00
|
|
|
|
# Hypercorn 配置
|
2025-11-17 17:13:22 +08:00
|
|
|
|
config = Config()
|
|
|
|
|
|
config.bind = [f"0.0.0.0:{flaskPort}"]
|
2025-11-18 22:09:19 +08:00
|
|
|
|
config.certfile = os.path.join(resource_dir, "server.crt")
|
|
|
|
|
|
config.keyfile = os.path.join(resource_dir, "server.key")
|
2025-11-25 18:13:02 +08:00
|
|
|
|
config.alpn_protocols = ["h2", "http/1.1"]
|
2025-11-18 22:09:19 +08:00
|
|
|
|
config.workers = 6 # 你机器 4GB → 推荐 3~4 个 worker
|
2025-11-17 17:13:22 +08:00
|
|
|
|
|
2025-11-18 22:09:19 +08:00
|
|
|
|
# 直接跑 Quart(ASGI 原生,不再用 WsgiToAsgi)
|
|
|
|
|
|
asyncio.run(serve(app, config))
|
2025-08-15 20:04:59 +08:00
|
|
|
|
|
|
|
|
|
|
if "--role=flask" in sys.argv:
|
|
|
|
|
|
_run_flask_role()
|
|
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
2025-11-25 18:13:02 +08:00
|
|
|
|
def _ensure_wintun_installed():
|
|
|
|
|
|
"""
|
|
|
|
|
|
确保 wintun.dll 已经在系统目录里:
|
|
|
|
|
|
- 优先从当前目录的 resources 中找 wintun.dll
|
|
|
|
|
|
- 如果 System32 中没有,就复制过去(需要管理员权限)
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# ==== 关键:统一获取 resources 目录 ====
|
|
|
|
|
|
if "__compiled__" in globals():
|
|
|
|
|
|
# Nuitka 编译后的 exe
|
|
|
|
|
|
base_dir = os.path.dirname(sys.executable) # exe 所在目录
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 开发环境运行 .py
|
|
|
|
|
|
cur_file = os.path.abspath(__file__) # Module/Main.py 所在目录
|
|
|
|
|
|
base_dir = os.path.dirname(os.path.dirname(cur_file)) # 回到 iOSAi 根目录
|
|
|
|
|
|
|
|
|
|
|
|
resource_dir = os.path.join(base_dir, "resources")
|
|
|
|
|
|
src = os.path.join(resource_dir, "wintun.dll")
|
|
|
|
|
|
|
|
|
|
|
|
# 1. 检查源文件是否存在
|
|
|
|
|
|
if not os.path.exists(src):
|
|
|
|
|
|
print(f"[wintun] 未找到资源文件: {src}")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 系统 System32 目录
|
|
|
|
|
|
windir = os.environ.get("WINDIR", r"C:\Windows")
|
|
|
|
|
|
system32 = Path(windir) / "System32"
|
|
|
|
|
|
dst = system32 / "wintun.dll"
|
|
|
|
|
|
|
|
|
|
|
|
# 3. System32 中已经存在则无需复制
|
|
|
|
|
|
if dst.exists():
|
|
|
|
|
|
print(f"[wintun] System32 中已存在: {dst}")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 4. 执行复制
|
|
|
|
|
|
import shutil
|
|
|
|
|
|
print(f"[wintun] 复制 {src} -> {dst}")
|
|
|
|
|
|
shutil.copy2(src, dst)
|
|
|
|
|
|
print("[wintun] 复制完成")
|
|
|
|
|
|
|
|
|
|
|
|
except PermissionError as e:
|
|
|
|
|
|
print(f"[wintun] 权限不足,无法写入 System32:{e}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"[wintun] 安装 wintun.dll 时异常: {e}")
|
|
|
|
|
|
|
2025-10-29 16:56:34 +08:00
|
|
|
|
# 启动锁
|
2025-09-24 16:32:05 +08:00
|
|
|
|
def main(arg):
|
2025-10-29 16:56:34 +08:00
|
|
|
|
if len(arg) != 2 or arg[1] != "iosai":
|
2025-09-24 16:32:05 +08:00
|
|
|
|
sys.exit(0)
|
2025-09-04 20:47:14 +08:00
|
|
|
|
|
2025-11-25 18:13:02 +08:00
|
|
|
|
# 判断是否为管理员身份原型
|
|
|
|
|
|
def isAdministrator():
|
|
|
|
|
|
"""
|
|
|
|
|
|
检测当前进程是否具有管理员权限。
|
|
|
|
|
|
- Windows 下调用 Shell32.IsUserAnAdmin()
|
|
|
|
|
|
- 如果不是管理员,直接退出程序
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
is_admin = ctypes.windll.shell32.IsUserAnAdmin()
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
# 非 Windows 或无法判断的情况,一律按“非管理员”处理
|
|
|
|
|
|
is_admin = False
|
|
|
|
|
|
|
|
|
|
|
|
if not is_admin:
|
|
|
|
|
|
print("[ERROR] 需要以管理员身份运行本程序!")
|
|
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
2025-08-11 22:06:48 +08:00
|
|
|
|
# 项目入口
|
2025-08-01 13:43:51 +08:00
|
|
|
|
if __name__ == "__main__":
|
2025-11-25 18:13:02 +08:00
|
|
|
|
# 检测是否有管理员身份权限
|
|
|
|
|
|
isAdministrator()
|
|
|
|
|
|
|
|
|
|
|
|
# 检测程序合法性
|
|
|
|
|
|
main(sys.argv)
|
2025-09-04 20:47:14 +08:00
|
|
|
|
|
2025-10-29 16:56:34 +08:00
|
|
|
|
# 清空日志
|
2025-11-07 14:31:07 +08:00
|
|
|
|
LogManager.clearLogs()
|
2025-10-29 16:56:34 +08:00
|
|
|
|
|
2025-08-28 16:02:28 +08:00
|
|
|
|
# 添加iOS开发包到电脑上
|
2025-08-28 15:46:17 +08:00
|
|
|
|
deployer = DevDiskImageDeployer(verbose=True)
|
|
|
|
|
|
deployer.deploy_all()
|
2025-08-06 22:11:33 +08:00
|
|
|
|
|
2025-11-25 18:13:02 +08:00
|
|
|
|
# 复制wintun.dll到system32目录下
|
|
|
|
|
|
_ensure_wintun_installed()
|
|
|
|
|
|
|
2025-08-15 20:04:59 +08:00
|
|
|
|
# 启动 Flask 子进程
|
2025-08-01 13:43:51 +08:00
|
|
|
|
manager = FlaskSubprocessManager.get_instance()
|
|
|
|
|
|
manager.start()
|
|
|
|
|
|
|
2025-08-15 20:04:59 +08:00
|
|
|
|
# 设备监听(即使失败/很快返回,也不会导致主进程退出)
|
2025-09-22 14:36:05 +08:00
|
|
|
|
try:
|
|
|
|
|
|
info = DeviceInfo()
|
|
|
|
|
|
info.listen()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print("[WARN] Device listener not running:", e)
|
2025-09-19 14:30:39 +08:00
|
|
|
|
|
2025-08-15 20:04:59 +08:00
|
|
|
|
# === 保活:阻塞主线程,直到收到 Ctrl+C/关闭 ===
|
|
|
|
|
|
import threading, time, signal
|
|
|
|
|
|
stop = threading.Event()
|
|
|
|
|
|
|
|
|
|
|
|
def _handle(_sig, _frm):
|
|
|
|
|
|
stop.set()
|
2025-09-24 16:32:05 +08:00
|
|
|
|
|
2025-08-15 20:04:59 +08:00
|
|
|
|
# Windows 上 SIGINT/SIGTERM 都可以拦到
|
|
|
|
|
|
try:
|
|
|
|
|
|
signal.signal(signal.SIGINT, _handle)
|
|
|
|
|
|
signal.signal(signal.SIGTERM, _handle)
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass # 某些环境可能不支持,忽略
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
while not stop.is_set():
|
|
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
finally:
|
|
|
|
|
|
# 进程退出前记得把子进程关掉
|
|
|
|
|
|
manager.stop()
|
2025-11-07 16:31:18 +08:00
|
|
|
|
|