From 034885d810eff649f13300d948e8a366a3224508 Mon Sep 17 00:00:00 2001 From: wonfen Date: Sat, 21 Jun 2025 21:38:42 +0800 Subject: [PATCH] feat: introduce event-driven proxy manager and optimize proxy config updates --- src-tauri/src/cmd/network.rs | 21 +- src-tauri/src/core/event_driven_proxy.rs | 603 ++++++++++++++++++ src-tauri/src/core/mod.rs | 3 +- src-tauri/src/core/sysopt.rs | 201 +----- .../setting/mods/sysproxy-viewer.tsx | 187 +++--- src/pages/_layout.tsx | 14 +- src/services/cmds.ts | 20 +- 7 files changed, 776 insertions(+), 273 deletions(-) create mode 100644 src-tauri/src/core/event_driven_proxy.rs diff --git a/src-tauri/src/cmd/network.rs b/src-tauri/src/cmd/network.rs index 7b7e469d..23a9ded6 100644 --- a/src-tauri/src/cmd/network.rs +++ b/src-tauri/src/cmd/network.rs @@ -1,8 +1,9 @@ use super::CmdResult; +use crate::core::EventDrivenProxyManager; use crate::wrap_err; use network_interface::NetworkInterface; use serde_yaml::Mapping; -use sysproxy::{Autoproxy, Sysproxy}; +use sysproxy::Sysproxy; use tokio::task::spawn_blocking; /// get the system proxy @@ -24,18 +25,24 @@ pub async fn get_sys_proxy() -> CmdResult { Ok(map) } -/// get the system proxy +/// 获取自动代理配置 #[tauri::command] pub async fn get_auto_proxy() -> CmdResult { - let current = spawn_blocking(Autoproxy::get_auto_proxy) - .await - .map_err(|e| format!("Failed to spawn blocking task for autoproxy: {}", e))? - .map_err(|e| format!("Failed to get auto proxy: {}", e))?; + log::debug!(target: "app", "开始获取自动代理配置(事件驱动)"); + + let proxy_manager = EventDrivenProxyManager::global(); + + let current = proxy_manager.get_auto_proxy_cached(); + // 异步请求更新,立即返回缓存数据 + tokio::spawn(async move { + let _ = proxy_manager.get_auto_proxy_async().await; + }); let mut map = Mapping::new(); map.insert("enable".into(), current.enable.into()); - map.insert("url".into(), current.url.into()); + map.insert("url".into(), current.url.clone().into()); + log::debug!(target: "app", "返回自动代理配置(缓存): enable={}, url={}", current.enable, current.url); Ok(map) } diff --git a/src-tauri/src/core/event_driven_proxy.rs b/src-tauri/src/core/event_driven_proxy.rs new file mode 100644 index 00000000..e7a22c66 --- /dev/null +++ b/src-tauri/src/core/event_driven_proxy.rs @@ -0,0 +1,603 @@ +use parking_lot::RwLock; +use std::sync::Arc; +use tokio::sync::{mpsc, oneshot}; +use tokio::time::{sleep, timeout, Duration}; + +use crate::config::{Config, IVerge}; +use crate::logging_error; +use crate::utils::logging::Type; +use once_cell::sync::Lazy; +use sysproxy::{Autoproxy, Sysproxy}; + +#[derive(Debug, Clone)] +pub enum ProxyEvent { + /// 配置变更事件 + ConfigChanged, + /// 强制检查代理状态 + #[allow(dead_code)] + ForceCheck, + /// 启用系统代理 + #[allow(dead_code)] + EnableProxy, + /// 禁用系统代理 + #[allow(dead_code)] + DisableProxy, + /// 切换到PAC模式 + #[allow(dead_code)] + SwitchToPac, + /// 切换到HTTP代理模式 + #[allow(dead_code)] + SwitchToHttp, + /// 应用启动事件 + AppStarted, + /// 应用关闭事件 + #[allow(dead_code)] + AppStopping, +} + +#[derive(Debug, Clone)] +pub struct ProxyState { + pub sys_enabled: bool, + pub pac_enabled: bool, + pub auto_proxy: Autoproxy, + pub sys_proxy: Sysproxy, + pub last_updated: std::time::Instant, + pub is_healthy: bool, +} + +impl Default for ProxyState { + fn default() -> Self { + Self { + sys_enabled: false, + pac_enabled: false, + auto_proxy: Autoproxy { + enable: false, + url: "".to_string(), + }, + sys_proxy: Sysproxy { + enable: false, + host: "127.0.0.1".to_string(), + port: 7890, + bypass: "".to_string(), + }, + last_updated: std::time::Instant::now(), + is_healthy: true, + } + } +} + +pub struct EventDrivenProxyManager { + state: Arc>, + event_sender: mpsc::UnboundedSender, + query_sender: mpsc::UnboundedSender, +} + +#[derive(Debug)] +struct QueryRequest { + response_tx: oneshot::Sender, +} + +// 配置结构体移到外部 +struct ProxyConfig { + sys_enabled: bool, + pac_enabled: bool, + guard_enabled: bool, +} + +static PROXY_MANAGER: Lazy = Lazy::new(|| EventDrivenProxyManager::new()); + +impl EventDrivenProxyManager { + pub fn global() -> &'static EventDrivenProxyManager { + &PROXY_MANAGER + } + + fn new() -> Self { + let state = Arc::new(RwLock::new(ProxyState::default())); + let (event_tx, event_rx) = mpsc::unbounded_channel(); + let (query_tx, query_rx) = mpsc::unbounded_channel(); + + Self::start_event_loop(state.clone(), event_rx, query_rx); + + Self { + state, + event_sender: event_tx, + query_sender: query_tx, + } + } + + /// 获取自动代理配置(缓存) + pub fn get_auto_proxy_cached(&self) -> Autoproxy { + self.state.read().auto_proxy.clone() + } + + /// 异步获取最新的自动代理配置 + pub async fn get_auto_proxy_async(&self) -> Autoproxy { + let (tx, rx) = oneshot::channel(); + let query = QueryRequest { response_tx: tx }; + + if self.query_sender.send(query).is_err() { + log::error!(target: "app", "发送查询请求失败,返回缓存数据"); + return self.get_auto_proxy_cached(); + } + + match timeout(Duration::from_secs(5), rx).await { + Ok(Ok(result)) => result, + _ => { + log::warn!(target: "app", "查询超时,返回缓存数据"); + self.get_auto_proxy_cached() + } + } + } + + /// 通知配置变更 + pub fn notify_config_changed(&self) { + self.send_event(ProxyEvent::ConfigChanged); + } + + /// 通知应用启动 + pub fn notify_app_started(&self) { + self.send_event(ProxyEvent::AppStarted); + } + + /// 通知应用即将关闭 + #[allow(dead_code)] + pub fn notify_app_stopping(&self) { + self.send_event(ProxyEvent::AppStopping); + } + + /// 启用系统代理 + #[allow(dead_code)] + pub fn enable_proxy(&self) { + self.send_event(ProxyEvent::EnableProxy); + } + + /// 禁用系统代理 + #[allow(dead_code)] + pub fn disable_proxy(&self) { + self.send_event(ProxyEvent::DisableProxy); + } + + /// 强制检查代理状态 + #[allow(dead_code)] + pub fn force_check(&self) { + self.send_event(ProxyEvent::ForceCheck); + } + + fn send_event(&self, event: ProxyEvent) { + if let Err(e) = self.event_sender.send(event) { + log::error!(target: "app", "发送代理事件失败: {}", e); + } + } + + fn start_event_loop( + state: Arc>, + mut event_rx: mpsc::UnboundedReceiver, + mut query_rx: mpsc::UnboundedReceiver, + ) { + tokio::spawn(async move { + log::info!(target: "app", "事件驱动代理管理器启动"); + + loop { + tokio::select! { + event = event_rx.recv() => { + match event { + Some(event) => { + log::debug!(target: "app", "处理代理事件: {:?}", event); + Self::handle_event(&state, event).await; + } + None => { + log::info!(target: "app", "事件通道关闭,代理管理器停止"); + break; + } + } + } + query = query_rx.recv() => { + match query { + Some(query) => { + let result = Self::handle_query(&state).await; + let _ = query.response_tx.send(result); + } + None => { + log::info!(target: "app", "查询通道关闭"); + break; + } + } + } + } + } + }); + } + + async fn handle_event(state: &Arc>, event: ProxyEvent) { + match event { + ProxyEvent::ConfigChanged | ProxyEvent::ForceCheck => { + Self::update_proxy_config(state).await; + } + ProxyEvent::EnableProxy => { + Self::enable_system_proxy(state).await; + } + ProxyEvent::DisableProxy => { + Self::disable_system_proxy(state).await; + } + ProxyEvent::SwitchToPac => { + Self::switch_proxy_mode(state, true).await; + } + ProxyEvent::SwitchToHttp => { + Self::switch_proxy_mode(state, false).await; + } + ProxyEvent::AppStarted => { + Self::initialize_proxy_state(state).await; + } + ProxyEvent::AppStopping => { + log::info!(target: "app", "清理代理状态"); + } + } + } + + async fn handle_query(state: &Arc>) -> Autoproxy { + let auto_proxy = Self::get_auto_proxy_with_timeout().await; + + Self::update_state_timestamp(state, |s| { + s.auto_proxy = auto_proxy.clone(); + }); + + auto_proxy + } + + async fn initialize_proxy_state(state: &Arc>) { + log::info!(target: "app", "初始化代理状态"); + + let config = Self::get_proxy_config(); + let auto_proxy = Self::get_auto_proxy_with_timeout().await; + let sys_proxy = Self::get_sys_proxy_with_timeout().await; + + Self::update_state_timestamp(state, |s| { + s.sys_enabled = config.sys_enabled; + s.pac_enabled = config.pac_enabled; + s.auto_proxy = auto_proxy; + s.sys_proxy = sys_proxy; + s.is_healthy = true; + }); + + log::info!(target: "app", "代理状态初始化完成: sys={}, pac={}", config.sys_enabled, config.pac_enabled); + } + + async fn update_proxy_config(state: &Arc>) { + log::debug!(target: "app", "更新代理配置"); + + let config = Self::get_proxy_config(); + + Self::update_state_timestamp(state, |s| { + s.sys_enabled = config.sys_enabled; + s.pac_enabled = config.pac_enabled; + }); + + if config.guard_enabled && config.sys_enabled { + Self::check_and_restore_proxy(state).await; + } + } + + async fn check_and_restore_proxy(state: &Arc>) { + let (sys_enabled, pac_enabled) = { + let s = state.read(); + (s.sys_enabled, s.pac_enabled) + }; + + if !sys_enabled { + return; + } + + log::debug!(target: "app", "检查代理状态"); + + if pac_enabled { + Self::check_and_restore_pac_proxy(state).await; + } else { + Self::check_and_restore_sys_proxy(state).await; + } + } + + async fn check_and_restore_pac_proxy(state: &Arc>) { + let current = Self::get_auto_proxy_with_timeout().await; + let expected = Self::get_expected_pac_config(); + + Self::update_state_timestamp(state, |s| { + s.auto_proxy = current.clone(); + }); + + if !current.enable || current.url != expected.url { + log::info!(target: "app", "PAC代理设置异常,正在恢复..."); + Self::restore_pac_proxy(&expected.url).await; + + sleep(Duration::from_millis(500)).await; + let restored = Self::get_auto_proxy_with_timeout().await; + + Self::update_state_timestamp(state, |s| { + s.is_healthy = restored.enable && restored.url == expected.url; + s.auto_proxy = restored; + }); + } + } + + async fn check_and_restore_sys_proxy(state: &Arc>) { + let current = Self::get_sys_proxy_with_timeout().await; + let expected = Self::get_expected_sys_proxy(); + + Self::update_state_timestamp(state, |s| { + s.sys_proxy = current.clone(); + }); + + if !current.enable || current.host != expected.host || current.port != expected.port { + log::info!(target: "app", "系统代理设置异常,正在恢复..."); + Self::restore_sys_proxy(&expected).await; + + sleep(Duration::from_millis(500)).await; + let restored = Self::get_sys_proxy_with_timeout().await; + + Self::update_state_timestamp(state, |s| { + s.is_healthy = restored.enable + && restored.host == expected.host + && restored.port == expected.port; + s.sys_proxy = restored; + }); + } + } + + async fn enable_system_proxy(state: &Arc>) { + log::info!(target: "app", "启用系统代理"); + + let pac_enabled = state.read().pac_enabled; + + if pac_enabled { + let expected = Self::get_expected_pac_config(); + Self::restore_pac_proxy(&expected.url).await; + } else { + let expected = Self::get_expected_sys_proxy(); + Self::restore_sys_proxy(&expected).await; + } + + Self::check_and_restore_proxy(state).await; + } + + async fn disable_system_proxy(_state: &Arc>) { + log::info!(target: "app", "禁用系统代理"); + + #[cfg(not(target_os = "windows"))] + { + let disabled_sys = Sysproxy::default(); + let disabled_auto = Autoproxy::default(); + + logging_error!(Type::System, true, disabled_auto.set_auto_proxy()); + logging_error!(Type::System, true, disabled_sys.set_system_proxy()); + } + } + + async fn switch_proxy_mode(state: &Arc>, to_pac: bool) { + log::info!(target: "app", "切换到{}模式", if to_pac { "PAC" } else { "HTTP代理" }); + + if to_pac { + let disabled_sys = Sysproxy::default(); + logging_error!(Type::System, true, disabled_sys.set_system_proxy()); + + let expected = Self::get_expected_pac_config(); + Self::restore_pac_proxy(&expected.url).await; + } else { + let disabled_auto = Autoproxy::default(); + logging_error!(Type::System, true, disabled_auto.set_auto_proxy()); + + let expected = Self::get_expected_sys_proxy(); + Self::restore_sys_proxy(&expected).await; + } + + Self::update_state_timestamp(state, |s| s.pac_enabled = to_pac); + Self::check_and_restore_proxy(state).await; + } + + async fn get_auto_proxy_with_timeout() -> Autoproxy { + let result = timeout( + Duration::from_secs(2), + tokio::task::spawn_blocking(|| Autoproxy::get_auto_proxy()), + ) + .await; + + match result { + Ok(Ok(Ok(proxy))) => proxy, + Ok(Ok(Err(e))) => { + log::warn!(target: "app", "获取自动代理失败: {}", e); + Autoproxy { + enable: false, + url: "".to_string(), + } + } + Ok(Err(e)) => { + log::error!(target: "app", "spawn_blocking失败: {}", e); + Autoproxy { + enable: false, + url: "".to_string(), + } + } + Err(_) => { + log::warn!(target: "app", "获取自动代理超时"); + Autoproxy { + enable: false, + url: "".to_string(), + } + } + } + } + + async fn get_sys_proxy_with_timeout() -> Sysproxy { + let result = timeout( + Duration::from_secs(2), + tokio::task::spawn_blocking(|| Sysproxy::get_system_proxy()), + ) + .await; + + match result { + Ok(Ok(Ok(proxy))) => proxy, + _ => { + log::warn!(target: "app", "获取系统代理失败或超时"); + Sysproxy { + enable: false, + host: "127.0.0.1".to_string(), + port: 7890, + bypass: "".to_string(), + } + } + } + } + + // 统一的状态更新方法 + fn update_state_timestamp(state: &Arc>, update_fn: F) + where + F: FnOnce(&mut ProxyState), + { + let mut state_guard = state.write(); + update_fn(&mut state_guard); + state_guard.last_updated = std::time::Instant::now(); + } + + fn get_proxy_config() -> ProxyConfig { + let verge_config = Config::verge(); + let verge = verge_config.latest(); + ProxyConfig { + sys_enabled: verge.enable_system_proxy.unwrap_or(false), + pac_enabled: verge.proxy_auto_config.unwrap_or(false), + guard_enabled: verge.enable_proxy_guard.unwrap_or(false), + } + } + + fn get_expected_pac_config() -> Autoproxy { + let verge_config = Config::verge(); + let verge = verge_config.latest(); + let (proxy_host, pac_port) = ( + verge + .proxy_host + .clone() + .unwrap_or_else(|| "127.0.0.1".to_string()), + IVerge::get_singleton_port(), + ); + Autoproxy { + enable: true, + url: format!("http://{}:{}/commands/pac", proxy_host, pac_port), + } + } + + fn get_expected_sys_proxy() -> Sysproxy { + let verge_config = Config::verge(); + let verge = verge_config.latest(); + let port = verge + .verge_mixed_port + .unwrap_or(Config::clash().data().get_mixed_port()); + let proxy_host = verge + .proxy_host + .clone() + .unwrap_or_else(|| "127.0.0.1".to_string()); + + Sysproxy { + enable: true, + host: proxy_host, + port, + bypass: Self::get_bypass_config(), + } + } + + fn get_bypass_config() -> String { + let verge_config = Config::verge(); + let verge = verge_config.latest(); + let use_default = verge.use_default_bypass.unwrap_or(true); + let custom_bypass = verge.system_proxy_bypass.clone().unwrap_or_default(); + + #[cfg(target_os = "windows")] + let default_bypass = "localhost;127.*;192.168.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;"; + + #[cfg(target_os = "linux")] + let default_bypass = + "localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,172.29.0.0/16,::1"; + + #[cfg(target_os = "macos")] + let default_bypass = "127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,172.29.0.0/16,localhost,*.local,*.crashlytics.com,"; + + if custom_bypass.is_empty() { + default_bypass.to_string() + } else if use_default { + format!("{},{}", default_bypass, custom_bypass) + } else { + custom_bypass + } + } + + async fn restore_pac_proxy(expected_url: &str) { + #[cfg(not(target_os = "windows"))] + { + let new_autoproxy = Autoproxy { + enable: true, + url: expected_url.to_string(), + }; + logging_error!(Type::System, true, new_autoproxy.set_auto_proxy()); + } + + #[cfg(target_os = "windows")] + { + Self::execute_sysproxy_command(&["pac", expected_url]).await; + } + } + + async fn restore_sys_proxy(expected: &Sysproxy) { + #[cfg(not(target_os = "windows"))] + { + logging_error!(Type::System, true, expected.set_system_proxy()); + } + + #[cfg(target_os = "windows")] + { + let address = format!("{}:{}", expected.host, expected.port); + Self::execute_sysproxy_command(&["global", &address, &expected.bypass]).await; + } + } + + #[cfg(target_os = "windows")] + async fn execute_sysproxy_command(args: &[&str]) { + use crate::{core::handle::Handle, utils::dirs}; + use tauri_plugin_shell::ShellExt; + + let app_handle = match Handle::global().app_handle() { + Ok(handle) => handle, + Err(e) => { + log::error!(target: "app", "获取应用句柄失败: {}", e); + return; + } + }; + + let binary_path = match dirs::service_path() { + Ok(path) => path, + Err(e) => { + log::error!(target: "app", "获取服务路径失败: {}", e); + return; + } + }; + + let sysproxy_exe = binary_path.with_file_name("sysproxy.exe"); + if !sysproxy_exe.exists() { + log::error!(target: "app", "sysproxy.exe 不存在"); + return; + } + + let shell = app_handle.shell(); + let output = shell + .command(sysproxy_exe.as_path().to_str().unwrap()) + .args(args) + .output() + .await; + + match output { + Ok(output) => { + if !output.status.success() { + log::error!(target: "app", "执行sysproxy命令失败: {:?}", args); + } + } + Err(e) => { + log::error!(target: "app", "执行sysproxy命令出错: {}", e); + } + } + } +} diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs index 5bcf3821..5cc2fa44 100644 --- a/src-tauri/src/core/mod.rs +++ b/src-tauri/src/core/mod.rs @@ -1,6 +1,7 @@ pub mod backup; #[allow(clippy::module_inception)] mod core; +pub mod event_driven_proxy; pub mod handle; pub mod hotkey; pub mod service; @@ -10,4 +11,4 @@ pub mod timer; pub mod tray; pub mod win_uwp; -pub use self::{core::*, timer::Timer}; +pub use self::{core::*, event_driven_proxy::EventDrivenProxyManager, timer::Timer}; diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index 8caa3684..71cca516 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -2,25 +2,20 @@ use crate::utils::autostart as startup_shortcut; use crate::{ config::{Config, IVerge}, - core::handle::Handle, + core::{handle::Handle, EventDrivenProxyManager}, logging, logging_error, - process::AsyncHandler, utils::logging::Type, }; use anyhow::Result; use once_cell::sync::OnceCell; -use parking_lot::Mutex; use std::sync::Arc; use sysproxy::{Autoproxy, Sysproxy}; use tauri::async_runtime::Mutex as TokioMutex; use tauri_plugin_autostart::ManagerExt; -use tokio::time::{sleep, Duration}; pub struct Sysopt { update_sysproxy: Arc>, reset_sysproxy: Arc>, - /// record whether the guard async is running or not - guard_state: Arc>, } #[cfg(target_os = "windows")] @@ -59,12 +54,15 @@ impl Sysopt { SYSOPT.get_or_init(|| Sysopt { update_sysproxy: Arc::new(TokioMutex::new(false)), reset_sysproxy: Arc::new(TokioMutex::new(false)), - guard_state: Arc::new(false.into()), }) } pub fn init_guard_sysproxy(&self) -> Result<()> { - self.guard_proxy(); + // 使用事件驱动代理管理器 + let proxy_manager = EventDrivenProxyManager::global(); + proxy_manager.notify_app_started(); + + log::info!(target: "app", "已启用事件驱动代理守卫"); Ok(()) } @@ -107,6 +105,8 @@ impl Sysopt { if !sys_enable { sys.set_system_proxy()?; auto.set_auto_proxy()?; + let proxy_manager = EventDrivenProxyManager::global(); + proxy_manager.notify_config_changed(); return Ok(()); } @@ -115,6 +115,8 @@ impl Sysopt { auto.enable = true; sys.set_system_proxy()?; auto.set_auto_proxy()?; + let proxy_manager = EventDrivenProxyManager::global(); + proxy_manager.notify_config_changed(); return Ok(()); } @@ -123,13 +125,18 @@ impl Sysopt { sys.enable = true; auto.set_auto_proxy()?; sys.set_system_proxy()?; + let proxy_manager = EventDrivenProxyManager::global(); + proxy_manager.notify_config_changed(); return Ok(()); } } #[cfg(target_os = "windows")] { if !sys_enable { - return self.reset_sysproxy().await; + let result = self.reset_sysproxy().await; + let proxy_manager = EventDrivenProxyManager::global(); + proxy_manager.notify_config_changed(); + return result; } use crate::{core::handle::Handle, utils::dirs}; use anyhow::bail; @@ -169,6 +176,8 @@ impl Sysopt { bail!("sysproxy exe run failed"); } } + let proxy_manager = EventDrivenProxyManager::global(); + proxy_manager.notify_config_changed(); Ok(()) } @@ -180,7 +189,16 @@ impl Sysopt { #[cfg(not(target_os = "windows"))] { let mut sysproxy: Sysproxy = Sysproxy::get_system_proxy()?; - let mut autoproxy = Autoproxy::get_auto_proxy()?; + let mut autoproxy = match Autoproxy::get_auto_proxy() { + Ok(ap) => ap, + Err(e) => { + log::warn!(target: "app", "重置代理时获取自动代理配置失败: {}, 使用默认配置", e); + Autoproxy { + enable: false, + url: "".to_string(), + } + } + }; sysproxy.enable = false; autoproxy.enable = false; autoproxy.set_auto_proxy()?; @@ -295,167 +313,4 @@ impl Sysopt { } } } - - fn guard_proxy(&self) { - let _lock = self.guard_state.lock(); - - AsyncHandler::spawn(move || async move { - // default duration is 10s - let mut wait_secs = 10u64; - - loop { - sleep(Duration::from_secs(wait_secs)).await; - - let (enable, guard, guard_duration, pac, proxy_host) = { - let verge = Config::verge(); - let verge = verge.latest(); - ( - verge.enable_system_proxy.unwrap_or(false), - verge.enable_proxy_guard.unwrap_or(false), - verge.proxy_guard_duration.unwrap_or(10), - verge.proxy_auto_config.unwrap_or(false), - verge - .proxy_host - .clone() - .unwrap_or_else(|| String::from("127.0.0.1")), - ) - }; - - // stop loop - if !enable || !guard { - continue; - } - - // update duration - wait_secs = guard_duration; - - log::debug!(target: "app", "try to guard the system proxy"); - - // 获取期望的代理端口 - let port = Config::verge() - .latest() - .verge_mixed_port - .unwrap_or(Config::clash().data().get_mixed_port()); - let pac_port = IVerge::get_singleton_port(); - let bypass = get_bypass(); - - // 检查系统代理配置 - if pac { - // 检查 PAC 代理设置 - let expected_url = format!("http://{}:{}/commands/pac", proxy_host, pac_port); - let autoproxy = match Autoproxy::get_auto_proxy() { - Ok(ap) => ap, - Err(e) => { - log::error!(target: "app", "failed to get the auto proxy: {}", e); - continue; - } - }; - - // 检查自动代理是否启用且URL是否正确 - if !autoproxy.enable || autoproxy.url != expected_url { - log::info!(target: "app", "auto proxy settings changed, restoring..."); - #[cfg(not(target_os = "windows"))] - { - let new_autoproxy = Autoproxy { - enable: true, - url: expected_url, - }; - logging_error!(Type::System, true, new_autoproxy.set_auto_proxy()); - } - - #[cfg(target_os = "windows")] - { - use crate::{core::handle::Handle, utils::dirs}; - use tauri_plugin_shell::ShellExt; - - let app_handle = Handle::global().app_handle().unwrap(); - let binary_path = match dirs::service_path() { - Ok(path) => path, - Err(e) => { - log::error!(target: "app", "failed to get service path: {}", e); - continue; - } - }; - - let sysproxy_exe = binary_path.with_file_name("sysproxy.exe"); - if !sysproxy_exe.exists() { - log::error!(target: "app", "sysproxy.exe not found"); - continue; - } - - let shell = app_handle.shell(); - let output = shell - .command(sysproxy_exe.as_path().to_str().unwrap()) - .args(["pac", expected_url.as_str()]) - .output() - .await - .unwrap(); - - if !output.status.success() { - log::error!(target: "app", "failed to set auto proxy"); - } - } - } - } else { - // 检查常规系统代理设置 - let sysproxy = match Sysproxy::get_system_proxy() { - Ok(sp) => sp, - Err(e) => { - log::error!(target: "app", "failed to get the system proxy: {}", e); - continue; - } - }; - - // 检查系统代理是否启用且配置是否匹配 - if !sysproxy.enable || sysproxy.host != proxy_host || sysproxy.port != port { - log::info!(target: "app", "system proxy settings changed, restoring..."); - #[cfg(not(target_os = "windows"))] - { - let new_sysproxy = Sysproxy { - enable: true, - host: proxy_host.clone(), - port, - bypass: bypass.clone(), - }; - logging_error!(Type::System, true, new_sysproxy.set_system_proxy()); - } - - #[cfg(target_os = "windows")] - { - use crate::{core::handle::Handle, utils::dirs}; - use tauri_plugin_shell::ShellExt; - - let app_handle = Handle::global().app_handle().unwrap(); - let binary_path = match dirs::service_path() { - Ok(path) => path, - Err(e) => { - log::error!(target: "app", "failed to get service path: {}", e); - continue; - } - }; - - let sysproxy_exe = binary_path.with_file_name("sysproxy.exe"); - if !sysproxy_exe.exists() { - log::error!(target: "app", "sysproxy.exe not found"); - continue; - } - - let address = format!("{}:{}", proxy_host, port); - let shell = app_handle.shell(); - let output = shell - .command(sysproxy_exe.as_path().to_str().unwrap()) - .args(["global", address.as_str(), bypass.as_ref()]) - .output() - .await - .unwrap(); - - if !output.status.success() { - log::error!(target: "app", "failed to set system proxy"); - } - } - } - } - } - }); - } } diff --git a/src/components/setting/mods/sysproxy-viewer.tsx b/src/components/setting/mods/sysproxy-viewer.tsx index 87b9af10..4481e8ad 100644 --- a/src/components/setting/mods/sysproxy-viewer.tsx +++ b/src/components/setting/mods/sysproxy-viewer.tsx @@ -81,7 +81,7 @@ export const SysproxyViewer = forwardRef((props, ref) => { const [open, setOpen] = useState(false); const [editorOpen, setEditorOpen] = useState(false); const [saving, setSaving] = useState(false); - const { verge, patchVerge } = useVerge(); + const { verge, patchVerge, mutateVerge } = useVerge(); const [hostOptions, setHostOptions] = useState([]); type SysProxy = Awaited>; @@ -281,93 +281,108 @@ export const SysproxyViewer = forwardRef((props, ref) => { } setSaving(true); - try { - const patch: Partial = {}; + setOpen(false); + setSaving(false); + const patch: Partial = {}; - if (value.guard !== enable_proxy_guard) { - patch.enable_proxy_guard = value.guard; - } - if (value.duration !== proxy_guard_duration) { - patch.proxy_guard_duration = value.duration; - } - if (value.bypass !== system_proxy_bypass) { - patch.system_proxy_bypass = value.bypass; - } - if (value.pac !== proxy_auto_config) { - patch.proxy_auto_config = value.pac; - } - if (value.use_default !== use_default_bypass) { - patch.use_default_bypass = value.use_default; - } - - let pacContent = value.pac_content; - if (pacContent) { - pacContent = pacContent.replace(/%proxy_host%/g, value.proxy_host); - // 将 mixed-port 转换为字符串 - const mixedPortStr = (clashConfig?.["mixed-port"] || "").toString(); - pacContent = pacContent.replace(/%mixed-port%/g, mixedPortStr); - } - - if (pacContent !== pac_file_content) { - patch.pac_file_content = pacContent; - } - - // 处理IPv6地址,如果是IPv6地址但没有被方括号包围,则添加方括号 - let proxyHost = value.proxy_host; - if ( - ipv6Regex.test(proxyHost) && - !proxyHost.startsWith("[") && - !proxyHost.endsWith("]") - ) { - proxyHost = `[${proxyHost}]`; - } - - if (proxyHost !== proxy_host) { - patch.proxy_host = proxyHost; - } - - // 判断是否需要重置系统代理 - const needResetProxy = - value.pac !== proxy_auto_config || - proxyHost !== proxy_host || - pacContent !== pac_file_content || - value.bypass !== system_proxy_bypass || - value.use_default !== use_default_bypass; - - await patchVerge(patch); - - // 更新系统代理状态,以便UI立即反映变化 - await Promise.all([mutate("getSystemProxy"), mutate("getAutotemProxy")]); - - // 只有在修改了影响系统代理的配置且系统代理当前启用时,才重置系统代理 - if (needResetProxy) { - const currentSysProxy = await getSystemProxy(); - const currentAutoProxy = await getAutotemProxy(); - - if (value.pac ? currentAutoProxy?.enable : currentSysProxy?.enable) { - // 临时关闭系统代理 - await patchVergeConfig({ enable_system_proxy: false }); - - // 减少等待时间 - await new Promise((resolve) => setTimeout(resolve, 200)); - - // 重新开启系统代理 - await patchVergeConfig({ enable_system_proxy: true }); - - // 更新UI状态 - await Promise.all([ - mutate("getSystemProxy"), - mutate("getAutotemProxy"), - ]); - } - } - - setOpen(false); - } catch (err: any) { - showNotice("error", err.toString()); - } finally { - setSaving(false); + if (value.guard !== enable_proxy_guard) { + patch.enable_proxy_guard = value.guard; } + if (value.duration !== proxy_guard_duration) { + patch.proxy_guard_duration = value.duration; + } + if (value.bypass !== system_proxy_bypass) { + patch.system_proxy_bypass = value.bypass; + } + if (value.pac !== proxy_auto_config) { + patch.proxy_auto_config = value.pac; + } + if (value.use_default !== use_default_bypass) { + patch.use_default_bypass = value.use_default; + } + + let pacContent = value.pac_content; + if (pacContent) { + pacContent = pacContent.replace(/%proxy_host%/g, value.proxy_host); + // 将 mixed-port 转换为字符串 + const mixedPortStr = (clashConfig?.["mixed-port"] || "").toString(); + pacContent = pacContent.replace(/%mixed-port%/g, mixedPortStr); + } + + if (pacContent !== pac_file_content) { + patch.pac_file_content = pacContent; + } + + // 处理IPv6地址,如果是IPv6地址但没有被方括号包围,则添加方括号 + let proxyHost = value.proxy_host; + if ( + ipv6Regex.test(proxyHost) && + !proxyHost.startsWith("[") && + !proxyHost.endsWith("]") + ) { + proxyHost = `[${proxyHost}]`; + } + + if (proxyHost !== proxy_host) { + patch.proxy_host = proxyHost; + } + + // 判断是否需要重置系统代理 + const needResetProxy = + value.pac !== proxy_auto_config || + proxyHost !== proxy_host || + pacContent !== pac_file_content || + value.bypass !== system_proxy_bypass || + value.use_default !== use_default_bypass; + + Promise.resolve().then(async () => { + try { + // 乐观更新本地状态 + if (Object.keys(patch).length > 0) { + mutateVerge({ ...verge, ...patch }, false); + } + if (Object.keys(patch).length > 0) { + await patchVerge(patch); + } + setTimeout(async () => { + try { + await Promise.all([ + mutate("getSystemProxy"), + mutate("getAutotemProxy"), + ]); + + // 如果需要重置代理且代理当前启用 + if (needResetProxy && enabled) { + const [currentSysProxy, currentAutoProxy] = await Promise.all([ + getSystemProxy(), + getAutotemProxy(), + ]); + + const isProxyActive = value.pac + ? currentAutoProxy?.enable + : currentSysProxy?.enable; + + if (isProxyActive) { + await patchVergeConfig({ enable_system_proxy: false }); + await new Promise((resolve) => setTimeout(resolve, 50)); + await patchVergeConfig({ enable_system_proxy: true }); + await Promise.all([ + mutate("getSystemProxy"), + mutate("getAutotemProxy"), + ]); + } + } + } catch (err) { + console.warn("代理状态更新失败:", err); + } + }, 50); + } catch (err: any) { + console.error("配置保存失败:", err); + mutateVerge(); + showNotice("error", err.toString()); + // setOpen(true); + } + }); }); return ( diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx index 5be8eb4c..2d763f5a 100644 --- a/src/pages/_layout.tsx +++ b/src/pages/_layout.tsx @@ -479,7 +479,19 @@ const Layout = () => { } return ( - + { + console.error(`[SWR Error] Key: ${key}, Error:`, error); + if (key !== "getAutotemProxy") { + console.error(`SWR Error for ${key}:`, error); + } + }, + dedupingInterval: 2000, + }} + >
("get_auto_proxy"); + try { + console.log("[API] 开始调用 get_auto_proxy"); + const result = await invoke<{ + enable: boolean; + url: string; + }>("get_auto_proxy"); + console.log("[API] get_auto_proxy 调用成功:", result); + return result; + } catch (error) { + console.error("[API] get_auto_proxy 调用失败:", error); + return { + enable: false, + url: "", + }; + } } export async function getAutoLaunchStatus() { @@ -122,7 +133,6 @@ export async function getAutoLaunchStatus() { return await invoke("get_auto_launch_status"); } catch (error) { console.error("获取自启动状态失败:", error); - // 出错时返回false作为默认值 return false; } }