From b03c52a501200a0049d3d4a64d2549b601e4930a Mon Sep 17 00:00:00 2001 From: GyDi Date: Mon, 14 Nov 2022 01:26:33 +0800 Subject: [PATCH 01/17] refactor: wip --- src-tauri/Cargo.lock | 8 +- src-tauri/src/cmds.rs | 210 +++----- src-tauri/src/config/clash.rs | 180 +++++++ src-tauri/src/config/mod.rs | 76 +-- src-tauri/src/config/prfitem.rs | 406 ++++++++++++++ src-tauri/src/config/profiles.rs | 379 +++++++++++++ src-tauri/src/config/verge.rs | 166 ++++++ src-tauri/src/core/clash_api.rs | 66 +++ src-tauri/src/core/core.rs | 262 +++++++++ src-tauri/src/core/core_service.rs | 217 ++++++++ src-tauri/src/core/handle.rs | 56 +- src-tauri/src/core/hotkey.rs | 72 +-- src-tauri/src/core/logger.rs | 36 ++ src-tauri/src/core/mod.rs | 566 ++++++++++---------- src-tauri/src/core/service.rs | 14 +- src-tauri/src/core/sysopt.rs | 143 +++-- src-tauri/src/core/timer.rs | 143 ++--- src-tauri/src/core/tray.rs | 38 +- src-tauri/src/{config => enhance}/field.rs | 0 src-tauri/src/{config => enhance}/merge.rs | 0 src-tauri/src/enhance/mod.rs | 68 +++ src-tauri/src/{config => enhance}/script.rs | 0 src-tauri/src/{config => enhance}/tun.rs | 0 src-tauri/src/feat.rs | 256 ++++++--- src-tauri/src/main.rs | 32 +- src-tauri/src/utils/config.rs | 2 +- src-tauri/src/utils/dirs.rs | 19 +- src-tauri/src/utils/help.rs | 50 +- src-tauri/src/utils/init.rs | 35 +- src-tauri/src/utils/resolve.rs | 62 +-- src-tauri/src/utils/server.rs | 16 +- src/services/cmds.ts | 6 +- 32 files changed, 2704 insertions(+), 880 deletions(-) create mode 100644 src-tauri/src/config/clash.rs create mode 100644 src-tauri/src/config/prfitem.rs create mode 100644 src-tauri/src/config/profiles.rs create mode 100644 src-tauri/src/config/verge.rs create mode 100644 src-tauri/src/core/clash_api.rs create mode 100644 src-tauri/src/core/core.rs create mode 100644 src-tauri/src/core/core_service.rs create mode 100644 src-tauri/src/core/logger.rs rename src-tauri/src/{config => enhance}/field.rs (100%) rename src-tauri/src/{config => enhance}/merge.rs (100%) create mode 100644 src-tauri/src/enhance/mod.rs rename src-tauri/src/{config => enhance}/script.rs (100%) rename src-tauri/src/{config => enhance}/tun.rs (100%) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 6ccfafa..c507246 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -488,9 +488,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", @@ -1860,9 +1860,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.4" +version = "0.24.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd8e4fb07cf672b1642304e731ef8a6a4c7891d67bb4fd4f5ce58cd6ed86803c" +checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" dependencies = [ "bytemuck", "byteorder", diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index e8ac49f..f622bb7 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -1,9 +1,10 @@ use crate::{ - core::Core, - data::{ClashInfo, Data, PrfItem, PrfOption, Profiles, Verge}, + config::*, + core::*, + feat, utils::{dirs, help}, }; -use crate::{log_if_err, ret_err, wrap_err}; +use crate::{ret_err, wrap_err}; use anyhow::Result; use serde_yaml::Mapping; use std::collections::{HashMap, VecDeque}; @@ -11,248 +12,189 @@ use sysproxy::Sysproxy; type CmdResult = Result; -/// get all profiles from `profiles.yaml` #[tauri::command] -pub fn get_profiles() -> CmdResult { - let global = Data::global(); - let profiles = global.profiles.lock(); +pub fn get_profiles() -> CmdResult { + let profiles = ProfilesN::global().config.lock(); Ok(profiles.clone()) } -/// manually exec enhanced profile #[tauri::command] -pub fn enhance_profiles() -> CmdResult { - let core = Core::global(); - wrap_err!(core.activate()) +pub async fn enhance_profiles() -> CmdResult { + wrap_err!(CoreManager::global().activate_config().await) } -/// import the profile from url -/// and save to `profiles.yaml` +#[deprecated] #[tauri::command] pub async fn import_profile(url: String, option: Option) -> CmdResult { let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?; - - let global = Data::global(); - let mut profiles = global.profiles.lock(); + let mut profiles = ProfilesN::global().config.lock(); wrap_err!(profiles.append_item(item)) } -/// new a profile -/// append a temp profile item file to the `profiles` dir -/// view the temp profile file by using vscode or other editor #[tauri::command] pub async fn create_profile(item: PrfItem, file_data: Option) -> CmdResult { let item = wrap_err!(PrfItem::from(item, file_data).await)?; - - let global = Data::global(); - let mut profiles = global.profiles.lock(); + let mut profiles = ProfilesN::global().config.lock(); wrap_err!(profiles.append_item(item)) } -/// Update the profile #[tauri::command] pub async fn update_profile(index: String, option: Option) -> CmdResult { - let core = Core::global(); - wrap_err!(core.update_profile_item(index, option).await) + wrap_err!(ProfilesN::global().update_item(index, option).await) } -/// change the current profile #[tauri::command] pub fn select_profile(index: String) -> CmdResult { - let global = Data::global(); - let mut profiles = global.profiles.lock(); + let mut profiles = ProfilesN::global().config.lock(); wrap_err!(profiles.put_current(index))?; drop(profiles); - let core = Core::global(); - wrap_err!(core.activate()) + wrap_err!(tauri::async_runtime::block_on(async { + CoreManager::global().activate_config().await + })) } /// change the profile chain #[tauri::command] pub fn change_profile_chain(chain: Option>) -> CmdResult { - let global = Data::global(); - let mut profiles = global.profiles.lock(); + let mut profiles = ProfilesN::global().config.lock(); wrap_err!(profiles.put_chain(chain))?; drop(profiles); - let core = Core::global(); - wrap_err!(core.activate()) + wrap_err!(tauri::async_runtime::block_on(async { + CoreManager::global().activate_config().await + })) } -/// change the profile valid fields #[tauri::command] pub fn change_profile_valid(valid: Option>) -> CmdResult { - let global = Data::global(); - let mut profiles = global.profiles.lock(); + let mut profiles = ProfilesN::global().config.lock(); wrap_err!(profiles.put_valid(valid))?; drop(profiles); - let core = Core::global(); - wrap_err!(core.activate()) + wrap_err!(tauri::async_runtime::block_on(async { + CoreManager::global().activate_config().await + })) } -/// delete profile item #[tauri::command] pub fn delete_profile(index: String) -> CmdResult { - let global = Data::global(); - let mut profiles = global.profiles.lock(); + let mut profiles = ProfilesN::global().config.lock(); if wrap_err!(profiles.delete_item(index))? { drop(profiles); - let core = Core::global(); - log_if_err!(core.activate()); + wrap_err!(tauri::async_runtime::block_on(async { + CoreManager::global().activate_config().await + }))?; } Ok(()) } -/// patch the profile config #[tauri::command] pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult { - let global = Data::global(); - let mut profiles = global.profiles.lock(); + let mut profiles = ProfilesN::global().config.lock(); wrap_err!(profiles.patch_item(index, profile))?; drop(profiles); - // update cron task - let core = Core::global(); - let mut timer = core.timer.lock(); - wrap_err!(timer.refresh()) + wrap_err!(timer::Timer::global().refresh()) } -/// run vscode command to edit the profile #[tauri::command] pub fn view_profile(index: String) -> CmdResult { - let global = Data::global(); - let profiles = global.profiles.lock(); + let profiles = ProfilesN::global().config.lock(); let item = wrap_err!(profiles.get_item(&index))?; - let file = item.file.clone(); - if file.is_none() { - ret_err!("file is null"); - } - - let path = dirs::app_profiles_dir().join(file.unwrap()); + 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!("file not found"); + ret_err!("the file not found"); } wrap_err!(help::open_file(path)) } -/// read the profile item file data #[tauri::command] pub fn read_profile_file(index: String) -> CmdResult { - let global = Data::global(); - let profiles = global.profiles.lock(); + let profiles = ProfilesN::global().config.lock(); let item = wrap_err!(profiles.get_item(&index))?; let data = wrap_err!(item.read_file())?; Ok(data) } -/// save the profile item file data #[tauri::command] pub fn save_profile_file(index: String, file_data: Option) -> CmdResult { if file_data.is_none() { return Ok(()); } - let global = Data::global(); - let profiles = global.profiles.lock(); + let profiles = ProfilesN::global().config.lock(); let item = wrap_err!(profiles.get_item(&index))?; wrap_err!(item.save_file(file_data.unwrap())) } -/// get the clash core info from the state -/// the caller can also get the infomation by clash's api #[tauri::command] -pub fn get_clash_info() -> CmdResult { - let global = Data::global(); - let clash = global.clash.lock(); - Ok(clash.info.clone()) +pub fn get_clash_info() -> CmdResult { + Ok(ClashN::global().info.lock().clone()) } -/// get the runtime clash config mapping #[tauri::command] pub fn get_runtime_config() -> CmdResult> { - let core = Core::global(); - let rt = core.runtime.lock(); - Ok(rt.config.clone()) + Ok(CoreManager::global().runtime_config.lock().config.clone()) } -/// get the runtime clash config yaml string #[tauri::command] pub fn get_runtime_yaml() -> CmdResult> { - let core = Core::global(); - let rt = core.runtime.lock(); - Ok(rt.config_yaml.clone()) + Ok(CoreManager::global() + .runtime_config + .lock() + .config_yaml + .clone()) } -/// get the runtime config exists keys #[tauri::command] pub fn get_runtime_exists() -> CmdResult> { - let core = Core::global(); - let rt = core.runtime.lock(); - Ok(rt.exists_keys.clone()) + Ok(CoreManager::global() + .runtime_config + .lock() + .exists_keys + .clone()) } -/// get the runtime enhanced chain log #[tauri::command] pub fn get_runtime_logs() -> CmdResult>> { - let core = Core::global(); - let rt = core.runtime.lock(); - Ok(rt.chain_logs.clone()) + Ok(CoreManager::global() + .runtime_config + .lock() + .chain_logs + .clone()) } -/// update the clash core config -/// after putting the change to the clash core -/// then we should save the latest config #[tauri::command] pub fn patch_clash_config(payload: Mapping) -> CmdResult { - let core = Core::global(); - wrap_err!(core.patch_clash(payload)) + wrap_err!(feat::patch_clash(payload)) } #[tauri::command] -pub fn get_verge_config() -> CmdResult { - let global = Data::global(); - let verge = global.verge.lock(); - Ok(verge.clone()) -} - -/// patch the verge config -/// this command only save the config and not responsible for other things -#[tauri::command] -pub fn patch_verge_config(payload: Verge) -> CmdResult { - let core = Core::global(); - wrap_err!(core.patch_verge(payload)) +pub fn get_verge_config() -> CmdResult { + Ok(VergeN::global().config.lock().clone()) } #[tauri::command] -pub fn update_hotkeys(hotkeys: Vec) -> CmdResult { - let core = Core::global(); - let mut hotkey = core.hotkey.lock(); - wrap_err!(hotkey.update(hotkeys)) +pub fn patch_verge_config(payload: IVerge) -> CmdResult { + wrap_err!(feat::patch_verge(payload)) } -/// change clash core #[tauri::command] pub fn change_clash_core(clash_core: Option) -> CmdResult { - let core = Core::global(); - wrap_err!(core.change_core(clash_core)) + wrap_err!(tauri::async_runtime::block_on(async { + CoreManager::global().change_core(clash_core).await + })) } /// restart the sidecar #[tauri::command] pub fn restart_sidecar() -> CmdResult { - let core = Core::global(); - wrap_err!(core.restart_clash()) -} - -/// kill all sidecars when update app -#[tauri::command] -pub fn kill_sidecar() { - tauri::api::process::kill_children(); + wrap_err!(CoreManager::global().run_core()) } /// get the system proxy @@ -273,44 +215,38 @@ pub fn get_sys_proxy() -> CmdResult { #[tauri::command] pub fn get_clash_logs() -> CmdResult> { - let core = Core::global(); - let service = core.service.lock(); - Ok(service.get_logs()) + Ok(logger::Logger::global().get_log()) } -/// open app config dir #[tauri::command] pub fn open_app_dir() -> CmdResult<()> { let app_dir = dirs::app_home_dir(); wrap_err!(open::that(app_dir)) } -/// open logs dir #[tauri::command] pub fn open_logs_dir() -> CmdResult<()> { let log_dir = dirs::app_logs_dir(); wrap_err!(open::that(log_dir)) } -/// open url #[tauri::command] pub fn open_web_url(url: String) -> CmdResult<()> { wrap_err!(open::that(url)) } -/// service mode #[cfg(windows)] pub mod service { use super::*; use crate::core::win_service::JsonResponse; #[tauri::command] - pub async fn start_service() -> CmdResult<()> { + pub async fn start_service() -> CmdResult { wrap_err!(crate::core::Service::start_service().await) } #[tauri::command] - pub async fn stop_service() -> CmdResult<()> { + pub async fn stop_service() -> CmdResult { wrap_err!(crate::core::Service::stop_service().await) } @@ -324,12 +260,12 @@ pub mod service { } #[tauri::command] - pub async fn install_service() -> CmdResult<()> { + pub async fn install_service() -> CmdResult { wrap_err!(crate::core::Service::install_service().await) } #[tauri::command] - pub async fn uninstall_service() -> CmdResult<()> { + pub async fn uninstall_service() -> CmdResult { wrap_err!(crate::core::Service::uninstall_service().await) } } @@ -339,23 +275,23 @@ pub mod service { use super::*; #[tauri::command] - pub async fn start_service() -> CmdResult<()> { + pub async fn start_service() -> CmdResult { Ok(()) } #[tauri::command] - pub async fn stop_service() -> CmdResult<()> { + pub async fn stop_service() -> CmdResult { Ok(()) } #[tauri::command] - pub async fn check_service() -> CmdResult<()> { + pub async fn check_service() -> CmdResult { Ok(()) } #[tauri::command] - pub async fn install_service() -> CmdResult<()> { + pub async fn install_service() -> CmdResult { Ok(()) } #[tauri::command] - pub async fn uninstall_service() -> CmdResult<()> { + pub async fn uninstall_service() -> CmdResult { Ok(()) } } diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs new file mode 100644 index 0000000..e498a34 --- /dev/null +++ b/src-tauri/src/config/clash.rs @@ -0,0 +1,180 @@ +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::sync::Arc; + +#[derive(Debug)] +pub struct ClashN { + /// maintain the clash config + pub config: Arc>, + /// some info + pub info: Arc>, +} + +impl ClashN { + pub fn global() -> &'static ClashN { + static DATA: OnceCell = 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 { + 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(); + + 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() { + config.insert(key, value); + } + + if change_info { + let mut info = self.info.lock(); + *info = ClashInfoN::from(&*config); + } + + self.save_config() + } +} + +#[derive(Default, Debug, Clone, Deserialize, Serialize)] +pub struct ClashInfoN { + /// clash sidecar status + pub status: String, + /// clash core port + pub port: Option, + /// same as `external-controller` + pub server: Option, + /// clash secret + pub secret: Option, +} + +impl ClashInfoN { + /// parse the clash's config.yaml + /// get some information + pub fn from(config: &Mapping) -> ClashInfoN { + 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, + }; + + ClashInfoN { + status: format!("{status}"), + port, + server, + secret, + } + } +} diff --git a/src-tauri/src/config/mod.rs b/src-tauri/src/config/mod.rs index 7020020..9aa880f 100644 --- a/src-tauri/src/config/mod.rs +++ b/src-tauri/src/config/mod.rs @@ -1,69 +1,9 @@ -mod field; -mod merge; -mod script; -mod tun; +mod clash; +mod prfitem; +mod profiles; +mod verge; -pub(self) use self::field::*; -use self::merge::*; -use self::script::*; -use self::tun::*; -use crate::data::ChainItem; -use crate::data::ChainType; -use serde_yaml::Mapping; -use std::collections::HashMap; -use std::collections::HashSet; - -type ResultLog = Vec<(String, String)>; - -pub fn enhance_config( - clash_config: Mapping, - profile_config: Mapping, - chain: Vec, - valid: Vec, - tun_mode: bool, -) -> (Mapping, Vec, HashMap) { - let mut config = profile_config; - let mut result_map = HashMap::new(); - let mut exists_keys = use_keys(&config); - - let valid = use_valid_fields(valid); - - chain.into_iter().for_each(|item| match item.data { - ChainType::Merge(merge) => { - exists_keys.extend(use_keys(&merge)); - config = use_merge(merge, config.to_owned()); - config = use_filter(config.to_owned(), &valid); - } - ChainType::Script(script) => { - let mut logs = vec![]; - - match use_script(script, config.to_owned()) { - Ok((res_config, res_logs)) => { - exists_keys.extend(use_keys(&res_config)); - config = use_filter(res_config, &valid); - logs.extend(res_logs); - } - Err(err) => logs.push(("exception".into(), err.to_string())), - } - - result_map.insert(item.uid, logs); - } - }); - - config = use_filter(config, &valid); - - for (key, value) in clash_config.into_iter() { - config.insert(key, value); - } - - let clash_fields = use_clash_fields(); - config = use_filter(config, &clash_fields); - config = use_tun(config, tun_mode); - config = use_sort(config); - - let mut exists_set = HashSet::new(); - exists_set.extend(exists_keys.into_iter().filter(|s| clash_fields.contains(s))); - exists_keys = exists_set.into_iter().collect(); - - (config, exists_keys, result_map) -} +pub use self::clash::*; +pub use self::prfitem::*; +pub use self::profiles::*; +pub use self::verge::*; diff --git a/src-tauri/src/config/prfitem.rs b/src-tauri/src/config/prfitem.rs new file mode 100644 index 0000000..62dc9b6 --- /dev/null +++ b/src-tauri/src/config/prfitem.rs @@ -0,0 +1,406 @@ +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, + + /// profile item type + /// enum value: remote | local | script | merge + #[serde(rename = "type")] + pub itype: Option, + + /// profile name + pub name: Option, + + /// profile file + pub file: Option, + + /// profile description + #[serde(skip_serializing_if = "Option::is_none")] + pub desc: Option, + + /// source url + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, + + /// selected information + #[serde(skip_serializing_if = "Option::is_none")] + pub selected: Option>, + + /// subscription user info + #[serde(skip_serializing_if = "Option::is_none")] + pub extra: Option, + + /// updated time + pub updated: Option, + + /// some options of the item + #[serde(skip_serializing_if = "Option::is_none")] + pub option: Option, + + /// the file data + #[serde(skip)] + pub file_data: Option, +} + +#[derive(Default, Debug, Clone, Deserialize, Serialize)] +pub struct PrfSelected { + pub name: Option, + pub now: Option, +} + +#[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, + + /// for `remote` profile + /// use system proxy + #[serde(skip_serializing_if = "Option::is_none")] + pub with_proxy: Option, + + /// for `remote` profile + /// use self proxy + #[serde(skip_serializing_if = "Option::is_none")] + pub self_proxy: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub update_interval: Option, +} + +impl PrfOption { + pub fn merge(one: Option, other: Option) -> Option { + 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) -> Result { + 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) -> Result { + 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, + desc: Option, + option: Option, + ) -> Result { + 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 clash = super::ClashN::global(); + let port = clash.info.lock().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::(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::() { + 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::(&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 { + 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 quick.js + pub fn from_script(name: String, desc: String) -> Result { + 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 { + 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 { + 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/config/profiles.rs b/src-tauri/src/config/profiles.rs new file mode 100644 index 0000000..fac9960 --- /dev/null +++ b/src-tauri/src/config/profiles.rs @@ -0,0 +1,379 @@ +use super::{prfitem::PrfItem, ChainItem, PrfOption}; +use crate::{ + core::CoreManager, + 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}; + +pub struct ProfilesN { + pub config: Arc>, +} + +impl ProfilesN { + pub fn global() -> &'static ProfilesN { + static PROFILES: OnceCell = 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) -> Result<()> { + let (url, opt) = { + let profiles = self.config.lock(); + let item = profiles.get_item(&uid)?; + + if let Some(typ) = item.itype.as_ref() { + // maybe only valid for `local` profile + if *typ != "remote" { + // reactivate the config + if Some(uid) == profiles.get_current() { + drop(profiles); + tauri::async_runtime::block_on(async { + CoreManager::global().activate_config().await + })?; + } + return Ok(()); + } + } + if item.url.is_none() { + bail!("failed to get the profile item url"); + } + (item.url.clone().unwrap(), item.option.clone()) + }; + + 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)?; + + // reactivate the profile + if Some(uid) == profiles.get_current() { + drop(profiles); + tauri::async_runtime::block_on(async { + 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, + + /// same as PrfConfig.chain + chain: Option>, + + /// record valid fields for clash + valid: Option>, + + /// profile list + items: Option>, +} + +macro_rules! patch { + ($lv: expr, $rv: expr, $key: tt) => { + if ($rv.$key).is_some() { + $lv.$key = $rv.$key; + } + }; +} + +impl IProfiles { + /// read the config from the file + pub fn read_file() -> Self { + let mut profiles = config::read_yaml::(dirs::profiles_path()); + if profiles.items.is_none() { + profiles.items = Some(vec![]); + } + // compatible 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 { + 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>) -> Result<()> { + self.chain = chain; + self.save_file() + } + + /// just change the `field` + pub fn put_valid(&mut self, valid: Option>) -> Result<()> { + self.valid = valid; + self.save_file() + } + + /// get items ref + pub fn get_items(&self) -> Option<&Vec> { + 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 { + 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 { + 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 { + 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::>(), + 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, + pub valid: Vec, +} + +#[derive(Default, Debug, Clone, Deserialize, Serialize)] +pub struct RuntimeResult { + pub config: Option, + pub config_yaml: Option, + // 记录在配置中(包括merge和script生成的)出现过的keys + // 这些keys不一定都生效 + pub exists_keys: Vec, + pub chain_logs: HashMap>, +} diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs new file mode 100644 index 0000000..97b5f10 --- /dev/null +++ b/src-tauri/src/config/verge.rs @@ -0,0 +1,166 @@ +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; + +pub struct VergeN { + pub config: Arc>, +} + +impl VergeN { + pub fn global() -> &'static VergeN { + static DATA: OnceCell = OnceCell::new(); + + DATA.get_or_init(|| { + let config = config::read_yaml::(dirs::verge_path()); + VergeN { + config: Arc::new(Mutex::new(config)), + } + }) + } + + /// Save IVerge App Config + pub fn save_file(&self) -> Result<()> { + let config = self.config.lock(); + + config::save_yaml( + dirs::verge_path(), + &*config, + Some("# The Config for Clash IVerge App\n\n"), + ) + } + + /// patch verge config + /// only save to file + pub fn patch_config(&self, patch: IVerge) -> Result<()> { + let mut config = self.config.lock(); + + macro_rules! patch { + ($key: tt) => { + if patch.$key.is_some() { + config.$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() + } + + /// 在初始化前尝试拿到单例端口的值 + pub fn get_singleton_port() -> u16 { + let config = config::read_yaml::(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)] +pub struct IVerge { + /// app listening port + /// for app singleton + pub app_singleton_port: Option, + + // i18n + pub language: Option, + + /// `light` or `dark` or `system` + pub theme_mode: Option, + + /// enable blur mode + /// maybe be able to set the alpha + pub theme_blur: Option, + + /// enable traffic graph default is true + pub traffic_graph: Option, + + /// clash tun mode + pub enable_tun_mode: Option, + + /// windows service mode + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_service_mode: Option, + + /// can the app auto startup + pub enable_auto_launch: Option, + + /// not show the window on launch + pub enable_silent_start: Option, + + /// set system proxy + pub enable_system_proxy: Option, + + /// enable proxy guard + pub enable_proxy_guard: Option, + + /// set system proxy bypass + pub system_proxy_bypass: Option, + + /// proxy guard duration + pub proxy_guard_duration: Option, + + /// theme setting + pub theme_setting: Option, + + /// web ui list + pub web_ui_list: Option>, + + /// clash core path + #[serde(skip_serializing_if = "Option::is_none")] + pub clash_core: Option, + + /// hotkey map + /// format: {func},{key} + pub hotkeys: Option>, + + /// 切换代理时自动关闭连接 + pub auto_close_connection: Option, + + /// 默认的延迟测试连接 + pub default_latency_test: Option, +} + +#[derive(Default, Debug, Clone, Deserialize, Serialize)] +pub struct IVergeTheme { + pub primary_color: Option, + pub secondary_color: Option, + pub primary_text: Option, + pub secondary_text: Option, + + pub info_color: Option, + pub error_color: Option, + pub warning_color: Option, + pub success_color: Option, + + pub font_family: Option, + pub css_injection: Option, +} diff --git a/src-tauri/src/core/clash_api.rs b/src-tauri/src/core/clash_api.rs new file mode 100644 index 0000000..2504280 --- /dev/null +++ b/src-tauri/src/core/clash_api.rs @@ -0,0 +1,66 @@ +use crate::{config, utils::dirs}; +use anyhow::{bail, Result}; +use reqwest::header::HeaderMap; +use serde_yaml::Mapping; +use std::collections::HashMap; + +/// PUT /configs +pub async fn put_configs() -> Result<()> { + let (url, headers) = clash_client_info()?; + let url = format!("{url}/configs"); + + let runtime_yaml = dirs::clash_runtime_yaml(); + let runtime_yaml = dirs::path_to_str(&runtime_yaml)?; + + let mut data = HashMap::new(); + data.insert("path", runtime_yaml); + + let client = reqwest::ClientBuilder::new().no_proxy().build()?; + let builder = client.put(&url).headers(headers).json(&data); + let response = builder.send().await?; + + match response.status().as_u16() { + 204 => Ok(()), + status @ _ => { + bail!("failed to put configs with status \"{status}\"") + } + } +} + +/// PATCH /configs +pub async fn patch_configs(config: &Mapping) -> Result<()> { + let (url, headers) = clash_client_info()?; + let url = format!("{url}/configs"); + + let client = reqwest::ClientBuilder::new().no_proxy().build()?; + let builder = client.patch(&url).headers(headers.clone()).json(config); + builder.send().await?; + Ok(()) +} + +/// 根据clash info获取clash服务地址和请求头 +fn clash_client_info() -> Result<(String, HeaderMap)> { + let info = { config::ClashN::global().info.lock().clone() }; + + if info.server.is_none() { + let status = &info.status; + if info.port.is_none() { + bail!("failed to parse config.yaml file with status {status}"); + } else { + bail!("failed to parse the server with status {status}"); + } + } + + let server = info.server.unwrap(); + let server = format!("http://{server}"); + + let mut headers = HeaderMap::new(); + headers.insert("Content-Type", "application/json".parse()?); + + if let Some(secret) = info.secret.as_ref() { + let secret = format!("Bearer {}", secret.clone()).parse()?; + headers.insert("Authorization", secret); + } + + Ok((server, headers)) +} diff --git a/src-tauri/src/core/core.rs b/src-tauri/src/core/core.rs new file mode 100644 index 0000000..92296e7 --- /dev/null +++ b/src-tauri/src/core/core.rs @@ -0,0 +1,262 @@ +use super::{clash_api, logger::Logger}; +use crate::{ + config::*, + enhance, + utils::{self, dirs}, +}; +use anyhow::{bail, Context, Result}; +use once_cell::sync::OnceCell; +use parking_lot::Mutex; +use std::{fs, io::Write, sync::Arc, time::Duration}; +use sysinfo::{Pid, PidExt, ProcessExt, System, SystemExt}; +use tauri::api::process::{Command, CommandChild, CommandEvent}; +use tokio::time::sleep; + +#[derive(Debug)] +pub struct CoreManager { + clash_core: Arc>, + + sidecar: Arc>>, + + #[allow(unused)] + use_service_mode: Arc>, + + pub runtime_config: Arc>, +} + +impl CoreManager { + pub fn global() -> &'static CoreManager { + static CORE_MANAGER: OnceCell = OnceCell::new(); + + CORE_MANAGER.get_or_init(|| CoreManager { + clash_core: Arc::new(Mutex::new("clash".into())), + sidecar: Arc::new(Mutex::new(None)), + runtime_config: Arc::new(Mutex::new(RuntimeResult::default())), + use_service_mode: Arc::new(Mutex::new(false)), + }) + } + + pub fn init(&self) -> Result<()> { + // kill old clash process + if let Ok(pid) = fs::read(dirs::clash_pid_path()) { + if let Ok(pid) = String::from_utf8_lossy(&pid).parse() { + let mut system = System::new(); + system.refresh_all(); + system.process(Pid::from_u32(pid)).map(|proc| { + if proc.name().contains("clash") { + proc.kill(); + } + }); + } + } + + // 使用配置的核心 + let verge = VergeN::global().config.lock(); + if let Some(verge_core) = verge.clash_core.as_ref() { + if verge_core == "clash" || verge_core == "clash-meta" { + let mut clash_core = self.clash_core.lock(); + *clash_core = verge_core.clone(); + } + } + + // 启动clash + self.run_core()?; + + // 更新配置 + tauri::async_runtime::spawn(async { + sleep(Duration::from_millis(100)).await; + crate::log_err!(Self::global().activate_config().await); + }); + + Ok(()) + } + + /// 检查配置是否正确 + pub fn check_config(&self) -> Result<()> { + let config_path = dirs::clash_runtime_yaml(); + let config_path = dirs::path_to_str(&config_path)?; + + let clash_core = { self.clash_core.lock().clone() }; + + let output = Command::new_sidecar(clash_core)? + .args(["-t", config_path]) + .output()?; + + if !output.status.success() { + Logger::global().set_log(output.stderr.clone()); + bail!("{}", output.stderr); // 过滤掉终端颜色值 + } + + Ok(()) + } + + /// 启动核心 + pub fn run_core(&self) -> Result<()> { + // 先纠正重要的配置字段 + self.correct_config()?; + + let mut sidecar = self.sidecar.lock(); + + if let Some(child) = sidecar.take() { + let _ = child.kill(); + } + + let app_dir = dirs::app_home_dir(); + let app_dir = dirs::path_to_str(&app_dir)?; + + let clash_core = { self.clash_core.lock().clone() }; + + // fix #212 + let args = match clash_core.as_str() { + "clash-meta" => vec!["-m", "-d", app_dir], + _ => vec!["-d", app_dir], + }; + + let cmd = Command::new_sidecar(clash_core)?; + let (mut rx, cmd_child) = cmd.args(args).spawn()?; + + // 将pid写入文件中 + crate::log_err!({ + let pid = cmd_child.pid(); + let path = dirs::clash_pid_path(); + fs::File::create(path) + .context("failed to create the pid file")? + .write(format!("{pid}").as_bytes()) + .context("failed to write pid to the file")?; + >::Ok(()) + }); + + *sidecar = Some(cmd_child); + + tauri::async_runtime::spawn(async move { + while let Some(event) = rx.recv().await { + match event { + CommandEvent::Stdout(line) => { + let can_short = line.starts_with("time=") && line.len() > 33; + let stdout = if can_short { &line[33..] } else { &line }; + log::info!(target: "app" ,"[clash]: {}", stdout); + Logger::global().set_log(line); + } + CommandEvent::Stderr(err) => { + log::error!(target: "app" ,"[clash error]: {err}"); + Logger::global().set_log(err); + } + CommandEvent::Error(err) => { + log::error!(target: "app" ,"[clash error]: {err}"); + Logger::global().set_log(err); + } + CommandEvent::Terminated(_) => { + log::info!(target: "app" ,"clash core Terminated"); + break; + } + _ => {} + } + } + }); + + Ok(()) + } + + /// 停止核心运行 + pub fn stop_core(&self) -> Result<()> { + let mut sidecar = self.sidecar.lock(); + if let Some(child) = sidecar.take() { + let _ = child.kill(); + } + Ok(()) + } + + /// 切换核心 + pub async fn change_core(&self, clash_core: Option) -> Result<()> { + let clash_core = clash_core.ok_or(anyhow::anyhow!("clash core is null"))?; + + if &clash_core != "clash" && &clash_core != "clash-meta" { + bail!("invalid clash core name \"{clash_core}\""); + } + + // 清掉旧日志 + Logger::global().clear_log(); + + let mut self_core = self.clash_core.lock(); + let old_core = self_core.clone(); // 保存一下旧值 + *self_core = clash_core.clone(); + drop(self_core); + + match self.run_core() { + Ok(_) => { + // 更新到配置文件 + let mut verge = VergeN::global().config.lock(); + verge.clash_core = Some(clash_core); + drop(verge); + + let _ = VergeN::global().save_file(); + + sleep(Duration::from_millis(100)).await; // 等一会儿再更新配置 + self.activate_config().await?; + Ok(()) + } + Err(err) => { + // 恢复旧的值 + let mut self_core = self.clash_core.lock(); + *self_core = old_core; + Err(err) + } + } + } + + /// 纠正一下配置 + /// 将mixed-port和external-controller都改为配置的内容 + pub fn correct_config(&self) -> Result<()> { + // todo!() + Ok(()) + } + + /// 激活一个配置 + pub async fn activate_config(&self) -> Result<()> { + let clash_config = { ClashN::global().config.lock().clone() }; + + let tun_mode = { VergeN::global().config.lock().enable_tun_mode.clone() }; + let tun_mode = tun_mode.unwrap_or(false); + + let pa = { ProfilesN::global().config.lock().gen_activate()? }; + + let (config, exists_keys, logs) = + enhance::enhance_config(clash_config, pa.current, pa.chain, pa.valid, tun_mode); + + // 保存到文件中 + let runtime_path = dirs::clash_runtime_yaml(); + utils::config::save_yaml(runtime_path, &config, Some("# Clash Verge Runtime Config"))?; + + // 检查配置是否正常 + self.check_config()?; + + // todo 是否需要检查核心是否运行 + + // 发送请求 发送5次 + for i in 0..5 { + match clash_api::put_configs().await { + Ok(_) => break, + Err(err) => { + if i < 4 { + log::error!(target: "app", "{err}"); + } else { + bail!(err); + } + } + } + sleep(Duration::from_millis(250)).await; + } + + // 保存结果 + let mut runtime = self.runtime_config.lock(); + let config_yaml = Some(serde_yaml::to_string(&config).unwrap_or("".into())); + *runtime = RuntimeResult { + config: Some(config), + config_yaml, + exists_keys, + chain_logs: logs, + }; + + Ok(()) + } +} diff --git a/src-tauri/src/core/core_service.rs b/src-tauri/src/core/core_service.rs new file mode 100644 index 0000000..7d52194 --- /dev/null +++ b/src-tauri/src/core/core_service.rs @@ -0,0 +1,217 @@ +#![cfg(target_os = "windows")] + +use crate::utils::{config, dirs}; +use anyhow::Context; +use deelevate::{PrivilegeLevel, Token}; +use runas::Command as RunasCommand; +use serde::{Deserialize, Serialize}; +use std::os::windows::process::CommandExt; +use std::{env::current_exe, process::Command as StdCommand}; + +const SERVICE_NAME: &str = "clash_verge_service"; + +const SERVICE_URL: &str = "http://127.0.0.1:33211"; + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct ResponseBody { + pub bin_path: String, + pub config_dir: String, + pub log_file: String, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct JsonResponse { + pub code: u64, + pub msg: String, + pub data: Option, +} + +impl Service { + /// Install the Clash Verge Service + /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 + pub async fn install_service() -> Result<()> { + let binary_path = dirs::service_path(); + let install_path = binary_path.with_file_name("install-service.exe"); + + if !install_path.exists() { + bail!("installer exe not found"); + } + + let token = Token::with_current_process()?; + let level = token.privilege_level()?; + + let status = match level { + PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).status()?, + _ => StdCommand::new(install_path) + .creation_flags(0x08000000) + .status()?, + }; + + if !status.success() { + bail!( + "failed to install service with status {}", + status.code().unwrap() + ); + } + + Ok(()) + } + + /// Uninstall the Clash Verge Service + /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 + pub async fn uninstall_service() -> Result<()> { + let binary_path = dirs::service_path(); + let uninstall_path = binary_path.with_file_name("uninstall-service.exe"); + + if !uninstall_path.exists() { + bail!("uninstaller exe not found"); + } + + let token = Token::with_current_process()?; + let level = token.privilege_level()?; + + let status = match level { + PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).status()?, + _ => StdCommand::new(uninstall_path) + .creation_flags(0x08000000) + .status()?, + }; + + if !status.success() { + bail!( + "failed to uninstall service with status {}", + status.code().unwrap() + ); + } + + Ok(()) + } + + /// [deprecated] + /// start service + /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 + pub async fn start_service() -> Result<()> { + let token = Token::with_current_process()?; + let level = token.privilege_level()?; + + let args = ["start", SERVICE_NAME]; + + let status = match level { + PrivilegeLevel::NotPrivileged => RunasCommand::new("sc").args(&args).status()?, + _ => StdCommand::new("sc").args(&args).status()?, + }; + + match status.success() { + true => Ok(()), + false => bail!( + "failed to start service with status {}", + status.code().unwrap() + ), + } + } + + /// stop service + pub async fn stop_service() -> Result<()> { + let url = format!("{SERVICE_URL}/stop_service"); + let res = reqwest::ClientBuilder::new() + .no_proxy() + .build()? + .post(url) + .send() + .await? + .json::() + .await + .context("failed to connect to the Clash Verge Service")?; + + if res.code != 0 { + bail!(res.msg); + } + + Ok(()) + } + + /// check the windows service status + pub async fn check_service() -> Result { + let url = format!("{SERVICE_URL}/get_clash"); + let response = reqwest::ClientBuilder::new() + .no_proxy() + .build()? + .get(url) + .send() + .await? + .json::() + .await + .context("failed to connect to the Clash Verge Service")?; + + Ok(response) + } + + /// start the clash by service + pub(super) async fn start_clash_by_service() -> Result<()> { + let status = Self::check_service().await?; + + if status.code == 0 { + Self::stop_clash_by_service().await?; + sleep(Duration::from_secs(1)).await; + } + + let clash_core = { + let global = Data::global(); + let verge = global.verge.lock(); + verge.clash_core.clone().unwrap_or("clash".into()) + }; + + let clash_bin = format!("{clash_core}.exe"); + let bin_path = current_exe().unwrap().with_file_name(clash_bin); + let bin_path = bin_path.as_os_str().to_str().unwrap(); + + let config_dir = dirs::app_home_dir(); + let config_dir = config_dir.as_os_str().to_str().unwrap(); + + let log_path = dirs::service_log_file(); + let log_path = log_path.as_os_str().to_str().unwrap(); + + let mut map = HashMap::new(); + map.insert("bin_path", bin_path); + map.insert("config_dir", config_dir); + map.insert("log_file", log_path); + + let url = format!("{SERVICE_URL}/start_clash"); + let res = reqwest::ClientBuilder::new() + .no_proxy() + .build()? + .post(url) + .json(&map) + .send() + .await? + .json::() + .await + .context("failed to connect to the Clash Verge Service")?; + + if res.code != 0 { + bail!(res.msg); + } + + Ok(()) + } + + /// stop the clash by service + pub(super) async fn stop_clash_by_service() -> Result<()> { + let url = format!("{SERVICE_URL}/stop_clash"); + let res = reqwest::ClientBuilder::new() + .no_proxy() + .build()? + .post(url) + .send() + .await? + .json::() + .await + .context("failed to connect to the Clash Verge Service")?; + + if res.code != 0 { + bail!(res.msg); + } + + Ok(()) + } +} diff --git a/src-tauri/src/core/handle.rs b/src-tauri/src/core/handle.rs index dd04a48..7026e34 100644 --- a/src-tauri/src/core/handle.rs +++ b/src-tauri/src/core/handle.rs @@ -1,65 +1,77 @@ use super::tray::Tray; use crate::log_if_err; use anyhow::{bail, Result}; +use once_cell::sync::OnceCell; +use parking_lot::Mutex; +use std::sync::Arc; use tauri::{AppHandle, Manager, Window}; #[derive(Debug, Default, Clone)] pub struct Handle { - pub app_handle: Option, + pub app_handle: Arc>>, } impl Handle { - pub fn set_inner(&mut self, app_handle: AppHandle) { - self.app_handle = Some(app_handle); + pub fn global() -> &'static Handle { + static HANDLE: OnceCell = OnceCell::new(); + + HANDLE.get_or_init(|| Handle { + app_handle: Arc::new(Mutex::new(None)), + }) + } + + pub fn init(&self, app_handle: AppHandle) { + *self.app_handle.lock() = Some(app_handle); } pub fn get_window(&self) -> Option { self.app_handle + .lock() .as_ref() .map_or(None, |a| a.get_window("main")) } - pub fn refresh_clash(&self) { - if let Some(window) = self.get_window() { + pub fn refresh_clash() { + if let Some(window) = Self::global().get_window() { log_if_err!(window.emit("verge://refresh-clash-config", "yes")); } } - pub fn refresh_verge(&self) { - if let Some(window) = self.get_window() { + pub fn refresh_verge() { + if let Some(window) = Self::global().get_window() { log_if_err!(window.emit("verge://refresh-verge-config", "yes")); } } #[allow(unused)] - pub fn refresh_profiles(&self) { - if let Some(window) = self.get_window() { + pub fn refresh_profiles() { + if let Some(window) = Self::global().get_window() { log_if_err!(window.emit("verge://refresh-profiles-config", "yes")); } } - pub fn notice_message(&self, status: String, msg: String) { - if let Some(window) = self.get_window() { - log_if_err!(window.emit("verge://notice-message", (status, msg))); + pub fn notice_message, M: Into>(status: S, msg: M) { + if let Some(window) = Self::global().get_window() { + log_if_err!(window.emit("verge://notice-message", (status.into(), msg.into()))); } } - pub fn update_systray(&self) -> Result<()> { - if self.app_handle.is_none() { - bail!("update_systray unhandle error"); + pub fn update_systray() -> Result<()> { + let app_handle = Self::global().app_handle.lock(); + if app_handle.is_none() { + bail!("update_systray unhandled error"); } - let app_handle = self.app_handle.as_ref().unwrap(); - Tray::update_systray(app_handle)?; + Tray::update_systray(app_handle.as_ref().unwrap())?; Ok(()) } /// update the system tray state - pub fn update_systray_part(&self) -> Result<()> { - if self.app_handle.is_none() { - bail!("update_systray unhandle error"); + pub fn update_systray_part() -> Result<()> { + let app_handle = Self::global().app_handle.lock(); + if app_handle.is_none() { + bail!("update_systray unhandled error"); } - let app_handle = self.app_handle.as_ref().unwrap(); - Tray::update_part(app_handle)?; + Tray::update_part(app_handle.as_ref().unwrap())?; Ok(()) } } diff --git a/src-tauri/src/core/hotkey.rs b/src-tauri/src/core/hotkey.rs index 4ce4847..48c4482 100644 --- a/src-tauri/src/core/hotkey.rs +++ b/src-tauri/src/core/hotkey.rs @@ -1,25 +1,30 @@ -use crate::{data::*, feat, log_if_err}; +use crate::{config, feat, log_err}; use anyhow::{bail, Result}; -use std::collections::HashMap; +use once_cell::sync::OnceCell; +use parking_lot::Mutex; +use std::{collections::HashMap, sync::Arc}; use tauri::{AppHandle, GlobalShortcutManager}; pub struct Hotkey { - current: Vec, // 保存当前的热键设置 - manager: Option, + current: Arc>>, // 保存当前的热键设置 + + app_handle: Arc>>, } impl Hotkey { - pub fn new() -> Hotkey { - Hotkey { - current: Vec::new(), - manager: None, - } + pub fn global() -> &'static Hotkey { + static HOTKEY: OnceCell = OnceCell::new(); + + HOTKEY.get_or_init(|| Hotkey { + current: Arc::new(Mutex::new(Vec::new())), + app_handle: Arc::new(Mutex::new(None)), + }) } - pub fn init(&mut self, app_handle: AppHandle) -> Result<()> { - self.manager = Some(app_handle); - let data = Data::global(); - let verge = data.verge.lock(); + pub fn init(&self, app_handle: AppHandle) -> Result<()> { + *self.app_handle.lock() = Some(app_handle); + + let verge = config::VergeN::global().config.lock(); if let Some(hotkeys) = verge.hotkeys.as_ref() { for hotkey in hotkeys.iter() { @@ -28,25 +33,26 @@ impl Hotkey { let key = iter.next(); if func.is_some() && key.is_some() { - log_if_err!(self.register(key.unwrap(), func.unwrap())); + log_err!(self.register(key.unwrap(), func.unwrap())); } else { log::error!(target: "app", "invalid hotkey \"{}\":\"{}\"", key.unwrap_or("None"), func.unwrap_or("None")); } } - self.current = hotkeys.clone(); + *self.current.lock() = hotkeys.clone(); } Ok(()) } fn get_manager(&self) -> Result { - if self.manager.is_none() { + let app_handle = self.app_handle.lock(); + if app_handle.is_none() { bail!("failed to get hotkey manager"); } - Ok(self.manager.as_ref().unwrap().global_shortcut_manager()) + Ok(app_handle.as_ref().unwrap().global_shortcut_manager()) } - fn register(&mut self, hotkey: &str, func: &str) -> Result<()> { + fn register(&self, hotkey: &str, func: &str) -> Result<()> { let mut manager = self.get_manager()?; if manager.is_registered(hotkey)? { @@ -54,16 +60,16 @@ impl Hotkey { } let f = match func.trim() { - "clash_mode_rule" => || feat::change_clash_mode("rule"), - "clash_mode_global" => || feat::change_clash_mode("global"), - "clash_mode_direct" => || feat::change_clash_mode("direct"), - "clash_mode_script" => || feat::change_clash_mode("script"), - "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(), + "clash_mode_rule" => || feat::change_clash_mode("rule".into()), + "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()), _ => bail!("invalid function \"{func}\""), }; @@ -73,14 +79,14 @@ impl Hotkey { Ok(()) } - fn unregister(&mut self, hotkey: &str) -> Result<()> { + fn unregister(&self, hotkey: &str) -> Result<()> { self.get_manager()?.unregister(&hotkey)?; log::info!(target: "app", "unregister hotkey {hotkey}"); Ok(()) } - pub fn update(&mut self, new_hotkeys: Vec) -> Result<()> { - let current = self.current.to_owned(); + pub fn update(&self, new_hotkeys: Vec) -> Result<()> { + let mut current = self.current.lock(); let old_map = Self::get_map_from_vec(¤t); let new_map = Self::get_map_from_vec(&new_hotkeys); @@ -91,10 +97,10 @@ impl Hotkey { }); add.iter().for_each(|(key, func)| { - log_if_err!(self.register(key, func)); + log_err!(self.register(key, func)); }); - self.current = new_hotkeys; + *current = new_hotkeys; Ok(()) } diff --git a/src-tauri/src/core/logger.rs b/src-tauri/src/core/logger.rs new file mode 100644 index 0000000..b426415 --- /dev/null +++ b/src-tauri/src/core/logger.rs @@ -0,0 +1,36 @@ +use once_cell::sync::OnceCell; +use parking_lot::Mutex; +use std::{collections::VecDeque, sync::Arc}; + +const LOGS_QUEUE_LEN: usize = 100; + +pub struct Logger { + log_data: Arc>>, +} + +impl Logger { + pub fn global() -> &'static Logger { + static LOGGER: OnceCell = OnceCell::new(); + + LOGGER.get_or_init(|| Logger { + log_data: Arc::new(Mutex::new(VecDeque::with_capacity(LOGS_QUEUE_LEN + 10))), + }) + } + + pub fn get_log(&self) -> VecDeque { + self.log_data.lock().clone() + } + + pub fn set_log(&self, text: String) { + let mut logs = self.log_data.lock(); + if logs.len() > LOGS_QUEUE_LEN { + logs.pop_front(); + } + logs.push_back(text); + } + + pub fn clear_log(&self) { + let mut logs = self.log_data.lock(); + logs.clear(); + } +} diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs index c91c609..c0d1d6a 100644 --- a/src-tauri/src/core/mod.rs +++ b/src-tauri/src/core/mod.rs @@ -1,349 +1,347 @@ -use self::handle::Handle; -use self::hotkey::Hotkey; -use self::sysopt::Sysopt; -use self::timer::Timer; -use crate::config::enhance_config; -use crate::data::*; -use crate::log_if_err; -use anyhow::{bail, Result}; -use once_cell::sync::OnceCell; -use parking_lot::Mutex; -use serde_yaml::{Mapping, Value}; -use std::sync::Arc; +// use self::handle::Handle; +// use self::hotkey::Hotkey; +// use self::sysopt::Sysopt; +// use self::timer::Timer; +// // use crate::data::*; +// // use crate::enhance::enhance_config; +// use crate::log_if_err; +// use anyhow::{bail, Result}; +// use once_cell::sync::OnceCell; +// use parking_lot::Mutex; +// use serde_yaml::{Mapping, Value}; +// use std::sync::Arc; -mod handle; -mod hotkey; -mod service; -mod sysopt; -mod timer; +pub mod clash_api; +mod core; +pub mod core_service; +pub mod handle; +pub mod hotkey; +pub mod logger; +// pub mod service; +pub mod sysopt; +pub mod timer; pub mod tray; -pub use self::service::*; +pub use self::core::*; +// pub use self::service::*; #[derive(Clone)] -pub struct Core { - pub service: Arc>, - pub sysopt: Arc>, - pub timer: Arc>, - pub hotkey: Arc>, - pub runtime: Arc>, - pub handle: Arc>, -} +pub struct Core {} impl Core { - pub fn global() -> &'static Core { - static CORE: OnceCell = OnceCell::new(); + // pub fn global() -> &'static Core { + // static CORE: OnceCell = OnceCell::new(); - CORE.get_or_init(|| Core { - service: Arc::new(Mutex::new(Service::new())), - sysopt: Arc::new(Mutex::new(Sysopt::new())), - timer: Arc::new(Mutex::new(Timer::new())), - hotkey: Arc::new(Mutex::new(Hotkey::new())), - runtime: Arc::new(Mutex::new(RuntimeResult::default())), - handle: Arc::new(Mutex::new(Handle::default())), - }) - } + // CORE.get_or_init(|| Core { + // service: Arc::new(Mutex::new(Service::new())), + // sysopt: Arc::new(Mutex::new(Sysopt::new())), + // timer: Arc::new(Mutex::new(Timer::new())), + // hotkey: Arc::new(Mutex::new(Hotkey::new())), + // runtime: Arc::new(Mutex::new(RuntimeResult::default())), + // handle: Arc::new(Mutex::new(Handle::default())), + // }) + // } - /// initialize the core state - pub fn init(&self, app_handle: tauri::AppHandle) { - // kill old clash process - Service::kill_old_clash(); + // /// initialize the core state + // pub fn init(&self, app_handle: tauri::AppHandle) { + // kill old clash process + // Service::kill_old_clash(); - let mut handle = self.handle.lock(); - handle.set_inner(app_handle.clone()); - drop(handle); + // let mut handle = self.handle.lock(); + // handle.set_inner(app_handle.clone()); + // drop(handle); - let mut service = self.service.lock(); - log_if_err!(service.start()); - drop(service); + // let mut service = self.service.lock(); + // log_if_err!(service.start()); + // drop(service); - log_if_err!(self.activate()); + // log_if_err!(self.activate()); - let mut sysopt = self.sysopt.lock(); - log_if_err!(sysopt.init_launch()); - log_if_err!(sysopt.init_sysproxy()); - drop(sysopt); + // let mut sysopt = self.sysopt.lock(); + // log_if_err!(sysopt.init_launch()); + // log_if_err!(sysopt.init_sysproxy()); + // drop(sysopt); - let handle = self.handle.lock(); - log_if_err!(handle.update_systray_part()); - drop(handle); + // let handle = self.handle.lock(); + // log_if_err!(handle.update_systray_part()); + // drop(handle); - let mut hotkey = self.hotkey.lock(); - log_if_err!(hotkey.init(app_handle)); - drop(hotkey); + // let mut hotkey = self.hotkey.lock(); + // log_if_err!(hotkey.init(app_handle)); + // drop(hotkey); - // timer initialize - let mut timer = self.timer.lock(); - log_if_err!(timer.restore()); - } + // // timer initialize + // let mut timer = self.timer.lock(); + // log_if_err!(timer.restore()); + // } - /// restart the clash sidecar - pub fn restart_clash(&self) -> Result<()> { - let mut service = self.service.lock(); - service.restart()?; - drop(service); - self.activate() - } + // /// restart the clash sidecar + // pub fn restart_clash(&self) -> Result<()> { + // let mut service = self.service.lock(); + // service.restart()?; + // drop(service); + // self.activate() + // } - /// change the clash core - pub fn change_core(&self, clash_core: Option) -> Result<()> { - let clash_core = clash_core.unwrap_or("clash".into()); + // /// change the clash core + // pub fn change_core(&self, clash_core: Option) -> Result<()> { + // let clash_core = clash_core.unwrap_or("clash".into()); - if &clash_core != "clash" && &clash_core != "clash-meta" { - bail!("invalid clash core name \"{clash_core}\""); - } + // if &clash_core != "clash" && &clash_core != "clash-meta" { + // bail!("invalid clash core name \"{clash_core}\""); + // } - let global = Data::global(); - let mut verge = global.verge.lock(); - verge.patch_config(Verge { - clash_core: Some(clash_core.clone()), - ..Verge::default() - })?; - drop(verge); + // let global = Data::global(); + // let mut verge = global.verge.lock(); + // verge.patch_config(Verge { + // clash_core: Some(clash_core.clone()), + // ..Verge::default() + // })?; + // drop(verge); - let mut service = self.service.lock(); - service.clear_logs(); - service.restart()?; - drop(service); + // let mut service = self.service.lock(); + // service.clear_logs(); + // service.restart()?; + // drop(service); - self.activate() - } + // self.activate() + // } - /// Patch Clash - /// handle the clash config changed - pub fn patch_clash(&self, patch: Mapping) -> Result<()> { - let patch_cloned = patch.clone(); - let clash_mode = patch.get("mode"); - let mixed_port = patch.get("mixed-port"); - let external = patch.get("external-controller"); - let secret = patch.get("secret"); + // /// Patch Clash + // /// handle the clash config changed + // pub fn patch_clash(&self, patch: Mapping) -> Result<()> { + // let patch_cloned = patch.clone(); + // let clash_mode = patch.get("mode"); + // let mixed_port = patch.get("mixed-port"); + // let external = patch.get("external-controller"); + // let secret = patch.get("secret"); - let valid_port = { - let global = Data::global(); - let mut clash = global.clash.lock(); - clash.patch_config(patch_cloned)?; - clash.info.port.is_some() - }; + // let valid_port = { + // let global = Data::global(); + // let mut clash = global.clash.lock(); + // clash.patch_config(patch_cloned)?; + // clash.info.port.is_some() + // }; - // todo: port check - if (mixed_port.is_some() && valid_port) || external.is_some() || secret.is_some() { - let mut service = self.service.lock(); - service.restart()?; - drop(service); + // // todo: port check + // if (mixed_port.is_some() && valid_port) || external.is_some() || secret.is_some() { + // let mut service = self.service.lock(); + // service.restart()?; + // drop(service); - self.activate()?; + // self.activate()?; - let mut sysopt = self.sysopt.lock(); - sysopt.init_sysproxy()?; - } + // let mut sysopt = self.sysopt.lock(); + // sysopt.init_sysproxy()?; + // } - if clash_mode.is_some() { - let handle = self.handle.lock(); - handle.update_systray_part()?; - } + // if clash_mode.is_some() { + // let handle = self.handle.lock(); + // handle.update_systray_part()?; + // } - Ok(()) - } + // Ok(()) + // } - /// Patch Verge - pub fn patch_verge(&self, patch: Verge) -> Result<()> { - // save the patch - let global = Data::global(); - let mut verge = global.verge.lock(); - verge.patch_config(patch.clone())?; - drop(verge); + // /// Patch Verge + // pub fn patch_verge(&self, patch: Verge) -> Result<()> { + // // save the patch + // let global = Data::global(); + // let mut verge = global.verge.lock(); + // verge.patch_config(patch.clone())?; + // drop(verge); - 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; + // 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")] - { - let service_mode = patch.enable_service_mode; + // #[cfg(target_os = "windows")] + // { + // let service_mode = patch.enable_service_mode; - // 重启服务 - if service_mode.is_some() { - let mut service = self.service.lock(); - service.restart()?; - drop(service); - } + // // 重启服务 + // if service_mode.is_some() { + // let mut service = self.service.lock(); + // service.restart()?; + // drop(service); + // } - if tun_mode.is_some() && *tun_mode.as_ref().unwrap_or(&false) { - let wintun_dll = crate::utils::dirs::app_home_dir().join("wintun.dll"); - if !wintun_dll.exists() { - bail!("failed to enable TUN for missing `wintun.dll`"); - } - } + // if tun_mode.is_some() && *tun_mode.as_ref().unwrap_or(&false) { + // let wintun_dll = crate::utils::dirs::app_home_dir().join("wintun.dll"); + // if !wintun_dll.exists() { + // bail!("failed to enable TUN for missing `wintun.dll`"); + // } + // } - if service_mode.is_some() || tun_mode.is_some() { - self.activate()?; - } - } + // if service_mode.is_some() || tun_mode.is_some() { + // self.activate()?; + // } + // } - #[cfg(not(target_os = "windows"))] - if tun_mode.is_some() { - self.activate()?; - } + // #[cfg(not(target_os = "windows"))] + // if tun_mode.is_some() { + // self.activate()?; + // } - let mut sysopt = self.sysopt.lock(); + // let mut sysopt = self.sysopt.lock(); - if auto_launch.is_some() { - sysopt.update_launch()?; - } - if system_proxy.is_some() || proxy_bypass.is_some() { - sysopt.update_sysproxy()?; - sysopt.guard_proxy(); - } - if proxy_guard.unwrap_or(false) { - sysopt.guard_proxy(); - } + // if auto_launch.is_some() { + // sysopt.update_launch()?; + // } + // if system_proxy.is_some() || proxy_bypass.is_some() { + // sysopt.update_sysproxy()?; + // sysopt.guard_proxy(); + // } + // if proxy_guard.unwrap_or(false) { + // sysopt.guard_proxy(); + // } - // 更新tray - if language.is_some() { - let handle = self.handle.lock(); - handle.update_systray()?; - } else if system_proxy.is_some() || tun_mode.is_some() { - let handle = self.handle.lock(); - handle.update_systray_part()?; - } + // // 更新tray + // if language.is_some() { + // let handle = self.handle.lock(); + // handle.update_systray()?; + // } else if system_proxy.is_some() || tun_mode.is_some() { + // let handle = self.handle.lock(); + // handle.update_systray_part()?; + // } - if patch.hotkeys.is_some() { - let mut hotkey = self.hotkey.lock(); - hotkey.update(patch.hotkeys.unwrap())?; - } + // if patch.hotkeys.is_some() { + // let mut hotkey = self.hotkey.lock(); + // hotkey.update(patch.hotkeys.unwrap())?; + // } - Ok(()) - } + // Ok(()) + // } - // update rule/global/direct/script mode - pub fn update_mode(&self, mode: &str) -> Result<()> { - // save config to file - let info = { - let global = Data::global(); - let mut clash = global.clash.lock(); - clash.config.insert(Value::from("mode"), Value::from(mode)); - clash.save_config()?; - clash.info.clone() - }; + // /// update rule/global/direct/script mode + // pub fn update_mode(&self, mode: &str) -> Result<()> { + // // save config to file + // let info = { + // let global = Data::global(); + // let mut clash = global.clash.lock(); + // clash.config.insert(Value::from("mode"), Value::from(mode)); + // clash.save_config()?; + // clash.info.clone() + // }; - let mut mapping = Mapping::new(); - mapping.insert(Value::from("mode"), Value::from(mode)); + // let mut mapping = Mapping::new(); + // mapping.insert(Value::from("mode"), Value::from(mode)); - let handle = self.handle.clone(); + // let handle = self.handle.clone(); - tauri::async_runtime::spawn(async move { - log_if_err!(Service::patch_config(info, mapping.to_owned()).await); + // tauri::async_runtime::spawn(async move { + // log_if_err!(Service::patch_config(info, mapping.to_owned()).await); - // update tray - let handle = handle.lock(); - handle.refresh_clash(); - log_if_err!(handle.update_systray_part()); - }); + // // update tray + // let handle = handle.lock(); + // handle.refresh_clash(); + // log_if_err!(handle.update_systray_part()); + // }); - Ok(()) - } + // Ok(()) + // } - /// activate the profile - /// auto activate enhanced profile - /// 触发clash配置更新 - pub fn activate(&self) -> Result<()> { - let global = Data::global(); + // /// activate the profile + // /// auto activate enhanced profile + // /// 触发clash配置更新 + // pub fn activate(&self) -> Result<()> { + // let global = Data::global(); - let verge = global.verge.lock(); - let clash = global.clash.lock(); - let profiles = global.profiles.lock(); + // let verge = global.verge.lock(); + // let clash = global.clash.lock(); + // let profiles = global.profiles.lock(); - let tun_mode = verge.enable_tun_mode.clone().unwrap_or(false); - let profile_activate = profiles.gen_activate()?; + // let tun_mode = verge.enable_tun_mode.clone().unwrap_or(false); + // let profile_activate = profiles.gen_activate()?; - let clash_config = clash.config.clone(); - let clash_info = clash.info.clone(); + // let clash_config = clash.config.clone(); + // let clash_info = clash.info.clone(); - drop(clash); - drop(verge); - drop(profiles); + // drop(clash); + // drop(verge); + // drop(profiles); - let (config, exists_keys, logs) = enhance_config( - clash_config, - profile_activate.current, - profile_activate.chain, - profile_activate.valid, - tun_mode, - ); + // let (config, exists_keys, logs) = enhance_config( + // clash_config, + // profile_activate.current, + // profile_activate.chain, + // profile_activate.valid, + // tun_mode, + // ); - let mut runtime = self.runtime.lock(); - *runtime = RuntimeResult { - config: Some(config.clone()), - config_yaml: Some(serde_yaml::to_string(&config).unwrap_or("".into())), - exists_keys, - chain_logs: logs, - }; - drop(runtime); + // let mut runtime = self.runtime.lock(); + // *runtime = RuntimeResult { + // config: Some(config.clone()), + // config_yaml: Some(serde_yaml::to_string(&config).unwrap_or("".into())), + // exists_keys, + // chain_logs: logs, + // }; + // drop(runtime); - let mut service = self.service.lock(); - service.check_start()?; - drop(service); + // let mut service = self.service.lock(); + // service.check_start()?; + // drop(service); - let handle = self.handle.clone(); - tauri::async_runtime::spawn(async move { - match Service::set_config(clash_info, config).await { - Ok(_) => { - let handle = handle.lock(); - handle.refresh_clash(); - handle.notice_message("set_config::ok".into(), "ok".into()); - } - Err(err) => { - let handle = handle.lock(); - handle.notice_message("set_config::error".into(), format!("{err}")); - log::error!(target: "app", "{err}") - } - } - }); + // let handle = self.handle.clone(); + // tauri::async_runtime::spawn(async move { + // match Service::set_config(clash_info, config).await { + // Ok(_) => { + // let handle = handle.lock(); + // handle.refresh_clash(); + // handle.notice_message("set_config::ok".into(), "ok".into()); + // } + // Err(err) => { + // let handle = handle.lock(); + // handle.notice_message("set_config::error".into(), format!("{err}")); + // log::error!(target: "app", "{err}") + // } + // } + // }); - Ok(()) - } + // Ok(()) + // } - /// Static function - /// update profile item - pub async fn update_profile_item(&self, uid: String, option: Option) -> Result<()> { - let global = Data::global(); + // /// Static function + // /// update profile item + // pub async fn update_profile_item(&self, uid: String, option: Option) -> Result<()> { + // let global = Data::global(); - let (url, opt) = { - let profiles = global.profiles.lock(); - let item = profiles.get_item(&uid)?; + // let (url, opt) = { + // let profiles = global.profiles.lock(); + // let item = profiles.get_item(&uid)?; - if let Some(typ) = item.itype.as_ref() { - // maybe only valid for `local` profile - if *typ != "remote" { - // reactivate the config - if Some(uid) == profiles.get_current() { - drop(profiles); - self.activate()?; - } - return Ok(()); - } - } - if item.url.is_none() { - bail!("failed to get the profile item url"); - } - (item.url.clone().unwrap(), item.option.clone()) - }; + // if let Some(typ) = item.itype.as_ref() { + // // maybe only valid for `local` profile + // if *typ != "remote" { + // // reactivate the config + // if Some(uid) == profiles.get_current() { + // drop(profiles); + // self.activate()?; + // } + // return Ok(()); + // } + // } + // if item.url.is_none() { + // bail!("failed to get the profile item url"); + // } + // (item.url.clone().unwrap(), item.option.clone()) + // }; - let merged_opt = PrfOption::merge(opt, option); - let item = PrfItem::from_url(&url, None, None, merged_opt).await?; + // let merged_opt = PrfOption::merge(opt, option); + // let item = PrfItem::from_url(&url, None, None, merged_opt).await?; - let mut profiles = global.profiles.lock(); - profiles.update_item(uid.clone(), item)?; + // let mut profiles = global.profiles.lock(); + // profiles.update_item(uid.clone(), item)?; - // reactivate the profile - if Some(uid) == profiles.get_current() { - drop(profiles); - self.activate()?; - } + // // reactivate the profile + // if Some(uid) == profiles.get_current() { + // drop(profiles); + // self.activate()?; + // } - Ok(()) - } + // Ok(()) + // } } diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index 21fb7ed..36c5fae 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -213,7 +213,7 @@ impl Service { /// update clash config /// using PUT methods pub async fn set_config(info: ClashInfo, config: Mapping) -> Result<()> { - let temp_path = dirs::profiles_temp_path(); + let temp_path = dirs::clash_runtime_yaml(); config::save_yaml(temp_path.clone(), &config, Some("# Clash Verge Temp File"))?; let (server, headers) = Self::clash_client_info(info)?; @@ -222,13 +222,13 @@ impl Service { data.insert("path", temp_path.as_os_str().to_str().unwrap()); macro_rules! report_err { - ($i: expr, $e: expr) => { - match $i { - 4 => bail!($e), - _ => log::error!(target: "app", $e), + ($i: expr, $e: expr) => { + match $i { + 4 => bail!($e), + _ => log::error!(target: "app", $e), + } + }; } - }; - } // retry 5 times for i in 0..5 { diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index 53bf939..7850474 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -1,23 +1,25 @@ -use crate::{data::*, log_if_err}; +use crate::{config, log_err}; use anyhow::{anyhow, bail, Result}; use auto_launch::{AutoLaunch, AutoLaunchBuilder}; +use once_cell::sync::OnceCell; +use parking_lot::Mutex; use std::sync::Arc; use sysproxy::Sysproxy; -use tauri::{async_runtime::Mutex, utils::platform::current_exe}; +use tauri::{async_runtime::Mutex as TokioMutex, utils::platform::current_exe}; pub struct Sysopt { /// current system proxy setting - cur_sysproxy: Option, + cur_sysproxy: Arc>>, /// record the original system proxy /// recover it when exit - old_sysproxy: Option, + old_sysproxy: Arc>>, /// helps to auto launch the app - auto_launch: Option, + auto_launch: Arc>>, /// record whether the guard async is running or not - guard_state: Arc>, + guard_state: Arc>, } #[cfg(target_os = "windows")] @@ -28,44 +30,45 @@ static DEFAULT_BYPASS: &str = "localhost,127.0.0.1/8,::1"; static DEFAULT_BYPASS: &str = "127.0.0.1,localhost,"; impl Sysopt { - pub fn new() -> Sysopt { - Sysopt { - cur_sysproxy: None, - old_sysproxy: None, - auto_launch: None, - guard_state: Arc::new(Mutex::new(false)), - } + pub fn global() -> &'static Sysopt { + static SYSOPT: OnceCell = OnceCell::new(); + + SYSOPT.get_or_init(|| Sysopt { + cur_sysproxy: Arc::new(Mutex::new(None)), + old_sysproxy: Arc::new(Mutex::new(None)), + auto_launch: Arc::new(Mutex::new(None)), + guard_state: Arc::new(TokioMutex::new(false)), + }) } /// init the sysproxy - pub fn init_sysproxy(&mut self) -> Result<()> { - let data = Data::global(); - let clash = data.clash.lock(); - let port = clash.info.port.clone(); + pub fn init_sysproxy(&self) -> Result<()> { + let port = { config::ClashN::global().info.lock().port.clone() }; if port.is_none() { bail!("clash port is none"); } - let verge = data.verge.lock(); + let port = port.unwrap().parse::()?; + let verge = config::VergeN::global().config.lock(); let enable = verge.enable_system_proxy.clone().unwrap_or(false); let bypass = verge.system_proxy_bypass.clone(); let bypass = bypass.unwrap_or(DEFAULT_BYPASS.into()); - let port = port.unwrap().parse::()?; - let host = String::from("127.0.0.1"); - - self.cur_sysproxy = Some(Sysproxy { + let current = Sysproxy { enable, - host, + host: String::from("127.0.0.1"), port, bypass, - }); + }; if enable { - self.old_sysproxy = Sysproxy::get_system_proxy().map_or(None, |p| Some(p)); - self.cur_sysproxy.as_ref().unwrap().set_system_proxy()?; + let old = Sysproxy::get_system_proxy().map_or(None, |p| Some(p)); + current.set_system_proxy()?; + + *self.old_sysproxy.lock() = old; + *self.cur_sysproxy.lock() = Some(current); } // run the system proxy guard @@ -74,37 +77,44 @@ impl Sysopt { } /// update the system proxy - pub fn update_sysproxy(&mut self) -> Result<()> { - if self.cur_sysproxy.is_none() || self.old_sysproxy.is_none() { + pub fn update_sysproxy(&self) -> Result<()> { + let mut cur_sysproxy = self.cur_sysproxy.lock(); + let old_sysproxy = self.old_sysproxy.lock(); + + if cur_sysproxy.is_none() || old_sysproxy.is_none() { + drop(cur_sysproxy); + drop(old_sysproxy); return self.init_sysproxy(); } - let data = Data::global(); - let verge = data.verge.lock(); + let verge = config::VergeN::global().config.lock(); let enable = verge.enable_system_proxy.clone().unwrap_or(false); let bypass = verge.system_proxy_bypass.clone(); let bypass = bypass.unwrap_or(DEFAULT_BYPASS.into()); - let mut sysproxy = self.cur_sysproxy.take().unwrap(); + let mut sysproxy = cur_sysproxy.take().unwrap(); sysproxy.enable = enable; sysproxy.bypass = bypass; - self.cur_sysproxy = Some(sysproxy); - self.cur_sysproxy.as_ref().unwrap().set_system_proxy()?; + sysproxy.set_system_proxy()?; + *cur_sysproxy = Some(sysproxy); Ok(()) } /// reset the sysproxy - pub fn reset_sysproxy(&mut self) -> Result<()> { - let cur = self.cur_sysproxy.take(); + pub fn reset_sysproxy(&self) -> Result<()> { + let mut cur_sysproxy = self.cur_sysproxy.lock(); + let mut old_sysproxy = self.old_sysproxy.lock(); - if let Some(mut old) = self.old_sysproxy.take() { + let cur_sysproxy = cur_sysproxy.take(); + + if let Some(mut old) = old_sysproxy.take() { // 如果原代理和当前代理 端口一致,就disable关闭,否则就恢复原代理设置 // 当前没有设置代理的时候,不确定旧设置是否和当前一致,全关了 - let port_same = cur.map_or(true, |cur| old.port == cur.port); + let port_same = cur_sysproxy.map_or(true, |cur| old.port == cur.port); if old.enable && port_same { old.enable = false; @@ -114,7 +124,7 @@ impl Sysopt { } old.set_system_proxy()?; - } else if let Some(mut cur @ Sysproxy { enable: true, .. }) = cur { + } else if let Some(mut cur @ Sysproxy { enable: true, .. }) = cur_sysproxy { // 没有原代理,就按现在的代理设置disable即可 log::info!(target: "app", "reset proxy by disabling the current proxy"); cur.enable = false; @@ -127,9 +137,8 @@ impl Sysopt { } /// init the auto launch - pub fn init_launch(&mut self) -> Result<()> { - let data = Data::global(); - let verge = data.verge.lock(); + pub fn init_launch(&self) -> Result<()> { + let verge = config::VergeN::global().config.lock(); let enable = verge.enable_auto_launch.clone().unwrap_or(false); let app_exe = current_exe()?; @@ -167,51 +176,41 @@ impl Sysopt { .set_app_path(&app_path) .build()?; - self.auto_launch = Some(auto); - // 避免在开发时将自启动关了 #[cfg(feature = "verge-dev")] if !enable { return Ok(()); } - let auto = self.auto_launch.as_ref().unwrap(); - // macos每次启动都更新登录项,避免重复设置登录项 #[cfg(target_os = "macos")] - { - let _ = auto.disable(); - if enable { - auto.enable()?; - } - } + let _ = auto.disable(); - #[cfg(not(target_os = "macos"))] - { - match enable { - true => auto.enable()?, - false => auto.disable()?, - }; + if enable { + auto.enable()?; } + *self.auto_launch.lock() = Some(auto); Ok(()) } /// update the startup - pub fn update_launch(&mut self) -> Result<()> { - if self.auto_launch.is_none() { + pub fn update_launch(&self) -> Result<()> { + let auto_launch = self.auto_launch.lock(); + + if auto_launch.is_none() { + drop(auto_launch); return self.init_launch(); } - let data = Data::global(); - let verge = data.verge.lock(); + let verge = config::VergeN::global().config.lock(); let enable = verge.enable_auto_launch.clone().unwrap_or(false); - let auto_launch = self.auto_launch.as_ref().unwrap(); + let auto_launch = auto_launch.as_ref().unwrap(); match enable { true => auto_launch.enable()?, - false => crate::log_if_err!(auto_launch.disable()), // 忽略关闭的错误 + false => log_err!(auto_launch.disable()), // 忽略关闭的错误 }; Ok(()) @@ -239,8 +238,7 @@ impl Sysopt { loop { sleep(Duration::from_secs(wait_secs)).await; - let global = Data::global(); - let verge = global.verge.lock(); + let verge = config::VergeN::global().config.lock(); let enable = verge.enable_system_proxy.clone().unwrap_or(false); let guard = verge.enable_proxy_guard.clone().unwrap_or(false); @@ -256,14 +254,10 @@ impl Sysopt { // update duration wait_secs = guard_duration; - let clash = global.clash.lock(); - let port = clash.info.port.clone(); - let port = port.unwrap_or("".into()).parse::(); - drop(clash); - log::debug!(target: "app", "try to guard the system proxy"); - match port { + let port = { config::ClashN::global().info.lock().port.clone() }; + match port.unwrap_or("".into()).parse::() { Ok(port) => { let sysproxy = Sysproxy { enable: true, @@ -272,14 +266,17 @@ impl Sysopt { bypass: bypass.unwrap_or(DEFAULT_BYPASS.into()), }; - log_if_err!(sysproxy.set_system_proxy()); + log_err!(sysproxy.set_system_proxy()); + } + Err(_) => { + log::error!(target: "app", "failed to parse clash port in guard proxy") } - Err(_) => log::error!(target: "app", "failed to parse clash port"), } } let mut state = guard_state.lock().await; *state = false; + drop(state); }); } } diff --git a/src-tauri/src/core/timer.rs b/src-tauri/src/core/timer.rs index 4d9ccba..8859dcc 100644 --- a/src-tauri/src/core/timer.rs +++ b/src-tauri/src/core/timer.rs @@ -1,50 +1,96 @@ -use super::Core; -use crate::utils::help::get_now; -use crate::{data::Data, log_if_err}; +use crate::config::{self, ProfilesN}; use anyhow::{Context, Result}; use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder}; +use once_cell::sync::OnceCell; +use parking_lot::Mutex; use std::collections::HashMap; +use std::sync::Arc; type TaskID = u64; pub struct Timer { /// cron manager - delay_timer: DelayTimer, + delay_timer: Arc>, /// save the current state - timer_map: HashMap, + timer_map: Arc>>, /// increment id - timer_count: TaskID, + timer_count: Arc>, } impl Timer { - pub fn new() -> Self { - Timer { - delay_timer: DelayTimerBuilder::default().build(), - timer_map: HashMap::new(), - timer_count: 1, - } + pub fn global() -> &'static Timer { + static TIMER: OnceCell = OnceCell::new(); + + TIMER.get_or_init(|| Timer { + delay_timer: Arc::new(Mutex::new(DelayTimerBuilder::default().build())), + timer_map: Arc::new(Mutex::new(HashMap::new())), + timer_count: Arc::new(Mutex::new(1)), + }) + } + + /// restore timer + pub fn init(&self) -> Result<()> { + self.refresh()?; + + 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| { + items + .iter() + // .filter_map(|item| { + // item.uid.is_some() && item.updated.is_some() && item.option.is_some() + // }) + .filter_map(|item| { + // mins to seconds + let interval = ((item.option.as_ref()?.update_interval?) as i64) * 60; + let updated = item.updated? as i64; + + if interval > 0 && cur_timestamp - updated >= interval { + Some(item) + } else { + None + } + }) + .for_each(|item| { + if let Some(uid) = item.uid.as_ref() { + if let Some((task_id, _)) = timer_map.get(uid) { + crate::log_err!(delay_timer.advance_task(*task_id)); + } + } + }) + }); + + Ok(()) } /// Correctly update all cron tasks - pub fn refresh(&mut self) -> Result<()> { + pub fn refresh(&self) -> Result<()> { let diff_map = self.gen_diff(); + let mut timer_map = self.timer_map.lock(); + let delay_timer = self.delay_timer.lock(); + for (uid, diff) in diff_map.into_iter() { match diff { DiffFlag::Del(tid) => { - let _ = self.timer_map.remove(&uid); - log_if_err!(self.delay_timer.remove_task(tid)); + let _ = timer_map.remove(&uid); + crate::log_err!(delay_timer.remove_task(tid)); } DiffFlag::Add(tid, val) => { - let _ = self.timer_map.insert(uid.clone(), (tid, val)); - log_if_err!(self.add_task(uid, tid, val)); + let _ = timer_map.insert(uid.clone(), (tid, val)); + crate::log_err!(self.add_task(uid, tid, val)); } DiffFlag::Mod(tid, val) => { - let _ = self.timer_map.insert(uid.clone(), (tid, val)); - log_if_err!(self.delay_timer.remove_task(tid)); - log_if_err!(self.add_task(uid, tid, val)); + let _ = timer_map.insert(uid.clone(), (tid, val)); + crate::log_err!(delay_timer.remove_task(tid)); + crate::log_err!(self.add_task(uid, tid, val)); } } } @@ -52,41 +98,9 @@ impl Timer { Ok(()) } - /// restore timer - pub fn restore(&mut self) -> Result<()> { - self.refresh()?; - - let cur_timestamp = get_now(); // seconds - - let global = Data::global(); - let profiles = global.profiles.lock(); - - profiles - .get_items() - .unwrap_or(&vec![]) - .iter() - .filter(|item| item.uid.is_some() && item.updated.is_some() && item.option.is_some()) - .filter(|item| { - // mins to seconds - let interval = - item.option.as_ref().unwrap().update_interval.unwrap_or(0) as usize * 60; - let updated = item.updated.unwrap(); - return interval > 0 && cur_timestamp - updated >= interval; - }) - .for_each(|item| { - let uid = item.uid.as_ref().unwrap(); - if let Some((task_id, _)) = self.timer_map.get(uid) { - log_if_err!(self.delay_timer.advance_task(*task_id)); - } - }); - - Ok(()) - } - /// generate a uid -> update_interval map fn gen_map(&self) -> HashMap { - let global = Data::global(); - let profiles = global.profiles.lock(); + let profiles = config::ProfilesN::global().config.lock(); let mut new_map = HashMap::new(); @@ -107,11 +121,13 @@ impl Timer { } /// generate the diff map for refresh - fn gen_diff(&mut self) -> HashMap { + fn gen_diff(&self) -> HashMap { let mut diff_map = HashMap::new(); + let timer_map = self.timer_map.lock(); + let new_map = self.gen_map(); - let cur_map = &self.timer_map; + let cur_map = &timer_map; cur_map.iter().for_each(|(uid, (tid, val))| { let new_val = new_map.get(uid).unwrap_or(&0); @@ -123,34 +139,31 @@ impl Timer { } }); - let mut count = self.timer_count; + let mut count = self.timer_count.lock(); new_map.iter().for_each(|(uid, val)| { if cur_map.get(uid).is_none() { - diff_map.insert(uid.clone(), DiffFlag::Add(count, *val)); + diff_map.insert(uid.clone(), DiffFlag::Add(*count, *val)); - count += 1; + *count += 1; } }); - self.timer_count = count; - diff_map } /// add a cron task fn add_task(&self, uid: String, tid: TaskID, minutes: u64) -> Result<()> { - let core = Core::global(); - let task = TaskBuilder::default() .set_task_id(tid) .set_maximum_parallel_runnable_num(1) .set_frequency_repeated_by_minutes(minutes) // .set_frequency_repeated_by_seconds(minutes) // for test - .spawn_async_routine(move || Self::async_task(core.to_owned(), uid.to_owned())) + .spawn_async_routine(move || Self::async_task(uid.to_owned())) .context("failed to create timer task")?; self.delay_timer + .lock() .add_task(task) .context("failed to add timer task")?; @@ -158,9 +171,9 @@ impl Timer { } /// the task runner - async fn async_task(core: Core, uid: String) { + async fn async_task(uid: String) { log::info!(target: "app", "running timer task `{uid}`"); - log_if_err!(core.update_profile_item(uid, None).await); + crate::log_err!(ProfilesN::global().update_item(uid, None).await); } } diff --git a/src-tauri/src/core/tray.rs b/src-tauri/src/core/tray.rs index a65c457..20762cb 100644 --- a/src-tauri/src/core/tray.rs +++ b/src-tauri/src/core/tray.rs @@ -1,5 +1,6 @@ -use crate::{data::Data, feat, utils::resolve}; -use anyhow::{Ok, Result}; +use crate::log_err; +use crate::{config, feat, utils::resolve}; +use anyhow::Result; use tauri::{ api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, SystemTraySubmenu, @@ -9,14 +10,16 @@ pub struct Tray {} impl Tray { pub fn tray_menu(app_handle: &AppHandle) -> SystemTrayMenu { - let data = Data::global(); let zh = { - let verge = data.verge.lock(); + let verge = config::VergeN::global().config.lock(); verge.language == Some("zh".into()) }; let version = app_handle.package_info().version.to_string(); + dbg!(&zh); + dbg!(&version); + if zh { SystemTrayMenu::new() .add_item(CustomMenuItem::new("open_window", "打开面板")) @@ -75,13 +78,14 @@ impl Tray { } pub fn update_part(app_handle: &AppHandle) -> Result<()> { - let global = Data::global(); - let clash = global.clash.lock(); - let mode = clash - .config - .get(&serde_yaml::Value::from("mode")) - .map(|val| val.as_str().unwrap_or("rule")) - .unwrap_or("rule"); + let mode = { + let clash = config::ClashN::global().config.lock(); + clash + .get("mode") + .map(|val| val.as_str().unwrap_or("rule")) + .unwrap_or("rule") + .to_owned() + }; let tray = app_handle.tray_handle(); @@ -90,7 +94,7 @@ impl Tray { let _ = tray.get_item("direct_mode").set_selected(mode == "direct"); let _ = tray.get_item("script_mode").set_selected(mode == "script"); - let verge = global.verge.lock(); + let verge = config::VergeN::global().config.lock(); let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false); let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false); @@ -105,12 +109,12 @@ impl Tray { SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() { mode @ ("rule_mode" | "global_mode" | "direct_mode" | "script_mode") => { let mode = &mode[0..mode.len() - 5]; - feat::change_clash_mode(mode); + feat::change_clash_mode(mode.into()); } "open_window" => resolve::create_window(app_handle), - "system_proxy" => feat::toggle_system_proxy(), - "tun_mode" => feat::toggle_tun_mode(), + "system_proxy" => log_err!(feat::toggle_system_proxy()), + "tun_mode" => log_err!(feat::toggle_tun_mode()), "restart_clash" => feat::restart_clash_core(), "restart_app" => api::process::restart(&app_handle.env()), "quit" => { @@ -124,7 +128,9 @@ impl Tray { SystemTrayEvent::LeftClick { .. } => { resolve::create_window(app_handle); } - _ => {} + e @ _ => { + dbg!("trya"); + } } } } diff --git a/src-tauri/src/config/field.rs b/src-tauri/src/enhance/field.rs similarity index 100% rename from src-tauri/src/config/field.rs rename to src-tauri/src/enhance/field.rs diff --git a/src-tauri/src/config/merge.rs b/src-tauri/src/enhance/merge.rs similarity index 100% rename from src-tauri/src/config/merge.rs rename to src-tauri/src/enhance/merge.rs diff --git a/src-tauri/src/enhance/mod.rs b/src-tauri/src/enhance/mod.rs new file mode 100644 index 0000000..0b00035 --- /dev/null +++ b/src-tauri/src/enhance/mod.rs @@ -0,0 +1,68 @@ +mod field; +mod merge; +mod script; +mod tun; + +pub(self) use self::field::*; +use self::merge::*; +use self::script::*; +use self::tun::*; +use crate::config::{ChainItem, ChainType}; +use serde_yaml::Mapping; +use std::collections::HashMap; +use std::collections::HashSet; + +type ResultLog = Vec<(String, String)>; + +pub fn enhance_config( + clash_config: Mapping, + profile_config: Mapping, + chain: Vec, + valid: Vec, + tun_mode: bool, +) -> (Mapping, Vec, HashMap) { + let mut config = profile_config; + let mut result_map = HashMap::new(); + let mut exists_keys = use_keys(&config); + + let valid = use_valid_fields(valid); + + chain.into_iter().for_each(|item| match item.data { + ChainType::Merge(merge) => { + exists_keys.extend(use_keys(&merge)); + config = use_merge(merge, config.to_owned()); + config = use_filter(config.to_owned(), &valid); + } + ChainType::Script(script) => { + let mut logs = vec![]; + + match use_script(script, config.to_owned()) { + Ok((res_config, res_logs)) => { + exists_keys.extend(use_keys(&res_config)); + config = use_filter(res_config, &valid); + logs.extend(res_logs); + } + Err(err) => logs.push(("exception".into(), err.to_string())), + } + + result_map.insert(item.uid, logs); + } + }); + + config = use_filter(config, &valid); + + for (key, value) in clash_config.into_iter() { + config.insert(key, value); + } + + let clash_fields = use_clash_fields(); + config = use_filter(config, &clash_fields); + config = use_tun(config, tun_mode); + config = use_sort(config); + + let mut exists_set = HashSet::new(); + exists_set.extend(exists_keys.into_iter().filter(|s| clash_fields.contains(s))); + exists_keys = exists_set.into_iter().collect(); + + (config, exists_keys, result_map) +} diff --git a/src-tauri/src/config/script.rs b/src-tauri/src/enhance/script.rs similarity index 100% rename from src-tauri/src/config/script.rs rename to src-tauri/src/enhance/script.rs diff --git a/src-tauri/src/config/tun.rs b/src-tauri/src/enhance/tun.rs similarity index 100% rename from src-tauri/src/config/tun.rs rename to src-tauri/src/enhance/tun.rs diff --git a/src-tauri/src/feat.rs b/src-tauri/src/feat.rs index 79fade1..39d6d98 100644 --- a/src-tauri/src/feat.rs +++ b/src-tauri/src/feat.rs @@ -1,102 +1,214 @@ +use crate::config::*; use crate::core::*; -use crate::data::*; -use crate::log_if_err; +use crate::log_err; +use anyhow::Result; +use serde_yaml::{Mapping, Value}; // 重启clash pub fn restart_clash_core() { - let core = Core::global(); - let mut service = core.service.lock(); - log_if_err!(service.restart()); - drop(service); - log_if_err!(core.activate()); + tauri::async_runtime::spawn(async { + CoreManager::global().run_core()?; + log_err!(handle_activate().await); + >::Ok(()) + }); } -// 切换模式 -pub fn change_clash_mode(mode: &str) { - let core = Core::global(); - log_if_err!(core.update_mode(mode)); +// 切换模式 rule/global/direct/script mode +pub fn change_clash_mode(mode: String) { + let mut mapping = Mapping::new(); + mapping.insert(Value::from("mode"), mode.clone().into()); + + tauri::async_runtime::spawn(async move { + match clash_api::patch_configs(&mapping).await { + Ok(_) => { + // 更新配置 + let mut clash = ClashN::global().config.lock(); + clash.insert(Value::from("mode"), mode.into()); + drop(clash); + + if let Ok(_) = ClashN::global().save_config() { + handle::Handle::refresh_clash(); + log_err!(handle::Handle::update_systray_part()); + } + } + Err(err) => { + log::error!(target: "app", "{err}"); + } + } + }); } // 切换系统代理 -pub fn toggle_system_proxy() { - let core = Core::global(); - let data = Data::global(); - - let verge = data.verge.lock(); - let enable = !verge.enable_system_proxy.clone().unwrap_or(false); - drop(verge); - - log_if_err!(core.patch_verge(Verge { - enable_system_proxy: Some(enable), - ..Verge::default() - })); - - let handle = core.handle.lock(); - let _ = handle.refresh_verge(); +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 enable_system_proxy() { - let core = Core::global(); - log_if_err!(core.patch_verge(Verge { +pub fn enable_system_proxy() -> Result<()> { + patch_verge(IVerge { enable_system_proxy: Some(true), - ..Verge::default() - })); - - let handle = core.handle.lock(); - let _ = handle.refresh_verge(); + ..IVerge::default() + })?; + handle::Handle::refresh_verge(); + Ok(()) } // 关闭系统代理 -pub fn disable_system_proxy() { - let core = Core::global(); - log_if_err!(core.patch_verge(Verge { +pub fn disable_system_proxy() -> Result<()> { + patch_verge(IVerge { enable_system_proxy: Some(false), - ..Verge::default() - })); - - let handle = core.handle.lock(); - let _ = handle.refresh_verge(); + ..IVerge::default() + })?; + handle::Handle::refresh_verge(); + Ok(()) } // 切换tun模式 -pub fn toggle_tun_mode() { - let core = Core::global(); - let data = Data::global(); +pub fn toggle_tun_mode() -> Result<()> { + let enable = { + let verge = VergeN::global().config.lock(); + verge.enable_tun_mode.clone().unwrap_or(false) + }; - let verge = data.verge.lock(); - let enable = !verge.enable_tun_mode.clone().unwrap_or(false); - drop(verge); - - log_if_err!(core.patch_verge(Verge { - enable_tun_mode: Some(enable), - ..Verge::default() - })); - - let handle = core.handle.lock(); - let _ = handle.refresh_verge(); + patch_verge(IVerge { + enable_tun_mode: Some(!enable), + ..IVerge::default() + })?; + handle::Handle::refresh_verge(); + Ok(()) } // 打开tun模式 -pub fn enable_tun_mode() { - let core = Core::global(); - log_if_err!(core.patch_verge(Verge { +pub fn enable_tun_mode() -> Result<()> { + patch_verge(IVerge { enable_tun_mode: Some(true), - ..Verge::default() - })); - - let handle = core.handle.lock(); - let _ = handle.refresh_verge(); + ..IVerge::default() + })?; + handle::Handle::refresh_verge(); + Ok(()) } // 关闭tun模式 -pub fn disable_tun_mode() { - let core = Core::global(); - log_if_err!(core.patch_verge(Verge { +pub fn disable_tun_mode() -> Result<()> { + patch_verge(IVerge { enable_tun_mode: Some(false), - ..Verge::default() - })); - - let handle = core.handle.lock(); - let _ = handle.refresh_verge(); + ..IVerge::default() + })?; + handle::Handle::refresh_verge(); + Ok(()) +} + +/// 修改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(); + + // 更新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); + } + + let old_info = ClashN::global().patch_info(ClashInfoN::from(&tmp_config))?; + + if let Err(err) = CoreManager::global().run_core() { + // 恢复旧值 + ClashN::global().patch_info(old_info)?; + return Err(err); + } + } + // 存好再搞 + 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 clash_mode { + log_err!(handle::Handle::update_systray_part()); + } + } + Err(err) => log::error!(target: "app", "{err}"), + } + }); + Ok(()) +} + +/// 修改verge的配置 +/// 一般都是一个个的修改 +pub fn patch_verge(patch: IVerge) -> Result<()> { + VergeN::global().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")] + {} + + if tun_mode.is_some() { + tauri::async_runtime::spawn(async { + log_err!(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 language.is_some() { + handle::Handle::update_systray()?; + } else if system_proxy.or(tun_mode).is_some() { + handle::Handle::update_systray_part()?; + } + + if patch.hotkeys.is_some() { + hotkey::Hotkey::global().update(patch.hotkeys.unwrap())?; + } + + Ok(()) +} + +/// 激活配置 +pub async fn handle_activate() -> Result<()> { + match CoreManager::global().activate_config().await { + Ok(_) => { + handle::Handle::refresh_clash(); + handle::Handle::notice_message("set_config::ok", "ok"); + Ok(()) + } + Err(err) => { + handle::Handle::notice_message("set_config::error", format!("{err}")); + Err(err) + } + } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 066939b..40010c6 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -6,12 +6,13 @@ mod cmds; mod config; mod core; -mod data; +// mod data; +mod enhance; mod feat; mod utils; use crate::utils::{init, resolve, server}; -use tauri::{api, Manager, SystemTray}; +use tauri::{api, CustomMenuItem, Manager, SystemTray, SystemTrayMenu}; fn main() -> std::io::Result<()> { // 单例检测 @@ -30,8 +31,8 @@ fn main() -> std::io::Result<()> { #[allow(unused_mut)] let mut builder = tauri::Builder::default() - .setup(|app| Ok(resolve::resolve_setup(app))) .system_tray(SystemTray::new()) + .setup(|app| Ok(resolve::resolve_setup(app))) .on_system_tray_event(core::tray::Tray::on_system_tray_event) .invoke_handler(tauri::generate_handler![ // common @@ -39,7 +40,7 @@ fn main() -> std::io::Result<()> { cmds::open_app_dir, cmds::open_logs_dir, cmds::open_web_url, - cmds::kill_sidecar, + // cmds::kill_sidecar, cmds::restart_sidecar, // clash cmds::get_clash_info, @@ -53,12 +54,12 @@ fn main() -> std::io::Result<()> { // verge cmds::get_verge_config, cmds::patch_verge_config, - cmds::update_hotkeys, + // cmds::update_hotkeys, // profile cmds::view_profile, cmds::patch_profile, cmds::create_profile, - cmds::import_profile, + // cmds::import_profile, cmds::update_profile, cmds::delete_profile, cmds::select_profile, @@ -96,22 +97,17 @@ fn main() -> std::io::Result<()> { ); } - #[allow(unused_mut)] - let mut app = builder + let app = builder .build(tauri::generate_context!()) .expect("error while running tauri application"); - #[cfg(target_os = "macos")] - app.set_activation_policy(tauri::ActivationPolicy::Accessory); + // let app_handle = app.app_handle(); + // ctrlc::set_handler(move || { + // resolve::resolve_reset(); + // app_handle.exit(0); + // }) + // .expect("error while exiting."); - let app_handle = app.app_handle(); - ctrlc::set_handler(move || { - resolve::resolve_reset(); - app_handle.exit(0); - }) - .expect("error while exiting."); - - #[allow(unused)] app.run(|app_handle, e| match e { tauri::RunEvent::ExitRequested { api, .. } => { api.prevent_exit(); diff --git a/src-tauri/src/utils/config.rs b/src-tauri/src/utils/config.rs index 17fe14b..efcdf6a 100644 --- a/src-tauri/src/utils/config.rs +++ b/src-tauri/src/utils/config.rs @@ -50,7 +50,7 @@ pub fn save_yaml(path: PathBuf, data: &T, prefix: Option<&str>) -> let data_str = serde_yaml::to_string(data)?; let yaml_str = match prefix { - Some(prefix) => format!("{prefix}{data_str}"), + Some(prefix) => format!("{prefix}\n{data_str}"), None => data_str, }; diff --git a/src-tauri/src/utils/dirs.rs b/src-tauri/src/utils/dirs.rs index b810180..68cb423 100644 --- a/src-tauri/src/utils/dirs.rs +++ b/src-tauri/src/utils/dirs.rs @@ -1,4 +1,3 @@ -use std::env::temp_dir; use std::path::PathBuf; use tauri::{ api::path::{home_dir, resource_dir}, @@ -13,7 +12,7 @@ static APP_DIR: &str = "clash-verge-dev"; static CLASH_CONFIG: &str = "config.yaml"; static VERGE_CONFIG: &str = "verge.yaml"; static PROFILE_YAML: &str = "profiles.yaml"; -static PROFILE_TEMP: &str = "clash-verge-runtime.yaml"; +static CLASH_RUNTIME_YAML: &str = "clash-verge-runtime.yaml"; static mut RESOURCE_DIR: Option = None; @@ -99,12 +98,8 @@ pub fn profiles_path() -> PathBuf { app_home_dir().join(PROFILE_YAML) } -pub fn profiles_temp_path() -> PathBuf { - #[cfg(not(feature = "debug-yml"))] - return temp_dir().join(PROFILE_TEMP); - - #[cfg(feature = "debug-yml")] - return app_home_dir().join(PROFILE_TEMP); +pub fn clash_runtime_yaml() -> PathBuf { + app_home_dir().join(CLASH_RUNTIME_YAML) } pub fn clash_pid_path() -> PathBuf { @@ -136,3 +131,11 @@ pub fn service_log_file() -> PathBuf { log_file } + +pub fn path_to_str(path: &PathBuf) -> anyhow::Result<&str> { + let path_str = path + .as_os_str() + .to_str() + .ok_or(anyhow::anyhow!("failed to get path from {:?}", path))?; + Ok(path_str) +} diff --git a/src-tauri/src/utils/help.rs b/src-tauri/src/utils/help.rs index b8ae558..0c5bb7e 100644 --- a/src-tauri/src/utils/help.rs +++ b/src-tauri/src/utils/help.rs @@ -69,28 +69,50 @@ pub fn open_file(path: PathBuf) -> Result<()> { Ok(()) } +#[macro_export] +macro_rules! error { + ($result: expr) => { + log::error!(target: "app", "{}", $result); + }; +} + #[macro_export] macro_rules! log_if_err { - ($result: expr) => { - if let Err(err) = $result { - log::error!(target: "app", "{err}"); - } - }; + ($result: expr) => { + if let Err(err) = $result { + log::error!(target: "app", "{err}"); + } + }; +} + +#[macro_export] +macro_rules! log_err { + ($result: expr) => { + if let Err(err) = $result { + log::error!(target: "app", "{err}"); + } + }; + + ($result: expr, $err_str: expr) => { + if let Err(_) = $result { + log::error!(target: "app", "{}", $err_str); + } + }; } /// wrap the anyhow error /// transform the error to String #[macro_export] macro_rules! wrap_err { - ($stat: expr) => { - match $stat { - Ok(a) => Ok(a), - Err(err) => { - log::error!(target: "app", "{}", err.to_string()); - Err(format!("{}", err.to_string())) - } - } - }; + ($stat: expr) => { + match $stat { + Ok(a) => Ok(a), + Err(err) => { + log::error!(target: "app", "{}", err.to_string()); + Err(format!("{}", err.to_string())) + } + } + }; } /// return the string literal error diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs index cadb64a..bb36352 100644 --- a/src-tauri/src/utils/init.rs +++ b/src-tauri/src/utils/init.rs @@ -87,19 +87,28 @@ pub fn init_resources(package_info: &PackageInfo) { } // copy the resource file - let mmdb_path = app_dir.join("Country.mmdb"); - let mmdb_tmpl = res_dir.join("Country.mmdb"); - if !mmdb_path.exists() && mmdb_tmpl.exists() { - let _ = fs::copy(mmdb_tmpl, mmdb_path); - } - - // copy the wintun.dll - #[cfg(target_os = "windows")] - { - let wintun_path = app_dir.join("wintun.dll"); - let wintun_tmpl = res_dir.join("wintun.dll"); - if !wintun_path.exists() && wintun_tmpl.exists() { - let _ = fs::copy(wintun_tmpl, wintun_path); + for file in ["Country.mmdb", "geoip.dat", "geosite.dat", "wintun.dll"].iter() { + let src_path = res_dir.join(file); + let target_path = app_dir.join(file); + if src_path.exists() { + let _ = fs::copy(src_path, target_path); } } + + // // copy the resource file + // let mmdb_path = app_dir.join("Country.mmdb"); + // let mmdb_tmpl = res_dir.join("Country.mmdb"); + // if !mmdb_path.exists() && mmdb_tmpl.exists() { + // let _ = fs::copy(mmdb_tmpl, mmdb_path); + // } + + // // copy the wintun.dll + // #[cfg(target_os = "windows")] + // { + // let wintun_path = app_dir.join("wintun.dll"); + // let wintun_tmpl = res_dir.join("wintun.dll"); + // if !wintun_path.exists() && wintun_tmpl.exists() { + // let _ = fs::copy(wintun_tmpl, wintun_path); + // } + // } } diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index 16ff18f..e6d8033 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -1,48 +1,48 @@ -use crate::{ - core::{tray, Core}, - data::Data, - utils::init, - utils::server, -}; +use crate::log_err; +use crate::{config::VergeN, core::*, utils::init, utils::server}; use tauri::{App, AppHandle, Manager}; /// handle something when start app -pub fn resolve_setup(app: &App) { - let _ = app - .tray_handle() - .set_menu(tray::Tray::tray_menu(&app.app_handle())); +pub fn resolve_setup(app: &mut App) { + #[cfg(target_os = "macos")] + app.set_activation_policy(tauri::ActivationPolicy::Accessory); + + handle::Handle::global().init(app.app_handle()); init::init_resources(app.package_info()); + // 启动核心 + log_err!(CoreManager::global().init()); + + // log_err!(app + // .tray_handle() + // .set_menu(tray::Tray::tray_menu(&app.app_handle()))); + + log_err!(tray::Tray::update_systray(&app.app_handle())); + + // setup a simple http server for singleton + server::embed_server(app.app_handle()); + let silent_start = { - let global = Data::global(); - let verge = global.verge.lock(); - let singleton = verge.app_singleton_port.clone(); - - // setup a simple http server for singleton - server::embed_server(&app.app_handle(), singleton); - - verge.enable_silent_start.clone().unwrap_or(false) + let verge = VergeN::global().config.lock(); + verge.enable_silent_start.clone() }; - - // core should be initialized after init_app fix #122 - let core = Core::global(); - core.init(app.app_handle()); - - if !silent_start { + if !silent_start.unwrap_or(false) { create_window(&app.app_handle()); } + + log_err!(sysopt::Sysopt::global().init_launch()); + log_err!(sysopt::Sysopt::global().init_sysproxy()); + + log_err!(handle::Handle::update_systray_part()); + log_err!(hotkey::Hotkey::global().init(app.app_handle())); + log_err!(timer::Timer::global().init()); } /// reset system proxy pub fn resolve_reset() { - let core = Core::global(); - let mut sysopt = core.sysopt.lock(); - crate::log_if_err!(sysopt.reset_sysproxy()); - drop(sysopt); - - let mut service = core.service.lock(); - crate::log_if_err!(service.stop()); + log_err!(sysopt::Sysopt::global().reset_sysproxy()); + log_err!(CoreManager::global().stop_core()); } /// create main window diff --git a/src-tauri/src/utils/server.rs b/src-tauri/src/utils/server.rs index c0162c6..1e68326 100644 --- a/src-tauri/src/utils/server.rs +++ b/src-tauri/src/utils/server.rs @@ -1,20 +1,14 @@ extern crate warp; use super::resolve; -use crate::data::Verge; +use crate::config::VergeN; use port_scanner::local_port_available; use tauri::AppHandle; use warp::Filter; -#[cfg(not(feature = "verge-dev"))] -const SERVER_PORT: u16 = 33331; -#[cfg(feature = "verge-dev")] -const SERVER_PORT: u16 = 11233; - /// check whether there is already exists pub fn check_singleton() -> Result<(), ()> { - let verge = Verge::new(); - let port = verge.app_singleton_port.unwrap_or(SERVER_PORT); + let port = VergeN::get_singleton_port(); if !local_port_available(port) { tauri::async_runtime::block_on(async { @@ -29,14 +23,14 @@ pub fn check_singleton() -> Result<(), ()> { /// The embed server only be used to implement singleton process /// maybe it can be used as pac server later -pub fn embed_server(app_handle: &AppHandle, port: Option) { +pub fn embed_server(app_handle: AppHandle) { let app_handle = app_handle.clone(); - let port = port.unwrap_or(SERVER_PORT); + let port = VergeN::get_singleton_port(); tauri::async_runtime::spawn(async move { let commands = warp::path!("commands" / "visible").map(move || { resolve::create_window(&app_handle); - return format!("ok"); + format!("ok") }); warp::serve(commands).bind(([127, 0, 0, 1], port)).await; diff --git a/src/services/cmds.ts b/src/services/cmds.ts index d99c60a..9ab45fe 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -136,9 +136,9 @@ export async function restartSidecar() { return invoke("restart_sidecar"); } -export async function killSidecar() { - return invoke("kill_sidecar"); -} +// export async function killSidecar() { +// return invoke("kill_sidecar"); +// } export async function openAppDir() { return invoke("open_app_dir").catch((err) => From d2852bb34a79ea4742058ef457db9d10eb8c10c3 Mon Sep 17 00:00:00 2001 From: GyDi Date: Mon, 14 Nov 2022 01:45:58 +0800 Subject: [PATCH 02/17] refactor: fix --- src-tauri/src/core/timer.rs | 17 +++++++++++------ src-tauri/src/core/tray.rs | 7 +------ src-tauri/src/main.rs | 4 ++-- src-tauri/src/utils/resolve.rs | 8 ++------ 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src-tauri/src/core/timer.rs b/src-tauri/src/core/timer.rs index 8859dcc..0c612b0 100644 --- a/src-tauri/src/core/timer.rs +++ b/src-tauri/src/core/timer.rs @@ -75,7 +75,7 @@ impl Timer { let diff_map = self.gen_diff(); let mut timer_map = self.timer_map.lock(); - let delay_timer = self.delay_timer.lock(); + let mut delay_timer = self.delay_timer.lock(); for (uid, diff) in diff_map.into_iter() { match diff { @@ -85,12 +85,12 @@ impl Timer { } DiffFlag::Add(tid, val) => { let _ = timer_map.insert(uid.clone(), (tid, val)); - crate::log_err!(self.add_task(uid, tid, val)); + crate::log_err!(self.add_task(&mut delay_timer, uid, tid, val)); } DiffFlag::Mod(tid, val) => { let _ = timer_map.insert(uid.clone(), (tid, val)); crate::log_err!(delay_timer.remove_task(tid)); - crate::log_err!(self.add_task(uid, tid, val)); + crate::log_err!(self.add_task(&mut delay_timer, uid, tid, val)); } } } @@ -153,7 +153,13 @@ impl Timer { } /// add a cron task - fn add_task(&self, uid: String, tid: TaskID, minutes: u64) -> Result<()> { + fn add_task( + &self, + delay_timer: &mut DelayTimer, + uid: String, + tid: TaskID, + minutes: u64, + ) -> Result<()> { let task = TaskBuilder::default() .set_task_id(tid) .set_maximum_parallel_runnable_num(1) @@ -162,8 +168,7 @@ impl Timer { .spawn_async_routine(move || Self::async_task(uid.to_owned())) .context("failed to create timer task")?; - self.delay_timer - .lock() + delay_timer .add_task(task) .context("failed to add timer task")?; diff --git a/src-tauri/src/core/tray.rs b/src-tauri/src/core/tray.rs index 20762cb..5a12f07 100644 --- a/src-tauri/src/core/tray.rs +++ b/src-tauri/src/core/tray.rs @@ -17,9 +17,6 @@ impl Tray { let version = app_handle.package_info().version.to_string(); - dbg!(&zh); - dbg!(&version); - if zh { SystemTrayMenu::new() .add_item(CustomMenuItem::new("open_window", "打开面板")) @@ -128,9 +125,7 @@ impl Tray { SystemTrayEvent::LeftClick { .. } => { resolve::create_window(app_handle); } - e @ _ => { - dbg!("trya"); - } + _ => {} } } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 40010c6..f0005e7 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -12,7 +12,7 @@ mod feat; mod utils; use crate::utils::{init, resolve, server}; -use tauri::{api, CustomMenuItem, Manager, SystemTray, SystemTrayMenu}; +use tauri::{api, Manager, SystemTray}; fn main() -> std::io::Result<()> { // 单例检测 @@ -59,7 +59,7 @@ fn main() -> std::io::Result<()> { cmds::view_profile, cmds::patch_profile, cmds::create_profile, - // cmds::import_profile, + cmds::import_profile, cmds::update_profile, cmds::delete_profile, cmds::select_profile, diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index e6d8033..9034fce 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -14,15 +14,11 @@ pub fn resolve_setup(app: &mut App) { // 启动核心 log_err!(CoreManager::global().init()); - // log_err!(app - // .tray_handle() - // .set_menu(tray::Tray::tray_menu(&app.app_handle()))); - - log_err!(tray::Tray::update_systray(&app.app_handle())); - // setup a simple http server for singleton server::embed_server(app.app_handle()); + log_err!(tray::Tray::update_systray(&app.app_handle())); + let silent_start = { let verge = VergeN::global().config.lock(); verge.enable_silent_start.clone() From 09965f1cc675aea6af74dc225398cf47bfa4c76b Mon Sep 17 00:00:00 2001 From: GyDi Date: Mon, 14 Nov 2022 19:31:22 +0800 Subject: [PATCH 03/17] feat: add draft --- src-tauri/src/config/draft.rs | 112 ++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src-tauri/src/config/draft.rs diff --git a/src-tauri/src/config/draft.rs b/src-tauri/src/config/draft.rs new file mode 100644 index 0000000..4e96462 --- /dev/null +++ b/src-tauri/src/config/draft.rs @@ -0,0 +1,112 @@ +use super::{IProfiles, IVerge}; +use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; +use serde_yaml::Mapping; +use std::sync::Arc; + +#[derive(Debug)] +pub struct Draft { + inner: Arc)>>, +} + +macro_rules! draft_define { + ($id: ident) => { + impl Draft<$id> { + pub fn data(&self) -> MappedMutexGuard<$id> { + MutexGuard::map(self.inner.lock(), |guard| &mut guard.0) + } + + pub fn draft(&self) -> MappedMutexGuard<$id> { + MutexGuard::map(self.inner.lock(), |mut inner| { + if inner.1.is_none() { + inner.1 = Some(inner.0.clone()); + } + + inner.1.as_mut().unwrap() + }) + } + + pub fn apply(&self) -> Option<$id> { + let mut inner = self.inner.lock(); + + match inner.1.take() { + Some(draft) => { + let old_value = inner.0.to_owned(); + inner.0 = draft.to_owned(); + Some(old_value) + } + None => None, + } + } + + pub fn discard(&self) -> Option<$id> { + let mut inner = self.inner.lock(); + inner.1.take() + } + } + + impl From<$id> for Draft<$id> { + fn from(data: $id) -> Self { + Draft { + inner: Arc::new(Mutex::new((data, None))), + } + } + } + }; +} + +draft_define!(IVerge); +draft_define!(Mapping); +draft_define!(IProfiles); + +#[test] +fn test_draft() { + let verge = IVerge { + enable_auto_launch: Some(true), + enable_tun_mode: Some(false), + ..IVerge::default() + }; + + let draft = Draft::from(verge); + + assert_eq!(draft.data().enable_auto_launch, Some(true)); + assert_eq!(draft.data().enable_tun_mode, Some(false)); + + assert_eq!(draft.draft().enable_auto_launch, Some(true)); + assert_eq!(draft.draft().enable_tun_mode, Some(false)); + + let mut d = draft.draft(); + d.enable_auto_launch = Some(false); + d.enable_tun_mode = Some(true); + drop(d); + + assert_eq!(draft.data().enable_auto_launch, Some(true)); + assert_eq!(draft.data().enable_tun_mode, Some(false)); + + assert_eq!(draft.draft().enable_auto_launch, Some(false)); + assert_eq!(draft.draft().enable_tun_mode, Some(true)); + + assert!(draft.apply().is_some()); + assert!(draft.apply().is_none()); + + assert_eq!(draft.data().enable_auto_launch, Some(false)); + assert_eq!(draft.data().enable_tun_mode, Some(true)); + + assert_eq!(draft.draft().enable_auto_launch, Some(false)); + assert_eq!(draft.draft().enable_tun_mode, Some(true)); + + let mut d = draft.draft(); + d.enable_auto_launch = Some(true); + drop(d); + + assert_eq!(draft.data().enable_auto_launch, Some(false)); + + assert_eq!(draft.draft().enable_auto_launch, Some(true)); + + assert!(draft.discard().is_some()); + + assert_eq!(draft.data().enable_auto_launch, Some(false)); + + assert!(draft.discard().is_none()); + + assert_eq!(draft.draft().enable_auto_launch, Some(false)); +} From dc941575fee2cd0524e1e5e65c85eb079949afdd Mon Sep 17 00:00:00 2001 From: GyDi Date: Mon, 14 Nov 2022 22:50:47 +0800 Subject: [PATCH 04/17] refactor: fix --- src-tauri/src/cmds.rs | 51 +++++++++++------------------------ src-tauri/src/config/clash.rs | 13 +-------- src-tauri/src/config/verge.rs | 1 + src-tauri/src/core/core.rs | 22 ++++++++------- 4 files changed, 30 insertions(+), 57 deletions(-) diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index f622bb7..39e9bf4 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -44,49 +44,32 @@ pub async fn update_profile(index: String, option: Option) -> CmdResu } #[tauri::command] -pub fn select_profile(index: String) -> CmdResult { - let mut profiles = ProfilesN::global().config.lock(); - wrap_err!(profiles.put_current(index))?; - drop(profiles); - - wrap_err!(tauri::async_runtime::block_on(async { - CoreManager::global().activate_config().await - })) +pub async fn select_profile(index: String) -> CmdResult { + wrap_err!({ ProfilesN::global().config.lock().put_current(index) })?; + wrap_err!(CoreManager::global().activate_config().await) } /// change the profile chain #[tauri::command] -pub fn change_profile_chain(chain: Option>) -> CmdResult { - let mut profiles = ProfilesN::global().config.lock(); - wrap_err!(profiles.put_chain(chain))?; - drop(profiles); - - wrap_err!(tauri::async_runtime::block_on(async { - CoreManager::global().activate_config().await - })) +pub async fn change_profile_chain(chain: Option>) -> CmdResult { + wrap_err!({ ProfilesN::global().config.lock().put_chain(chain) })?; + wrap_err!(CoreManager::global().activate_config().await) } #[tauri::command] -pub fn change_profile_valid(valid: Option>) -> CmdResult { - let mut profiles = ProfilesN::global().config.lock(); - wrap_err!(profiles.put_valid(valid))?; - drop(profiles); - - wrap_err!(tauri::async_runtime::block_on(async { - CoreManager::global().activate_config().await - })) +pub async fn change_profile_valid(valid: Option>) -> CmdResult { + wrap_err!({ ProfilesN::global().config.lock().put_valid(valid) })?; + wrap_err!(CoreManager::global().activate_config().await) } #[tauri::command] -pub fn delete_profile(index: String) -> CmdResult { - let mut profiles = ProfilesN::global().config.lock(); - if wrap_err!(profiles.delete_item(index))? { - drop(profiles); +pub async fn delete_profile(index: String) -> CmdResult { + let should_update = { wrap_err!(ProfilesN::global().config.lock().delete_item(index))? }; - wrap_err!(tauri::async_runtime::block_on(async { - CoreManager::global().activate_config().await - }))?; + if should_update { + wrap_err!(CoreManager::global().activate_config().await)?; } + Ok(()) } @@ -185,10 +168,8 @@ pub fn patch_verge_config(payload: IVerge) -> CmdResult { } #[tauri::command] -pub fn change_clash_core(clash_core: Option) -> CmdResult { - wrap_err!(tauri::async_runtime::block_on(async { - CoreManager::global().change_core(clash_core).await - })) +pub async fn change_clash_core(clash_core: Option) -> CmdResult { + wrap_err!(CoreManager::global().change_core(clash_core).await) } /// restart the sidecar diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index e498a34..a00f3c9 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -58,22 +58,11 @@ impl ClashN { pub fn patch_config(&self, patch: Mapping) -> Result<()> { let mut config = self.config.lock(); - 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() { config.insert(key, value); } - if change_info { - let mut info = self.info.lock(); - *info = ClashInfoN::from(&*config); - } + drop(config); self.save_config() } diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index 97b5f10..47593b7 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -67,6 +67,7 @@ impl VergeN { patch!(auto_close_connection); patch!(default_latency_test); + drop(config); self.save_file() } diff --git a/src-tauri/src/core/core.rs b/src-tauri/src/core/core.rs index 92296e7..4c587aa 100644 --- a/src-tauri/src/core/core.rs +++ b/src-tauri/src/core/core.rs @@ -79,12 +79,12 @@ impl CoreManager { let clash_core = { self.clash_core.lock().clone() }; let output = Command::new_sidecar(clash_core)? - .args(["-t", config_path]) + .args(["-t", "-f", config_path]) .output()?; if !output.status.success() { - Logger::global().set_log(output.stderr.clone()); - bail!("{}", output.stderr); // 过滤掉终端颜色值 + Logger::global().set_log(output.stdout.clone()); + bail!("{}", output.stdout); // 过滤掉终端颜色值 } Ok(()) @@ -177,17 +177,19 @@ impl CoreManager { // 清掉旧日志 Logger::global().clear_log(); - let mut self_core = self.clash_core.lock(); - let old_core = self_core.clone(); // 保存一下旧值 - *self_core = clash_core.clone(); - drop(self_core); + let old_core = { + let mut self_core = self.clash_core.lock(); + let old_core = self_core.to_owned(); // 保存一下旧值 + *self_core = clash_core.clone(); + old_core + }; match self.run_core() { Ok(_) => { // 更新到配置文件 - let mut verge = VergeN::global().config.lock(); - verge.clash_core = Some(clash_core); - drop(verge); + { + VergeN::global().config.lock().clash_core = Some(clash_core); + } let _ = VergeN::global().save_file(); From c8e6f3a627e8dcc421d8c586e143056f3816a417 Mon Sep 17 00:00:00 2001 From: GyDi Date: Mon, 14 Nov 2022 23:07:51 +0800 Subject: [PATCH 05/17] refactor: rm update item block_on --- src-tauri/src/config/profiles.rs | 46 ++++++++++++++------------------ 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index fac9960..ad50f63 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -27,41 +27,35 @@ impl ProfilesN { /// 更新单个配置 pub async fn update_item(&self, uid: String, option: Option) -> Result<()> { - let (url, opt) = { + 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 let Some(typ) = item.itype.as_ref() { - // maybe only valid for `local` profile - if *typ != "remote" { - // reactivate the config - if Some(uid) == profiles.get_current() { - drop(profiles); - tauri::async_runtime::block_on(async { - CoreManager::global().activate_config().await - })?; - } - return Ok(()); - } - } - if item.url.is_none() { + 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())) } - (item.url.clone().unwrap(), item.option.clone()) }; - let merged_opt = PrfOption::merge(opt, option); - let item = PrfItem::from_url(&url, None, None, merged_opt).await?; + 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)?; + let mut profiles = self.config.lock(); + profiles.update_item(uid.clone(), item)?; - // reactivate the profile - if Some(uid) == profiles.get_current() { - drop(profiles); - tauri::async_runtime::block_on(async { - CoreManager::global().activate_config().await - })?; + Some(uid) == profiles.get_current() + } + None => true, + }; + + if should_update { + CoreManager::global().activate_config().await?; } Ok(()) From ee68d80d0a876dd9216b8248d8668e2bdfe28a3b Mon Sep 17 00:00:00 2001 From: GyDi Date: Tue, 15 Nov 2022 01:33:50 +0800 Subject: [PATCH 06/17] refactor: wip --- src-tauri/src/config/config.rs | 20 ++++++++ src-tauri/src/config/draft.rs | 2 +- src-tauri/src/config/mod.rs | 4 ++ src-tauri/src/config/verge.rs | 91 +++++++++++++++++++--------------- 4 files changed, 77 insertions(+), 40 deletions(-) create mode 100644 src-tauri/src/config/config.rs diff --git a/src-tauri/src/config/config.rs b/src-tauri/src/config/config.rs new file mode 100644 index 0000000..324a62f --- /dev/null +++ b/src-tauri/src/config/config.rs @@ -0,0 +1,20 @@ +use super::{Draft, IVerge}; +use once_cell::sync::OnceCell; + +pub struct Config { + verge_config: Draft, +} + +impl Config { + pub fn global() -> &'static Config { + static CONFIG: OnceCell = OnceCell::new(); + + CONFIG.get_or_init(|| Config { + verge_config: Draft::from(IVerge::new()), + }) + } + + pub fn verge() -> Draft { + Self::global().verge_config.clone() + } +} diff --git a/src-tauri/src/config/draft.rs b/src-tauri/src/config/draft.rs index 4e96462..01f7bec 100644 --- a/src-tauri/src/config/draft.rs +++ b/src-tauri/src/config/draft.rs @@ -3,7 +3,7 @@ use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; use serde_yaml::Mapping; use std::sync::Arc; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Draft { inner: Arc)>>, } diff --git a/src-tauri/src/config/mod.rs b/src-tauri/src/config/mod.rs index 9aa880f..554a4d4 100644 --- a/src-tauri/src/config/mod.rs +++ b/src-tauri/src/config/mod.rs @@ -1,9 +1,13 @@ mod clash; +mod config; +mod draft; mod prfitem; mod profiles; mod verge; pub use self::clash::*; +pub use self::config::*; +pub use self::draft::*; pub use self::prfitem::*; pub use self::profiles::*; pub use self::verge::*; diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index 47593b7..9a2bc45 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -23,51 +23,15 @@ impl VergeN { /// Save IVerge App Config pub fn save_file(&self) -> Result<()> { - let config = self.config.lock(); - - config::save_yaml( - dirs::verge_path(), - &*config, - Some("# The Config for Clash IVerge App\n\n"), - ) + self.config.lock().save_file() } /// patch verge config /// only save to file pub fn patch_config(&self, patch: IVerge) -> Result<()> { - let mut config = self.config.lock(); - - macro_rules! patch { - ($key: tt) => { - if patch.$key.is_some() { - config.$key = patch.$key; - } - }; + { + self.config.lock().patch_config(patch); } - - 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); - - drop(config); self.save_file() } @@ -165,3 +129,52 @@ pub struct IVergeTheme { pub font_family: Option, pub css_injection: Option, } + +impl IVerge { + pub fn new() -> Self { + config::read_yaml::(dirs::verge_path()) + } + + /// Save IVerge App Config + pub fn save_file(&self) -> Result<()> { + config::save_yaml( + dirs::verge_path(), + &self, + Some("# The Config for Clash IVerge App\n\n"), + ) + } + + /// patch verge config + /// only save to file + pub fn patch_config(&mut self, patch: IVerge) { + 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); + } +} From abdbf158d1700a77dbc2ee1a0bb18dcc2b0edc75 Mon Sep 17 00:00:00 2001 From: GyDi Date: Wed, 16 Nov 2022 01:26:41 +0800 Subject: [PATCH 07/17] refactor: wip --- src-tauri/src/config/clash.rs | 98 +++++++++++++++++++++++++++++++- src-tauri/src/config/config.rs | 28 ++++++++- src-tauri/src/config/draft.rs | 17 +++++- src-tauri/src/config/profiles.rs | 5 ++ src-tauri/src/config/verge.rs | 4 +- src-tauri/src/core/core.rs | 58 ++++++++----------- src-tauri/src/core/hotkey.rs | 6 +- src-tauri/src/core/sysopt.rs | 98 +++++++++++++++++++------------- src-tauri/src/core/tray.rs | 15 +++-- src-tauri/src/feat.rs | 6 ++ src-tauri/src/utils/tmpl.rs | 2 +- 11 files changed, 247 insertions(+), 90 deletions(-) diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index a00f3c9..e59b307 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -4,9 +4,37 @@ use once_cell::sync::OnceCell; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use serde_yaml::{Mapping, Value}; -use std::sync::Arc; +use std::{net::SocketAddr, sync::Arc}; + +#[derive(Default, Debug, Clone)] +pub struct IClashTemp(pub Mapping); + +impl IClashTemp { + pub fn new() -> Self { + Self(config::read_merge_mapping(dirs::clash_path())) + } + + pub fn patch_config(&mut self, patch: Mapping) { + for (key, value) in patch.into_iter() { + self.0.insert(key, value); + } + } + + pub fn save_config(&self) -> Result<()> { + config::save_yaml( + dirs::clash_path(), + &self.0, + Some("# Default Config For ClashN Core\n\n"), + ) + } + + pub fn get_info(&self) -> Result { + Ok(ClashInfoN::from(&self.0)) + } +} #[derive(Debug)] +#[deprecated] pub struct ClashN { /// maintain the clash config pub config: Arc>, @@ -167,3 +195,71 @@ impl ClashInfoN { } } } + +#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "kebab-case")] +pub struct IClash { + pub mixed_port: Option, + pub allow_lan: Option, + pub log_level: Option, + pub ipv6: Option, + pub mode: Option, + pub external_controller: Option, + pub secret: Option, + pub dns: Option, + pub tun: Option, + pub interface_name: Option, +} + +#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "kebab-case")] +pub struct IClashTUN { + pub enable: Option, + pub stack: Option, + pub auto_route: Option, + pub auto_detect_interface: Option, + pub dns_hijack: Option>, +} + +#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "kebab-case")] +pub struct IClashDNS { + pub enable: Option, + pub listen: Option, + pub default_nameserver: Option>, + pub enhanced_mode: Option, + pub fake_ip_range: Option, + pub use_hosts: Option, + pub fake_ip_filter: Option>, + pub nameserver: Option>, + pub fallback: Option>, + pub fallback_filter: Option, + pub nameserver_policy: Option>, +} + +#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "kebab-case")] +pub struct IClashFallbackFilter { + pub geoip: Option, + pub geoip_code: Option, + pub ipcidr: Option>, + pub domain: Option>, +} + +#[test] +fn test() { + let socket = SocketAddr::new("127.0.0.1".parse().unwrap(), 9090); + + let s = "[::]:8080".parse::(); + + dbg!(s); + + // match "::8080".parse::() { + // 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 324a62f..9ca78df 100644 --- a/src-tauri/src/config/config.rs +++ b/src-tauri/src/config/config.rs @@ -1,8 +1,12 @@ -use super::{Draft, IVerge}; +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, verge_config: Draft, + profiles_config: Draft, } impl Config { @@ -10,11 +14,33 @@ impl Config { static CONFIG: OnceCell = OnceCell::new(); CONFIG.get_or_init(|| Config { + clash_config: Draft::from(IClashTemp::new()), verge_config: Draft::from(IVerge::new()), + profiles_config: Draft::from(IProfiles::new()), }) } + // 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 { + Self::global().clash_config.clone() + } + pub fn verge() -> Draft { Self::global().verge_config.clone() } + + pub fn profiles() -> Draft { + Self::global().profiles_config.clone() + } } diff --git a/src-tauri/src/config/draft.rs b/src-tauri/src/config/draft.rs index 01f7bec..64bc5c8 100644 --- a/src-tauri/src/config/draft.rs +++ b/src-tauri/src/config/draft.rs @@ -1,4 +1,4 @@ -use super::{IProfiles, IVerge}; +use super::{IClash, IClashTemp, IProfiles, IVerge}; use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; use serde_yaml::Mapping; use std::sync::Arc; @@ -15,6 +15,16 @@ macro_rules! draft_define { MutexGuard::map(self.inner.lock(), |guard| &mut guard.0) } + pub fn latest(&self) -> MappedMutexGuard<$id> { + MutexGuard::map(self.inner.lock(), |inner| { + if inner.1.is_none() { + &mut inner.0 + } else { + inner.1.as_mut().unwrap() + } + }) + } + pub fn draft(&self) -> MappedMutexGuard<$id> { MutexGuard::map(self.inner.lock(), |mut inner| { if inner.1.is_none() { @@ -54,6 +64,8 @@ macro_rules! draft_define { }; } +draft_define!(IClash); +draft_define!(IClashTemp); draft_define!(IVerge); draft_define!(Mapping); draft_define!(IProfiles); @@ -85,6 +97,9 @@ fn test_draft() { assert_eq!(draft.draft().enable_auto_launch, Some(false)); assert_eq!(draft.draft().enable_tun_mode, Some(true)); + assert_eq!(draft.latest().enable_auto_launch, Some(false)); + assert_eq!(draft.latest().enable_tun_mode, Some(true)); + assert!(draft.apply().is_some()); assert!(draft.apply().is_none()); diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index ad50f63..3779c51 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -12,6 +12,7 @@ use std::collections::HashMap; use std::sync::Arc; use std::{fs, io::Write}; +#[deprecated] pub struct ProfilesN { pub config: Arc>, } @@ -87,6 +88,10 @@ macro_rules! patch { } impl IProfiles { + pub fn new() -> Self { + Self::read_file() + } + /// read the config from the file pub fn read_file() -> Self { let mut profiles = config::read_yaml::(dirs::profiles_path()); diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index 9a2bc45..7df68f9 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -5,6 +5,7 @@ use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use std::sync::Arc; +#[deprecated] pub struct VergeN { pub config: Arc>, } @@ -51,8 +52,7 @@ impl VergeN { /// ### `verge.yaml` schema #[derive(Default, Debug, Clone, Deserialize, Serialize)] pub struct IVerge { - /// app listening port - /// for app singleton + /// app listening port for app singleton pub app_singleton_port: Option, // i18n diff --git a/src-tauri/src/core/core.rs b/src-tauri/src/core/core.rs index 4c587aa..30fe6cc 100644 --- a/src-tauri/src/core/core.rs +++ b/src-tauri/src/core/core.rs @@ -1,7 +1,7 @@ use super::{clash_api, logger::Logger}; use crate::{ config::*, - enhance, + enhance, log_err, utils::{self, dirs}, }; use anyhow::{bail, Context, Result}; @@ -14,8 +14,6 @@ use tokio::time::sleep; #[derive(Debug)] pub struct CoreManager { - clash_core: Arc>, - sidecar: Arc>>, #[allow(unused)] @@ -29,7 +27,6 @@ impl CoreManager { static CORE_MANAGER: OnceCell = OnceCell::new(); CORE_MANAGER.get_or_init(|| CoreManager { - clash_core: Arc::new(Mutex::new("clash".into())), sidecar: Arc::new(Mutex::new(None)), runtime_config: Arc::new(Mutex::new(RuntimeResult::default())), use_service_mode: Arc::new(Mutex::new(false)), @@ -50,14 +47,14 @@ impl CoreManager { } } - // 使用配置的核心 - let verge = VergeN::global().config.lock(); - if let Some(verge_core) = verge.clash_core.as_ref() { - if verge_core == "clash" || verge_core == "clash-meta" { - let mut clash_core = self.clash_core.lock(); - *clash_core = verge_core.clone(); - } - } + // // 使用配置的核心 + // let verge_core = { Config::verge().clash_core.clone() }; + // if let Some(verge_core) = verge_core { + // if verge_core == "clash" || verge_core == "clash-meta" { + // let mut clash_core = self.clash_core.lock(); + // *clash_core = verge_core; + // } + // } // 启动clash self.run_core()?; @@ -76,7 +73,8 @@ impl CoreManager { let config_path = dirs::clash_runtime_yaml(); let config_path = dirs::path_to_str(&config_path)?; - let clash_core = { self.clash_core.lock().clone() }; + let clash_core = { Config::verge().latest().clash_core.clone() }; + let clash_core = clash_core.unwrap_or("clash".into()); let output = Command::new_sidecar(clash_core)? .args(["-t", "-f", config_path]) @@ -104,7 +102,8 @@ impl CoreManager { let app_dir = dirs::app_home_dir(); let app_dir = dirs::path_to_str(&app_dir)?; - let clash_core = { self.clash_core.lock().clone() }; + let clash_core = { Config::verge().latest().clash_core.clone() }; + let clash_core = clash_core.unwrap_or("clash".into()); // fix #212 let args = match clash_core.as_str() { @@ -177,30 +176,23 @@ impl CoreManager { // 清掉旧日志 Logger::global().clear_log(); - let old_core = { - let mut self_core = self.clash_core.lock(); - let old_core = self_core.to_owned(); // 保存一下旧值 - *self_core = clash_core.clone(); - old_core - }; + { + Config::verge().draft().clash_core = Some(clash_core); + } match self.run_core() { Ok(_) => { - // 更新到配置文件 - { - VergeN::global().config.lock().clash_core = Some(clash_core); - } - - let _ = VergeN::global().save_file(); + log_err!({ + Config::verge().apply(); + Config::verge().latest().save_file() + }); sleep(Duration::from_millis(100)).await; // 等一会儿再更新配置 self.activate_config().await?; Ok(()) } Err(err) => { - // 恢复旧的值 - let mut self_core = self.clash_core.lock(); - *self_core = old_core; + Config::verge().discard(); Err(err) } } @@ -215,15 +207,15 @@ impl CoreManager { /// 激活一个配置 pub async fn activate_config(&self) -> Result<()> { - let clash_config = { ClashN::global().config.lock().clone() }; + let clash_config = { Config::clash().latest().clone() }; - let tun_mode = { VergeN::global().config.lock().enable_tun_mode.clone() }; + let tun_mode = { Config::verge().latest().enable_tun_mode.clone() }; let tun_mode = tun_mode.unwrap_or(false); - let pa = { ProfilesN::global().config.lock().gen_activate()? }; + let pa = { Config::profiles().latest().gen_activate()? }; let (config, exists_keys, logs) = - enhance::enhance_config(clash_config, pa.current, pa.chain, pa.valid, tun_mode); + enhance::enhance_config(clash_config.0, pa.current, pa.chain, pa.valid, tun_mode); // 保存到文件中 let runtime_path = dirs::clash_runtime_yaml(); diff --git a/src-tauri/src/core/hotkey.rs b/src-tauri/src/core/hotkey.rs index 48c4482..cd844db 100644 --- a/src-tauri/src/core/hotkey.rs +++ b/src-tauri/src/core/hotkey.rs @@ -1,4 +1,4 @@ -use crate::{config, feat, log_err}; +use crate::{config::Config, feat, log_err}; use anyhow::{bail, Result}; use once_cell::sync::OnceCell; use parking_lot::Mutex; @@ -24,9 +24,9 @@ impl Hotkey { pub fn init(&self, app_handle: AppHandle) -> Result<()> { *self.app_handle.lock() = Some(app_handle); - let verge = config::VergeN::global().config.lock(); + let verge = Config::verge(); - if let Some(hotkeys) = verge.hotkeys.as_ref() { + if let Some(hotkeys) = verge.latest().hotkeys.as_ref() { for hotkey in hotkeys.iter() { let mut iter = hotkey.split(','); let func = iter.next(); diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index 7850474..f9231e0 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -1,4 +1,4 @@ -use crate::{config, log_err}; +use crate::{config::Config, log_err}; use anyhow::{anyhow, bail, Result}; use auto_launch::{AutoLaunch, AutoLaunchBuilder}; use once_cell::sync::OnceCell; @@ -43,7 +43,7 @@ impl Sysopt { /// init the sysproxy pub fn init_sysproxy(&self) -> Result<()> { - let port = { config::ClashN::global().info.lock().port.clone() }; + let port = { Config::clash().latest().get_info()?.port }; if port.is_none() { bail!("clash port is none"); @@ -51,16 +51,20 @@ impl Sysopt { let port = port.unwrap().parse::()?; - let verge = config::VergeN::global().config.lock(); - let enable = verge.enable_system_proxy.clone().unwrap_or(false); - let bypass = verge.system_proxy_bypass.clone(); - let bypass = bypass.unwrap_or(DEFAULT_BYPASS.into()); + let (enable, bypass) = { + let verge = Config::verge(); + let verge = verge.latest(); + ( + verge.enable_system_proxy.clone().unwrap_or(false), + verge.system_proxy_bypass.clone(), + ) + }; let current = Sysproxy { enable, host: String::from("127.0.0.1"), port, - bypass, + bypass: bypass.unwrap_or(DEFAULT_BYPASS.into()), }; if enable { @@ -87,16 +91,18 @@ impl Sysopt { return self.init_sysproxy(); } - let verge = config::VergeN::global().config.lock(); - - let enable = verge.enable_system_proxy.clone().unwrap_or(false); - let bypass = verge.system_proxy_bypass.clone(); - let bypass = bypass.unwrap_or(DEFAULT_BYPASS.into()); - + let (enable, bypass) = { + let verge = Config::verge(); + let verge = verge.latest(); + ( + verge.enable_system_proxy.clone().unwrap_or(false), + verge.system_proxy_bypass.clone(), + ) + }; let mut sysproxy = cur_sysproxy.take().unwrap(); sysproxy.enable = enable; - sysproxy.bypass = bypass; + sysproxy.bypass = bypass.unwrap_or(DEFAULT_BYPASS.into()); sysproxy.set_system_proxy()?; *cur_sysproxy = Some(sysproxy); @@ -138,8 +144,13 @@ impl Sysopt { /// init the auto launch pub fn init_launch(&self) -> Result<()> { - let verge = config::VergeN::global().config.lock(); - let enable = verge.enable_auto_launch.clone().unwrap_or(false); + let enable = { + Config::verge() + .latest() + .enable_auto_launch + .clone() + .unwrap_or(false) + }; let app_exe = current_exe()?; let app_exe = dunce::canonicalize(app_exe)?; @@ -202,10 +213,13 @@ impl Sysopt { drop(auto_launch); return self.init_launch(); } - - let verge = config::VergeN::global().config.lock(); - let enable = verge.enable_auto_launch.clone().unwrap_or(false); - + let enable = { + Config::verge() + .latest() + .enable_auto_launch + .clone() + .unwrap_or(false) + }; let auto_launch = auto_launch.as_ref().unwrap(); match enable { @@ -238,13 +252,16 @@ impl Sysopt { loop { sleep(Duration::from_secs(wait_secs)).await; - let verge = config::VergeN::global().config.lock(); - - let enable = verge.enable_system_proxy.clone().unwrap_or(false); - let guard = verge.enable_proxy_guard.clone().unwrap_or(false); - let guard_duration = verge.proxy_guard_duration.clone().unwrap_or(10); - let bypass = verge.system_proxy_bypass.clone(); - drop(verge); + let (enable, guard, guard_duration, bypass) = { + let verge = Config::verge(); + let verge = verge.latest(); + ( + verge.enable_system_proxy.clone().unwrap_or(false), + verge.enable_proxy_guard.clone().unwrap_or(false), + verge.proxy_guard_duration.clone().unwrap_or(10), + verge.system_proxy_bypass.clone(), + ) + }; // stop loop if !enable || !guard { @@ -256,20 +273,21 @@ impl Sysopt { log::debug!(target: "app", "try to guard the system proxy"); - let port = { config::ClashN::global().info.lock().port.clone() }; - match port.unwrap_or("".into()).parse::() { - Ok(port) => { - let sysproxy = Sysproxy { - enable: true, - host: "127.0.0.1".into(), - port, - bypass: bypass.unwrap_or(DEFAULT_BYPASS.into()), - }; + if let Ok(info) = { Config::clash().latest().get_info() } { + match info.port.unwrap_or("".into()).parse::() { + Ok(port) => { + let sysproxy = Sysproxy { + enable: true, + host: "127.0.0.1".into(), + port, + bypass: bypass.unwrap_or(DEFAULT_BYPASS.into()), + }; - log_err!(sysproxy.set_system_proxy()); - } - Err(_) => { - log::error!(target: "app", "failed to parse clash port in guard proxy") + log_err!(sysproxy.set_system_proxy()); + } + Err(_) => { + log::error!(target: "app", "failed to parse clash port in guard proxy") + } } } } diff --git a/src-tauri/src/core/tray.rs b/src-tauri/src/core/tray.rs index 5a12f07..36fa652 100644 --- a/src-tauri/src/core/tray.rs +++ b/src-tauri/src/core/tray.rs @@ -1,5 +1,5 @@ use crate::log_err; -use crate::{config, feat, utils::resolve}; +use crate::{config::Config, feat, utils::resolve}; use anyhow::Result; use tauri::{ api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, @@ -10,10 +10,7 @@ pub struct Tray {} impl Tray { pub fn tray_menu(app_handle: &AppHandle) -> SystemTrayMenu { - let zh = { - let verge = config::VergeN::global().config.lock(); - verge.language == Some("zh".into()) - }; + let zh = { Config::verge().latest().language == Some("zh".into()) }; let version = app_handle.package_info().version.to_string(); @@ -76,8 +73,9 @@ impl Tray { pub fn update_part(app_handle: &AppHandle) -> Result<()> { let mode = { - let clash = config::ClashN::global().config.lock(); - clash + Config::clash() + .latest() + .0 .get("mode") .map(|val| val.as_str().unwrap_or("rule")) .unwrap_or("rule") @@ -91,7 +89,8 @@ impl Tray { let _ = tray.get_item("direct_mode").set_selected(mode == "direct"); let _ = tray.get_item("script_mode").set_selected(mode == "script"); - let verge = config::VergeN::global().config.lock(); + let verge = Config::verge(); + let verge = verge.latest(); let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false); let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false); diff --git a/src-tauri/src/feat.rs b/src-tauri/src/feat.rs index 39d6d98..d94fe7d 100644 --- a/src-tauri/src/feat.rs +++ b/src-tauri/src/feat.rs @@ -212,3 +212,9 @@ pub async fn handle_activate() -> Result<()> { } } } + +/// 更新某个profile +/// 如果更新当前配置就激活配置 +pub async fn update_profile(uid: String, option: Option) -> Result<()> { + Ok(()) +} diff --git a/src-tauri/src/utils/tmpl.rs b/src-tauri/src/utils/tmpl.rs index dc311f6..8d20a2e 100644 --- a/src-tauri/src/utils/tmpl.rs +++ b/src-tauri/src/utils/tmpl.rs @@ -19,7 +19,7 @@ items: ~ "; /// template for `verge.yaml` -pub const VERGE_CONFIG: &[u8] = b"# Defaulf Config For Clash Verge +pub const VERGE_CONFIG: &[u8] = b"# Default Config For Clash Verge language: en theme_mode: light From 63b474a32c7338d67eaf56223ac532b90ecc620b Mon Sep 17 00:00:00 2001 From: GyDi Date: Thu, 17 Nov 2022 17:07:13 +0800 Subject: [PATCH 08/17] refactor: wip --- src-tauri/src/cmds.rs | 95 +++++--- src-tauri/src/config/clash.rs | 84 ------- src-tauri/src/config/config.rs | 14 -- src-tauri/src/config/draft.rs | 7 +- src-tauri/src/config/prfitem.rs | 6 +- src-tauri/src/config/profiles.rs | 71 +----- src-tauri/src/config/verge.rs | 59 +---- src-tauri/src/core/clash_api.rs | 4 +- src-tauri/src/core/hotkey.rs | 12 +- src-tauri/src/core/sysopt.rs | 18 +- src-tauri/src/core/timer.rs | 13 +- src-tauri/src/core/tray.rs | 5 +- src-tauri/src/data/clash.rs | 167 ------------- src-tauri/src/data/mod.rs | 32 --- src-tauri/src/data/prfitem.rs | 406 ------------------------------- src-tauri/src/data/profiles.rs | 329 ------------------------- src-tauri/src/data/verge.rs | 136 ----------- src-tauri/src/feat.rs | 349 +++++++++++++++----------- src-tauri/src/main.rs | 1 - src-tauri/src/utils/resolve.rs | 8 +- src-tauri/src/utils/server.rs | 6 +- 21 files changed, 324 insertions(+), 1498 deletions(-) delete mode 100644 src-tauri/src/data/clash.rs delete mode 100644 src-tauri/src/data/mod.rs delete mode 100644 src-tauri/src/data/prfitem.rs delete mode 100644 src-tauri/src/data/profiles.rs delete mode 100644 src-tauri/src/data/verge.rs diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index 39e9bf4..d980176 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -14,8 +14,7 @@ type CmdResult = Result; #[tauri::command] pub fn get_profiles() -> CmdResult { - 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) -> 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) -> 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) -> 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>) -> 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>) -> 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 { - 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) -> 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 { - 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>> { } #[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 { - 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 e59b307..d590383 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>, - /// some info - pub info: Arc>, -} - -impl ClashN { - pub fn global() -> &'static ClashN { - static DATA: OnceCell = 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 { - 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>, pub domain: Option>, } - -#[test] -fn test() { - let socket = SocketAddr::new("127.0.0.1".parse().unwrap(), 9090); - - let s = "[::]:8080".parse::(); - - dbg!(s); - - // match "::8080".parse::() { - // 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 9ca78df..56b7149 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, @@ -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 { Self::global().clash_config.clone() } diff --git a/src-tauri/src/config/draft.rs b/src-tauri/src/config/draft.rs index 64bc5c8..2f70b28 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 62dc9b6..a377fb9 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, @@ -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 3779c51..fe7f4c0 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>, -} - -impl ProfilesN { - pub fn global() -> &'static ProfilesN { - static PROFILES: OnceCell = 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) -> 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, + pub current: Option, /// same as PrfConfig.chain - chain: Option>, + pub chain: Option>, /// record valid fields for clash - valid: Option>, + pub valid: Option>, /// profile list - items: Option>, + pub items: Option>, } 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 7df68f9..c5986a9 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>, -} - -impl VergeN { - pub fn global() -> &'static VergeN { - static DATA: OnceCell = OnceCell::new(); - - DATA.get_or_init(|| { - let config = config::read_yaml::(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::(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::(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 2504280..3dd796c 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 cd844db..38565bc 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 f9231e0..152ce58 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 0c612b0..8537367 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 { - 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 36fa652..8ec8d09 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 73ecd35..0000000 --- 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, - - /// same as `external-controller` - pub server: Option, - - /// clash secret - pub secret: Option, -} - -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 eb6a406..0000000 --- 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>, - pub verge: Arc>, - pub profiles: Arc>, -} - -impl Data { - pub fn global() -> &'static Data { - static DATA: OnceCell = 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 90b08f3..0000000 --- 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, - - /// profile item type - /// enum value: remote | local | script | merge - #[serde(rename = "type")] - pub itype: Option, - - /// profile name - pub name: Option, - - /// profile file - pub file: Option, - - /// profile description - #[serde(skip_serializing_if = "Option::is_none")] - pub desc: Option, - - /// source url - #[serde(skip_serializing_if = "Option::is_none")] - pub url: Option, - - /// selected infomation - #[serde(skip_serializing_if = "Option::is_none")] - pub selected: Option>, - - /// subscription user info - #[serde(skip_serializing_if = "Option::is_none")] - pub extra: Option, - - /// updated time - pub updated: Option, - - /// some options of the item - #[serde(skip_serializing_if = "Option::is_none")] - pub option: Option, - - /// the file data - #[serde(skip)] - pub file_data: Option, -} - -#[derive(Default, Debug, Clone, Deserialize, Serialize)] -pub struct PrfSelected { - pub name: Option, - pub now: Option, -} - -#[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, - - /// for `remote` profile - /// use system proxy - #[serde(skip_serializing_if = "Option::is_none")] - pub with_proxy: Option, - - /// for `remote` profile - /// use self proxy - #[serde(skip_serializing_if = "Option::is_none")] - pub self_proxy: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub update_interval: Option, -} - -impl PrfOption { - pub fn merge(one: Option, other: Option) -> Option { - 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) -> Result { - 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) -> Result { - 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, - desc: Option, - option: Option, - ) -> Result { - 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::(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::() { - 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::(&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 { - 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 { - 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 { - 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 { - 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 6272cd8..0000000 --- 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, - - /// same as PrfConfig.chain - chain: Option>, - - /// record valid fields for clash - valid: Option>, - - /// profile list - items: Option>, -} - -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::(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 { - 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>) -> Result<()> { - self.chain = chain; - self.save_file() - } - - /// just change the `field` - pub fn put_valid(&mut self, valid: Option>) -> Result<()> { - self.valid = valid; - self.save_file() - } - - /// get items ref - pub fn get_items(&self) -> Option<&Vec> { - 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 { - 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 { - 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 { - 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::>(), - 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, - pub valid: Vec, -} - -#[derive(Default, Debug, Clone, Deserialize, Serialize)] -pub struct RuntimeResult { - pub config: Option, - pub config_yaml: Option, - // 记录在配置中(包括merge和script生成的)出现过的keys - // 这些keys不一定都生效 - pub exists_keys: Vec, - pub chain_logs: HashMap>, -} diff --git a/src-tauri/src/data/verge.rs b/src-tauri/src/data/verge.rs deleted file mode 100644 index 97c2016..0000000 --- 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, - - // i18n - pub language: Option, - - /// `light` or `dark` or `system` - pub theme_mode: Option, - - /// enable blur mode - /// maybe be able to set the alpha - pub theme_blur: Option, - - /// enable traffic graph default is true - pub traffic_graph: Option, - - /// clash tun mode - pub enable_tun_mode: Option, - - /// windows service mode - #[serde(skip_serializing_if = "Option::is_none")] - pub enable_service_mode: Option, - - /// can the app auto startup - pub enable_auto_launch: Option, - - /// not show the window on launch - pub enable_silent_start: Option, - - /// set system proxy - pub enable_system_proxy: Option, - - /// enable proxy guard - pub enable_proxy_guard: Option, - - /// set system proxy bypass - pub system_proxy_bypass: Option, - - /// proxy guard duration - pub proxy_guard_duration: Option, - - /// theme setting - pub theme_setting: Option, - - /// web ui list - pub web_ui_list: Option>, - - /// clash core path - #[serde(skip_serializing_if = "Option::is_none")] - pub clash_core: Option, - - /// hotkey map - /// format: {func},{key} - pub hotkeys: Option>, - - /// 切换代理时自动关闭连接 - pub auto_close_connection: Option, - - /// 默认的延迟测试连接 - pub default_latency_test: Option, -} - -#[derive(Default, Debug, Clone, Deserialize, Serialize)] -pub struct VergeTheme { - pub primary_color: Option, - pub secondary_color: Option, - pub primary_text: Option, - pub secondary_text: Option, - - pub info_color: Option, - pub error_color: Option, - pub warning_color: Option, - pub success_color: Option, - - pub font_family: Option, - pub css_injection: Option, -} - -impl Verge { - pub fn new() -> Self { - config::read_yaml::(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 d94fe7d..2667ebe 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); -// 打开系统代理 -pub fn enable_system_proxy() -> Result<()> { - patch_verge(IVerge { - enable_system_proxy: Some(true), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) -} - -// 关闭系统代理 -pub fn disable_system_proxy() -> Result<()> { - patch_verge(IVerge { - enable_system_proxy: Some(false), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) -} - -// 切换tun模式 -pub fn toggle_tun_mode() -> Result<()> { - let enable = { - let verge = VergeN::global().config.lock(); - verge.enable_tun_mode.clone().unwrap_or(false) - }; - - patch_verge(IVerge { - enable_tun_mode: Some(!enable), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) -} - -// 打开tun模式 -pub fn enable_tun_mode() -> Result<()> { - patch_verge(IVerge { - enable_tun_mode: Some(true), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) -} - -// 关闭tun模式 -pub fn disable_tun_mode() -> Result<()> { - patch_verge(IVerge { - enable_tun_mode: Some(false), - ..IVerge::default() - })?; - handle::Handle::refresh_verge(); - Ok(()) -} - -/// 修改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(); - - // 更新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); - } - - let old_info = ClashN::global().patch_info(ClashInfoN::from(&tmp_config))?; - - if let Err(err) = CoreManager::global().run_core() { - // 恢复旧值 - ClashN::global().patch_info(old_info)?; - return Err(err); - } - } - // 存好再搞 - 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 clash_mode { - log_err!(handle::Handle::update_systray_part()); - } - } + match patch_verge(IVerge { + enable_system_proxy: Some(!enable), + ..IVerge::default() + }) + .await + { + Ok(_) => handle::Handle::refresh_verge(), Err(err) => log::error!(target: "app", "{err}"), } }); - 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() { + 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() { + let enable = Config::verge().data().enable_tun_mode.clone(); + let enable = enable.unwrap_or(false); + + 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() { + 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() { + 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 async fn patch_clash(patch: Mapping) -> Result<()> { + Config::clash().draft().patch_config(patch.clone()); + + 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"); + } + } + } + }; + + // 激活配置 + handle_activate().await?; + + // 更新系统代理 + if mixed_port.is_some() { + log_err!(sysopt::Sysopt::global().init_sysproxy()); + } + + if patch.get("mode").is_some() { + log_err!(handle::Handle::update_systray_part()); + } + + >::Ok(()) + } { + Ok(()) => { + Config::clash().apply(); + Config::clash().data().save_config()?; + 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()?; + } + + >::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) -> 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 f0005e7..b019d03 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 9034fce..e7810d7 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 1e68326..1d2ff59 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 || { From df93cb103c2f22090deb8e6db70c550a334d19e4 Mon Sep 17 00:00:00 2001 From: GyDi Date: Thu, 17 Nov 2022 20:19:40 +0800 Subject: [PATCH 09/17] refactor: for windows --- src-tauri/Cargo.lock | 36 ++--- src-tauri/src/cmds.rs | 20 +-- src-tauri/src/config/prfitem.rs | 8 +- src-tauri/src/core/core.rs | 78 +++++++---- src-tauri/src/core/core_service.rs | 217 ---------------------------- src-tauri/src/core/handle.rs | 10 +- src-tauri/src/core/mod.rs | 2 +- src-tauri/src/core/win_service.rs | 218 +++++++++++++++++++++++++++++ src-tauri/src/feat.rs | 7 +- src-tauri/src/main.rs | 12 +- src-tauri/src/utils/config.rs | 2 +- src-tauri/src/utils/dirs.rs | 18 +-- src-tauri/src/utils/help.rs | 9 +- src-tauri/src/utils/init.rs | 26 +--- src-tauri/src/utils/resolve.rs | 4 +- 15 files changed, 332 insertions(+), 335 deletions(-) delete mode 100644 src-tauri/src/core/core_service.rs create mode 100644 src-tauri/src/core/win_service.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c507246..f7aed53 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -985,9 +985,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.3", "crypto-common", @@ -3235,9 +3235,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" dependencies = [ "base64", "bytes", @@ -3670,7 +3670,7 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -3681,7 +3681,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -3705,7 +3705,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.6", ] [[package]] @@ -3955,9 +3955,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42c460173627564bde252ca5ebf346ba5b37c5cee1a445782bacc8e9b8d38b5e" +checksum = "a0382fcc02b87420a0d024ff6ec2dbfccdccafb46de8c03fb07dbf2f36810a42" dependencies = [ "bitflags", "cairo-rs", @@ -3995,7 +3995,7 @@ dependencies = [ "scopeguard", "serde", "unicode-segmentation", - "uuid 1.2.1", + "uuid 1.2.2", "windows 0.39.0", "windows-implement", "x11-dl", @@ -4060,7 +4060,7 @@ dependencies = [ "time 0.3.17", "tokio", "url", - "uuid 1.2.1", + "uuid 1.2.2", "webkit2gtk", "webview2-com", "windows 0.39.0", @@ -4105,7 +4105,7 @@ dependencies = [ "tauri-utils", "thiserror", "time 0.3.17", - "uuid 1.2.1", + "uuid 1.2.2", "walkdir", ] @@ -4138,7 +4138,7 @@ dependencies = [ "serde_json", "tauri-utils", "thiserror", - "uuid 1.2.1", + "uuid 1.2.2", "webview2-com", "windows 0.39.0", ] @@ -4156,7 +4156,7 @@ dependencies = [ "raw-window-handle", "tauri-runtime", "tauri-utils", - "uuid 1.2.1", + "uuid 1.2.2", "webkit2gtk", "webview2-com", "windows 0.39.0", @@ -4708,9 +4708,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c" dependencies = [ "getrandom 0.2.8", ] @@ -5358,9 +5358,9 @@ dependencies = [ [[package]] name = "wry" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923d297b203eae65b095af16c02978b7932be1968012b4da7138390edf34dea5" +checksum = "e0fd80bb2bd8e8eae26d59c5164e70233f29f7593cb886a958024a4fd8b5cd21" dependencies = [ "base64", "block", diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index d980176..8b09293 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -19,7 +19,7 @@ pub fn get_profiles() -> CmdResult { #[tauri::command] pub async fn enhance_profiles() -> CmdResult { - wrap_err!(CoreManager::global().activate_config().await) + wrap_err!(feat::handle_activate().await) } #[deprecated] @@ -209,8 +209,8 @@ pub async fn change_clash_core(clash_core: Option) -> CmdResult { /// restart the sidecar #[tauri::command] -pub fn restart_sidecar() -> CmdResult { - wrap_err!(CoreManager::global().run_core()) +pub async fn restart_sidecar() -> CmdResult { + wrap_err!(CoreManager::global().run_core().await) } /// get the system proxy @@ -254,22 +254,22 @@ pub fn open_web_url(url: String) -> CmdResult<()> { #[cfg(windows)] pub mod service { use super::*; - use crate::core::win_service::JsonResponse; + use crate::core::win_service; #[tauri::command] pub async fn start_service() -> CmdResult { - wrap_err!(crate::core::Service::start_service().await) + wrap_err!(win_service::start_service().await) } #[tauri::command] pub async fn stop_service() -> CmdResult { - wrap_err!(crate::core::Service::stop_service().await) + wrap_err!(win_service::stop_service().await) } #[tauri::command] - pub async fn check_service() -> CmdResult { + pub async fn check_service() -> CmdResult { // no log - match crate::core::Service::check_service().await { + match win_service::check_service().await { Ok(res) => Ok(res), Err(err) => Err(err.to_string()), } @@ -277,12 +277,12 @@ pub mod service { #[tauri::command] pub async fn install_service() -> CmdResult { - wrap_err!(crate::core::Service::install_service().await) + wrap_err!(win_service::install_service().await) } #[tauri::command] pub async fn uninstall_service() -> CmdResult { - wrap_err!(crate::core::Service::uninstall_service().await) + wrap_err!(win_service::uninstall_service().await) } } diff --git a/src-tauri/src/config/prfitem.rs b/src-tauri/src/config/prfitem.rs index a377fb9..cb81212 100644 --- a/src-tauri/src/config/prfitem.rs +++ b/src-tauri/src/config/prfitem.rs @@ -172,7 +172,7 @@ impl PrfItem { selected: None, extra: None, option: None, - updated: Some(help::get_now()), + updated: Some(chrono::Local::now().timestamp() as usize), file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())), }) } @@ -301,7 +301,7 @@ impl PrfItem { selected: None, extra, option, - updated: Some(help::get_now()), + updated: Some(chrono::Local::now().timestamp() as usize), file_data: Some(data), }) } @@ -322,7 +322,7 @@ impl PrfItem { selected: None, extra: None, option: None, - updated: Some(help::get_now()), + updated: Some(chrono::Local::now().timestamp() as usize), file_data: Some(tmpl::ITEM_MERGE.into()), }) } @@ -343,7 +343,7 @@ impl PrfItem { selected: None, extra: None, option: None, - updated: Some(help::get_now()), + updated: Some(chrono::Local::now().timestamp() as usize), file_data: Some(tmpl::ITEM_SCRIPT.into()), }) } diff --git a/src-tauri/src/core/core.rs b/src-tauri/src/core/core.rs index 30fe6cc..f9dee69 100644 --- a/src-tauri/src/core/core.rs +++ b/src-tauri/src/core/core.rs @@ -47,22 +47,13 @@ impl CoreManager { } } - // // 使用配置的核心 - // let verge_core = { Config::verge().clash_core.clone() }; - // if let Some(verge_core) = verge_core { - // if verge_core == "clash" || verge_core == "clash-meta" { - // let mut clash_core = self.clash_core.lock(); - // *clash_core = verge_core; - // } - // } - - // 启动clash - self.run_core()?; - - // 更新配置 tauri::async_runtime::spawn(async { - sleep(Duration::from_millis(100)).await; - crate::log_err!(Self::global().activate_config().await); + // 启动clash + if Self::global().run_core().await.is_ok() { + // 更新配置 + sleep(Duration::from_millis(100)).await; + crate::log_err!(Self::global().activate_config().await); + } }); Ok(()) @@ -89,9 +80,35 @@ impl CoreManager { } /// 启动核心 - pub fn run_core(&self) -> Result<()> { - // 先纠正重要的配置字段 - self.correct_config()?; + pub async fn run_core(&self) -> Result<()> { + #[cfg(target_os = "windows")] + { + use super::win_service; + + // 服务模式 + let enable = { + let enable = Config::verge().data().enable_service_mode.clone(); + enable.unwrap_or(false) + }; + + *self.use_service_mode.lock() = enable; + + if enable { + // 服务模式启动失败就直接运行sidecar + match { + win_service::check_service().await?; + win_service::run_core_by_service().await + } { + Ok(_) => return Ok(()), + Err(err) => { + // 修改这个值,免得stop出错 + *self.use_service_mode.lock() = false; + + log::error!(target: "app", "{err}"); + } + } + } + } let mut sidecar = self.sidecar.lock(); @@ -137,15 +154,15 @@ impl CoreManager { Logger::global().set_log(line); } CommandEvent::Stderr(err) => { - log::error!(target: "app" ,"[clash error]: {err}"); + log::error!(target: "app" ,"[clash]: {err}"); Logger::global().set_log(err); } CommandEvent::Error(err) => { - log::error!(target: "app" ,"[clash error]: {err}"); + log::error!(target: "app" ,"[clash]: {err}"); Logger::global().set_log(err); } CommandEvent::Terminated(_) => { - log::info!(target: "app" ,"clash core Terminated"); + log::info!(target: "app" ,"clash core terminated"); break; } _ => {} @@ -158,6 +175,14 @@ impl CoreManager { /// 停止核心运行 pub fn stop_core(&self) -> Result<()> { + #[cfg(target_os = "windows")] + if *self.use_service_mode.lock() { + tauri::async_runtime::block_on(async move { + log_err!(super::win_service::stop_core_by_service().await); + }); + return Ok(()); + } + let mut sidecar = self.sidecar.lock(); if let Some(child) = sidecar.take() { let _ = child.kill(); @@ -180,7 +205,7 @@ impl CoreManager { Config::verge().draft().clash_core = Some(clash_core); } - match self.run_core() { + match self.run_core().await { Ok(_) => { log_err!({ Config::verge().apply(); @@ -198,13 +223,6 @@ impl CoreManager { } } - /// 纠正一下配置 - /// 将mixed-port和external-controller都改为配置的内容 - pub fn correct_config(&self) -> Result<()> { - // todo!() - Ok(()) - } - /// 激活一个配置 pub async fn activate_config(&self) -> Result<()> { let clash_config = { Config::clash().latest().clone() }; @@ -224,8 +242,6 @@ impl CoreManager { // 检查配置是否正常 self.check_config()?; - // todo 是否需要检查核心是否运行 - // 发送请求 发送5次 for i in 0..5 { match clash_api::put_configs().await { diff --git a/src-tauri/src/core/core_service.rs b/src-tauri/src/core/core_service.rs deleted file mode 100644 index 7d52194..0000000 --- a/src-tauri/src/core/core_service.rs +++ /dev/null @@ -1,217 +0,0 @@ -#![cfg(target_os = "windows")] - -use crate::utils::{config, dirs}; -use anyhow::Context; -use deelevate::{PrivilegeLevel, Token}; -use runas::Command as RunasCommand; -use serde::{Deserialize, Serialize}; -use std::os::windows::process::CommandExt; -use std::{env::current_exe, process::Command as StdCommand}; - -const SERVICE_NAME: &str = "clash_verge_service"; - -const SERVICE_URL: &str = "http://127.0.0.1:33211"; - -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct ResponseBody { - pub bin_path: String, - pub config_dir: String, - pub log_file: String, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct JsonResponse { - pub code: u64, - pub msg: String, - pub data: Option, -} - -impl Service { - /// Install the Clash Verge Service - /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 - pub async fn install_service() -> Result<()> { - let binary_path = dirs::service_path(); - let install_path = binary_path.with_file_name("install-service.exe"); - - if !install_path.exists() { - bail!("installer exe not found"); - } - - let token = Token::with_current_process()?; - let level = token.privilege_level()?; - - let status = match level { - PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).status()?, - _ => StdCommand::new(install_path) - .creation_flags(0x08000000) - .status()?, - }; - - if !status.success() { - bail!( - "failed to install service with status {}", - status.code().unwrap() - ); - } - - Ok(()) - } - - /// Uninstall the Clash Verge Service - /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 - pub async fn uninstall_service() -> Result<()> { - let binary_path = dirs::service_path(); - let uninstall_path = binary_path.with_file_name("uninstall-service.exe"); - - if !uninstall_path.exists() { - bail!("uninstaller exe not found"); - } - - let token = Token::with_current_process()?; - let level = token.privilege_level()?; - - let status = match level { - PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).status()?, - _ => StdCommand::new(uninstall_path) - .creation_flags(0x08000000) - .status()?, - }; - - if !status.success() { - bail!( - "failed to uninstall service with status {}", - status.code().unwrap() - ); - } - - Ok(()) - } - - /// [deprecated] - /// start service - /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 - pub async fn start_service() -> Result<()> { - let token = Token::with_current_process()?; - let level = token.privilege_level()?; - - let args = ["start", SERVICE_NAME]; - - let status = match level { - PrivilegeLevel::NotPrivileged => RunasCommand::new("sc").args(&args).status()?, - _ => StdCommand::new("sc").args(&args).status()?, - }; - - match status.success() { - true => Ok(()), - false => bail!( - "failed to start service with status {}", - status.code().unwrap() - ), - } - } - - /// stop service - pub async fn stop_service() -> Result<()> { - let url = format!("{SERVICE_URL}/stop_service"); - let res = reqwest::ClientBuilder::new() - .no_proxy() - .build()? - .post(url) - .send() - .await? - .json::() - .await - .context("failed to connect to the Clash Verge Service")?; - - if res.code != 0 { - bail!(res.msg); - } - - Ok(()) - } - - /// check the windows service status - pub async fn check_service() -> Result { - let url = format!("{SERVICE_URL}/get_clash"); - let response = reqwest::ClientBuilder::new() - .no_proxy() - .build()? - .get(url) - .send() - .await? - .json::() - .await - .context("failed to connect to the Clash Verge Service")?; - - Ok(response) - } - - /// start the clash by service - pub(super) async fn start_clash_by_service() -> Result<()> { - let status = Self::check_service().await?; - - if status.code == 0 { - Self::stop_clash_by_service().await?; - sleep(Duration::from_secs(1)).await; - } - - let clash_core = { - let global = Data::global(); - let verge = global.verge.lock(); - verge.clash_core.clone().unwrap_or("clash".into()) - }; - - let clash_bin = format!("{clash_core}.exe"); - let bin_path = current_exe().unwrap().with_file_name(clash_bin); - let bin_path = bin_path.as_os_str().to_str().unwrap(); - - let config_dir = dirs::app_home_dir(); - let config_dir = config_dir.as_os_str().to_str().unwrap(); - - let log_path = dirs::service_log_file(); - let log_path = log_path.as_os_str().to_str().unwrap(); - - let mut map = HashMap::new(); - map.insert("bin_path", bin_path); - map.insert("config_dir", config_dir); - map.insert("log_file", log_path); - - let url = format!("{SERVICE_URL}/start_clash"); - let res = reqwest::ClientBuilder::new() - .no_proxy() - .build()? - .post(url) - .json(&map) - .send() - .await? - .json::() - .await - .context("failed to connect to the Clash Verge Service")?; - - if res.code != 0 { - bail!(res.msg); - } - - Ok(()) - } - - /// stop the clash by service - pub(super) async fn stop_clash_by_service() -> Result<()> { - let url = format!("{SERVICE_URL}/stop_clash"); - let res = reqwest::ClientBuilder::new() - .no_proxy() - .build()? - .post(url) - .send() - .await? - .json::() - .await - .context("failed to connect to the Clash Verge Service")?; - - if res.code != 0 { - bail!(res.msg); - } - - Ok(()) - } -} diff --git a/src-tauri/src/core/handle.rs b/src-tauri/src/core/handle.rs index 7026e34..5b46cea 100644 --- a/src-tauri/src/core/handle.rs +++ b/src-tauri/src/core/handle.rs @@ -1,5 +1,5 @@ use super::tray::Tray; -use crate::log_if_err; +use crate::log_err; use anyhow::{bail, Result}; use once_cell::sync::OnceCell; use parking_lot::Mutex; @@ -33,26 +33,26 @@ impl Handle { pub fn refresh_clash() { if let Some(window) = Self::global().get_window() { - log_if_err!(window.emit("verge://refresh-clash-config", "yes")); + log_err!(window.emit("verge://refresh-clash-config", "yes")); } } pub fn refresh_verge() { if let Some(window) = Self::global().get_window() { - log_if_err!(window.emit("verge://refresh-verge-config", "yes")); + log_err!(window.emit("verge://refresh-verge-config", "yes")); } } #[allow(unused)] pub fn refresh_profiles() { if let Some(window) = Self::global().get_window() { - log_if_err!(window.emit("verge://refresh-profiles-config", "yes")); + log_err!(window.emit("verge://refresh-profiles-config", "yes")); } } pub fn notice_message, M: Into>(status: S, msg: M) { if let Some(window) = Self::global().get_window() { - log_if_err!(window.emit("verge://notice-message", (status.into(), msg.into()))); + log_err!(window.emit("verge://notice-message", (status.into(), msg.into()))); } } diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs index c0d1d6a..20e5600 100644 --- a/src-tauri/src/core/mod.rs +++ b/src-tauri/src/core/mod.rs @@ -13,7 +13,6 @@ pub mod clash_api; mod core; -pub mod core_service; pub mod handle; pub mod hotkey; pub mod logger; @@ -21,6 +20,7 @@ pub mod logger; pub mod sysopt; pub mod timer; pub mod tray; +pub mod win_service; pub use self::core::*; // pub use self::service::*; diff --git a/src-tauri/src/core/win_service.rs b/src-tauri/src/core/win_service.rs new file mode 100644 index 0000000..b0ddb8f --- /dev/null +++ b/src-tauri/src/core/win_service.rs @@ -0,0 +1,218 @@ +#![cfg(target_os = "windows")] + +use crate::config::Config; +use crate::utils::dirs; +use anyhow::{bail, Context, Result}; +use deelevate::{PrivilegeLevel, Token}; +use runas::Command as RunasCommand; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::os::windows::process::CommandExt; +use std::time::Duration; +use std::{env::current_exe, process::Command as StdCommand}; +use tokio::time::sleep; + +const SERVICE_NAME: &str = "clash_verge_service"; + +const SERVICE_URL: &str = "http://127.0.0.1:33211"; + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct ResponseBody { + pub core_type: Option, + pub bin_path: String, + pub config_dir: String, + pub log_file: String, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct JsonResponse { + pub code: u64, + pub msg: String, + pub data: Option, +} + +/// Install the Clash Verge Service +/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 +pub async fn install_service() -> Result<()> { + let binary_path = dirs::service_path(); + let install_path = binary_path.with_file_name("install-service.exe"); + + if !install_path.exists() { + bail!("installer exe not found"); + } + + let token = Token::with_current_process()?; + let level = token.privilege_level()?; + + let status = match level { + PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).status()?, + _ => StdCommand::new(install_path) + .creation_flags(0x08000000) + .status()?, + }; + + if !status.success() { + bail!( + "failed to install service with status {}", + status.code().unwrap() + ); + } + + Ok(()) +} + +/// Uninstall the Clash Verge Service +/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 +pub async fn uninstall_service() -> Result<()> { + let binary_path = dirs::service_path(); + let uninstall_path = binary_path.with_file_name("uninstall-service.exe"); + + if !uninstall_path.exists() { + bail!("uninstaller exe not found"); + } + + let token = Token::with_current_process()?; + let level = token.privilege_level()?; + + let status = match level { + PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).status()?, + _ => StdCommand::new(uninstall_path) + .creation_flags(0x08000000) + .status()?, + }; + + if !status.success() { + bail!( + "failed to uninstall service with status {}", + status.code().unwrap() + ); + } + + Ok(()) +} + +/// start service +/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 +#[deprecated] +pub async fn start_service() -> Result<()> { + let token = Token::with_current_process()?; + let level = token.privilege_level()?; + + let args = ["start", SERVICE_NAME]; + + let status = match level { + PrivilegeLevel::NotPrivileged => RunasCommand::new("sc").args(&args).status()?, + _ => StdCommand::new("sc").args(&args).status()?, + }; + + match status.success() { + true => Ok(()), + false => bail!( + "failed to start service with status {}", + status.code().unwrap() + ), + } +} + +/// stop service +pub async fn stop_service() -> Result<()> { + let url = format!("{SERVICE_URL}/stop_service"); + let res = reqwest::ClientBuilder::new() + .no_proxy() + .build()? + .post(url) + .send() + .await? + .json::() + .await + .context("failed to connect to the Clash Verge Service")?; + + if res.code != 0 { + bail!(res.msg); + } + + Ok(()) +} + +/// check the windows service status +pub async fn check_service() -> Result { + let url = format!("{SERVICE_URL}/get_clash"); + let response = reqwest::ClientBuilder::new() + .no_proxy() + .build()? + .get(url) + .send() + .await? + .json::() + .await + .context("failed to connect to the Clash Verge Service")?; + + Ok(response) +} + +/// start the clash by service +pub(super) async fn run_core_by_service() -> Result<()> { + let status = check_service().await?; + + if status.code == 0 { + stop_core_by_service().await?; + sleep(Duration::from_secs(1)).await; + } + + let clash_core = { Config::verge().latest().clash_core.clone() }; + let clash_core = clash_core.unwrap_or("clash".into()); + + let clash_bin = format!("{clash_core}.exe"); + let bin_path = current_exe()?.with_file_name(clash_bin); + let bin_path = dirs::path_to_str(&bin_path)?; + + let config_dir = dirs::app_home_dir(); + let config_dir = dirs::path_to_str(&config_dir)?; + + let log_path = dirs::service_log_file(); + let log_path = dirs::path_to_str(&log_path)?; + + let mut map = HashMap::new(); + map.insert("core_type", clash_core.as_str()); + map.insert("bin_path", bin_path); + map.insert("config_dir", config_dir); + map.insert("log_file", log_path); + + let url = format!("{SERVICE_URL}/start_clash"); + let res = reqwest::ClientBuilder::new() + .no_proxy() + .build()? + .post(url) + .json(&map) + .send() + .await? + .json::() + .await + .context("failed to connect to the Clash Verge Service")?; + + if res.code != 0 { + bail!(res.msg); + } + + Ok(()) +} + +/// stop the clash by service +pub(super) async fn stop_core_by_service() -> Result<()> { + let url = format!("{SERVICE_URL}/stop_clash"); + let res = reqwest::ClientBuilder::new() + .no_proxy() + .build()? + .post(url) + .send() + .await? + .json::() + .await + .context("failed to connect to the Clash Verge Service")?; + + if res.code != 0 { + bail!(res.msg); + } + + Ok(()) +} diff --git a/src-tauri/src/feat.rs b/src-tauri/src/feat.rs index 2667ebe..4436664 100644 --- a/src-tauri/src/feat.rs +++ b/src-tauri/src/feat.rs @@ -7,9 +7,10 @@ use serde_yaml::{Mapping, Value}; // 重启clash pub fn restart_clash_core() { tauri::async_runtime::spawn(async { - CoreManager::global().run_core()?; - log_err!(handle_activate().await); - >::Ok(()) + match CoreManager::global().run_core().await { + Ok(_) => log_err!(handle_activate().await), + Err(err) => log::error!(target: "app", "{err}"), + } }); } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b019d03..1c89859 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -11,7 +11,7 @@ mod feat; mod utils; use crate::utils::{init, resolve, server}; -use tauri::{api, Manager, SystemTray}; +use tauri::{api, SystemTray}; fn main() -> std::io::Result<()> { // 单例检测 @@ -20,13 +20,7 @@ fn main() -> std::io::Result<()> { return Ok(()); } - #[cfg(target_os = "windows")] - unsafe { - use crate::utils::dirs; - dirs::init_portable_flag(); - } - - crate::log_if_err!(init::init_config()); + crate::log_err!(init::init_config()); #[allow(unused_mut)] let mut builder = tauri::Builder::default() @@ -118,6 +112,8 @@ fn main() -> std::io::Result<()> { } #[cfg(target_os = "macos")] tauri::RunEvent::WindowEvent { label, event, .. } => { + use tauri::Manager; + if label == "main" { match event { tauri::WindowEvent::CloseRequested { api, .. } => { diff --git a/src-tauri/src/utils/config.rs b/src-tauri/src/utils/config.rs index efcdf6a..bcb99f6 100644 --- a/src-tauri/src/utils/config.rs +++ b/src-tauri/src/utils/config.rs @@ -34,7 +34,7 @@ pub fn read_merge_mapping(path: PathBuf) -> Mapping { match serde_yaml::from_str::(&yaml_str) { Ok(mut val) => { - crate::log_if_err!(val.apply_merge()); + crate::log_err!(val.apply_merge()); val.as_mapping().unwrap_or(&map).to_owned() } Err(_) => { diff --git a/src-tauri/src/utils/dirs.rs b/src-tauri/src/utils/dirs.rs index 68cb423..8cb322d 100644 --- a/src-tauri/src/utils/dirs.rs +++ b/src-tauri/src/utils/dirs.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use std::path::PathBuf; use tauri::{ api::path::{home_dir, resource_dir}, @@ -23,20 +24,21 @@ static mut PORTABLE_FLAG: bool = false; pub static mut APP_VERSION: &str = "v1.1.1"; /// initialize portable flag -#[allow(unused)] -pub unsafe fn init_portable_flag() { - #[cfg(target_os = "windows")] - { - use tauri::utils::platform::current_exe; +#[cfg(target_os = "windows")] +pub unsafe fn init_portable_flag() -> Result<()> { + use tauri::utils::platform::current_exe; - let exe = current_exe().unwrap(); - let dir = exe.parent().unwrap(); + let exe = current_exe()?; + + if let Some(dir) = exe.parent() { let dir = PathBuf::from(dir).join(".config/PORTABLE"); if dir.exists() { PORTABLE_FLAG = true; } } + + Ok(()) } /// get the verge app home dir @@ -123,7 +125,7 @@ pub fn service_log_file() -> PathBuf { let log_dir = app_logs_dir().join("service"); - let local_time = Local::now().format("%Y-%m-%d-%H%M%S").to_string(); + let local_time = Local::now().format("%Y-%m-%d-%H%M").to_string(); let log_file = format!("{}.log", local_time); let log_file = log_dir.join(log_file); diff --git a/src-tauri/src/utils/help.rs b/src-tauri/src/utils/help.rs index 0c5bb7e..8ad5925 100644 --- a/src-tauri/src/utils/help.rs +++ b/src-tauri/src/utils/help.rs @@ -3,14 +3,6 @@ use nanoid::nanoid; use std::path::PathBuf; use std::process::Command; use std::str::FromStr; -use std::time::{SystemTime, UNIX_EPOCH}; - -pub fn get_now() -> usize { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs() as _ -} const ALPHABET: [char; 62] = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', @@ -76,6 +68,7 @@ macro_rules! error { }; } +#[deprecated] #[macro_export] macro_rules! log_if_err { ($result: expr) => { diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs index bb36352..7f6c74a 100644 --- a/src-tauri/src/utils/init.rs +++ b/src-tauri/src/utils/init.rs @@ -17,11 +17,11 @@ fn init_log() -> Result<()> { let _ = fs::create_dir_all(&log_dir); } - let local_time = Local::now().format("%Y-%m-%d-%H%M%S").to_string(); + let local_time = Local::now().format("%Y-%m-%d-%H%M").to_string(); let log_file = format!("{}.log", local_time); let log_file = log_dir.join(log_file); - let time_format = "{d(%Y-%m-%d %H:%M:%S)} - {m}{n}"; + let time_format = "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}"; let stdout = ConsoleAppender::builder() .encoder(Box::new(PatternEncoder::new(time_format))) .build(); @@ -47,6 +47,11 @@ fn init_log() -> Result<()> { /// Initialize all the files from resources pub fn init_config() -> Result<()> { + #[cfg(target_os = "windows")] + unsafe { + let _ = dirs::init_portable_flag(); + } + let _ = init_log(); let app_dir = dirs::app_home_dir(); @@ -94,21 +99,4 @@ pub fn init_resources(package_info: &PackageInfo) { let _ = fs::copy(src_path, target_path); } } - - // // copy the resource file - // let mmdb_path = app_dir.join("Country.mmdb"); - // let mmdb_tmpl = res_dir.join("Country.mmdb"); - // if !mmdb_path.exists() && mmdb_tmpl.exists() { - // let _ = fs::copy(mmdb_tmpl, mmdb_path); - // } - - // // copy the wintun.dll - // #[cfg(target_os = "windows")] - // { - // let wintun_path = app_dir.join("wintun.dll"); - // let wintun_tmpl = res_dir.join("wintun.dll"); - // if !wintun_path.exists() && wintun_tmpl.exists() { - // let _ = fs::copy(wintun_tmpl, wintun_path); - // } - // } } diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index e7810d7..37c258c 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -93,10 +93,10 @@ pub fn create_window(app_handle: &AppHandle) { } #[cfg(target_os = "macos")] - crate::log_if_err!(builder.decorations(true).inner_size(800.0, 642.0).build()); + crate::log_err!(builder.decorations(true).inner_size(800.0, 642.0).build()); #[cfg(target_os = "linux")] - crate::log_if_err!(builder + crate::log_err!(builder .decorations(false) .transparent(true) .inner_size(800.0, 636.0) From 1880da63516f9bba32cf4ca7a6934dbf23081e6c Mon Sep 17 00:00:00 2001 From: GyDi Date: Thu, 17 Nov 2022 22:52:22 +0800 Subject: [PATCH 10/17] refactor: rm dead code --- src-tauri/src/cmds.rs | 24 +-- src-tauri/src/core/mod.rs | 336 ------------------------------ src-tauri/src/core/win_service.rs | 43 ---- src-tauri/src/main.rs | 2 - src/services/cmds.ts | 8 - 5 files changed, 1 insertion(+), 412 deletions(-) diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index 8b09293..2dbb5e4 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -256,23 +256,9 @@ pub mod service { use super::*; use crate::core::win_service; - #[tauri::command] - pub async fn start_service() -> CmdResult { - wrap_err!(win_service::start_service().await) - } - - #[tauri::command] - pub async fn stop_service() -> CmdResult { - wrap_err!(win_service::stop_service().await) - } - #[tauri::command] pub async fn check_service() -> CmdResult { - // no log - match win_service::check_service().await { - Ok(res) => Ok(res), - Err(err) => Err(err.to_string()), - } + wrap_err!(win_service::check_service().await) } #[tauri::command] @@ -290,14 +276,6 @@ pub mod service { pub mod service { use super::*; - #[tauri::command] - pub async fn start_service() -> CmdResult { - Ok(()) - } - #[tauri::command] - pub async fn stop_service() -> CmdResult { - Ok(()) - } #[tauri::command] pub async fn check_service() -> CmdResult { Ok(()) diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs index 20e5600..18655ab 100644 --- a/src-tauri/src/core/mod.rs +++ b/src-tauri/src/core/mod.rs @@ -1,347 +1,11 @@ -// use self::handle::Handle; -// use self::hotkey::Hotkey; -// use self::sysopt::Sysopt; -// use self::timer::Timer; -// // use crate::data::*; -// // use crate::enhance::enhance_config; -// use crate::log_if_err; -// use anyhow::{bail, Result}; -// use once_cell::sync::OnceCell; -// use parking_lot::Mutex; -// use serde_yaml::{Mapping, Value}; -// use std::sync::Arc; - pub mod clash_api; mod core; pub mod handle; pub mod hotkey; pub mod logger; -// pub mod service; pub mod sysopt; pub mod timer; pub mod tray; pub mod win_service; pub use self::core::*; -// pub use self::service::*; - -#[derive(Clone)] -pub struct Core {} - -impl Core { - // pub fn global() -> &'static Core { - // static CORE: OnceCell = OnceCell::new(); - - // CORE.get_or_init(|| Core { - // service: Arc::new(Mutex::new(Service::new())), - // sysopt: Arc::new(Mutex::new(Sysopt::new())), - // timer: Arc::new(Mutex::new(Timer::new())), - // hotkey: Arc::new(Mutex::new(Hotkey::new())), - // runtime: Arc::new(Mutex::new(RuntimeResult::default())), - // handle: Arc::new(Mutex::new(Handle::default())), - // }) - // } - - // /// initialize the core state - // pub fn init(&self, app_handle: tauri::AppHandle) { - // kill old clash process - // Service::kill_old_clash(); - - // let mut handle = self.handle.lock(); - // handle.set_inner(app_handle.clone()); - // drop(handle); - - // let mut service = self.service.lock(); - // log_if_err!(service.start()); - // drop(service); - - // log_if_err!(self.activate()); - - // let mut sysopt = self.sysopt.lock(); - // log_if_err!(sysopt.init_launch()); - // log_if_err!(sysopt.init_sysproxy()); - // drop(sysopt); - - // let handle = self.handle.lock(); - // log_if_err!(handle.update_systray_part()); - // drop(handle); - - // let mut hotkey = self.hotkey.lock(); - // log_if_err!(hotkey.init(app_handle)); - // drop(hotkey); - - // // timer initialize - // let mut timer = self.timer.lock(); - // log_if_err!(timer.restore()); - // } - - // /// restart the clash sidecar - // pub fn restart_clash(&self) -> Result<()> { - // let mut service = self.service.lock(); - // service.restart()?; - // drop(service); - // self.activate() - // } - - // /// change the clash core - // pub fn change_core(&self, clash_core: Option) -> Result<()> { - // let clash_core = clash_core.unwrap_or("clash".into()); - - // if &clash_core != "clash" && &clash_core != "clash-meta" { - // bail!("invalid clash core name \"{clash_core}\""); - // } - - // let global = Data::global(); - // let mut verge = global.verge.lock(); - // verge.patch_config(Verge { - // clash_core: Some(clash_core.clone()), - // ..Verge::default() - // })?; - // drop(verge); - - // let mut service = self.service.lock(); - // service.clear_logs(); - // service.restart()?; - // drop(service); - - // self.activate() - // } - - // /// Patch Clash - // /// handle the clash config changed - // pub fn patch_clash(&self, patch: Mapping) -> Result<()> { - // let patch_cloned = patch.clone(); - // let clash_mode = patch.get("mode"); - // let mixed_port = patch.get("mixed-port"); - // let external = patch.get("external-controller"); - // let secret = patch.get("secret"); - - // let valid_port = { - // let global = Data::global(); - // let mut clash = global.clash.lock(); - // clash.patch_config(patch_cloned)?; - // clash.info.port.is_some() - // }; - - // // todo: port check - // if (mixed_port.is_some() && valid_port) || external.is_some() || secret.is_some() { - // let mut service = self.service.lock(); - // service.restart()?; - // drop(service); - - // self.activate()?; - - // let mut sysopt = self.sysopt.lock(); - // sysopt.init_sysproxy()?; - // } - - // if clash_mode.is_some() { - // let handle = self.handle.lock(); - // handle.update_systray_part()?; - // } - - // Ok(()) - // } - - // /// Patch Verge - // pub fn patch_verge(&self, patch: Verge) -> Result<()> { - // // save the patch - // let global = Data::global(); - // let mut verge = global.verge.lock(); - // verge.patch_config(patch.clone())?; - // drop(verge); - - // 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")] - // { - // let service_mode = patch.enable_service_mode; - - // // 重启服务 - // if service_mode.is_some() { - // let mut service = self.service.lock(); - // service.restart()?; - // drop(service); - // } - - // if tun_mode.is_some() && *tun_mode.as_ref().unwrap_or(&false) { - // let wintun_dll = crate::utils::dirs::app_home_dir().join("wintun.dll"); - // if !wintun_dll.exists() { - // bail!("failed to enable TUN for missing `wintun.dll`"); - // } - // } - - // if service_mode.is_some() || tun_mode.is_some() { - // self.activate()?; - // } - // } - - // #[cfg(not(target_os = "windows"))] - // if tun_mode.is_some() { - // self.activate()?; - // } - - // let mut sysopt = self.sysopt.lock(); - - // if auto_launch.is_some() { - // sysopt.update_launch()?; - // } - // if system_proxy.is_some() || proxy_bypass.is_some() { - // sysopt.update_sysproxy()?; - // sysopt.guard_proxy(); - // } - // if proxy_guard.unwrap_or(false) { - // sysopt.guard_proxy(); - // } - - // // 更新tray - // if language.is_some() { - // let handle = self.handle.lock(); - // handle.update_systray()?; - // } else if system_proxy.is_some() || tun_mode.is_some() { - // let handle = self.handle.lock(); - // handle.update_systray_part()?; - // } - - // if patch.hotkeys.is_some() { - // let mut hotkey = self.hotkey.lock(); - // hotkey.update(patch.hotkeys.unwrap())?; - // } - - // Ok(()) - // } - - // /// update rule/global/direct/script mode - // pub fn update_mode(&self, mode: &str) -> Result<()> { - // // save config to file - // let info = { - // let global = Data::global(); - // let mut clash = global.clash.lock(); - // clash.config.insert(Value::from("mode"), Value::from(mode)); - // clash.save_config()?; - // clash.info.clone() - // }; - - // let mut mapping = Mapping::new(); - // mapping.insert(Value::from("mode"), Value::from(mode)); - - // let handle = self.handle.clone(); - - // tauri::async_runtime::spawn(async move { - // log_if_err!(Service::patch_config(info, mapping.to_owned()).await); - - // // update tray - // let handle = handle.lock(); - // handle.refresh_clash(); - // log_if_err!(handle.update_systray_part()); - // }); - - // Ok(()) - // } - - // /// activate the profile - // /// auto activate enhanced profile - // /// 触发clash配置更新 - // pub fn activate(&self) -> Result<()> { - // let global = Data::global(); - - // let verge = global.verge.lock(); - // let clash = global.clash.lock(); - // let profiles = global.profiles.lock(); - - // let tun_mode = verge.enable_tun_mode.clone().unwrap_or(false); - // let profile_activate = profiles.gen_activate()?; - - // let clash_config = clash.config.clone(); - // let clash_info = clash.info.clone(); - - // drop(clash); - // drop(verge); - // drop(profiles); - - // let (config, exists_keys, logs) = enhance_config( - // clash_config, - // profile_activate.current, - // profile_activate.chain, - // profile_activate.valid, - // tun_mode, - // ); - - // let mut runtime = self.runtime.lock(); - // *runtime = RuntimeResult { - // config: Some(config.clone()), - // config_yaml: Some(serde_yaml::to_string(&config).unwrap_or("".into())), - // exists_keys, - // chain_logs: logs, - // }; - // drop(runtime); - - // let mut service = self.service.lock(); - // service.check_start()?; - // drop(service); - - // let handle = self.handle.clone(); - // tauri::async_runtime::spawn(async move { - // match Service::set_config(clash_info, config).await { - // Ok(_) => { - // let handle = handle.lock(); - // handle.refresh_clash(); - // handle.notice_message("set_config::ok".into(), "ok".into()); - // } - // Err(err) => { - // let handle = handle.lock(); - // handle.notice_message("set_config::error".into(), format!("{err}")); - // log::error!(target: "app", "{err}") - // } - // } - // }); - - // Ok(()) - // } - - // /// Static function - // /// update profile item - // pub async fn update_profile_item(&self, uid: String, option: Option) -> Result<()> { - // let global = Data::global(); - - // let (url, opt) = { - // let profiles = global.profiles.lock(); - // let item = profiles.get_item(&uid)?; - - // if let Some(typ) = item.itype.as_ref() { - // // maybe only valid for `local` profile - // if *typ != "remote" { - // // reactivate the config - // if Some(uid) == profiles.get_current() { - // drop(profiles); - // self.activate()?; - // } - // return Ok(()); - // } - // } - // if item.url.is_none() { - // bail!("failed to get the profile item url"); - // } - // (item.url.clone().unwrap(), item.option.clone()) - // }; - - // let merged_opt = PrfOption::merge(opt, option); - // let item = PrfItem::from_url(&url, None, None, merged_opt).await?; - - // let mut profiles = global.profiles.lock(); - // profiles.update_item(uid.clone(), item)?; - - // // reactivate the profile - // if Some(uid) == profiles.get_current() { - // drop(profiles); - // self.activate()?; - // } - - // Ok(()) - // } -} diff --git a/src-tauri/src/core/win_service.rs b/src-tauri/src/core/win_service.rs index b0ddb8f..3120474 100644 --- a/src-tauri/src/core/win_service.rs +++ b/src-tauri/src/core/win_service.rs @@ -91,49 +91,6 @@ pub async fn uninstall_service() -> Result<()> { Ok(()) } -/// start service -/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 -#[deprecated] -pub async fn start_service() -> Result<()> { - let token = Token::with_current_process()?; - let level = token.privilege_level()?; - - let args = ["start", SERVICE_NAME]; - - let status = match level { - PrivilegeLevel::NotPrivileged => RunasCommand::new("sc").args(&args).status()?, - _ => StdCommand::new("sc").args(&args).status()?, - }; - - match status.success() { - true => Ok(()), - false => bail!( - "failed to start service with status {}", - status.code().unwrap() - ), - } -} - -/// stop service -pub async fn stop_service() -> Result<()> { - let url = format!("{SERVICE_URL}/stop_service"); - let res = reqwest::ClientBuilder::new() - .no_proxy() - .build()? - .post(url) - .send() - .await? - .json::() - .await - .context("failed to connect to the Clash Verge Service")?; - - if res.code != 0 { - bail!(res.msg); - } - - Ok(()) -} - /// check the windows service status pub async fn check_service() -> Result { let url = format!("{SERVICE_URL}/get_clash"); diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 1c89859..f78c4e8 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -63,8 +63,6 @@ fn main() -> std::io::Result<()> { cmds::read_profile_file, cmds::save_profile_file, // service mode - cmds::service::start_service, - cmds::service::stop_service, cmds::service::check_service, cmds::service::install_service, cmds::service::uninstall_service, diff --git a/src/services/cmds.ts b/src/services/cmds.ts index 9ab45fe..2203be6 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -158,14 +158,6 @@ export async function openWebUrl(url: string) { /// service mode -export async function startService() { - return invoke("start_service"); -} - -export async function stopService() { - return invoke("stop_service"); -} - export async function checkService() { try { const result = await invoke("check_service"); From 5a35c5b92850db9fa632fac97e63360ac4fc6dbd Mon Sep 17 00:00:00 2001 From: GyDi Date: Thu, 17 Nov 2022 22:53:41 +0800 Subject: [PATCH 11/17] refactor: fix --- src-tauri/src/feat.rs | 7 ++++++- src-tauri/src/utils/resolve.rs | 14 +++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/feat.rs b/src-tauri/src/feat.rs index 4436664..834c1bd 100644 --- a/src-tauri/src/feat.rs +++ b/src-tauri/src/feat.rs @@ -153,7 +153,12 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> { }; // 激活配置 - handle_activate().await?; + if mixed_port.is_some() + || patch.get("secret").is_some() + || patch.get("external-controller").is_some() + { + handle_activate().await?; + } // 更新系统代理 if mixed_port.is_some() { diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index 37c258c..9e6792e 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -60,31 +60,31 @@ pub fn create_window(app_handle: &AppHandle) { #[cfg(target_os = "windows")] { - use crate::utils::winhelp; use std::time::Duration; use tokio::time::sleep; use window_shadows::set_shadow; - use window_vibrancy::apply_blur; match builder .decorations(false) .transparent(true) .inner_size(800.0, 636.0) + .visible(false) .build() { Ok(_) => { let app_handle = app_handle.clone(); + if let Some(window) = app_handle.get_window("main") { + let _ = set_shadow(&window, true); + } + tauri::async_runtime::spawn(async move { sleep(Duration::from_secs(1)).await; if let Some(window) = app_handle.get_window("main") { let _ = window.show(); - let _ = set_shadow(&window, true); - - if !winhelp::is_win11() { - let _ = apply_blur(&window, None); - } + let _ = window.unminimize(); + let _ = window.set_focus(); } }); } From f24cbb6692b2576001ef204834dfc1bcdff4e46e Mon Sep 17 00:00:00 2001 From: GyDi Date: Thu, 17 Nov 2022 23:21:13 +0800 Subject: [PATCH 12/17] refactor: rm code --- src-tauri/src/config/profiles.rs | 5 +- src-tauri/src/core/service.rs | 537 ------------------------------ src-tauri/src/core/win_service.rs | 2 - src-tauri/src/utils/dirs.rs | 7 +- src-tauri/src/utils/mod.rs | 2 +- 5 files changed, 5 insertions(+), 548 deletions(-) delete mode 100644 src-tauri/src/core/service.rs diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index fe7f4c0..27f9067 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -77,7 +77,7 @@ impl IProfiles { if items.iter().find(|&each| each.uid == some_uid).is_some() { self.current = some_uid; - return self.save_file(); // todo remove + return Ok(()); } bail!("invalid uid \"{uid}\""); @@ -102,8 +102,7 @@ impl IProfiles { /// 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(); + if let Some(items) = self.items.as_ref() { let some_uid = Some(uid.clone()); for each in items.iter() { diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs deleted file mode 100644 index 36c5fae..0000000 --- a/src-tauri/src/core/service.rs +++ /dev/null @@ -1,537 +0,0 @@ -use crate::data::{ClashInfo, Data}; -use crate::log_if_err; -use crate::utils::{config, dirs}; -use anyhow::{bail, Result}; -use parking_lot::RwLock; -use reqwest::header::HeaderMap; -use serde_yaml::Mapping; -use std::fs; -use std::io::Write; -use std::sync::Arc; -use std::{ - collections::{HashMap, VecDeque}, - time::Duration, -}; -use tauri::api::process::{Command, CommandChild, CommandEvent}; -use tokio::time::sleep; - -const LOGS_QUEUE_LEN: usize = 100; - -#[derive(Debug)] -pub struct Service { - sidecar: Option, - - logs: Arc>>, - - #[allow(unused)] - use_service_mode: bool, -} - -impl Service { - pub fn new() -> Service { - let queue = VecDeque::with_capacity(LOGS_QUEUE_LEN + 10); - - Service { - sidecar: None, - logs: Arc::new(RwLock::new(queue)), - use_service_mode: false, - } - } - - pub fn start(&mut self) -> Result<()> { - #[cfg(not(target_os = "windows"))] - self.start_clash_by_sidecar()?; - - #[cfg(target_os = "windows")] - { - let enable = { - let data = Data::global(); - let verge = data.verge.lock(); - verge.enable_service_mode.clone().unwrap_or(false) - }; - - self.use_service_mode = enable; - - if !enable { - return self.start_clash_by_sidecar(); - } - - tauri::async_runtime::spawn(async move { - match Self::check_service().await { - Ok(status) => { - // 未启动clash - if status.code != 0 { - log_if_err!(Self::start_clash_by_service().await); - } - } - Err(err) => log::error!(target: "app", "{err}"), - } - }); - } - - Ok(()) - } - - pub fn stop(&mut self) -> Result<()> { - #[cfg(not(target_os = "windows"))] - self.stop_clash_by_sidecar()?; - - #[cfg(target_os = "windows")] - { - let _ = self.stop_clash_by_sidecar(); - - if self.use_service_mode { - tauri::async_runtime::block_on(async move { - log_if_err!(Self::stop_clash_by_service().await); - }); - } - } - - Ok(()) - } - - pub fn restart(&mut self) -> Result<()> { - self.stop()?; - self.start() - } - - pub fn get_logs(&self) -> VecDeque { - self.logs.read().clone() - } - - #[allow(unused)] - pub fn set_logs(&self, text: String) { - let mut logs = self.logs.write(); - if logs.len() > LOGS_QUEUE_LEN { - (*logs).pop_front(); - } - (*logs).push_back(text); - } - - pub fn clear_logs(&self) { - let mut logs = self.logs.write(); - (*logs).clear(); - } - - /// start the clash sidecar - fn start_clash_by_sidecar(&mut self) -> Result<()> { - if self.sidecar.is_some() { - let sidecar = self.sidecar.take().unwrap(); - let _ = sidecar.kill(); - } - - let clash_core: String = { - let global = Data::global(); - let verge = global.verge.lock(); - verge.clash_core.clone().unwrap_or("clash".into()) - }; - - let app_dir = dirs::app_home_dir(); - let app_dir = app_dir.as_os_str().to_str().unwrap(); - - // fix #212 - let args = match clash_core.as_str() { - "clash-meta" => vec!["-m", "-d", app_dir], - _ => vec!["-d", app_dir], - }; - - let cmd = Command::new_sidecar(clash_core)?; - - let (mut rx, cmd_child) = cmd.args(args).spawn()?; - - // 将pid写入文件中 - let pid = cmd_child.pid(); - log_if_err!(|| -> Result<()> { - let path = dirs::clash_pid_path(); - fs::File::create(path)?.write(format!("{pid}").as_bytes())?; - Ok(()) - }()); - - self.sidecar = Some(cmd_child); - - // clash log - let logs = self.logs.clone(); - tauri::async_runtime::spawn(async move { - let write_log = |text: String| { - let mut logs = logs.write(); - if logs.len() >= LOGS_QUEUE_LEN { - (*logs).pop_front(); - } - (*logs).push_back(text); - }; - - while let Some(event) = rx.recv().await { - match event { - CommandEvent::Stdout(line) => { - let can_short = line.starts_with("time=") && line.len() > 33; - let stdout = if can_short { &line[33..] } else { &line }; - log::info!(target: "app" ,"[clash]: {}", stdout); - write_log(line); - } - CommandEvent::Stderr(err) => { - log::error!(target: "app" ,"[clash error]: {}", err); - write_log(err); - } - CommandEvent::Error(err) => log::error!(target: "app" ,"{err}"), - CommandEvent::Terminated(_) => break, - _ => {} - } - } - }); - - Ok(()) - } - - /// stop the clash sidecar - fn stop_clash_by_sidecar(&mut self) -> Result<()> { - if let Some(sidecar) = self.sidecar.take() { - sidecar.kill()?; - } - Ok(()) - } - - pub fn check_start(&mut self) -> Result<()> { - #[cfg(target_os = "windows")] - { - let global = Data::global(); - let verge = global.verge.lock(); - let service_mode = verge.enable_service_mode.unwrap_or(false); - - if !service_mode && self.sidecar.is_none() { - self.start()?; - } - } - - #[cfg(not(target_os = "windows"))] - if self.sidecar.is_none() { - self.start()?; - } - - Ok(()) - } - - /// update clash config - /// using PUT methods - pub async fn set_config(info: ClashInfo, config: Mapping) -> Result<()> { - let temp_path = dirs::clash_runtime_yaml(); - config::save_yaml(temp_path.clone(), &config, Some("# Clash Verge Temp File"))?; - - let (server, headers) = Self::clash_client_info(info)?; - - let mut data = HashMap::new(); - data.insert("path", temp_path.as_os_str().to_str().unwrap()); - - macro_rules! report_err { - ($i: expr, $e: expr) => { - match $i { - 4 => bail!($e), - _ => log::error!(target: "app", $e), - } - }; - } - - // retry 5 times - for i in 0..5 { - let headers = headers.clone(); - match reqwest::ClientBuilder::new().no_proxy().build() { - Ok(client) => { - let builder = client.put(&server).headers(headers).json(&data); - match builder.send().await { - Ok(resp) => match resp.status().as_u16() { - 204 => break, - // 配置有问题不重试 - 400 => bail!("failed to update clash config with status 400"), - status @ _ => { - report_err!(i, "failed to activate clash with status \"{status}\"") - } - }, - Err(err) => report_err!(i, "{err}"), - } - } - Err(err) => report_err!(i, "{err}"), - } - sleep(Duration::from_millis(500)).await; - } - - Ok(()) - } - - /// patch clash config - pub async fn patch_config(info: ClashInfo, config: Mapping) -> Result<()> { - let (server, headers) = Self::clash_client_info(info)?; - - let client = reqwest::ClientBuilder::new().no_proxy().build()?; - let builder = client.patch(&server).headers(headers.clone()).json(&config); - builder.send().await?; - Ok(()) - } - - /// get clash client url and headers from clash info - fn clash_client_info(info: ClashInfo) -> Result<(String, HeaderMap)> { - if info.server.is_none() { - let status = &info.status; - if info.port.is_none() { - bail!("failed to parse config.yaml file with status {status}"); - } else { - bail!("failed to parse the server with status {status}"); - } - } - - let server = info.server.unwrap(); - let server = format!("http://{server}/configs"); - - let mut headers = HeaderMap::new(); - headers.insert("Content-Type", "application/json".parse().unwrap()); - - if let Some(secret) = info.secret.as_ref() { - let secret = format!("Bearer {}", secret.clone()).parse().unwrap(); - headers.insert("Authorization", secret); - } - - Ok((server, headers)) - } - - /// kill old clash process - pub fn kill_old_clash() { - use sysinfo::{Pid, PidExt, ProcessExt, System, SystemExt}; - - if let Ok(pid) = fs::read(dirs::clash_pid_path()) { - if let Ok(pid) = String::from_utf8_lossy(&pid).parse() { - let mut system = System::new(); - system.refresh_all(); - - let proc = system.process(Pid::from_u32(pid)); - if let Some(proc) = proc { - proc.kill(); - } - } - } - } -} - -impl Drop for Service { - fn drop(&mut self) { - log_if_err!(self.stop()); - } -} - -/// ### Service Mode -/// -#[cfg(target_os = "windows")] -pub mod win_service { - use super::*; - use anyhow::Context; - use deelevate::{PrivilegeLevel, Token}; - use runas::Command as RunasCommand; - use serde::{Deserialize, Serialize}; - use std::os::windows::process::CommandExt; - use std::{env::current_exe, process::Command as StdCommand}; - - const SERVICE_NAME: &str = "clash_verge_service"; - - const SERVICE_URL: &str = "http://127.0.0.1:33211"; - - #[derive(Debug, Deserialize, Serialize, Clone)] - pub struct ResponseBody { - pub bin_path: String, - pub config_dir: String, - pub log_file: String, - } - - #[derive(Debug, Deserialize, Serialize, Clone)] - pub struct JsonResponse { - pub code: u64, - pub msg: String, - pub data: Option, - } - - impl Service { - /// Install the Clash Verge Service - /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 - pub async fn install_service() -> Result<()> { - let binary_path = dirs::service_path(); - let install_path = binary_path.with_file_name("install-service.exe"); - - if !install_path.exists() { - bail!("installer exe not found"); - } - - let token = Token::with_current_process()?; - let level = token.privilege_level()?; - - let status = match level { - PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).status()?, - _ => StdCommand::new(install_path) - .creation_flags(0x08000000) - .status()?, - }; - - if !status.success() { - bail!( - "failed to install service with status {}", - status.code().unwrap() - ); - } - - Ok(()) - } - - /// Uninstall the Clash Verge Service - /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 - pub async fn uninstall_service() -> Result<()> { - let binary_path = dirs::service_path(); - let uninstall_path = binary_path.with_file_name("uninstall-service.exe"); - - if !uninstall_path.exists() { - bail!("uninstaller exe not found"); - } - - let token = Token::with_current_process()?; - let level = token.privilege_level()?; - - let status = match level { - PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).status()?, - _ => StdCommand::new(uninstall_path) - .creation_flags(0x08000000) - .status()?, - }; - - if !status.success() { - bail!( - "failed to uninstall service with status {}", - status.code().unwrap() - ); - } - - Ok(()) - } - - /// [deprecated] - /// start service - /// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程 - pub async fn start_service() -> Result<()> { - let token = Token::with_current_process()?; - let level = token.privilege_level()?; - - let args = ["start", SERVICE_NAME]; - - let status = match level { - PrivilegeLevel::NotPrivileged => RunasCommand::new("sc").args(&args).status()?, - _ => StdCommand::new("sc").args(&args).status()?, - }; - - match status.success() { - true => Ok(()), - false => bail!( - "failed to start service with status {}", - status.code().unwrap() - ), - } - } - - /// stop service - pub async fn stop_service() -> Result<()> { - let url = format!("{SERVICE_URL}/stop_service"); - let res = reqwest::ClientBuilder::new() - .no_proxy() - .build()? - .post(url) - .send() - .await? - .json::() - .await - .context("failed to connect to the Clash Verge Service")?; - - if res.code != 0 { - bail!(res.msg); - } - - Ok(()) - } - - /// check the windows service status - pub async fn check_service() -> Result { - let url = format!("{SERVICE_URL}/get_clash"); - let response = reqwest::ClientBuilder::new() - .no_proxy() - .build()? - .get(url) - .send() - .await? - .json::() - .await - .context("failed to connect to the Clash Verge Service")?; - - Ok(response) - } - - /// start the clash by service - pub(super) async fn start_clash_by_service() -> Result<()> { - let status = Self::check_service().await?; - - if status.code == 0 { - Self::stop_clash_by_service().await?; - sleep(Duration::from_secs(1)).await; - } - - let clash_core = { - let global = Data::global(); - let verge = global.verge.lock(); - verge.clash_core.clone().unwrap_or("clash".into()) - }; - - let clash_bin = format!("{clash_core}.exe"); - let bin_path = current_exe().unwrap().with_file_name(clash_bin); - let bin_path = bin_path.as_os_str().to_str().unwrap(); - - let config_dir = dirs::app_home_dir(); - let config_dir = config_dir.as_os_str().to_str().unwrap(); - - let log_path = dirs::service_log_file(); - let log_path = log_path.as_os_str().to_str().unwrap(); - - let mut map = HashMap::new(); - map.insert("bin_path", bin_path); - map.insert("config_dir", config_dir); - map.insert("log_file", log_path); - - let url = format!("{SERVICE_URL}/start_clash"); - let res = reqwest::ClientBuilder::new() - .no_proxy() - .build()? - .post(url) - .json(&map) - .send() - .await? - .json::() - .await - .context("failed to connect to the Clash Verge Service")?; - - if res.code != 0 { - bail!(res.msg); - } - - Ok(()) - } - - /// stop the clash by service - pub(super) async fn stop_clash_by_service() -> Result<()> { - let url = format!("{SERVICE_URL}/stop_clash"); - let res = reqwest::ClientBuilder::new() - .no_proxy() - .build()? - .post(url) - .send() - .await? - .json::() - .await - .context("failed to connect to the Clash Verge Service")?; - - if res.code != 0 { - bail!(res.msg); - } - - Ok(()) - } - } -} diff --git a/src-tauri/src/core/win_service.rs b/src-tauri/src/core/win_service.rs index 3120474..b4ef1a1 100644 --- a/src-tauri/src/core/win_service.rs +++ b/src-tauri/src/core/win_service.rs @@ -12,8 +12,6 @@ use std::time::Duration; use std::{env::current_exe, process::Command as StdCommand}; use tokio::time::sleep; -const SERVICE_NAME: &str = "clash_verge_service"; - const SERVICE_URL: &str = "http://127.0.0.1:33211"; #[derive(Debug, Deserialize, Serialize, Clone)] diff --git a/src-tauri/src/utils/dirs.rs b/src-tauri/src/utils/dirs.rs index 8cb322d..4eb56c5 100644 --- a/src-tauri/src/utils/dirs.rs +++ b/src-tauri/src/utils/dirs.rs @@ -108,14 +108,11 @@ pub fn clash_pid_path() -> PathBuf { unsafe { RESOURCE_DIR.clone().unwrap().join("clash.pid") } } -#[cfg(windows)] -static SERVICE_PATH: &str = "clash-verge-service.exe"; - #[cfg(windows)] pub fn service_path() -> PathBuf { unsafe { let res_dir = RESOURCE_DIR.clone().unwrap(); - res_dir.join(SERVICE_PATH) + res_dir.join("clash-verge-service.exe") } } @@ -134,7 +131,7 @@ pub fn service_log_file() -> PathBuf { log_file } -pub fn path_to_str(path: &PathBuf) -> anyhow::Result<&str> { +pub fn path_to_str(path: &PathBuf) -> Result<&str> { let path_str = path .as_os_str() .to_str() diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index e6c37a8..8fb66eb 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -5,4 +5,4 @@ pub mod init; pub mod resolve; pub mod server; pub mod tmpl; -mod winhelp; +// mod winhelp; From 4ae00714d252558490810278a8f8c6f22351726a Mon Sep 17 00:00:00 2001 From: GyDi Date: Fri, 18 Nov 2022 07:59:53 +0800 Subject: [PATCH 13/17] fix: change template --- src-tauri/src/utils/tmpl.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/utils/tmpl.rs b/src-tauri/src/utils/tmpl.rs index 8d20a2e..69aa805 100644 --- a/src-tauri/src/utils/tmpl.rs +++ b/src-tauri/src/utils/tmpl.rs @@ -15,20 +15,26 @@ secret: "" pub const PROFILES_CONFIG: &[u8] = b"# Profiles Config for Clash Verge current: ~ +chain: ~ +valid: + - dns items: ~ "; /// template for `verge.yaml` pub const VERGE_CONFIG: &[u8] = b"# Default Config For Clash Verge +clash_core: clash language: en -theme_mode: light +theme_mode: system theme_blur: false traffic_graph: true -enable_self_startup: false +enable_auto_launch: false +enable_silent_start: false enable_system_proxy: false enable_proxy_guard: false proxy_guard_duration: 10 +auto_close_connection: true "; /// template for new a profile item @@ -60,9 +66,7 @@ append-proxy-groups: /// enhanced profile pub const ITEM_SCRIPT: &str = "// Define the `main` function -// The argument to this function is the clash config -// or the result of the previous handler -// so you should return the config after processing + function main(params) { return params; } From be81cd72afc8381fa7cc8696239cd4a7767d3311 Mon Sep 17 00:00:00 2001 From: GyDi Date: Fri, 18 Nov 2022 08:24:27 +0800 Subject: [PATCH 14/17] fix: adjust singleton detect --- src-tauri/src/utils/server.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/utils/server.rs b/src-tauri/src/utils/server.rs index 1d2ff59..f4e9836 100644 --- a/src-tauri/src/utils/server.rs +++ b/src-tauri/src/utils/server.rs @@ -2,19 +2,26 @@ extern crate warp; use super::resolve; use crate::config::IVerge; +use anyhow::{bail, Result}; use port_scanner::local_port_available; use tauri::AppHandle; use warp::Filter; /// check whether there is already exists -pub fn check_singleton() -> Result<(), ()> { +pub fn check_singleton() -> Result<()> { let port = IVerge::get_singleton_port(); if !local_port_available(port) { tauri::async_runtime::block_on(async { - let url = format!("http://127.0.0.1:{}/commands/visible", port); - reqwest::get(url).await.unwrap(); - Err(()) + let url = format!("http://127.0.0.1:{port}/commands/visible"); + let resp = reqwest::get(url).await?.text().await?; + + if &resp == "ok" { + bail!("app exists"); + } + + log::error!("failed to setup singleton listen server"); + Ok(()) }) } else { Ok(()) @@ -24,7 +31,6 @@ pub fn check_singleton() -> Result<(), ()> { /// The embed server only be used to implement singleton process /// maybe it can be used as pac server later pub fn embed_server(app_handle: AppHandle) { - let app_handle = app_handle.clone(); let port = IVerge::get_singleton_port(); tauri::async_runtime::spawn(async move { From 34daffbc966755568153130052c7d3219847e770 Mon Sep 17 00:00:00 2001 From: GyDi Date: Fri, 18 Nov 2022 09:35:05 +0800 Subject: [PATCH 15/17] refactor: adjust all path methods and reduce unwrap --- src-tauri/src/cmds.rs | 6 +-- src-tauri/src/config/clash.rs | 23 +++++++++- src-tauri/src/config/prfitem.rs | 10 ++--- src-tauri/src/config/profiles.rs | 72 +++++++++++++++++-------------- src-tauri/src/config/verge.rs | 34 ++++++++++++--- src-tauri/src/core/clash_api.rs | 2 +- src-tauri/src/core/core.rs | 17 ++++---- src-tauri/src/utils/config.rs | 51 +++++++++------------- src-tauri/src/utils/dirs.rs | 73 ++++++++++++++++++++------------ src-tauri/src/utils/init.rs | 60 +++++++++++++------------- src-tauri/src/utils/tmpl.rs | 37 ---------------- 11 files changed, 204 insertions(+), 181 deletions(-) diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index 2dbb5e4..c96e90e 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -121,7 +121,7 @@ pub fn view_profile(index: String) -> CmdResult { .ok_or("the file field is null") }?; - let path = dirs::app_profiles_dir().join(file); + let path = wrap_err!(dirs::app_profiles_dir())?.join(file); if !path.exists() { ret_err!("the file not found"); } @@ -236,13 +236,13 @@ pub fn get_clash_logs() -> CmdResult> { #[tauri::command] pub fn open_app_dir() -> CmdResult<()> { - let app_dir = dirs::app_home_dir(); + let app_dir = wrap_err!(dirs::app_home_dir())?; wrap_err!(open::that(app_dir)) } #[tauri::command] pub fn open_logs_dir() -> CmdResult<()> { - let log_dir = dirs::app_logs_dir(); + let log_dir = wrap_err!(dirs::app_logs_dir())?; wrap_err!(open::that(log_dir)) } diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index d590383..e864d48 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -8,7 +8,26 @@ pub struct IClashTemp(pub Mapping); impl IClashTemp { pub fn new() -> Self { - Self(config::read_merge_mapping(dirs::clash_path())) + match dirs::clash_path().and_then(|path| config::read_merge_mapping(&path)) { + Ok(map) => Self(map), + Err(err) => { + log::error!(target: "app", "{err}"); + Self::template() + } + } + } + + pub fn template() -> Self { + let mut map = Mapping::new(); + + map.insert("mixed-port".into(), 7892.into()); + map.insert("log-level".into(), "info".into()); + map.insert("allow-lan".into(), false.into()); + map.insert("mode".into(), "rule".into()); + map.insert("external-controller".into(), "127.0.0.1:9090".into()); + map.insert("secret".into(), "".into()); + + Self(map) } pub fn patch_config(&mut self, patch: Mapping) { @@ -19,7 +38,7 @@ impl IClashTemp { pub fn save_config(&self) -> Result<()> { config::save_yaml( - dirs::clash_path(), + dirs::clash_path()?, &self.0, Some("# Default Config For ClashN Core\n\n"), ) diff --git a/src-tauri/src/config/prfitem.rs b/src-tauri/src/config/prfitem.rs index cb81212..b2e6444 100644 --- a/src-tauri/src/config/prfitem.rs +++ b/src-tauri/src/config/prfitem.rs @@ -355,7 +355,7 @@ impl PrfItem { } let file = self.file.clone().unwrap(); - let path = dirs::app_profiles_dir().join(file); + let path = dirs::app_profiles_dir()?.join(file); fs::read_to_string(path).context("failed to read the file") } @@ -366,7 +366,7 @@ impl PrfItem { } let file = self.file.clone().unwrap(); - let path = dirs::app_profiles_dir().join(file); + let path = dirs::app_profiles_dir()?.join(file); fs::write(path, data.as_bytes()).context("failed to save the file") } @@ -375,7 +375,7 @@ impl PrfItem { 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); + let path = dirs::app_profiles_dir().ok()?.join(file); if !path.exists() { return None; @@ -384,11 +384,11 @@ impl PrfItem { match itype { "script" => Some(ChainItem { uid, - data: ChainType::Script(fs::read_to_string(path).unwrap_or("".into())), + data: ChainType::Script(fs::read_to_string(path).ok()?), }), "merge" => Some(ChainItem { uid, - data: ChainType::Merge(config::read_merge_mapping(path)), + data: ChainType::Merge(config::read_merge_mapping(&path).ok()?), }), _ => None, } diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index 27f9067..847e233 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -32,30 +32,40 @@ macro_rules! patch { impl IProfiles { pub fn new() -> Self { - Self::read_file() + match dirs::profiles_path().and_then(|path| config::read_yaml::(&path)) { + Ok(mut profiles) => { + if profiles.items.is_none() { + profiles.items = Some(vec![]); + } + // compatible 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 + } + Err(err) => { + log::error!(target: "app", "{err}"); + Self::template() + } + } } - /// read the config from the file - pub fn read_file() -> Self { - let mut profiles = config::read_yaml::(dirs::profiles_path()); - if profiles.items.is_none() { - profiles.items = Some(vec![]); + pub fn template() -> Self { + Self { + valid: Some(vec!["dns".into()]), + items: Some(vec![]), + ..Self::default() } - // compatible 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(), + dirs::profiles_path()?, self, Some("# Profiles Config for Clash Verge\n\n"), ) @@ -131,7 +141,7 @@ impl IProfiles { } let file = item.file.clone().unwrap(); - let path = dirs::app_profiles_dir().join(&file); + let path = dirs::app_profiles_dir()?.join(&file); fs::File::create(path) .context(format!("failed to create file \"{}\"", file))? @@ -200,7 +210,7 @@ impl IProfiles { // the file must exists each.file = Some(file.clone()); - let path = dirs::app_profiles_dir().join(&file); + let path = dirs::app_profiles_dir()?.join(&file); fs::File::create(path) .context(format!("failed to create file \"{}\"", file))? @@ -235,10 +245,12 @@ impl IProfiles { 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); - } + let _ = dirs::app_profiles_dir().map(|path| { + let path = path.join(file); + if path.exists() { + let _ = fs::remove_file(path); + } + }); }); } @@ -263,22 +275,18 @@ impl IProfiles { return Ok(config); } - let current = self.current.clone().unwrap(); + let current = self.current.as_ref().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), + if item.uid.as_ref() == Some(current) { + let file_path = match item.file.as_ref() { + 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())); + return Ok(config::read_merge_mapping(&file_path)?); } } - bail!("failed to find current profile \"uid:{current}\""); + bail!("failed to find the current profile \"uid:{current}\""); } /// generate the data for activate clash config diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index c5986a9..8c21cfe 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -85,13 +85,36 @@ pub struct IVergeTheme { impl IVerge { pub fn new() -> Self { - config::read_yaml::(dirs::verge_path()) + match dirs::verge_path().and_then(|path| config::read_yaml::(&path)) { + Ok(config) => config, + Err(err) => { + log::error!(target: "app", "{err}"); + Self::template() + } + } + } + + pub fn template() -> Self { + Self { + clash_core: Some("clash".into()), + language: Some("en".into()), + theme_mode: Some("system".into()), + theme_blur: Some(false), + traffic_graph: Some(true), + enable_auto_launch: Some(false), + enable_silent_start: Some(false), + enable_system_proxy: Some(false), + enable_proxy_guard: Some(false), + proxy_guard_duration: Some(30), + auto_close_connection: Some(true), + ..Self::default() + } } /// Save IVerge App Config pub fn save_file(&self) -> Result<()> { config::save_yaml( - dirs::verge_path(), + dirs::verge_path()?, &self, Some("# The Config for Clash IVerge App\n\n"), ) @@ -133,13 +156,14 @@ impl IVerge { /// 在初始化前尝试拿到单例端口的值 pub fn get_singleton_port() -> u16 { - let config = config::read_yaml::(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) + match dirs::verge_path().and_then(|path| config::read_yaml::(&path)) { + Ok(config) => config.app_singleton_port.unwrap_or(SERVER_PORT), + Err(_) => SERVER_PORT, // 这里就不log错误了 + } } } diff --git a/src-tauri/src/core/clash_api.rs b/src-tauri/src/core/clash_api.rs index 3dd796c..35e55d2 100644 --- a/src-tauri/src/core/clash_api.rs +++ b/src-tauri/src/core/clash_api.rs @@ -9,7 +9,7 @@ pub async fn put_configs() -> Result<()> { let (url, headers) = clash_client_info()?; let url = format!("{url}/configs"); - let runtime_yaml = dirs::clash_runtime_yaml(); + let runtime_yaml = dirs::clash_runtime_yaml()?; let runtime_yaml = dirs::path_to_str(&runtime_yaml)?; let mut data = HashMap::new(); diff --git a/src-tauri/src/core/core.rs b/src-tauri/src/core/core.rs index f9dee69..4011902 100644 --- a/src-tauri/src/core/core.rs +++ b/src-tauri/src/core/core.rs @@ -35,8 +35,10 @@ impl CoreManager { pub fn init(&self) -> Result<()> { // kill old clash process - if let Ok(pid) = fs::read(dirs::clash_pid_path()) { - if let Ok(pid) = String::from_utf8_lossy(&pid).parse() { + let _ = dirs::clash_pid_path() + .and_then(|path| fs::read(path).map(|p| p.to_vec()).context("")) + .and_then(|pid| String::from_utf8_lossy(&pid).parse().context("")) + .map(|pid| { let mut system = System::new(); system.refresh_all(); system.process(Pid::from_u32(pid)).map(|proc| { @@ -44,8 +46,7 @@ impl CoreManager { proc.kill(); } }); - } - } + }); tauri::async_runtime::spawn(async { // 启动clash @@ -61,7 +62,7 @@ impl CoreManager { /// 检查配置是否正确 pub fn check_config(&self) -> Result<()> { - let config_path = dirs::clash_runtime_yaml(); + let config_path = dirs::clash_runtime_yaml()?; let config_path = dirs::path_to_str(&config_path)?; let clash_core = { Config::verge().latest().clash_core.clone() }; @@ -116,7 +117,7 @@ impl CoreManager { let _ = child.kill(); } - let app_dir = dirs::app_home_dir(); + let app_dir = dirs::app_home_dir()?; let app_dir = dirs::path_to_str(&app_dir)?; let clash_core = { Config::verge().latest().clash_core.clone() }; @@ -134,7 +135,7 @@ impl CoreManager { // 将pid写入文件中 crate::log_err!({ let pid = cmd_child.pid(); - let path = dirs::clash_pid_path(); + let path = dirs::clash_pid_path()?; fs::File::create(path) .context("failed to create the pid file")? .write(format!("{pid}").as_bytes()) @@ -236,7 +237,7 @@ impl CoreManager { enhance::enhance_config(clash_config.0, pa.current, pa.chain, pa.valid, tun_mode); // 保存到文件中 - let runtime_path = dirs::clash_runtime_yaml(); + let runtime_path = dirs::clash_runtime_yaml()?; utils::config::save_yaml(runtime_path, &config, Some("# Clash Verge Runtime Config"))?; // 检查配置是否正常 diff --git a/src-tauri/src/utils/config.rs b/src-tauri/src/utils/config.rs index bcb99f6..c1c563a 100644 --- a/src-tauri/src/utils/config.rs +++ b/src-tauri/src/utils/config.rs @@ -1,47 +1,36 @@ -use anyhow::{Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use serde::{de::DeserializeOwned, Serialize}; use serde_yaml::{Mapping, Value}; use std::{fs, path::PathBuf}; /// read data from yaml as struct T -pub fn read_yaml(path: PathBuf) -> T { +pub fn read_yaml(path: &PathBuf) -> Result { if !path.exists() { - log::error!(target: "app", "file not found \"{}\"", path.display()); - return T::default(); + bail!("file not found \"{}\"", path.display()); } - let yaml_str = fs::read_to_string(&path).unwrap_or("".into()); + let yaml_str = fs::read_to_string(&path) + .context(format!("failed to read the file \"{}\"", path.display()))?; - match serde_yaml::from_str::(&yaml_str) { - Ok(val) => val, - Err(_) => { - log::error!(target: "app", "failed to read yaml file \"{}\"", path.display()); - T::default() - } - } + serde_yaml::from_str::(&yaml_str).context(format!( + "failed to read the file with yaml format \"{}\"", + path.display() + )) } /// read mapping from yaml fix #165 -pub fn read_merge_mapping(path: PathBuf) -> Mapping { - let map = Mapping::new(); +pub fn read_merge_mapping(path: &PathBuf) -> Result { + let mut val: Value = read_yaml(path)?; + val.apply_merge() + .context(format!("failed to apply merge \"{}\"", path.display()))?; - if !path.exists() { - log::error!(target: "app", "file not found \"{}\"", path.display()); - return map; - } - - let yaml_str = fs::read_to_string(&path).unwrap_or("".into()); - - match serde_yaml::from_str::(&yaml_str) { - Ok(mut val) => { - crate::log_err!(val.apply_merge()); - val.as_mapping().unwrap_or(&map).to_owned() - } - Err(_) => { - log::error!(target: "app", "failed to read yaml file \"{}\"", path.display()); - map - } - } + Ok(val + .as_mapping() + .ok_or(anyhow!( + "failed to transform to yaml mapping \"{}\"", + path.display() + ))? + .to_owned()) } /// save the data to the file diff --git a/src-tauri/src/utils/dirs.rs b/src-tauri/src/utils/dirs.rs index 4eb56c5..8c32125 100644 --- a/src-tauri/src/utils/dirs.rs +++ b/src-tauri/src/utils/dirs.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use std::path::PathBuf; +use std::{env::temp_dir, path::PathBuf}; use tauri::{ api::path::{home_dir, resource_dir}, Env, PackageInfo, @@ -13,7 +13,6 @@ static APP_DIR: &str = "clash-verge-dev"; static CLASH_CONFIG: &str = "config.yaml"; static VERGE_CONFIG: &str = "verge.yaml"; static PROFILE_YAML: &str = "profiles.yaml"; -static CLASH_RUNTIME_YAML: &str = "clash-verge-runtime.yaml"; static mut RESOURCE_DIR: Option = None; @@ -21,7 +20,7 @@ static mut RESOURCE_DIR: Option = None; #[allow(unused)] static mut PORTABLE_FLAG: bool = false; -pub static mut APP_VERSION: &str = "v1.1.1"; +pub static mut APP_VERSION: &str = "v1.1.2"; /// initialize portable flag #[cfg(target_os = "windows")] @@ -42,29 +41,37 @@ pub unsafe fn init_portable_flag() -> Result<()> { } /// get the verge app home dir -pub fn app_home_dir() -> PathBuf { +pub fn app_home_dir() -> Result { #[cfg(target_os = "windows")] unsafe { use tauri::utils::platform::current_exe; if !PORTABLE_FLAG { - home_dir().unwrap().join(".config").join(APP_DIR) + Ok(home_dir() + .ok_or(anyhow::anyhow!("failed to get app home dir"))? + .join(".config") + .join(APP_DIR)) } else { - let app_exe = current_exe().unwrap(); - let app_exe = dunce::canonicalize(app_exe).unwrap(); - let app_dir = app_exe.parent().unwrap(); - PathBuf::from(app_dir).join(".config").join(APP_DIR) + let app_exe = current_exe()?; + let app_exe = dunce::canonicalize(app_exe)?; + let app_dir = app_exe + .parent() + .ok_or(anyhow::anyhow!("failed to get the portable app dir"))?; + Ok(PathBuf::from(app_dir).join(".config").join(APP_DIR)) } } #[cfg(not(target_os = "windows"))] - home_dir().unwrap().join(".config").join(APP_DIR) + Ok(home_dir() + .ok_or(anyhow::anyhow!("failed to get the app home dir"))? + .join(".config") + .join(APP_DIR)) } /// get the resources dir -pub fn app_resources_dir(package_info: &PackageInfo) -> PathBuf { +pub fn app_resources_dir(package_info: &PackageInfo) -> Result { let res_dir = resource_dir(package_info, &Env::default()) - .unwrap() + .ok_or(anyhow::anyhow!("failed to get the resource dir"))? .join("resources"); unsafe { @@ -75,37 +82,49 @@ pub fn app_resources_dir(package_info: &PackageInfo) -> PathBuf { APP_VERSION = Box::leak(Box::new(ver_str)); } - res_dir + Ok(res_dir) } /// profiles dir -pub fn app_profiles_dir() -> PathBuf { - app_home_dir().join("profiles") +pub fn app_profiles_dir() -> Result { + Ok(app_home_dir()?.join("profiles")) } /// logs dir -pub fn app_logs_dir() -> PathBuf { - app_home_dir().join("logs") +pub fn app_logs_dir() -> Result { + Ok(app_home_dir()?.join("logs")) } -pub fn clash_path() -> PathBuf { - app_home_dir().join(CLASH_CONFIG) +pub fn clash_path() -> Result { + Ok(app_home_dir()?.join(CLASH_CONFIG)) } -pub fn verge_path() -> PathBuf { - app_home_dir().join(VERGE_CONFIG) +pub fn verge_path() -> Result { + Ok(app_home_dir()?.join(VERGE_CONFIG)) } -pub fn profiles_path() -> PathBuf { - app_home_dir().join(PROFILE_YAML) +pub fn profiles_path() -> Result { + Ok(app_home_dir()?.join(PROFILE_YAML)) } -pub fn clash_runtime_yaml() -> PathBuf { - app_home_dir().join(CLASH_RUNTIME_YAML) +pub fn clash_runtime_yaml() -> Result { + Ok(app_home_dir()?.join("clash-verge-runtime.yaml")) } -pub fn clash_pid_path() -> PathBuf { - unsafe { RESOURCE_DIR.clone().unwrap().join("clash.pid") } +pub fn clash_check_yaml() -> Result { + Ok(temp_dir().join("clash-verge-check.yaml")) +} + +pub fn app_res_dir() -> Result { + unsafe { + Ok(RESOURCE_DIR + .clone() + .ok_or(anyhow::anyhow!("failed to get the resource dir"))?) + } +} + +pub fn clash_pid_path() -> Result { + unsafe { Ok(RESOURCE_DIR.clone().unwrap().join("clash.pid")) } } #[cfg(windows)] diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs index 7f6c74a..5f8d2a6 100644 --- a/src-tauri/src/utils/init.rs +++ b/src-tauri/src/utils/init.rs @@ -12,7 +12,7 @@ use tauri::PackageInfo; /// initialize this instance's log file fn init_log() -> Result<()> { - let log_dir = dirs::app_logs_dir(); + let log_dir = dirs::app_logs_dir()?; if !log_dir.exists() { let _ = fs::create_dir_all(&log_dir); } @@ -54,42 +54,40 @@ pub fn init_config() -> Result<()> { let _ = init_log(); - let app_dir = dirs::app_home_dir(); - let profiles_dir = dirs::app_profiles_dir(); + let _ = dirs::app_home_dir().map(|app_dir| { + if !app_dir.exists() { + let _ = fs::create_dir_all(&app_dir); + } - if !app_dir.exists() { - let _ = fs::create_dir_all(&app_dir); - } - if !profiles_dir.exists() { - let _ = fs::create_dir_all(&profiles_dir); - } + // // target path + // let clash_path = app_dir.join("config.yaml"); + // let verge_path = app_dir.join("verge.yaml"); + // let profile_path = app_dir.join("profiles.yaml"); - // target path - let clash_path = app_dir.join("config.yaml"); - let verge_path = app_dir.join("verge.yaml"); - let profile_path = app_dir.join("profiles.yaml"); + // if !clash_path.exists() { + // fs::File::create(clash_path)?.write(tmpl::CLASH_CONFIG)?; + // } + // if !verge_path.exists() { + // fs::File::create(verge_path)?.write(tmpl::VERGE_CONFIG)?; + // } + // if !profile_path.exists() { + // fs::File::create(profile_path)?.write(tmpl::PROFILES_CONFIG)?; + // } + }); + + let _ = dirs::app_profiles_dir().map(|profiles_dir| { + if !profiles_dir.exists() { + let _ = fs::create_dir_all(&profiles_dir); + } + }); - if !clash_path.exists() { - fs::File::create(clash_path)?.write(tmpl::CLASH_CONFIG)?; - } - if !verge_path.exists() { - fs::File::create(verge_path)?.write(tmpl::VERGE_CONFIG)?; - } - if !profile_path.exists() { - fs::File::create(profile_path)?.write(tmpl::PROFILES_CONFIG)?; - } Ok(()) } /// initialize app -pub fn init_resources(package_info: &PackageInfo) { - // create app dir - let app_dir = dirs::app_home_dir(); - let res_dir = dirs::app_resources_dir(package_info); - - if !app_dir.exists() { - let _ = fs::create_dir_all(&app_dir); - } +pub fn init_resources(package_info: &PackageInfo) -> Result<()> { + let app_dir = dirs::app_home_dir()?; + let res_dir = dirs::app_resources_dir(package_info)?; // copy the resource file for file in ["Country.mmdb", "geoip.dat", "geosite.dat", "wintun.dll"].iter() { @@ -99,4 +97,6 @@ pub fn init_resources(package_info: &PackageInfo) { let _ = fs::copy(src_path, target_path); } } + + Ok(()) } diff --git a/src-tauri/src/utils/tmpl.rs b/src-tauri/src/utils/tmpl.rs index 69aa805..ec17c33 100644 --- a/src-tauri/src/utils/tmpl.rs +++ b/src-tauri/src/utils/tmpl.rs @@ -1,42 +1,5 @@ ///! Some config file template -/// template for clash core `config.yaml` -pub const CLASH_CONFIG: &[u8] = br#"# Default Config For Clash Core - -mixed-port: 7890 -log-level: info -allow-lan: false -external-controller: 127.0.0.1:9090 -mode: rule -secret: "" -"#; - -/// template for `profiles.yaml` -pub const PROFILES_CONFIG: &[u8] = b"# Profiles Config for Clash Verge - -current: ~ -chain: ~ -valid: - - dns -items: ~ -"; - -/// template for `verge.yaml` -pub const VERGE_CONFIG: &[u8] = b"# Default Config For Clash Verge - -clash_core: clash -language: en -theme_mode: system -theme_blur: false -traffic_graph: true -enable_auto_launch: false -enable_silent_start: false -enable_system_proxy: false -enable_proxy_guard: false -proxy_guard_duration: 10 -auto_close_connection: true -"; - /// template for new a profile item pub const ITEM_LOCAL: &str = "# Profile Template for clash verge From 2667ed13f15fc10f8a99473e54b7743027aeb9fe Mon Sep 17 00:00:00 2001 From: GyDi Date: Fri, 18 Nov 2022 18:18:41 +0800 Subject: [PATCH 16/17] refactor: done --- src-tauri/src/cmds.rs | 115 ++++++------------ src-tauri/src/config/clash.rs | 12 +- src-tauri/src/config/config.rs | 81 +++++++++++- src-tauri/src/config/draft.rs | 7 +- src-tauri/src/config/mod.rs | 2 + src-tauri/src/config/prfitem.rs | 4 +- src-tauri/src/config/profiles.rs | 57 +++++---- src-tauri/src/config/runtime.rs | 18 +++ src-tauri/src/config/verge.rs | 12 +- src-tauri/src/core/clash_api.rs | 10 +- src-tauri/src/core/core.rs | 79 ++++-------- src-tauri/src/core/timer.rs | 3 - src-tauri/src/feat.rs | 56 +++++---- src-tauri/src/main.rs | 15 +-- src-tauri/src/utils/config.rs | 48 -------- src-tauri/src/utils/dirs.rs | 10 +- src-tauri/src/utils/help.rs | 62 +++++++--- src-tauri/src/utils/init.rs | 41 +++---- src-tauri/src/utils/mod.rs | 1 - src-tauri/src/utils/resolve.rs | 3 +- src/components/profile/enhanced.tsx | 10 +- .../setting/mods/clash-field-viewer.tsx | 4 +- src/pages/profiles.tsx | 14 +-- src/services/cmds.ts | 20 +-- 24 files changed, 343 insertions(+), 341 deletions(-) create mode 100644 src-tauri/src/config/runtime.rs delete mode 100644 src-tauri/src/utils/config.rs diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs index c96e90e..fb05e43 100644 --- a/src-tauri/src/cmds.rs +++ b/src-tauri/src/cmds.rs @@ -5,7 +5,7 @@ use crate::{ utils::{dirs, help}, }; use crate::{ret_err, wrap_err}; -use anyhow::Result; +use anyhow::{Context, Result}; use serde_yaml::Mapping; use std::collections::{HashMap, VecDeque}; use sysproxy::Sysproxy; @@ -19,7 +19,9 @@ pub fn get_profiles() -> CmdResult { #[tauri::command] pub async fn enhance_profiles() -> CmdResult { - wrap_err!(feat::handle_activate().await) + wrap_err!(CoreManager::global().update_config().await)?; + handle::Handle::refresh_clash(); + Ok(()) } #[deprecated] @@ -40,75 +42,41 @@ pub async fn update_profile(index: String, option: Option) -> CmdResu wrap_err!(feat::update_profile(index, option).await) } -#[tauri::command] -pub async fn select_profile(index: String) -> CmdResult { - 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>) -> CmdResult { - 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>) -> CmdResult { - 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!({ Config::profiles().data().delete_item(index) })?; if should_update { - wrap_err!(feat::handle_activate().await)?; + wrap_err!(CoreManager::global().update_config().await)?; + handle::Handle::refresh_clash(); } Ok(()) } +/// 修改profiles的 +#[tauri::command] +pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult { + wrap_err!({ Config::profiles().draft().patch_config(profiles) })?; + + match CoreManager::global().update_config().await { + Ok(_) => { + handle::Handle::refresh_clash(); + Config::profiles().apply(); + wrap_err!(Config::profiles().data().save_file())?; + Ok(()) + } + Err(err) => { + Config::profiles().discard(); + log::error!(target: "app", "{err}"); + Err(format!("{err}")) + } + } +} + +/// 修改某个profile item的 #[tauri::command] pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult { wrap_err!(Config::profiles().data().patch_item(index, profile))?; - wrap_err!(timer::Timer::global().refresh()) } @@ -157,34 +125,29 @@ pub fn get_clash_info() -> CmdResult { #[tauri::command] pub fn get_runtime_config() -> CmdResult> { - Ok(CoreManager::global().runtime_config.lock().config.clone()) + Ok(Config::runtime().latest().config.clone()) } #[tauri::command] -pub fn get_runtime_yaml() -> CmdResult> { - Ok(CoreManager::global() - .runtime_config - .lock() - .config_yaml - .clone()) +pub fn get_runtime_yaml() -> CmdResult { + let runtime = Config::runtime(); + let runtime = runtime.latest(); + let config = runtime.config.as_ref(); + wrap_err!(config + .ok_or(anyhow::anyhow!("failed to parse config to yaml file")) + .and_then( + |config| serde_yaml::to_string(config).context("failed to convert config to yaml") + )) } #[tauri::command] pub fn get_runtime_exists() -> CmdResult> { - Ok(CoreManager::global() - .runtime_config - .lock() - .exists_keys - .clone()) + Ok(Config::runtime().latest().exists_keys.clone()) } #[tauri::command] pub fn get_runtime_logs() -> CmdResult>> { - Ok(CoreManager::global() - .runtime_config - .lock() - .chain_logs - .clone()) + Ok(Config::runtime().latest().chain_logs.clone()) } #[tauri::command] diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index e864d48..549ccfc 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -1,4 +1,4 @@ -use crate::utils::{config, dirs}; +use crate::utils::{dirs, help}; use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_yaml::{Mapping, Value}; @@ -8,7 +8,7 @@ pub struct IClashTemp(pub Mapping); impl IClashTemp { pub fn new() -> Self { - match dirs::clash_path().and_then(|path| config::read_merge_mapping(&path)) { + match dirs::clash_path().and_then(|path| help::read_merge_mapping(&path)) { Ok(map) => Self(map), Err(err) => { log::error!(target: "app", "{err}"); @@ -20,7 +20,7 @@ impl IClashTemp { pub fn template() -> Self { let mut map = Mapping::new(); - map.insert("mixed-port".into(), 7892.into()); + map.insert("mixed-port".into(), 7890.into()); map.insert("log-level".into(), "info".into()); map.insert("allow-lan".into(), false.into()); map.insert("mode".into(), "rule".into()); @@ -37,10 +37,10 @@ impl IClashTemp { } pub fn save_config(&self) -> Result<()> { - config::save_yaml( - dirs::clash_path()?, + help::save_yaml( + &dirs::clash_path()?, &self.0, - Some("# Default Config For ClashN Core\n\n"), + Some("# Generated by Clash Verge"), ) } diff --git a/src-tauri/src/config/config.rs b/src-tauri/src/config/config.rs index 56b7149..1ba7a1e 100644 --- a/src-tauri/src/config/config.rs +++ b/src-tauri/src/config/config.rs @@ -1,10 +1,20 @@ -use super::{Draft, IClashTemp, IProfiles, IVerge}; +use super::{Draft, IClashTemp, IProfiles, IRuntime, IVerge}; +use crate::{ + enhance, + utils::{dirs, help}, +}; +use anyhow::{anyhow, Result}; use once_cell::sync::OnceCell; +use std::{env::temp_dir, path::PathBuf}; + +pub const RUNTIME_CONFIG: &str = "clash-verge.yaml"; +pub const CHECK_CONFIG: &str = "clash-verge-check.yaml"; pub struct Config { clash_config: Draft, verge_config: Draft, profiles_config: Draft, + runtime_config: Draft, } impl Config { @@ -15,6 +25,7 @@ impl Config { clash_config: Draft::from(IClashTemp::new()), verge_config: Draft::from(IVerge::new()), profiles_config: Draft::from(IProfiles::new()), + runtime_config: Draft::from(IRuntime::new()), }) } @@ -29,4 +40,72 @@ impl Config { pub fn profiles() -> Draft { Self::global().profiles_config.clone() } + + pub fn runtime() -> Draft { + Self::global().runtime_config.clone() + } + + /// 初始化配置 + pub fn init_config() -> Result<()> { + crate::log_err!(Self::generate()); + if let Err(err) = Self::generate_file(ConfigType::Run) { + log::error!(target: "app", "{err}"); + + let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG); + // 如果不存在就将默认的clash文件拿过来 + if !runtime_path.exists() { + help::save_yaml( + &runtime_path, + &Config::clash().latest().0, + Some("# Clash Verge Runtime"), + )?; + } + } + Ok(()) + } + + /// 将配置丢到对应的文件中 + pub fn generate_file(typ: ConfigType) -> Result { + let path = match typ { + ConfigType::Run => dirs::app_home_dir()?.join(RUNTIME_CONFIG), + ConfigType::Check => temp_dir().join(CHECK_CONFIG), + }; + + let runtime = Config::runtime(); + let runtime = runtime.latest(); + let config = runtime + .config + .as_ref() + .ok_or(anyhow!("failed to get runtime config"))?; + + help::save_yaml(&path, &config, Some("# Generated by Clash Verge"))?; + Ok(path) + } + + /// 生成配置存好 + pub fn generate() -> Result<()> { + let clash_config = { Config::clash().latest().clone() }; + + let tun_mode = { Config::verge().latest().enable_tun_mode.clone() }; + let tun_mode = tun_mode.unwrap_or(false); + + let pa = { Config::profiles().latest().gen_activate()? }; + + let (config, exists_keys, logs) = + enhance::enhance_config(clash_config.0, pa.current, pa.chain, pa.valid, tun_mode); + + *Config::runtime().draft() = IRuntime { + config: Some(config), + exists_keys, + chain_logs: logs, + }; + + Ok(()) + } +} + +#[derive(Debug)] +pub enum ConfigType { + Run, + Check, } diff --git a/src-tauri/src/config/draft.rs b/src-tauri/src/config/draft.rs index 2f70b28..f1edf67 100644 --- a/src-tauri/src/config/draft.rs +++ b/src-tauri/src/config/draft.rs @@ -1,4 +1,4 @@ -use super::{IClashTemp, IProfiles, IVerge}; +use super::{IClashTemp, IProfiles, IRuntime, IVerge}; use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; use std::sync::Arc; @@ -10,6 +10,7 @@ pub struct Draft { macro_rules! draft_define { ($id: ident) => { impl Draft<$id> { + #[allow(unused)] pub fn data(&self) -> MappedMutexGuard<$id> { MutexGuard::map(self.inner.lock(), |guard| &mut guard.0) } @@ -65,9 +66,9 @@ macro_rules! draft_define { // draft_define!(IClash); draft_define!(IClashTemp); -draft_define!(IVerge); -// draft_define!(Mapping); draft_define!(IProfiles); +draft_define!(IRuntime); +draft_define!(IVerge); #[test] fn test_draft() { diff --git a/src-tauri/src/config/mod.rs b/src-tauri/src/config/mod.rs index 554a4d4..b246a76 100644 --- a/src-tauri/src/config/mod.rs +++ b/src-tauri/src/config/mod.rs @@ -3,6 +3,7 @@ mod config; mod draft; mod prfitem; mod profiles; +mod runtime; mod verge; pub use self::clash::*; @@ -10,4 +11,5 @@ pub use self::config::*; pub use self::draft::*; pub use self::prfitem::*; pub use self::profiles::*; +pub use self::runtime::*; pub use self::verge::*; diff --git a/src-tauri/src/config/prfitem.rs b/src-tauri/src/config/prfitem.rs index b2e6444..664ad05 100644 --- a/src-tauri/src/config/prfitem.rs +++ b/src-tauri/src/config/prfitem.rs @@ -1,4 +1,4 @@ -use crate::utils::{config, dirs, help, tmpl}; +use crate::utils::{dirs, help, tmpl}; use anyhow::{bail, Context, Result}; use reqwest::StatusCode; use serde::{Deserialize, Serialize}; @@ -388,7 +388,7 @@ impl PrfItem { }), "merge" => Some(ChainItem { uid, - data: ChainType::Merge(config::read_merge_mapping(&path).ok()?), + data: ChainType::Merge(help::read_merge_mapping(&path).ok()?), }), _ => None, } diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index 847e233..302b055 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -1,9 +1,8 @@ use super::{prfitem::PrfItem, ChainItem}; -use crate::utils::{config, dirs, help}; +use crate::utils::{dirs, help}; use anyhow::{bail, Context, Result}; use serde::{Deserialize, Serialize}; use serde_yaml::Mapping; -use std::collections::HashMap; use std::{fs, io::Write}; /// Define the `profiles.yaml` schema @@ -32,7 +31,7 @@ macro_rules! patch { impl IProfiles { pub fn new() -> Self { - match dirs::profiles_path().and_then(|path| config::read_yaml::(&path)) { + match dirs::profiles_path().and_then(|path| help::read_yaml::(&path)) { Ok(mut profiles) => { if profiles.items.is_none() { profiles.items = Some(vec![]); @@ -62,21 +61,45 @@ impl IProfiles { } } - /// save the config to the file pub fn save_file(&self) -> Result<()> { - config::save_yaml( - dirs::profiles_path()?, + help::save_yaml( + &dirs::profiles_path()?, self, - Some("# Profiles Config for Clash Verge\n\n"), + Some("# Profiles Config for Clash Verge"), ) } - /// get the current uid + /// 只修改current,valid和chain + pub fn patch_config(&mut self, patch: IProfiles) -> Result<()> { + if self.items.is_none() { + self.items = Some(vec![]); + } + + if let Some(current) = patch.current { + let items = self.items.as_ref().unwrap(); + let some_uid = Some(current); + + if items.iter().any(|e| e.uid == some_uid) { + self.current = some_uid; + } + } + + if let Some(chain) = patch.chain { + self.chain = Some(chain); + } + + if let Some(valid) = patch.valid { + self.valid = Some(valid); + } + + Ok(()) + } + pub fn get_current(&self) -> Option { self.current.clone() } - /// only change the main to the target id + #[deprecated] pub fn put_current(&mut self, uid: String) -> Result<()> { if self.items.is_none() { self.items = Some(vec![]); @@ -93,13 +116,13 @@ impl IProfiles { bail!("invalid uid \"{uid}\""); } - /// just change the `chain` + #[deprecated] pub fn put_chain(&mut self, chain: Option>) -> Result<()> { self.chain = chain; self.save_file() } - /// just change the `field` + #[deprecated] pub fn put_valid(&mut self, valid: Option>) -> Result<()> { self.valid = valid; self.save_file() @@ -283,7 +306,7 @@ impl IProfiles { None => bail!("failed to get the file field"), }; - return Ok(config::read_merge_mapping(&file_path)?); + return Ok(help::read_merge_mapping(&file_path)?); } } bail!("failed to find the current profile \"uid:{current}\""); @@ -316,13 +339,3 @@ pub struct PrfActivate { pub chain: Vec, pub valid: Vec, } - -#[derive(Default, Debug, Clone, Deserialize, Serialize)] -pub struct RuntimeResult { - pub config: Option, - pub config_yaml: Option, - // 记录在配置中(包括merge和script生成的)出现过的keys - // 这些keys不一定都生效 - pub exists_keys: Vec, - pub chain_logs: HashMap>, -} diff --git a/src-tauri/src/config/runtime.rs b/src-tauri/src/config/runtime.rs new file mode 100644 index 0000000..3b67b06 --- /dev/null +++ b/src-tauri/src/config/runtime.rs @@ -0,0 +1,18 @@ +use serde::{Deserialize, Serialize}; +use serde_yaml::Mapping; +use std::collections::HashMap; + +#[derive(Default, Debug, Clone, Deserialize, Serialize)] +pub struct IRuntime { + pub config: Option, + // 记录在配置中(包括merge和script生成的)出现过的keys + // 这些keys不一定都生效 + pub exists_keys: Vec, + pub chain_logs: HashMap>, +} + +impl IRuntime { + pub fn new() -> Self { + Self::default() + } +} diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index 8c21cfe..60143ee 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -1,4 +1,4 @@ -use crate::utils::{config, dirs}; +use crate::utils::{dirs, help}; use anyhow::Result; use serde::{Deserialize, Serialize}; @@ -85,7 +85,7 @@ pub struct IVergeTheme { impl IVerge { pub fn new() -> Self { - match dirs::verge_path().and_then(|path| config::read_yaml::(&path)) { + match dirs::verge_path().and_then(|path| help::read_yaml::(&path)) { Ok(config) => config, Err(err) => { log::error!(target: "app", "{err}"); @@ -113,11 +113,7 @@ impl IVerge { /// Save IVerge App Config pub fn save_file(&self) -> Result<()> { - config::save_yaml( - dirs::verge_path()?, - &self, - Some("# The Config for Clash IVerge App\n\n"), - ) + help::save_yaml(&dirs::verge_path()?, &self, Some("# Clash Verge Config")) } /// patch verge config @@ -161,7 +157,7 @@ impl IVerge { #[cfg(feature = "verge-dev")] const SERVER_PORT: u16 = 11233; - match dirs::verge_path().and_then(|path| config::read_yaml::(&path)) { + match dirs::verge_path().and_then(|path| help::read_yaml::(&path)) { Ok(config) => config.app_singleton_port.unwrap_or(SERVER_PORT), Err(_) => SERVER_PORT, // 这里就不log错误了 } diff --git a/src-tauri/src/core/clash_api.rs b/src-tauri/src/core/clash_api.rs index 35e55d2..cb00db8 100644 --- a/src-tauri/src/core/clash_api.rs +++ b/src-tauri/src/core/clash_api.rs @@ -1,19 +1,17 @@ -use crate::{config::Config, utils::dirs}; +use crate::config::Config; use anyhow::{bail, Result}; use reqwest::header::HeaderMap; use serde_yaml::Mapping; use std::collections::HashMap; /// PUT /configs -pub async fn put_configs() -> Result<()> { +/// path 是绝对路径 +pub async fn put_configs(path: &str) -> Result<()> { let (url, headers) = clash_client_info()?; let url = format!("{url}/configs"); - let runtime_yaml = dirs::clash_runtime_yaml()?; - let runtime_yaml = dirs::path_to_str(&runtime_yaml)?; - let mut data = HashMap::new(); - data.insert("path", runtime_yaml); + data.insert("path", path); let client = reqwest::ClientBuilder::new().no_proxy().build()?; let builder = client.put(&url).headers(headers).json(&data); diff --git a/src-tauri/src/core/core.rs b/src-tauri/src/core/core.rs index 4011902..b45ddf3 100644 --- a/src-tauri/src/core/core.rs +++ b/src-tauri/src/core/core.rs @@ -1,9 +1,6 @@ use super::{clash_api, logger::Logger}; -use crate::{ - config::*, - enhance, log_err, - utils::{self, dirs}, -}; +use crate::log_err; +use crate::{config::*, utils::dirs}; use anyhow::{bail, Context, Result}; use once_cell::sync::OnceCell; use parking_lot::Mutex; @@ -18,8 +15,6 @@ pub struct CoreManager { #[allow(unused)] use_service_mode: Arc>, - - pub runtime_config: Arc>, } impl CoreManager { @@ -28,7 +23,6 @@ impl CoreManager { CORE_MANAGER.get_or_init(|| CoreManager { sidecar: Arc::new(Mutex::new(None)), - runtime_config: Arc::new(Mutex::new(RuntimeResult::default())), use_service_mode: Arc::new(Mutex::new(false)), }) } @@ -50,11 +44,7 @@ impl CoreManager { tauri::async_runtime::spawn(async { // 启动clash - if Self::global().run_core().await.is_ok() { - // 更新配置 - sleep(Duration::from_millis(100)).await; - crate::log_err!(Self::global().activate_config().await); - } + log_err!(Self::global().run_core().await); }); Ok(()) @@ -62,7 +52,7 @@ impl CoreManager { /// 检查配置是否正确 pub fn check_config(&self) -> Result<()> { - let config_path = dirs::clash_runtime_yaml()?; + let config_path = Config::generate_file(ConfigType::Check)?; let config_path = dirs::path_to_str(&config_path)?; let clash_core = { Config::verge().latest().clash_core.clone() }; @@ -82,6 +72,8 @@ impl CoreManager { /// 启动核心 pub async fn run_core(&self) -> Result<()> { + let config_path = Config::generate_file(ConfigType::Run)?; + #[cfg(target_os = "windows")] { use super::win_service; @@ -123,10 +115,12 @@ impl CoreManager { let clash_core = { Config::verge().latest().clash_core.clone() }; let clash_core = clash_core.unwrap_or("clash".into()); + let config_path = dirs::path_to_str(&config_path)?; + // fix #212 let args = match clash_core.as_str() { - "clash-meta" => vec!["-m", "-d", app_dir], - _ => vec!["-d", app_dir], + "clash-meta" => vec!["-m", "-d", app_dir, "-f", config_path], + _ => vec!["-d", app_dir, "-f", config_path], }; let cmd = Command::new_sidecar(clash_core)?; @@ -199,53 +193,42 @@ impl CoreManager { bail!("invalid clash core name \"{clash_core}\""); } + Config::verge().draft().clash_core = Some(clash_core); + // 清掉旧日志 Logger::global().clear_log(); - { - Config::verge().draft().clash_core = Some(clash_core); - } - match self.run_core().await { Ok(_) => { - log_err!({ - Config::verge().apply(); - Config::verge().latest().save_file() - }); - - sleep(Duration::from_millis(100)).await; // 等一会儿再更新配置 - self.activate_config().await?; + Config::verge().apply(); + Config::runtime().apply(); + log_err!(Config::verge().latest().save_file()); Ok(()) } Err(err) => { Config::verge().discard(); + Config::runtime().discard(); Err(err) } } } - /// 激活一个配置 - pub async fn activate_config(&self) -> Result<()> { - let clash_config = { Config::clash().latest().clone() }; - - let tun_mode = { Config::verge().latest().enable_tun_mode.clone() }; - let tun_mode = tun_mode.unwrap_or(false); - - let pa = { Config::profiles().latest().gen_activate()? }; - - let (config, exists_keys, logs) = - enhance::enhance_config(clash_config.0, pa.current, pa.chain, pa.valid, tun_mode); - - // 保存到文件中 - let runtime_path = dirs::clash_runtime_yaml()?; - utils::config::save_yaml(runtime_path, &config, Some("# Clash Verge Runtime Config"))?; + /// 更新proxies那些 + /// 如果涉及端口和外部控制则需要重启 + pub async fn update_config(&self) -> Result<()> { + // 更新配置 + Config::generate()?; // 检查配置是否正常 self.check_config()?; + // 更新运行时配置 + let path = Config::generate_file(ConfigType::Run)?; + let path = dirs::path_to_str(&path)?; + // 发送请求 发送5次 for i in 0..5 { - match clash_api::put_configs().await { + match clash_api::put_configs(path).await { Ok(_) => break, Err(err) => { if i < 4 { @@ -258,16 +241,6 @@ impl CoreManager { sleep(Duration::from_millis(250)).await; } - // 保存结果 - let mut runtime = self.runtime_config.lock(); - let config_yaml = Some(serde_yaml::to_string(&config).unwrap_or("".into())); - *runtime = RuntimeResult { - config: Some(config), - config_yaml, - exists_keys, - chain_logs: logs, - }; - Ok(()) } } diff --git a/src-tauri/src/core/timer.rs b/src-tauri/src/core/timer.rs index 8537367..1b40f0f 100644 --- a/src-tauri/src/core/timer.rs +++ b/src-tauri/src/core/timer.rs @@ -43,9 +43,6 @@ impl Timer { Config::profiles().latest().get_items().map(|items| { items .iter() - // .filter_map(|item| { - // item.uid.is_some() && item.updated.is_some() && item.option.is_some() - // }) .filter_map(|item| { // mins to seconds let interval = ((item.option.as_ref()?.update_interval?) as i64) * 60; diff --git a/src-tauri/src/feat.rs b/src-tauri/src/feat.rs index 834c1bd..7c73631 100644 --- a/src-tauri/src/feat.rs +++ b/src-tauri/src/feat.rs @@ -1,3 +1,9 @@ +//! +//! feat mod 里的函数主要用于 +//! - hotkey 快捷键 +//! - timer 定时器 +//! - cmds 页面调用 +//! use crate::config::*; use crate::core::*; use crate::log_err; @@ -8,8 +14,14 @@ use serde_yaml::{Mapping, Value}; pub fn restart_clash_core() { tauri::async_runtime::spawn(async { match CoreManager::global().run_core().await { - Ok(_) => log_err!(handle_activate().await), - Err(err) => log::error!(target: "app", "{err}"), + Ok(_) => { + handle::Handle::refresh_clash(); + handle::Handle::notice_message("set_config::ok", "ok"); + } + Err(err) => { + handle::Handle::notice_message("set_config::error", format!("{err}")); + log::error!(target:"app", "{err}"); + } } }); } @@ -157,7 +169,7 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> { || patch.get("secret").is_some() || patch.get("external-controller").is_some() { - handle_activate().await?; + CoreManager::global().run_core().await?; } // 更新系统代理 @@ -196,10 +208,12 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> { match { #[cfg(target_os = "windows")] - {} + { + todo!() + } if tun_mode.is_some() { - handle_activate().await?; + update_core_config().await?; } if auto_launch.is_some() { @@ -238,21 +252,6 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> { } } -/// 激活配置 -pub async fn handle_activate() -> Result<()> { - match CoreManager::global().activate_config().await { - Ok(_) => { - handle::Handle::refresh_clash(); - handle::Handle::notice_message("set_config::ok", "ok"); - Ok(()) - } - Err(err) => { - handle::Handle::notice_message("set_config::error", format!("{err}")); - Err(err) - } - } -} - /// 更新某个profile /// 如果更新当前配置就激活配置 pub async fn update_profile(uid: String, option: Option) -> Result<()> { @@ -286,8 +285,23 @@ pub async fn update_profile(uid: String, option: Option) -> Result<() }; if should_update { - handle_activate().await?; + update_core_config().await?; } Ok(()) } + +/// 更新配置 +async fn update_core_config() -> Result<()> { + match CoreManager::global().update_config().await { + Ok(_) => { + handle::Handle::refresh_clash(); + handle::Handle::notice_message("set_config::ok", "ok"); + Ok(()) + } + Err(err) => { + handle::Handle::notice_message("set_config::error", format!("{err}")); + Err(err) + } + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index f78c4e8..abe0e7d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -49,17 +49,15 @@ fn main() -> std::io::Result<()> { cmds::patch_verge_config, // cmds::update_hotkeys, // profile + cmds::get_profiles, + cmds::enhance_profiles, + cmds::patch_profiles_config, cmds::view_profile, cmds::patch_profile, cmds::create_profile, cmds::import_profile, cmds::update_profile, cmds::delete_profile, - cmds::select_profile, - cmds::get_profiles, - cmds::enhance_profiles, - cmds::change_profile_chain, - cmds::change_profile_valid, cmds::read_profile_file, cmds::save_profile_file, // service mode @@ -92,13 +90,6 @@ fn main() -> std::io::Result<()> { .build(tauri::generate_context!()) .expect("error while running tauri application"); - // let app_handle = app.app_handle(); - // ctrlc::set_handler(move || { - // resolve::resolve_reset(); - // app_handle.exit(0); - // }) - // .expect("error while exiting."); - app.run(|app_handle, e| match e { tauri::RunEvent::ExitRequested { api, .. } => { api.prevent_exit(); diff --git a/src-tauri/src/utils/config.rs b/src-tauri/src/utils/config.rs deleted file mode 100644 index c1c563a..0000000 --- a/src-tauri/src/utils/config.rs +++ /dev/null @@ -1,48 +0,0 @@ -use anyhow::{anyhow, bail, Context, Result}; -use serde::{de::DeserializeOwned, Serialize}; -use serde_yaml::{Mapping, Value}; -use std::{fs, path::PathBuf}; - -/// read data from yaml as struct T -pub fn read_yaml(path: &PathBuf) -> Result { - if !path.exists() { - bail!("file not found \"{}\"", path.display()); - } - - let yaml_str = fs::read_to_string(&path) - .context(format!("failed to read the file \"{}\"", path.display()))?; - - serde_yaml::from_str::(&yaml_str).context(format!( - "failed to read the file with yaml format \"{}\"", - path.display() - )) -} - -/// read mapping from yaml fix #165 -pub fn read_merge_mapping(path: &PathBuf) -> Result { - let mut val: Value = read_yaml(path)?; - val.apply_merge() - .context(format!("failed to apply merge \"{}\"", path.display()))?; - - Ok(val - .as_mapping() - .ok_or(anyhow!( - "failed to transform to yaml mapping \"{}\"", - path.display() - ))? - .to_owned()) -} - -/// save the data to the file -/// can set `prefix` string to add some comments -pub fn save_yaml(path: PathBuf, data: &T, prefix: Option<&str>) -> Result<()> { - let data_str = serde_yaml::to_string(data)?; - - let yaml_str = match prefix { - Some(prefix) => format!("{prefix}\n{data_str}"), - None => data_str, - }; - - let path_str = path.as_os_str().to_string_lossy().to_string(); - fs::write(path, yaml_str.as_bytes()).context(format!("failed to save file \"{path_str}\"")) -} diff --git a/src-tauri/src/utils/dirs.rs b/src-tauri/src/utils/dirs.rs index 8c32125..553408a 100644 --- a/src-tauri/src/utils/dirs.rs +++ b/src-tauri/src/utils/dirs.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use std::{env::temp_dir, path::PathBuf}; +use std::path::PathBuf; use tauri::{ api::path::{home_dir, resource_dir}, Env, PackageInfo, @@ -107,14 +107,6 @@ pub fn profiles_path() -> Result { Ok(app_home_dir()?.join(PROFILE_YAML)) } -pub fn clash_runtime_yaml() -> Result { - Ok(app_home_dir()?.join("clash-verge-runtime.yaml")) -} - -pub fn clash_check_yaml() -> Result { - Ok(temp_dir().join("clash-verge-check.yaml")) -} - pub fn app_res_dir() -> Result { unsafe { Ok(RESOURCE_DIR diff --git a/src-tauri/src/utils/help.rs b/src-tauri/src/utils/help.rs index 8ad5925..2d4a32f 100644 --- a/src-tauri/src/utils/help.rs +++ b/src-tauri/src/utils/help.rs @@ -1,8 +1,52 @@ -use anyhow::Result; +use anyhow::{anyhow, bail, Context, Result}; use nanoid::nanoid; -use std::path::PathBuf; -use std::process::Command; -use std::str::FromStr; +use serde::{de::DeserializeOwned, Serialize}; +use serde_yaml::{Mapping, Value}; +use std::{fs, path::PathBuf, process::Command, str::FromStr}; + +/// read data from yaml as struct T +pub fn read_yaml(path: &PathBuf) -> Result { + if !path.exists() { + bail!("file not found \"{}\"", path.display()); + } + + let yaml_str = fs::read_to_string(&path) + .context(format!("failed to read the file \"{}\"", path.display()))?; + + serde_yaml::from_str::(&yaml_str).context(format!( + "failed to read the file with yaml format \"{}\"", + path.display() + )) +} + +/// read mapping from yaml fix #165 +pub fn read_merge_mapping(path: &PathBuf) -> Result { + let mut val: Value = read_yaml(path)?; + val.apply_merge() + .context(format!("failed to apply merge \"{}\"", path.display()))?; + + Ok(val + .as_mapping() + .ok_or(anyhow!( + "failed to transform to yaml mapping \"{}\"", + path.display() + ))? + .to_owned()) +} + +/// save the data to the file +/// can set `prefix` string to add some comments +pub fn save_yaml(path: &PathBuf, data: &T, prefix: Option<&str>) -> Result<()> { + let data_str = serde_yaml::to_string(data)?; + + let yaml_str = match prefix { + Some(prefix) => format!("{prefix}\n\n{data_str}"), + None => data_str, + }; + + let path_str = path.as_os_str().to_string_lossy().to_string(); + fs::write(path, yaml_str.as_bytes()).context(format!("failed to save file \"{path_str}\"")) +} const ALPHABET: [char; 62] = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', @@ -68,16 +112,6 @@ macro_rules! error { }; } -#[deprecated] -#[macro_export] -macro_rules! log_if_err { - ($result: expr) => { - if let Err(err) = $result { - log::error!(target: "app", "{err}"); - } - }; -} - #[macro_export] macro_rules! log_err { ($result: expr) => { diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs index 5f8d2a6..3aeeb6c 100644 --- a/src-tauri/src/utils/init.rs +++ b/src-tauri/src/utils/init.rs @@ -1,4 +1,4 @@ -use crate::utils::{dirs, tmpl}; +use crate::utils::dirs; use anyhow::Result; use chrono::Local; use log::LevelFilter; @@ -7,7 +7,6 @@ use log4rs::append::file::FileAppender; use log4rs::config::{Appender, Config, Logger, Root}; use log4rs::encode::pattern::PatternEncoder; use std::fs; -use std::io::Write; use tauri::PackageInfo; /// initialize this instance's log file @@ -21,13 +20,20 @@ fn init_log() -> Result<()> { let log_file = format!("{}.log", local_time); let log_file = log_dir.join(log_file); + #[cfg(feature = "verge-dev")] + let time_format = "{d(%Y-%m-%d %H:%M:%S)} {l} - {M} {m}{n}"; + #[cfg(not(feature = "verge-dev"))] let time_format = "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}"; - let stdout = ConsoleAppender::builder() - .encoder(Box::new(PatternEncoder::new(time_format))) - .build(); - let tofile = FileAppender::builder() - .encoder(Box::new(PatternEncoder::new(time_format))) - .build(log_file)?; + + let encode = Box::new(PatternEncoder::new(time_format)); + + let stdout = ConsoleAppender::builder().encoder(encode.clone()).build(); + let tofile = FileAppender::builder().encoder(encode).build(log_file)?; + + #[cfg(feature = "verge-dev")] + let level = LevelFilter::Debug; + #[cfg(not(feature = "verge-dev"))] + let level = LevelFilter::Info; let config = Config::builder() .appender(Appender::builder().build("stdout", Box::new(stdout))) @@ -36,9 +42,9 @@ fn init_log() -> Result<()> { Logger::builder() .appenders(["file", "stdout"]) .additive(false) - .build("app", LevelFilter::Info), + .build("app", level), ) - .build(Root::builder().appender("stdout").build(LevelFilter::Info))?; + .build(Root::builder().appender("stdout").build(level))?; log4rs::init_config(config)?; @@ -58,21 +64,6 @@ pub fn init_config() -> Result<()> { if !app_dir.exists() { let _ = fs::create_dir_all(&app_dir); } - - // // target path - // let clash_path = app_dir.join("config.yaml"); - // let verge_path = app_dir.join("verge.yaml"); - // let profile_path = app_dir.join("profiles.yaml"); - - // if !clash_path.exists() { - // fs::File::create(clash_path)?.write(tmpl::CLASH_CONFIG)?; - // } - // if !verge_path.exists() { - // fs::File::create(verge_path)?.write(tmpl::VERGE_CONFIG)?; - // } - // if !profile_path.exists() { - // fs::File::create(profile_path)?.write(tmpl::PROFILES_CONFIG)?; - // } }); let _ = dirs::app_profiles_dir().map(|profiles_dir| { diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index 8fb66eb..aeb0a60 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -1,4 +1,3 @@ -pub mod config; pub mod dirs; pub mod help; pub mod init; diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index 9e6792e..737042c 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -10,9 +10,10 @@ pub fn resolve_setup(app: &mut App) { handle::Handle::global().init(app.app_handle()); - init::init_resources(app.package_info()); + log_err!(init::init_resources(app.package_info())); // 启动核心 + log_err!(Config::init_config()); log_err!(CoreManager::global().init()); // setup a simple http server for singleton diff --git a/src/components/profile/enhanced.tsx b/src/components/profile/enhanced.tsx index 6c9a5fd..3c38913 100644 --- a/src/components/profile/enhanced.tsx +++ b/src/components/profile/enhanced.tsx @@ -7,7 +7,7 @@ import { getProfiles, deleteProfile, enhanceProfiles, - changeProfileChain, + patchProfilesConfig, getRuntimeLogs, } from "@/services/cmds"; import ProfileMore from "./profile-more"; @@ -43,7 +43,7 @@ const EnhancedMode = (props: Props) => { if (chain.includes(uid)) return; const newChain = [...chain, uid]; - await changeProfileChain(newChain); + await patchProfilesConfig({ chain: newChain }); mutateProfiles((conf = {}) => ({ ...conf, chain: newChain }), true); mutateLogs(); }); @@ -52,7 +52,7 @@ const EnhancedMode = (props: Props) => { if (!chain.includes(uid)) return; const newChain = chain.filter((i) => i !== uid); - await changeProfileChain(newChain); + await patchProfilesConfig({ chain: newChain }); mutateProfiles((conf = {}) => ({ ...conf, chain: newChain }), true); mutateLogs(); }); @@ -72,7 +72,7 @@ const EnhancedMode = (props: Props) => { if (!chain.includes(uid)) return; const newChain = [uid].concat(chain.filter((i) => i !== uid)); - await changeProfileChain(newChain); + await patchProfilesConfig({ chain: newChain }); mutateProfiles((conf = {}) => ({ ...conf, chain: newChain }), true); mutateLogs(); }); @@ -81,7 +81,7 @@ const EnhancedMode = (props: Props) => { if (!chain.includes(uid)) return; const newChain = chain.filter((i) => i !== uid).concat([uid]); - await changeProfileChain(newChain); + await patchProfilesConfig({ chain: newChain }); mutateProfiles((conf = {}) => ({ ...conf, chain: newChain }), true); mutateLogs(); }); diff --git a/src/components/setting/mods/clash-field-viewer.tsx b/src/components/setting/mods/clash-field-viewer.tsx index c365a9b..dbfa51d 100644 --- a/src/components/setting/mods/clash-field-viewer.tsx +++ b/src/components/setting/mods/clash-field-viewer.tsx @@ -15,9 +15,9 @@ import { } from "@mui/material"; import { InfoRounded } from "@mui/icons-material"; import { - changeProfileValid, getProfiles, getRuntimeExists, + patchProfilesConfig, } from "@/services/cmds"; import { ModalHandler } from "@/hooks/use-modal-handler"; import { @@ -91,7 +91,7 @@ const ClashFieldViewer = ({ handler }: Props) => { if (curSet.size === oldSet.size && curSet.size === joinSet.size) return; try { - await changeProfileValid([...curSet]); + await patchProfilesConfig({ valid: [...curSet] }); mutateProfile(); // Notice.success("Refresh clash config", 1000); } catch (err: any) { diff --git a/src/pages/profiles.tsx b/src/pages/profiles.tsx index 241a497..a56f275 100644 --- a/src/pages/profiles.tsx +++ b/src/pages/profiles.tsx @@ -7,7 +7,7 @@ import { useTranslation } from "react-i18next"; import { getProfiles, patchProfile, - selectProfile, + patchProfilesConfig, importProfile, } from "@/services/cmds"; import { getProxies, updateProxy } from "@/services/api"; @@ -113,7 +113,7 @@ const ProfilePage = () => { if (!newProfiles.current && remoteItem) { const current = remoteItem.uid; - selectProfile(current); + patchProfilesConfig({ current }); mutate("getProfiles", { ...newProfiles, current }, true); mutate("getRuntimeLogs"); } @@ -125,13 +125,13 @@ const ProfilePage = () => { } }; - const onSelect = useLockFn(async (uid: string, force: boolean) => { - if (!force && uid === profiles.current) return; + const onSelect = useLockFn(async (current: string, force: boolean) => { + if (!force && current === profiles.current) return; try { - await selectProfile(uid); - setCurrentProfile(uid); - mutate("getProfiles", { ...profiles, current: uid }, true); + await patchProfilesConfig({ current }); + setCurrentProfile(current); + mutate("getProfiles", { ...profiles, current: current }, true); mutate("getRuntimeLogs"); // if (force) Notice.success("Refresh clash config", 1000); } catch (err: any) { diff --git a/src/services/cmds.ts b/src/services/cmds.ts index 2203be6..43faf4b 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -32,6 +32,10 @@ export async function enhanceProfiles() { return invoke("enhance_profiles"); } +export async function patchProfilesConfig(profiles: CmdType.ProfilesConfig) { + return invoke("patch_profiles_config"); +} + export async function createProfile( item: Partial, fileData?: string | null @@ -76,18 +80,6 @@ export async function patchProfile( return invoke("patch_profile", { index, profile }); } -export async function selectProfile(index: string) { - return invoke("select_profile", { index }); -} - -export async function changeProfileChain(chain?: string[]) { - return invoke("change_profile_chain", { chain }); -} - -export async function changeProfileValid(valid?: string[]) { - return invoke("change_profile_valid", { valid }); -} - export async function getClashInfo() { return invoke("get_clash_info"); } @@ -136,10 +128,6 @@ export async function restartSidecar() { return invoke("restart_sidecar"); } -// export async function killSidecar() { -// return invoke("kill_sidecar"); -// } - export async function openAppDir() { return invoke("open_app_dir").catch((err) => Notice.error(err?.message || err.toString(), 1500) From 58366c0b870f9578f9dddd4668641f943f198d32 Mon Sep 17 00:00:00 2001 From: GyDi Date: Fri, 18 Nov 2022 18:19:26 +0800 Subject: [PATCH 17/17] chore: rm code --- src-tauri/src/config/profiles.rs | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs index 302b055..f6a6399 100644 --- a/src-tauri/src/config/profiles.rs +++ b/src-tauri/src/config/profiles.rs @@ -99,35 +99,6 @@ impl IProfiles { self.current.clone() } - #[deprecated] - 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 Ok(()); - } - - bail!("invalid uid \"{uid}\""); - } - - #[deprecated] - pub fn put_chain(&mut self, chain: Option>) -> Result<()> { - self.chain = chain; - self.save_file() - } - - #[deprecated] - pub fn put_valid(&mut self, valid: Option>) -> Result<()> { - self.valid = valid; - self.save_file() - } - /// get items ref pub fn get_items(&self) -> Option<&Vec> { self.items.as_ref()