refactor: replace MihomoManager with IpcManager and remove Mihomo module
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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延迟
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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()?),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
pub mod general;
|
||||
|
||||
pub use general::IpcManager;
|
||||
|
||||
pub struct Rate {
|
||||
// pub up: usize,
|
||||
// pub down: usize,
|
||||
}
|
||||
|
||||
@@ -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 方法
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
pub mod lightweight;
|
||||
pub mod mihomo;
|
||||
pub mod sysinfo;
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user