From d4cb16f4ffc56844ecbddfed3df416caa5aa9676 Mon Sep 17 00:00:00 2001 From: oomeow Date: Sun, 2 Nov 2025 22:33:50 +0800 Subject: [PATCH 1/2] perf: select proxy (#5284) * perf: improve select proxy for group * chore: update --- src-tauri/src/cmd/profile.rs | 11 ++++++----- src-tauri/src/core/tray/mod.rs | 1 - src/hooks/use-profiles.ts | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs index 9ab8e21a..949ddce1 100644 --- a/src-tauri/src/cmd/profile.rs +++ b/src-tauri/src/cmd/profile.rs @@ -570,14 +570,16 @@ pub async fn patch_profiles_config_by_profile_index(profile_index: String) -> Cm pub async fn patch_profile(index: String, profile: PrfItem) -> CmdResult { // 保存修改前检查是否有更新 update_interval let profiles = Config::profiles().await; - let should_refresh_timer = if let Ok(old_profile) = profiles.latest_ref().get_item(&index) { + let should_refresh_timer = if let Ok(old_profile) = profiles.latest_ref().get_item(&index) + && let Some(new_option) = profile.option.as_ref() + { let old_interval = old_profile.option.as_ref().and_then(|o| o.update_interval); - let new_interval = profile.option.as_ref().and_then(|o| o.update_interval); + let new_interval = new_option.update_interval; let old_allow_auto_update = old_profile .option .as_ref() .and_then(|o| o.allow_auto_update); - let new_allow_auto_update = profile.option.as_ref().and_then(|o| o.allow_auto_update); + let new_allow_auto_update = new_option.allow_auto_update; (old_interval != new_interval) || (old_allow_auto_update != new_allow_auto_update) } else { false @@ -589,14 +591,13 @@ pub async fn patch_profile(index: String, profile: PrfItem) -> CmdResult { // 如果更新间隔或允许自动更新变更,异步刷新定时器 if should_refresh_timer { - let index_clone = index.clone(); crate::process::AsyncHandler::spawn(move || async move { logging!(info, Type::Timer, "定时器更新间隔已变更,正在刷新定时器..."); if let Err(e) = crate::core::Timer::global().refresh().await { logging!(error, Type::Timer, "刷新定时器失败: {}", e); } else { // 刷新成功后发送自定义事件,不触发配置重载 - crate::core::handle::Handle::notify_timer_updated(index_clone); + crate::core::handle::Handle::notify_timer_updated(index); } }); } diff --git a/src-tauri/src/core/tray/mod.rs b/src-tauri/src/core/tray/mod.rs index 474b0202..ef300bbe 100644 --- a/src-tauri/src/core/tray/mod.rs +++ b/src-tauri/src/core/tray/mod.rs @@ -308,7 +308,6 @@ impl Tray { let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false); let tun_mode_available = cmd::system::is_admin().unwrap_or_default() || service::is_service_available().await.is_ok(); - println!("tun_mode_available: {}", tun_mode_available); let mode = { Config::clash() .await diff --git a/src/hooks/use-profiles.ts b/src/hooks/use-profiles.ts index fdb73462..c517c835 100644 --- a/src/hooks/use-profiles.ts +++ b/src/hooks/use-profiles.ts @@ -62,7 +62,9 @@ export const useProfiles = () => { const patchCurrent = async (value: Partial) => { if (profiles?.current) { await patchProfile(profiles.current, value); - mutateProfiles(); + if (!value.selected) { + mutateProfiles(); + } } }; From 34cac0fa3aed8ef653cf62c19f42eaa5ae663135 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Sun, 2 Nov 2025 22:41:22 +0800 Subject: [PATCH 2/2] feat: improve subscription update method and enhance error handling for profile updates --- UPDATELOG.md | 1 + src-tauri/src/core/sysopt.rs | 7 ++ src-tauri/src/core/timer.rs | 18 ++- src-tauri/src/feat/profile.rs | 129 ++++++++++++---------- src-tauri/src/utils/network.rs | 7 ++ src/pages/_layout/notificationHandlers.ts | 2 + 6 files changed, 105 insertions(+), 59 deletions(-) diff --git a/UPDATELOG.md b/UPDATELOG.md index 8b7081cf..2fa6bc73 100644 --- a/UPDATELOG.md +++ b/UPDATELOG.md @@ -85,6 +85,7 @@ - 添加热键绑定错误的提示信息 - 在 macOS 10.15 及更高版本默认包含 Mihomo-go122,以解决 Intel 架构 Mac 无法运行内核的问题 - Tun 模式不可用时,禁用系统托盘的 Tun 模式菜单 +- 改进订阅更新方式,仍失败需打开订阅设置 `允许危险证书` diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index d73d9a91..c24d8be9 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -15,6 +15,7 @@ use sysproxy::{Autoproxy, Sysproxy}; use tauri_plugin_autostart::ManagerExt; pub struct Sysopt { + initialed: AtomicBool, update_sysproxy: AtomicBool, reset_sysproxy: AtomicBool, } @@ -84,6 +85,7 @@ async fn execute_sysproxy_command(args: Vec) -> Result<()> impl Default for Sysopt { fn default() -> Self { Sysopt { + initialed: AtomicBool::new(false), update_sysproxy: AtomicBool::new(false), reset_sysproxy: AtomicBool::new(false), } @@ -94,6 +96,10 @@ impl Default for Sysopt { singleton_lazy!(Sysopt, SYSOPT, Sysopt::default); impl Sysopt { + pub fn is_initialed(&self) -> bool { + self.initialed.load(Ordering::SeqCst) + } + pub fn init_guard_sysproxy(&self) -> Result<()> { // 使用事件驱动代理管理器 let proxy_manager = EventDrivenProxyManager::global(); @@ -105,6 +111,7 @@ impl Sysopt { /// init the sysproxy pub async fn update_sysproxy(&self) -> Result<()> { + self.initialed.store(true, Ordering::SeqCst); if self .update_sysproxy .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) diff --git a/src-tauri/src/core/timer.rs b/src-tauri/src/core/timer.rs index 31ad95cb..e8506df1 100644 --- a/src-tauri/src/core/timer.rs +++ b/src-tauri/src/core/timer.rs @@ -1,4 +1,7 @@ -use crate::{config::Config, feat, logging, logging_error, singleton, utils::logging::Type}; +use crate::{ + config::Config, core::sysopt::Sysopt, feat, logging, logging_error, singleton, + utils::logging::Type, +}; use anyhow::{Context, Result}; use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder}; use parking_lot::RwLock; @@ -10,7 +13,9 @@ use std::{ Arc, atomic::{AtomicBool, AtomicU64, Ordering}, }, + time::Duration, }; +use tokio::time::{sleep, timeout}; type TaskID = u64; @@ -390,6 +395,7 @@ impl Timer { .spawn_async_routine(move || { let uid = uid.clone(); Box::pin(async move { + Self::wait_until_sysopt(Duration::from_millis(1000)).await; Self::async_task(&uid).await; }) as Pin + Send>> }) @@ -519,6 +525,16 @@ impl Timer { // Emit completed event Self::emit_update_event(uid, false); } + + async fn wait_until_sysopt(max_wait: Duration) { + let _ = timeout(max_wait, async { + while !Sysopt::global().is_initialed() { + logging!(warn, Type::Timer, "Waiting for Sysopt to be initialized..."); + sleep(Duration::from_millis(30)).await; + } + }) + .await; + } } #[derive(Debug)] diff --git a/src-tauri/src/feat/profile.rs b/src-tauri/src/feat/profile.rs index 5cee61d2..f3832e38 100644 --- a/src-tauri/src/feat/profile.rs +++ b/src-tauri/src/feat/profile.rs @@ -86,20 +86,25 @@ async fn perform_profile_update( option: Option<&PrfOption>, ) -> Result { logging!(info, Type::Config, "[订阅更新] 开始下载新的订阅内容"); - let merged_opt = PrfOption::merge(opt, option); + let mut merged_opt = PrfOption::merge(opt, option); + let is_current = { + let profiles = Config::profiles().await; + profiles.latest_ref().is_current_profile_index(uid) + }; + let profile_name = { + let profiles = Config::profiles().await; + profiles + .latest_ref() + .get_name_by_uid(uid) + .unwrap_or_default() + }; + let mut last_err; match PrfItem::from_url(url, None, None, merged_opt.as_ref()).await { Ok(mut item) => { logging!(info, Type::Config, "[订阅更新] 更新订阅配置成功"); - let profiles = Config::profiles().await; profiles_draft_update_item_safe(uid, &mut item).await?; - let is_current = Some(uid.clone()) == profiles.latest_ref().get_current(); - logging!( - info, - Type::Config, - "[订阅更新] 是否为当前使用的订阅: {is_current}" - ); - Ok(is_current) + return Ok(is_current); } Err(err) => { logging!( @@ -107,57 +112,65 @@ async fn perform_profile_update( Type::Config, "Warning: [订阅更新] 正常更新失败: {err},尝试使用Clash代理更新" ); - - let original_with_proxy = merged_opt.as_ref().and_then(|o| o.with_proxy); - let original_self_proxy = merged_opt.as_ref().and_then(|o| o.self_proxy); - - let mut fallback_opt = merged_opt.unwrap_or_default(); - fallback_opt.with_proxy = Some(false); - fallback_opt.self_proxy = Some(true); - - match PrfItem::from_url(url, None, None, Some(&fallback_opt)).await { - Ok(mut item) => { - logging!(info, Type::Config, "[订阅更新] 使用Clash代理更新成功"); - - if let Some(option) = item.option.as_mut() { - option.with_proxy = original_with_proxy; - option.self_proxy = original_self_proxy; - } - - let profiles = Config::profiles().await; - profiles_draft_update_item_safe(uid, &mut item).await?; - - let profile_name = item.name.clone().unwrap_or_else(|| uid.clone()); - handle::Handle::notice_message("update_with_clash_proxy", profile_name); - - let is_current = Some(uid.clone()) == profiles.latest_ref().get_current(); - logging!( - info, - Type::Config, - "[订阅更新] 是否为当前使用的订阅: {is_current}" - ); - Ok(is_current) - } - Err(retry_err) => { - let failed_profile_name = Config::profiles() - .await - .latest_ref() - .get_name_by_uid(uid) - .unwrap_or_default(); - logging!( - error, - Type::Config, - "[订阅更新] 使用Clash代理更新仍然失败: {failed_profile_name} - {retry_err}" - ); - handle::Handle::notice_message( - "update_failed_even_with_clash", - format!("{failed_profile_name} - {retry_err}"), - ); - Err(retry_err) - } - } + last_err = err; } } + + merged_opt.get_or_insert_with(PrfOption::default).self_proxy = Some(true); + merged_opt.get_or_insert_with(PrfOption::default).with_proxy = Some(false); + + match PrfItem::from_url(url, None, None, merged_opt.as_ref()).await { + Ok(mut item) => { + logging!( + info, + Type::Config, + "[订阅更新] 使用 Clash代理 更新订阅配置成功" + ); + profiles_draft_update_item_safe(uid, &mut item).await?; + handle::Handle::notice_message("update_with_clash_proxy", profile_name); + drop(last_err); + return Ok(is_current); + } + Err(err) => { + logging!( + warn, + Type::Config, + "Warning: [订阅更新] 正常更新失败: {err},尝试使用Clash代理更新" + ); + last_err = err; + } + } + + merged_opt.get_or_insert_with(PrfOption::default).self_proxy = Some(false); + merged_opt.get_or_insert_with(PrfOption::default).with_proxy = Some(true); + + match PrfItem::from_url(url, None, None, merged_opt.as_ref()).await { + Ok(mut item) => { + logging!( + info, + Type::Config, + "[订阅更新] 使用 系统代理 更新订阅配置成功" + ); + profiles_draft_update_item_safe(uid, &mut item).await?; + handle::Handle::notice_message("update_with_clash_proxy", profile_name); + drop(last_err); + return Ok(is_current); + } + Err(err) => { + logging!( + warn, + Type::Config, + "Warning: [订阅更新] 正常更新失败: {err},尝试使用系统代理更新" + ); + last_err = err; + } + } + + handle::Handle::notice_message( + "update_failed_even_with_clash", + format!("{profile_name} - {last_err}"), + ); + Ok(is_current) } pub async fn update_profile( diff --git a/src-tauri/src/utils/network.rs b/src-tauri/src/utils/network.rs index cc765e65..65ef63a2 100644 --- a/src-tauri/src/utils/network.rs +++ b/src-tauri/src/utils/network.rs @@ -1,6 +1,7 @@ use crate::config::Config; use anyhow::Result; use base64::{Engine as _, engine::general_purpose}; +use isahc::config::DnsCache; use isahc::prelude::*; use isahc::{HttpClient, config::SslOption}; use isahc::{ @@ -143,6 +144,12 @@ impl NetworkManager { builder = builder.redirect_policy(RedirectPolicy::Follow); + // 禁用缓存,不关心连接复用 + builder = builder.connection_cache_size(0); + + // 禁用 DNS 缓存,避免因 DNS 变化导致的问题 + builder = builder.dns_cache(DnsCache::Disable); + Ok(builder.build()?) } } diff --git a/src/pages/_layout/notificationHandlers.ts b/src/pages/_layout/notificationHandlers.ts index 5696b5ea..afa674c8 100644 --- a/src/pages/_layout/notificationHandlers.ts +++ b/src/pages/_layout/notificationHandlers.ts @@ -19,6 +19,8 @@ export const handleNoticeMessage = ( showNotice("error", msg); }, "set_config::error": () => showNotice("error", msg), + // 后端暂时没有启用相关通知, 批量更新可能造成扰人提醒 + // update_success: () => showNotice("success", t("Update subscription successfully")), update_with_clash_proxy: () => showNotice( "success",