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)