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]
pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult {
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?;
wrap_err!(Config::profiles().data().append_item(item))
let existing_uid = {
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);
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")]
pub announce: Option<String>,
/// profile announce url
#[serde(skip_serializing_if = "Option::is_none")]
pub announce_url: Option<String>,
/// the file data
#[serde(skip)]
pub file_data: Option<String>,
@@ -126,6 +130,9 @@ pub struct PrfOption {
#[serde(skip_serializing_if = "Option::is_none")]
pub use_hwid: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub update_always: Option<bool>,
}
impl PrfOption {
@@ -146,6 +153,7 @@ impl PrfOption {
a.groups = b.groups.or(a.groups);
a.timeout_seconds = b.timeout_seconds.or(a.timeout_seconds);
a.use_hwid = b.use_hwid.or(a.use_hwid);
a.update_always = b.update_always.or(a.update_always);
Some(a)
}
t => t.0.or(t.1),
@@ -246,6 +254,7 @@ impl PrfItem {
home: None,
support_url: None,
announce: None,
announce_url: None,
updated: Some(chrono::Local::now().timestamp() as usize),
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 update_interval = opt_ref.and_then(|o| o.update_interval);
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 script = opt_ref.and_then(|o| o.script.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") {
Some(value) => {
let str_value = value.to_str().unwrap_or("");
@@ -403,6 +417,14 @@ impl PrfItem {
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") {
Some(value) => {
let str_value = value.to_str().unwrap_or("");
@@ -472,6 +494,7 @@ impl PrfItem {
extra,
option: Some(PrfOption {
update_interval,
update_always,
merge,
script,
rules,
@@ -482,6 +505,7 @@ impl PrfItem {
home,
support_url,
announce,
announce_url,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(data.into()),
})
@@ -511,6 +535,7 @@ impl PrfItem {
home: None,
support_url: None,
announce: None,
announce_url: None,
updated: Some(chrono::Local::now().timestamp() as usize),
file_data: Some(template),
})
@@ -535,6 +560,7 @@ impl PrfItem {
home: None,
support_url: None,
announce: None,
announce_url: None,
selected: None,
extra: None,
option: None,
@@ -558,6 +584,7 @@ impl PrfItem {
home: None,
support_url: None,
announce: None,
announce_url: None,
selected: None,
extra: None,
option: None,
@@ -581,6 +608,7 @@ impl PrfItem {
home: None,
support_url: None,
announce: None,
announce_url: None,
selected: None,
extra: None,
option: None,
@@ -604,6 +632,7 @@ impl PrfItem {
home: None,
support_url: None,
announce: None,
announce_url: None,
selected: None,
extra: None,
option: None,

View File

@@ -221,6 +221,7 @@ impl IProfiles {
each.updated = item.updated;
each.home = item.home;
each.announce = item.announce;
each.announce_url = item.announce_url;
each.support_url = item.support_url;
each.name = item.name;
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::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, "初始化完成,继续执行");
Ok(())
})
@@ -295,6 +303,7 @@ pub fn run() {
cmd::read_profile_file,
cmd::save_profile_file,
cmd::get_next_update_time,
cmd::update_profiles_on_startup,
// script validation
cmd::script_validate_notice,
cmd::validate_script_file,

View File

@@ -447,6 +447,21 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
</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
control={control}
name="option.with_proxy"

View File

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