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>>>, 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);
CloseHandle(process_handle);
result != 0
} }
let result = TerminateProcess(handle, 1) != 0;
CloseHandle(handle);
result
}) })
.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 { if !success {
// 短暂等待并验证进程是否真正终止 logging!(warn, Type::Core, "无法终止进程: {} (PID: {})", process_name, pid);
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; return false;
}
let still_running = self.is_process_running(pid).await.unwrap_or(false); tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
if still_running {
logging!( if self.is_process_running(pid).await.unwrap_or(false) {
warn, logging!(warn, Type::Core, "进程 {} (PID: {}) 终止命令成功但进程仍在运行", process_name, pid);
Type::Core,
"进程 {} (PID: {}) 终止命令成功但进程仍在运行",
process_name,
pid
);
false
} else {
logging!(
info,
Type::Core,
"成功终止进程: {} (PID: {})",
process_name,
pid
);
true
}
} else {
logging!(
warn,
Type::Core,
"无法终止进程: {} (PID: {})",
process_name,
pid
);
false false
} else {
logging!(info, Type::Core, "成功终止进程: {} (PID: {})", process_name, pid);
true
} }
} }
/// 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 result = GetExitCodeProcess(process_handle, &mut exit_code);
CloseHandle(process_handle);
if result == 0 {
return Ok(false);
}
Ok(exit_code == 259)
} }
let mut exit_code: DWORD = 0;
let result = GetExitCodeProcess(handle, &mut exit_code);
CloseHandle(handle);
Ok(result != 0 && 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")]

View File

@@ -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") { let _ = window.set_title("Clash Verge");
logging!(info, Type::Window, "设置macOS窗口标题");
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,197 +310,110 @@ 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| { std::process::exit(1);
logging!( });
error,
Type::Setup,
"Failed to build Tauri application: {}",
e
);
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);
}
tauri::WindowEvent::Focused(focused) => {
event_handlers::handle_window_focus(focused);
}
tauri::WindowEvent::Destroyed => {
event_handlers::handle_window_destroyed();
}
_ => {}
} }
tauri::WindowEvent::Focused(focused) => {
event_handlers::handle_window_focus(focused);
}
tauri::WindowEvent::Destroyed => {
event_handlers::handle_window_destroyed();
}
_ => {}
} }
} }
_ => {} _ => {}

View File

@@ -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;
} }

View File

@@ -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)