feat: improve subscription update method and enhance error handling for profile updates
This commit is contained in:
@@ -85,6 +85,7 @@
|
|||||||
- 添加热键绑定错误的提示信息
|
- 添加热键绑定错误的提示信息
|
||||||
- 在 macOS 10.15 及更高版本默认包含 Mihomo-go122,以解决 Intel 架构 Mac 无法运行内核的问题
|
- 在 macOS 10.15 及更高版本默认包含 Mihomo-go122,以解决 Intel 架构 Mac 无法运行内核的问题
|
||||||
- Tun 模式不可用时,禁用系统托盘的 Tun 模式菜单
|
- Tun 模式不可用时,禁用系统托盘的 Tun 模式菜单
|
||||||
|
- 改进订阅更新方式,仍失败需打开订阅设置 `允许危险证书`
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use sysproxy::{Autoproxy, Sysproxy};
|
|||||||
use tauri_plugin_autostart::ManagerExt;
|
use tauri_plugin_autostart::ManagerExt;
|
||||||
|
|
||||||
pub struct Sysopt {
|
pub struct Sysopt {
|
||||||
|
initialed: AtomicBool,
|
||||||
update_sysproxy: AtomicBool,
|
update_sysproxy: AtomicBool,
|
||||||
reset_sysproxy: AtomicBool,
|
reset_sysproxy: AtomicBool,
|
||||||
}
|
}
|
||||||
@@ -84,6 +85,7 @@ async fn execute_sysproxy_command(args: Vec<std::string::String>) -> Result<()>
|
|||||||
impl Default for Sysopt {
|
impl Default for Sysopt {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Sysopt {
|
Sysopt {
|
||||||
|
initialed: AtomicBool::new(false),
|
||||||
update_sysproxy: AtomicBool::new(false),
|
update_sysproxy: AtomicBool::new(false),
|
||||||
reset_sysproxy: AtomicBool::new(false),
|
reset_sysproxy: AtomicBool::new(false),
|
||||||
}
|
}
|
||||||
@@ -94,6 +96,10 @@ impl Default for Sysopt {
|
|||||||
singleton_lazy!(Sysopt, SYSOPT, Sysopt::default);
|
singleton_lazy!(Sysopt, SYSOPT, Sysopt::default);
|
||||||
|
|
||||||
impl Sysopt {
|
impl Sysopt {
|
||||||
|
pub fn is_initialed(&self) -> bool {
|
||||||
|
self.initialed.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init_guard_sysproxy(&self) -> Result<()> {
|
pub fn init_guard_sysproxy(&self) -> Result<()> {
|
||||||
// 使用事件驱动代理管理器
|
// 使用事件驱动代理管理器
|
||||||
let proxy_manager = EventDrivenProxyManager::global();
|
let proxy_manager = EventDrivenProxyManager::global();
|
||||||
@@ -105,6 +111,7 @@ impl Sysopt {
|
|||||||
|
|
||||||
/// init the sysproxy
|
/// init the sysproxy
|
||||||
pub async fn update_sysproxy(&self) -> Result<()> {
|
pub async fn update_sysproxy(&self) -> Result<()> {
|
||||||
|
self.initialed.store(true, Ordering::SeqCst);
|
||||||
if self
|
if self
|
||||||
.update_sysproxy
|
.update_sysproxy
|
||||||
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
||||||
|
|||||||
@@ -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 anyhow::{Context, Result};
|
||||||
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
|
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
@@ -10,7 +13,9 @@ use std::{
|
|||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicBool, AtomicU64, Ordering},
|
atomic::{AtomicBool, AtomicU64, Ordering},
|
||||||
},
|
},
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
use tokio::time::{sleep, timeout};
|
||||||
|
|
||||||
type TaskID = u64;
|
type TaskID = u64;
|
||||||
|
|
||||||
@@ -390,6 +395,7 @@ impl Timer {
|
|||||||
.spawn_async_routine(move || {
|
.spawn_async_routine(move || {
|
||||||
let uid = uid.clone();
|
let uid = uid.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
Self::wait_until_sysopt(Duration::from_millis(1000)).await;
|
||||||
Self::async_task(&uid).await;
|
Self::async_task(&uid).await;
|
||||||
}) as Pin<Box<dyn std::future::Future<Output = ()> + Send>>
|
}) as Pin<Box<dyn std::future::Future<Output = ()> + Send>>
|
||||||
})
|
})
|
||||||
@@ -519,6 +525,16 @@ impl Timer {
|
|||||||
// Emit completed event
|
// Emit completed event
|
||||||
Self::emit_update_event(uid, false);
|
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)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -86,20 +86,25 @@ async fn perform_profile_update(
|
|||||||
option: Option<&PrfOption>,
|
option: Option<&PrfOption>,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
logging!(info, Type::Config, "[订阅更新] 开始下载新的订阅内容");
|
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 {
|
match PrfItem::from_url(url, None, None, merged_opt.as_ref()).await {
|
||||||
Ok(mut item) => {
|
Ok(mut item) => {
|
||||||
logging!(info, Type::Config, "[订阅更新] 更新订阅配置成功");
|
logging!(info, Type::Config, "[订阅更新] 更新订阅配置成功");
|
||||||
let profiles = Config::profiles().await;
|
|
||||||
profiles_draft_update_item_safe(uid, &mut item).await?;
|
profiles_draft_update_item_safe(uid, &mut item).await?;
|
||||||
let is_current = Some(uid.clone()) == profiles.latest_ref().get_current();
|
return Ok(is_current);
|
||||||
logging!(
|
|
||||||
info,
|
|
||||||
Type::Config,
|
|
||||||
"[订阅更新] 是否为当前使用的订阅: {is_current}"
|
|
||||||
);
|
|
||||||
Ok(is_current)
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
logging!(
|
logging!(
|
||||||
@@ -107,57 +112,65 @@ async fn perform_profile_update(
|
|||||||
Type::Config,
|
Type::Config,
|
||||||
"Warning: [订阅更新] 正常更新失败: {err},尝试使用Clash代理更新"
|
"Warning: [订阅更新] 正常更新失败: {err},尝试使用Clash代理更新"
|
||||||
);
|
);
|
||||||
|
last_err = err;
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
pub async fn update_profile(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
|
use isahc::config::DnsCache;
|
||||||
use isahc::prelude::*;
|
use isahc::prelude::*;
|
||||||
use isahc::{HttpClient, config::SslOption};
|
use isahc::{HttpClient, config::SslOption};
|
||||||
use isahc::{
|
use isahc::{
|
||||||
@@ -143,6 +144,12 @@ impl NetworkManager {
|
|||||||
|
|
||||||
builder = builder.redirect_policy(RedirectPolicy::Follow);
|
builder = builder.redirect_policy(RedirectPolicy::Follow);
|
||||||
|
|
||||||
|
// 禁用缓存,不关心连接复用
|
||||||
|
builder = builder.connection_cache_size(0);
|
||||||
|
|
||||||
|
// 禁用 DNS 缓存,避免因 DNS 变化导致的问题
|
||||||
|
builder = builder.dns_cache(DnsCache::Disable);
|
||||||
|
|
||||||
Ok(builder.build()?)
|
Ok(builder.build()?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export const handleNoticeMessage = (
|
|||||||
showNotice("error", msg);
|
showNotice("error", msg);
|
||||||
},
|
},
|
||||||
"set_config::error": () => showNotice("error", msg),
|
"set_config::error": () => showNotice("error", msg),
|
||||||
|
// 后端暂时没有启用相关通知, 批量更新可能造成扰人提醒
|
||||||
|
// update_success: () => showNotice("success", t("Update subscription successfully")),
|
||||||
update_with_clash_proxy: () =>
|
update_with_clash_proxy: () =>
|
||||||
showNotice(
|
showNotice(
|
||||||
"success",
|
"success",
|
||||||
|
|||||||
Reference in New Issue
Block a user