refactor: replace MihomoManager with IpcManager and remove Mihomo module

This commit is contained in:
Tunglies
2025-06-25 01:32:50 +08:00
Unverified
parent 195fc8310c
commit 10f467a8b3
13 changed files with 154 additions and 181 deletions

View File

@@ -78,7 +78,7 @@ hmac = "0.12.1"
sha2 = "0.10.9"
hex = "0.4.3"
scopeguard = "1.2.0"
kode-bridge = "0.1.0"
kode-bridge = "0.1.2"
[target.'cfg(windows)'.dependencies]
runas = "=1.2.0"

View File

@@ -1,7 +1,5 @@
use super::CmdResult;
use crate::{
config::*, core::*, feat, module::mihomo::MihomoManager, process::AsyncHandler, wrap_err,
};
use crate::{config::*, core::*, feat, ipc::IpcManager, process::AsyncHandler, wrap_err};
use serde_yaml::Mapping;
/// 复制Clash环境变量
@@ -90,9 +88,10 @@ pub async fn clash_api_get_proxy_delay(
url: Option<String>,
timeout: i32,
) -> CmdResult<serde_json::Value> {
MihomoManager::global()
IpcManager::global()
.test_proxy_delay(&name, url, timeout)
.await
.map_err(|e| e.to_string())
}
/// 测试URL延迟

View File

@@ -1,8 +1,10 @@
use super::CmdResult;
use crate::module::mihomo::MihomoManager;
use std::time::Duration;
use crate::state::proxy::ProxyRequestCache;
use crate::{core::handle, module::mihomo::MihomoManager, state::proxy::CmdProxyState};
use std::{
sync::Mutex,
time::{Duration, Instant},
};
use tauri::Manager;
const PROXIES_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
const PROVIDERS_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
@@ -10,34 +12,88 @@ const PROVIDERS_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
#[tauri::command]
pub async fn get_proxies() -> CmdResult<serde_json::Value> {
let manager = MihomoManager::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())
let app_handle = handle::Handle::global().app_handle().unwrap();
let cmd_proxy_state = app_handle.state::<Mutex<CmdProxyState>>();
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)
}
/// 强制刷新代理缓存用于profile切换
#[tauri::command]
pub async fn force_refresh_proxies() -> CmdResult<serde_json::Value> {
let cache = ProxyRequestCache::global();
let key = ProxyRequestCache::make_key("proxies", "default");
cache.map.remove(&key);
get_proxies().await
let manager = MihomoManager::global();
let app_handle = handle::Handle::global().app_handle().unwrap();
let cmd_proxy_state = app_handle.state::<Mutex<CmdProxyState>>();
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)
}
#[tauri::command]
pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
let manager = MihomoManager::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())
let app_handle = handle::Handle::global().app_handle().unwrap();
let cmd_proxy_state = app_handle.state::<Mutex<CmdProxyState>>();
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)
}

View File

