added update profiles at startup, “announce-url” header, and also when adding check if the profile already exists and if it does, just update it

This commit is contained in:
coolcoala
2025-07-18 04:12:55 +03:00
parent 5e855e4755
commit 31d368979e
6 changed files with 121 additions and 3 deletions

View File

@@ -129,8 +129,24 @@ pub async fn enhance_profiles() -> CmdResult {
/// 导入配置文件 /// 导入配置文件
#[tauri::command] #[tauri::command]
pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult { pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult {
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?; let existing_uid = {
wrap_err!(Config::profiles().data().append_item(item)) let profiles = Config::profiles();
let profiles = profiles.latest();
profiles.items.as_ref()
.and_then(|items| items.iter().find(|item| item.url.as_deref() == Some(&url)))
.and_then(|item| item.uid.clone())
};
if let Some(uid) = existing_uid {
logging!(info, Type::Cmd, true, "The profile with URL {} already exists (UID: {}). Running the update...", url, uid);
update_profile(uid, option).await
} else {
logging!(info, Type::Cmd, true, "Profile with URL {} not found. Create a new one...", url);
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?;
wrap_err!(Config::profiles().data().append_item(item))
}
} }
/// 重新排序配置文件 /// 重新排序配置文件
@@ -647,3 +663,48 @@ pub fn get_next_update_time(uid: String) -> CmdResult<Option<i64>> {
let next_time = timer.get_next_update_time(&uid); let next_time = timer.get_next_update_time(&uid);
Ok(next_time) Ok(next_time)
} }
#[tauri::command]
pub async fn update_profiles_on_startup() -> CmdResult {
logging!(info, Type::Cmd, true, "Checking profiles for updates at startup...");
let profiles_to_update = {
let profiles = Config::profiles();
let profiles = profiles.latest();
profiles.items.as_ref()
.map_or_else(
Vec::new,
|items| items.iter()
.filter(|item| item.option.as_ref().is_some_and(|opt| opt.update_always == Some(true)))
.filter_map(|item| item.uid.clone())
.collect()
)
};
if profiles_to_update.is_empty() {
logging!(info, Type::Cmd, true, "No profiles to update immediately.");
return Ok(());
}
logging!(info, Type::Cmd, true, "Found profiles to update: {:?}", profiles_to_update);
let mut update_futures = Vec::new();
for uid in profiles_to_update {
update_futures.push(update_profile(uid, None));
}
let results = futures::future::join_all(update_futures).await;
if results.iter().any(|res| res.is_ok()) {
logging!(info, Type::Cmd, true, "The startup update is complete, restart the kernel...");
CoreManager::global().update_config().await.map_err(|e| e.to_string())?;
handle::Handle::refresh_clash();
} else {
logging!(warn, Type::Cmd, true, "All updates completed with errors on startup.");
}
Ok(())
}

View File

