refactor: invock mihomo api by use tauri-plugin-mihomo
This commit is contained in:
@@ -1,21 +1,15 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use super::CmdResult;
|
||||
use crate::{
|
||||
cache::CacheProxy,
|
||||
config::Config,
|
||||
core::{CoreManager, handle},
|
||||
};
|
||||
use crate::{
|
||||
config::*,
|
||||
feat,
|
||||
ipc::{self, IpcManager},
|
||||
logging,
|
||||
utils::logging::Type,
|
||||
wrap_err,
|
||||
core::{self, CoreManager, RunningMode, handle, logger},
|
||||
};
|
||||
use crate::{config::*, feat, logging, utils::logging::Type, wrap_err};
|
||||
use serde_yaml_ng::Mapping;
|
||||
use std::time::Duration;
|
||||
// use std::time::Duration;
|
||||
|
||||
const CONFIG_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
|
||||
// const CONFIG_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
|
||||
|
||||
/// 复制Clash环境变量
|
||||
#[tauri::command]
|
||||
@@ -113,18 +107,18 @@ pub async fn restart_core() -> CmdResult {
|
||||
}
|
||||
|
||||
/// 获取代理延迟
|
||||
#[tauri::command]
|
||||
pub async fn clash_api_get_proxy_delay(
|
||||
name: String,
|
||||
url: Option<String>,
|
||||
timeout: i32,
|
||||
) -> CmdResult<serde_json::Value> {
|
||||
wrap_err!(
|
||||
IpcManager::global()
|
||||
.test_proxy_delay(&name, url, timeout)
|
||||
.await
|
||||
)
|
||||
}
|
||||
// #[tauri::command]
|
||||
// pub async fn clash_api_get_proxy_delay(
|
||||
// name: String,
|
||||
// url: Option<String>,
|
||||
// timeout: i32,
|
||||
// ) -> CmdResult<serde_json::Value> {
|
||||
// wrap_err!(
|
||||
// IpcManager::global()
|
||||
// .test_proxy_delay(&name, url, timeout)
|
||||
// .await
|
||||
// )
|
||||
// }
|
||||
|
||||
/// 测试URL延迟
|
||||
#[tauri::command]
|
||||
@@ -307,317 +301,328 @@ pub async fn validate_dns_config() -> CmdResult<(bool, String)> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取Clash版本信息
|
||||
#[tauri::command]
|
||||
pub async fn get_clash_version() -> CmdResult<serde_json::Value> {
|
||||
wrap_err!(IpcManager::global().get_version().await)
|
||||
}
|
||||
// 获取Clash版本信息
|
||||
// #[tauri::command]
|
||||
// pub async fn get_clash_version() -> CmdResult<serde_json::Value> {
|
||||
// wrap_err!(IpcManager::global().get_version().await)
|
||||
// }
|
||||
|
||||
/// 获取Clash配置
|
||||
#[tauri::command]
|
||||
pub async fn get_clash_config() -> CmdResult<serde_json::Value> {
|
||||
let manager = IpcManager::global();
|
||||
let cache = CacheProxy::global();
|
||||
let key = CacheProxy::make_key("clash_config", "default");
|
||||
let value = cache
|
||||
.get_or_fetch(key, CONFIG_REFRESH_INTERVAL, || async {
|
||||
manager.get_config().await.unwrap_or_else(|e| {
|
||||
logging!(error, Type::Cmd, "Failed to fetch clash config: {e}");
|
||||
serde_json::Value::Object(serde_json::Map::new())
|
||||
})
|
||||
})
|
||||
.await;
|
||||
Ok((*value).clone())
|
||||
}
|
||||
// 获取Clash配置
|
||||
// #[tauri::command]
|
||||
// pub async fn get_clash_config() -> CmdResult<serde_json::Value> {
|
||||
// let manager = IpcManager::global();
|
||||
// let cache = CacheProxy::global();
|
||||
// let key = CacheProxy::make_key("clash_config", "default");
|
||||
// let value = cache
|
||||
// .get_or_fetch(key, CONFIG_REFRESH_INTERVAL, || async {
|
||||
// manager.get_config().await.unwrap_or_else(|e| {
|
||||
// logging!(error, Type::Cmd, "Failed to fetch clash config: {e}");
|
||||
// serde_json::Value::Object(serde_json::Map::new())
|
||||
// })
|
||||
// })
|
||||
// .await;
|
||||
// Ok((*value).clone())
|
||||
// }
|
||||
|
||||
/// 强制刷新Clash配置缓存
|
||||
#[tauri::command]
|
||||
pub async fn force_refresh_clash_config() -> CmdResult<serde_json::Value> {
|
||||
let cache = CacheProxy::global();
|
||||
let key = CacheProxy::make_key("clash_config", "default");
|
||||
cache.map.remove(&key);
|
||||
get_clash_config().await
|
||||
}
|
||||
// 强制刷新Clash配置缓存
|
||||
// #[tauri::command]
|
||||
// pub async fn force_refresh_clash_config() -> CmdResult<serde_json::Value> {
|
||||
// let cache = CacheProxy::global();
|
||||
// let key = CacheProxy::make_key("clash_config", "default");
|
||||
// cache.map.remove(&key);
|
||||
// get_clash_config().await
|
||||
// }
|
||||
|
||||
/// 更新地理数据
|
||||
#[tauri::command]
|
||||
pub async fn update_geo_data() -> CmdResult {
|
||||
wrap_err!(IpcManager::global().update_geo_data().await)
|
||||
}
|
||||
// 更新地理数据
|
||||
// #[tauri::command]
|
||||
// pub async fn update_geo_data() -> CmdResult {
|
||||
// wrap_err!(IpcManager::global().update_geo_data().await)
|
||||
// }
|
||||
|
||||
/// 升级Clash核心
|
||||
#[tauri::command]
|
||||
pub async fn upgrade_clash_core() -> CmdResult {
|
||||
wrap_err!(IpcManager::global().upgrade_core().await)
|
||||
}
|
||||
// 升级Clash核心
|
||||
// #[tauri::command]
|
||||
// pub async fn upgrade_clash_core() -> CmdResult {
|
||||
// wrap_err!(IpcManager::global().upgrade_core().await)
|
||||
// }
|
||||
|
||||
/// 获取规则
|
||||
#[tauri::command]
|
||||
pub async fn get_clash_rules() -> CmdResult<serde_json::Value> {
|
||||
wrap_err!(IpcManager::global().get_rules().await)
|
||||
}
|
||||
// 获取规则
|
||||
// #[tauri::command]
|
||||
// pub async fn get_clash_rules() -> CmdResult<serde_json::Value> {
|
||||
// wrap_err!(IpcManager::global().get_rules().await)
|
||||
// }
|
||||
|
||||
/// 更新代理选择
|
||||
#[tauri::command]
|
||||
pub async fn update_proxy_choice(group: String, proxy: String) -> CmdResult {
|
||||
wrap_err!(IpcManager::global().update_proxy(&group, &proxy).await)
|
||||
}
|
||||
// 更新代理选择
|
||||
// #[tauri::command]
|
||||
// pub async fn update_proxy_choice(group: String, proxy: String) -> CmdResult {
|
||||
// wrap_err!(IpcManager::global().update_proxy(&group, &proxy).await)
|
||||
// }
|
||||
|
||||
/// 获取代理提供者
|
||||
#[tauri::command]
|
||||
pub async fn get_proxy_providers() -> CmdResult<serde_json::Value> {
|
||||
wrap_err!(IpcManager::global().get_providers_proxies().await)
|
||||
}
|
||||
// 获取代理提供者
|
||||
// #[tauri::command]
|
||||
// pub async fn get_proxy_providers() -> CmdResult<serde_json::Value> {
|
||||
// wrap_err!(IpcManager::global().get_providers_proxies().await)
|
||||
// }
|
||||
|
||||
/// 获取规则提供者
|
||||
#[tauri::command]
|
||||
pub async fn get_rule_providers() -> CmdResult<serde_json::Value> {
|
||||
wrap_err!(IpcManager::global().get_rule_providers().await)
|
||||
}
|
||||
// 获取规则提供者
|
||||
// #[tauri::command]
|
||||
// pub async fn get_rule_providers() -> CmdResult<serde_json::Value> {
|
||||
// wrap_err!(IpcManager::global().get_rule_providers().await)
|
||||
// }
|
||||
|
||||
/// 代理提供者健康检查
|
||||
#[tauri::command]
|
||||
pub async fn proxy_provider_health_check(name: String) -> CmdResult {
|
||||
wrap_err!(
|
||||
IpcManager::global()
|
||||
.proxy_provider_health_check(&name)
|
||||
.await
|
||||
)
|
||||
}
|
||||
// 代理提供者健康检查
|
||||
// #[tauri::command]
|
||||
// pub async fn proxy_provider_health_check(name: String) -> CmdResult {
|
||||
// wrap_err!(
|
||||
// IpcManager::global()
|
||||
// .proxy_provider_health_check(&name)
|
||||
// .await
|
||||
// )
|
||||
// }
|
||||
|
||||
/// 更新代理提供者
|
||||
#[tauri::command]
|
||||
pub async fn update_proxy_provider(name: String) -> CmdResult {
|
||||
wrap_err!(IpcManager::global().update_proxy_provider(&name).await)
|
||||
}
|
||||
// 更新代理提供者
|
||||
// #[tauri::command]
|
||||
// pub async fn update_proxy_provider(name: String) -> CmdResult {
|
||||
// wrap_err!(IpcManager::global().update_proxy_provider(&name).await)
|
||||
// }
|
||||
|
||||
/// 更新规则提供者
|
||||
#[tauri::command]
|
||||
pub async fn update_rule_provider(name: String) -> CmdResult {
|
||||
wrap_err!(IpcManager::global().update_rule_provider(&name).await)
|
||||
}
|
||||
// 更新规则提供者
|
||||
// #[tauri::command]
|
||||
// pub async fn update_rule_provider(name: String) -> CmdResult {
|
||||
// wrap_err!(IpcManager::global().update_rule_provider(&name).await)
|
||||
// }
|
||||
|
||||
/// 获取连接
|
||||
#[tauri::command]
|
||||
pub async fn get_clash_connections() -> CmdResult<serde_json::Value> {
|
||||
wrap_err!(IpcManager::global().get_connections().await)
|
||||
}
|
||||
// 获取连接
|
||||
// #[tauri::command]
|
||||
// pub async fn get_clash_connections() -> CmdResult<serde_json::Value> {
|
||||
// wrap_err!(IpcManager::global().get_connections().await)
|
||||
// }
|
||||
|
||||
/// 删除连接
|
||||
#[tauri::command]
|
||||
pub async fn delete_clash_connection(id: String) -> CmdResult {
|
||||
wrap_err!(IpcManager::global().delete_connection(&id).await)
|
||||
}
|
||||
// 删除连接
|
||||
// #[tauri::command]
|
||||
// pub async fn delete_clash_connection(id: String) -> CmdResult {
|
||||
// wrap_err!(IpcManager::global().delete_connection(&id).await)
|
||||
// }
|
||||
|
||||
/// 关闭所有连接
|
||||
#[tauri::command]
|
||||
pub async fn close_all_clash_connections() -> CmdResult {
|
||||
wrap_err!(IpcManager::global().close_all_connections().await)
|
||||
}
|
||||
// 关闭所有连接
|
||||
// #[tauri::command]
|
||||
// pub async fn close_all_clash_connections() -> CmdResult {
|
||||
// wrap_err!(IpcManager::global().close_all_connections().await)
|
||||
// }
|
||||
|
||||
/// 获取流量数据 (使用新的IPC流式监控)
|
||||
#[tauri::command]
|
||||
pub async fn get_traffic_data() -> CmdResult<serde_json::Value> {
|
||||
let traffic = crate::ipc::get_current_traffic().await;
|
||||
let result = serde_json::json!({
|
||||
"up": traffic.total_up,
|
||||
"down": traffic.total_down,
|
||||
"up_rate": traffic.up_rate,
|
||||
"down_rate": traffic.down_rate,
|
||||
"last_updated": traffic.last_updated.elapsed().as_secs()
|
||||
});
|
||||
Ok(result)
|
||||
}
|
||||
// 获取流量数据 (使用新的IPC流式监控)
|
||||
// #[tauri::command]
|
||||
// pub async fn get_traffic_data() -> CmdResult<serde_json::Value> {
|
||||
// let traffic = crate::ipc::get_current_traffic().await;
|
||||
// let result = serde_json::json!({
|
||||
// "up": traffic.total_up,
|
||||
// "down": traffic.total_down,
|
||||
// "up_rate": traffic.up_rate,
|
||||
// "down_rate": traffic.down_rate,
|
||||
// "last_updated": traffic.last_updated.elapsed().as_secs()
|
||||
// });
|
||||
// Ok(result)
|
||||
// }
|
||||
|
||||
// 获取内存数据 (使用新的IPC流式监控)
|
||||
// #[tauri::command]
|
||||
// pub async fn get_memory_data() -> CmdResult<serde_json::Value> {
|
||||
// let memory = crate::ipc::get_current_memory().await;
|
||||
// let usage_percent = if memory.oslimit > 0 {
|
||||
// (memory.inuse as f64 / memory.oslimit as f64) * 100.0
|
||||
// } else {
|
||||
// 0.0
|
||||
// };
|
||||
// let result = serde_json::json!({
|
||||
// "inuse": memory.inuse,
|
||||
// "oslimit": memory.oslimit,
|
||||
// "usage_percent": usage_percent,
|
||||
// "last_updated": memory.last_updated.elapsed().as_secs()
|
||||
// });
|
||||
// Ok(result)
|
||||
// }
|
||||
|
||||
// 启动流量监控服务 (IPC流式监控自动启动,此函数为兼容性保留)
|
||||
// #[tauri::command]
|
||||
// pub async fn start_traffic_service() -> CmdResult {
|
||||
// logging!(trace, Type::Ipc, "启动流量监控服务 (IPC流式监控)");
|
||||
// // 新的IPC监控在首次访问时自动启动
|
||||
// // 触发一次访问以确保监控器已初始化
|
||||
// let _ = crate::ipc::get_current_traffic().await;
|
||||
// let _ = crate::ipc::get_current_memory().await;
|
||||
// logging!(info, Type::Ipc, "IPC流式监控已激活");
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// 停止流量监控服务 (IPC流式监控无需显式停止,此函数为兼容性保留)
|
||||
// #[tauri::command]
|
||||
// pub async fn stop_traffic_service() -> CmdResult {
|
||||
// logging!(trace, Type::Ipc, "停止流量监控服务请求 (IPC流式监控)");
|
||||
// // 新的IPC监控是持久的,无需显式停止
|
||||
// logging!(info, Type::Ipc, "IPC流式监控继续运行");
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// 获取格式化的流量数据 (包含单位,便于前端显示)
|
||||
// #[tauri::command]
|
||||
// pub async fn get_formatted_traffic_data() -> CmdResult<serde_json::Value> {
|
||||
// logging!(trace, Type::Ipc, "获取格式化流量数据");
|
||||
// let (up_rate, down_rate, total_up, total_down, is_fresh) =
|
||||
// crate::ipc::get_formatted_traffic().await;
|
||||
// let result = serde_json::json!({
|
||||
// "up_rate_formatted": up_rate,
|
||||
// "down_rate_formatted": down_rate,
|
||||
// "total_up_formatted": total_up,
|
||||
// "total_down_formatted": total_down,
|
||||
// "is_fresh": is_fresh
|
||||
// });
|
||||
// logging!(
|
||||
// debug,
|
||||
// Type::Ipc,
|
||||
// "格式化流量数据: ↑{up_rate}/s ↓{down_rate}/s (总计: ↑{total_up} ↓{total_down})"
|
||||
// );
|
||||
// Ok(result)
|
||||
// }
|
||||
|
||||
// 获取格式化的内存数据 (包含单位,便于前端显示)
|
||||
// #[tauri::command]
|
||||
// pub async fn get_formatted_memory_data() -> CmdResult<serde_json::Value> {
|
||||
// logging!(info, Type::Ipc, "获取格式化内存数据");
|
||||
// let (inuse, oslimit, usage_percent, is_fresh) = crate::ipc::get_formatted_memory().await;
|
||||
// let result = serde_json::json!({
|
||||
// "inuse_formatted": inuse,
|
||||
// "oslimit_formatted": oslimit,
|
||||
// "usage_percent": usage_percent,
|
||||
// "is_fresh": is_fresh
|
||||
// });
|
||||
// logging!(
|
||||
// debug,
|
||||
// Type::Ipc,
|
||||
// "格式化内存数据: {inuse} / {oslimit} ({usage_percent:.1}%)"
|
||||
// );
|
||||
// Ok(result)
|
||||
// }
|
||||
|
||||
// 获取系统监控概览 (流量+内存,便于前端一次性获取所有状态)
|
||||
// #[tauri::command]
|
||||
// pub async fn get_system_monitor_overview() -> CmdResult<serde_json::Value> {
|
||||
// logging!(debug, Type::Ipc, "获取系统监控概览");
|
||||
|
||||
// // 并发获取流量和内存数据
|
||||
// let (traffic, memory) = tokio::join!(
|
||||
// crate::ipc::get_current_traffic(),
|
||||
// crate::ipc::get_current_memory()
|
||||
// );
|
||||
|
||||
// let (traffic_formatted, memory_formatted) = tokio::join!(
|
||||
// crate::ipc::get_formatted_traffic(),
|
||||
// crate::ipc::get_formatted_memory()
|
||||
// );
|
||||
|
||||
// let traffic_is_fresh = traffic.last_updated.elapsed().as_secs() < 5;
|
||||
// let memory_is_fresh = memory.last_updated.elapsed().as_secs() < 10;
|
||||
|
||||
// let result = serde_json::json!({
|
||||
// "traffic": {
|
||||
// "raw": {
|
||||
// "up": traffic.total_up,
|
||||
// "down": traffic.total_down,
|
||||
// "up_rate": traffic.up_rate,
|
||||
// "down_rate": traffic.down_rate
|
||||
// },
|
||||
// "formatted": {
|
||||
// "up_rate": traffic_formatted.0,
|
||||
// "down_rate": traffic_formatted.1,
|
||||
// "total_up": traffic_formatted.2,
|
||||
// "total_down": traffic_formatted.3
|
||||
// },
|
||||
// "is_fresh": traffic_is_fresh
|
||||
// },
|
||||
// "memory": {
|
||||
// "raw": {
|
||||
// "inuse": memory.inuse,
|
||||
// "oslimit": memory.oslimit,
|
||||
// "usage_percent": if memory.oslimit > 0 {
|
||||
// (memory.inuse as f64 / memory.oslimit as f64) * 100.0
|
||||
// } else {
|
||||
// 0.0
|
||||
// }
|
||||
// },
|
||||
// "formatted": {
|
||||
// "inuse": memory_formatted.0,
|
||||
// "oslimit": memory_formatted.1,
|
||||
// "usage_percent": memory_formatted.2
|
||||
// },
|
||||
// "is_fresh": memory_is_fresh
|
||||
// },
|
||||
// "overall_status": if traffic_is_fresh && memory_is_fresh { "healthy" } else { "stale" }
|
||||
// });
|
||||
|
||||
// Ok(result)
|
||||
// }
|
||||
|
||||
// 获取代理组延迟
|
||||
// #[tauri::command]
|
||||
// pub async fn get_group_proxy_delays(
|
||||
// group_name: String,
|
||||
// url: Option<String>,
|
||||
// timeout: Option<i32>,
|
||||
// ) -> CmdResult<serde_json::Value> {
|
||||
// wrap_err!(
|
||||
// IpcManager::global()
|
||||
// .get_group_proxy_delays(&group_name, url, timeout.unwrap_or(10000))
|
||||
// .await
|
||||
// )
|
||||
// }
|
||||
|
||||
// 检查调试是否启用
|
||||
// #[tauri::command]
|
||||
// pub async fn is_clash_debug_enabled() -> CmdResult<bool> {
|
||||
// match IpcManager::global().is_debug_enabled().await {
|
||||
// Ok(enabled) => Ok(enabled),
|
||||
// Err(_) => Ok(false),
|
||||
// }
|
||||
// }
|
||||
|
||||
// 垃圾回收
|
||||
// #[tauri::command]
|
||||
// pub async fn clash_gc() -> CmdResult {
|
||||
// wrap_err!(IpcManager::global().gc().await)
|
||||
// }
|
||||
|
||||
// 获取日志 (使用新的流式实现)
|
||||
// #[tauri::command]
|
||||
// pub async fn get_clash_logs() -> CmdResult<serde_json::Value> {
|
||||
// Ok(ipc::get_logs_json().await)
|
||||
// }
|
||||
|
||||
/// 获取内存数据 (使用新的IPC流式监控)
|
||||
#[tauri::command]
|
||||
pub async fn get_memory_data() -> CmdResult<serde_json::Value> {
|
||||
let memory = crate::ipc::get_current_memory().await;
|
||||
let usage_percent = if memory.oslimit > 0 {
|
||||
(memory.inuse as f64 / memory.oslimit as f64) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
pub async fn get_clash_logs() -> CmdResult<VecDeque<String>> {
|
||||
let logs = match core::CoreManager::global().get_running_mode() {
|
||||
// TODO: 服务模式下日志获取接口
|
||||
RunningMode::Service => VecDeque::new(),
|
||||
RunningMode::Sidecar => logger::Logger::global().get_logs().clone(),
|
||||
_ => VecDeque::new(),
|
||||
};
|
||||
let result = serde_json::json!({
|
||||
"inuse": memory.inuse,
|
||||
"oslimit": memory.oslimit,
|
||||
"usage_percent": usage_percent,
|
||||
"last_updated": memory.last_updated.elapsed().as_secs()
|
||||
});
|
||||
Ok(result)
|
||||
Ok(logs)
|
||||
}
|
||||
|
||||
/// 启动流量监控服务 (IPC流式监控自动启动,此函数为兼容性保留)
|
||||
#[tauri::command]
|
||||
pub async fn start_traffic_service() -> CmdResult {
|
||||
logging!(trace, Type::Ipc, "启动流量监控服务 (IPC流式监控)");
|
||||
// 新的IPC监控在首次访问时自动启动
|
||||
// 触发一次访问以确保监控器已初始化
|
||||
let _ = crate::ipc::get_current_traffic().await;
|
||||
let _ = crate::ipc::get_current_memory().await;
|
||||
logging!(info, Type::Ipc, "IPC流式监控已激活");
|
||||
Ok(())
|
||||
}
|
||||
// 启动日志监控
|
||||
// #[tauri::command]
|
||||
// pub async fn start_logs_monitoring(level: Option<String>) -> CmdResult {
|
||||
// ipc::start_logs_monitoring(level).await;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/// 停止流量监控服务 (IPC流式监控无需显式停止,此函数为兼容性保留)
|
||||
#[tauri::command]
|
||||
pub async fn stop_traffic_service() -> CmdResult {
|
||||
logging!(trace, Type::Ipc, "停止流量监控服务请求 (IPC流式监控)");
|
||||
// 新的IPC监控是持久的,无需显式停止
|
||||
logging!(info, Type::Ipc, "IPC流式监控继续运行");
|
||||
Ok(())
|
||||
}
|
||||
// 停止日志监控
|
||||
// #[tauri::command]
|
||||
// pub async fn stop_logs_monitoring() -> CmdResult {
|
||||
// ipc::stop_logs_monitoring().await;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/// 获取格式化的流量数据 (包含单位,便于前端显示)
|
||||
#[tauri::command]
|
||||
pub async fn get_formatted_traffic_data() -> CmdResult<serde_json::Value> {
|
||||
logging!(trace, Type::Ipc, "获取格式化流量数据");
|
||||
let (up_rate, down_rate, total_up, total_down, is_fresh) =
|
||||
crate::ipc::get_formatted_traffic().await;
|
||||
let result = serde_json::json!({
|
||||
"up_rate_formatted": up_rate,
|
||||
"down_rate_formatted": down_rate,
|
||||
"total_up_formatted": total_up,
|
||||
"total_down_formatted": total_down,
|
||||
"is_fresh": is_fresh
|
||||
});
|
||||
logging!(
|
||||
debug,
|
||||
Type::Ipc,
|
||||
"格式化流量数据: ↑{up_rate}/s ↓{down_rate}/s (总计: ↑{total_up} ↓{total_down})"
|
||||
);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 获取格式化的内存数据 (包含单位,便于前端显示)
|
||||
#[tauri::command]
|
||||
pub async fn get_formatted_memory_data() -> CmdResult<serde_json::Value> {
|
||||
logging!(info, Type::Ipc, "获取格式化内存数据");
|
||||
let (inuse, oslimit, usage_percent, is_fresh) = crate::ipc::get_formatted_memory().await;
|
||||
let result = serde_json::json!({
|
||||
"inuse_formatted": inuse,
|
||||
"oslimit_formatted": oslimit,
|
||||
"usage_percent": usage_percent,
|
||||
"is_fresh": is_fresh
|
||||
});
|
||||
logging!(
|
||||
debug,
|
||||
Type::Ipc,
|
||||
"格式化内存数据: {inuse} / {oslimit} ({usage_percent:.1}%)"
|
||||
);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 获取系统监控概览 (流量+内存,便于前端一次性获取所有状态)
|
||||
#[tauri::command]
|
||||
pub async fn get_system_monitor_overview() -> CmdResult<serde_json::Value> {
|
||||
logging!(debug, Type::Ipc, "获取系统监控概览");
|
||||
|
||||
// 并发获取流量和内存数据
|
||||
let (traffic, memory) = tokio::join!(
|
||||
crate::ipc::get_current_traffic(),
|
||||
crate::ipc::get_current_memory()
|
||||
);
|
||||
|
||||
let (traffic_formatted, memory_formatted) = tokio::join!(
|
||||
crate::ipc::get_formatted_traffic(),
|
||||
crate::ipc::get_formatted_memory()
|
||||
);
|
||||
|
||||
let traffic_is_fresh = traffic.last_updated.elapsed().as_secs() < 5;
|
||||
let memory_is_fresh = memory.last_updated.elapsed().as_secs() < 10;
|
||||
|
||||
let result = serde_json::json!({
|
||||
"traffic": {
|
||||
"raw": {
|
||||
"up": traffic.total_up,
|
||||
"down": traffic.total_down,
|
||||
"up_rate": traffic.up_rate,
|
||||
"down_rate": traffic.down_rate
|
||||
},
|
||||
"formatted": {
|
||||
"up_rate": traffic_formatted.0,
|
||||
"down_rate": traffic_formatted.1,
|
||||
"total_up": traffic_formatted.2,
|
||||
"total_down": traffic_formatted.3
|
||||
},
|
||||
"is_fresh": traffic_is_fresh
|
||||
},
|
||||
"memory": {
|
||||
"raw": {
|
||||
"inuse": memory.inuse,
|
||||
"oslimit": memory.oslimit,
|
||||
"usage_percent": if memory.oslimit > 0 {
|
||||
(memory.inuse as f64 / memory.oslimit as f64) * 100.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
},
|
||||
"formatted": {
|
||||
"inuse": memory_formatted.0,
|
||||
"oslimit": memory_formatted.1,
|
||||
"usage_percent": memory_formatted.2
|
||||
},
|
||||
"is_fresh": memory_is_fresh
|
||||
},
|
||||
"overall_status": if traffic_is_fresh && memory_is_fresh { "healthy" } else { "stale" }
|
||||
});
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// 获取代理组延迟
|
||||
#[tauri::command]
|
||||
pub async fn get_group_proxy_delays(
|
||||
group_name: String,
|
||||
url: Option<String>,
|
||||
timeout: Option<i32>,
|
||||
) -> CmdResult<serde_json::Value> {
|
||||
wrap_err!(
|
||||
IpcManager::global()
|
||||
.get_group_proxy_delays(&group_name, url, timeout.unwrap_or(10000))
|
||||
.await
|
||||
)
|
||||
}
|
||||
|
||||
/// 检查调试是否启用
|
||||
#[tauri::command]
|
||||
pub async fn is_clash_debug_enabled() -> CmdResult<bool> {
|
||||
match IpcManager::global().is_debug_enabled().await {
|
||||
Ok(enabled) => Ok(enabled),
|
||||
Err(_) => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// 垃圾回收
|
||||
#[tauri::command]
|
||||
pub async fn clash_gc() -> CmdResult {
|
||||
wrap_err!(IpcManager::global().gc().await)
|
||||
}
|
||||
|
||||
/// 获取日志 (使用新的流式实现)
|
||||
#[tauri::command]
|
||||
pub async fn get_clash_logs() -> CmdResult<serde_json::Value> {
|
||||
Ok(ipc::get_logs_json().await)
|
||||
}
|
||||
|
||||
/// 启动日志监控
|
||||
#[tauri::command]
|
||||
pub async fn start_logs_monitoring(level: Option<String>) -> CmdResult {
|
||||
ipc::start_logs_monitoring(level).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 停止日志监控
|
||||
#[tauri::command]
|
||||
pub async fn stop_logs_monitoring() -> CmdResult {
|
||||
ipc::stop_logs_monitoring().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 清除日志
|
||||
#[tauri::command]
|
||||
pub async fn clear_logs() -> CmdResult {
|
||||
ipc::clear_logs().await;
|
||||
Ok(())
|
||||
}
|
||||
// 清除日志
|
||||
// #[tauri::command]
|
||||
// pub async fn clear_logs() -> CmdResult {
|
||||
// ipc::clear_logs().await;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
@@ -481,11 +481,11 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
|
||||
handle::Handle::refresh_clash();
|
||||
|
||||
// 强制刷新代理缓存,确保profile切换后立即获取最新节点数据
|
||||
crate::process::AsyncHandler::spawn(|| async move {
|
||||
if let Err(e) = super::proxy::force_refresh_proxies().await {
|
||||
log::warn!(target: "app", "强制刷新代理缓存失败: {e}");
|
||||
}
|
||||
});
|
||||
// crate::process::AsyncHandler::spawn(|| async move {
|
||||
// if let Err(e) = super::proxy::force_refresh_proxies().await {
|
||||
// log::warn!(target: "app", "强制刷新代理缓存失败: {e}");
|
||||
// }
|
||||
// });
|
||||
|
||||
if let Err(e) = Tray::global().update_tooltip().await {
|
||||
log::warn!(target: "app", "异步更新托盘提示失败: {e}");
|
||||
|
||||
@@ -3,57 +3,60 @@ use tauri::Emitter;
|
||||
use super::CmdResult;
|
||||
use crate::{
|
||||
cache::CacheProxy,
|
||||
core::{handle::Handle, tray::Tray},
|
||||
ipc::IpcManager,
|
||||
core::{
|
||||
handle::{self, Handle},
|
||||
tray::Tray,
|
||||
},
|
||||
logging,
|
||||
utils::logging::Type,
|
||||
};
|
||||
use std::time::Duration;
|
||||
// use std::time::Duration;
|
||||
|
||||
const PROXIES_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
|
||||
const PROVIDERS_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
|
||||
// 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<serde_json::Value> {
|
||||
let cache = CacheProxy::global();
|
||||
let key = CacheProxy::make_key("proxies", "default");
|
||||
let value = cache
|
||||
.get_or_fetch(key, PROXIES_REFRESH_INTERVAL, || async {
|
||||
let manager = IpcManager::global();
|
||||
manager.get_proxies().await.unwrap_or_else(|e| {
|
||||
logging!(error, Type::Cmd, "Failed to fetch proxies: {e}");
|
||||
serde_json::Value::Object(serde_json::Map::new())
|
||||
})
|
||||
})
|
||||
.await;
|
||||
Ok((*value).clone())
|
||||
}
|
||||
// #[tauri::command]
|
||||
// pub async fn get_proxies() -> CmdResult<serde_json::Value> {
|
||||
// let cache = CacheProxy::global();
|
||||
// let key = CacheProxy::make_key("proxies", "default");
|
||||
// let value = cache
|
||||
// .get_or_fetch(key, PROXIES_REFRESH_INTERVAL, || async {
|
||||
// let manager = IpcManager::global();
|
||||
// manager.get_proxies().await.unwrap_or_else(|e| {
|
||||
// logging!(error, Type::Cmd, "Failed to fetch proxies: {e}");
|
||||
// serde_json::Value::Object(serde_json::Map::new())
|
||||
// })
|
||||
// })
|
||||
// .await;
|
||||
// Ok((*value).clone())
|
||||
// }
|
||||
|
||||
/// 强制刷新代理缓存用于profile切换
|
||||
#[tauri::command]
|
||||
pub async fn force_refresh_proxies() -> CmdResult<serde_json::Value> {
|
||||
let cache = CacheProxy::global();
|
||||
let key = CacheProxy::make_key("proxies", "default");
|
||||
cache.map.remove(&key);
|
||||
get_proxies().await
|
||||
}
|
||||
// #[tauri::command]
|
||||
// pub async fn force_refresh_proxies() -> CmdResult<serde_json::Value> {
|
||||
// let cache = CacheProxy::global();
|
||||
// let key = CacheProxy::make_key("proxies", "default");
|
||||
// cache.map.remove(&key);
|
||||
// get_proxies().await
|
||||
// }
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
|
||||
let cache = CacheProxy::global();
|
||||
let key = CacheProxy::make_key("providers", "default");
|
||||
let value = cache
|
||||
.get_or_fetch(key, PROVIDERS_REFRESH_INTERVAL, || async {
|
||||
let manager = IpcManager::global();
|
||||
manager.get_providers_proxies().await.unwrap_or_else(|e| {
|
||||
logging!(error, Type::Cmd, "Failed to fetch provider proxies: {e}");
|
||||
serde_json::Value::Object(serde_json::Map::new())
|
||||
})
|
||||
})
|
||||
.await;
|
||||
Ok((*value).clone())
|
||||
}
|
||||
// #[tauri::command]
|
||||
// pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
|
||||
// let cache = CacheProxy::global();
|
||||
// let key = CacheProxy::make_key("providers", "default");
|
||||
// let value = cache
|
||||
// .get_or_fetch(key, PROVIDERS_REFRESH_INTERVAL, || async {
|
||||
// let manager = IpcManager::global();
|
||||
// manager.get_providers_proxies().await.unwrap_or_else(|e| {
|
||||
// logging!(error, Type::Cmd, "Failed to fetch provider proxies: {e}");
|
||||
// serde_json::Value::Object(serde_json::Map::new())
|
||||
// })
|
||||
// })
|
||||
// .await;
|
||||
// Ok((*value).clone())
|
||||
// }
|
||||
|
||||
// TODO: 前端通过 emit 发送更新事件, tray 监听更新事件
|
||||
/// 同步托盘和GUI的代理选择状态
|
||||
#[tauri::command]
|
||||
pub async fn sync_tray_proxy_selection() -> CmdResult<()> {
|
||||
@@ -74,7 +77,11 @@ pub async fn sync_tray_proxy_selection() -> CmdResult<()> {
|
||||
/// 更新代理选择并同步托盘和GUI状态
|
||||
#[tauri::command]
|
||||
pub async fn update_proxy_and_sync(group: String, proxy: String) -> CmdResult<()> {
|
||||
match IpcManager::global().update_proxy(&group, &proxy).await {
|
||||
match handle::Handle::mihomo()
|
||||
.await
|
||||
.select_node_for_group(&group, &proxy)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
// println!("Proxy updated successfully: {} -> {}", group,proxy);
|
||||
logging!(
|
||||
@@ -93,10 +100,8 @@ pub async fn update_proxy_and_sync(group: String, proxy: String) -> CmdResult<()
|
||||
logging!(error, Type::Cmd, "Failed to sync tray menu: {}", e);
|
||||
}
|
||||
|
||||
if let Some(app_handle) = Handle::global().app_handle() {
|
||||
let _ = app_handle.emit("verge://force-refresh-proxies", ());
|
||||
let _ = app_handle.emit("verge://refresh-proxy-config", ());
|
||||
}
|
||||
let app_handle = Handle::app_handle();
|
||||
let _ = app_handle.emit("verge://refresh-proxy-config", ());
|
||||
|
||||
logging!(
|
||||
info,
|
||||
|
||||
@@ -28,9 +28,7 @@ pub async fn export_diagnostic_info() -> CmdResult<()> {
|
||||
let sysinfo = PlatformSpecification::new_sync();
|
||||
let info = format!("{sysinfo:?}");
|
||||
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or("Failed to get app handle")?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let cliboard = app_handle.clipboard();
|
||||
if cliboard.write_text(info).is_err() {
|
||||
logging!(error, Type::System, "Failed to write to clipboard");
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::AsyncHandler;
|
||||
use crate::core::logger::Logger;
|
||||
use crate::{
|
||||
config::*,
|
||||
core::{
|
||||
handle,
|
||||
service::{self, SERVICE_MANAGER, ServiceStatus},
|
||||
},
|
||||
ipc::IpcManager,
|
||||
logging, logging_error, singleton_lazy,
|
||||
utils::{
|
||||
dirs,
|
||||
@@ -250,11 +250,7 @@ impl CoreManager {
|
||||
let clash_core = Config::verge().await.latest_ref().get_valid_clash_core();
|
||||
logging!(info, Type::Config, true, "使用内核: {}", clash_core);
|
||||
|
||||
let app_handle = handle::Handle::global().app_handle().ok_or_else(|| {
|
||||
let msg = "Failed to get app handle";
|
||||
logging!(error, Type::Core, true, "{}", msg);
|
||||
anyhow::anyhow!(msg)
|
||||
})?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let app_dir = dirs::app_home_dir()?;
|
||||
let app_dir_str = dirs::path_to_str(&app_dir)?;
|
||||
logging!(info, Type::Config, true, "验证目录: {}", app_dir_str);
|
||||
@@ -414,7 +410,11 @@ impl CoreManager {
|
||||
logging_error!(Type::Core, true, "{}", msg);
|
||||
msg
|
||||
});
|
||||
match IpcManager::global().put_configs_force(run_path_str?).await {
|
||||
match handle::Handle::mihomo()
|
||||
.await
|
||||
.reload_config(true, run_path_str?)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
Config::runtime().await.apply();
|
||||
logging!(info, Type::Core, true, "Configuration updated successfully");
|
||||
@@ -733,9 +733,7 @@ impl CoreManager {
|
||||
logging!(info, Type::Core, true, "Running core by sidecar");
|
||||
|
||||
let config_file = &Config::generate_file(ConfigType::Run).await?;
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or(anyhow::anyhow!("failed to get app handle"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let clash_core = Config::verge().await.latest_ref().get_valid_clash_core();
|
||||
let config_dir = dirs::app_home_dir()?;
|
||||
|
||||
@@ -774,12 +772,16 @@ impl CoreManager {
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
tauri_plugin_shell::process::CommandEvent::Stdout(line) => {
|
||||
if let Err(e) = writeln!(log_file, "{}", String::from_utf8_lossy(&line)) {
|
||||
let line = String::from_utf8_lossy(&line);
|
||||
Logger::global().append_log(line.to_string());
|
||||
if let Err(e) = writeln!(log_file, "{}", line) {
|
||||
eprintln!("[Sidecar] write stdout failed: {e}");
|
||||
}
|
||||
}
|
||||
tauri_plugin_shell::process::CommandEvent::Stderr(line) => {
|
||||
let _ = writeln!(log_file, "[stderr] {}", String::from_utf8_lossy(&line));
|
||||
let line = String::from_utf8_lossy(&line);
|
||||
Logger::global().append_log(line.to_string());
|
||||
let _ = writeln!(log_file, "[stderr] {}", line);
|
||||
}
|
||||
tauri_plugin_shell::process::CommandEvent::Terminated(term) => {
|
||||
let _ = writeln!(log_file, "[terminated] {:?}", term);
|
||||
@@ -900,6 +902,7 @@ impl CoreManager {
|
||||
|
||||
/// 停止核心运行
|
||||
pub async fn stop_core(&self) -> Result<()> {
|
||||
Logger::global().clear_logs();
|
||||
match self.get_running_mode() {
|
||||
RunningMode::Service => self.stop_core_by_service().await,
|
||||
RunningMode::Sidecar => self.stop_core_by_sidecar(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::singleton;
|
||||
use crate::{APP_HANDLE, singleton};
|
||||
use parking_lot::RwLock;
|
||||
use std::{
|
||||
sync::{
|
||||
@@ -10,6 +10,8 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tauri::{AppHandle, Emitter, Manager, WebviewWindow};
|
||||
use tauri_plugin_mihomo::{Mihomo, MihomoExt};
|
||||
use tokio::sync::{RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use crate::{logging, utils::logging::Type};
|
||||
|
||||
@@ -107,7 +109,7 @@ impl NotificationSystem {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(window) = handle.get_window() {
|
||||
if let Some(window) = Handle::get_window() {
|
||||
*system.last_emit_time.write() = Instant::now();
|
||||
|
||||
let (event_name_str, payload_result) = match event {
|
||||
@@ -249,7 +251,6 @@ impl NotificationSystem {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Handle {
|
||||
pub app_handle: Arc<RwLock<Option<AppHandle>>>,
|
||||
pub is_exiting: Arc<RwLock<bool>>,
|
||||
startup_errors: Arc<RwLock<Vec<ErrorMessage>>>,
|
||||
startup_completed: Arc<RwLock<bool>>,
|
||||
@@ -259,7 +260,6 @@ pub struct Handle {
|
||||
impl Default for Handle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
app_handle: Arc::new(RwLock::new(None)),
|
||||
is_exiting: Arc::new(RwLock::new(false)),
|
||||
startup_errors: Arc::new(RwLock::new(Vec::new())),
|
||||
startup_completed: Arc::new(RwLock::new(false)),
|
||||
@@ -276,12 +276,7 @@ impl Handle {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn init(&self, app_handle: AppHandle) {
|
||||
{
|
||||
let mut handle = self.app_handle.write();
|
||||
*handle = Some(app_handle);
|
||||
}
|
||||
|
||||
pub fn init(&self) {
|
||||
let mut system_opt = self.notification_system.write();
|
||||
if let Some(system) = system_opt.as_mut() {
|
||||
system.start();
|
||||
@@ -289,12 +284,21 @@ impl Handle {
|
||||
}
|
||||
|
||||
/// 获取 AppHandle
|
||||
pub fn app_handle(&self) -> Option<AppHandle> {
|
||||
self.app_handle.read().clone()
|
||||
pub fn app_handle() -> &'static AppHandle {
|
||||
APP_HANDLE.get().expect("failed to get global app handle")
|
||||
}
|
||||
|
||||
pub fn get_window(&self) -> Option<WebviewWindow> {
|
||||
let app_handle = self.app_handle()?;
|
||||
pub async fn mihomo() -> RwLockReadGuard<'static, Mihomo> {
|
||||
Self::app_handle().mihomo().read().await
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub async fn mihomo_mut() -> RwLockWriteGuard<'static, Mihomo> {
|
||||
Self::app_handle().mihomo().write().await
|
||||
}
|
||||
|
||||
pub fn get_window() -> Option<WebviewWindow> {
|
||||
let app_handle = Self::app_handle();
|
||||
let window: Option<WebviewWindow> = app_handle.get_webview_window("main");
|
||||
if window.is_none() {
|
||||
log::debug!(target:"app", "main window not found");
|
||||
|
||||
@@ -200,9 +200,7 @@ impl Hotkey {
|
||||
hotkey: &str,
|
||||
function: HotkeyFunction,
|
||||
) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let manager = app_handle.global_shortcut();
|
||||
|
||||
logging!(
|
||||
@@ -375,9 +373,7 @@ impl Hotkey {
|
||||
}
|
||||
|
||||
pub fn reset(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let manager = app_handle.global_shortcut();
|
||||
manager.unregister_all()?;
|
||||
Ok(())
|
||||
@@ -390,9 +386,7 @@ impl Hotkey {
|
||||
}
|
||||
|
||||
pub fn unregister(&self, hotkey: &str) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for hotkey registration"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let manager = app_handle.global_shortcut();
|
||||
manager.unregister(hotkey)?;
|
||||
logging!(debug, Type::Hotkey, "Unregister hotkey {}", hotkey);
|
||||
@@ -468,17 +462,7 @@ impl Hotkey {
|
||||
|
||||
impl Drop for Hotkey {
|
||||
fn drop(&mut self) {
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Hotkey,
|
||||
"Failed to get app handle during hotkey cleanup"
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
if let Err(e) = app_handle.global_shortcut().unregister_all() {
|
||||
logging!(
|
||||
error,
|
||||
|
||||
37
src-tauri/src/core/logger.rs
Normal file
37
src-tauri/src/core/logger.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use std::{collections::VecDeque, sync::Arc};
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::{RwLock, RwLockReadGuard};
|
||||
|
||||
const LOGS_QUEUE_LEN: usize = 100;
|
||||
|
||||
pub struct Logger {
|
||||
logs: Arc<RwLock<VecDeque<String>>>,
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
pub fn global() -> &'static Logger {
|
||||
static LOGGER: OnceCell<Logger> = OnceCell::new();
|
||||
|
||||
LOGGER.get_or_init(|| Logger {
|
||||
logs: Arc::new(RwLock::new(VecDeque::with_capacity(LOGS_QUEUE_LEN + 10))),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_logs(&self) -> RwLockReadGuard<'_, VecDeque<String>> {
|
||||
self.logs.read()
|
||||
}
|
||||
|
||||
pub fn append_log(&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();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ mod core;
|
||||
pub mod event_driven_proxy;
|
||||
pub mod handle;
|
||||
pub mod hotkey;
|
||||
pub mod logger;
|
||||
pub mod service;
|
||||
pub mod service_ipc;
|
||||
pub mod sysopt;
|
||||
|
||||
@@ -293,10 +293,7 @@ impl Sysopt {
|
||||
|
||||
/// 尝试使用原来的自启动方法
|
||||
fn try_original_autostart_method(&self, is_enable: bool) {
|
||||
let Some(app_handle) = Handle::global().app_handle() else {
|
||||
log::error!(target: "app", "App handle not available for autostart");
|
||||
return;
|
||||
};
|
||||
let app_handle = Handle::app_handle();
|
||||
let autostart_manager = app_handle.autolaunch();
|
||||
|
||||
if is_enable {
|
||||
@@ -323,9 +320,7 @@ impl Sysopt {
|
||||
}
|
||||
|
||||
// 回退到原来的方法
|
||||
let app_handle = Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("App handle not available"))?;
|
||||
let app_handle = Handle::app_handle();
|
||||
let autostart_manager = app_handle.autolaunch();
|
||||
|
||||
match autostart_manager.is_enabled() {
|
||||
|
||||
@@ -3,16 +3,13 @@ use tauri::Emitter;
|
||||
use tauri::tray::TrayIconBuilder;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod speed_rate;
|
||||
use crate::ipc::Rate;
|
||||
use crate::module::lightweight;
|
||||
use crate::process::AsyncHandler;
|
||||
use crate::utils::window_manager::WindowManager;
|
||||
use crate::{
|
||||
Type, cmd,
|
||||
config::Config,
|
||||
feat,
|
||||
ipc::IpcManager,
|
||||
logging,
|
||||
feat, logging,
|
||||
module::lightweight::is_in_lightweight_mode,
|
||||
singleton_lazy,
|
||||
utils::{dirs::find_target_icons, i18n::t},
|
||||
@@ -54,7 +51,7 @@ fn should_handle_tray_click() -> bool {
|
||||
*last_click = now;
|
||||
true
|
||||
} else {
|
||||
log::debug!(target: "app", "托盘点击被防抖机制忽略,距离上次点击 {:?}ms",
|
||||
log::debug!(target: "app", "托盘点击被防抖机制忽略,距离上次点击 {:?}ms",
|
||||
now.duration_since(*last_click).as_millis());
|
||||
false
|
||||
}
|
||||
@@ -189,18 +186,14 @@ singleton_lazy!(Tray, TRAY, Tray::default);
|
||||
|
||||
impl Tray {
|
||||
pub async fn init(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray initialization"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
self.create_tray_from_handle(&app_handle).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 更新托盘点击行为
|
||||
pub async fn update_click_behavior(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray update"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let tray_event = { Config::verge().await.latest_ref().tray_event.clone() };
|
||||
let tray_event: String = tray_event.unwrap_or("main_window".into());
|
||||
let tray = app_handle
|
||||
@@ -240,13 +233,7 @@ impl Tray {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::warn!(target: "app", "更新托盘菜单失败: app_handle不存在");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
// 设置更新状态
|
||||
self.menu_updating.store(true, Ordering::Release);
|
||||
@@ -309,7 +296,7 @@ impl Tray {
|
||||
/// 更新托盘图标
|
||||
#[cfg(target_os = "macos")]
|
||||
pub async fn update_icon(&self, _rate: Option<Rate>) -> Result<()> {
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
let app_handle = match handle::Handle::app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::warn!(target: "app", "更新托盘图标失败: app_handle不存在");
|
||||
@@ -345,14 +332,8 @@ impl Tray {
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub async fn update_icon(&self, _rate: Option<Rate>) -> Result<()> {
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::warn!(target: "app", "更新托盘图标失败: app_handle不存在");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
pub async fn update_icon(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
let tray = match app_handle.tray_by_id("main") {
|
||||
Some(tray) => tray,
|
||||
@@ -379,9 +360,7 @@ impl Tray {
|
||||
|
||||
/// 更新托盘显示状态的函数
|
||||
pub async fn update_tray_display(&self) -> Result<()> {
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get app handle for tray update"))?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let _tray = app_handle
|
||||
.tray_by_id("main")
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?;
|
||||
@@ -394,13 +373,7 @@ impl Tray {
|
||||
|
||||
/// 更新托盘提示
|
||||
pub async fn update_tooltip(&self) -> Result<()> {
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::warn!(target: "app", "更新托盘提示失败: app_handle不存在");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
let verge = Config::verge().await.latest_ref().clone();
|
||||
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
|
||||
@@ -454,7 +427,7 @@ impl Tray {
|
||||
// self.update_menu().await?;
|
||||
// 更新轻量模式显示状态
|
||||
self.update_tray_display().await?;
|
||||
self.update_icon(None).await?;
|
||||
self.update_icon().await?;
|
||||
self.update_tooltip().await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -540,7 +513,7 @@ impl Tray {
|
||||
// 确保所有状态更新完成
|
||||
self.update_tray_display().await?;
|
||||
// self.update_menu().await?;
|
||||
self.update_icon(None).await?;
|
||||
self.update_icon().await?;
|
||||
self.update_tooltip().await?;
|
||||
|
||||
Ok(())
|
||||
@@ -568,14 +541,7 @@ async fn create_tray_menu(
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
let proxy_nodes_data = cmd::get_proxies().await.unwrap_or_else(|e| {
|
||||
logging!(
|
||||
error,
|
||||
Type::Cmd,
|
||||
"Failed to fetch proxies for tray menu: {e}"
|
||||
);
|
||||
serde_json::Value::Object(serde_json::Map::new())
|
||||
});
|
||||
let proxy_nodes_data = handle::Handle::mihomo().await.get_proxies().await?;
|
||||
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
|
||||
@@ -628,102 +594,96 @@ async fn create_tray_menu(
|
||||
let mut submenus = Vec::new();
|
||||
let mut group_name_submenus_hash = HashMap::new();
|
||||
|
||||
if let Some(proxies) = proxy_nodes_data.get("proxies").and_then(|v| v.as_object()) {
|
||||
for (group_name, group_data) in proxies.iter() {
|
||||
// Filter groups based on mode
|
||||
let should_show = match mode {
|
||||
for (group_name, group_data) in proxy_nodes_data.proxies.iter() {
|
||||
// Filter groups based on mode
|
||||
let should_show = match mode {
|
||||
"global" => group_name == "GLOBAL",
|
||||
_ => group_name != "GLOBAL",
|
||||
} &&
|
||||
// Check if the group is hidden
|
||||
!group_data.get("hidden").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
!group_data.hidden.unwrap_or_default();
|
||||
|
||||
if !should_show {
|
||||
continue;
|
||||
if !should_show {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(all_proxies) = group_data.all.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let now_proxy = group_data.now.as_deref().unwrap_or_default();
|
||||
|
||||
// Create proxy items
|
||||
let group_items: Vec<CheckMenuItem<Wry>> = all_proxies
|
||||
.iter()
|
||||
.filter_map(|proxy_str| {
|
||||
let is_selected = *proxy_str == now_proxy;
|
||||
let item_id = format!("proxy_{}_{}", group_name, proxy_str);
|
||||
|
||||
// Get delay for display
|
||||
let delay_text = proxy_nodes_data
|
||||
.proxies
|
||||
.get(proxy_str)
|
||||
.and_then(|h| h.history.last())
|
||||
.map(|h| match h.delay {
|
||||
0 => "-ms".to_string(),
|
||||
delay if delay >= 10000 => "-ms".to_string(),
|
||||
_ => format!("{}ms", h.delay),
|
||||
})
|
||||
.unwrap_or_else(|| "-ms".to_string());
|
||||
|
||||
let display_text = format!("{} | {}", proxy_str, delay_text);
|
||||
|
||||
CheckMenuItem::with_id(
|
||||
app_handle,
|
||||
item_id,
|
||||
display_text,
|
||||
true,
|
||||
is_selected,
|
||||
None::<&str>,
|
||||
)
|
||||
.map_err(|e| log::warn!(target: "app", "创建代理菜单项失败: {}", e))
|
||||
.ok()
|
||||
})
|
||||
.collect();
|
||||
|
||||
if group_items.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine if group is active
|
||||
let is_group_active = match mode {
|
||||
"global" => group_name == "GLOBAL" && !now_proxy.is_empty(),
|
||||
"direct" => false,
|
||||
_ => {
|
||||
current_profile_selected
|
||||
.iter()
|
||||
.any(|s| s.name.as_deref() == Some(&group_name))
|
||||
&& !now_proxy.is_empty()
|
||||
}
|
||||
};
|
||||
|
||||
let Some(all_proxies) = group_data.get("all").and_then(|v| v.as_array()) else {
|
||||
continue;
|
||||
};
|
||||
let group_display_name = if is_group_active {
|
||||
format!("✓ {}", group_name)
|
||||
} else {
|
||||
group_name.to_string()
|
||||
};
|
||||
|
||||
let now_proxy = group_data.get("now").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let group_items_refs: Vec<&dyn IsMenuItem<Wry>> = group_items
|
||||
.iter()
|
||||
.map(|item| item as &dyn IsMenuItem<Wry>)
|
||||
.collect();
|
||||
|
||||
// Create proxy items
|
||||
let group_items: Vec<CheckMenuItem<Wry>> = all_proxies
|
||||
.iter()
|
||||
.filter_map(|proxy_name| proxy_name.as_str())
|
||||
.filter_map(|proxy_str| {
|
||||
let is_selected = proxy_str == now_proxy;
|
||||
let item_id = format!("proxy_{}_{}", group_name, proxy_str);
|
||||
|
||||
// Get delay for display
|
||||
let delay_text = proxies
|
||||
.get(proxy_str)
|
||||
.and_then(|p| p.get("history"))
|
||||
.and_then(|h| h.as_array())
|
||||
.and_then(|h| h.last())
|
||||
.and_then(|r| r.get("delay"))
|
||||
.and_then(|d| d.as_i64())
|
||||
.map(|delay| match delay {
|
||||
-1 => "-ms".to_string(),
|
||||
delay if delay >= 10000 => "-ms".to_string(),
|
||||
_ => format!("{}ms", delay),
|
||||
})
|
||||
.unwrap_or_else(|| "-ms".to_string());
|
||||
|
||||
let display_text = format!("{} | {}", proxy_str, delay_text);
|
||||
|
||||
CheckMenuItem::with_id(
|
||||
app_handle,
|
||||
item_id,
|
||||
display_text,
|
||||
true,
|
||||
is_selected,
|
||||
None::<&str>,
|
||||
)
|
||||
.map_err(|e| log::warn!(target: "app", "创建代理菜单项失败: {}", e))
|
||||
.ok()
|
||||
})
|
||||
.collect();
|
||||
|
||||
if group_items.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine if group is active
|
||||
let is_group_active = match mode {
|
||||
"global" => group_name == "GLOBAL" && !now_proxy.is_empty(),
|
||||
"direct" => false,
|
||||
_ => {
|
||||
current_profile_selected
|
||||
.iter()
|
||||
.any(|s| s.name.as_deref() == Some(group_name))
|
||||
&& !now_proxy.is_empty()
|
||||
}
|
||||
};
|
||||
|
||||
let group_display_name = if is_group_active {
|
||||
format!("✓ {}", group_name)
|
||||
} else {
|
||||
group_name.to_string()
|
||||
};
|
||||
|
||||
let group_items_refs: Vec<&dyn IsMenuItem<Wry>> = group_items
|
||||
.iter()
|
||||
.map(|item| item as &dyn IsMenuItem<Wry>)
|
||||
.collect();
|
||||
|
||||
if let Ok(submenu) = Submenu::with_id_and_items(
|
||||
app_handle,
|
||||
format!("proxy_group_{}", group_name),
|
||||
group_display_name,
|
||||
true,
|
||||
&group_items_refs,
|
||||
) {
|
||||
group_name_submenus_hash.insert(group_name.to_string(), submenu);
|
||||
} else {
|
||||
log::warn!(target: "app", "创建代理组子菜单失败: {}", group_name);
|
||||
}
|
||||
if let Ok(submenu) = Submenu::with_id_and_items(
|
||||
app_handle,
|
||||
format!("proxy_group_{}", group_name),
|
||||
group_display_name,
|
||||
true,
|
||||
&group_items_refs,
|
||||
) {
|
||||
group_name_submenus_hash.insert(group_name.to_string(), submenu);
|
||||
} else {
|
||||
log::warn!(target: "app", "创建代理组子菜单失败: {}", group_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1069,16 +1029,16 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
|
||||
log::error!(target: "app", "切换代理失败: {} -> {}, 错误: {:?}", group_name, proxy_name, e);
|
||||
|
||||
// Fallback to IPC update
|
||||
if (IpcManager::global()
|
||||
.update_proxy(group_name, proxy_name)
|
||||
if (handle::Handle::mihomo()
|
||||
.await
|
||||
.select_node_for_group(group_name, proxy_name)
|
||||
.await)
|
||||
.is_ok()
|
||||
{
|
||||
log::info!(target: "app", "代理切换回退成功: {} -> {}", group_name, proxy_name);
|
||||
|
||||
if let Some(app_handle) = handle::Handle::global().app_handle() {
|
||||
let _ = app_handle.emit("verge://force-refresh-proxies", ());
|
||||
}
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let _ = app_handle.emit("verge://force-refresh-proxies", ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
config::Config,
|
||||
core::{CoreManager, handle, tray},
|
||||
ipc::IpcManager,
|
||||
logging_error,
|
||||
process::AsyncHandler,
|
||||
utils::{logging::Type, resolve},
|
||||
@@ -27,30 +26,18 @@ pub async fn restart_app() {
|
||||
// logging_error!(Type::Core, true, CoreManager::global().stop_core().await);
|
||||
resolve::resolve_reset_async().await;
|
||||
|
||||
handle::Handle::global()
|
||||
.app_handle()
|
||||
.map(|app_handle| {
|
||||
app_handle.restart();
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
logging_error!(
|
||||
Type::System,
|
||||
false,
|
||||
"{}",
|
||||
"Failed to get app handle for restart"
|
||||
);
|
||||
});
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
app_handle.restart();
|
||||
}
|
||||
|
||||
fn after_change_clash_mode() {
|
||||
AsyncHandler::spawn(move || async {
|
||||
match IpcManager::global().get_connections().await {
|
||||
let mihomo = handle::Handle::mihomo().await;
|
||||
match mihomo.get_connections().await {
|
||||
Ok(connections) => {
|
||||
if let Some(connections_array) = connections["connections"].as_array() {
|
||||
if let Some(connections_array) = connections.connections {
|
||||
for connection in connections_array {
|
||||
if let Some(id) = connection["id"].as_str() {
|
||||
let _ = IpcManager::global().delete_connection(id).await;
|
||||
}
|
||||
let _ = mihomo.close_connection(&connection.id).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,7 +57,11 @@ pub async fn change_clash_mode(mode: String) {
|
||||
"mode": mode
|
||||
});
|
||||
log::debug!(target: "app", "change clash mode to {mode}");
|
||||
match IpcManager::global().patch_configs(json_value).await {
|
||||
match handle::Handle::mihomo()
|
||||
.await
|
||||
.patch_base_config(&json_value)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
// 更新订阅
|
||||
Config::clash().await.data_mut().patch_config(mapping);
|
||||
@@ -80,11 +71,7 @@ pub async fn change_clash_mode(mode: String) {
|
||||
if clash_data.save_config().await.is_ok() {
|
||||
handle::Handle::refresh_clash();
|
||||
logging_error!(Type::Tray, true, tray::Tray::global().update_menu().await);
|
||||
logging_error!(
|
||||
Type::Tray,
|
||||
true,
|
||||
tray::Tray::global().update_icon(None).await
|
||||
);
|
||||
logging_error!(Type::Tray, true, tray::Tray::global().update_icon().await);
|
||||
}
|
||||
|
||||
let is_auto_close_connection = Config::verge()
|
||||
|
||||
@@ -23,11 +23,7 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
|
||||
} else {
|
||||
if patch.get("mode").is_some() {
|
||||
logging_error!(Type::Tray, true, tray::Tray::global().update_menu().await);
|
||||
logging_error!(
|
||||
Type::Tray,
|
||||
true,
|
||||
tray::Tray::global().update_icon(None).await
|
||||
);
|
||||
logging_error!(Type::Tray, true, tray::Tray::global().update_icon().await);
|
||||
}
|
||||
Config::runtime().await.draft_mut().patch_config(patch);
|
||||
CoreManager::global().update_config().await?;
|
||||
@@ -211,7 +207,7 @@ pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> {
|
||||
tray::Tray::global().update_menu().await?;
|
||||
}
|
||||
if (update_flags & (UpdateFlags::SystrayIcon as i32)) != 0 {
|
||||
tray::Tray::global().update_icon(None).await?;
|
||||
tray::Tray::global().update_icon().await?;
|
||||
}
|
||||
if (update_flags & (UpdateFlags::SystrayTooltip as i32)) != 0 {
|
||||
tray::Tray::global().update_tooltip().await?;
|
||||
|
||||
@@ -143,15 +143,15 @@ pub async fn update_profile(
|
||||
Ok(_) => {
|
||||
logging!(info, Type::Config, true, "[订阅更新] 更新成功");
|
||||
handle::Handle::refresh_clash();
|
||||
if let Err(err) = cmd::proxy::force_refresh_proxies().await {
|
||||
logging!(
|
||||
error,
|
||||
Type::Config,
|
||||
true,
|
||||
"[订阅更新] 代理组刷新失败: {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
// if let Err(err) = cmd::proxy::force_refresh_proxies().await {
|
||||
// logging!(
|
||||
// error,
|
||||
// Type::Config,
|
||||
// true,
|
||||
// "[订阅更新] 代理组刷新失败: {}",
|
||||
// err
|
||||
// );
|
||||
// }
|
||||
}
|
||||
Err(err) => {
|
||||
logging!(error, Type::Config, true, "[订阅更新] 更新失败: {}", err);
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use crate::{
|
||||
config::{Config, IVerge},
|
||||
core::handle,
|
||||
ipc::IpcManager,
|
||||
logging,
|
||||
utils::logging::Type,
|
||||
};
|
||||
use std::env;
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
@@ -26,7 +23,7 @@ pub async fn toggle_system_proxy() {
|
||||
// 如果当前系统代理即将关闭,且自动关闭连接设置为true,则关闭所有连接
|
||||
if enable
|
||||
&& auto_close_connection
|
||||
&& let Err(err) = IpcManager::global().close_all_connections().await
|
||||
&& let Err(err) = handle::Handle::mihomo().await.close_all_connections().await
|
||||
{
|
||||
log::error!(target: "app", "Failed to close all connections: {err}");
|
||||
}
|
||||
@@ -78,14 +75,7 @@ pub async fn copy_clash_env() {
|
||||
.unwrap_or_else(|| "127.0.0.1".to_string()),
|
||||
};
|
||||
|
||||
let Some(app_handle) = handle::Handle::global().app_handle() else {
|
||||
logging!(
|
||||
error,
|
||||
Type::System,
|
||||
"Failed to get app handle for proxy operation"
|
||||
);
|
||||
return;
|
||||
};
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let port = {
|
||||
Config::verge()
|
||||
.await
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::utils::window_manager::WindowManager;
|
||||
use crate::{
|
||||
config::Config,
|
||||
core::{CoreManager, handle, sysopt},
|
||||
ipc::IpcManager,
|
||||
logging,
|
||||
module::lightweight,
|
||||
utils::logging::Type,
|
||||
@@ -25,18 +24,11 @@ pub async fn quit() {
|
||||
logging!(debug, Type::System, true, "启动退出流程");
|
||||
|
||||
// 获取应用句柄并设置退出标志
|
||||
let Some(app_handle) = handle::Handle::global().app_handle() else {
|
||||
logging!(
|
||||
error,
|
||||
Type::System,
|
||||
"Failed to get app handle for quit operation"
|
||||
);
|
||||
return;
|
||||
};
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
handle::Handle::global().set_is_exiting();
|
||||
|
||||
// 优先关闭窗口,提供立即反馈
|
||||
if let Some(window) = handle::Handle::global().get_window() {
|
||||
if let Some(window) = handle::Handle::get_window() {
|
||||
let _ = window.hide();
|
||||
log::info!(target: "app", "窗口已隐藏");
|
||||
}
|
||||
@@ -70,7 +62,9 @@ async fn clean_async() -> bool {
|
||||
let disable_tun = serde_json::json!({"tun": {"enable": false}});
|
||||
match timeout(
|
||||
Duration::from_secs(3),
|
||||
IpcManager::global().patch_configs(disable_tun),
|
||||
handle::Handle::mihomo()
|
||||
.await
|
||||
.patch_base_config(&disable_tun),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -216,7 +210,7 @@ pub async fn hide() {
|
||||
add_light_weight_timer().await;
|
||||
}
|
||||
|
||||
if let Some(window) = handle::Handle::global().get_window()
|
||||
if let Some(window) = handle::Handle::get_window()
|
||||
&& window.is_visible().unwrap_or(false)
|
||||
{
|
||||
let _ = window.hide();
|
||||
|
||||
@@ -7,28 +7,30 @@ pub mod config;
|
||||
mod core;
|
||||
mod enhance;
|
||||
mod feat;
|
||||
mod ipc;
|
||||
// mod ipc;
|
||||
mod module;
|
||||
mod process;
|
||||
mod utils;
|
||||
#[cfg(target_os = "macos")]
|
||||
use crate::utils::window_manager::WindowManager;
|
||||
use crate::{
|
||||
core::handle,
|
||||
core::hotkey,
|
||||
process::AsyncHandler,
|
||||
utils::{resolve, server},
|
||||
};
|
||||
use config::Config;
|
||||
use tauri::AppHandle;
|
||||
use once_cell::sync::OnceCell;
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::Manager;
|
||||
use tauri::{AppHandle, Manager};
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use tokio::time::{Duration, timeout};
|
||||
use utils::logging::Type;
|
||||
|
||||
pub static APP_HANDLE: OnceCell<AppHandle> = OnceCell::new();
|
||||
|
||||
/// Application initialization helper functions
|
||||
mod app_init {
|
||||
use super::*;
|
||||
@@ -41,7 +43,7 @@ mod app_init {
|
||||
Ok(result) => {
|
||||
if result.is_err() {
|
||||
logging!(info, Type::Setup, true, "检测到已有应用实例运行");
|
||||
if let Some(app_handle) = handle::Handle::global().app_handle() {
|
||||
if let Some(app_handle) = APP_HANDLE.get() {
|
||||
app_handle.exit(0);
|
||||
} else {
|
||||
std::process::exit(0);
|
||||
@@ -75,7 +77,13 @@ mod app_init {
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.plugin(tauri_plugin_http::init());
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.plugin(
|
||||
tauri_plugin_mihomo::Builder::new()
|
||||
.protocol(tauri_plugin_mihomo::models::Protocol::LocalSocket)
|
||||
.socket_path(crate::config::IClashTemp::guard_external_controller_ipc())
|
||||
.build(),
|
||||
);
|
||||
|
||||
// Devtools plugin only in debug mode with feature tauri-dev
|
||||
// to avoid duplicated registering of logger since the devtools plugin also registers a logger
|
||||
@@ -184,9 +192,9 @@ mod app_init {
|
||||
cmd::update_proxy_chain_config_in_runtime,
|
||||
cmd::invoke_uwp_tool,
|
||||
cmd::copy_clash_env,
|
||||
cmd::get_proxies,
|
||||
cmd::force_refresh_proxies,
|
||||
cmd::get_providers_proxies,
|
||||
// cmd::get_proxies,
|
||||
// cmd::force_refresh_proxies,
|
||||
// cmd::get_providers_proxies,
|
||||
cmd::sync_tray_proxy_selection,
|
||||
cmd::update_proxy_and_sync,
|
||||
cmd::save_dns_config,
|
||||
@@ -194,36 +202,36 @@ mod app_init {
|
||||
cmd::check_dns_config_exists,
|
||||
cmd::get_dns_config_content,
|
||||
cmd::validate_dns_config,
|
||||
cmd::get_clash_version,
|
||||
cmd::get_clash_config,
|
||||
cmd::force_refresh_clash_config,
|
||||
cmd::update_geo_data,
|
||||
cmd::upgrade_clash_core,
|
||||
cmd::get_clash_rules,
|
||||
cmd::update_proxy_choice,
|
||||
cmd::get_proxy_providers,
|
||||
cmd::get_rule_providers,
|
||||
cmd::proxy_provider_health_check,
|
||||
cmd::update_proxy_provider,
|
||||
cmd::update_rule_provider,
|
||||
cmd::get_clash_connections,
|
||||
cmd::delete_clash_connection,
|
||||
cmd::close_all_clash_connections,
|
||||
cmd::get_group_proxy_delays,
|
||||
cmd::is_clash_debug_enabled,
|
||||
cmd::clash_gc,
|
||||
// cmd::get_clash_version,
|
||||
// cmd::get_clash_config,
|
||||
// cmd::force_refresh_clash_config,
|
||||
// cmd::update_geo_data,
|
||||
// cmd::upgrade_clash_core,
|
||||
// cmd::get_clash_rules,
|
||||
// cmd::update_proxy_choice,
|
||||
// cmd::get_proxy_providers,
|
||||
// cmd::get_rule_providers,
|
||||
// cmd::proxy_provider_health_check,
|
||||
// cmd::update_proxy_provider,
|
||||
// cmd::update_rule_provider,
|
||||
// cmd::get_clash_connections,
|
||||
// cmd::delete_clash_connection,
|
||||
// cmd::close_all_clash_connections,
|
||||
// cmd::get_group_proxy_delays,
|
||||
// cmd::is_clash_debug_enabled,
|
||||
// cmd::clash_gc,
|
||||
// Logging and monitoring
|
||||
cmd::get_clash_logs,
|
||||
cmd::start_logs_monitoring,
|
||||
cmd::stop_logs_monitoring,
|
||||
cmd::clear_logs,
|
||||
cmd::get_traffic_data,
|
||||
cmd::get_memory_data,
|
||||
cmd::get_formatted_traffic_data,
|
||||
cmd::get_formatted_memory_data,
|
||||
cmd::get_system_monitor_overview,
|
||||
cmd::start_traffic_service,
|
||||
cmd::stop_traffic_service,
|
||||
// cmd::start_logs_monitoring,
|
||||
// cmd::stop_logs_monitoring,
|
||||
// cmd::clear_logs,
|
||||
// cmd::get_traffic_data,
|
||||
// cmd::get_memory_data,
|
||||
// cmd::get_formatted_traffic_data,
|
||||
// cmd::get_formatted_memory_data,
|
||||
// cmd::get_system_monitor_overview,
|
||||
// cmd::start_traffic_service,
|
||||
// cmd::stop_traffic_service,
|
||||
// Verge configuration
|
||||
cmd::get_verge_config,
|
||||
cmd::patch_verge_config,
|
||||
@@ -252,7 +260,7 @@ mod app_init {
|
||||
cmd::script_validate_notice,
|
||||
cmd::validate_script_file,
|
||||
// Clash API
|
||||
cmd::clash_api_get_proxy_delay,
|
||||
// cmd::clash_api_get_proxy_delay,
|
||||
// Backup and WebDAV
|
||||
cmd::create_webdav_backup,
|
||||
cmd::save_webdav_config,
|
||||
@@ -306,6 +314,10 @@ pub fn run() {
|
||||
.setup(|app| {
|
||||
logging!(info, Type::Setup, true, "开始应用初始化...");
|
||||
|
||||
APP_HANDLE
|
||||
.set(app.app_handle().clone())
|
||||
.expect("failed to set global app handle");
|
||||
|
||||
// Setup autostart plugin
|
||||
if let Err(e) = app_init::setup_autostart(app) {
|
||||
logging!(error, Type::Setup, true, "Failed to setup autostart: {}", e);
|
||||
@@ -333,11 +345,9 @@ pub fn run() {
|
||||
);
|
||||
}
|
||||
|
||||
let app_handle = app.handle().clone();
|
||||
|
||||
logging!(info, Type::Setup, true, "执行主要设置操作...");
|
||||
|
||||
resolve::resolve_setup_handle(app_handle);
|
||||
resolve::resolve_setup_handle();
|
||||
resolve::resolve_setup_async();
|
||||
resolve::resolve_setup_sync();
|
||||
|
||||
@@ -353,13 +363,13 @@ pub fn run() {
|
||||
use super::*;
|
||||
|
||||
/// Handle application ready/resumed events
|
||||
pub fn handle_ready_resumed(app_handle: &AppHandle) {
|
||||
pub fn handle_ready_resumed(_app_handle: &AppHandle) {
|
||||
logging!(info, Type::System, true, "应用就绪或恢复");
|
||||
handle::Handle::global().init(app_handle.clone());
|
||||
handle::Handle::global().init();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
if let Some(window) = app_handle.get_webview_window("main") {
|
||||
if let Some(window) = _app_handle.get_webview_window("main") {
|
||||
logging!(info, Type::Window, true, "设置macOS窗口标题");
|
||||
let _ = window.set_title("Clash Verge");
|
||||
}
|
||||
@@ -410,7 +420,7 @@ pub fn run() {
|
||||
log::info!(target: "app", "closing window...");
|
||||
if let tauri::WindowEvent::CloseRequested { api, .. } = api {
|
||||
api.prevent_close();
|
||||
if let Some(window) = core::handle::Handle::global().get_window() {
|
||||
if let Some(window) = core::handle::Handle::get_window() {
|
||||
let _ = window.hide();
|
||||
} else {
|
||||
logging!(warn, Type::Window, true, "尝试隐藏窗口但窗口不存在");
|
||||
|
||||
@@ -219,7 +219,7 @@ pub async fn add_light_weight_timer() {
|
||||
}
|
||||
|
||||
fn setup_window_close_listener() {
|
||||
if let Some(window) = handle::Handle::global().get_window() {
|
||||
if let Some(window) = handle::Handle::get_window() {
|
||||
let handler = window.listen("tauri://close-requested", move |_event| {
|
||||
std::mem::drop(AsyncHandler::spawn(|| async {
|
||||
if let Err(e) = setup_light_weight_timer().await {
|
||||
@@ -239,7 +239,7 @@ fn setup_window_close_listener() {
|
||||
}
|
||||
|
||||
fn cancel_window_close_listener() {
|
||||
if let Some(window) = handle::Handle::global().get_window() {
|
||||
if let Some(window) = handle::Handle::get_window() {
|
||||
let handler = WINDOW_CLOSE_HANDLER.swap(0, Ordering::AcqRel);
|
||||
if handler != 0 {
|
||||
window.unlisten(handler);
|
||||
@@ -249,7 +249,7 @@ fn cancel_window_close_listener() {
|
||||
}
|
||||
|
||||
fn setup_webview_focus_listener() {
|
||||
if let Some(window) = handle::Handle::global().get_window() {
|
||||
if let Some(window) = handle::Handle::get_window() {
|
||||
let handler = window.listen("tauri://focus", move |_event| {
|
||||
log_err!(cancel_light_weight_timer());
|
||||
logging!(
|
||||
@@ -264,7 +264,7 @@ fn setup_webview_focus_listener() {
|
||||
}
|
||||
|
||||
fn cancel_webview_focus_listener() {
|
||||
if let Some(window) = handle::Handle::global().get_window() {
|
||||
if let Some(window) = handle::Handle::get_window() {
|
||||
let handler = WEBVIEW_FOCUS_HANDLER.swap(0, Ordering::AcqRel);
|
||||
if handler != 0 {
|
||||
window.unlisten(handler);
|
||||
|
||||
@@ -38,17 +38,7 @@ impl PlatformSpecification {
|
||||
let system_kernel_version = System::kernel_version().unwrap_or("Null".into());
|
||||
let system_arch = System::cpu_arch();
|
||||
|
||||
let Some(handler) = handle::Handle::global().app_handle() else {
|
||||
return Self {
|
||||
system_name,
|
||||
system_version,
|
||||
system_kernel_version,
|
||||
system_arch,
|
||||
verge_version: "unknown".into(),
|
||||
running_mode: "NotRunning".to_string(),
|
||||
is_admin: false,
|
||||
};
|
||||
};
|
||||
let handler = handle::Handle::app_handle();
|
||||
let verge_version = handler.package_info().version.to_string();
|
||||
|
||||
// 使用默认值避免在同步上下文中执行异步操作
|
||||
|
||||
@@ -51,53 +51,7 @@ pub fn app_home_dir() -> Result<PathBuf> {
|
||||
}
|
||||
|
||||
// 避免在Handle未初始化时崩溃
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::warn!(target: "app", "app_handle not initialized, using default path");
|
||||
// 使用可执行文件目录作为备用
|
||||
let exe_path = tauri::utils::platform::current_exe()?;
|
||||
let exe_dir = exe_path
|
||||
.parent()
|
||||
.ok_or(anyhow::anyhow!("failed to get executable directory"))?;
|
||||
|
||||
// 使用系统临时目录 + 应用ID
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if let Some(local_app_data) = std::env::var_os("LOCALAPPDATA") {
|
||||
let path = PathBuf::from(local_app_data).join(APP_ID);
|
||||
return Ok(path);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
if let Some(home) = std::env::var_os("HOME") {
|
||||
let path = PathBuf::from(home)
|
||||
.join("Library")
|
||||
.join("Application Support")
|
||||
.join(APP_ID);
|
||||
return Ok(path);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if let Some(home) = std::env::var_os("HOME") {
|
||||
let path = PathBuf::from(home)
|
||||
.join(".local")
|
||||
.join("share")
|
||||
.join(APP_ID);
|
||||
return Ok(path);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果无法获取系统目录,则回退到可执行文件目录
|
||||
let fallback_dir = PathBuf::from(exe_dir).join(".config").join(APP_ID);
|
||||
log::warn!(target: "app", "Using fallback data directory: {fallback_dir:?}");
|
||||
return Ok(fallback_dir);
|
||||
}
|
||||
};
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
match app_handle.path().data_dir() {
|
||||
Ok(dir) => Ok(dir.join(APP_ID)),
|
||||
@@ -111,18 +65,7 @@ pub fn app_home_dir() -> Result<PathBuf> {
|
||||
/// get the resources dir
|
||||
pub fn app_resources_dir() -> Result<PathBuf> {
|
||||
// 避免在Handle未初始化时崩溃
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::warn!(target: "app", "app_handle not initialized in app_resources_dir, using fallback");
|
||||
// 使用可执行文件目录作为备用
|
||||
let exe_dir = tauri::utils::platform::current_exe()?
|
||||
.parent()
|
||||
.ok_or(anyhow::anyhow!("failed to get executable directory"))?
|
||||
.to_path_buf();
|
||||
return Ok(exe_dir.join("resources"));
|
||||
}
|
||||
};
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
match app_handle.path().resource_dir() {
|
||||
Ok(dir) => Ok(dir.join("resources")),
|
||||
|
||||
@@ -494,15 +494,7 @@ pub fn init_scheme() -> Result<()> {
|
||||
}
|
||||
|
||||
pub async fn startup_script() -> Result<()> {
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"app_handle not available for startup script execution"
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let script_path = {
|
||||
let verge = Config::verge().await;
|
||||
let verge = verge.latest_ref();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
pub async fn set_public_dns(dns_server: String) {
|
||||
use crate::{core::handle, utils::dirs};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
let app_handle = match handle::Handle::app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::error!(target: "app", "app_handle not available for DNS configuration");
|
||||
@@ -50,7 +50,7 @@ pub async fn set_public_dns(dns_server: String) {
|
||||
pub async fn restore_public_dns() {
|
||||
use crate::{core::handle, utils::dirs};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
let app_handle = match handle::Handle::global().app_handle() {
|
||||
let app_handle = match handle::Handle::app_handle() {
|
||||
Some(handle) => handle,
|
||||
None => {
|
||||
log::error!(target: "app", "app_handle not available for DNS restoration");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
@@ -18,8 +17,8 @@ pub mod ui;
|
||||
pub mod window;
|
||||
pub mod window_script;
|
||||
|
||||
pub fn resolve_setup_handle(app_handle: AppHandle) {
|
||||
init_handle(app_handle);
|
||||
pub fn resolve_setup_handle() {
|
||||
init_handle();
|
||||
}
|
||||
|
||||
pub fn resolve_setup_sync() {
|
||||
@@ -118,9 +117,9 @@ pub async fn resolve_reset_async() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_handle(app_handle: AppHandle) {
|
||||
pub fn init_handle() {
|
||||
logging!(info, Type::Setup, true, "Initializing app handle...");
|
||||
handle::Handle::global().init(app_handle);
|
||||
handle::Handle::global().init();
|
||||
}
|
||||
|
||||
pub(super) fn init_scheme() {
|
||||
|
||||
@@ -2,7 +2,7 @@ use tauri::WebviewWindow;
|
||||
|
||||
use crate::{
|
||||
core::handle,
|
||||
logging, logging_error,
|
||||
logging_error,
|
||||
utils::{
|
||||
logging::Type,
|
||||
resolve::window_script::{INITIAL_LOADING_OVERLAY, WINDOW_INITIAL_SCRIPT},
|
||||
@@ -18,18 +18,10 @@ const MINIMAL_HEIGHT: f64 = 520.0;
|
||||
|
||||
/// 构建新的 WebView 窗口
|
||||
pub fn build_new_window() -> Result<WebviewWindow, String> {
|
||||
let app_handle = handle::Handle::global().app_handle().ok_or_else(|| {
|
||||
logging!(
|
||||
error,
|
||||
Type::Window,
|
||||
true,
|
||||
"无法获取app_handle,窗口创建失败"
|
||||
);
|
||||
"无法获取app_handle".to_string()
|
||||
})?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
match tauri::WebviewWindowBuilder::new(
|
||||
&app_handle,
|
||||
app_handle,
|
||||
"main", /* the unique window label */
|
||||
tauri::WebviewUrl::App("index.html".into()),
|
||||
)
|
||||
|
||||
@@ -67,7 +67,7 @@ fn should_handle_window_operation() -> bool {
|
||||
let now = Instant::now();
|
||||
let elapsed = now.duration_since(*last_operation);
|
||||
|
||||
log::debug!(target: "app", "[防抖] 检查窗口操作间隔: {}ms (需要>={}ms)",
|
||||
log::debug!(target: "app", "[防抖] 检查窗口操作间隔: {}ms (需要>={}ms)",
|
||||
elapsed.as_millis(), WINDOW_OPERATION_DEBOUNCE_MS);
|
||||
|
||||
if elapsed >= Duration::from_millis(WINDOW_OPERATION_DEBOUNCE_MS) {
|
||||
@@ -76,7 +76,7 @@ fn should_handle_window_operation() -> bool {
|
||||
log::info!(target: "app", "[防抖] 窗口操作被允许执行");
|
||||
true
|
||||
} else {
|
||||
log::warn!(target: "app", "[防抖] 窗口操作被防抖机制忽略,距离上次操作 {}ms < {}ms",
|
||||
log::warn!(target: "app", "[防抖] 窗口操作被防抖机制忽略,距离上次操作 {}ms < {}ms",
|
||||
elapsed.as_millis(), WINDOW_OPERATION_DEBOUNCE_MS);
|
||||
false
|
||||
}
|
||||
@@ -117,9 +117,8 @@ impl WindowManager {
|
||||
|
||||
/// 获取主窗口实例
|
||||
pub fn get_main_window() -> Option<WebviewWindow<Wry>> {
|
||||
handle::Handle::global()
|
||||
.app_handle()
|
||||
.and_then(|app| app.get_webview_window("main"))
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
app_handle.get_webview_window("main")
|
||||
}
|
||||
|
||||
/// 智能显示主窗口
|
||||
|
||||
Reference in New Issue
Block a user