diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 50858960..788246f6 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -375,6 +375,25 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "async-process" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" +dependencies = [ + "async-channel 2.5.0", + "async-io 2.4.1", + "async-lock 3.4.0", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.3.0", + "futures-lite 2.6.0", + "rustix 1.0.7", + "tracing", +] + [[package]] name = "async-recursion" version = "1.1.1" @@ -1106,6 +1125,7 @@ dependencies = [ "boa_engine", "chrono", "criterion", + "dashmap 6.1.0", "deelevate", "delay_timer", "dirs 6.0.0", @@ -1149,6 +1169,7 @@ dependencies = [ "tauri-plugin-dialog", "tauri-plugin-fs", "tauri-plugin-global-shortcut", + "tauri-plugin-notification", "tauri-plugin-process", "tauri-plugin-shell", "tauri-plugin-updater", @@ -3980,6 +4001,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "mac-notification-sys" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119c8490084af61b44c9eda9d626475847a186737c0378c85e32d77c33a01cd4" +dependencies = [ + "cc", + "objc2 0.6.1", + "objc2-foundation 0.3.1", + "time", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -4331,6 +4364,20 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "notify-rust" +version = "4.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6442248665a5aa2514e794af3b39661a8e73033b1cc5e59899e1276117ee4400" +dependencies = [ + "futures-lite 2.6.0", + "log", + "mac-notification-sys", + "serde", + "tauri-winrt-notification", + "zbus", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -6670,7 +6717,7 @@ dependencies = [ "async-io 1.13.0", "async-lock 2.8.0", "async-net", - "async-process", + "async-process 1.8.1", "blocking", "futures-lite 1.13.0", ] @@ -7272,6 +7319,25 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "tauri-plugin-notification" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe06ed89cff6d0ec06ff4f544fb961e4718348a33309f56ccb2086e77bc9116" +dependencies = [ + "log", + "notify-rust", + "rand 0.8.5", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", + "time", + "url", +] + [[package]] name = "tauri-plugin-process" version = "2.3.0" @@ -7449,6 +7515,18 @@ dependencies = [ "toml 0.8.23", ] +[[package]] +name = "tauri-winrt-notification" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" +dependencies = [ + "quick-xml 0.37.5", + "thiserror 2.0.12", + "windows 0.61.3", + "windows-version", +] + [[package]] name = "tempfile" version = "3.20.0" @@ -9593,8 +9671,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597f45e98bc7e6f0988276012797855613cd8269e23b5be62cc4e5d28b7e515d" dependencies = [ "async-broadcast", + "async-executor", + "async-io 2.4.1", + "async-lock 3.4.0", + "async-process 2.3.1", "async-recursion", + "async-task", "async-trait", + "blocking", "enumflags2", "event-listener 5.3.0", "futures-core", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 830b14a4..40cd3797 100755 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -79,6 +79,8 @@ sha2 = "0.10.9" hex = "0.4.3" scopeguard = "1.2.0" kode-bridge = { git = "https://github.com/KodeBarinn/kode-bridge", branch = "dev" } +dashmap = "6.1.0" +tauri-plugin-notification = "2.3.0" [target.'cfg(windows)'.dependencies] runas = "=1.2.0" @@ -140,4 +142,4 @@ crate-type = ["staticlib", "cdylib", "rlib"] [dev-dependencies] criterion = "0.6.0" -tempfile = "3.20.0" \ No newline at end of file +tempfile = "3.20.0" diff --git a/src-tauri/capabilities/desktop.json b/src-tauri/capabilities/desktop.json index 823c9af9..a61c708a 100755 --- a/src-tauri/capabilities/desktop.json +++ b/src-tauri/capabilities/desktop.json @@ -17,6 +17,7 @@ "autostart:allow-enable", "autostart:allow-disable", "autostart:allow-is-enabled", - "core:window:allow-set-theme" + "core:window:allow-set-theme", + "notification:default" ] } diff --git a/src-tauri/src/cmd/proxy.rs b/src-tauri/src/cmd/proxy.rs index 6034f218..f34738b3 100644 --- a/src-tauri/src/cmd/proxy.rs +++ b/src-tauri/src/cmd/proxy.rs @@ -1,99 +1,41 @@ use super::CmdResult; -use crate::{core::handle, module::mihomo::MihomoManager, state::proxy::CmdProxyState}; -use std::{ - sync::Mutex, - time::{Duration, Instant}, -}; -use tauri::Manager; +use crate::{ipc::IpcManager, state::proxy::ProxyRequestCache}; +use std::time::Duration; const PROXIES_REFRESH_INTERVAL: Duration = Duration::from_secs(60); const PROVIDERS_REFRESH_INTERVAL: Duration = Duration::from_secs(60); #[tauri::command] pub async fn get_proxies() -> CmdResult { - let manager = MihomoManager::global(); - - let app_handle = handle::Handle::global().app_handle().unwrap(); - let cmd_proxy_state = app_handle.state::>(); - - let should_refresh = { - let mut state = cmd_proxy_state.lock().unwrap(); - let now = Instant::now(); - if now.duration_since(state.last_refresh_time) > PROXIES_REFRESH_INTERVAL { - state.need_refresh = true; - state.last_refresh_time = now; - } - state.need_refresh - }; - - if should_refresh { - let proxies = manager.get_refresh_proxies().await?; - { - let mut state = cmd_proxy_state.lock().unwrap(); - state.proxies = Box::new(proxies); - state.need_refresh = false; - } - log::debug!(target: "app", "proxies刷新成功"); - } - - let proxies = { - let state = cmd_proxy_state.lock().unwrap(); - state.proxies.clone() - }; - Ok(*proxies) + let manager = IpcManager::global(); + let cache = ProxyRequestCache::global(); + let key = ProxyRequestCache::make_key("proxies", "default"); + let value = cache + .get_or_fetch(key, PROXIES_REFRESH_INTERVAL, || async { + manager.get_refresh_proxies().await.expect("fetch failed") + }) + .await; + Ok((*value).clone()) } /// 强制刷新代理缓存用于profile切换 #[tauri::command] pub async fn force_refresh_proxies() -> CmdResult { - let manager = MihomoManager::global(); - let app_handle = handle::Handle::global().app_handle().unwrap(); - let cmd_proxy_state = app_handle.state::>(); - - log::debug!(target: "app", "强制刷新代理缓存"); - - let proxies = manager.get_refresh_proxies().await?; - - { - let mut state = cmd_proxy_state.lock().unwrap(); - state.proxies = Box::new(proxies.clone()); - state.need_refresh = false; - state.last_refresh_time = Instant::now(); - } - - log::debug!(target: "app", "强制刷新代理缓存完成"); - Ok(proxies) + let cache = ProxyRequestCache::global(); + let key = ProxyRequestCache::make_key("proxies", "default"); + cache.map.remove(&key); + get_proxies().await } #[tauri::command] pub async fn get_providers_proxies() -> CmdResult { - let app_handle = handle::Handle::global().app_handle().unwrap(); - let cmd_proxy_state = app_handle.state::>(); - - let should_refresh = { - let mut state = cmd_proxy_state.lock().unwrap(); - let now = Instant::now(); - if now.duration_since(state.last_refresh_time) > PROVIDERS_REFRESH_INTERVAL { - state.need_refresh = true; - state.last_refresh_time = now; - } - state.need_refresh - }; - - if should_refresh { - let manager = MihomoManager::global(); - let providers = manager.get_providers_proxies().await?; - { - let mut state = cmd_proxy_state.lock().unwrap(); - state.providers_proxies = Box::new(providers); - state.need_refresh = false; - } - log::debug!(target: "app", "providers_proxies刷新成功"); - } - - let providers_proxies = { - let state = cmd_proxy_state.lock().unwrap(); - state.providers_proxies.clone() - }; - Ok(*providers_proxies) + let manager = IpcManager::global(); + let cache = ProxyRequestCache::global(); + let key = ProxyRequestCache::make_key("providers", "default"); + let value = cache + .get_or_fetch(key, PROVIDERS_REFRESH_INTERVAL, || async { + manager.get_providers_proxies().await.expect("fetch failed") + }) + .await; + Ok((*value).clone()) } diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs index df3eec24..00113977 100644 --- a/src-tauri/src/config/clash.rs +++ b/src-tauri/src/config/clash.rs @@ -57,6 +57,10 @@ impl IClashTemp { map.insert("ipv6".into(), true.into()); map.insert("mode".into(), "rule".into()); map.insert("external-controller".into(), "127.0.0.1:9097".into()); + #[cfg(unix)] + map.insert("external-controller-unix".into(), "mihomo.sock".into()); + #[cfg(windows)] + map.insert("external-controller-pipe".into(), r"\\.\pipe\mihomo".into()); cors_map.insert("allow-private-network".into(), true.into()); cors_map.insert( "allow-origins".into(), @@ -88,6 +92,11 @@ impl IClashTemp { let socks_port = Self::guard_socks_port(&config); let port = Self::guard_port(&config); let ctrl = Self::guard_server_ctrl(&config); + #[cfg(unix)] + let external_controller_unix = Self::guard_external_controller_unix(&config); + #[cfg(windows)] + let external_controller_pipe = Self::guard_external_controller_pipe(&config); + #[cfg(not(target_os = "windows"))] config.insert("redir-port".into(), redir_port.into()); #[cfg(target_os = "linux")] @@ -96,7 +105,16 @@ impl IClashTemp { config.insert("socks-port".into(), socks_port.into()); config.insert("port".into(), port.into()); config.insert("external-controller".into(), ctrl.into()); - + #[cfg(unix)] + config.insert( + "external-controller-unix".into(), + external_controller_unix.into(), + ); + #[cfg(windows)] + config.insert( + "external-controller-pipe".into(), + external_controller_pipe.into(), + ); config } @@ -257,6 +275,26 @@ impl IClashTemp { Err(_) => "127.0.0.1:9097".into(), } } + + #[cfg(unix)] + pub fn guard_external_controller_unix(config: &Mapping) -> String { + config + .get("external-controller-unix") + .and_then(|value| value.as_str()) + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .unwrap_or_else(|| "mihomo.sock".to_string()) + } + + #[cfg(windows)] + pub fn guard_external_controller_pipe(config: &Mapping) -> String { + config + .get("external-controller-pipe") + .and_then(|value| value.as_str()) + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .unwrap_or_else(|| r"\\.\pipe\mihomo".to_string()) + } } #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] diff --git a/src-tauri/src/core/tray/mod.rs b/src-tauri/src/core/tray/mod.rs index 21cc20cd..e7b1c2f7 100644 --- a/src-tauri/src/core/tray/mod.rs +++ b/src-tauri/src/core/tray/mod.rs @@ -2,7 +2,6 @@ use once_cell::sync::OnceCell; use tauri::tray::TrayIconBuilder; #[cfg(target_os = "macos")] pub mod speed_rate; -#[cfg(target_os = "macos")] use crate::ipc::Rate; use crate::{ cmd, diff --git a/src-tauri/src/ipc/general.rs b/src-tauri/src/ipc/general.rs index 72a05296..025372c9 100644 --- a/src-tauri/src/ipc/general.rs +++ b/src-tauri/src/ipc/general.rs @@ -6,7 +6,10 @@ use kode_bridge::{ use serde_json::json; use std::sync::OnceLock; -use crate::utils::dirs::ipc_path; +use crate::{ + logging, + utils::{dirs::ipc_path, logging::Type}, +}; pub struct IpcManager { ipc_path: String, @@ -26,6 +29,13 @@ impl IpcManager { "IpcManager initialized with IPC path: {}", instance.ipc_path ); + logging!( + info, + Type::IPC, + true, + "IpcManager initialized with IPC path: {}", + instance.ipc_path + ); instance }) } @@ -91,6 +101,7 @@ impl IpcManager { } impl IpcManager { + #[allow(dead_code)] pub async fn is_mihomo_running(&self) -> Result<(), AnyError> { let url = "/version"; let _response = self.send_request("GET", url, None).await?; diff --git a/src-tauri/src/utils/dirs.rs b/src-tauri/src/utils/dirs.rs index 372b2c4f..3ee71612 100644 --- a/src-tauri/src/utils/dirs.rs +++ b/src-tauri/src/utils/dirs.rs @@ -257,6 +257,5 @@ pub fn ipc_path() -> Result { #[cfg(target_os = "windows")] pub fn ipc_path() -> Result { - let res_dir = app_home_dir()?; - Ok(res_dir.join(r"\\.\pipe\mihomo")) + Ok(PathBuf::from(r"\\.\pipe\mihomo")) } diff --git a/src-tauri/src/utils/logging.rs b/src-tauri/src/utils/logging.rs index ec07ef9f..7fd3762a 100644 --- a/src-tauri/src/utils/logging.rs +++ b/src-tauri/src/utils/logging.rs @@ -17,6 +17,7 @@ pub enum Type { Lightweight, Network, ProxyMode, + IPC, } impl fmt::Display for Type { @@ -37,6 +38,7 @@ impl fmt::Display for Type { Type::Lightweight => write!(f, "[Lightweight]"), Type::Network => write!(f, "[Network]"), Type::ProxyMode => write!(f, "[ProxMode]"), + Type::IPC => write!(f, "[IPC]"), } } }