diff --git a/src-tauri/src/core/core.rs b/src-tauri/src/core/core.rs index 6b037f67..dd735642 100644 --- a/src-tauri/src/core/core.rs +++ b/src-tauri/src/core/core.rs @@ -184,23 +184,22 @@ impl CoreManager { async fn validate_config_internal(&self, config_path: &str) -> Result<(bool, String)> { // 检查程序是否正在退出,如果是则跳过验证 if handle::Handle::global().is_exiting() { - println!("[core配置验证] 应用正在退出,跳过验证"); + logging!(info, Type::Core, true, "应用正在退出,跳过验证"); return Ok((true, String::new())); } - println!("[core配置验证] 开始验证配置文件: {}", config_path); + logging!(info, Type::Core, true, "开始验证配置文件: {}", config_path); let clash_core = { Config::verge().latest().clash_core.clone() }; let clash_core = clash_core.unwrap_or("verge-mihomo".into()); - println!("[core配置验证] 使用内核: {}", clash_core); + logging!(info, Type::Core, true, "使用内核: {}", clash_core); let app_handle = handle::Handle::global().app_handle().unwrap(); let test_dir = dirs::app_home_dir()?.join("test"); let test_dir = dirs::path_to_str(&test_dir)?; - println!("[core配置验证] 测试目录: {}", test_dir); + logging!(info, Type::Core, true, "测试目录: {}", test_dir); // 使用子进程运行clash验证配置 - println!("[core配置验证] 运行子进程验证配置"); let output = app_handle .shell() .sidecar(clash_core)? @@ -216,18 +215,14 @@ impl CoreManager { let has_error = !output.status.success() || error_keywords.iter().any(|&kw| stderr.contains(kw)); - println!("\n[core配置验证] -------- 验证结果 --------"); - println!("[core配置验证] 进程退出状态: {:?}", output.status); + logging!(info, Type::Core, true, "-------- 验证结果 --------"); if !stderr.is_empty() { - println!("[core配置验证] stderr输出:\n{}", stderr); - } - if !stdout.is_empty() { - println!("[core配置验证] stdout输出:\n{}", stdout); + logging!(info, Type::Core, true, "stderr输出:\n{}", stderr); } if has_error { - println!("[core配置验证] 发现错误,开始处理错误信息"); + logging!(info, Type::Core, true, "发现错误,开始处理错误信息"); let error_msg = if !stdout.is_empty() { stdout.to_string() } else if !stderr.is_empty() { @@ -238,11 +233,11 @@ impl CoreManager { "验证进程被终止".to_string() }; - println!("[core配置验证] -------- 验证结束 --------\n"); + logging!(info, Type::Core, true, "-------- 验证结束 --------\n"); Ok((false, error_msg)) // 返回错误消息给调用者处理 } else { - println!("[core配置验证] 验证成功"); - println!("[core配置验证] -------- 验证结束 --------\n"); + logging!(info, Type::Core, true, "验证成功"); + logging!(info, Type::Core, true, "-------- 验证结束 --------\n"); Ok((true, String::new())) } } diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index ac2f8283..119c0f50 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -119,43 +119,56 @@ pub struct VersionJsonResponse { } #[cfg(target_os = "windows")] -pub async fn reinstall_service() -> Result<()> { - log::info!(target:"app", "reinstall service"); - - // 获取当前服务状态 - let mut service_state = ServiceState::get(); - - // 检查是否允许重装 - if !service_state.can_reinstall() { - log::warn!(target:"app", "service reinstall rejected: cooldown period or max attempts reached"); - bail!("Service reinstallation is rate limited. Please try again later."); - } +pub async fn uninstall_service() -> Result<()> { + logging!(info, Type::Service, true, "uninstall service"); use deelevate::{PrivilegeLevel, Token}; use runas::Command as RunasCommand; use std::os::windows::process::CommandExt; let binary_path = dirs::service_path()?; - let install_path = binary_path.with_file_name("install-service.exe"); let uninstall_path = binary_path.with_file_name("uninstall-service.exe"); - if !install_path.exists() { - bail!(format!("installer not found: {install_path:?}")); - } - if !uninstall_path.exists() { bail!(format!("uninstaller not found: {uninstall_path:?}")); } let token = Token::with_current_process()?; let level = token.privilege_level()?; - let _ = match level { + let status = match level { PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).show(false).status()?, _ => StdCommand::new(uninstall_path) .creation_flags(0x08000000) .status()?, }; + if !status.success() { + bail!( + "failed to uninstall service with status {}", + status.code().unwrap() + ); + } + + Ok(()) +} + +#[cfg(target_os = "windows")] +pub async fn install_service() -> Result<()> { + logging!(info, Type::Service, true, "install service"); + + use deelevate::{PrivilegeLevel, Token}; + use runas::Command as RunasCommand; + use std::os::windows::process::CommandExt; + + let binary_path = dirs::service_path()?; + let install_path = binary_path.with_file_name("install-service.exe"); + + if !install_path.exists() { + bail!(format!("installer not found: {install_path:?}")); + } + + let token = Token::with_current_process()?; + let level = token.privilege_level()?; let status = match level { PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).show(false).status()?, _ => StdCommand::new(install_path) @@ -164,41 +177,73 @@ pub async fn reinstall_service() -> Result<()> { }; if !status.success() { - let error = format!( + bail!( "failed to install service with status {}", status.code().unwrap() ); - service_state.last_error = Some(error.clone()); - service_state.save()?; - bail!(error); } - // 记录安装信息并保存 - service_state.record_install(); - service_state.last_error = None; - service_state.save()?; - Ok(()) } -#[cfg(target_os = "linux")] +#[cfg(target_os = "windows")] pub async fn reinstall_service() -> Result<()> { - log::info!(target:"app", "reinstall service"); + logging!(info, Type::Service, true, "reinstall service"); + + // 获取当前服务状态 + let mut service_state = ServiceState::get(); + + // 检查是否允许重装 + if !service_state.can_reinstall() { + logging!( + warn, + Type::Service, + true, + "service reinstall rejected: cooldown period or max attempts reached" + ); + bail!("Service reinstallation is rate limited. Please try again later."); + } + + // 先卸载服务 + if let Err(err) = uninstall_service().await { + logging!( + warn, + Type::Service, + true, + "failed to uninstall service: {}", + err + ); + } + + // 再安装服务 + match install_service().await { + Ok(_) => { + // 记录安装信息并保存 + service_state.record_install(); + service_state.last_error = None; + service_state.save()?; + Ok(()) + } + Err(err) => { + let error = format!("failed to install service: {}", err); + service_state.last_error = Some(error.clone()); + service_state.save()?; + bail!(error) + } + } +} + +#[cfg(target_os = "linux")] +pub async fn uninstall_service() -> Result<()> { + logging!(info, Type::Service, true, "uninstall service"); use users::get_effective_uid; - let install_path = tauri::utils::platform::current_exe()?.with_file_name("install-service"); - let uninstall_path = tauri::utils::platform::current_exe()?.with_file_name("uninstall-service"); - if !install_path.exists() { - bail!(format!("installer not found: {install_path:?}")); - } - if !uninstall_path.exists() { bail!(format!("uninstaller not found: {uninstall_path:?}")); } - let install_shell: String = install_path.to_string_lossy().replace(" ", "\\ "); let uninstall_shell: String = uninstall_path.to_string_lossy().replace(" ", "\\ "); let elevator = crate::utils::help::linux_elevator(); @@ -210,8 +255,38 @@ pub async fn reinstall_service() -> Result<()> { .arg(uninstall_shell) .status()?, }; - log::info!(target:"app", "status code:{}", status.code().unwrap()); + logging!( + info, + Type::Service, + true, + "uninstall status code:{}", + status.code().unwrap() + ); + if !status.success() { + bail!( + "failed to uninstall service with status {}", + status.code().unwrap() + ); + } + + Ok(()) +} + +#[cfg(target_os = "linux")] +pub async fn install_service() -> Result<()> { + logging!(info, Type::Service, true, "install service"); + use users::get_effective_uid; + + let install_path = tauri::utils::platform::current_exe()?.with_file_name("install-service"); + + if !install_path.exists() { + bail!(format!("installer not found: {install_path:?}")); + } + + let install_shell: String = install_path.to_string_lossy().replace(" ", "\\ "); + + let elevator = crate::utils::help::linux_elevator(); let status = match get_effective_uid() { 0 => StdCommand::new(install_shell).status()?, _ => StdCommand::new(elevator.clone()) @@ -220,6 +295,13 @@ pub async fn reinstall_service() -> Result<()> { .arg(install_shell) .status()?, }; + logging!( + info, + Type::Service, + true, + "install status code:{}", + status.code().unwrap() + ); if !status.success() { bail!( @@ -228,42 +310,113 @@ pub async fn reinstall_service() -> Result<()> { ); } - // 记录安装信息并保存 - let mut service_state = ServiceState::get(); - service_state.record_install(); - service_state.last_error = None; - service_state.save()?; - Ok(()) } -#[cfg(target_os = "macos")] +#[cfg(target_os = "linux")] pub async fn reinstall_service() -> Result<()> { + logging!(info, Type::Service, true, "reinstall service"); + + // 获取当前服务状态 + let mut service_state = ServiceState::get(); + + // 检查是否允许重装 + if !service_state.can_reinstall() { + logging!( + warn, + Type::Service, + true, + "service reinstall rejected: cooldown period or max attempts reached" + ); + bail!("Service reinstallation is rate limited. Please try again later."); + } + + // 先卸载服务 + if let Err(err) = uninstall_service().await { + logging!( + warn, + Type::Service, + true, + "failed to uninstall service: {}", + err + ); + } + + // 再安装服务 + match install_service().await { + Ok(_) => { + // 记录安装信息并保存 + service_state.record_install(); + service_state.last_error = None; + service_state.save()?; + Ok(()) + } + Err(err) => { + let error = format!("failed to install service: {}", err); + service_state.last_error = Some(error.clone()); + service_state.save()?; + bail!(error) + } + } +} + +#[cfg(target_os = "macos")] +pub async fn uninstall_service() -> Result<()> { use crate::utils::i18n::t; - log::info!(target:"app", "reinstall service"); + logging!(info, Type::Service, true, "uninstall service"); let binary_path = dirs::service_path()?; - let install_path = binary_path.with_file_name("install-service"); let uninstall_path = binary_path.with_file_name("uninstall-service"); - if !install_path.exists() { - bail!(format!("installer not found: {install_path:?}")); - } - if !uninstall_path.exists() { bail!(format!("uninstaller not found: {uninstall_path:?}")); } - let install_shell: String = install_path.to_string_lossy().into_owned(); let uninstall_shell: String = uninstall_path.to_string_lossy().into_owned(); let prompt = t("Service Administrator Prompt"); let command = format!( - r#"do shell script "sudo '{uninstall_shell}' && sudo '{install_shell}'" with administrator privileges with prompt "{prompt}""# + r#"do shell script "sudo '{uninstall_shell}'" with administrator privileges with prompt "{prompt}""# ); - log::debug!(target: "app", "command: {}", command); + logging!(debug, Type::Service, true, "uninstall command: {}", command); + + let status = StdCommand::new("osascript") + .args(vec!["-e", &command]) + .status()?; + + if !status.success() { + bail!( + "failed to uninstall service with status {}", + status.code().unwrap() + ); + } + + Ok(()) +} + +#[cfg(target_os = "macos")] +pub async fn install_service() -> Result<()> { + use crate::utils::i18n::t; + + logging!(info, Type::Service, true, "install service"); + + let binary_path = dirs::service_path()?; + let install_path = binary_path.with_file_name("install-service"); + + if !install_path.exists() { + bail!(format!("installer not found: {install_path:?}")); + } + + let install_shell: String = install_path.to_string_lossy().into_owned(); + + let prompt = t("Service Administrator Prompt"); + let command = format!( + r#"do shell script "sudo '{install_shell}'" with administrator privileges with prompt "{prompt}""# + ); + + logging!(debug, Type::Service, true, "install command: {}", command); let status = StdCommand::new("osascript") .args(vec!["-e", &command]) @@ -276,15 +429,56 @@ pub async fn reinstall_service() -> Result<()> { ); } - // 记录安装信息并保存 - let mut service_state = ServiceState::get(); - service_state.record_install(); - service_state.last_error = None; - service_state.save()?; - Ok(()) } +#[cfg(target_os = "macos")] +pub async fn reinstall_service() -> Result<()> { + logging!(info, Type::Service, true, "reinstall service"); + + // 获取当前服务状态 + let mut service_state = ServiceState::get(); + + // 检查是否允许重装 + if !service_state.can_reinstall() { + logging!( + warn, + Type::Service, + true, + "service reinstall rejected: cooldown period or max attempts reached" + ); + bail!("Service reinstallation is rate limited. Please try again later."); + } + + // 先卸载服务 + if let Err(err) = uninstall_service().await { + logging!( + warn, + Type::Service, + true, + "failed to uninstall service: {}", + err + ); + } + + // 再安装服务 + match install_service().await { + Ok(_) => { + // 记录安装信息并保存 + service_state.record_install(); + service_state.last_error = None; + service_state.save()?; + Ok(()) + } + Err(err) => { + let error = format!("failed to install service: {}", err); + service_state.last_error = Some(error.clone()); + service_state.save()?; + bail!(error) + } + } +} + /// check the windows service status pub async fn check_service() -> Result { let url = format!("{SERVICE_URL}/get_clash"); diff --git a/src-tauri/src/core/tray/mod.rs b/src-tauri/src/core/tray/mod.rs index ccce0468..d7ce7b7d 100644 --- a/src-tauri/src/core/tray/mod.rs +++ b/src-tauri/src/core/tray/mod.rs @@ -665,7 +665,6 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) { "restart_app" => feat::restart_app(), "entry_lightweight_mode" => entry_lightweight_mode(), "quit" => { - println!("quit"); feat::quit(Some(0)); } id if id.starts_with("profiles_") => { diff --git a/src-tauri/src/feat/proxy.rs b/src-tauri/src/feat/proxy.rs index dc1d28ea..cb7febb0 100644 --- a/src-tauri/src/feat/proxy.rs +++ b/src-tauri/src/feat/proxy.rs @@ -1,6 +1,12 @@ use crate::{ config::{Config, IVerge}, - core::handle, + core::{ + handle, + service::{install_service, is_service_available, reinstall_service}, + CoreManager, + }, + logging, logging_error, + utils::logging::Type, }; use std::env; use tauri_plugin_clipboard_manager::ClipboardExt; @@ -28,6 +34,19 @@ pub fn toggle_system_proxy() { /// Toggle TUN mode on/off pub fn toggle_tun_mode(not_save_file: Option) { + // tauri::async_runtime::spawn(async move { + // logging!( + // info, + // Type::Service, + // true, + // "Toggle TUN mode need install service" + // ); + // if is_service_available().await.is_err() { + // logging_error!(Type::Service, true, install_service().await); + // } + // logging_error!(Type::Core, true, CoreManager::global().restart_core().await); + // }); + let enable = Config::verge().data().enable_tun_mode; let enable = enable.unwrap_or(false); diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index 80edfaa5..a3149d6e 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -1,7 +1,12 @@ #[cfg(target_os = "macos")] use crate::AppHandleManager; use crate::{ - config::{Config, IVerge, PrfItem}, core::*, log_err, module::lightweight, utils::{error, init, server}, wrap_err + config::{Config, IVerge, PrfItem}, + core::*, + log_err, + module::lightweight, + utils::{error, init, server}, + wrap_err, }; use anyhow::{bail, Result}; use once_cell::sync::OnceCell; @@ -58,7 +63,7 @@ pub async fn resolve_setup(app: &mut App) { log_err!(Config::init_config().await); if service::check_service().await.is_err() { - match service::reinstall_service().await { + match service::install_service().await { Ok(_) => { log::info!(target:"app", "install service susccess."); #[cfg(not(target_os = "macos"))] diff --git a/src/components/setting/setting-system.tsx b/src/components/setting/setting-system.tsx index 3e0ce233..bdd47393 100644 --- a/src/components/setting/setting-system.tsx +++ b/src/components/setting/setting-system.tsx @@ -40,11 +40,18 @@ const SettingSystem = ({ onError }: Props) => { "getRunningMode", getRunningMode, ); - const { data: autoLaunchEnabled } = useSWR("getAutoLaunchStatus", getAutoLaunchStatus); + const { data: autoLaunchEnabled } = useSWR( + "getAutoLaunchStatus", + getAutoLaunchStatus, + ); // 当实际自启动状态与配置不同步时更新配置 useEffect(() => { - if (autoLaunchEnabled !== undefined && verge && verge.enable_auto_launch !== autoLaunchEnabled) { + if ( + autoLaunchEnabled !== undefined && + verge && + verge.enable_auto_launch !== autoLaunchEnabled + ) { // 静默更新配置,不触发UI刷新 mutateVerge({ ...verge, enable_auto_launch: autoLaunchEnabled }, false); }