mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
refactor: enhance error handling and logging in core components and server initialization
This commit is contained in:
@@ -46,14 +46,10 @@ pub struct CoreManager {
|
|||||||
last_update: Arc<Mutex<Option<Instant>>>,
|
last_update: Arc<Mutex<Option<Instant>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 内核运行模式
|
#[derive(Debug, Clone, Copy, serde::Serialize, PartialEq, Eq)]
|
||||||
#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)]
|
|
||||||
pub enum RunningMode {
|
pub enum RunningMode {
|
||||||
/// 服务模式运行
|
|
||||||
Service,
|
Service,
|
||||||
/// Sidecar 模式运行
|
|
||||||
Sidecar,
|
Sidecar,
|
||||||
/// 未运行
|
|
||||||
NotRunning,
|
NotRunning,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,12 +65,16 @@ impl fmt::Display for RunningMode {
|
|||||||
|
|
||||||
use crate::config::IVerge;
|
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 {
|
impl CoreManager {
|
||||||
/// 使用默认配置
|
|
||||||
pub async fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> {
|
pub async fn use_default_config(&self, msg_type: &str, msg_content: &str) -> Result<()> {
|
||||||
let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG);
|
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();
|
let clash_config = Config::clash().await.latest_ref().0.clone();
|
||||||
|
|
||||||
*Config::runtime().await.draft_mut() = Box::new(IRuntime {
|
*Config::runtime().await.draft_mut() = Box::new(IRuntime {
|
||||||
@@ -215,115 +215,85 @@ impl CoreManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn should_restart_on_reload_error(err: &MihomoError) -> bool {
|
fn should_restart_on_reload_error(err: &MihomoError) -> bool {
|
||||||
match err {
|
fn is_connection_io_error(kind: std::io::ErrorKind) -> bool {
|
||||||
MihomoError::ConnectionFailed | MihomoError::ConnectionLost => true,
|
matches!(
|
||||||
MihomoError::Io(io_err) => matches!(
|
kind,
|
||||||
io_err.kind(),
|
|
||||||
std::io::ErrorKind::ConnectionAborted
|
std::io::ErrorKind::ConnectionAborted
|
||||||
| std::io::ErrorKind::ConnectionRefused
|
| std::io::ErrorKind::ConnectionRefused
|
||||||
| std::io::ErrorKind::ConnectionReset
|
| std::io::ErrorKind::ConnectionReset
|
||||||
| std::io::ErrorKind::NotFound
|
| 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) => {
|
MihomoError::Reqwest(req_err) => {
|
||||||
if req_err.is_connect() || req_err.is_timeout() {
|
if req_err.is_connect() || req_err.is_timeout() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let err_text = req_err.to_string();
|
|
||||||
if let Some(source) = req_err.source() {
|
if let Some(source) = req_err.source() {
|
||||||
if let Some(io_err) = source.downcast_ref::<std::io::Error>() {
|
if let Some(io_err) = source.downcast_ref::<std::io::Error>() {
|
||||||
if matches!(
|
if is_connection_io_error(io_err.kind()) {
|
||||||
io_err.kind(),
|
|
||||||
std::io::ErrorKind::ConnectionAborted
|
|
||||||
| std::io::ErrorKind::ConnectionRefused
|
|
||||||
| std::io::ErrorKind::ConnectionReset
|
|
||||||
| std::io::ErrorKind::NotFound
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if source.to_string().contains("Failed to create connection") {
|
} else if contains_error_pattern(&source.to_string()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err_text.contains("Failed to create connection")
|
contains_error_pattern(&req_err.to_string())
|
||||||
|| 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")
|
|
||||||
}
|
}
|
||||||
|
MihomoError::FailedResponse(msg) => contains_error_pattern(msg),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoreManager {
|
impl CoreManager {
|
||||||
/// 清理多余的 mihomo 进程
|
|
||||||
async fn cleanup_orphaned_mihomo_processes(&self) -> Result<()> {
|
async fn cleanup_orphaned_mihomo_processes(&self) -> Result<()> {
|
||||||
logging!(info, Type::Core, "开始清理多余的 mihomo 进程");
|
logging!(info, Type::Core, "开始清理多余的 mihomo 进程");
|
||||||
|
|
||||||
// 获取当前管理的进程 PID
|
let current_pid = self.child_sidecar.lock().as_ref().and_then(|child| child.pid());
|
||||||
let current_pid = {
|
|
||||||
let child_guard = self.child_sidecar.lock();
|
|
||||||
child_guard.as_ref().map(|child| child.pid())
|
|
||||||
};
|
|
||||||
|
|
||||||
let target_processes = ["verge-mihomo", "verge-mihomo-alpha"];
|
let target_processes = ["verge-mihomo", "verge-mihomo-alpha"];
|
||||||
|
|
||||||
// 并行查找所有目标进程
|
let process_futures = target_processes.iter().map(|&target| {
|
||||||
let mut process_futures = Vec::new();
|
|
||||||
for &target in &target_processes {
|
|
||||||
let process_name = if cfg!(windows) {
|
let process_name = if cfg!(windows) {
|
||||||
format!("{target}.exe")
|
format!("{target}.exe")
|
||||||
} else {
|
} 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;
|
let process_results = futures::future::join_all(process_futures).await;
|
||||||
|
|
||||||
// 收集所有需要终止的进程PID
|
let pids_to_kill: Vec<_> = process_results
|
||||||
let mut pids_to_kill = Vec::new();
|
.into_iter()
|
||||||
for result in process_results {
|
.filter_map(|result| result.ok())
|
||||||
match result {
|
.flat_map(|(pids, process_name)| {
|
||||||
Ok((pids, process_name)) => {
|
pids.into_iter()
|
||||||
for pid in pids {
|
.filter(|&pid| Some(pid) != current_pid)
|
||||||
// 跳过当前管理的进程
|
.map(move |pid| (pid, process_name.clone()))
|
||||||
if let Some(current) = current_pid
|
})
|
||||||
&& Some(pid) == current
|
.collect();
|
||||||
{
|
|
||||||
logging!(
|
|
||||||
debug,
|
|
||||||
Type::Core,
|
|
||||||
"跳过当前管理的进程: {} (PID: {})",
|
|
||||||
process_name,
|
|
||||||
pid
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pids_to_kill.push((pid, process_name.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
logging!(debug, Type::Core, "查找进程时发生错误: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if pids_to_kill.is_empty() {
|
if pids_to_kill.is_empty() {
|
||||||
logging!(debug, Type::Core, "未发现多余的 mihomo 进程");
|
logging!(debug, Type::Core, "未发现多余的 mihomo 进程");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut kill_futures = Vec::new();
|
let kill_futures = pids_to_kill.iter()
|
||||||
for (pid, process_name) in &pids_to_kill {
|
.map(|(pid, name)| self.kill_process_with_verification(*pid, name.clone()));
|
||||||
kill_futures.push(self.kill_process_with_verification(*pid, process_name.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let kill_results = futures::future::join_all(kill_futures).await;
|
let killed_count = futures::future::join_all(kill_futures)
|
||||||
|
.await
|
||||||
let killed_count = kill_results.into_iter().filter(|&success| success).count();
|
.into_iter()
|
||||||
|
.filter(|&success| success)
|
||||||
|
.count();
|
||||||
|
|
||||||
if killed_count > 0 {
|
if killed_count > 0 {
|
||||||
logging!(
|
logging!(
|
||||||
@@ -355,10 +325,9 @@ impl CoreManager {
|
|||||||
|
|
||||||
let process_name_clone = process_name.clone();
|
let process_name_clone = process_name.clone();
|
||||||
let pids = AsyncHandler::spawn_blocking(move || -> Result<Vec<u32>> {
|
let pids = AsyncHandler::spawn_blocking(move || -> Result<Vec<u32>> {
|
||||||
let mut pids = Vec::new();
|
let mut pids = Vec::with_capacity(8);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// 创建进程快照
|
|
||||||
let snapshot: HANDLE = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
let snapshot: HANDLE = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
if snapshot == winapi::um::handleapi::INVALID_HANDLE_VALUE {
|
if snapshot == winapi::um::handleapi::INVALID_HANDLE_VALUE {
|
||||||
return Err(anyhow::anyhow!("Failed to create process snapshot"));
|
return Err(anyhow::anyhow!("Failed to create process snapshot"));
|
||||||
@@ -367,28 +336,24 @@ impl CoreManager {
|
|||||||
let mut pe32: PROCESSENTRY32W = mem::zeroed();
|
let mut pe32: PROCESSENTRY32W = mem::zeroed();
|
||||||
pe32.dwSize = mem::size_of::<PROCESSENTRY32W>() as u32;
|
pe32.dwSize = mem::size_of::<PROCESSENTRY32W>() as u32;
|
||||||
|
|
||||||
// 获取第一个进程
|
|
||||||
if Process32FirstW(snapshot, &mut pe32) != 0 {
|
if Process32FirstW(snapshot, &mut pe32) != 0 {
|
||||||
loop {
|
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());
|
.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) {
|
if exe_file.eq_ignore_ascii_case(&process_name_clone) {
|
||||||
pids.push(pe32.th32ProcessID);
|
pids.push(pe32.th32ProcessID);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if Process32NextW(snapshot, &mut pe32) == 0 {
|
if Process32NextW(snapshot, &mut pe32) == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭句柄
|
|
||||||
CloseHandle(snapshot);
|
CloseHandle(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,7 +372,6 @@ impl CoreManager {
|
|||||||
.output()
|
.output()
|
||||||
.await?
|
.await?
|
||||||
} else {
|
} else {
|
||||||
// Linux
|
|
||||||
tokio::process::Command::new("pidof")
|
tokio::process::Command::new("pidof")
|
||||||
.arg(&process_name)
|
.arg(&process_name)
|
||||||
.output()
|
.output()
|
||||||
@@ -419,28 +383,17 @@ impl CoreManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
let mut pids = Vec::new();
|
let pids: Vec<u32> = stdout
|
||||||
|
.split_whitespace()
|
||||||
// Unix系统直接解析PID列表
|
.filter_map(|s| s.parse().ok())
|
||||||
for pid_str in stdout.split_whitespace() {
|
.collect();
|
||||||
if let Ok(pid) = pid_str.parse::<u32>() {
|
|
||||||
pids.push(pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((pids, process_name))
|
Ok((pids, process_name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 终止进程并验证结果 - 使用Windows API直接终止,更优雅高效
|
|
||||||
async fn kill_process_with_verification(&self, pid: u32, process_name: String) -> bool {
|
async fn kill_process_with_verification(&self, pid: u32, process_name: String) -> bool {
|
||||||
logging!(
|
logging!(info, Type::Core, "尝试终止进程: {} (PID: {})", process_name, pid);
|
||||||
info,
|
|
||||||
Type::Core,
|
|
||||||
"尝试终止进程: {} (PID: {})",
|
|
||||||
process_name,
|
|
||||||
pid
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let success = {
|
let success = {
|
||||||
@@ -448,93 +401,60 @@ impl CoreManager {
|
|||||||
use winapi::um::processthreadsapi::{OpenProcess, TerminateProcess};
|
use winapi::um::processthreadsapi::{OpenProcess, TerminateProcess};
|
||||||
use winapi::um::winnt::{HANDLE, PROCESS_TERMINATE};
|
use winapi::um::winnt::{HANDLE, PROCESS_TERMINATE};
|
||||||
|
|
||||||
AsyncHandler::spawn_blocking(move || -> bool {
|
AsyncHandler::spawn_blocking(move || unsafe {
|
||||||
unsafe {
|
let handle: HANDLE = OpenProcess(PROCESS_TERMINATE, 0, pid);
|
||||||
let process_handle: HANDLE = OpenProcess(PROCESS_TERMINATE, 0, pid);
|
if handle.is_null() {
|
||||||
if process_handle.is_null() {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let result = TerminateProcess(process_handle, 1);
|
let result = TerminateProcess(handle, 1) != 0;
|
||||||
CloseHandle(process_handle);
|
CloseHandle(handle);
|
||||||
|
result
|
||||||
result != 0
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let success = {
|
let success = tokio::process::Command::new("kill")
|
||||||
tokio::process::Command::new("kill")
|
|
||||||
.args(["-9", &pid.to_string()])
|
.args(["-9", &pid.to_string()])
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await
|
||||||
.map(|output| output.status.success())
|
.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;
|
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||||
|
|
||||||
let still_running = self.is_process_running(pid).await.unwrap_or(false);
|
if self.is_process_running(pid).await.unwrap_or(false) {
|
||||||
if still_running {
|
logging!(warn, Type::Core, "进程 {} (PID: {}) 终止命令成功但进程仍在运行", process_name, pid);
|
||||||
logging!(
|
|
||||||
warn,
|
|
||||||
Type::Core,
|
|
||||||
"进程 {} (PID: {}) 终止命令成功但进程仍在运行",
|
|
||||||
process_name,
|
|
||||||
pid
|
|
||||||
);
|
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
logging!(
|
logging!(info, Type::Core, "成功终止进程: {} (PID: {})", process_name, pid);
|
||||||
info,
|
|
||||||
Type::Core,
|
|
||||||
"成功终止进程: {} (PID: {})",
|
|
||||||
process_name,
|
|
||||||
pid
|
|
||||||
);
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
logging!(
|
|
||||||
warn,
|
|
||||||
Type::Core,
|
|
||||||
"无法终止进程: {} (PID: {})",
|
|
||||||
process_name,
|
|
||||||
pid
|
|
||||||
);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Windows API检查进程
|
|
||||||
async fn is_process_running(&self, pid: u32) -> Result<bool> {
|
async fn is_process_running(&self, pid: u32) -> Result<bool> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
use winapi::shared::minwindef::DWORD;
|
use winapi::shared::minwindef::DWORD;
|
||||||
use winapi::um::handleapi::CloseHandle;
|
use winapi::um::handleapi::CloseHandle;
|
||||||
use winapi::um::processthreadsapi::GetExitCodeProcess;
|
use winapi::um::processthreadsapi::{GetExitCodeProcess, OpenProcess};
|
||||||
use winapi::um::processthreadsapi::OpenProcess;
|
|
||||||
use winapi::um::winnt::{HANDLE, PROCESS_QUERY_INFORMATION};
|
use winapi::um::winnt::{HANDLE, PROCESS_QUERY_INFORMATION};
|
||||||
|
|
||||||
AsyncHandler::spawn_blocking(move || -> Result<bool> {
|
AsyncHandler::spawn_blocking(move || unsafe {
|
||||||
unsafe {
|
let handle: HANDLE = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid);
|
||||||
let process_handle: HANDLE = OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid);
|
if handle.is_null() {
|
||||||
if process_handle.is_null() {
|
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let mut exit_code: DWORD = 0;
|
let mut exit_code: DWORD = 0;
|
||||||
let result = GetExitCodeProcess(process_handle, &mut exit_code);
|
let result = GetExitCodeProcess(handle, &mut exit_code);
|
||||||
CloseHandle(process_handle);
|
CloseHandle(handle);
|
||||||
|
Ok(result != 0 && exit_code == 259)
|
||||||
if result == 0 {
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
Ok(exit_code == 259)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
@@ -580,18 +500,10 @@ impl CoreManager {
|
|||||||
AsyncHandler::spawn(|| async move {
|
AsyncHandler::spawn(|| async move {
|
||||||
while let Some(event) = rx.recv().await {
|
while let Some(event) = rx.recv().await {
|
||||||
match event {
|
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 mut now = DeferredNow::default();
|
||||||
let message =
|
let message = CompactString::from(String::from_utf8_lossy(&line).as_ref());
|
||||||
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 w = shared_writer.lock().await;
|
let w = shared_writer.lock().await;
|
||||||
write_sidecar_log(w, &mut now, Level::Error, &message);
|
write_sidecar_log(w, &mut now, Level::Error, &message);
|
||||||
ClashLogger::global().append_log(message);
|
ClashLogger::global().append_log(message);
|
||||||
@@ -660,22 +572,13 @@ impl Default for CoreManager {
|
|||||||
|
|
||||||
impl CoreManager {
|
impl CoreManager {
|
||||||
pub async fn init(&self) -> Result<()> {
|
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 {
|
if let Err(e) = self.cleanup_orphaned_mihomo_processes().await {
|
||||||
logging!(
|
logging!(warn, Type::Core, "清理遗留 mihomo 进程失败: {}", e);
|
||||||
warn,
|
|
||||||
Type::Core,
|
|
||||||
"应用初始化时清理多余 mihomo 进程失败: {}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用简化的启动流程
|
|
||||||
logging!(info, Type::Core, "开始核心初始化");
|
|
||||||
self.start_core().await?;
|
self.start_core().await?;
|
||||||
|
|
||||||
logging!(info, Type::Core, "核心初始化完成");
|
logging!(info, Type::Core, "核心初始化完成");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -686,8 +589,7 @@ impl CoreManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_running_mode(&self) -> RunningMode {
|
pub fn get_running_mode(&self) -> RunningMode {
|
||||||
let guard = self.running.lock();
|
*self.running.lock()
|
||||||
(*guard).clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ mod app_init {
|
|||||||
app.deep_link().on_open_url(|event| {
|
app.deep_link().on_open_url(|event| {
|
||||||
let url = event.urls().first().map(|u| u.to_string());
|
let url = event.urls().first().map(|u| u.to_string());
|
||||||
if let Some(url) = url {
|
if let Some(url) = url {
|
||||||
AsyncHandler::spawn(|| async {
|
let _ = AsyncHandler::spawn(|| async {
|
||||||
if let Err(e) = resolve::resolve_scheme(url).await {
|
if let Err(e) = resolve::resolve_scheme(url).await {
|
||||||
logging!(error, Type::Setup, "Failed to resolve scheme: {}", e);
|
logging!(error, Type::Setup, "Failed to resolve scheme: {}", e);
|
||||||
}
|
}
|
||||||
@@ -123,11 +123,9 @@ mod app_init {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate all command handlers for the application
|
|
||||||
pub fn generate_handlers()
|
pub fn generate_handlers()
|
||||||
-> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Send + Sync + 'static {
|
-> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Send + Sync + 'static {
|
||||||
tauri::generate_handler![
|
tauri::generate_handler![
|
||||||
// Common commands
|
|
||||||
cmd::get_sys_proxy,
|
cmd::get_sys_proxy,
|
||||||
cmd::get_auto_proxy,
|
cmd::get_auto_proxy,
|
||||||
cmd::open_app_dir,
|
cmd::open_app_dir,
|
||||||
@@ -138,27 +136,22 @@ mod app_init {
|
|||||||
cmd::get_network_interfaces,
|
cmd::get_network_interfaces,
|
||||||
cmd::get_system_hostname,
|
cmd::get_system_hostname,
|
||||||
cmd::restart_app,
|
cmd::restart_app,
|
||||||
// Core management
|
|
||||||
cmd::start_core,
|
cmd::start_core,
|
||||||
cmd::stop_core,
|
cmd::stop_core,
|
||||||
cmd::restart_core,
|
cmd::restart_core,
|
||||||
// Application lifecycle
|
|
||||||
cmd::notify_ui_ready,
|
cmd::notify_ui_ready,
|
||||||
cmd::update_ui_stage,
|
cmd::update_ui_stage,
|
||||||
cmd::get_running_mode,
|
cmd::get_running_mode,
|
||||||
cmd::get_app_uptime,
|
cmd::get_app_uptime,
|
||||||
cmd::get_auto_launch_status,
|
cmd::get_auto_launch_status,
|
||||||
cmd::is_admin,
|
cmd::is_admin,
|
||||||
// Lightweight mode
|
|
||||||
cmd::entry_lightweight_mode,
|
cmd::entry_lightweight_mode,
|
||||||
cmd::exit_lightweight_mode,
|
cmd::exit_lightweight_mode,
|
||||||
// Service management
|
|
||||||
cmd::install_service,
|
cmd::install_service,
|
||||||
cmd::uninstall_service,
|
cmd::uninstall_service,
|
||||||
cmd::reinstall_service,
|
cmd::reinstall_service,
|
||||||
cmd::repair_service,
|
cmd::repair_service,
|
||||||
cmd::is_service_available,
|
cmd::is_service_available,
|
||||||
// Clash core commands
|
|
||||||
cmd::get_clash_info,
|
cmd::get_clash_info,
|
||||||
cmd::patch_clash_config,
|
cmd::patch_clash_config,
|
||||||
cmd::patch_clash_mode,
|
cmd::patch_clash_mode,
|
||||||
@@ -178,7 +171,6 @@ mod app_init {
|
|||||||
cmd::get_dns_config_content,
|
cmd::get_dns_config_content,
|
||||||
cmd::validate_dns_config,
|
cmd::validate_dns_config,
|
||||||
cmd::get_clash_logs,
|
cmd::get_clash_logs,
|
||||||
// Verge configuration
|
|
||||||
cmd::get_verge_config,
|
cmd::get_verge_config,
|
||||||
cmd::patch_verge_config,
|
cmd::patch_verge_config,
|
||||||
cmd::test_delay,
|
cmd::test_delay,
|
||||||
@@ -188,7 +180,6 @@ mod app_init {
|
|||||||
cmd::open_devtools,
|
cmd::open_devtools,
|
||||||
cmd::exit_app,
|
cmd::exit_app,
|
||||||
cmd::get_network_interfaces_info,
|
cmd::get_network_interfaces_info,
|
||||||
// Profile management
|
|
||||||
cmd::get_profiles,
|
cmd::get_profiles,
|
||||||
cmd::enhance_profiles,
|
cmd::enhance_profiles,
|
||||||
cmd::patch_profiles_config,
|
cmd::patch_profiles_config,
|
||||||
@@ -202,10 +193,8 @@ mod app_init {
|
|||||||
cmd::read_profile_file,
|
cmd::read_profile_file,
|
||||||
cmd::save_profile_file,
|
cmd::save_profile_file,
|
||||||
cmd::get_next_update_time,
|
cmd::get_next_update_time,
|
||||||
// Script validation
|
|
||||||
cmd::script_validate_notice,
|
cmd::script_validate_notice,
|
||||||
cmd::validate_script_file,
|
cmd::validate_script_file,
|
||||||
// Backup and WebDAV
|
|
||||||
cmd::create_local_backup,
|
cmd::create_local_backup,
|
||||||
cmd::list_local_backup,
|
cmd::list_local_backup,
|
||||||
cmd::delete_local_backup,
|
cmd::delete_local_backup,
|
||||||
@@ -216,10 +205,8 @@ mod app_init {
|
|||||||
cmd::list_webdav_backup,
|
cmd::list_webdav_backup,
|
||||||
cmd::delete_webdav_backup,
|
cmd::delete_webdav_backup,
|
||||||
cmd::restore_webdav_backup,
|
cmd::restore_webdav_backup,
|
||||||
// Diagnostics and system info
|
|
||||||
cmd::export_diagnostic_info,
|
cmd::export_diagnostic_info,
|
||||||
cmd::get_system_info,
|
cmd::get_system_info,
|
||||||
// Media unlock checker
|
|
||||||
cmd::get_unlock_items,
|
cmd::get_unlock_items,
|
||||||
cmd::check_media_unlock,
|
cmd::check_media_unlock,
|
||||||
]
|
]
|
||||||
@@ -227,18 +214,15 @@ mod app_init {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
// Setup singleton check
|
|
||||||
if app_init::init_singleton_check().is_err() {
|
if app_init::init_singleton_check().is_err() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = utils::dirs::init_portable_flag();
|
let _ = utils::dirs::init_portable_flag();
|
||||||
|
|
||||||
// Set Linux environment variable
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
linux::configure_environment();
|
linux::configure_environment();
|
||||||
|
|
||||||
// Create and configure the Tauri builder
|
|
||||||
let builder = app_init::setup_plugins(tauri::Builder::default())
|
let builder = app_init::setup_plugins(tauri::Builder::default())
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
logging!(info, Type::Setup, "开始应用初始化...");
|
logging!(info, Type::Setup, "开始应用初始化...");
|
||||||
@@ -248,89 +232,56 @@ pub fn run() {
|
|||||||
.set(app.app_handle().clone())
|
.set(app.app_handle().clone())
|
||||||
.expect("failed to set global app handle");
|
.expect("failed to set global app handle");
|
||||||
|
|
||||||
// Setup autostart plugin
|
|
||||||
if let Err(e) = app_init::setup_autostart(app) {
|
if let Err(e) = app_init::setup_autostart(app) {
|
||||||
logging!(error, Type::Setup, "Failed to setup autostart: {}", e);
|
logging!(error, Type::Setup, "Failed to setup autostart: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup deep links
|
|
||||||
if let Err(e) = app_init::setup_deep_links(app) {
|
if let Err(e) = app_init::setup_deep_links(app) {
|
||||||
logging!(error, Type::Setup, "Failed to setup deep links: {}", e);
|
logging!(error, Type::Setup, "Failed to setup deep links: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup window state management
|
|
||||||
if let Err(e) = app_init::setup_window_state(app) {
|
if let Err(e) = app_init::setup_window_state(app) {
|
||||||
logging!(error, Type::Setup, "Failed to setup window state: {}", e);
|
logging!(error, Type::Setup, "Failed to setup window state: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
logging!(info, Type::Setup, "执行主要设置操作...");
|
|
||||||
|
|
||||||
resolve::resolve_setup_handle();
|
resolve::resolve_setup_handle();
|
||||||
// 异步任务启动,不等待完成
|
|
||||||
resolve::resolve_setup_async();
|
resolve::resolve_setup_async();
|
||||||
resolve::resolve_setup_sync();
|
resolve::resolve_setup_sync();
|
||||||
|
|
||||||
logging!(info, Type::Setup, "初始化已启动,继续执行");
|
logging!(info, Type::Setup, "初始化已启动");
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.invoke_handler(app_init::generate_handlers());
|
.invoke_handler(app_init::generate_handlers());
|
||||||
|
|
||||||
/// Event handling helper functions
|
|
||||||
mod event_handlers {
|
mod event_handlers {
|
||||||
use crate::core::handle;
|
use crate::core::handle;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Handle application ready/resumed events
|
|
||||||
pub fn handle_ready_resumed(_app_handle: &AppHandle) {
|
pub fn handle_ready_resumed(_app_handle: &AppHandle) {
|
||||||
// 双重检查:确保不在退出状态
|
|
||||||
if handle::Handle::global().is_exiting() {
|
if handle::Handle::global().is_exiting() {
|
||||||
logging!(
|
logging!(debug, Type::System, "应用正在退出,跳过处理");
|
||||||
debug,
|
|
||||||
Type::System,
|
|
||||||
"handle_ready_resumed: 应用正在退出,跳过处理"
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logging!(info, Type::System, "应用就绪或恢复");
|
logging!(info, Type::System, "应用就绪");
|
||||||
handle::Handle::global().init();
|
handle::Handle::global().init();
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[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, "设置macOS窗口标题");
|
|
||||||
let _ = window.set_title("Clash Verge");
|
let _ = window.set_title("Clash Verge");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle application reopen events (macOS)
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub async fn handle_reopen(has_visible_windows: bool) {
|
pub async fn handle_reopen(has_visible_windows: bool) {
|
||||||
logging!(
|
|
||||||
info,
|
|
||||||
Type::System,
|
|
||||||
"处理 macOS 应用重新打开事件: has_visible_windows={}",
|
|
||||||
has_visible_windows
|
|
||||||
);
|
|
||||||
|
|
||||||
handle::Handle::global().init();
|
handle::Handle::global().init();
|
||||||
|
|
||||||
if !has_visible_windows {
|
if !has_visible_windows {
|
||||||
// 当没有可见窗口时,设置为 regular 模式并显示主窗口
|
|
||||||
handle::Handle::global().set_activation_policy_regular();
|
handle::Handle::global().set_activation_policy_regular();
|
||||||
|
let _ = WindowManager::show_main_window().await;
|
||||||
logging!(info, Type::System, "没有可见窗口,尝试显示主窗口");
|
|
||||||
|
|
||||||
let result = WindowManager::show_main_window().await;
|
|
||||||
logging!(info, Type::System, "窗口显示操作完成,结果: {:?}", result);
|
|
||||||
} else {
|
|
||||||
logging!(info, Type::System, "已有可见窗口,无需额外操作");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle window close requests
|
|
||||||
pub fn handle_window_close(api: &tauri::WindowEvent) {
|
pub fn handle_window_close(api: &tauri::WindowEvent) {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
handle::Handle::global().set_activation_policy_accessory();
|
handle::Handle::global().set_activation_policy_accessory();
|
||||||
@@ -339,20 +290,16 @@ pub fn run() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!(target: "app", "closing window...");
|
|
||||||
if let tauri::WindowEvent::CloseRequested { api, .. } = api {
|
if let tauri::WindowEvent::CloseRequested { api, .. } = api {
|
||||||
api.prevent_close();
|
api.prevent_close();
|
||||||
if let Some(window) = core::handle::Handle::get_window() {
|
if let Some(window) = core::handle::Handle::get_window() {
|
||||||
let _ = window.hide();
|
let _ = window.hide();
|
||||||
} else {
|
|
||||||
logging!(warn, Type::Window, "尝试隐藏窗口但窗口不存在");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle window focus events
|
|
||||||
pub fn handle_window_focus(focused: bool) {
|
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()
|
let is_enable_global_hotkey = Config::verge()
|
||||||
.await
|
.await
|
||||||
.latest_ref()
|
.latest_ref()
|
||||||
@@ -363,185 +310,99 @@ pub fn run() {
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
use crate::core::hotkey::SystemHotkey;
|
use crate::core::hotkey::SystemHotkey;
|
||||||
if let Err(e) = hotkey::Hotkey::global()
|
let _ = hotkey::Hotkey::global().register_system_hotkey(SystemHotkey::CmdQ).await;
|
||||||
.register_system_hotkey(SystemHotkey::CmdQ)
|
let _ = hotkey::Hotkey::global().register_system_hotkey(SystemHotkey::CmdW).await;
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_enable_global_hotkey
|
if !is_enable_global_hotkey {
|
||||||
&& let Err(e) = hotkey::Hotkey::global().init().await
|
let _ = hotkey::Hotkey::global().init().await;
|
||||||
{
|
|
||||||
logging!(error, Type::Hotkey, "Failed to init hotkeys: {}", e);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle unfocused state
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
use crate::core::hotkey::SystemHotkey;
|
use crate::core::hotkey::SystemHotkey;
|
||||||
if let Err(e) =
|
let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdQ);
|
||||||
hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdQ)
|
let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdW);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_enable_global_hotkey && let Err(e) = hotkey::Hotkey::global().reset() {
|
if !is_enable_global_hotkey {
|
||||||
logging!(error, Type::Hotkey, "Failed to reset hotkeys: {}", e);
|
let _ = hotkey::Hotkey::global().reset();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle window destroyed events
|
|
||||||
pub fn handle_window_destroyed() {
|
pub fn handle_window_destroyed() {
|
||||||
AsyncHandler::spawn(|| async {
|
let _ = AsyncHandler::spawn(|| async {
|
||||||
if let Err(e) = handle::Handle::mihomo()
|
let _ = handle::Handle::mihomo()
|
||||||
.await
|
.await
|
||||||
.clear_all_ws_connections()
|
.clear_all_ws_connections()
|
||||||
.await
|
.await;
|
||||||
{
|
|
||||||
logging!(warn, Type::Window, "清理 WebSocket 连接失败: {}", e);
|
|
||||||
} else {
|
|
||||||
logging!(info, Type::Window, "WebSocket 连接已清理");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
use crate::core::hotkey::SystemHotkey;
|
use crate::core::hotkey::SystemHotkey;
|
||||||
if let Err(e) =
|
let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdQ);
|
||||||
hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdQ)
|
let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdW);
|
||||||
{
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock context for Clippy to avoid build errors
|
|
||||||
#[cfg(feature = "clippy")]
|
#[cfg(feature = "clippy")]
|
||||||
let context = tauri::test::mock_context(tauri::test::noop_assets());
|
let context = tauri::test::mock_context(tauri::test::noop_assets());
|
||||||
#[cfg(feature = "clippy")]
|
#[cfg(feature = "clippy")]
|
||||||
let app = builder.build(context).unwrap_or_else(|e| {
|
let app = builder.build(context).unwrap_or_else(|e| {
|
||||||
logging!(
|
logging!(error, Type::Setup, "Failed to build Tauri application: {}", e);
|
||||||
error,
|
|
||||||
Type::Setup,
|
|
||||||
"Failed to build Tauri application: {}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Build the application
|
|
||||||
#[cfg(not(feature = "clippy"))]
|
#[cfg(not(feature = "clippy"))]
|
||||||
let app = builder
|
let app = builder.build(tauri::generate_context!()).unwrap_or_else(|e| {
|
||||||
.build(tauri::generate_context!())
|
logging!(error, Type::Setup, "Failed to build Tauri application: {}", e);
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
logging!(
|
|
||||||
error,
|
|
||||||
Type::Setup,
|
|
||||||
"Failed to build Tauri application: {}",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.run(|app_handle, e| {
|
app.run(|app_handle, e| {
|
||||||
match e {
|
match e {
|
||||||
tauri::RunEvent::Ready | tauri::RunEvent::Resumed => {
|
tauri::RunEvent::Ready | tauri::RunEvent::Resumed => {
|
||||||
// 如果正在退出,忽略 Ready/Resumed 事件
|
|
||||||
if core::handle::Handle::global().is_exiting() {
|
if core::handle::Handle::global().is_exiting() {
|
||||||
logging!(debug, Type::System, "忽略 Ready/Resumed 事件,应用正在退出");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
event_handlers::handle_ready_resumed(app_handle);
|
event_handlers::handle_ready_resumed(app_handle);
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
tauri::RunEvent::Reopen {
|
tauri::RunEvent::Reopen { has_visible_windows, .. } => {
|
||||||
has_visible_windows,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
// 如果正在退出,忽略 Reopen 事件
|
|
||||||
if core::handle::Handle::global().is_exiting() {
|
if core::handle::Handle::global().is_exiting() {
|
||||||
logging!(debug, Type::System, "忽略 Reopen 事件,应用正在退出");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AsyncHandler::spawn(move || async move {
|
let _ = AsyncHandler::spawn(move || async move {
|
||||||
event_handlers::handle_reopen(has_visible_windows).await;
|
event_handlers::handle_reopen(has_visible_windows).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
tauri::RunEvent::ExitRequested { api, code, .. } => {
|
tauri::RunEvent::ExitRequested { api, code, .. } => {
|
||||||
tauri::async_runtime::block_on(async {
|
tauri::async_runtime::block_on(async {
|
||||||
let _ = handle::Handle::mihomo()
|
let _ = handle::Handle::mihomo().await.clear_all_ws_connections().await;
|
||||||
.await
|
|
||||||
.clear_all_ws_connections()
|
|
||||||
.await;
|
|
||||||
});
|
});
|
||||||
// 如果已经在退出流程中,不要阻止退出
|
|
||||||
if core::handle::Handle::global().is_exiting() {
|
if core::handle::Handle::global().is_exiting() {
|
||||||
logging!(
|
|
||||||
info,
|
|
||||||
Type::System,
|
|
||||||
"应用正在退出,允许 ExitRequested (code: {:?})",
|
|
||||||
code
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只阻止外部的无退出码请求(如用户取消系统关机)
|
|
||||||
if code.is_none() {
|
if code.is_none() {
|
||||||
logging!(debug, Type::System, "阻止外部退出请求");
|
|
||||||
api.prevent_exit();
|
api.prevent_exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tauri::RunEvent::Exit => {
|
tauri::RunEvent::Exit => {
|
||||||
let handle = core::handle::Handle::global();
|
let handle = core::handle::Handle::global();
|
||||||
|
if !handle.is_exiting() {
|
||||||
if handle.is_exiting() {
|
|
||||||
logging!(
|
|
||||||
debug,
|
|
||||||
Type::System,
|
|
||||||
"Exit事件触发,但退出流程已执行,跳过重复清理"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
logging!(debug, Type::System, "Exit事件触发,执行清理流程");
|
|
||||||
handle.set_is_exiting();
|
handle.set_is_exiting();
|
||||||
EventDrivenProxyManager::global().notify_app_stopping();
|
EventDrivenProxyManager::global().notify_app_stopping();
|
||||||
feat::clean();
|
feat::clean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tauri::RunEvent::WindowEvent { label, event, .. } => {
|
tauri::RunEvent::WindowEvent { label, event, .. } if label == "main" => {
|
||||||
if label == "main" {
|
|
||||||
match event {
|
match event {
|
||||||
tauri::WindowEvent::CloseRequested { .. } => {
|
tauri::WindowEvent::CloseRequested { .. } => {
|
||||||
event_handlers::handle_window_close(&event);
|
event_handlers::handle_window_close(&event);
|
||||||
@@ -555,7 +416,6 @@ pub fn run() {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,91 +26,54 @@ pub fn resolve_setup_handle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_setup_sync() {
|
pub fn resolve_setup_sync() {
|
||||||
AsyncHandler::spawn(|| async {
|
let _ = AsyncHandler::spawn(|| async {
|
||||||
AsyncHandler::spawn_blocking(init_scheme);
|
let _ = AsyncHandler::spawn_blocking(init_scheme);
|
||||||
AsyncHandler::spawn_blocking(init_embed_server);
|
let _ = AsyncHandler::spawn_blocking(init_embed_server);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_setup_async() {
|
pub fn resolve_setup_async() {
|
||||||
let start_time = std::time::Instant::now();
|
let _ = AsyncHandler::spawn(|| async {
|
||||||
logging!(
|
|
||||||
info,
|
|
||||||
Type::Setup,
|
|
||||||
"开始执行异步设置任务... 线程ID: {:?}",
|
|
||||||
std::thread::current().id()
|
|
||||||
);
|
|
||||||
|
|
||||||
AsyncHandler::spawn(|| async {
|
|
||||||
#[cfg(not(feature = "tauri-dev"))]
|
#[cfg(not(feature = "tauri-dev"))]
|
||||||
resolve_setup_logger().await;
|
resolve_setup_logger().await;
|
||||||
logging!(
|
logging!(info, Type::ClashVergeRev, "Version: {}", env!("CARGO_PKG_VERSION"));
|
||||||
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;
|
init_verge_config().await;
|
||||||
|
|
||||||
Config::verify_config_initialization().await;
|
Config::verify_config_initialization().await;
|
||||||
|
|
||||||
// 优先创建窗口,提升启动体验
|
|
||||||
init_window().await;
|
init_window().await;
|
||||||
|
|
||||||
// 后台异步初始化核心,不阻塞窗口显示
|
|
||||||
let core_init = AsyncHandler::spawn(|| async {
|
let core_init = AsyncHandler::spawn(|| async {
|
||||||
init_service_manager().await;
|
init_service_manager().await;
|
||||||
init_core_manager().await;
|
init_core_manager().await;
|
||||||
init_system_proxy().await;
|
init_system_proxy().await;
|
||||||
AsyncHandler::spawn_blocking(|| {
|
let _ = AsyncHandler::spawn_blocking(init_system_proxy_guard);
|
||||||
init_system_proxy_guard();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let tray_and_refresh = async {
|
let tray_init = async {
|
||||||
init_tray().await;
|
init_tray().await;
|
||||||
refresh_tray_menu().await;
|
refresh_tray_menu().await;
|
||||||
};
|
};
|
||||||
|
|
||||||
futures::join!(
|
let _ = futures::join!(
|
||||||
core_init,
|
core_init,
|
||||||
tray_and_refresh,
|
tray_init,
|
||||||
init_timer(),
|
init_timer(),
|
||||||
init_hotkey(),
|
init_hotkey(),
|
||||||
init_auto_lightweight_mode(),
|
init_auto_lightweight_mode(),
|
||||||
init_once_auto_lightweight(),
|
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> {
|
pub async fn resolve_reset_async() -> Result<(), anyhow::Error> {
|
||||||
logging!(info, Type::Tray, "Resetting system proxy");
|
|
||||||
sysopt::Sysopt::global().reset_sysproxy().await?;
|
sysopt::Sysopt::global().reset_sysproxy().await?;
|
||||||
|
|
||||||
logging!(info, Type::Core, "Stopping core service");
|
|
||||||
CoreManager::global().stop_core().await?;
|
CoreManager::global().stop_core().await?;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
use dns::restore_public_dns;
|
use dns::restore_public_dns;
|
||||||
|
|
||||||
logging!(info, Type::System, "Restoring system DNS settings");
|
|
||||||
restore_public_dns().await;
|
restore_public_dns().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,95 +81,69 @@ pub async fn resolve_reset_async() -> Result<(), anyhow::Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_handle() {
|
pub fn init_handle() {
|
||||||
logging!(info, Type::Setup, "Initializing app handle...");
|
|
||||||
handle::Handle::global().init();
|
handle::Handle::global().init();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn init_scheme() {
|
pub(super) fn init_scheme() {
|
||||||
logging!(info, Type::Setup, "Initializing custom URL scheme");
|
|
||||||
logging_error!(Type::Setup, init::init_scheme());
|
logging_error!(Type::Setup, init::init_scheme());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "tauri-dev"))]
|
#[cfg(not(feature = "tauri-dev"))]
|
||||||
pub(super) async fn resolve_setup_logger() {
|
pub(super) async fn resolve_setup_logger() {
|
||||||
logging!(info, Type::Setup, "Initializing global logger...");
|
|
||||||
logging_error!(Type::Setup, init::init_logger().await);
|
logging_error!(Type::Setup, init::init_logger().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn resolve_scheme(param: String) -> Result<()> {
|
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);
|
logging_error!(Type::Setup, scheme::resolve_scheme(param).await);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn init_embed_server() {
|
pub(super) fn init_embed_server() {
|
||||||
logging!(info, Type::Setup, "Initializing embedded server...");
|
|
||||||
server::embed_server();
|
server::embed_server();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_resources() {
|
pub(super) async fn init_resources() {
|
||||||
logging!(info, Type::Setup, "Initializing resources...");
|
|
||||||
logging_error!(Type::Setup, init::init_resources().await);
|
logging_error!(Type::Setup, init::init_resources().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_startup_script() {
|
pub(super) async fn init_startup_script() {
|
||||||
logging!(info, Type::Setup, "Initializing startup script");
|
|
||||||
logging_error!(Type::Setup, init::startup_script().await);
|
logging_error!(Type::Setup, init::startup_script().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_timer() {
|
pub(super) async fn init_timer() {
|
||||||
logging!(info, Type::Setup, "Initializing timer...");
|
|
||||||
logging_error!(Type::Setup, Timer::global().init().await);
|
logging_error!(Type::Setup, Timer::global().init().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_hotkey() {
|
pub(super) async fn init_hotkey() {
|
||||||
logging!(info, Type::Setup, "Initializing hotkey...");
|
|
||||||
logging_error!(Type::Setup, Hotkey::global().init().await);
|
logging_error!(Type::Setup, Hotkey::global().init().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_once_auto_lightweight() {
|
pub(super) async fn init_once_auto_lightweight() {
|
||||||
logging!(
|
|
||||||
info,
|
|
||||||
Type::Lightweight,
|
|
||||||
"Running auto lightweight mode check..."
|
|
||||||
);
|
|
||||||
run_once_auto_lightweight().await;
|
run_once_auto_lightweight().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_auto_lightweight_mode() {
|
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);
|
logging_error!(Type::Setup, auto_lightweight_mode_init().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init_work_config() {
|
pub async fn init_work_config() {
|
||||||
logging!(info, Type::Setup, "Initializing work configuration...");
|
|
||||||
logging_error!(Type::Setup, init::init_config().await);
|
logging_error!(Type::Setup, init::init_config().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_tray() {
|
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" {
|
if std::env::var("CLASH_VERGE_DISABLE_TRAY").unwrap_or_default() == "1" {
|
||||||
logging!(info, Type::Setup, "System tray disabled via --no-tray flag");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logging!(info, Type::Setup, "Initializing system tray...");
|
|
||||||
logging_error!(Type::Setup, Tray::global().init().await);
|
logging_error!(Type::Setup, Tray::global().init().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_verge_config() {
|
pub(super) async fn init_verge_config() {
|
||||||
logging!(info, Type::Setup, "Initializing verge configuration...");
|
|
||||||
logging_error!(Type::Setup, Config::init_config().await);
|
logging_error!(Type::Setup, Config::init_config().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_service_manager() {
|
pub(super) async fn init_service_manager() {
|
||||||
logging!(info, Type::Setup, "Initializing service manager...");
|
|
||||||
clash_verge_service_ipc::set_config(ServiceManager::config()).await;
|
clash_verge_service_ipc::set_config(ServiceManager::config()).await;
|
||||||
if !is_service_ipc_path_exists() {
|
if !is_service_ipc_path_exists() {
|
||||||
logging!(
|
|
||||||
warn,
|
|
||||||
Type::Setup,
|
|
||||||
"Service IPC path does not exist, service may be unavailable"
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if SERVICE_MANAGER.lock().await.init().await.is_ok() {
|
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() {
|
pub(super) async fn init_core_manager() {
|
||||||
logging!(info, Type::Setup, "Initializing core manager...");
|
|
||||||
logging_error!(Type::Setup, CoreManager::global().init().await);
|
logging_error!(Type::Setup, CoreManager::global().init().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_system_proxy() {
|
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() {
|
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());
|
logging_error!(Type::Setup, sysopt::Sysopt::global().init_guard_sysproxy());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn refresh_tray_menu() {
|
pub(super) async fn refresh_tray_menu() {
|
||||||
logging!(info, Type::Setup, "Refreshing tray menu...");
|
|
||||||
logging_error!(Type::Setup, Tray::global().update_part().await);
|
logging_error!(Type::Setup, Tray::global().update_part().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn init_window() {
|
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")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
|
||||||
if is_silent_start {
|
if is_silent_start {
|
||||||
use crate::core::handle::Handle;
|
use crate::core::handle::Handle;
|
||||||
|
|
||||||
Handle::global().set_activation_policy_accessory();
|
Handle::global().set_activation_policy_accessory();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
WindowManager::create_window(!is_silent_start).await;
|
WindowManager::create_window(!is_silent_start).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ pub fn embed_server() {
|
|||||||
.expect("failed to set shutdown signal for embedded server");
|
.expect("failed to set shutdown signal for embedded server");
|
||||||
let port = IVerge::get_singleton_port();
|
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 {
|
let visible = warp::path!("commands" / "visible").and_then(|| async {
|
||||||
logging!(info, Type::Window, "检测到从单例模式恢复应用窗口");
|
logging!(info, Type::Window, "检测到从单例模式恢复应用窗口");
|
||||||
if !lightweight::exit_lightweight_mode().await {
|
if !lightweight::exit_lightweight_mode().await {
|
||||||
@@ -84,20 +84,17 @@ pub fn embed_server() {
|
|||||||
let verge_config = Config::verge().await;
|
let verge_config = Config::verge().await;
|
||||||
let clash_config = Config::clash().await;
|
let clash_config = Config::clash().await;
|
||||||
|
|
||||||
let content = verge_config
|
let pac_content = verge_config
|
||||||
.latest_ref()
|
.latest_ref()
|
||||||
.pac_file_content
|
.pac_file_content
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or(DEFAULT_PAC.into());
|
.unwrap_or(DEFAULT_PAC.into());
|
||||||
|
|
||||||
let mixed_port = verge_config
|
let pac_port = verge_config
|
||||||
.latest_ref()
|
.latest_ref()
|
||||||
.verge_mixed_port
|
.verge_mixed_port
|
||||||
.unwrap_or(clash_config.latest_ref().get_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 pac = warp::path!("commands" / "pac").map(move || {
|
||||||
let processed_content = pac_content.replace("%mixed-port%", &format!("{pac_port}"));
|
let processed_content = pac_content.replace("%mixed-port%", &format!("{pac_port}"));
|
||||||
warp::http::Response::builder()
|
warp::http::Response::builder()
|
||||||
@@ -110,9 +107,8 @@ pub fn embed_server() {
|
|||||||
let scheme = warp::path!("commands" / "scheme")
|
let scheme = warp::path!("commands" / "scheme")
|
||||||
.and(warp::query::<QueryParam>())
|
.and(warp::query::<QueryParam>())
|
||||||
.map(|query: QueryParam| {
|
.map(|query: QueryParam| {
|
||||||
// Spawn async work in a fire-and-forget manner
|
|
||||||
let param = query.param.clone();
|
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);
|
logging_error!(Type::Setup, resolve::resolve_scheme(param).await);
|
||||||
});
|
});
|
||||||
warp::reply::with_status::<String>("ok".into(), warp::http::StatusCode::OK)
|
warp::reply::with_status::<String>("ok".into(), warp::http::StatusCode::OK)
|
||||||
|
|||||||
Reference in New Issue
Block a user