Merge branch 'dev' into chore/i18n

# Conflicts:
#	src/pages/_layout/notificationHandlers.ts
This commit is contained in:
Slinetrac
2025-11-02 23:11:26 +08:00
Unverified
8 changed files with 112 additions and 66 deletions

View File

@@ -85,6 +85,7 @@
- 添加热键绑定错误的提示信息
- 在 macOS 10.15 及更高版本默认包含 Mihomo-go122以解决 Intel 架构 Mac 无法运行内核的问题
- Tun 模式不可用时,禁用系统托盘的 Tun 模式菜单
- 改进订阅更新方式,仍失败需打开订阅设置 `允许危险证书`
</details>

View File

@@ -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);
}
});
}

View File

@@ -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<std::string::String>) -> 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)

View File

@@ -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<Box<dyn std::future::Future<Output = ()> + 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)]

View File

@@ -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

View File

@@ -86,20 +86,25 @@ async fn perform_profile_update(
option: Option<&PrfOption>,
) -> Result<bool> {
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(

View File

@@ -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()?)
}
}

View File

@@ -62,7 +62,9 @@ export const useProfiles = () => {
const patchCurrent = async (value: Partial<IProfileItem>) => {
if (profiles?.current) {
await patchProfile(profiles.current, value);
mutateProfiles();
if (!value.selected) {
mutateProfiles();
}
}
};