@@ -63,6 +63,10 @@ pub struct PrfItem {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub announce: Option<String>, pub announce: Option<String>,
/// profile announce url
#[serde(skip_serializing_if = "Option::is_none")]
pub announce_url: Option<String>,
/// the file data /// the file data
#[serde(skip)] #[serde(skip)]
pub file_data: Option<String>, pub file_data: Option<String>,
@@ -126,6 +130,9 @@ pub struct PrfOption {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub use_hwid: Option<bool>, pub use_hwid: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub update_always: Option<bool>,
} }
impl PrfOption { impl PrfOption {
@@ -146,6 +153,7 @@ impl PrfOption {
a.groups = b.groups.or(a.groups); a.groups = b.groups.or(a.groups);
a.timeout_seconds = b.timeout_seconds.or(a.timeout_seconds); a.timeout_seconds = b.timeout_seconds.or(a.timeout_seconds);
a.use_hwid = b.use_hwid.or(a.use_hwid); a.use_hwid = b.use_hwid.or(a.use_hwid);
a.update_always = b.update_always.or(a.update_always);
Some(a) Some(a)
} }
t => t.0.or(t.1), t => t.0.or(t.1),
@@ -246,6 +254,7 @@ impl PrfItem {
home: None, home: None,
support_url: None, support_url: None,
announce: None, announce: None,
announce_url: None,
updated: Some(chrono::Local::now().timestamp() as usize), updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())), file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())),
}) })
@@ -267,7 +276,7 @@ impl PrfItem {
let user_agent = opt_ref.and_then(|o| o.user_agent.clone()); let user_agent = opt_ref.and_then(|o| o.user_agent.clone());
let update_interval = opt_ref.and_then(|o| o.update_interval); let update_interval = opt_ref.and_then(|o| o.update_interval);
let timeout = opt_ref.and_then(|o| o.timeout_seconds).unwrap_or(20); let timeout = opt_ref.and_then(|o| o.timeout_seconds).unwrap_or(20);
let use_hwid = opt_ref.is_some_and(|o| o.use_hwid.unwrap_or(true)); let use_hwid = Config::verge().latest().enable_send_hwid.unwrap_or(true);
let mut merge = opt_ref.and_then(|o| o.merge.clone()); let mut merge = opt_ref.and_then(|o| o.merge.clone());
let mut script = opt_ref.and_then(|o| o.script.clone()); let mut script = opt_ref.and_then(|o| o.script.clone());
let mut rules = opt_ref.and_then(|o| o.rules.clone()); let mut rules = opt_ref.and_then(|o| o.rules.clone());
@@ -373,6 +382,11 @@ impl PrfItem {
}, },
}; };
let update_always = match header.get("update-always") {
Some(value) => value.to_str().unwrap_or("false").parse::<bool>().ok(),
None => None,
};
let home = match header.get("profile-web-page-url") { let home = match header.get("profile-web-page-url") {
Some(value) => { Some(value) => {
let str_value = value.to_str().unwrap_or(""); let str_value = value.to_str().unwrap_or("");
@@ -403,6 +417,14 @@ impl PrfItem {
None => None, None => None,
}; };
let announce_url = match header.get("announce-url") {
Some(value) => {
let str_value = value.to_str().unwrap_or("");
Some(str_value.to_string())
}
None => None,
};
let profile_title = match header.get("profile-title") { let profile_title = match header.get("profile-title") {
Some(value) => { Some(value) => {
let str_value = value.to_str().unwrap_or(""); let str_value = value.to_str().unwrap_or("");
@@ -472,6 +494,7 @@ impl PrfItem {
extra, extra,
option: Some(PrfOption { option: Some(PrfOption {
update_interval, update_interval,
update_always,
merge, merge,
script, script,
rules, rules,
@@ -482,6 +505,7 @@ impl PrfItem {
home, home,
support_url, support_url,
announce, announce,
announce_url,
updated: Some(chrono::Local::now().timestamp() as usize), updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(data.into()), file_data: Some(data.into()),
}) })
@@ -511,6 +535,7 @@ impl PrfItem {
home: None, home: None,
support_url: None, support_url: None,
announce: None, announce: None,
announce_url: None,
updated: Some(chrono::Local::now().timestamp() as usize), updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(template), file_data: Some(template),
}) })
@@ -535,6 +560,7 @@ impl PrfItem {
home: None, home: None,
support_url: None, support_url: None,
announce: None, announce: None,
announce_url: None,
selected: None, selected: None,
extra: None, extra: None,
option: None, option: None,
@@ -558,6 +584,7 @@ impl PrfItem {
home: None, home: None,
support_url: None, support_url: None,
announce: None, announce: None,
announce_url: None,
selected: None, selected: None,
extra: None, extra: None,
option: None, option: None,
@@ -581,6 +608,7 @@ impl PrfItem {
home: None, home: None,
support_url: None, support_url: None,
announce: None, announce: None,
announce_url: None,
selected: None, selected: None,
extra: None, extra: None,
option: None, option: None,
@@ -604,6 +632,7 @@ impl PrfItem {
home: None, home: None,
support_url: None, support_url: None,
announce: None, announce: None,
announce_url: None,
selected: None, selected: None,
extra: None, extra: None,
option: None, option: None,

View File

@@ -221,6 +221,7 @@ impl IProfiles {
each.updated = item.updated; each.updated = item.updated;
each.home = item.home; each.home = item.home;
each.announce = item.announce; each.announce = item.announce;
each.announce_url = item.announce_url;
each.support_url = item.support_url; each.support_url = item.support_url;
each.name = item.name; each.name = item.name;
each.url = item.url; each.url = item.url;

View File

@@ -216,6 +216,14 @@ pub fn run() {
app.manage(Mutex::new(state::proxy::CmdProxyState::default())); app.manage(Mutex::new(state::proxy::CmdProxyState::default()));
app.manage(Mutex::new(state::lightweight::LightWeightState::default())); app.manage(Mutex::new(state::lightweight::LightWeightState::default()));
tauri::async_runtime::spawn(async {
tokio::time::sleep(Duration::from_secs(5)).await;
logging!(info, Type::Cmd, true, "Running profile updates at startup...");
if let Err(e) = crate::cmd::update_profiles_on_startup().await {
log::error!("Failed to update profiles on startup: {}", e);
}
});
logging!(info, Type::Setup, true, "初始化完成,继续执行"); logging!(info, Type::Setup, true, "初始化完成,继续执行");
Ok(()) Ok(())
}) })
@@ -295,6 +303,7 @@ pub fn run() {
cmd::read_profile_file, cmd::read_profile_file,
cmd::save_profile_file, cmd::save_profile_file,
cmd::get_next_update_time, cmd::get_next_update_time,
cmd::update_profiles_on_startup,
// script validation // script validation
cmd::script_validate_notice, cmd::script_validate_notice,
cmd::validate_script_file, cmd::validate_script_file,

View File

@@ -447,6 +447,21 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
</FormItem> </FormItem>
)} )}
/> />
<FormField
control={control}
name="option.update_always"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between">
<FormLabel>{t("Update on Startup")}</FormLabel>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
<FormField <FormField
control={control} control={control}
name="option.with_proxy" name="option.with_proxy"

View File

@@ -203,6 +203,7 @@ interface IProfileItem {
home?: string; home?: string;
support_url?: string; support_url?: string;
announce?: string; announce?: string;
announce_url?: string;
} }
interface IProfileOption { interface IProfileOption {
@@ -210,6 +211,7 @@ interface IProfileOption {
with_proxy?: boolean; with_proxy?: boolean;
self_proxy?: boolean; self_proxy?: boolean;
update_interval?: number; update_interval?: number;
update_always?: boolean;
timeout_seconds?: number; timeout_seconds?: number;
danger_accept_invalid_certs?: boolean; danger_accept_invalid_certs?: boolean;
merge?: string; merge?: string;
@@ -752,6 +754,7 @@ interface IVergeConfig {
enable_global_hotkey?: boolean; enable_global_hotkey?: boolean;
enable_dns_settings?: boolean; enable_dns_settings?: boolean;
primary_action?: "tun-mode" | "system-proxy"; primary_action?: "tun-mode" | "system-proxy";
enable_send_hwid?: boolean;
proxy_auto_config?: boolean; proxy_auto_config?: boolean;
pac_file_content?: string; pac_file_content?: string;
proxy_host?: string; proxy_host?: string;