diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index 39e9bf4e63c23196a5bba55032c45bcaa58d8d65..d98017644411166e0cc15299fb070556829a16c9 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -14,8 +14,7 @@ type CmdResult<T = ()> = Result<T, String>; #[tauri::command] pub fn get_profiles() -> CmdResult<IProfiles> { - let profiles = ProfilesN::global().config.lock(); - Ok(profiles.clone()) + Ok(Config::profiles().data().clone()) } #[tauri::command] @@ -27,47 +26,80 @@ 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)?; - let mut profiles = ProfilesN::global().config.lock(); - wrap_err!(profiles.append_item(item)) + wrap_err!(Config::profiles().data().append_item(item)) } #[tauri::command] pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResult { let item = wrap_err!(PrfItem::from(item, file_data).await)?; - let mut profiles = ProfilesN::global().config.lock(); - wrap_err!(profiles.append_item(item)) + wrap_err!(Config::profiles().data().append_item(item)) } #[tauri::command] pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResult { - wrap_err!(ProfilesN::global().update_item(index, option).await) + wrap_err!(feat::update_profile(index, option).await) } #[tauri::command] pub async fn select_profile(index: String) -> CmdResult { - wrap_err!({ ProfilesN::global().config.lock().put_current(index) })?; - wrap_err!(CoreManager::global().activate_config().await) + wrap_err!({ Config::profiles().draft().put_current(index) })?; + + match feat::handle_activate().await { + Ok(_) => { + Config::profiles().apply(); + wrap_err!(Config::profiles().data().save_file())?; + Ok(()) + } + Err(err) => { + Config::profiles().discard(); + log::error!(target: "app", "{err}"); + Err(format!("{err}")) + } + } } /// change the profile chain #[tauri::command] pub async fn change_profile_chain(chain: Option<Vec<String>>) -> CmdResult { - wrap_err!({ ProfilesN::global().config.lock().put_chain(chain) })?; - wrap_err!(CoreManager::global().activate_config().await) + wrap_err!({ Config::profiles().draft().put_chain(chain) })?; + + match feat::handle_activate().await { + Ok(_) => { + Config::profiles().apply(); + wrap_err!(Config::profiles().data().save_file())?; + Ok(()) + } + Err(err) => { + Config::profiles().discard(); + log::error!(target: "app", "{err}"); + Err(format!("{err}")) + } + } } #[tauri::command] pub async fn change_profile_valid(valid: Option<Vec<String>>) -> CmdResult { - wrap_err!({ ProfilesN::global().config.lock().put_valid(valid) })?; - wrap_err!(CoreManager::global().activate_config().await) + wrap_err!({ Config::profiles().draft().put_valid(valid) })?; + + match feat::handle_activate().await { + Ok(_) => { + Config::profiles().apply(); + wrap_err!(Config::profiles().data().save_file())?; + Ok(()) + } + Err(err) => { + Config::profiles().discard(); + log::error!(target: "app", "{err}"); + Err(format!("{err}")) + } + } } #[tauri::command] pub async fn delete_profile(index: String) -> CmdResult { - let should_update = { wrap_err!(ProfilesN::global().config.lock().delete_item(index))? }; - + let should_update = wrap_err!({ Config::profiles().data().delete_item(index) })?; if should_update { - wrap_err!(CoreManager::global().activate_config().await)?; + wrap_err!(feat::handle_activate().await)?; } Ok(()) @@ -75,19 +107,20 @@ pub async fn delete_profile(index: String) -> CmdResult { #[tauri::command] pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult { - let mut profiles = ProfilesN::global().config.lock(); - wrap_err!(profiles.patch_item(index, profile))?; - drop(profiles); + wrap_err!(Config::profiles().data().patch_item(index, profile))?; wrap_err!(timer::Timer::global().refresh()) } #[tauri::command] pub fn view_profile(index: String) -> CmdResult { - let profiles = ProfilesN::global().config.lock(); - let item = wrap_err!(profiles.get_item(&index))?; + let file = { + wrap_err!(Config::profiles().latest().get_item(&index))? + .file + .clone() + .ok_or("the file field is null") + }?; - let file = item.file.clone().ok_or("the file field is null")?; let path = dirs::app_profiles_dir().join(file); if !path.exists() { ret_err!("the file not found"); @@ -98,7 +131,8 @@ pub fn view_profile(index: String) -> CmdResult { #[tauri::command] pub fn read_profile_file(index: String) -> CmdResult<String> { - let profiles = ProfilesN::global().config.lock(); + let profiles = Config::profiles(); + let profiles = profiles.latest(); let item = wrap_err!(profiles.get_item(&index))?; let data = wrap_err!(item.read_file())?; Ok(data) @@ -110,14 +144,15 @@ pub fn save_profile_file(index: String, file_data: Option<String>) -> CmdResult return Ok(()); } - let profiles = ProfilesN::global().config.lock(); + let profiles = Config::profiles(); + let profiles = profiles.latest(); let item = wrap_err!(profiles.get_item(&index))?; wrap_err!(item.save_file(file_data.unwrap())) } #[tauri::command] pub fn get_clash_info() -> CmdResult<ClashInfoN> { - Ok(ClashN::global().info.lock().clone()) + wrap_err!(Config::clash().latest().get_info()) } #[tauri::command] @@ -153,18 +188,18 @@ pub fn get_runtime_logs() -> CmdResult<HashMap<String, Vec<(String, String)>>> { } #[tauri::command] -pub fn patch_clash_config(payload: Mapping) -> CmdResult { - wrap_err!(feat::patch_clash(payload)) +pub async fn patch_clash_config(payload: Mapping) -> CmdResult { + wrap_err!(feat::patch_clash(payload).await) } #[tauri::command] pub fn get_verge_config() -> CmdResult<IVerge> { - Ok(VergeN::global().config.lock().clone()) + Ok(Config::verge().data().clone()) } #[tauri::command] -pub fn patch_verge_config(payload: IVerge) -> CmdResult { - wrap_err!(feat::patch_verge(payload)) +pub async fn patch_verge_config(payload: IVerge) -> CmdResult { + wrap_err!(feat::patch_verge(payload).await) } #[tauri::command] diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index e59b307bcd8afd922704988badec2efd2f8bb26e..d590383ea2ee95622a0db6d609b33add441236ee 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -1,10 +1,7 @@ use crate::utils::{config, dirs}; use anyhow::Result; -use once_cell::sync::OnceCell; -use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use serde_yaml::{Mapping, Value}; -use std::{net::SocketAddr, sync::Arc}; #[derive(Default, Debug, Clone)] pub struct IClashTemp(pub Mapping); @@ -33,69 +30,6 @@ impl IClashTemp { } } -#[derive(Debug)] -#[deprecated] -pub struct ClashN { - /// maintain the clash config - pub config: Arc<Mutex<Mapping>>, - /// some info - pub info: Arc<Mutex<ClashInfoN>>, -} - -impl ClashN { - pub fn global() -> &'static ClashN { - static DATA: OnceCell<ClashN> = OnceCell::new(); - - DATA.get_or_init(|| { - let config = ClashN::read_config(); - let info = ClashInfoN::from(&config); - - ClashN { - config: Arc::new(Mutex::new(config)), - info: Arc::new(Mutex::new(info)), - } - }) - } - - /// get clash config - pub fn read_config() -> Mapping { - config::read_merge_mapping(dirs::clash_path()) - } - - /// save the clash config - pub fn save_config(&self) -> Result<()> { - let config = self.config.lock(); - - config::save_yaml( - dirs::clash_path(), - &*config, - Some("# Default Config For ClashN Core\n\n"), - ) - } - - /// 返回旧值 - pub fn patch_info(&self, info: ClashInfoN) -> Result<ClashInfoN> { - let mut old_info = self.info.lock(); - let old = (*old_info).to_owned(); - *old_info = info; - Ok(old) - } - - /// patch update the clash config - /// if the port is changed then return true - pub fn patch_config(&self, patch: Mapping) -> Result<()> { - let mut config = self.config.lock(); - - for (key, value) in patch.into_iter() { - config.insert(key, value); - } - - drop(config); - - self.save_config() - } -} - #[derive(Default, Debug, Clone, Deserialize, Serialize)] pub struct ClashInfoN { /// clash sidecar status @@ -245,21 +179,3 @@ pub struct IClashFallbackFilter { pub ipcidr: Option<Vec<String>>, pub domain: Option<Vec<String>>, } - -#[test] -fn test() { - let socket = SocketAddr::new("127.0.0.1".parse().unwrap(), 9090); - - let s = "[::]:8080".parse::<SocketAddr>(); - - dbg!(s); - - // match "::8080".parse::<SocketAddr>() { - // Ok(_) => {} - // Err(err) => { - - // } - // } - - // assert_eq!(":8080".parse(), Ok(socket)); -} diff --git a/src-tauri/src/config/config.rs b/src-tauri/src/config/config.rs index 9ca78df158628da7eb5685929364c0014df6706b..56b7149f6d2ba61e36f9a8af9d9cab63f562ff7a 100644 --- a/src-tauri/src/config/config.rs +++ b/src-tauri/src/config/config.rs @@ -1,7 +1,5 @@ use super::{Draft, IClashTemp, IProfiles, IVerge}; -use crate::config::ClashN; use once_cell::sync::OnceCell; -use serde_yaml::Mapping; pub struct Config { clash_config: Draft<IClashTemp>, @@ -20,18 +18,6 @@ impl Config { }) } - // pub fn clash<'a>() -> MappedMutexGuard<'a, IClash> { - // Self::global().clash_config.latest() - // } - - // pub fn verge<'a>() -> MappedMutexGuard<'a, IVerge> { - // Self::global().verge_config.latest() - // } - - // pub fn profiles<'a>() -> MappedMutexGuard<'a, IProfiles> { - // Self::global().profiles_config.latest() - // } - pub fn clash() -> Draft<IClashTemp> { Self::global().clash_config.clone() } diff --git a/src-tauri/src/config/draft.rs b/src-tauri/src/config/draft.rs index 64bc5c82135d8a5405d0972d3d823fe1c84e5272..2f70b288b7c130ccbbf85c4b72f92b38ca3fe9a9 100644 --- a/src-tauri/src/config/draft.rs +++ b/src-tauri/src/config/draft.rs @@ -1,6 +1,5 @@ -use super::{IClash, IClashTemp, IProfiles, IVerge}; +use super::{IClashTemp, IProfiles, IVerge}; use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; -use serde_yaml::Mapping; use std::sync::Arc; #[derive(Debug, Clone)] @@ -64,10 +63,10 @@ macro_rules! draft_define { }; } -draft_define!(IClash); +// draft_define!(IClash); draft_define!(IClashTemp); draft_define!(IVerge); -draft_define!(Mapping); +// draft_define!(Mapping); draft_define!(IProfiles); #[test] diff --git a/src-tauri/src/config/prfitem.rs b/src-tauri/src/config/prfitem.rs index 62dc9b63d6287cc889531301b9cc29cab5e1d29e..a377fb918b80905aaab4061832a1229bad79fe96 100644 --- a/src-tauri/src/config/prfitem.rs +++ b/src-tauri/src/config/prfitem.rs @@ -6,6 +6,8 @@ use serde_yaml::Mapping; use std::fs; use sysproxy::Sysproxy; +use super::Config; + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct PrfItem { pub uid: Option<String>, @@ -192,9 +194,9 @@ impl PrfItem { // ä½¿ç”¨è½¯ä»¶è‡ªå·±çš„ä»£ç† if self_proxy { - let clash = super::ClashN::global(); - let port = clash.info.lock().port.clone(); + let port = Config::clash().data().get_info()?.port; let port = port.ok_or(anyhow::anyhow!("failed to get clash info port"))?; + let proxy_scheme = format!("http://127.0.0.1:{port}"); if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) { diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index 3779c51d370dbf803c4e7a954fdbc5145fc6fc6e..fe7f4c02eddd3b88cd9c9a6c593de2edebf3f8fd 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -1,82 +1,25 @@ -use super::{prfitem::PrfItem, ChainItem, PrfOption}; -use crate::{ - core::CoreManager, - utils::{config, dirs, help}, -}; +use super::{prfitem::PrfItem, ChainItem}; +use crate::utils::{config, dirs, help}; use anyhow::{bail, Context, Result}; -use once_cell::sync::OnceCell; -use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use serde_yaml::Mapping; use std::collections::HashMap; -use std::sync::Arc; use std::{fs, io::Write}; -#[deprecated] -pub struct ProfilesN { - pub config: Arc<Mutex<IProfiles>>, -} - -impl ProfilesN { - pub fn global() -> &'static ProfilesN { - static PROFILES: OnceCell<ProfilesN> = OnceCell::new(); - - PROFILES.get_or_init(|| ProfilesN { - config: Arc::new(Mutex::new(IProfiles::read_file())), - }) - } - - /// æ›´æ–°å•ä¸ªé…ç½® - pub async fn update_item(&self, uid: String, option: Option<PrfOption>) -> Result<()> { - let url_opt = { - let profiles = self.config.lock(); - let item = profiles.get_item(&uid)?; - let is_remote = item.itype.as_ref().map_or(false, |s| s == "remote"); - - if !is_remote { - None // 直接更新 - } else if item.url.is_none() { - bail!("failed to get the profile item url"); - } else { - Some((item.url.clone().unwrap(), item.option.clone())) - } - }; - - let should_update = match url_opt { - Some((url, opt)) => { - let merged_opt = PrfOption::merge(opt, option); - let item = PrfItem::from_url(&url, None, None, merged_opt).await?; - - let mut profiles = self.config.lock(); - profiles.update_item(uid.clone(), item)?; - - Some(uid) == profiles.get_current() - } - None => true, - }; - - if should_update { - CoreManager::global().activate_config().await?; - } - - Ok(()) - } -} - /// Define the `profiles.yaml` schema #[derive(Default, Debug, Clone, Deserialize, Serialize)] pub struct IProfiles { /// same as PrfConfig.current - current: Option<String>, + pub current: Option<String>, /// same as PrfConfig.chain - chain: Option<Vec<String>>, + pub chain: Option<Vec<String>>, /// record valid fields for clash - valid: Option<Vec<String>>, + pub valid: Option<Vec<String>>, /// profile list - items: Option<Vec<PrfItem>>, + pub items: Option<Vec<PrfItem>>, } macro_rules! patch { @@ -134,7 +77,7 @@ impl IProfiles { if items.iter().find(|&each| each.uid == some_uid).is_some() { self.current = some_uid; - return self.save_file(); + return self.save_file(); // todo remove } bail!("invalid uid \"{uid}\""); diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index 7df68f9a4b94beacd9b9b9466c70c908baf018ec..c5986a95cc6bf822488642a2a743e7da0c9d6ec5 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -1,53 +1,6 @@ use crate::utils::{config, dirs}; use anyhow::Result; -use once_cell::sync::OnceCell; -use parking_lot::Mutex; use serde::{Deserialize, Serialize}; -use std::sync::Arc; - -#[deprecated] -pub struct VergeN { - pub config: Arc<Mutex<IVerge>>, -} - -impl VergeN { - pub fn global() -> &'static VergeN { - static DATA: OnceCell<VergeN> = OnceCell::new(); - - DATA.get_or_init(|| { - let config = config::read_yaml::<IVerge>(dirs::verge_path()); - VergeN { - config: Arc::new(Mutex::new(config)), - } - }) - } - - /// Save IVerge App Config - pub fn save_file(&self) -> Result<()> { - self.config.lock().save_file() - } - - /// patch verge config - /// only save to file - pub fn patch_config(&self, patch: IVerge) -> Result<()> { - { - self.config.lock().patch_config(patch); - } - self.save_file() - } - - /// 在åˆå§‹åŒ–å‰å°è¯•æ‹¿åˆ°å•ä¾‹ç«¯å£çš„值 - pub fn get_singleton_port() -> u16 { - let config = config::read_yaml::<IVerge>(dirs::verge_path()); - - #[cfg(not(feature = "verge-dev"))] - const SERVER_PORT: u16 = 33331; - #[cfg(feature = "verge-dev")] - const SERVER_PORT: u16 = 11233; - - config.app_singleton_port.unwrap_or(SERVER_PORT) - } -} /// ### `verge.yaml` schema #[derive(Default, Debug, Clone, Deserialize, Serialize)] @@ -177,4 +130,16 @@ impl IVerge { patch!(auto_close_connection); patch!(default_latency_test); } + + /// 在åˆå§‹åŒ–å‰å°è¯•æ‹¿åˆ°å•ä¾‹ç«¯å£çš„值 + pub fn get_singleton_port() -> u16 { + let config = config::read_yaml::<IVerge>(dirs::verge_path()); + + #[cfg(not(feature = "verge-dev"))] + const SERVER_PORT: u16 = 33331; + #[cfg(feature = "verge-dev")] + const SERVER_PORT: u16 = 11233; + + config.app_singleton_port.unwrap_or(SERVER_PORT) + } } diff --git a/src-tauri/src/core/clash_api.rs b/src-tauri/src/core/clash_api.rs index 2504280a746b032db455552017c34f160e0ceaa1..3dd796c6525d1ddd33b7af918f1b31cc2c7956ef 100644 --- a/src-tauri/src/core/clash_api.rs +++ b/src-tauri/src/core/clash_api.rs @@ -1,4 +1,4 @@ -use crate::{config, utils::dirs}; +use crate::{config::Config, utils::dirs}; use anyhow::{bail, Result}; use reqwest::header::HeaderMap; use serde_yaml::Mapping; @@ -40,7 +40,7 @@ pub async fn patch_configs(config: &Mapping) -> Result<()> { /// æ ¹æ®clash info获å–clashæœåŠ¡åœ°å€å’Œè¯·æ±‚头 fn clash_client_info() -> Result<(String, HeaderMap)> { - let info = { config::ClashN::global().info.lock().clone() }; + let info = { Config::clash().data().get_info()? }; if info.server.is_none() { let status = &info.status; diff --git a/src-tauri/src/core/hotkey.rs b/src-tauri/src/core/hotkey.rs index cd844db5a61a7d40187dc0d5d2352c479a0b5d02..38565bc326fc9c595aac8c38b1b6d7bb6d310bc8 100644 --- a/src-tauri/src/core/hotkey.rs +++ b/src-tauri/src/core/hotkey.rs @@ -64,12 +64,12 @@ impl Hotkey { "clash_mode_global" => || feat::change_clash_mode("global".into()), "clash_mode_direct" => || feat::change_clash_mode("direct".into()), "clash_mode_script" => || feat::change_clash_mode("script".into()), - "toggle_system_proxy" => || log_err!(feat::toggle_system_proxy()), - "enable_system_proxy" => || log_err!(feat::enable_system_proxy()), - "disable_system_proxy" => || log_err!(feat::disable_system_proxy()), - "toggle_tun_mode" => || log_err!(feat::toggle_tun_mode()), - "enable_tun_mode" => || log_err!(feat::enable_tun_mode()), - "disable_tun_mode" => || log_err!(feat::disable_tun_mode()), + "toggle_system_proxy" => || feat::toggle_system_proxy(), + "enable_system_proxy" => || feat::enable_system_proxy(), + "disable_system_proxy" => || feat::disable_system_proxy(), + "toggle_tun_mode" => || feat::toggle_tun_mode(), + "enable_tun_mode" => || feat::enable_tun_mode(), + "disable_tun_mode" => || feat::disable_tun_mode(), _ => bail!("invalid function \"{func}\""), }; diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index f9231e07376399c3302d2a2f5a3441509e02852c..152ce581e0ac3ba6178534ca507005d669b95cd3 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -144,13 +144,8 @@ impl Sysopt { /// init the auto launch pub fn init_launch(&self) -> Result<()> { - let enable = { - Config::verge() - .latest() - .enable_auto_launch - .clone() - .unwrap_or(false) - }; + let enable = { Config::verge().latest().enable_auto_launch.clone() }; + let enable = enable.unwrap_or(false); let app_exe = current_exe()?; let app_exe = dunce::canonicalize(app_exe)?; @@ -213,13 +208,8 @@ impl Sysopt { drop(auto_launch); return self.init_launch(); } - let enable = { - Config::verge() - .latest() - .enable_auto_launch - .clone() - .unwrap_or(false) - }; + let enable = { Config::verge().latest().enable_auto_launch.clone() }; + let enable = enable.unwrap_or(false); let auto_launch = auto_launch.as_ref().unwrap(); match enable { diff --git a/src-tauri/src/core/timer.rs b/src-tauri/src/core/timer.rs index 0c612b0c1946fff6e7f4d17c47974838ed7de1a0..853736723786f0f5cdaff1461e34cef15498f7be 100644 --- a/src-tauri/src/core/timer.rs +++ b/src-tauri/src/core/timer.rs @@ -1,4 +1,5 @@ -use crate::config::{self, ProfilesN}; +use crate::config::Config; +use crate::feat; use anyhow::{Context, Result}; use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder}; use once_cell::sync::OnceCell; @@ -36,12 +37,10 @@ impl Timer { let cur_timestamp = chrono::Local::now().timestamp(); - let profiles = config::ProfilesN::global().config.lock(); - let timer_map = self.timer_map.lock(); let delay_timer = self.delay_timer.lock(); - profiles.get_items().map(|items| { + Config::profiles().latest().get_items().map(|items| { items .iter() // .filter_map(|item| { @@ -100,11 +99,9 @@ impl Timer { /// generate a uid -> update_interval map fn gen_map(&self) -> HashMap<String, u64> { - let profiles = config::ProfilesN::global().config.lock(); - let mut new_map = HashMap::new(); - if let Some(items) = profiles.get_items() { + if let Some(items) = Config::profiles().latest().get_items() { for item in items.iter() { if item.option.is_some() { let option = item.option.as_ref().unwrap(); @@ -178,7 +175,7 @@ impl Timer { /// the task runner async fn async_task(uid: String) { log::info!(target: "app", "running timer task `{uid}`"); - crate::log_err!(ProfilesN::global().update_item(uid, None).await); + crate::log_err!(feat::update_profile(uid, None).await); } } diff --git a/src-tauri/src/core/tray.rs b/src-tauri/src/core/tray.rs index 36fa65283517d0a0a1ddc6de5e9be97d029c5fc0..8ec8d095bc1136b62fa99aaa268dafb645b777c6 100644 --- a/src-tauri/src/core/tray.rs +++ b/src-tauri/src/core/tray.rs @@ -1,4 +1,3 @@ -use crate::log_err; use crate::{config::Config, feat, utils::resolve}; use anyhow::Result; use tauri::{ @@ -109,8 +108,8 @@ impl Tray { } "open_window" => resolve::create_window(app_handle), - "system_proxy" => log_err!(feat::toggle_system_proxy()), - "tun_mode" => log_err!(feat::toggle_tun_mode()), + "system_proxy" => feat::toggle_system_proxy(), + "tun_mode" => feat::toggle_tun_mode(), "restart_clash" => feat::restart_clash_core(), "restart_app" => api::process::restart(&app_handle.env()), "quit" => { diff --git a/src-tauri/src/data/clash.rs b/src-tauri/src/data/clash.rs deleted file mode 100644 index 73ecd35381c77912e8cd138a9ee2e722159ad7c2..0000000000000000000000000000000000000000 --- a/src-tauri/src/data/clash.rs +++ /dev/null @@ -1,167 +0,0 @@ -use crate::utils::{config, dirs}; -use anyhow::Result; -use serde::{Deserialize, Serialize}; -use serde_yaml::{Mapping, Value}; - -#[derive(Default, Debug, Clone, Deserialize, Serialize)] -pub struct ClashInfo { - /// clash sidecar status - pub status: String, - - /// clash core port - pub port: Option<String>, - - /// same as `external-controller` - pub server: Option<String>, - - /// clash secret - pub secret: Option<String>, -} - -impl ClashInfo { - /// parse the clash's config.yaml - /// get some information - pub fn from(config: &Mapping) -> ClashInfo { - let key_port_1 = Value::from("mixed-port"); - let key_port_2 = Value::from("port"); - let key_server = Value::from("external-controller"); - let key_secret = Value::from("secret"); - - let mut status: u32 = 0; - - let port = match config.get(&key_port_1) { - Some(value) => match value { - Value::String(val_str) => Some(val_str.clone()), - Value::Number(val_num) => Some(val_num.to_string()), - _ => { - status |= 0b1; - None - } - }, - _ => { - status |= 0b10; - None - } - }; - let port = match port { - Some(_) => port, - None => match config.get(&key_port_2) { - Some(value) => match value { - Value::String(val_str) => Some(val_str.clone()), - Value::Number(val_num) => Some(val_num.to_string()), - _ => { - status |= 0b100; - None - } - }, - _ => { - status |= 0b1000; - None - } - }, - }; - - // `external-controller` could be - // "127.0.0.1:9090" or ":9090" - let server = match config.get(&key_server) { - Some(value) => match value.as_str() { - Some(val_str) => { - if val_str.starts_with(":") { - Some(format!("127.0.0.1{val_str}")) - } else if val_str.starts_with("0.0.0.0:") { - Some(format!("127.0.0.1:{}", &val_str[8..])) - } else if val_str.starts_with("[::]:") { - Some(format!("127.0.0.1:{}", &val_str[5..])) - } else { - Some(val_str.into()) - } - } - None => { - status |= 0b10000; - None - } - }, - None => { - status |= 0b100000; - None - } - }; - - let secret = match config.get(&key_secret) { - Some(value) => match value { - Value::String(val_str) => Some(val_str.clone()), - Value::Bool(val_bool) => Some(val_bool.to_string()), - Value::Number(val_num) => Some(val_num.to_string()), - _ => None, - }, - _ => None, - }; - - ClashInfo { - status: format!("{status}"), - port, - server, - secret, - } - } -} - -#[derive(Debug)] -pub struct Clash { - /// maintain the clash config - pub config: Mapping, - - /// some info - pub info: ClashInfo, -} - -impl Clash { - pub fn new() -> Clash { - let config = Clash::read_config(); - let info = ClashInfo::from(&config); - - Clash { config, info } - } - - /// get clash config - pub fn read_config() -> Mapping { - config::read_merge_mapping(dirs::clash_path()) - } - - /// save the clash config - pub fn save_config(&self) -> Result<()> { - config::save_yaml( - dirs::clash_path(), - &self.config, - Some("# Default Config For Clash Core\n\n"), - ) - } - - /// patch update the clash config - /// if the port is changed then return true - pub fn patch_config(&mut self, patch: Mapping) -> Result<()> { - let port_key = Value::from("mixed-port"); - let server_key = Value::from("external-controller"); - let secret_key = Value::from("secret"); - - let change_info = patch.contains_key(&port_key) - || patch.contains_key(&server_key) - || patch.contains_key(&secret_key); - - for (key, value) in patch.into_iter() { - self.config.insert(key, value); - } - - if change_info { - self.info = ClashInfo::from(&self.config); - } - - self.save_config() - } -} - -impl Default for Clash { - fn default() -> Self { - Clash::new() - } -} diff --git a/src-tauri/src/data/mod.rs b/src-tauri/src/data/mod.rs deleted file mode 100644 index eb6a406433242d3e6f06a485c2286ece68451439..0000000000000000000000000000000000000000 --- a/src-tauri/src/data/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -mod clash; -mod prfitem; -mod profiles; -mod verge; - -pub use self::clash::*; -pub use self::prfitem::*; -pub use self::profiles::*; -pub use self::verge::*; - -use once_cell::sync::OnceCell; -use parking_lot::Mutex; -use std::sync::Arc; - -#[derive(Debug, Clone)] -pub struct Data { - pub clash: Arc<Mutex<Clash>>, - pub verge: Arc<Mutex<Verge>>, - pub profiles: Arc<Mutex<Profiles>>, -} - -impl Data { - pub fn global() -> &'static Data { - static DATA: OnceCell<Data> = OnceCell::new(); - - DATA.get_or_init(|| Data { - clash: Arc::new(Mutex::new(Clash::new())), - verge: Arc::new(Mutex::new(Verge::new())), - profiles: Arc::new(Mutex::new(Profiles::new())), - }) - } -} diff --git a/src-tauri/src/data/prfitem.rs b/src-tauri/src/data/prfitem.rs deleted file mode 100644 index 90b08f36045e1df6c8d041b8e67aa40512440243..0000000000000000000000000000000000000000 --- a/src-tauri/src/data/prfitem.rs +++ /dev/null @@ -1,406 +0,0 @@ -use crate::utils::{config, dirs, help, tmpl}; -use anyhow::{bail, Context, Result}; -use reqwest::StatusCode; -use serde::{Deserialize, Serialize}; -use serde_yaml::Mapping; -use std::fs; -use sysproxy::Sysproxy; - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct PrfItem { - pub uid: Option<String>, - - /// profile item type - /// enum value: remote | local | script | merge - #[serde(rename = "type")] - pub itype: Option<String>, - - /// profile name - pub name: Option<String>, - - /// profile file - pub file: Option<String>, - - /// profile description - #[serde(skip_serializing_if = "Option::is_none")] - pub desc: Option<String>, - - /// source url - #[serde(skip_serializing_if = "Option::is_none")] - pub url: Option<String>, - - /// selected infomation - #[serde(skip_serializing_if = "Option::is_none")] - pub selected: Option<Vec<PrfSelected>>, - - /// subscription user info - #[serde(skip_serializing_if = "Option::is_none")] - pub extra: Option<PrfExtra>, - - /// updated time - pub updated: Option<usize>, - - /// some options of the item - #[serde(skip_serializing_if = "Option::is_none")] - pub option: Option<PrfOption>, - - /// the file data - #[serde(skip)] - pub file_data: Option<String>, -} - -#[derive(Default, Debug, Clone, Deserialize, Serialize)] -pub struct PrfSelected { - pub name: Option<String>, - pub now: Option<String>, -} - -#[derive(Default, Debug, Clone, Copy, Deserialize, Serialize)] -pub struct PrfExtra { - pub upload: usize, - pub download: usize, - pub total: usize, - pub expire: usize, -} - -#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] -pub struct PrfOption { - /// for `remote` profile's http request - /// see issue #13 - #[serde(skip_serializing_if = "Option::is_none")] - pub user_agent: Option<String>, - - /// for `remote` profile - /// use system proxy - #[serde(skip_serializing_if = "Option::is_none")] - pub with_proxy: Option<bool>, - - /// for `remote` profile - /// use self proxy - #[serde(skip_serializing_if = "Option::is_none")] - pub self_proxy: Option<bool>, - - #[serde(skip_serializing_if = "Option::is_none")] - pub update_interval: Option<u64>, -} - -impl PrfOption { - pub fn merge(one: Option<Self>, other: Option<Self>) -> Option<Self> { - match (one, other) { - (Some(mut a), Some(b)) => { - a.user_agent = b.user_agent.or(a.user_agent); - a.with_proxy = b.with_proxy.or(a.with_proxy); - a.self_proxy = b.self_proxy.or(a.self_proxy); - a.update_interval = b.update_interval.or(a.update_interval); - Some(a) - } - t @ _ => t.0.or(t.1), - } - } -} - -impl Default for PrfItem { - fn default() -> Self { - PrfItem { - uid: None, - itype: None, - name: None, - desc: None, - file: None, - url: None, - selected: None, - extra: None, - updated: None, - option: None, - file_data: None, - } - } -} - -impl PrfItem { - /// From partial item - /// must contain `itype` - pub async fn from(item: PrfItem, file_data: Option<String>) -> Result<PrfItem> { - if item.itype.is_none() { - bail!("type should not be null"); - } - - match item.itype.unwrap().as_str() { - "remote" => { - if item.url.is_none() { - bail!("url should not be null"); - } - let url = item.url.as_ref().unwrap().as_str(); - let name = item.name; - let desc = item.desc; - PrfItem::from_url(url, name, desc, item.option).await - } - "local" => { - let name = item.name.unwrap_or("Local File".into()); - let desc = item.desc.unwrap_or("".into()); - PrfItem::from_local(name, desc, file_data) - } - "merge" => { - let name = item.name.unwrap_or("Merge".into()); - let desc = item.desc.unwrap_or("".into()); - PrfItem::from_merge(name, desc) - } - "script" => { - let name = item.name.unwrap_or("Script".into()); - let desc = item.desc.unwrap_or("".into()); - PrfItem::from_script(name, desc) - } - typ @ _ => bail!("invalid profile item type \"{typ}\""), - } - } - - /// ## Local type - /// create a new item from name/desc - pub fn from_local(name: String, desc: String, file_data: Option<String>) -> Result<PrfItem> { - let uid = help::get_uid("l"); - let file = format!("{uid}.yaml"); - - Ok(PrfItem { - uid: Some(uid), - itype: Some("local".into()), - name: Some(name), - desc: Some(desc), - file: Some(file), - url: None, - selected: None, - extra: None, - option: None, - updated: Some(help::get_now()), - file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())), - }) - } - - /// ## Remote type - /// create a new item from url - pub async fn from_url( - url: &str, - name: Option<String>, - desc: Option<String>, - option: Option<PrfOption>, - ) -> Result<PrfItem> { - let opt_ref = option.as_ref(); - let with_proxy = opt_ref.map_or(false, |o| o.with_proxy.unwrap_or(false)); - let self_proxy = opt_ref.map_or(false, |o| o.self_proxy.unwrap_or(false)); - let user_agent = opt_ref.map_or(None, |o| o.user_agent.clone()); - - let mut builder = reqwest::ClientBuilder::new().no_proxy(); - - // ä½¿ç”¨è½¯ä»¶è‡ªå·±çš„ä»£ç† - if self_proxy { - let data = super::Data::global(); - let port = data.clash.lock().info.port.clone(); - let port = port.ok_or(anyhow::anyhow!("failed to get clash info port"))?; - let proxy_scheme = format!("http://127.0.0.1:{port}"); - - if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) { - builder = builder.proxy(proxy); - } - if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) { - builder = builder.proxy(proxy); - } - if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) { - builder = builder.proxy(proxy); - } - } - // ä½¿ç”¨ç³»ç»Ÿä»£ç† - else if with_proxy { - match Sysproxy::get_system_proxy() { - Ok(p @ Sysproxy { enable: true, .. }) => { - let proxy_scheme = format!("http://{}:{}", p.host, p.port); - - if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) { - builder = builder.proxy(proxy); - } - if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) { - builder = builder.proxy(proxy); - } - if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) { - builder = builder.proxy(proxy); - } - } - _ => {} - }; - } - - let version = unsafe { dirs::APP_VERSION }; - let version = format!("clash-verge/{version}"); - builder = builder.user_agent(user_agent.unwrap_or(version)); - - let resp = builder.build()?.get(url).send().await?; - - let status_code = resp.status(); - if !StatusCode::is_success(&status_code) { - bail!("failed to fetch remote profile with status {status_code}") - } - - let header = resp.headers(); - - // parse the Subscription UserInfo - let extra = match header.get("Subscription-Userinfo") { - Some(value) => { - let sub_info = value.to_str().unwrap_or(""); - - Some(PrfExtra { - upload: help::parse_str(sub_info, "upload=").unwrap_or(0), - download: help::parse_str(sub_info, "download=").unwrap_or(0), - total: help::parse_str(sub_info, "total=").unwrap_or(0), - expire: help::parse_str(sub_info, "expire=").unwrap_or(0), - }) - } - None => None, - }; - - // parse the Content-Disposition - let filename = match header.get("Content-Disposition") { - Some(value) => { - let filename = value.to_str().unwrap_or(""); - help::parse_str::<String>(filename, "filename=") - } - None => None, - }; - - // parse the profile-update-interval - let option = match header.get("profile-update-interval") { - Some(value) => match value.to_str().unwrap_or("").parse::<u64>() { - Ok(val) => Some(PrfOption { - update_interval: Some(val * 60), // hour -> min - ..PrfOption::default() - }), - Err(_) => None, - }, - None => None, - }; - - let uid = help::get_uid("r"); - let file = format!("{uid}.yaml"); - let name = name.unwrap_or(filename.unwrap_or("Remote File".into())); - let data = resp.text_with_charset("utf-8").await?; - - // check the data whether the valid yaml format - let yaml = serde_yaml::from_str::<Mapping>(&data) // - .context("the remote profile data is invalid yaml")?; - - if !yaml.contains_key("proxies") && !yaml.contains_key("proxy-providers") { - bail!("profile does not contain `proxies` or `proxy-providers`"); - } - - Ok(PrfItem { - uid: Some(uid), - itype: Some("remote".into()), - name: Some(name), - desc, - file: Some(file), - url: Some(url.into()), - selected: None, - extra, - option, - updated: Some(help::get_now()), - file_data: Some(data), - }) - } - - /// ## Merge type (enhance) - /// create the enhanced item by using `merge` rule - pub fn from_merge(name: String, desc: String) -> Result<PrfItem> { - let uid = help::get_uid("m"); - let file = format!("{uid}.yaml"); - - Ok(PrfItem { - uid: Some(uid), - itype: Some("merge".into()), - name: Some(name), - desc: Some(desc), - file: Some(file), - url: None, - selected: None, - extra: None, - option: None, - updated: Some(help::get_now()), - file_data: Some(tmpl::ITEM_MERGE.into()), - }) - } - - /// ## Script type (enhance) - /// create the enhanced item by using javascript(browserjs) - pub fn from_script(name: String, desc: String) -> Result<PrfItem> { - let uid = help::get_uid("s"); - let file = format!("{uid}.js"); // js ext - - Ok(PrfItem { - uid: Some(uid), - itype: Some("script".into()), - name: Some(name), - desc: Some(desc), - file: Some(file), - url: None, - selected: None, - extra: None, - option: None, - updated: Some(help::get_now()), - file_data: Some(tmpl::ITEM_SCRIPT.into()), - }) - } - - /// get the file data - pub fn read_file(&self) -> Result<String> { - if self.file.is_none() { - bail!("could not find the file"); - } - - let file = self.file.clone().unwrap(); - let path = dirs::app_profiles_dir().join(file); - fs::read_to_string(path).context("failed to read the file") - } - - /// save the file data - pub fn save_file(&self, data: String) -> Result<()> { - if self.file.is_none() { - bail!("could not find the file"); - } - - let file = self.file.clone().unwrap(); - let path = dirs::app_profiles_dir().join(file); - fs::write(path, data.as_bytes()).context("failed to save the file") - } - - /// get the data for enhanced mode - pub fn to_enhance(&self) -> Option<ChainItem> { - let itype = self.itype.as_ref()?.as_str(); - let file = self.file.clone()?; - let uid = self.uid.clone().unwrap_or("".into()); - let path = dirs::app_profiles_dir().join(file); - - if !path.exists() { - return None; - } - - match itype { - "script" => Some(ChainItem { - uid, - data: ChainType::Script(fs::read_to_string(path).unwrap_or("".into())), - }), - "merge" => Some(ChainItem { - uid, - data: ChainType::Merge(config::read_merge_mapping(path)), - }), - _ => None, - } - } -} - -#[derive(Debug, Clone)] -pub struct ChainItem { - pub uid: String, - pub data: ChainType, -} - -#[derive(Debug, Clone)] -pub enum ChainType { - Merge(Mapping), - Script(String), -} diff --git a/src-tauri/src/data/profiles.rs b/src-tauri/src/data/profiles.rs deleted file mode 100644 index 6272cd8ccedc0a4b37390b7917296693e92cba10..0000000000000000000000000000000000000000 --- a/src-tauri/src/data/profiles.rs +++ /dev/null @@ -1,329 +0,0 @@ -use super::prfitem::PrfItem; -use super::ChainItem; -use crate::utils::{config, dirs, help}; -use anyhow::{bail, Context, Result}; -use serde::{Deserialize, Serialize}; -use serde_yaml::Mapping; -use std::collections::HashMap; -use std::{fs, io::Write}; - -/// -/// ## Profiles Config -/// -/// Define the `profiles.yaml` schema -/// -#[derive(Default, Debug, Clone, Deserialize, Serialize)] -pub struct Profiles { - /// same as PrfConfig.current - current: Option<String>, - - /// same as PrfConfig.chain - chain: Option<Vec<String>>, - - /// record valid fields for clash - valid: Option<Vec<String>>, - - /// profile list - items: Option<Vec<PrfItem>>, -} - -macro_rules! patch { - ($lv: expr, $rv: expr, $key: tt) => { - if ($rv.$key).is_some() { - $lv.$key = $rv.$key; - } - }; -} - -impl Profiles { - pub fn new() -> Self { - Profiles::read_file() - } - - /// read the config from the file - pub fn read_file() -> Self { - let mut profiles = config::read_yaml::<Self>(dirs::profiles_path()); - - if profiles.items.is_none() { - profiles.items = Some(vec![]); - } - - // compatiable with the old old old version - profiles.items.as_mut().map(|items| { - for mut item in items.iter_mut() { - if item.uid.is_none() { - item.uid = Some(help::get_uid("d")); - } - } - }); - - profiles - } - - /// save the config to the file - pub fn save_file(&self) -> Result<()> { - config::save_yaml( - dirs::profiles_path(), - self, - Some("# Profiles Config for Clash Verge\n\n"), - ) - } - - /// get the current uid - pub fn get_current(&self) -> Option<String> { - self.current.clone() - } - - /// only change the main to the target id - pub fn put_current(&mut self, uid: String) -> Result<()> { - if self.items.is_none() { - self.items = Some(vec![]); - } - - let items = self.items.as_ref().unwrap(); - let some_uid = Some(uid.clone()); - - if items.iter().find(|&each| each.uid == some_uid).is_some() { - self.current = some_uid; - return self.save_file(); - } - - bail!("invalid uid \"{uid}\""); - } - - /// just change the `chain` - pub fn put_chain(&mut self, chain: Option<Vec<String>>) -> Result<()> { - self.chain = chain; - self.save_file() - } - - /// just change the `field` - pub fn put_valid(&mut self, valid: Option<Vec<String>>) -> Result<()> { - self.valid = valid; - self.save_file() - } - - /// get items ref - pub fn get_items(&self) -> Option<&Vec<PrfItem>> { - self.items.as_ref() - } - - /// find the item by the uid - pub fn get_item(&self, uid: &String) -> Result<&PrfItem> { - if self.items.is_some() { - let items = self.items.as_ref().unwrap(); - let some_uid = Some(uid.clone()); - - for each in items.iter() { - if each.uid == some_uid { - return Ok(each); - } - } - } - - bail!("failed to get the profile item \"uid:{uid}\""); - } - - /// append new item - /// if the file_data is some - /// then should save the data to file - pub fn append_item(&mut self, mut item: PrfItem) -> Result<()> { - if item.uid.is_none() { - bail!("the uid should not be null"); - } - - // save the file data - // move the field value after save - if let Some(file_data) = item.file_data.take() { - if item.file.is_none() { - bail!("the file should not be null"); - } - - let file = item.file.clone().unwrap(); - let path = dirs::app_profiles_dir().join(&file); - - fs::File::create(path) - .context(format!("failed to create file \"{}\"", file))? - .write(file_data.as_bytes()) - .context(format!("failed to write to file \"{}\"", file))?; - } - - if self.items.is_none() { - self.items = Some(vec![]); - } - - self.items.as_mut().map(|items| items.push(item)); - self.save_file() - } - - /// update the item value - pub fn patch_item(&mut self, uid: String, item: PrfItem) -> Result<()> { - let mut items = self.items.take().unwrap_or(vec![]); - - for mut each in items.iter_mut() { - if each.uid == Some(uid.clone()) { - patch!(each, item, itype); - patch!(each, item, name); - patch!(each, item, desc); - patch!(each, item, file); - patch!(each, item, url); - patch!(each, item, selected); - patch!(each, item, extra); - patch!(each, item, updated); - patch!(each, item, option); - - self.items = Some(items); - return self.save_file(); - } - } - - self.items = Some(items); - bail!("failed to find the profile item \"uid:{uid}\"") - } - - /// be used to update the remote item - /// only patch `updated` `extra` `file_data` - pub fn update_item(&mut self, uid: String, mut item: PrfItem) -> Result<()> { - if self.items.is_none() { - self.items = Some(vec![]); - } - - // find the item - let _ = self.get_item(&uid)?; - - if let Some(items) = self.items.as_mut() { - let some_uid = Some(uid.clone()); - - for mut each in items.iter_mut() { - if each.uid == some_uid { - each.extra = item.extra; - each.updated = item.updated; - - // save the file data - // move the field value after save - if let Some(file_data) = item.file_data.take() { - let file = each.file.take(); - let file = - file.unwrap_or(item.file.take().unwrap_or(format!("{}.yaml", &uid))); - - // the file must exists - each.file = Some(file.clone()); - - let path = dirs::app_profiles_dir().join(&file); - - fs::File::create(path) - .context(format!("failed to create file \"{}\"", file))? - .write(file_data.as_bytes()) - .context(format!("failed to write to file \"{}\"", file))?; - } - - break; - } - } - } - - self.save_file() - } - - /// delete item - /// if delete the current then return true - pub fn delete_item(&mut self, uid: String) -> Result<bool> { - let current = self.current.as_ref().unwrap_or(&uid); - let current = current.clone(); - - let mut items = self.items.take().unwrap_or(vec![]); - let mut index = None; - - // get the index - for i in 0..items.len() { - if items[i].uid == Some(uid.clone()) { - index = Some(i); - break; - } - } - - if let Some(index) = index { - items.remove(index).file.map(|file| { - let path = dirs::app_profiles_dir().join(file); - if path.exists() { - let _ = fs::remove_file(path); - } - }); - } - - // delete the original uid - if current == uid { - self.current = match items.len() > 0 { - true => items[0].uid.clone(), - false => None, - }; - } - - self.items = Some(items); - self.save_file()?; - Ok(current == uid) - } - - /// generate the current Mapping data - fn gen_current(&self) -> Result<Mapping> { - let config = Mapping::new(); - - if self.current.is_none() || self.items.is_none() { - return Ok(config); - } - - let current = self.current.clone().unwrap(); - for item in self.items.as_ref().unwrap().iter() { - if item.uid == Some(current.clone()) { - let file_path = match item.file.clone() { - Some(file) => dirs::app_profiles_dir().join(file), - None => bail!("failed to get the file field"), - }; - - if !file_path.exists() { - bail!("failed to read the file \"{}\"", file_path.display()); - } - - return Ok(config::read_merge_mapping(file_path.clone())); - } - } - bail!("failed to find current profile \"uid:{current}\""); - } - - /// generate the data for activate clash config - pub fn gen_activate(&self) -> Result<PrfActivate> { - let current = self.gen_current()?; - let chain = match self.chain.as_ref() { - Some(chain) => chain - .iter() - .filter_map(|uid| self.get_item(uid).ok()) - .filter_map(|item| item.to_enhance()) - .collect::<Vec<ChainItem>>(), - None => vec![], - }; - let valid = self.valid.clone().unwrap_or(vec![]); - - Ok(PrfActivate { - current, - chain, - valid, - }) - } -} - -#[derive(Default, Clone)] -pub struct PrfActivate { - pub current: Mapping, - pub chain: Vec<ChainItem>, - pub valid: Vec<String>, -} - -#[derive(Default, Debug, Clone, Deserialize, Serialize)] -pub struct RuntimeResult { - pub config: Option<Mapping>, - pub config_yaml: Option<String>, - // 记录在é…ç½®ä¸ï¼ˆåŒ…括mergeå’Œscript生æˆçš„)出现过的keys - // 这些keysä¸ä¸€å®šéƒ½ç”Ÿæ•ˆ - pub exists_keys: Vec<String>, - pub chain_logs: HashMap<String, Vec<(String, String)>>, -} diff --git a/src-tauri/src/data/verge.rs b/src-tauri/src/data/verge.rs deleted file mode 100644 index 97c20166afc28f48fb9df587d730a782c3addec9..0000000000000000000000000000000000000000 --- a/src-tauri/src/data/verge.rs +++ /dev/null @@ -1,136 +0,0 @@ -use crate::utils::{config, dirs}; -use anyhow::Result; -use serde::{Deserialize, Serialize}; - -/// ### `verge.yaml` schema -#[derive(Default, Debug, Clone, Deserialize, Serialize)] -pub struct Verge { - /// app listening port - /// for app singleton - pub app_singleton_port: Option<u16>, - - // i18n - pub language: Option<String>, - - /// `light` or `dark` or `system` - pub theme_mode: Option<String>, - - /// enable blur mode - /// maybe be able to set the alpha - pub theme_blur: Option<bool>, - - /// enable traffic graph default is true - pub traffic_graph: Option<bool>, - - /// clash tun mode - pub enable_tun_mode: Option<bool>, - - /// windows service mode - #[serde(skip_serializing_if = "Option::is_none")] - pub enable_service_mode: Option<bool>, - - /// can the app auto startup - pub enable_auto_launch: Option<bool>, - - /// not show the window on launch - pub enable_silent_start: Option<bool>, - - /// set system proxy - pub enable_system_proxy: Option<bool>, - - /// enable proxy guard - pub enable_proxy_guard: Option<bool>, - - /// set system proxy bypass - pub system_proxy_bypass: Option<String>, - - /// proxy guard duration - pub proxy_guard_duration: Option<u64>, - - /// theme setting - pub theme_setting: Option<VergeTheme>, - - /// web ui list - pub web_ui_list: Option<Vec<String>>, - - /// clash core path - #[serde(skip_serializing_if = "Option::is_none")] - pub clash_core: Option<String>, - - /// hotkey map - /// format: {func},{key} - pub hotkeys: Option<Vec<String>>, - - /// 切æ¢ä»£ç†æ—¶è‡ªåŠ¨å…³é—连接 - pub auto_close_connection: Option<bool>, - - /// 默认的延迟测试连接 - pub default_latency_test: Option<String>, -} - -#[derive(Default, Debug, Clone, Deserialize, Serialize)] -pub struct VergeTheme { - pub primary_color: Option<String>, - pub secondary_color: Option<String>, - pub primary_text: Option<String>, - pub secondary_text: Option<String>, - - pub info_color: Option<String>, - pub error_color: Option<String>, - pub warning_color: Option<String>, - pub success_color: Option<String>, - - pub font_family: Option<String>, - pub css_injection: Option<String>, -} - -impl Verge { - pub fn new() -> Self { - config::read_yaml::<Verge>(dirs::verge_path()) - } - - /// Save Verge App Config - pub fn save_file(&self) -> Result<()> { - config::save_yaml( - dirs::verge_path(), - self, - Some("# The Config for Clash Verge App\n\n"), - ) - } - - /// patch verge config - /// only save to file - pub fn patch_config(&mut self, patch: Verge) -> Result<()> { - macro_rules! patch { - ($key: tt) => { - if patch.$key.is_some() { - self.$key = patch.$key; - } - }; - } - - patch!(language); - patch!(theme_mode); - patch!(theme_blur); - patch!(traffic_graph); - - patch!(enable_tun_mode); - patch!(enable_service_mode); - patch!(enable_auto_launch); - patch!(enable_silent_start); - patch!(enable_system_proxy); - patch!(enable_proxy_guard); - patch!(system_proxy_bypass); - patch!(proxy_guard_duration); - - patch!(theme_setting); - patch!(web_ui_list); - patch!(clash_core); - patch!(hotkeys); - - patch!(auto_close_connection); - patch!(default_latency_test); - - self.save_file() - } -} diff --git a/src-tauri/src/feat.rs b/src-tauri/src/feat.rs index d94fe7d44e7b2b382ac9341faeed07cc57b69220..2667ebe2443764a1f996f9950dead31f032bf601 100644 --- a/src-tauri/src/feat.rs +++ b/src-tauri/src/feat.rs @@ -1,7 +1,7 @@ use crate::config::*; use crate::core::*; use crate::log_err; -use anyhow::Result; +use anyhow::{bail, Result}; use serde_yaml::{Mapping, Value}; // é‡å¯clash @@ -22,11 +22,9 @@ pub fn change_clash_mode(mode: String) { match clash_api::patch_configs(&mapping).await { Ok(_) => { // æ›´æ–°é…ç½® - let mut clash = ClashN::global().config.lock(); - clash.insert(Value::from("mode"), mode.into()); - drop(clash); + Config::clash().data().patch_config(mapping); - if let Ok(_) = ClashN::global().save_config() { + if let Ok(_) = Config::clash().data().save_config() { handle::Handle::refresh_clash(); log_err!(handle::Handle::update_systray_part()); } @@ -39,163 +37,199 @@ pub fn change_clash_mode(mode: String) { } // 切æ¢ç³»ç»Ÿä»£ç† -pub fn toggle_system_proxy() -> Result<()> { - let enable = { - let verge = VergeN::global().config.lock(); - verge.enable_system_proxy.clone().unwrap_or(false) - }; - patch_verge(IVerge { - enable_system_proxy: Some(!enable), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) +pub fn toggle_system_proxy() { + let enable = Config::verge().draft().enable_system_proxy.clone(); + let enable = enable.unwrap_or(false); + + tauri::async_runtime::spawn(async move { + match patch_verge(IVerge { + enable_system_proxy: Some(!enable), + ..IVerge::default() + }) + .await + { + Ok(_) => handle::Handle::refresh_verge(), + Err(err) => log::error!(target: "app", "{err}"), + } + }); } // æ‰“å¼€ç³»ç»Ÿä»£ç† -pub fn enable_system_proxy() -> Result<()> { - patch_verge(IVerge { - enable_system_proxy: Some(true), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) +pub fn enable_system_proxy() { + tauri::async_runtime::spawn(async { + match patch_verge(IVerge { + enable_system_proxy: Some(true), + ..IVerge::default() + }) + .await + { + Ok(_) => handle::Handle::refresh_verge(), + Err(err) => log::error!(target: "app", "{err}"), + } + }); } // å…³é—ç³»ç»Ÿä»£ç† -pub fn disable_system_proxy() -> Result<()> { - patch_verge(IVerge { - enable_system_proxy: Some(false), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) +pub fn disable_system_proxy() { + tauri::async_runtime::spawn(async { + match patch_verge(IVerge { + enable_system_proxy: Some(false), + ..IVerge::default() + }) + .await + { + Ok(_) => handle::Handle::refresh_verge(), + Err(err) => log::error!(target: "app", "{err}"), + } + }); } // 切æ¢tunæ¨¡å¼ -pub fn toggle_tun_mode() -> Result<()> { - let enable = { - let verge = VergeN::global().config.lock(); - verge.enable_tun_mode.clone().unwrap_or(false) - }; +pub fn toggle_tun_mode() { + let enable = Config::verge().data().enable_tun_mode.clone(); + let enable = enable.unwrap_or(false); - patch_verge(IVerge { - enable_tun_mode: Some(!enable), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) + tauri::async_runtime::spawn(async move { + match patch_verge(IVerge { + enable_tun_mode: Some(!enable), + ..IVerge::default() + }) + .await + { + Ok(_) => handle::Handle::refresh_verge(), + Err(err) => log::error!(target: "app", "{err}"), + } + }); } // 打开tunæ¨¡å¼ -pub fn enable_tun_mode() -> Result<()> { - patch_verge(IVerge { - enable_tun_mode: Some(true), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) +pub fn enable_tun_mode() { + tauri::async_runtime::spawn(async { + match patch_verge(IVerge { + enable_tun_mode: Some(true), + ..IVerge::default() + }) + .await + { + Ok(_) => handle::Handle::refresh_verge(), + Err(err) => log::error!(target: "app", "{err}"), + } + }); } // å…³é—tunæ¨¡å¼ -pub fn disable_tun_mode() -> Result<()> { - patch_verge(IVerge { - enable_tun_mode: Some(false), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) +pub fn disable_tun_mode() { + tauri::async_runtime::spawn(async { + match patch_verge(IVerge { + enable_tun_mode: Some(false), + ..IVerge::default() + }) + .await + { + Ok(_) => handle::Handle::refresh_verge(), + Err(err) => log::error!(target: "app", "{err}"), + } + }); } /// 修改clashçš„é…ç½® -pub fn patch_clash(patch: Mapping) -> Result<()> { - let patch_cloned = patch.clone(); - let clash_mode = patch.get("mode").is_some(); - let mixed_port = patch.get("mixed-port").is_some(); - let external = patch.get("external-controller").is_some(); - let secret = patch.get("secret").is_some(); +pub async fn patch_clash(patch: Mapping) -> Result<()> { + Config::clash().draft().patch_config(patch.clone()); - // æ›´æ–°infoä¿¡æ¯ - if mixed_port || external || secret { - let mut tmp_config = { ClashN::global().config.lock().clone() }; - - for (key, value) in patch.into_iter() { - tmp_config.insert(key, value); - } + match { + let mixed_port = patch.get("mixed-port"); + if mixed_port.is_some() { + let changed = mixed_port != Config::clash().data().0.get("mixed-port"); + // 检查端å£å 用 + if changed { + if let Some(port) = mixed_port.clone().unwrap().as_u64() { + if !port_scanner::local_port_available(port as u16) { + Config::clash().discard(); + bail!("the port not available"); + } + } + } + }; - let old_info = ClashN::global().patch_info(ClashInfoN::from(&tmp_config))?; + // 激活é…ç½® + handle_activate().await?; - if let Err(err) = CoreManager::global().run_core() { - // æ¢å¤æ—§å€¼ - ClashN::global().patch_info(old_info)?; - return Err(err); + // æ›´æ–°ç³»ç»Ÿä»£ç† + if mixed_port.is_some() { + log_err!(sysopt::Sysopt::global().init_sysproxy()); } - } - // å˜å¥½å†æž - ClashN::global().patch_config(patch_cloned)?; - // 激活é…ç½® - tauri::async_runtime::spawn(async move { - match handle_activate().await { - Ok(_) => { - // æ›´æ–°ç³»ç»Ÿä»£ç† - if mixed_port { - log_err!(sysopt::Sysopt::global().init_sysproxy()); - } + if patch.get("mode").is_some() { + log_err!(handle::Handle::update_systray_part()); + } - if clash_mode { - log_err!(handle::Handle::update_systray_part()); - } - } - Err(err) => log::error!(target: "app", "{err}"), + <Result<()>>::Ok(()) + } { + Ok(()) => { + Config::clash().apply(); + Config::clash().data().save_config()?; + Ok(()) } - }); - Ok(()) + Err(err) => { + Config::clash().discard(); + Err(err) + } + } } /// 修改vergeçš„é…ç½® /// 一般都是一个个的修改 -pub fn patch_verge(patch: IVerge) -> Result<()> { - VergeN::global().patch_config(patch.clone())?; +pub async fn patch_verge(patch: IVerge) -> Result<()> { + Config::verge().draft().patch_config(patch.clone()); let tun_mode = patch.enable_tun_mode; let auto_launch = patch.enable_auto_launch; let system_proxy = patch.enable_system_proxy; let proxy_bypass = patch.system_proxy_bypass; - let proxy_guard = patch.enable_proxy_guard; let language = patch.language; - #[cfg(target_os = "windows")] - {} + match { + #[cfg(target_os = "windows")] + {} - if tun_mode.is_some() { - tauri::async_runtime::spawn(async { - log_err!(handle_activate().await); - }); - } + if tun_mode.is_some() { + handle_activate().await?; + } - if auto_launch.is_some() { - sysopt::Sysopt::global().update_launch()?; - } - if system_proxy.is_some() || proxy_bypass.is_some() { - sysopt::Sysopt::global().update_sysproxy()?; - sysopt::Sysopt::global().guard_proxy(); - } - if proxy_guard.unwrap_or(false) { - sysopt::Sysopt::global().guard_proxy(); - } + if auto_launch.is_some() { + sysopt::Sysopt::global().update_launch()?; + } + if system_proxy.is_some() || proxy_bypass.is_some() { + sysopt::Sysopt::global().update_sysproxy()?; + sysopt::Sysopt::global().guard_proxy(); + } - if language.is_some() { - handle::Handle::update_systray()?; - } else if system_proxy.or(tun_mode).is_some() { - handle::Handle::update_systray_part()?; - } + if let Some(true) = patch.enable_proxy_guard { + sysopt::Sysopt::global().guard_proxy(); + } - if patch.hotkeys.is_some() { - hotkey::Hotkey::global().update(patch.hotkeys.unwrap())?; - } + if let Some(hotkeys) = patch.hotkeys { + hotkey::Hotkey::global().update(hotkeys)?; + } - Ok(()) + if language.is_some() { + handle::Handle::update_systray()?; + } else if system_proxy.or(tun_mode).is_some() { + handle::Handle::update_systray_part()?; + } + + <Result<()>>::Ok(()) + } { + Ok(()) => { + Config::verge().apply(); + Config::verge().data().save_file()?; + Ok(()) + } + Err(err) => { + Config::verge().discard(); + Err(err) + } + } } /// 激活é…ç½® @@ -216,5 +250,38 @@ pub async fn handle_activate() -> Result<()> { /// æ›´æ–°æŸä¸ªprofile /// 如果更新当å‰é…置就激活é…ç½® pub async fn update_profile(uid: String, option: Option<PrfOption>) -> Result<()> { + let url_opt = { + let profiles = Config::profiles(); + let profiles = profiles.latest(); + let item = profiles.get_item(&uid)?; + let is_remote = item.itype.as_ref().map_or(false, |s| s == "remote"); + + if !is_remote { + None // 直接更新 + } else if item.url.is_none() { + bail!("failed to get the profile item url"); + } else { + Some((item.url.clone().unwrap(), item.option.clone())) + } + }; + + let should_update = match url_opt { + Some((url, opt)) => { + let merged_opt = PrfOption::merge(opt, option); + let item = PrfItem::from_url(&url, None, None, merged_opt).await?; + + let profiles = Config::profiles(); + let mut profiles = profiles.latest(); + profiles.update_item(uid.clone(), item)?; + + Some(uid) == profiles.get_current() + } + None => true, + }; + + if should_update { + handle_activate().await?; + } + Ok(()) } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index f0005e7247f552bd0567823bcd3e4e607c6bc6f1..b019d0362bac0633c5967c53cd8ef06ea6814be2 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -6,7 +6,6 @@ mod cmds; mod config; mod core; -// mod data; mod enhance; mod feat; mod utils; diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index 9034fce2fb3bfeac420415f2f88065ba5fd4a15b..e7810d789854b519799ffd9b182efadf64d5c607 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -1,5 +1,6 @@ +use crate::config::Config; use crate::log_err; -use crate::{config::VergeN, core::*, utils::init, utils::server}; +use crate::{core::*, utils::init, utils::server}; use tauri::{App, AppHandle, Manager}; /// handle something when start app @@ -19,10 +20,7 @@ pub fn resolve_setup(app: &mut App) { log_err!(tray::Tray::update_systray(&app.app_handle())); - let silent_start = { - let verge = VergeN::global().config.lock(); - verge.enable_silent_start.clone() - }; + let silent_start = { Config::verge().data().enable_silent_start.clone() }; if !silent_start.unwrap_or(false) { create_window(&app.app_handle()); } diff --git a/src-tauri/src/utils/server.rs b/src-tauri/src/utils/server.rs index 1e68326ff47bed25385cd33171fa3c7a5ce27bef..1d2ff59b0a1d10f35660524f5e094fbd68e9188b 100644 --- a/src-tauri/src/utils/server.rs +++ b/src-tauri/src/utils/server.rs @@ -1,14 +1,14 @@ extern crate warp; use super::resolve; -use crate::config::VergeN; +use crate::config::IVerge; use port_scanner::local_port_available; use tauri::AppHandle; use warp::Filter; /// check whether there is already exists pub fn check_singleton() -> Result<(), ()> { - let port = VergeN::get_singleton_port(); + let port = IVerge::get_singleton_port(); if !local_port_available(port) { tauri::async_runtime::block_on(async { @@ -25,7 +25,7 @@ pub fn check_singleton() -> Result<(), ()> { /// maybe it can be used as pac server later pub fn embed_server(app_handle: AppHandle) { let app_handle = app_handle.clone(); - let port = VergeN::get_singleton_port(); + let port = IVerge::get_singleton_port(); tauri::async_runtime::spawn(async move { let commands = warp::path!("commands" / "visible").map(move || {