refactor: replace println with logging in core validation and tray quit function

This commit is contained in:
Tunglies 2025-03-26 04:31:38 +08:00
parent 0ec4f46052
commit 98d3a48710
6 changed files with 298 additions and 79 deletions

View File

@ -184,23 +184,22 @@ impl CoreManager {
async fn validate_config_internal(&self, config_path: &str) -> Result<(bool, String)> { async fn validate_config_internal(&self, config_path: &str) -> Result<(bool, String)> {
// 检查程序是否正在退出,如果是则跳过验证 // 检查程序是否正在退出,如果是则跳过验证
if handle::Handle::global().is_exiting() { if handle::Handle::global().is_exiting() {
println!("[core配置验证] 应用正在退出,跳过验证"); logging!(info, Type::Core, true, "应用正在退出,跳过验证");
return Ok((true, String::new())); 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 = { Config::verge().latest().clash_core.clone() };
let clash_core = clash_core.unwrap_or("verge-mihomo".into()); 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 app_handle = handle::Handle::global().app_handle().unwrap();
let test_dir = dirs::app_home_dir()?.join("test"); let test_dir = dirs::app_home_dir()?.join("test");
let test_dir = dirs::path_to_str(&test_dir)?; let test_dir = dirs::path_to_str(&test_dir)?;
println!("[core配置验证] 测试目录: {}", test_dir); logging!(info, Type::Core, true, "测试目录: {}", test_dir);
// 使用子进程运行clash验证配置 // 使用子进程运行clash验证配置
println!("[core配置验证] 运行子进程验证配置");
let output = app_handle let output = app_handle
.shell() .shell()
.sidecar(clash_core)? .sidecar(clash_core)?
@ -216,18 +215,14 @@ impl CoreManager {
let has_error = let has_error =
!output.status.success() || error_keywords.iter().any(|&kw| stderr.contains(kw)); !output.status.success() || error_keywords.iter().any(|&kw| stderr.contains(kw));
println!("\n[core配置验证] -------- 验证结果 --------"); logging!(info, Type::Core, true, "-------- 验证结果 --------");
println!("[core配置验证] 进程退出状态: {:?}", output.status);
if !stderr.is_empty() { if !stderr.is_empty() {
println!("[core配置验证] stderr输出:\n{}", stderr); logging!(info, Type::Core, true, "stderr输出:\n{}", stderr);
}
if !stdout.is_empty() {
println!("[core配置验证] stdout输出:\n{}", stdout);
} }
if has_error { if has_error {
println!("[core配置验证] 发现错误,开始处理错误信息"); logging!(info, Type::Core, true, "发现错误,开始处理错误信息");
let error_msg = if !stdout.is_empty() { let error_msg = if !stdout.is_empty() {
stdout.to_string() stdout.to_string()
} else if !stderr.is_empty() { } else if !stderr.is_empty() {
@ -238,11 +233,11 @@ impl CoreManager {
"验证进程被终止".to_string() "验证进程被终止".to_string()
}; };
println!("[core配置验证] -------- 验证结束 --------\n"); logging!(info, Type::Core, true, "-------- 验证结束 --------\n");
Ok((false, error_msg)) // 返回错误消息给调用者处理 Ok((false, error_msg)) // 返回错误消息给调用者处理
} else { } else {
println!("[core配置验证] 验证成功"); logging!(info, Type::Core, true, "验证成功");
println!("[core配置验证] -------- 验证结束 --------\n"); logging!(info, Type::Core, true, "-------- 验证结束 --------\n");
Ok((true, String::new())) Ok((true, String::new()))
} }
} }

View File

@ -119,43 +119,56 @@ pub struct VersionJsonResponse {
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub async fn reinstall_service() -> Result<()> { pub async fn uninstall_service() -> Result<()> {
log::info!(target:"app", "reinstall service"); logging!(info, Type::Service, true, "uninstall 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.");
}
use deelevate::{PrivilegeLevel, Token}; use deelevate::{PrivilegeLevel, Token};
use runas::Command as RunasCommand; use runas::Command as RunasCommand;
use std::os::windows::process::CommandExt; use std::os::windows::process::CommandExt;
let binary_path = dirs::service_path()?; 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"); 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() { if !uninstall_path.exists() {
bail!(format!("uninstaller not found: {uninstall_path:?}")); bail!(format!("uninstaller not found: {uninstall_path:?}"));
} }
let token = Token::with_current_process()?; let token = Token::with_current_process()?;
let level = token.privilege_level()?; let level = token.privilege_level()?;
let _ = match level { let status = match level {
PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).show(false).status()?, PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).show(false).status()?,
_ => StdCommand::new(uninstall_path) _ => StdCommand::new(uninstall_path)
.creation_flags(0x08000000) .creation_flags(0x08000000)
.status()?, .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 { let status = match level {
PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).show(false).status()?, PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).show(false).status()?,
_ => StdCommand::new(install_path) _ => StdCommand::new(install_path)
@ -164,41 +177,73 @@ pub async fn reinstall_service() -> Result<()> {
}; };
if !status.success() { if !status.success() {
let error = format!( bail!(
"failed to install service with status {}", "failed to install service with status {}",
status.code().unwrap() 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(()) Ok(())
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "windows")]
pub async fn reinstall_service() -> Result<()> { 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; 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"); 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() { if !uninstall_path.exists() {
bail!(format!("uninstaller not found: {uninstall_path:?}")); 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 uninstall_shell: String = uninstall_path.to_string_lossy().replace(" ", "\\ ");
let elevator = crate::utils::help::linux_elevator(); let elevator = crate::utils::help::linux_elevator();
@ -210,8 +255,38 @@ pub async fn reinstall_service() -> Result<()> {
.arg(uninstall_shell) .arg(uninstall_shell)
.status()?, .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() { let status = match get_effective_uid() {
0 => StdCommand::new(install_shell).status()?, 0 => StdCommand::new(install_shell).status()?,
_ => StdCommand::new(elevator.clone()) _ => StdCommand::new(elevator.clone())
@ -220,6 +295,13 @@ pub async fn reinstall_service() -> Result<()> {
.arg(install_shell) .arg(install_shell)
.status()?, .status()?,
}; };
logging!(
info,
Type::Service,
true,
"install status code:{}",
status.code().unwrap()
);
if !status.success() { if !status.success() {
bail!( 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(()) Ok(())
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "linux")]
pub async fn reinstall_service() -> Result<()> { 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; 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 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"); 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() { if !uninstall_path.exists() {
bail!(format!("uninstaller not found: {uninstall_path:?}")); 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 uninstall_shell: String = uninstall_path.to_string_lossy().into_owned();
let prompt = t("Service Administrator Prompt"); let prompt = t("Service Administrator Prompt");
let command = format!( 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") let status = StdCommand::new("osascript")
.args(vec!["-e", &command]) .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(()) 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 /// check the windows service status
pub async fn check_service() -> Result<JsonResponse> { pub async fn check_service() -> Result<JsonResponse> {
let url = format!("{SERVICE_URL}/get_clash"); let url = format!("{SERVICE_URL}/get_clash");

View File

@ -665,7 +665,6 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
"restart_app" => feat::restart_app(), "restart_app" => feat::restart_app(),
"entry_lightweight_mode" => entry_lightweight_mode(), "entry_lightweight_mode" => entry_lightweight_mode(),
"quit" => { "quit" => {
println!("quit");
feat::quit(Some(0)); feat::quit(Some(0));
} }
id if id.starts_with("profiles_") => { id if id.starts_with("profiles_") => {

View File

@ -1,6 +1,12 @@
use crate::{ use crate::{
config::{Config, IVerge}, 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 std::env;
use tauri_plugin_clipboard_manager::ClipboardExt; use tauri_plugin_clipboard_manager::ClipboardExt;
@ -28,6 +34,19 @@ pub fn toggle_system_proxy() {
/// Toggle TUN mode on/off /// Toggle TUN mode on/off
pub fn toggle_tun_mode(not_save_file: Option<bool>) { pub fn toggle_tun_mode(not_save_file: Option<bool>) {
// 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 = Config::verge().data().enable_tun_mode;
let enable = enable.unwrap_or(false); let enable = enable.unwrap_or(false);

View File

@ -1,7 +1,12 @@
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use crate::AppHandleManager; use crate::AppHandleManager;
use crate::{ 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 anyhow::{bail, Result};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@ -58,7 +63,7 @@ pub async fn resolve_setup(app: &mut App) {
log_err!(Config::init_config().await); log_err!(Config::init_config().await);
if service::check_service().await.is_err() { if service::check_service().await.is_err() {
match service::reinstall_service().await { match service::install_service().await {
Ok(_) => { Ok(_) => {
log::info!(target:"app", "install service susccess."); log::info!(target:"app", "install service susccess.");
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]

View File

@ -40,11 +40,18 @@ const SettingSystem = ({ onError }: Props) => {
"getRunningMode", "getRunningMode",
getRunningMode, getRunningMode,
); );
const { data: autoLaunchEnabled } = useSWR("getAutoLaunchStatus", getAutoLaunchStatus); const { data: autoLaunchEnabled } = useSWR(
"getAutoLaunchStatus",
getAutoLaunchStatus,
);
// 当实际自启动状态与配置不同步时更新配置 // 当实际自启动状态与配置不同步时更新配置
useEffect(() => { useEffect(() => {
if (autoLaunchEnabled !== undefined && verge && verge.enable_auto_launch !== autoLaunchEnabled) { if (
autoLaunchEnabled !== undefined &&
verge &&
verge.enable_auto_launch !== autoLaunchEnabled
) {
// 静默更新配置不触发UI刷新 // 静默更新配置不触发UI刷新
mutateVerge({ ...verge, enable_auto_launch: autoLaunchEnabled }, false); mutateVerge({ ...verge, enable_auto_launch: autoLaunchEnabled }, false);
} }