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:
@@ -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(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
3
src/services/types.d.ts
vendored
3
src/services/types.d.ts
vendored
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user