refactor: enhance error handling and logging in core components and server initialization

This commit is contained in:
xmk23333
2025-10-20 16:34:38 +08:00
parent d6bba4f68c
commit b9dd62e2e6
4 changed files with 161 additions and 478 deletions

View File

@@ -46,14 +46,10 @@ pub struct CoreManager {
last_update: Arc<Mutex<Option<Instant>>>,
}
/// 内核运行模式
#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, serde::Serialize, PartialEq, Eq)]
pub enum RunningMode {
/// 服务模式运行
Service,
/// Sidecar 模式运行
Sidecar,
/// 未运行
NotRunning,
}
@@ -69,12 +65,16 @@ impl fmt::Display for RunningMode {
use crate::config::IVerge;
const CONNECTION_ERROR_PATTERNS: &[&str] = &[
"Failed to create connection",
"The system cannot find the file specified",
"operation timed out",
"connection refused",
];
impl CoreManager {
/// 使用默认配置
pub async fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> {
let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG);
// Extract clash config before async operations
let clash_config = Config::clash().await.latest_ref().0.clone();
*Config::runtime().await.draft_mut() = Box::new(IRuntime {
@@ -215,115 +215,85 @@ impl CoreManager {
}
fn should_restart_on_reload_error(err: &MihomoError) -> bool {
match err {
MihomoError::ConnectionFailed | MihomoError::ConnectionLost => true,
MihomoError::Io(io_err) => matches!(
io_err.kind(),
fn is_connection_io_error(kind: std::io::ErrorKind) -> bool {
matches!(
kind,
std::io::ErrorKind::ConnectionAborted
| std::io::ErrorKind::ConnectionRefused
| std::io::ErrorKind::ConnectionReset
| std::io::ErrorKind::NotFound
),
)
}
fn contains_error_pattern(text: &str) -> bool {
CONNECTION_ERROR_PATTERNS.iter().any(|p| text.contains(p))
}
match err {
MihomoError::ConnectionFailed | MihomoError::ConnectionLost => true,
MihomoError::Io(io_err) => is_connection_io_error(io_err.kind()),
MihomoError::Reqwest(req_err) => {
if req_err.is_connect() || req_err.is_timeout() {
return true;
}
let err_text = req_err.to_string();
if let Some(source) = req_err.source() {
if let Some(io_err) = source.downcast_ref::<std::io::Error>() {
if matches!(
io_err.kind(),
std::io::ErrorKind::ConnectionAborted
| std::io::ErrorKind::ConnectionRefused
| std::io::ErrorKind::ConnectionReset
| std::io::ErrorKind::NotFound
) {
if is_connection_io_error(io_err.kind()) {
return true;
}
} else if source.to_string().contains("Failed to create connection") {
} else if contains_error_pattern(&source.to_string()) {
return true;
}
}
err_text.contains("Failed to create connection")
|| err_text.contains("The system cannot find the file specified")
|| err_text.contains("operation timed out")
|| err_text.contains("connection refused")
}
MihomoError::FailedResponse(msg) => {
msg.contains("Failed to create connection") || msg.contains("connection refused")
contains_error_pattern(&req_err.to_string())
}
MihomoError::FailedResponse(msg) => contains_error_pattern(msg),
_ => false,
}
}
}
impl CoreManager {
/// 清理多余的 mihomo 进程
async fn cleanup_orphaned_mihomo_processes(&self) -> Result<()> {
logging!(info, Type::Core, "开始清理多余的 mihomo 进程");
// 获取当前管理的进程 PID
let current_pid = {
let child_guard = self.child_sidecar.lock();
child_guard.as_ref().map(|child| child.pid())
};
let current_pid = self.child_sidecar.lock().as_ref().and_then(|child| child.pid());
let target_processes = ["verge-mihomo", "verge-mihomo-alpha"];
// 并行查找所有目标进程
let mut process_futures = Vec::new();
for &target in &target_processes {
let process_futures = target_processes.iter().map(|&target| {
let process_name = if cfg!(windows) {
format!("{target}.exe")
} else {
target.into()
target.to_string()
};
process_futures.push(self.find_processes_by_name(process_name, target));
}
self.find_processes_by_name(process_name, target)
});
let process_results = futures::future::join_all(process_futures).await;
// 收集所有需要终止的进程PID
let mut pids_to_kill = Vec::new();
for result in process_results {
match result {
Ok((pids, process_name)) => {
for pid in pids {
// 跳过当前管理的进程
if let Some(current) = current_pid
&& Some(pid) == current
{
logging!(
debug,
Type::Core,
"跳过当前管理的进程: {} (PID: {})",
process_name,
pid
);
continue;
}
pids_to_kill.push((pid, process_name.clone()));
}
}
Err(e) => {
logging!(debug, Type::Core, "查找进程时发生错误: {}", e);
}
}
}
let pids_to_kill: Vec<_> = process_results
.into_iter()
.filter_map(|result| result.ok())
.flat_map(|(pids, process_name)| {
pids.into_iter()
.filter(|&pid| Some(pid) != current_pid)
.map(move |pid| (pid, process_name.clone()))
})
.collect();
if pids_to_kill.is_empty() {
logging!(debug, Type::Core, "未发现多余的 mihomo 进程");
return Ok(());
}
let mut kill_futures = Vec::new();
for (pid, process_name) in &pids_to_kill {
kill_futures.push(self.kill_process_with_verification(*pid, process_name.clone()));
}
let kill_futures = pids_to_kill.iter()
.map(|(pid, name)| self.kill_process_with_verification(*pid, name.clone()));
let kill_results = futures::future::join_all(kill_futures).await;
let killed_count = kill_results.into_iter().filter(|&success| success).count();
let killed_count = futures::future::join_all(kill_futures)
.await
.into_iter()
.filter(|&success| success)
.count();
if killed_count > 0 {
logging!(
@@ -355,10 +325,9 @@ impl CoreManager {
let process_name_clone = process_name.clone();
let pids = AsyncHandler::spawn_blocking(move || -> Result<Vec<u32>> {
let mut pids = Vec::new();
let mut pids = Vec::with_capacity(8);
unsafe {
// 创建进程快照
let snapshot: HANDLE = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if snapshot == winapi::um::handleapi::INVALID_HANDLE_VALUE {
return Err(anyhow::anyhow!("Failed to create process snapshot"));
@@ -367,28 +336,24 @@ impl CoreManager {
let mut pe32: PROCESSENTRY32W = mem::zeroed();
pe32.dwSize = mem::size_of::<PROCESSENTRY32W>() as u32;
// 获取第一个进程
if Process32FirstW(snapshot, &mut pe32) != 0 {
loop {
// 将宽字符转换为String
let end_pos = pe32
.szExeFile
.iter()
.position(|&x| x == 0)
let end_pos = pe32.szExeFile.iter().position(|&x| x == 0)
.unwrap_or(pe32.szExeFile.len());
let exe_file = String::from_utf16_lossy(&pe32.szExeFile[..end_pos]);
// 检查进程名是否匹配
if end_pos > 0 {
let exe_file = String::from_utf16_lossy(&pe32.szExeFile[..end_pos]);
if exe_file.eq_ignore_ascii_case(&process_name_clone) {
pids.push(pe32.th32ProcessID);
}
}
if Process32NextW(snapshot, &mut pe32) == 0 {
break;
}
}
}
// 关闭句柄
CloseHandle(snapshot);
}
@@ -407,7 +372,6 @@ impl CoreManager {
.output()
.await?
} else {
// Linux
tokio::process::Command::new("pidof")
.arg(&process_name)
.output()
@@ -419,28 +383,17 @@ impl CoreManager {
}
let stdout = String::from_utf8_lossy(&output.stdout);
let mut pids = Vec::new();
// Unix系统直接解析PID列表
for pid_str in stdout.split_whitespace() {
if let Ok(pid) = pid_str.parse::<u32>() {
pids.push(pid);
}
}
let pids: Vec<u32> = stdout
.split_whitespace()
.filter_map(|s| s.parse().ok())
.collect();
Ok((pids, process_name))
}
}
/// 终止进程并验证结果 - 使用Windows API直接终止更优雅高效
async fn kill_process_with_verification(&self, pid: u32, process_name: String) -> bool {
logging!(
info,
Type::Core,
"尝试终止进程: {} (PID: {})",
process_name,
pid
);
logging!(info, Type::Core, "尝试终止进程: {} (PID: {})", process_name, pid);
#[cfg(windows)]
let success = {
@@ -448,93 +401,60 @@ impl CoreManager {
use winapi::um::processthreadsapi::{OpenProcess, TerminateProcess};
use winapi::um::winnt::{HANDLE, PROCESS_TERMINATE};
AsyncHandler::spawn_blocking(move || -> bool {
unsafe {
let process_handle: HANDLE = OpenProcess(PROCESS_TERMINATE, 0, pid);
if process_handle.is_null() {
AsyncHandler::spawn_blocking(move || unsafe {
let handle: HANDLE = OpenProcess(PROCESS_TERMINATE, 0, pid);
if handle.is_null() {
return false;
}
let result = TerminateProcess(process_handle, 1);
CloseHandle(process_handle);
result != 0
}
let result = TerminateProcess(handle, 1) != 0;
CloseHandle(handle);
result
})
.await
.unwrap_or(false)
};
#[cfg(not(windows))]
let success = {
tokio::process::Command::new("kill")
let success = tokio::process::Command::new("kill")
.args(["-9", &pid.to_string()])
.output()
.await
.map(|output| output.status.success())
.unwrap_or(false)
};
.unwrap_or(false);
if !success {
logging!(warn, Type::Core, "无法终止进程: {} (PID: {})", process_name, pid);
return false;
}
if success {
// 短暂等待并验证进程是否真正终止
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let still_running = self.is_process_running(pid).await.unwrap_or(false);
if still_running {
logging!(
warn,
Type::Core,
"进程 {} (PID: {}) 终止命令成功但进程仍在运行",
process_name,
pid
);
if self.is_process_running(pid).await.unwrap_or(false) {
logging!(warn, Type::Core, "进程 {} (PID: {}) 终止命令成功但进程仍在运行", process_name, pid);
false
} else {
logging!(
info,
Type::Core,
"成功终止进程: {} (PID: {})",
process_name,
pid
);
logging!(info, Type::Core, "成功终止进程: {} (PID: {})", process_name, pid);
true
}
} else {
logging!(
warn,
Type::Core,
"无法终止进程: {} (PID: {})",
process_name,
pid
);
false
}
}
/// Windows API检查进程
async fn is_process_running(&self, pid: u32) -> Result<bool> {
#[cfg(windows)]
{
use winapi::shared::minwindef::DWORD;
use winapi::um::handleapi::CloseHandle;
use winapi::um::processthreadsapi::GetExitCodeProcess;
use winapi::um::processthreadsapi::OpenProcess;
use winapi::um::processthreadsapi::{GetExitCodeProcess, OpenProcess};
use winapi::um::winnt::{HANDLE, PROCESS_QUERY_INFORMATION};
AsyncHandler::spawn_blocking(move || -> Result<bool> {
unsafe {
let process_handle: HANDLE = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid);
if process_handle.is_null() {
AsyncHandler::spawn_blocking(move || unsafe {
let handle: HANDLE = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid);
if handle.is_null() {
return Ok(false);
}
let mut exit_code: DWORD = 0;
let result = GetExitCodeProcess(process_handle, &mut exit_code);
CloseHandle(process_handle);
if result == 0 {
return Ok(false);
}
Ok(exit_code == 259)
}
let result = GetExitCodeProcess(handle, &mut exit_code);
CloseHandle(handle);
Ok(result != 0 && exit_code == 259)
})
.await?
}
@@ -580,18 +500,10 @@ impl CoreManager {
AsyncHandler::spawn(|| async move {
while let Some(event) = rx.recv().await {
match event {
tauri_plugin_shell::process::CommandEvent::Stdout(line) => {
tauri_plugin_shell::process::CommandEvent::Stdout(line)
| tauri_plugin_shell::process::CommandEvent::Stderr(line) => {
let mut now = DeferredNow::default();
let message =
CompactString::from(String::from_utf8_lossy(&line).into_owned());
let w = shared_writer.lock().await;
write_sidecar_log(w, &mut now, Level::Error, &message);
ClashLogger::global().append_log(message);
}
tauri_plugin_shell::process::CommandEvent::Stderr(line) => {
let mut now = DeferredNow::default();
let message =
CompactString::from(String::from_utf8_lossy(&line).into_owned());
let message = CompactString::from(String::from_utf8_lossy(&line).as_ref());
let w = shared_writer.lock().await;
write_sidecar_log(w, &mut now, Level::Error, &message);
ClashLogger::global().append_log(message);
@@ -660,22 +572,13 @@ impl Default for CoreManager {
impl CoreManager {
pub async fn init(&self) -> Result<()> {
logging!(info, Type::Core, "Initializing core");
logging!(info, Type::Core, "开始核心初始化");
// 应用启动时先清理任何遗留的 mihomo 进程
if let Err(e) = self.cleanup_orphaned_mihomo_processes().await {
logging!(
warn,
Type::Core,
"应用初始化时清理多余 mihomo 进程失败: {}",
e
);
logging!(warn, Type::Core, "清理遗留 mihomo 进程失败: {}", e);
}
// 使用简化的启动流程
logging!(info, Type::Core, "开始核心初始化");
self.start_core().await?;
logging!(info, Type::Core, "核心初始化完成");
Ok(())
}
@@ -686,8 +589,7 @@ impl CoreManager {
}
pub fn get_running_mode(&self) -> RunningMode {
let guard = self.running.lock();
(*guard).clone()
*self.running.lock()
}
#[cfg(target_os = "windows")]

View File

@@ -84,7 +84,7 @@ mod app_init {
app.deep_link().on_open_url(|event| {
let url = event.urls().first().map(|u| u.to_string());
if let Some(url) = url {
AsyncHandler::spawn(|| async {
let _ = AsyncHandler::spawn(|| async {
if let Err(e) = resolve::resolve_scheme(url).await {
logging!(error, Type::Setup, "Failed to resolve scheme: {}", e);
}
@@ -123,11 +123,9 @@ mod app_init {
Ok(())
}
/// Generate all command handlers for the application
pub fn generate_handlers()
-> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Send + Sync + 'static {
tauri::generate_handler![
// Common commands
cmd::get_sys_proxy,
cmd::get_auto_proxy,
cmd::open_app_dir,
@@ -138,27 +136,22 @@ mod app_init {
cmd::get_network_interfaces,
cmd::get_system_hostname,
cmd::restart_app,
// Core management
cmd::start_core,
cmd::stop_core,
cmd::restart_core,
// Application lifecycle
cmd::notify_ui_ready,
cmd::update_ui_stage,
cmd::get_running_mode,
cmd::get_app_uptime,
cmd::get_auto_launch_status,
cmd::is_admin,
// Lightweight mode
cmd::entry_lightweight_mode,
cmd::exit_lightweight_mode,
// Service management
cmd::install_service,
cmd::uninstall_service,
cmd::reinstall_service,
cmd::repair_service,
cmd::is_service_available,
// Clash core commands
cmd::get_clash_info,
cmd::patch_clash_config,
cmd::patch_clash_mode,
@@ -178,7 +171,6 @@ mod app_init {
cmd::get_dns_config_content,
cmd::validate_dns_config,
cmd::get_clash_logs,
// Verge configuration
cmd::get_verge_config,
cmd::patch_verge_config,
cmd::test_delay,
@@ -188,7 +180,6 @@ mod app_init {
cmd::open_devtools,
cmd::exit_app,
cmd::get_network_interfaces_info,
// Profile management
cmd::get_profiles,
cmd::enhance_profiles,
cmd::patch_profiles_config,
@@ -202,10 +193,8 @@ mod app_init {
cmd::read_profile_file,
cmd::save_profile_file,
cmd::get_next_update_time,
// Script validation
cmd::script_validate_notice,
cmd::validate_script_file,
// Backup and WebDAV
cmd::create_local_backup,
cmd::list_local_backup,
cmd::delete_local_backup,
@@ -216,10 +205,8 @@ mod app_init {
cmd::list_webdav_backup,
cmd::delete_webdav_backup,
cmd::restore_webdav_backup,
// Diagnostics and system info
cmd::export_diagnostic_info,
cmd::get_system_info,
// Media unlock checker
cmd::get_unlock_items,
cmd::check_media_unlock,
]
@@ -227,18 +214,15 @@ mod app_init {
}
pub fn run() {
// Setup singleton check
if app_init::init_singleton_check().is_err() {
return;
}
let _ = utils::dirs::init_portable_flag();
// Set Linux environment variable
#[cfg(target_os = "linux")]
linux::configure_environment();
// Create and configure the Tauri builder
let builder = app_init::setup_plugins(tauri::Builder::default())
.setup(|app| {
logging!(info, Type::Setup, "开始应用初始化...");
@@ -248,89 +232,56 @@ pub fn run() {
.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, "Failed to setup autostart: {}", e);
}
// Setup deep links
if let Err(e) = app_init::setup_deep_links(app) {
logging!(error, Type::Setup, "Failed to setup deep links: {}", e);
}
// Setup window state management
if let Err(e) = app_init::setup_window_state(app) {
logging!(error, Type::Setup, "Failed to setup window state: {}", e);
}
logging!(info, Type::Setup, "执行主要设置操作...");
resolve::resolve_setup_handle();
// 异步任务启动,不等待完成
resolve::resolve_setup_async();
resolve::resolve_setup_sync();
logging!(info, Type::Setup, "初始化已启动,继续执行");
logging!(info, Type::Setup, "初始化已启动");
Ok(())
})
.invoke_handler(app_init::generate_handlers());
/// Event handling helper functions
mod event_handlers {
use crate::core::handle;
use super::*;
/// Handle application ready/resumed events
pub fn handle_ready_resumed(_app_handle: &AppHandle) {
// 双重检查:确保不在退出状态
if handle::Handle::global().is_exiting() {
logging!(
debug,
Type::System,
"handle_ready_resumed: 应用正在退出,跳过处理"
);
logging!(debug, Type::System, "应用正在退出,跳过处理");
return;
}
logging!(info, Type::System, "应用就绪或恢复");
logging!(info, Type::System, "应用就绪");
handle::Handle::global().init();
#[cfg(target_os = "macos")]
{
if let Some(window) = _app_handle.get_webview_window("main") {
logging!(info, Type::Window, "设置macOS窗口标题");
let _ = window.set_title("Clash Verge");
}
}
}
/// Handle application reopen events (macOS)
#[cfg(target_os = "macos")]
pub async fn handle_reopen(has_visible_windows: bool) {
logging!(
info,
Type::System,
"处理 macOS 应用重新打开事件: has_visible_windows={}",
has_visible_windows
);
handle::Handle::global().init();
if !has_visible_windows {
// 当没有可见窗口时,设置为 regular 模式并显示主窗口
handle::Handle::global().set_activation_policy_regular();
logging!(info, Type::System, "没有可见窗口,尝试显示主窗口");
let result = WindowManager::show_main_window().await;
logging!(info, Type::System, "窗口显示操作完成,结果: {:?}", result);
} else {
logging!(info, Type::System, "已有可见窗口,无需额外操作");
let _ = WindowManager::show_main_window().await;
}
}
/// Handle window close requests
pub fn handle_window_close(api: &tauri::WindowEvent) {
#[cfg(target_os = "macos")]
handle::Handle::global().set_activation_policy_accessory();
@@ -339,20 +290,16 @@ pub fn run() {
return;
}
log::info!(target: "app", "closing window...");
if let tauri::WindowEvent::CloseRequested { api, .. } = api {
api.prevent_close();
if let Some(window) = core::handle::Handle::get_window() {
let _ = window.hide();
} else {
logging!(warn, Type::Window, "尝试隐藏窗口但窗口不存在");
}
}
}
/// Handle window focus events
pub fn handle_window_focus(focused: bool) {
AsyncHandler::spawn(move || async move {
let _ = AsyncHandler::spawn(move || async move {
let is_enable_global_hotkey = Config::verge()
.await
.latest_ref()
@@ -363,185 +310,99 @@ pub fn run() {
#[cfg(target_os = "macos")]
{
use crate::core::hotkey::SystemHotkey;
if let Err(e) = hotkey::Hotkey::global()
.register_system_hotkey(SystemHotkey::CmdQ)
.await
{
logging!(error, Type::Hotkey, "Failed to register CMD+Q: {}", e);
}
if let Err(e) = hotkey::Hotkey::global()
.register_system_hotkey(SystemHotkey::CmdW)
.await
{
logging!(error, Type::Hotkey, "Failed to register CMD+W: {}", e);
}
let _ = hotkey::Hotkey::global().register_system_hotkey(SystemHotkey::CmdQ).await;
let _ = hotkey::Hotkey::global().register_system_hotkey(SystemHotkey::CmdW).await;
}
if !is_enable_global_hotkey
&& let Err(e) = hotkey::Hotkey::global().init().await
{
logging!(error, Type::Hotkey, "Failed to init hotkeys: {}", e);
if !is_enable_global_hotkey {
let _ = hotkey::Hotkey::global().init().await;
}
return;
}
// Handle unfocused state
#[cfg(target_os = "macos")]
{
use crate::core::hotkey::SystemHotkey;
if let Err(e) =
hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdQ)
{
logging!(error, Type::Hotkey, "Failed to unregister CMD+Q: {}", e);
}
if let Err(e) =
hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdW)
{
logging!(error, Type::Hotkey, "Failed to unregister CMD+W: {}", e);
}
let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdQ);
let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdW);
}
if !is_enable_global_hotkey && let Err(e) = hotkey::Hotkey::global().reset() {
logging!(error, Type::Hotkey, "Failed to reset hotkeys: {}", e);
if !is_enable_global_hotkey {
let _ = hotkey::Hotkey::global().reset();
}
});
}
/// Handle window destroyed events
pub fn handle_window_destroyed() {
AsyncHandler::spawn(|| async {
if let Err(e) = handle::Handle::mihomo()
let _ = AsyncHandler::spawn(|| async {
let _ = handle::Handle::mihomo()
.await
.clear_all_ws_connections()
.await
{
logging!(warn, Type::Window, "清理 WebSocket 连接失败: {}", e);
} else {
logging!(info, Type::Window, "WebSocket 连接已清理");
}
.await;
});
#[cfg(target_os = "macos")]
{
use crate::core::hotkey::SystemHotkey;
if let Err(e) =
hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdQ)
{
logging!(
error,
Type::Hotkey,
"Failed to unregister CMD+Q on destroy: {}",
e
);
}
if let Err(e) =
hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdW)
{
logging!(
error,
Type::Hotkey,
"Failed to unregister CMD+W on destroy: {}",
e
);
}
let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdQ);
let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdW);
}
}
}
// Mock context for Clippy to avoid build errors
#[cfg(feature = "clippy")]
let context = tauri::test::mock_context(tauri::test::noop_assets());
#[cfg(feature = "clippy")]
let app = builder.build(context).unwrap_or_else(|e| {
logging!(
error,
Type::Setup,
"Failed to build Tauri application: {}",
e
);
logging!(error, Type::Setup, "Failed to build Tauri application: {}", e);
std::process::exit(1);
});
// Build the application
#[cfg(not(feature = "clippy"))]
let app = builder
.build(tauri::generate_context!())
.unwrap_or_else(|e| {
logging!(
error,
Type::Setup,
"Failed to build Tauri application: {}",
e
);
let app = builder.build(tauri::generate_context!()).unwrap_or_else(|e| {
logging!(error, Type::Setup, "Failed to build Tauri application: {}", e);
std::process::exit(1);
});
app.run(|app_handle, e| {
match e {
tauri::RunEvent::Ready | tauri::RunEvent::Resumed => {
// 如果正在退出,忽略 Ready/Resumed 事件
if core::handle::Handle::global().is_exiting() {
logging!(debug, Type::System, "忽略 Ready/Resumed 事件,应用正在退出");
return;
}
event_handlers::handle_ready_resumed(app_handle);
}
#[cfg(target_os = "macos")]
tauri::RunEvent::Reopen {
has_visible_windows,
..
} => {
// 如果正在退出,忽略 Reopen 事件
tauri::RunEvent::Reopen { has_visible_windows, .. } => {
if core::handle::Handle::global().is_exiting() {
logging!(debug, Type::System, "忽略 Reopen 事件,应用正在退出");
return;
}
AsyncHandler::spawn(move || async move {
let _ = AsyncHandler::spawn(move || async move {
event_handlers::handle_reopen(has_visible_windows).await;
});
}
tauri::RunEvent::ExitRequested { api, code, .. } => {
tauri::async_runtime::block_on(async {
let _ = handle::Handle::mihomo()
.await
.clear_all_ws_connections()
.await;
let _ = handle::Handle::mihomo().await.clear_all_ws_connections().await;
});
// 如果已经在退出流程中,不要阻止退出
if core::handle::Handle::global().is_exiting() {
logging!(
info,
Type::System,
"应用正在退出,允许 ExitRequested (code: {:?})",
code
);
return;
}
// 只阻止外部的无退出码请求(如用户取消系统关机)
if code.is_none() {
logging!(debug, Type::System, "阻止外部退出请求");
api.prevent_exit();
}
}
tauri::RunEvent::Exit => {
let handle = core::handle::Handle::global();
if handle.is_exiting() {
logging!(
debug,
Type::System,
"Exit事件触发但退出流程已执行跳过重复清理"
);
} else {
logging!(debug, Type::System, "Exit事件触发执行清理流程");
if !handle.is_exiting() {
handle.set_is_exiting();
EventDrivenProxyManager::global().notify_app_stopping();
feat::clean();
}
}
tauri::RunEvent::WindowEvent { label, event, .. } => {
if label == "main" {
tauri::RunEvent::WindowEvent { label, event, .. } if label == "main" => {
match event {
tauri::WindowEvent::CloseRequested { .. } => {
event_handlers::handle_window_close(&event);
@@ -555,7 +416,6 @@ pub fn run() {
_ => {}
}
}
}
_ => {}
}
});

View File

@@ -26,91 +26,54 @@ pub fn resolve_setup_handle() {
}
pub fn resolve_setup_sync() {
AsyncHandler::spawn(|| async {
AsyncHandler::spawn_blocking(init_scheme);
AsyncHandler::spawn_blocking(init_embed_server);
let _ = AsyncHandler::spawn(|| async {
let _ = AsyncHandler::spawn_blocking(init_scheme);
let _ = AsyncHandler::spawn_blocking(init_embed_server);
});
}
pub fn resolve_setup_async() {
let start_time = std::time::Instant::now();
logging!(
info,
Type::Setup,
"开始执行异步设置任务... 线程ID: {:?}",
std::thread::current().id()
);
AsyncHandler::spawn(|| async {
let _ = AsyncHandler::spawn(|| async {
#[cfg(not(feature = "tauri-dev"))]
resolve_setup_logger().await;
logging!(
info,
Type::ClashVergeRev,
"Version: {}",
env!("CARGO_PKG_VERSION")
);
logging!(info, Type::ClashVergeRev, "Version: {}", env!("CARGO_PKG_VERSION"));
futures::join!(init_work_config(), init_resources(), init_startup_script(),);
futures::join!(init_work_config(), init_resources(), init_startup_script());
init_verge_config().await;
Config::verify_config_initialization().await;
// 优先创建窗口,提升启动体验
init_window().await;
// 后台异步初始化核心,不阻塞窗口显示
let core_init = AsyncHandler::spawn(|| async {
init_service_manager().await;
init_core_manager().await;
init_system_proxy().await;
AsyncHandler::spawn_blocking(|| {
init_system_proxy_guard();
});
let _ = AsyncHandler::spawn_blocking(init_system_proxy_guard);
});
let tray_and_refresh = async {
let tray_init = async {
init_tray().await;
refresh_tray_menu().await;
};
futures::join!(
let _ = futures::join!(
core_init,
tray_and_refresh,
tray_init,
init_timer(),
init_hotkey(),
init_auto_lightweight_mode(),
init_once_auto_lightweight(),
);
});
let elapsed = start_time.elapsed();
logging!(
info,
Type::Setup,
"异步设置任务启动完成,耗时: {:?}",
elapsed
);
if elapsed.as_secs() > 10 {
logging!(warn, Type::Setup, "异步设置任务耗时较长({:?})", elapsed);
}
}
// 其它辅助函数不变
pub async fn resolve_reset_async() -> Result<(), anyhow::Error> {
logging!(info, Type::Tray, "Resetting system proxy");
sysopt::Sysopt::global().reset_sysproxy().await?;
logging!(info, Type::Core, "Stopping core service");
CoreManager::global().stop_core().await?;
#[cfg(target_os = "macos")]
{
use dns::restore_public_dns;
logging!(info, Type::System, "Restoring system DNS settings");
restore_public_dns().await;
}
@@ -118,95 +81,69 @@ pub async fn resolve_reset_async() -> Result<(), anyhow::Error> {
}
pub fn init_handle() {
logging!(info, Type::Setup, "Initializing app handle...");
handle::Handle::global().init();
}
pub(super) fn init_scheme() {
logging!(info, Type::Setup, "Initializing custom URL scheme");
logging_error!(Type::Setup, init::init_scheme());
}
#[cfg(not(feature = "tauri-dev"))]
pub(super) async fn resolve_setup_logger() {
logging!(info, Type::Setup, "Initializing global logger...");
logging_error!(Type::Setup, init::init_logger().await);
}
pub async fn resolve_scheme(param: String) -> Result<()> {
logging!(info, Type::Setup, "Resolving scheme for param: {}", param);
logging_error!(Type::Setup, scheme::resolve_scheme(param).await);
Ok(())
}
pub(super) fn init_embed_server() {
logging!(info, Type::Setup, "Initializing embedded server...");
server::embed_server();
}
pub(super) async fn init_resources() {
logging!(info, Type::Setup, "Initializing resources...");
logging_error!(Type::Setup, init::init_resources().await);
}
pub(super) async fn init_startup_script() {
logging!(info, Type::Setup, "Initializing startup script");
logging_error!(Type::Setup, init::startup_script().await);
}
pub(super) async fn init_timer() {
logging!(info, Type::Setup, "Initializing timer...");
logging_error!(Type::Setup, Timer::global().init().await);
}
pub(super) async fn init_hotkey() {
logging!(info, Type::Setup, "Initializing hotkey...");
logging_error!(Type::Setup, Hotkey::global().init().await);
}
pub(super) async fn init_once_auto_lightweight() {
logging!(
info,
Type::Lightweight,
"Running auto lightweight mode check..."
);
run_once_auto_lightweight().await;
}
pub(super) async fn init_auto_lightweight_mode() {
logging!(info, Type::Setup, "Initializing auto lightweight mode...");
logging_error!(Type::Setup, auto_lightweight_mode_init().await);
}
pub async fn init_work_config() {
logging!(info, Type::Setup, "Initializing work configuration...");
logging_error!(Type::Setup, init::init_config().await);
}
pub(super) async fn init_tray() {
// Check if tray should be disabled via environment variable
if std::env::var("CLASH_VERGE_DISABLE_TRAY").unwrap_or_default() == "1" {
logging!(info, Type::Setup, "System tray disabled via --no-tray flag");
return;
}
logging!(info, Type::Setup, "Initializing system tray...");
logging_error!(Type::Setup, Tray::global().init().await);
}
pub(super) async fn init_verge_config() {
logging!(info, Type::Setup, "Initializing verge configuration...");
logging_error!(Type::Setup, Config::init_config().await);
}
pub(super) async fn init_service_manager() {
logging!(info, Type::Setup, "Initializing service manager...");
clash_verge_service_ipc::set_config(ServiceManager::config()).await;
if !is_service_ipc_path_exists() {
logging!(
warn,
Type::Setup,
"Service IPC path does not exist, service may be unavailable"
);
return;
}
if SERVICE_MANAGER.lock().await.init().await.is_ok() {
@@ -215,39 +152,27 @@ pub(super) async fn init_service_manager() {
}
pub(super) async fn init_core_manager() {
logging!(info, Type::Setup, "Initializing core manager...");
logging_error!(Type::Setup, CoreManager::global().init().await);
}
pub(super) async fn init_system_proxy() {
logging!(info, Type::Setup, "Initializing system proxy...");
logging_error!(
Type::Setup,
sysopt::Sysopt::global().update_sysproxy().await
);
logging_error!(Type::Setup, sysopt::Sysopt::global().update_sysproxy().await);
}
pub(super) fn init_system_proxy_guard() {
logging!(info, Type::Setup, "Initializing system proxy guard...");
logging_error!(Type::Setup, sysopt::Sysopt::global().init_guard_sysproxy());
}
pub(super) async fn refresh_tray_menu() {
logging!(info, Type::Setup, "Refreshing tray menu...");
logging_error!(Type::Setup, Tray::global().update_part().await);
}
pub(super) async fn init_window() {
logging!(info, Type::Setup, "Initializing main window...");
let is_silent_start =
{ Config::verge().await.latest_ref().enable_silent_start }.unwrap_or(false);
let is_silent_start = Config::verge().await.latest_ref().enable_silent_start.unwrap_or(false);
#[cfg(target_os = "macos")]
{
if is_silent_start {
use crate::core::handle::Handle;
Handle::global().set_activation_policy_accessory();
}
}
WindowManager::create_window(!is_silent_start).await;
}

View File

@@ -67,7 +67,7 @@ pub fn embed_server() {
.expect("failed to set shutdown signal for embedded server");
let port = IVerge::get_singleton_port();
AsyncHandler::spawn(move || async move {
let _ = AsyncHandler::spawn(move || async move {
let visible = warp::path!("commands" / "visible").and_then(|| async {
logging!(info, Type::Window, "检测到从单例模式恢复应用窗口");
if !lightweight::exit_lightweight_mode().await {
@@ -84,20 +84,17 @@ pub fn embed_server() {
let verge_config = Config::verge().await;
let clash_config = Config::clash().await;
let content = verge_config
let pac_content = verge_config
.latest_ref()
.pac_file_content
.clone()
.unwrap_or(DEFAULT_PAC.into());
let mixed_port = verge_config
let pac_port = verge_config
.latest_ref()
.verge_mixed_port
.unwrap_or(clash_config.latest_ref().get_mixed_port());
// Clone the content and port for the closure to avoid borrowing issues
let pac_content = content.clone();
let pac_port = mixed_port;
let pac = warp::path!("commands" / "pac").map(move || {
let processed_content = pac_content.replace("%mixed-port%", &format!("{pac_port}"));
warp::http::Response::builder()
@@ -110,9 +107,8 @@ pub fn embed_server() {
let scheme = warp::path!("commands" / "scheme")
.and(warp::query::<QueryParam>())
.map(|query: QueryParam| {
// Spawn async work in a fire-and-forget manner
let param = query.param.clone();
tokio::task::spawn_local(async move {
let _ = tokio::task::spawn_local(async move {
logging_error!(Type::Setup, resolve::resolve_scheme(param).await);
});
warp::reply::with_status::<String>("ok".into(), warp::http::StatusCode::OK)