feat: optimize node fetching with conditional retries and implement post-connection proxy health checks and UI status updates

This commit is contained in:
baoweise
2026-05-28 23:43:19 +08:00
parent 9ca777488c
commit 09d1d6b02b
2 changed files with 44 additions and 11 deletions
+13 -1
View File
@@ -359,9 +359,18 @@ def print_status():
connecting_msg = state.get('last_check_message') or '正在建立加密隧道并验证路由规则...'
print_line(format_line("节点状态", f"{yellow}{connecting_msg}{reset}"))
elif active_ip:
print_line(format_line("节点 IP", active_ip))
proxy_ip = state.get("proxy_ip", "-")
proxy_latency = state.get("proxy_latency_ms", 0)
proxy_ok = state.get("proxy_ok", False)
print_line(format_line("节点 IP (入口)", active_ip))
print_line(format_line("节点地区", active_loc))
print_line(format_line("节点延迟 (直连测试)", latency))
if proxy_ok and proxy_ip and proxy_ip != "-":
print_line(format_line("出口 IP (出站)", proxy_ip))
print_line(format_line("本地代理延迟", f"{proxy_latency} ms" if proxy_latency else "检测中..."))
else:
print_line(format_line("出口 IP (出站)", f"{red}[检测中/未就绪]{reset}"))
else:
print_line(format_line("节点状态", "无活动连接"))
print_line()
@@ -652,6 +661,9 @@ def get_status_state():
state.get("active_openvpn_node_id", ""),
state.get("last_check_message", ""),
state.get("active_node_latency", ""),
state.get("proxy_ip", "-"),
state.get("proxy_latency_ms", 0),
state.get("proxy_ok", False),
check_port_listening(7928),
check_service_active("aimilivpn.service"),
check_openvpn_process(),
+31 -10
View File
@@ -239,7 +239,7 @@ def fetch_api_text() -> str:
"Accept": "text/plain,*/*",
},
)
with urllib.request.urlopen(request, timeout=45) as response:
with urllib.request.urlopen(request, timeout=12) as response:
return response.read().decode("utf-8", errors="replace")
def parse_vpngate_rows(text: str) -> list[dict[str, str]]:
@@ -299,8 +299,12 @@ def fetch_candidates() -> list[dict[str, Any]]:
candidates: list[dict[str, Any]] = []
seen_ips = set()
log_to_json("INFO", "Main", "开始拉取官方 API 节点列表...")
for i in range(3):
# 检查本地是否有节点缓存,以确定最大重试尝试次数
has_cache = len(cached_nodes()) > 0
max_attempts = 1 if has_cache else 2
log_to_json("INFO", "Main", f"开始拉取官方 API 节点列表 (最大尝试次数: {max_attempts})...")
for i in range(max_attempts):
if i > 0:
time.sleep(1.5)
try:
@@ -320,7 +324,7 @@ def fetch_candidates() -> list[dict[str, Any]]:
except Exception as e:
print(f"[fetch_candidates] Fetch {i+1} failed: {e}", flush=True)
log_to_json("WARNING", "Main", f"{i+1} 次拉取 API 节点失败: {e}")
if i == 0 and not candidates:
if i == max_attempts - 1 and not candidates:
log_to_json("ERROR", "Main", f"获取官方 API 节点失败: {e}")
raise
@@ -819,6 +823,7 @@ def connect_node(node_id: str) -> str:
print("[连接] 正在建立其他连接中,跳过此请求", flush=True)
return "Already connecting"
is_connecting = True
active_openvpn_node_id = node_id
set_state(active_openvpn_node_id=node_id, is_connecting=True, active_node_latency="正在连接", last_check_message="正在初始化连接配置...")
try:
@@ -854,6 +859,8 @@ def connect_node(node_id: str) -> str:
write_json(NODES_FILE, nodes)
log_to_json("ERROR", "VPN", f"连接节点 {node_id} 失败: {message}")
set_state(active_openvpn_node_id="", is_connecting=False, active_node_latency="无活动连接", last_check_message=f"连接失败: {message}")
with lock:
active_openvpn_node_id = ""
raise RuntimeError(message)
active_openvpn_process = process
@@ -882,6 +889,24 @@ def connect_node(node_id: str) -> str:
if item["active"]:
item["probe_message"] = f"Active node. HTTP proxy: http://{LOCAL_PROXY_HOST}:{LOCAL_PROXY_PORT}"
write_json(NODES_FILE, nodes)
set_state(last_check_message="正在测试本地代理出站联通性与出口 IP...")
res = check_proxy_health()
if res["ok"]:
set_state(
proxy_ok=True,
proxy_ip=res["ip"],
proxy_latency_ms=res["latency_ms"],
proxy_error=""
)
else:
set_state(
proxy_ok=False,
proxy_ip="-",
proxy_latency_ms=0,
proxy_error=res.get("error", "未知错误")
)
latency_str = f"{last_active_latency} ms" if last_active_latency > 0 else "检测超时"
set_state(active_openvpn_node_id=node_id, is_connecting=False, last_check_message=f"Connected {node_id}", active_node_latency=latency_str)
log_to_json("INFO", "VPN", f"节点 {node_id} 连接成功,出口网卡 tun0 已启用")
@@ -915,12 +940,8 @@ def maintain_valid_nodes(force: bool = False) -> str:
candidates = fetch_candidates()
except Exception as exc:
vpn_utils.check_and_fix_dns()
try:
set_state(is_connecting=True, last_check_message="解析失败重试:正在拉取最新的免费 VPN 节点列表...")
candidates = fetch_candidates()
except Exception as exc2:
set_state(last_fetch_at=time.time(), last_fetch_status="error", last_fetch_message=str(exc2))
candidates = []
set_state(last_fetch_at=time.time(), last_fetch_status="error", last_fetch_message=str(exc))
candidates = []
if not candidates:
is_connecting = False