@@ -4,8 +4,8 @@ use crate::{
handle,
service::{self},
},
ipc::IpcManager,
logging, logging_error,
module::mihomo::MihomoManager,
utils::{
dirs,
help::{self},
@@ -413,10 +413,7 @@ impl CoreManager {
logging_error!(Type::Core, true, "{}", msg);
msg
});
match MihomoManager::global()
.put_configs_force(run_path_str?)
.await
{
match IpcManager::global().put_configs_force(run_path_str?).await {
Ok(_) => {
Config::runtime().apply();
logging!(info, Type::Core, true, "Configuration updated successfully");

View File

@@ -2,11 +2,13 @@ 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,
config::Config,
feat, logging,
module::{lightweight::is_in_lightweight_mode, mihomo::Rate},
module::lightweight::is_in_lightweight_mode,
utils::{dirs::find_target_icons, i18n::t, resolve::VERSION},
Type,
};

View File

@@ -1,8 +1,8 @@
use crate::{
config::Config,
core::{handle, tray, CoreManager},
ipc::IpcManager,
logging_error,
module::mihomo::MihomoManager,
process::AsyncHandler,
utils::{logging::Type, resolve},
};
@@ -38,12 +38,12 @@ pub fn restart_app() {
fn after_change_clash_mode() {
AsyncHandler::spawn(move || async {
match MihomoManager::global().get_connections().await {
match IpcManager::global().get_connections().await {
Ok(connections) => {
if let Some(connections_array) = connections["connections"].as_array() {
for connection in connections_array {
if let Some(id) = connection["id"].as_str() {
let _ = MihomoManager::global().delete_connection(id).await;
let _ = IpcManager::global().delete_connection(id).await;
}
}
}
@@ -65,7 +65,7 @@ pub fn change_clash_mode(mode: String) {
});
AsyncHandler::spawn(move || async move {
log::debug!(target: "app", "change clash mode to {mode}");
match MihomoManager::global().patch_configs(json_value).await {
match IpcManager::global().patch_configs(json_value).await {
Ok(_) => {
// 更新订阅
Config::clash().data_mut().patch_config(mapping);

View File

@@ -1,6 +1,7 @@
use crate::{
config::{Config, IVerge},
core::handle,
ipc::IpcManager,
process::AsyncHandler,
};
use std::env;
@@ -18,11 +19,8 @@ pub fn toggle_system_proxy() {
AsyncHandler::spawn(move || async move {
// 如果当前系统代理即将关闭且自动关闭连接设置为true则关闭所有连接
if enable && auto_close_connection {
if let Err(err) = crate::module::mihomo::MihomoManager::global()
.close_all_connections()
.await
{
log::error!(target: "app", "Failed to close all connections: {err}");
if let Err(err) = IpcManager::global().close_all_connections().await {
log::error!(target: "app", "Failed to close all connections: {}", err);
}
}

View File

@@ -3,8 +3,8 @@ use crate::AppHandleManager;
use crate::{
config::Config,
core::{handle, sysopt, CoreManager},
ipc::IpcManager,
logging,
module::mihomo::MihomoManager,
utils::logging::Type,
};
@@ -107,7 +107,7 @@ async fn clean_async() -> bool {
});
match timeout(
Duration::from_secs(2),
MihomoManager::global().patch_configs(disable_tun),
IpcManager::global().patch_configs(disable_tun),
)
.await
{

View File

@@ -1,15 +1,45 @@
use kode_bridge::{errors::AnyError, IpcHttpClient};
use kode_bridge::{
errors::{AnyError, AnyResult},
types::Response,
IpcHttpClient,
};
use serde_json::json;
use std::sync::OnceLock;
use crate::utils::dirs::ipc_path;
pub struct IpcManager {
client: IpcHttpClient,
ipc_path: String,
}
static INSTANCE: OnceLock<IpcManager> = OnceLock::new();
impl IpcManager {
pub fn global() -> &'static IpcManager {
INSTANCE.get_or_init(|| {
let ipc_path_buf = ipc_path().unwrap();
let ipc_path = ipc_path_buf.to_str().unwrap_or_default();
let instance = IpcManager {
ipc_path: ipc_path.to_string(),
};
println!(
"IpcManager initialized with IPC path: {}",
instance.ipc_path
);
instance
})
}
}
impl IpcManager {
pub async fn new(socket_path: &str) -> Self {
IpcManager {
client: IpcHttpClient::new(socket_path).await,
}
pub async fn request(
&self,
method: &str,
path: &str,
body: Option<&serde_json::Value>,
) -> AnyResult<Response> {
let client = IpcHttpClient::new(&self.ipc_path);
Ok(client.request(method, path, body).await?)
}
}
@@ -20,18 +50,18 @@ impl IpcManager {
path: &str,
body: Option<&serde_json::Value>,
) -> Result<serde_json::Value, AnyError> {
// Ok(self.client.request(method, path, body).await?.json()?)
let response = self.client.request(method, path, body).await?;
if method == "PATCH" {
if response.status == 204 {
Ok(serde_json::json!({"code": 204}))
} else {
Ok(response.json()?)
let response = IpcManager::global().request(method, path, body).await?;
match method {
"GET" => Ok(response.json()?),
"PATCH" => {
if response.status == 204 {
Ok(serde_json::json!({"code": 204}))
} else {
Ok(response.json()?)
}
}
} else if method == "PUT" {
Ok(json!(response.body))
} else {
Ok(response.json()?)
"PUT" => Ok(json!(response.body)),
_ => Ok(response.json()?),
}
}

View File

@@ -1,3 +1,8 @@
pub mod general;
pub use general::IpcManager;
pub struct Rate {
// pub up: usize,
// pub down: usize,
}

View File

@@ -1,113 +0,0 @@
use crate::config::Config;
use crate::ipc::IpcManager;
#[cfg(unix)]
use crate::utils::dirs::{ipc_path, path_to_str};
use once_cell::sync::Lazy;
use parking_lot::{Mutex, RwLock};
use std::time::{Duration, Instant};
use tauri::http::HeaderMap;
// 缓存的最大有效期5秒
const CACHE_TTL: Duration = Duration::from_secs(5);
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Rate {
pub up: u64,
pub down: u64,
}
// 缓存MihomoManager实例
struct MihomoCache {
manager: IpcManager,
created_at: Instant,
server: String,
}
// 使用RwLock替代Mutex允许多个读取操作并发进行
pub struct MihomoManager {
mihomo_cache: RwLock<Option<MihomoCache>>,
create_lock: Mutex<()>,
}
impl MihomoManager {
fn __global() -> &'static MihomoManager {
static INSTANCE: Lazy<MihomoManager> = Lazy::new(|| MihomoManager {
mihomo_cache: RwLock::new(None),
create_lock: Mutex::new(()),
});
&INSTANCE
}
pub fn global() -> IpcManager {
let instance = MihomoManager::__global();
// 尝试从缓存读取(只需读锁)
{
let cache = instance.mihomo_cache.read();
if let Some(cache_entry) = &*cache {
let (current_server, _) = MihomoManager::get_clash_client_info()
.unwrap_or_else(|| (String::new(), HeaderMap::new()));
// 检查缓存是否有效
if cache_entry.server == current_server
&& cache_entry.created_at.elapsed() < CACHE_TTL
{
return cache_entry.manager.clone();
}
}
}
// 缓存无效,获取创建锁
let _create_guard = instance.create_lock.lock();
// 再次检查缓存(双重检查锁定模式)
{
let cache = instance.mihomo_cache.read();
if let Some(cache_entry) = &*cache {
let (current_server, _) = MihomoManager::get_clash_client_info()
.unwrap_or_else(|| (String::new(), HeaderMap::new()));
if cache_entry.server == current_server
&& cache_entry.created_at.elapsed() < CACHE_TTL
{
return cache_entry.manager;
}
}
}
// 创建新实例
let (current_server, headers) = MihomoManager::get_clash_client_info()
.unwrap_or_else(|| (String::new(), HeaderMap::new()));
// ! unix
#[cfg(unix)]
let manager = IpcManager::new(path_to_str(ipc_path()?));
// ! windows
// 更新缓存
{
let mut cache = instance.mihomo_cache.write();
*cache = Some(MihomoCache {
manager: manager,
created_at: Instant::now(),
server: current_server,
});
}
manager
}
}
impl MihomoManager {
pub fn get_clash_client_info() -> Option<(String, HeaderMap)> {
let client = { Config::clash().latest_ref().get_client_info() };
let server = format!("http://{}", client.server);
let mut headers = HeaderMap::new();
headers.insert("Content-Type", "application/json".parse().unwrap());
if let Some(secret) = client.secret {
let secret = format!("Bearer {secret}").parse().unwrap();
headers.insert("Authorization", secret);
}
Some((server, headers))
}
// 已移除未使用的 get_clash_client_info_or_default 和 get_traffic_ws_url 方法
}

View File

@@ -1,3 +1,2 @@
pub mod lightweight;
pub mod mihomo;
pub mod sysinfo;

View File

@@ -245,18 +245,18 @@ pub fn get_encryption_key() -> Result<Vec<u8>> {
#[cfg(target_os = "macos")]
pub fn ipc_path() -> Result<PathBuf> {
let res_dir = app_resources_dir()?;
let res_dir = app_home_dir()?;
Ok(res_dir.join("mihomo.sock"))
}
#[cfg(target_os = "linux")]
pub fn ipc_path() -> Result<PathBuf> {
let res_dir = app_resources_dir()?;
let res_dir = app_home_dir()?;
Ok(res_dir.join("mihomo.sock"))
}
#[cfg(target_os = "windows")]
pub fn ipc_path() -> Result<PathBuf> {
let res_dir = app_resources_dir()?;
let res_dir = app_home_dir()?;
Ok(res_dir.join(r"\\.\pipe\mihomo"))
}