diff --git a/vpngate_manager.py b/vpngate_manager.py index ee703a2..1937ba5 100644 --- a/vpngate_manager.py +++ b/vpngate_manager.py @@ -218,7 +218,7 @@ def get_state() -> dict[str, Any]: state["username"] = ui_cfg.get("username", "admin") state["port"] = ui_cfg.get("port", 8787) state["secret_path"] = ui_cfg.get("secret_path", "EJsW2EeBo9lY") - state["enable_force_country"] = ui_cfg.get("enable_force_country", False) + state["routing_mode"] = ui_cfg.get("routing_mode", "auto") state["force_country"] = ui_cfg.get("force_country", "") return state @@ -775,20 +775,33 @@ def auto_switch_node(attempt: int = 0) -> None: print("[自动切换] 连续切换失败已达 3 次,停止切换以防止主线程死锁,将在后台重新加载节点...", flush=True) return + ui_cfg = load_ui_config() + routing_mode = ui_cfg.get("routing_mode", "auto") + target_country = ui_cfg.get("force_country", "") + + if routing_mode == "fixed_ip": + print("[自动切换] 当前处于固定 IP 模式,不进行自动切换。", flush=True) + if active_openvpn_node_id: + if not active_openvpn_running(): + print(f"[自动切换] 固定 IP 模式检测到连接已断开,尝试重新连接原节点: {active_openvpn_node_id}", flush=True) + def reconnect_bg(): + try: + connect_node(active_openvpn_node_id) + except Exception as e: + print(f"[自动切换] 重新连接固定节点失败: {e}", flush=True) + threading.Thread(target=reconnect_bg, daemon=True).start() + return + # Find the next best available node with lock: nodes = read_json(NODES_FILE, []) - ui_cfg = load_ui_config() - enable_force = ui_cfg.get("enable_force_country", False) - target_country = ui_cfg.get("force_country", "") - candidates = [ n for n in nodes if n.get("probe_status") == "available" and not n.get("active") ] - if enable_force and target_country: + if routing_mode == "fixed_region" and target_country: candidates = [n for n in candidates if n.get("country") == target_country] candidates.sort(key=lambda n: (parse_int(n.get("latency_ms")) or 999999, -parse_int(n.get("score")))) @@ -807,6 +820,8 @@ def auto_switch_node(attempt: int = 0) -> None: auto_switch_node(attempt + 1) else: msg = "没有可用的备选节点,将自动断开并清理当前连接状态,同时在后台异步获取新节点..." + if routing_mode == "fixed_region" and target_country: + msg = f"没有可用的【{target_country}】备选节点,已断开连接,将在后台持续尝试获取新节点..." print(f"[自动切换] {msg}", flush=True) log_to_json("WARNING", "VPN", msg) stop_active_openvpn() @@ -815,7 +830,7 @@ def auto_switch_node(attempt: int = 0) -> None: for item in nodes: item["active"] = False write_json(NODES_FILE, nodes) - set_state(active_openvpn_node_id="", last_check_message="没有可用的备选节点,已断开") + set_state(active_openvpn_node_id="", last_check_message=msg) def bg_fetch_and_switch(): try: @@ -991,7 +1006,15 @@ def maintain_valid_nodes(force: bool = False) -> str: # Test the first 10 non-active nodes from the new list with lock: current_nodes = read_json(NODES_FILE, []) - to_test = [n for n in current_nodes if not n.get("active")][:10] + ui_cfg = load_ui_config() + routing_mode = ui_cfg.get("routing_mode", "auto") + target_country = ui_cfg.get("force_country", "") + + if routing_mode == "fixed_region" and target_country: + to_test = [n for n in current_nodes if not n.get("active") and n.get("country") == target_country][:10] + else: + to_test = [n for n in current_nodes if not n.get("active")][:10] + to_test_ids = [n["id"] for n in to_test] print(f"[维护线程] 正在检测新获取列表的前 10 个节点: {to_test_ids}", flush=True) @@ -1004,15 +1027,19 @@ def maintain_valid_nodes(force: bool = False) -> str: merged = read_json(NODES_FILE, []) if not active_openvpn_running(): ui_cfg = load_ui_config() - enable_force = ui_cfg.get("enable_force_country", False) + routing_mode = ui_cfg.get("routing_mode", "auto") target_country = ui_cfg.get("force_country", "") - available_candidates = [n for n in merged if n.get("probe_status") == "available"] - if enable_force and target_country: - available_candidates = [n for n in available_candidates if n.get("country") == target_country] - - if available_candidates: - auto_switch_node() + if routing_mode == "fixed_ip": + if active_openvpn_node_id: + auto_switch_node() + else: + available_candidates = [n for n in merged if n.get("probe_status") == "available"] + if routing_mode == "fixed_region" and target_country: + available_candidates = [n for n in available_candidates if n.get("country") == target_country] + + if available_candidates: + auto_switch_node() valid_nodes_count = len([n for n in merged if n.get("probe_status") == "available"]) message = f"Fetched {len(candidates)} nodes. Tested first 10 nodes." @@ -2325,22 +2352,26 @@ INDEX_HTML = r"""