From fb22c57717307466c7365e8a0e85ff0afa767493 Mon Sep 17 00:00:00 2001 From: baoweise-bot Date: Sun, 21 Jun 2026 17:00:12 +0800 Subject: [PATCH] Show node maintenance and testing progress --- vpngate_manager.py | 131 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 112 insertions(+), 19 deletions(-) diff --git a/vpngate_manager.py b/vpngate_manager.py index 4f61c2f..deb13ea 100644 --- a/vpngate_manager.py +++ b/vpngate_manager.py @@ -355,6 +355,7 @@ def get_state() -> dict[str, Any]: state.pop("password", None) state["active_openvpn_node_id"] = active_openvpn_node_id state["is_connecting"] = is_connecting + state["maintenance_running"] = maintenance_lock.locked() state.setdefault("api_url", API_URL) state.setdefault("target_valid_nodes", TARGET_VALID_NODES) state.setdefault("fetch_interval_seconds", FETCH_INTERVAL_SECONDS) @@ -1162,7 +1163,7 @@ def sort_all_nodes(nodes: list[dict[str, Any]]) -> list[dict[str, Any]]: ) ) untested_nodes = sorted( - [n for n in nodes if n.get("probe_status") == "not_checked" and not n.get("active")], + [n for n in nodes if n.get("probe_status") in ("not_checked", "testing") and not n.get("active")], key=lambda n: (-parse_int(n.get("score")), parse_int(n.get("ping"))) ) unavailable_nodes = sorted( @@ -1405,6 +1406,13 @@ def test_multiple_nodes(node_ids: list[str]) -> list[dict[str, Any]]: with lock: nodes = read_nodes() to_test = [n for n in nodes if n.get("id") in node_ids] + now = time.time() + for n in nodes: + if n.get("id") in node_ids and not n.get("active") and n.get("probe_status") != "unavailable": + n["probe_status"] = "testing" + n["probe_message"] = "正在检测节点连通性..." + n["probed_at"] = now + write_json(NODES_FILE, sort_all_nodes(nodes)) def test_worker(args: tuple[int, dict[str, Any]]) -> dict[str, Any]: idx, n_info = args @@ -1482,6 +1490,13 @@ def test_multiple_nodes(node_ids: list[str]) -> list[dict[str, Any]]: "probe_message": f"Test exception: {e}", "latency_ms": 0 } + with lock: + current_nodes = read_nodes() + for n in current_nodes: + if n.get("id") == nid: + n.update(updated_nodes_map[nid]) + break + write_json(NODES_FILE, sort_all_nodes(current_nodes)) # 批量查询并丰富可用节点的地理及 ISP 信息,防止并发时被定位 API 接口限流 successful_nodes = [res for res in updated_nodes_map.values() if res.get("probe_status") == "available"] @@ -1746,9 +1761,14 @@ def maintain_valid_nodes(force: bool = False) -> str: return "没有拉取到新节点" with lock: + current_nodes = read_nodes() + current_by_id = { + str(n.get("id")): n + for n in current_nodes + if n.get("id") + } active_node = None if active_openvpn_node_id: - current_nodes = read_nodes() active_node = next((n for n in current_nodes if n.get("id") == active_openvpn_node_id), None) merged: list[dict[str, Any]] = [] @@ -1760,6 +1780,22 @@ def maintain_valid_nodes(force: bool = False) -> str: for cand in candidates: if cand["id"] not in seen_ids: + previous = current_by_id.get(str(cand["id"])) + if previous: + for key in [ + "probe_status", + "probe_message", + "latency_ms", + "probed_at", + "owner", + "asn", + "as_name", + "location", + "ip_type", + "quality", + ]: + if previous.get(key) not in (None, ""): + cand[key] = previous.get(key) merged.append(cand) seen_ids.add(cand["id"]) @@ -2817,6 +2853,12 @@ INDEX_HTML = r""" border-color: rgba(245, 158, 11, 0.2); } + .testing { + background: rgba(59, 130, 246, 0.12); + color: #93c5fd; + border-color: rgba(59, 130, 246, 0.24); + } + .current-badge { background: rgba(99, 102, 241, 0.15); color: #818cf8; @@ -3171,6 +3213,7 @@ INDEX_HTML = r"""