diff --git a/src-tauri/src/cmd/app.rs b/src-tauri/src/cmd/app.rs new file mode 100644 index 00000000..f9e447b2 --- /dev/null +++ b/src-tauri/src/cmd/app.rs @@ -0,0 +1,122 @@ +use crate::{ + utils::dirs, + feat, + wrap_err, +}; +use super::CmdResult; +use tauri::Manager; + +/// 打开应用程序所在目录 +#[tauri::command] +pub fn open_app_dir() -> CmdResult<()> { + let app_dir = wrap_err!(dirs::app_home_dir())?; + wrap_err!(open::that(app_dir)) +} + +/// 打开核心所在目录 +#[tauri::command] +pub fn open_core_dir() -> CmdResult<()> { + let core_dir = wrap_err!(tauri::utils::platform::current_exe())?; + let core_dir = core_dir.parent().ok_or("failed to get core dir")?; + wrap_err!(open::that(core_dir)) +} + +/// 打开日志目录 +#[tauri::command] +pub fn open_logs_dir() -> CmdResult<()> { + let log_dir = wrap_err!(dirs::app_logs_dir())?; + wrap_err!(open::that(log_dir)) +} + +/// 打开网页链接 +#[tauri::command] +pub fn open_web_url(url: String) -> CmdResult<()> { + wrap_err!(open::that(url)) +} + +/// 打开/关闭开发者工具 +#[tauri::command] +pub fn open_devtools(app_handle: tauri::AppHandle) { + if let Some(window) = app_handle.get_webview_window("main") { + if !window.is_devtools_open() { + window.open_devtools(); + } else { + window.close_devtools(); + } + } +} + +/// 退出应用 +#[tauri::command] +pub fn exit_app() { + feat::quit(Some(0)); +} + +/// 重启应用 +#[tauri::command] +pub async fn restart_app() -> CmdResult<()> { + feat::restart_app(); + Ok(()) +} + +/// 获取便携版标识 +#[tauri::command] +pub fn get_portable_flag() -> CmdResult { + Ok(*dirs::PORTABLE_FLAG.get().unwrap_or(&false)) +} + +/// 获取应用目录 +#[tauri::command] +pub fn get_app_dir() -> CmdResult { + let app_home_dir = wrap_err!(dirs::app_home_dir())? + .to_string_lossy() + .to_string(); + Ok(app_home_dir) +} + +/// 下载图标缓存 +#[tauri::command] +pub async fn download_icon_cache(url: String, name: String) -> CmdResult { + let icon_cache_dir = wrap_err!(dirs::app_home_dir())?.join("icons").join("cache"); + let icon_path = icon_cache_dir.join(name); + if !icon_cache_dir.exists() { + let _ = std::fs::create_dir_all(&icon_cache_dir); + } + if !icon_path.exists() { + let response = wrap_err!(reqwest::get(url).await)?; + + let mut file = wrap_err!(std::fs::File::create(&icon_path))?; + + let content = wrap_err!(response.bytes().await)?; + wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?; + } + Ok(icon_path.to_string_lossy().to_string()) +} + +/// 复制图标文件 +#[tauri::command] +pub fn copy_icon_file(path: String, name: String) -> CmdResult { + let file_path = std::path::Path::new(&path); + let icon_dir = wrap_err!(dirs::app_home_dir())?.join("icons"); + if !icon_dir.exists() { + let _ = std::fs::create_dir_all(&icon_dir); + } + let ext = match file_path.extension() { + Some(e) => e.to_string_lossy().to_string(), + None => "ico".to_string(), + }; + + let png_dest_path = icon_dir.join(format!("{name}.png")); + let ico_dest_path = icon_dir.join(format!("{name}.ico")); + let dest_path = icon_dir.join(format!("{name}.{ext}")); + if file_path.exists() { + std::fs::remove_file(png_dest_path).unwrap_or_default(); + std::fs::remove_file(ico_dest_path).unwrap_or_default(); + match std::fs::copy(file_path, &dest_path) { + Ok(_) => Ok(dest_path.to_string_lossy().to_string()), + Err(err) => Err(err.to_string()), + } + } else { + Err("file not found".to_string()) + } +} diff --git a/src-tauri/src/cmd/clash.rs b/src-tauri/src/cmd/clash.rs new file mode 100644 index 00000000..e57826f6 --- /dev/null +++ b/src-tauri/src/cmd/clash.rs @@ -0,0 +1,79 @@ +use crate::{ + config::*, + core::*, + feat, + wrap_err, +}; +use super::CmdResult; +use serde_yaml::Mapping; + +/// 复制Clash环境变量 +#[tauri::command] +pub fn copy_clash_env() -> CmdResult { + feat::copy_clash_env(); + Ok(()) +} + +/// 获取Clash信息 +#[tauri::command] +pub fn get_clash_info() -> CmdResult { + Ok(Config::clash().latest().get_client_info()) +} + +/// 修改Clash配置 +#[tauri::command] +pub async fn patch_clash_config(payload: Mapping) -> CmdResult { + wrap_err!(feat::patch_clash(payload).await) +} + +/// 修改Clash模式 +#[tauri::command] +pub async fn patch_clash_mode(payload: String) -> CmdResult { + Ok(feat::change_clash_mode(payload)) +} + +/// 切换Clash核心 +#[tauri::command] +pub async fn change_clash_core(clash_core: String) -> CmdResult> { + log::info!(target: "app", "changing core to {clash_core}"); + + match CoreManager::global().change_core(Some(clash_core.clone())).await { + Ok(_) => { + log::info!(target: "app", "core changed to {clash_core}"); + handle::Handle::notice_message("config_core::change_success", &clash_core); + handle::Handle::refresh_clash(); + Ok(None) + } + Err(err) => { + let error_msg = err.to_string(); + log::error!(target: "app", "failed to change core: {error_msg}"); + handle::Handle::notice_message("config_core::change_error", &error_msg); + Ok(Some(error_msg)) + } + } +} + +/// 重启核心 +#[tauri::command] +pub async fn restart_core() -> CmdResult { + wrap_err!(CoreManager::global().restart_core().await) +} + +/// 获取代理延迟 +#[tauri::command] +pub async fn clash_api_get_proxy_delay( + name: String, + url: Option, + timeout: i32, +) -> CmdResult { + match clash_api::get_proxy_delay(name, url, timeout).await { + Ok(res) => Ok(res), + Err(err) => Err(err.to_string()), + } +} + +/// 测试URL延迟 +#[tauri::command] +pub async fn test_delay(url: String) -> CmdResult { + Ok(feat::test_delay(url).await.unwrap_or(10000u32)) +} diff --git a/src-tauri/src/cmd/mod.rs b/src-tauri/src/cmd/mod.rs new file mode 100644 index 00000000..e0c2b91d --- /dev/null +++ b/src-tauri/src/cmd/mod.rs @@ -0,0 +1,28 @@ +use anyhow::Result; + +// Common result type used by command functions +pub type CmdResult = Result; + +// Command modules +pub mod profile; +pub mod validate; +pub mod uwp; +pub mod webdav; +pub mod app; +pub mod network; +pub mod clash; +pub mod verge; +pub mod runtime; +pub mod save_profile; + +// Re-export all command functions for backwards compatibility +pub use profile::*; +pub use validate::*; +pub use uwp::*; +pub use webdav::*; +pub use app::*; +pub use network::*; +pub use clash::*; +pub use verge::*; +pub use runtime::*; +pub use save_profile::*; \ No newline at end of file diff --git a/src-tauri/src/cmd/network.rs b/src-tauri/src/cmd/network.rs new file mode 100644 index 00000000..d1db0cec --- /dev/null +++ b/src-tauri/src/cmd/network.rs @@ -0,0 +1,64 @@ +use crate::wrap_err; +use super::CmdResult; +use sysproxy::{Autoproxy, Sysproxy}; +use serde_yaml::Mapping; +use network_interface::NetworkInterface; + +/// get the system proxy +#[tauri::command] +pub fn get_sys_proxy() -> CmdResult { + let current = wrap_err!(Sysproxy::get_system_proxy())?; + let mut map = Mapping::new(); + map.insert("enable".into(), current.enable.into()); + map.insert( + "server".into(), + format!("{}:{}", current.host, current.port).into(), + ); + map.insert("bypass".into(), current.bypass.into()); + + Ok(map) +} + +/// get the system proxy +#[tauri::command] +pub fn get_auto_proxy() -> CmdResult { + let current = wrap_err!(Autoproxy::get_auto_proxy())?; + + let mut map = Mapping::new(); + map.insert("enable".into(), current.enable.into()); + map.insert("url".into(), current.url.into()); + + Ok(map) +} + +/// 获取网络接口列表 +#[tauri::command] +pub fn get_network_interfaces() -> Vec { + use sysinfo::Networks; + let mut result = Vec::new(); + let networks = Networks::new_with_refreshed_list(); + for (interface_name, _) in &networks { + result.push(interface_name.clone()); + } + result +} + +/// 获取网络接口详细信息 +#[tauri::command] +pub fn get_network_interfaces_info() -> CmdResult> { + use network_interface::NetworkInterface; + use network_interface::NetworkInterfaceConfig; + + let names = get_network_interfaces(); + let interfaces = wrap_err!(NetworkInterface::show())?; + + let mut result = Vec::new(); + + for interface in interfaces { + if names.contains(&interface.name) { + result.push(interface); + } + } + + Ok(result) +} diff --git a/src-tauri/src/cmd/profile.rs b/src-tauri/src/cmd/profile.rs new file mode 100644 index 00000000..00622d56 --- /dev/null +++ b/src-tauri/src/cmd/profile.rs @@ -0,0 +1,176 @@ +use crate::{ + config::*, + core::*, + feat, + utils::{dirs, help}, + log_err, ret_err, wrap_err, +}; +use super::CmdResult; + +/// 获取配置文件列表 +#[tauri::command] +pub fn get_profiles() -> CmdResult { + let _ = tray::Tray::global().update_menu(); + Ok(Config::profiles().data().clone()) +} + +/// 增强配置文件 +#[tauri::command] +pub async fn enhance_profiles() -> CmdResult { + match CoreManager::global().update_config().await { + Ok((true, _)) => { + println!("[enhance_profiles] 配置更新成功"); + log_err!(tray::Tray::global().update_tooltip()); + handle::Handle::refresh_clash(); + Ok(()) + } + Ok((false, error_msg)) => { + println!("[enhance_profiles] 配置验证失败: {}", error_msg); + handle::Handle::notice_message("config_validate::error", &error_msg); + Ok(()) + } + Err(e) => { + println!("[enhance_profiles] 更新过程发生错误: {}", e); + handle::Handle::notice_message("config_validate::process_terminated", &e.to_string()); + Ok(()) + } + } +} + +/// 导入配置文件 +#[tauri::command] +pub async fn import_profile(url: String, option: Option) -> CmdResult { + let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?; + wrap_err!(Config::profiles().data().append_item(item)) +} + +/// 重新排序配置文件 +#[tauri::command] +pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult { + wrap_err!(Config::profiles().data().reorder(active_id, over_id)) +} + +/// 创建配置文件 +#[tauri::command] +pub async fn create_profile(item: PrfItem, file_data: Option) -> CmdResult { + let item = wrap_err!(PrfItem::from(item, file_data).await)?; + wrap_err!(Config::profiles().data().append_item(item)) +} + +/// 更新配置文件 +#[tauri::command] +pub async fn update_profile(index: String, option: Option) -> CmdResult { + wrap_err!(feat::update_profile(index, option).await) +} + +/// 删除配置文件 +#[tauri::command] +pub async fn delete_profile(index: String) -> CmdResult { + let should_update = wrap_err!({ Config::profiles().data().delete_item(index) })?; + if should_update { + wrap_err!(CoreManager::global().update_config().await)?; + handle::Handle::refresh_clash(); + } + Ok(()) +} + +/// 修改profiles的配置 +#[tauri::command] +pub async fn patch_profiles_config( + profiles: IProfiles +) -> CmdResult { + println!("[cmd配置patch] 开始修改配置文件"); + + // 保存当前配置,以便在验证失败时恢复 + let current_profile = Config::profiles().latest().current.clone(); + println!("[cmd配置patch] 当前配置: {:?}", current_profile); + + // 更新profiles配置 + println!("[cmd配置patch] 正在更新配置草稿"); + wrap_err!({ Config::profiles().draft().patch_config(profiles) })?; + + // 更新配置并进行验证 + match CoreManager::global().update_config().await { + Ok((true, _)) => { + println!("[cmd配置patch] 配置更新成功"); + handle::Handle::refresh_clash(); + let _ = tray::Tray::global().update_tooltip(); + Config::profiles().apply(); + wrap_err!(Config::profiles().data().save_file())?; + Ok(true) + } + Ok((false, error_msg)) => { + println!("[cmd配置patch] 配置验证失败: {}", error_msg); + Config::profiles().discard(); + + // 如果验证失败,恢复到之前的配置 + if let Some(prev_profile) = current_profile { + println!("[cmd配置patch] 尝试恢复到之前的配置: {}", prev_profile); + let restore_profiles = IProfiles { + current: Some(prev_profile), + items: None, + }; + // 静默恢复,不触发验证 + wrap_err!({ Config::profiles().draft().patch_config(restore_profiles) })?; + Config::profiles().apply(); + wrap_err!(Config::profiles().data().save_file())?; + println!("[cmd配置patch] 成功恢复到之前的配置"); + } + + // 发送验证错误通知 + handle::Handle::notice_message("config_validate::error", &error_msg); + Ok(false) + } + Err(e) => { + println!("[cmd配置patch] 更新过程发生错误: {}", e); + Config::profiles().discard(); + handle::Handle::notice_message("config_validate::boot_error", &e.to_string()); + Ok(false) + } + } +} + +/// 根据profile name修改profiles +#[tauri::command] +pub async fn patch_profiles_config_by_profile_index( + _app_handle: tauri::AppHandle, + profile_index: String +) -> CmdResult { + let profiles = IProfiles{current: Some(profile_index), items: None}; + patch_profiles_config(profiles).await +} + +/// 修改某个profile item的 +#[tauri::command] +pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult { + wrap_err!(Config::profiles().data().patch_item(index, profile))?; + Ok(()) +} + +/// 查看配置文件 +#[tauri::command] +pub fn view_profile(app_handle: tauri::AppHandle, index: String) -> CmdResult { + let file = { + wrap_err!(Config::profiles().latest().get_item(&index))? + .file + .clone() + .ok_or("the file field is null") + }?; + + let path = wrap_err!(dirs::app_profiles_dir())?.join(file); + if !path.exists() { + ret_err!("the file not found"); + } + + wrap_err!(help::open_file(app_handle, path)) +} + +/// 读取配置文件内容 +#[tauri::command] +pub fn read_profile_file(index: String) -> CmdResult { + let profiles = Config::profiles(); + let profiles = profiles.latest(); + let item = wrap_err!(profiles.get_item(&index))?; + let data = wrap_err!(item.read_file())?; + Ok(data) +} diff --git a/src-tauri/src/cmd/runtime.rs b/src-tauri/src/cmd/runtime.rs new file mode 100644 index 00000000..34e21c47 --- /dev/null +++ b/src-tauri/src/cmd/runtime.rs @@ -0,0 +1,39 @@ +use crate::{ + config::*, + wrap_err, +}; +use super::CmdResult; +use anyhow::Context; +use std::collections::HashMap; +use serde_yaml::Mapping; + +/// 获取运行时配置 +#[tauri::command] +pub fn get_runtime_config() -> CmdResult> { + Ok(Config::runtime().latest().config.clone()) +} + +/// 获取运行时YAML配置 +#[tauri::command] +pub fn get_runtime_yaml() -> CmdResult { + let runtime = Config::runtime(); + let runtime = runtime.latest(); + let config = runtime.config.as_ref(); + wrap_err!(config + .ok_or(anyhow::anyhow!("failed to parse config to yaml file")) + .and_then( + |config| serde_yaml::to_string(config).context("failed to convert config to yaml") + )) +} + +/// 获取运行时存在的键 +#[tauri::command] +pub fn get_runtime_exists() -> CmdResult> { + Ok(Config::runtime().latest().exists_keys.clone()) +} + +/// 获取运行时日志 +#[tauri::command] +pub fn get_runtime_logs() -> CmdResult>> { + Ok(Config::runtime().latest().chain_logs.clone()) +} diff --git a/src-tauri/src/cmd/save_profile.rs b/src-tauri/src/cmd/save_profile.rs new file mode 100644 index 00000000..abece254 --- /dev/null +++ b/src-tauri/src/cmd/save_profile.rs @@ -0,0 +1,111 @@ +use crate::{ + config::*, + core::*, + utils::dirs, + wrap_err, +}; +use super::CmdResult; +use std::fs; + +/// 保存profiles的配置 +#[tauri::command] +pub async fn save_profile_file(index: String, file_data: Option) -> CmdResult { + if file_data.is_none() { + return Ok(()); + } + + // 在异步操作前完成所有文件操作 + let (file_path, original_content, is_merge_file) = { + let profiles = Config::profiles(); + let profiles_guard = profiles.latest(); + let item = wrap_err!(profiles_guard.get_item(&index))?; + // 确定是否为merge类型文件 + let is_merge = item.itype.as_ref().map_or(false, |t| t == "merge"); + let content = wrap_err!(item.read_file())?; + let path = item.file.clone().ok_or("file field is null")?; + let profiles_dir = wrap_err!(dirs::app_profiles_dir())?; + (profiles_dir.join(path), content, is_merge) + }; + + // 保存新的配置文件 + wrap_err!(fs::write(&file_path, file_data.clone().unwrap()))?; + + let file_path_str = file_path.to_string_lossy().to_string(); + println!("[cmd配置save] 开始验证配置文件: {}, 是否为merge文件: {}", file_path_str, is_merge_file); + + // 对于 merge 文件,只进行语法验证,不进行后续内核验证 + if is_merge_file { + println!("[cmd配置save] 检测到merge文件,只进行语法验证"); + match CoreManager::global().validate_config_file(&file_path_str, Some(true)).await { + Ok((true, _)) => { + println!("[cmd配置save] merge文件语法验证通过"); + // 成功后尝试更新整体配置 + if let Err(e) = CoreManager::global().update_config().await { + println!("[cmd配置save] 更新整体配置时发生错误: {}", e); + log::warn!(target: "app", "更新整体配置时发生错误: {}", e); + } + return Ok(()); + } + Ok((false, error_msg)) => { + println!("[cmd配置save] merge文件语法验证失败: {}", error_msg); + // 恢复原始配置文件 + wrap_err!(fs::write(&file_path, original_content))?; + // 发送合并文件专用错误通知 + let result = (false, error_msg.clone()); + crate::cmd::validate::handle_yaml_validation_notice(&result, "合并配置文件"); + return Ok(()); + } + Err(e) => { + println!("[cmd配置save] 验证过程发生错误: {}", e); + // 恢复原始配置文件 + wrap_err!(fs::write(&file_path, original_content))?; + return Err(e.to_string()); + } + } + } + + // 非merge文件使用完整验证流程 + match CoreManager::global().validate_config_file(&file_path_str, None).await { + Ok((true, _)) => { + println!("[cmd配置save] 验证成功"); + Ok(()) + } + Ok((false, error_msg)) => { + println!("[cmd配置save] 验证失败: {}", error_msg); + // 恢复原始配置文件 + wrap_err!(fs::write(&file_path, original_content))?; + + // 智能判断错误类型 + let is_script_error = file_path_str.ends_with(".js") || + error_msg.contains("Script syntax error") || + error_msg.contains("Script must contain a main function") || + error_msg.contains("Failed to read script file"); + + if error_msg.contains("YAML syntax error") || + error_msg.contains("Failed to read file:") || + (!file_path_str.ends_with(".js") && !is_script_error) { + // 普通YAML错误使用YAML通知处理 + println!("[cmd配置save] YAML配置文件验证失败,发送通知"); + let result = (false, error_msg.clone()); + crate::cmd::validate::handle_yaml_validation_notice(&result, "YAML配置文件"); + } else if is_script_error { + // 脚本错误使用专门的通知处理 + println!("[cmd配置save] 脚本文件验证失败,发送通知"); + let result = (false, error_msg.clone()); + crate::cmd::validate::handle_script_validation_notice(&result, "脚本文件"); + } else { + // 普通配置错误使用一般通知 + println!("[cmd配置save] 其他类型验证失败,发送一般通知"); + handle::Handle::notice_message("config_validate::error", &error_msg); + } + + Ok(()) + } + Err(e) => { + println!("[cmd配置save] 验证过程发生错误: {}", e); + // 恢复原始配置文件 + wrap_err!(fs::write(&file_path, original_content))?; + Err(e.to_string()) + } + } +} diff --git a/src-tauri/src/cmd/uwp.rs b/src-tauri/src/cmd/uwp.rs new file mode 100644 index 00000000..67db3055 --- /dev/null +++ b/src-tauri/src/cmd/uwp.rs @@ -0,0 +1,29 @@ +use super::CmdResult; + +/// Platform-specific implementation for UWP functionality +#[cfg(windows)] +mod platform { + use super::CmdResult; + use crate::core::win_uwp; + use crate::wrap_err; + + pub async fn invoke_uwp_tool() -> CmdResult { + wrap_err!(win_uwp::invoke_uwptools().await) + } +} + +/// Stub implementation for non-Windows platforms +#[cfg(not(windows))] +mod platform { + use super::CmdResult; + + pub async fn invoke_uwp_tool() -> CmdResult { + Ok(()) + } +} + +/// Command exposed to Tauri +#[tauri::command] +pub async fn invoke_uwp_tool() -> CmdResult { + platform::invoke_uwp_tool().await +} diff --git a/src-tauri/src/cmd/validate.rs b/src-tauri/src/cmd/validate.rs new file mode 100644 index 00000000..017dc1a2 --- /dev/null +++ b/src-tauri/src/cmd/validate.rs @@ -0,0 +1,101 @@ +use crate::core::*; +use super::CmdResult; + +/// 发送脚本验证通知消息 +#[tauri::command] +pub async fn script_validate_notice(status: String, msg: String) -> CmdResult { + handle::Handle::notice_message(&status, &msg); + Ok(()) +} + +/// 处理脚本验证相关的所有消息通知 +/// 统一通知接口,保持消息类型一致性 +pub fn handle_script_validation_notice(result: &(bool, String), file_type: &str) { + if !result.0 { + let error_msg = &result.1; + + // 根据错误消息内容判断错误类型 + let status = if error_msg.starts_with("File not found:") { + "config_validate::file_not_found" + } else if error_msg.starts_with("Failed to read script file:") { + "config_validate::script_error" + } else if error_msg.starts_with("Script syntax error:") { + "config_validate::script_syntax_error" + } else if error_msg == "Script must contain a main function" { + "config_validate::script_missing_main" + } else { + // 如果是其他类型错误,作为一般脚本错误处理 + "config_validate::script_error" + }; + + log::warn!(target: "app", "{} 验证失败: {}", file_type, error_msg); + handle::Handle::notice_message(status, error_msg); + } +} + +/// 验证指定脚本文件 +#[tauri::command] +pub async fn validate_script_file(file_path: String) -> CmdResult { + log::info!(target: "app", "验证脚本文件: {}", file_path); + + match CoreManager::global().validate_config_file(&file_path, None).await { + Ok(result) => { + handle_script_validation_notice(&result, "脚本文件"); + Ok(result.0) // 返回验证结果布尔值 + }, + Err(e) => { + let error_msg = e.to_string(); + log::error!(target: "app", "验证脚本文件过程发生错误: {}", error_msg); + handle::Handle::notice_message("config_validate::process_terminated", &error_msg); + Ok(false) + } + } +} + +/// 处理YAML验证相关的所有消息通知 +/// 统一通知接口,保持消息类型一致性 +pub fn handle_yaml_validation_notice(result: &(bool, String), file_type: &str) { + if !result.0 { + let error_msg = &result.1; + println!("[通知] 处理{}验证错误: {}", file_type, error_msg); + + // 检查是否为merge文件 + let is_merge_file = file_type.contains("合并"); + + // 根据错误消息内容判断错误类型 + let status = if error_msg.starts_with("File not found:") { + "config_validate::file_not_found" + } else if error_msg.starts_with("Failed to read file:") { + "config_validate::yaml_read_error" + } else if error_msg.starts_with("YAML syntax error:") { + if is_merge_file { + "config_validate::merge_syntax_error" + } else { + "config_validate::yaml_syntax_error" + } + } else if error_msg.contains("mapping values are not allowed") { + if is_merge_file { + "config_validate::merge_mapping_error" + } else { + "config_validate::yaml_mapping_error" + } + } else if error_msg.contains("did not find expected key") { + if is_merge_file { + "config_validate::merge_key_error" + } else { + "config_validate::yaml_key_error" + } + } else { + // 如果是其他类型错误,根据文件类型作为一般错误处理 + if is_merge_file { + "config_validate::merge_error" + } else { + "config_validate::yaml_error" + } + }; + + log::warn!(target: "app", "{} 验证失败: {}", file_type, error_msg); + println!("[通知] 发送通知: status={}, msg={}", status, error_msg); + handle::Handle::notice_message(status, error_msg); + } +} diff --git a/src-tauri/src/cmd/verge.rs b/src-tauri/src/cmd/verge.rs new file mode 100644 index 00000000..d80e5e3d --- /dev/null +++ b/src-tauri/src/cmd/verge.rs @@ -0,0 +1,20 @@ +use crate::{ + config::*, + feat, + wrap_err, +}; +use super::CmdResult; + +/// 获取Verge配置 +#[tauri::command] +pub fn get_verge_config() -> CmdResult { + let verge = Config::verge(); + let verge_data = verge.data().clone(); + Ok(IVergeResponse::from(verge_data)) +} + +/// 修改Verge配置 +#[tauri::command] +pub async fn patch_verge_config(payload: IVerge) -> CmdResult { + wrap_err!(feat::patch_verge(payload, false).await) +} diff --git a/src-tauri/src/cmd/webdav.rs b/src-tauri/src/cmd/webdav.rs new file mode 100644 index 00000000..ddd39a50 --- /dev/null +++ b/src-tauri/src/cmd/webdav.rs @@ -0,0 +1,51 @@ +use crate::{ + core, + config::*, + feat, + wrap_err, +}; +use super::CmdResult; +use reqwest_dav::list_cmd::ListFile; + +/// 保存 WebDAV 配置 +#[tauri::command] +pub async fn save_webdav_config(url: String, username: String, password: String) -> CmdResult<()> { + let patch = IVerge { + webdav_url: Some(url), + webdav_username: Some(username), + webdav_password: Some(password), + ..IVerge::default() + }; + Config::verge().draft().patch_config(patch.clone()); + Config::verge().apply(); + Config::verge() + .data() + .save_file() + .map_err(|err| err.to_string())?; + core::backup::WebDavClient::global().reset(); + Ok(()) +} + +/// 创建 WebDAV 备份并上传 +#[tauri::command] +pub async fn create_webdav_backup() -> CmdResult<()> { + wrap_err!(feat::create_backup_and_upload_webdav().await) +} + +/// 列出 WebDAV 上的备份文件 +#[tauri::command] +pub async fn list_webdav_backup() -> CmdResult> { + wrap_err!(feat::list_wevdav_backup().await) +} + +/// 删除 WebDAV 上的备份文件 +#[tauri::command] +pub async fn delete_webdav_backup(filename: String) -> CmdResult<()> { + wrap_err!(feat::delete_webdav_backup(filename).await) +} + +/// 从 WebDAV 恢复备份文件 +#[tauri::command] +pub async fn restore_webdav_backup(filename: String) -> CmdResult<()> { + wrap_err!(feat::restore_webdav_backup(filename).await) +} diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs deleted file mode 100644 index f9e6ed0c..00000000 --- a/src-tauri/src/cmds.rs +++ /dev/null @@ -1,698 +0,0 @@ -use crate::{ - config::*, - core::*, - feat, - utils::{dirs, help}, -}; -use crate::{log_err, ret_err, wrap_err}; -use anyhow::{Context, Result}; -use network_interface::NetworkInterface; -use serde_yaml::Mapping; -use std::collections::HashMap; -use sysproxy::{Autoproxy, Sysproxy}; -type CmdResult = Result; -use reqwest_dav::list_cmd::ListFile; -use tauri::Manager; -use std::fs; - -#[tauri::command] -pub fn copy_clash_env() -> CmdResult { - feat::copy_clash_env(); - Ok(()) -} - -#[tauri::command] -pub fn get_profiles() -> CmdResult { - let _ = tray::Tray::global().update_menu(); - Ok(Config::profiles().data().clone()) -} - -#[tauri::command] -pub async fn enhance_profiles() -> CmdResult { - match CoreManager::global().update_config().await { - Ok((true, _)) => { - println!("[enhance_profiles] 配置更新成功"); - log_err!(tray::Tray::global().update_tooltip()); - handle::Handle::refresh_clash(); - Ok(()) - } - Ok((false, error_msg)) => { - println!("[enhance_profiles] 配置验证失败: {}", error_msg); - handle::Handle::notice_message("config_validate::error", &error_msg); - Ok(()) - } - Err(e) => { - println!("[enhance_profiles] 更新过程发生错误: {}", e); - handle::Handle::notice_message("config_validate::process_terminated", &e.to_string()); - Ok(()) - } - } -} - -#[tauri::command] -pub async fn import_profile(url: String, option: Option) -> CmdResult { - let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?; - wrap_err!(Config::profiles().data().append_item(item)) -} - -#[tauri::command] -pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult { - wrap_err!(Config::profiles().data().reorder(active_id, over_id)) -} - -#[tauri::command] -pub async fn create_profile(item: PrfItem, file_data: Option) -> CmdResult { - let item = wrap_err!(PrfItem::from(item, file_data).await)?; - wrap_err!(Config::profiles().data().append_item(item)) -} - -#[tauri::command] -pub async fn update_profile(index: String, option: Option) -> CmdResult { - wrap_err!(feat::update_profile(index, option).await) -} - -#[tauri::command] -pub async fn delete_profile(index: String) -> CmdResult { - let should_update = wrap_err!({ Config::profiles().data().delete_item(index) })?; - if should_update { - wrap_err!(CoreManager::global().update_config().await)?; - handle::Handle::refresh_clash(); - } - Ok(()) -} - -/// 修改profiles的配置 -#[tauri::command] -pub async fn patch_profiles_config( - profiles: IProfiles -) -> CmdResult { - println!("[cmd配置patch] 开始修改配置文件"); - - // 保存当前配置,以便在验证失败时恢复 - let current_profile = Config::profiles().latest().current.clone(); - println!("[cmd配置patch] 当前配置: {:?}", current_profile); - - // 更新profiles配置 - println!("[cmd配置patch] 正在更新配置草稿"); - wrap_err!({ Config::profiles().draft().patch_config(profiles) })?; - - // 更新配置并进行验证 - match CoreManager::global().update_config().await { - Ok((true, _)) => { - println!("[cmd配置patch] 配置更新成功"); - handle::Handle::refresh_clash(); - let _ = tray::Tray::global().update_tooltip(); - Config::profiles().apply(); - wrap_err!(Config::profiles().data().save_file())?; - Ok(true) - } - Ok((false, error_msg)) => { - println!("[cmd配置patch] 配置验证失败: {}", error_msg); - Config::profiles().discard(); - - // 如果验证失败,恢复到之前的配置 - if let Some(prev_profile) = current_profile { - println!("[cmd配置patch] 尝试恢复到之前的配置: {}", prev_profile); - let restore_profiles = IProfiles { - current: Some(prev_profile), - items: None, - }; - // 静默恢复,不触发验证 - wrap_err!({ Config::profiles().draft().patch_config(restore_profiles) })?; - Config::profiles().apply(); - wrap_err!(Config::profiles().data().save_file())?; - println!("[cmd配置patch] 成功恢复到之前的配置"); - } - - // 发送验证错误通知 - handle::Handle::notice_message("config_validate::error", &error_msg); - Ok(false) - } - Err(e) => { - println!("[cmd配置patch] 更新过程发生错误: {}", e); - Config::profiles().discard(); - handle::Handle::notice_message("config_validate::boot_error", &e.to_string()); - Ok(false) - } - } -} - -/// 根据profile name修改profiles -#[tauri::command] -pub async fn patch_profiles_config_by_profile_index( - _app_handle: tauri::AppHandle, - profile_index: String -) -> CmdResult { - let profiles = IProfiles{current: Some(profile_index), items: None}; - patch_profiles_config(profiles).await -} - -/// 修改某个profile item的 -#[tauri::command] -pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult { - wrap_err!(Config::profiles().data().patch_item(index, profile))?; - Ok(()) -} - -#[tauri::command] -pub fn view_profile(app_handle: tauri::AppHandle, index: String) -> CmdResult { - let file = { - wrap_err!(Config::profiles().latest().get_item(&index))? - .file - .clone() - .ok_or("the file field is null") - }?; - - let path = wrap_err!(dirs::app_profiles_dir())?.join(file); - if !path.exists() { - ret_err!("the file not found"); - } - - wrap_err!(help::open_file(app_handle, path)) -} - -#[tauri::command] -pub fn read_profile_file(index: String) -> CmdResult { - let profiles = Config::profiles(); - let profiles = profiles.latest(); - let item = wrap_err!(profiles.get_item(&index))?; - let data = wrap_err!(item.read_file())?; - Ok(data) -} -/// 保存profiles的配置 -#[tauri::command] -pub async fn save_profile_file(index: String, file_data: Option) -> CmdResult { - if file_data.is_none() { - return Ok(()); - } - - // 在异步操作前完成所有文件操作 - let (file_path, original_content, is_merge_file) = { - let profiles = Config::profiles(); - let profiles_guard = profiles.latest(); - let item = wrap_err!(profiles_guard.get_item(&index))?; - // 确定是否为merge类型文件 - let is_merge = item.itype.as_ref().map_or(false, |t| t == "merge"); - let content = wrap_err!(item.read_file())?; - let path = item.file.clone().ok_or("file field is null")?; - let profiles_dir = wrap_err!(dirs::app_profiles_dir())?; - (profiles_dir.join(path), content, is_merge) - }; - - // 保存新的配置文件 - wrap_err!(fs::write(&file_path, file_data.clone().unwrap()))?; - - let file_path_str = file_path.to_string_lossy().to_string(); - println!("[cmd配置save] 开始验证配置文件: {}, 是否为merge文件: {}", file_path_str, is_merge_file); - - // 对于 merge 文件,只进行语法验证,不进行后续内核验证 - if is_merge_file { - println!("[cmd配置save] 检测到merge文件,只进行语法验证"); - match CoreManager::global().validate_config_file(&file_path_str, Some(true)).await { - Ok((true, _)) => { - println!("[cmd配置save] merge文件语法验证通过"); - // 成功后尝试更新整体配置 - if let Err(e) = CoreManager::global().update_config().await { - println!("[cmd配置save] 更新整体配置时发生错误: {}", e); - log::warn!(target: "app", "更新整体配置时发生错误: {}", e); - } - return Ok(()); - } - Ok((false, error_msg)) => { - println!("[cmd配置save] merge文件语法验证失败: {}", error_msg); - // 恢复原始配置文件 - wrap_err!(fs::write(&file_path, original_content))?; - // 发送合并文件专用错误通知 - let result = (false, error_msg.clone()); - handle_yaml_validation_notice(&result, "合并配置文件"); - return Ok(()); - } - Err(e) => { - println!("[cmd配置save] 验证过程发生错误: {}", e); - // 恢复原始配置文件 - wrap_err!(fs::write(&file_path, original_content))?; - return Err(e.to_string()); - } - } - } - - // 非merge文件使用完整验证流程 - match CoreManager::global().validate_config_file(&file_path_str, None).await { - Ok((true, _)) => { - println!("[cmd配置save] 验证成功"); - Ok(()) - } - Ok((false, error_msg)) => { - println!("[cmd配置save] 验证失败: {}", error_msg); - // 恢复原始配置文件 - wrap_err!(fs::write(&file_path, original_content))?; - - // 智能判断错误类型 - let is_script_error = file_path_str.ends_with(".js") || - error_msg.contains("Script syntax error") || - error_msg.contains("Script must contain a main function") || - error_msg.contains("Failed to read script file"); - - if error_msg.contains("YAML syntax error") || - error_msg.contains("Failed to read file:") || - (!file_path_str.ends_with(".js") && !is_script_error) { - // 普通YAML错误使用YAML通知处理 - println!("[cmd配置save] YAML配置文件验证失败,发送通知"); - let result = (false, error_msg.clone()); - handle_yaml_validation_notice(&result, "YAML配置文件"); - } else if is_script_error { - // 脚本错误使用专门的通知处理 - println!("[cmd配置save] 脚本文件验证失败,发送通知"); - let result = (false, error_msg.clone()); - handle_script_validation_notice(&result, "脚本文件"); - } else { - // 普通配置错误使用一般通知 - println!("[cmd配置save] 其他类型验证失败,发送一般通知"); - handle::Handle::notice_message("config_validate::error", &error_msg); - } - - Ok(()) - } - Err(e) => { - println!("[cmd配置save] 验证过程发生错误: {}", e); - // 恢复原始配置文件 - wrap_err!(fs::write(&file_path, original_content))?; - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub fn get_clash_info() -> CmdResult { - Ok(Config::clash().latest().get_client_info()) -} - -#[tauri::command] -pub fn get_runtime_config() -> CmdResult> { - Ok(Config::runtime().latest().config.clone()) -} - -#[tauri::command] -pub fn get_runtime_yaml() -> CmdResult { - let runtime = Config::runtime(); - let runtime = runtime.latest(); - let config = runtime.config.as_ref(); - wrap_err!(config - .ok_or(anyhow::anyhow!("failed to parse config to yaml file")) - .and_then( - |config| serde_yaml::to_string(config).context("failed to convert config to yaml") - )) -} - -#[tauri::command] -pub fn get_runtime_exists() -> CmdResult> { - Ok(Config::runtime().latest().exists_keys.clone()) -} - -#[tauri::command] -pub fn get_runtime_logs() -> CmdResult>> { - Ok(Config::runtime().latest().chain_logs.clone()) -} - -#[tauri::command] -pub async fn patch_clash_config(payload: Mapping) -> CmdResult { - wrap_err!(feat::patch_clash(payload).await) -} - -#[tauri::command] -pub async fn patch_clash_mode(payload: String) -> CmdResult { - Ok(feat::change_clash_mode(payload)) -} - - -#[tauri::command] -pub fn get_verge_config() -> CmdResult { - let verge = Config::verge(); - let verge_data = verge.data().clone(); - Ok(IVergeResponse::from(verge_data)) -} - -#[tauri::command] -pub async fn patch_verge_config(payload: IVerge) -> CmdResult { - wrap_err!(feat::patch_verge(payload, false).await) -} - -#[tauri::command] -pub async fn change_clash_core(clash_core: String) -> CmdResult> { - log::info!(target: "app", "changing core to {clash_core}"); - - match CoreManager::global().change_core(Some(clash_core.clone())).await { - Ok(_) => { - log::info!(target: "app", "core changed to {clash_core}"); - handle::Handle::notice_message("config_core::change_success", &clash_core); - handle::Handle::refresh_clash(); - Ok(None) - } - Err(err) => { - let error_msg = err.to_string(); - log::error!(target: "app", "failed to change core: {error_msg}"); - handle::Handle::notice_message("config_core::change_error", &error_msg); - Ok(Some(error_msg)) - } - } -} - -/// restart the sidecar -#[tauri::command] -pub async fn restart_core() -> CmdResult { - wrap_err!(CoreManager::global().restart_core().await) -} - -/// get the system proxy -#[tauri::command] -pub fn get_sys_proxy() -> CmdResult { - let current = wrap_err!(Sysproxy::get_system_proxy())?; - let mut map = Mapping::new(); - map.insert("enable".into(), current.enable.into()); - map.insert( - "server".into(), - format!("{}:{}", current.host, current.port).into(), - ); - map.insert("bypass".into(), current.bypass.into()); - - Ok(map) -} - -/// get the system proxy -#[tauri::command] -pub fn get_auto_proxy() -> CmdResult { - let current = wrap_err!(Autoproxy::get_auto_proxy())?; - - let mut map = Mapping::new(); - map.insert("enable".into(), current.enable.into()); - map.insert("url".into(), current.url.into()); - - Ok(map) -} - -#[tauri::command] -pub fn open_app_dir() -> CmdResult<()> { - let app_dir = wrap_err!(dirs::app_home_dir())?; - wrap_err!(open::that(app_dir)) -} - -#[tauri::command] -pub fn open_core_dir() -> CmdResult<()> { - let core_dir = wrap_err!(tauri::utils::platform::current_exe())?; - let core_dir = core_dir.parent().ok_or("failed to get core dir")?; - wrap_err!(open::that(core_dir)) -} - -#[tauri::command] -pub fn open_logs_dir() -> CmdResult<()> { - let log_dir = wrap_err!(dirs::app_logs_dir())?; - wrap_err!(open::that(log_dir)) -} - -#[tauri::command] -pub fn open_web_url(url: String) -> CmdResult<()> { - wrap_err!(open::that(url)) -} - -#[cfg(windows)] -pub mod uwp { - use super::*; - use crate::core::win_uwp; - - #[tauri::command] - pub async fn invoke_uwp_tool() -> CmdResult { - wrap_err!(win_uwp::invoke_uwptools().await) - } -} - -#[tauri::command] -pub async fn clash_api_get_proxy_delay( - name: String, - url: Option, - timeout: i32, -) -> CmdResult { - match clash_api::get_proxy_delay(name, url, timeout).await { - Ok(res) => Ok(res), - Err(err) => Err(err.to_string()), - } -} - -#[tauri::command] -pub fn get_portable_flag() -> CmdResult { - Ok(*dirs::PORTABLE_FLAG.get().unwrap_or(&false)) -} - -#[tauri::command] -pub async fn test_delay(url: String) -> CmdResult { - Ok(feat::test_delay(url).await.unwrap_or(10000u32)) -} - -#[tauri::command] -pub fn get_app_dir() -> CmdResult { - let app_home_dir = wrap_err!(dirs::app_home_dir())? - .to_string_lossy() - .to_string(); - Ok(app_home_dir) -} - -#[tauri::command] -pub async fn download_icon_cache(url: String, name: String) -> CmdResult { - let icon_cache_dir = wrap_err!(dirs::app_home_dir())?.join("icons").join("cache"); - let icon_path = icon_cache_dir.join(name); - if !icon_cache_dir.exists() { - let _ = std::fs::create_dir_all(&icon_cache_dir); - } - if !icon_path.exists() { - let response = wrap_err!(reqwest::get(url).await)?; - - let mut file = wrap_err!(std::fs::File::create(&icon_path))?; - - let content = wrap_err!(response.bytes().await)?; - wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?; - } - Ok(icon_path.to_string_lossy().to_string()) -} -#[tauri::command] -pub fn copy_icon_file(path: String, name: String) -> CmdResult { - let file_path = std::path::Path::new(&path); - let icon_dir = wrap_err!(dirs::app_home_dir())?.join("icons"); - if !icon_dir.exists() { - let _ = std::fs::create_dir_all(&icon_dir); - } - let ext = match file_path.extension() { - Some(e) => e.to_string_lossy().to_string(), - None => "ico".to_string(), - }; - - let png_dest_path = icon_dir.join(format!("{name}.png")); - let ico_dest_path = icon_dir.join(format!("{name}.ico")); - let dest_path = icon_dir.join(format!("{name}.{ext}")); - if file_path.exists() { - std::fs::remove_file(png_dest_path).unwrap_or_default(); - std::fs::remove_file(ico_dest_path).unwrap_or_default(); - match std::fs::copy(file_path, &dest_path) { - Ok(_) => Ok(dest_path.to_string_lossy().to_string()), - Err(err) => Err(err.to_string()), - } - } else { - Err("file not found".to_string()) - } -} - -#[tauri::command] -pub fn get_network_interfaces() -> Vec { - use sysinfo::Networks; - let mut result = Vec::new(); - let networks = Networks::new_with_refreshed_list(); - for (interface_name, _) in &networks { - result.push(interface_name.clone()); - } - result -} - -#[tauri::command] -pub fn get_network_interfaces_info() -> CmdResult> { - use network_interface::NetworkInterface; - use network_interface::NetworkInterfaceConfig; - - let names = get_network_interfaces(); - let interfaces = wrap_err!(NetworkInterface::show())?; - - let mut result = Vec::new(); - - for interface in interfaces { - if names.contains(&interface.name) { - result.push(interface); - } - } - - Ok(result) -} - -#[tauri::command] -pub fn open_devtools(app_handle: tauri::AppHandle) { - if let Some(window) = app_handle.get_webview_window("main") { - if !window.is_devtools_open() { - window.open_devtools(); - } else { - window.close_devtools(); - } - } -} - -#[tauri::command] -pub fn exit_app() { - feat::quit(Some(0)); -} - -#[tauri::command] -pub async fn save_webdav_config(url: String, username: String, password: String) -> CmdResult<()> { - let patch = IVerge { - webdav_url: Some(url), - webdav_username: Some(username), - webdav_password: Some(password), - ..IVerge::default() - }; - Config::verge().draft().patch_config(patch.clone()); - Config::verge().apply(); - Config::verge() - .data() - .save_file() - .map_err(|err| err.to_string())?; - backup::WebDavClient::global().reset(); - Ok(()) -} - -#[tauri::command] -pub async fn create_webdav_backup() -> CmdResult<()> { - wrap_err!(feat::create_backup_and_upload_webdav().await) -} - -#[tauri::command] -pub async fn list_webdav_backup() -> CmdResult> { - wrap_err!(feat::list_wevdav_backup().await) -} - -#[tauri::command] -pub async fn delete_webdav_backup(filename: String) -> CmdResult<()> { - wrap_err!(feat::delete_webdav_backup(filename).await) -} - -#[tauri::command] -pub async fn restore_webdav_backup(filename: String) -> CmdResult<()> { - wrap_err!(feat::restore_webdav_backup(filename).await) -} - -#[tauri::command] -pub async fn restart_app() -> CmdResult<()> { - feat::restart_app(); - Ok(()) -} - -#[cfg(not(windows))] -pub mod uwp { - use super::*; - - #[tauri::command] - pub async fn invoke_uwp_tool() -> CmdResult { - Ok(()) - } -} - -#[tauri::command] -pub async fn script_validate_notice(status: String, msg: String) -> CmdResult { - handle::Handle::notice_message(&status, &msg); - Ok(()) -} - -/// 处理脚本验证相关的所有消息通知 -/// 统一通知接口,保持消息类型一致性 -pub fn handle_script_validation_notice(result: &(bool, String), file_type: &str) { - if !result.0 { - let error_msg = &result.1; - - // 根据错误消息内容判断错误类型 - let status = if error_msg.starts_with("File not found:") { - "config_validate::file_not_found" - } else if error_msg.starts_with("Failed to read script file:") { - "config_validate::script_error" - } else if error_msg.starts_with("Script syntax error:") { - "config_validate::script_syntax_error" - } else if error_msg == "Script must contain a main function" { - "config_validate::script_missing_main" - } else { - // 如果是其他类型错误,作为一般脚本错误处理 - "config_validate::script_error" - }; - - log::warn!(target: "app", "{} 验证失败: {}", file_type, error_msg); - handle::Handle::notice_message(status, error_msg); - } -} - -/// 验证指定脚本文件 -#[tauri::command] -pub async fn validate_script_file(file_path: String) -> CmdResult { - log::info!(target: "app", "验证脚本文件: {}", file_path); - - match CoreManager::global().validate_config_file(&file_path, None).await { - Ok(result) => { - handle_script_validation_notice(&result, "脚本文件"); - Ok(result.0) // 返回验证结果布尔值 - }, - Err(e) => { - let error_msg = e.to_string(); - log::error!(target: "app", "验证脚本文件过程发生错误: {}", error_msg); - handle::Handle::notice_message("config_validate::process_terminated", &error_msg); - Ok(false) - } - } -} - -/// 处理YAML验证相关的所有消息通知 -/// 统一通知接口,保持消息类型一致性 -pub fn handle_yaml_validation_notice(result: &(bool, String), file_type: &str) { - if !result.0 { - let error_msg = &result.1; - println!("[通知] 处理{}验证错误: {}", file_type, error_msg); - - // 检查是否为merge文件 - let is_merge_file = file_type.contains("合并"); - - // 根据错误消息内容判断错误类型 - let status = if error_msg.starts_with("File not found:") { - "config_validate::file_not_found" - } else if error_msg.starts_with("Failed to read file:") { - "config_validate::yaml_read_error" - } else if error_msg.starts_with("YAML syntax error:") { - if is_merge_file { - "config_validate::merge_syntax_error" - } else { - "config_validate::yaml_syntax_error" - } - } else if error_msg.contains("mapping values are not allowed") { - if is_merge_file { - "config_validate::merge_mapping_error" - } else { - "config_validate::yaml_mapping_error" - } - } else if error_msg.contains("did not find expected key") { - if is_merge_file { - "config_validate::merge_key_error" - } else { - "config_validate::yaml_key_error" - } - } else { - // 如果是其他类型错误,根据文件类型作为一般错误处理 - if is_merge_file { - "config_validate::merge_error" - } else { - "config_validate::yaml_error" - } - }; - - log::warn!(target: "app", "{} 验证失败: {}", file_type, error_msg); - println!("[通知] 发送通知: status={}, msg={}", status, error_msg); - handle::Handle::notice_message(status, error_msg); - } -} diff --git a/src-tauri/src/core/tray/mod.rs b/src-tauri/src/core/tray/mod.rs index b1a358d4..68f69fa1 100644 --- a/src-tauri/src/core/tray/mod.rs +++ b/src-tauri/src/core/tray/mod.rs @@ -3,7 +3,7 @@ use once_cell::sync::OnceCell; pub mod speed_rate; use crate::core::clash_api::Rate; use crate::{ - cmds, + cmd, config::Config, feat, resolve, utils::resolve::VERSION, @@ -596,9 +596,9 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) { "system_proxy" => feat::toggle_system_proxy(), "tun_mode" => feat::toggle_tun_mode(None), "copy_env" => feat::copy_clash_env(), - "open_app_dir" => crate::log_err!(cmds::open_app_dir()), - "open_core_dir" => crate::log_err!(cmds::open_core_dir()), - "open_logs_dir" => crate::log_err!(cmds::open_logs_dir()), + "open_app_dir" => crate::log_err!(cmd::open_app_dir()), + "open_core_dir" => crate::log_err!(cmd::open_core_dir()), + "open_logs_dir" => crate::log_err!(cmd::open_logs_dir()), "restart_clash" => feat::restart_clash_core(), "restart_app" => feat::restart_app(), "quit" => { diff --git a/src-tauri/src/feat/profile.rs b/src-tauri/src/feat/profile.rs index 899bc3fc..9b2d6613 100644 --- a/src-tauri/src/feat/profile.rs +++ b/src-tauri/src/feat/profile.rs @@ -1,4 +1,4 @@ -use crate::cmds; +use crate::cmd; use crate::config::{Config, PrfItem, PrfOption}; use crate::core::handle; use crate::core::CoreManager; @@ -9,7 +9,7 @@ use anyhow::{bail, Result}; pub fn toggle_proxy_profile(profile_index: String) { tauri::async_runtime::spawn(async move { let app_handle = handle::Handle::global().app_handle().unwrap(); - match cmds::patch_profiles_config_by_profile_index(app_handle, profile_index).await { + match cmd::patch_profiles_config_by_profile_index(app_handle, profile_index).await { Ok(_) => { let _ = tray::Tray::global().update_menu(); } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 3ac2bf2c..7b49c195 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,4 +1,4 @@ -mod cmds; +mod cmd; mod config; mod core; mod enhance; @@ -9,7 +9,7 @@ use crate::utils::{resolve, resolve::resolve_scheme, server}; use config::Config; use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_deep_link::DeepLinkExt; -use std::{sync::{Mutex, Once}}; +use std::sync::{Mutex, Once}; use tauri::AppHandle; /// A global singleton handle to the application. @@ -134,61 +134,61 @@ pub fn run() { }) .invoke_handler(tauri::generate_handler![ // common - cmds::get_sys_proxy, - cmds::get_auto_proxy, - cmds::open_app_dir, - cmds::open_logs_dir, - cmds::open_web_url, - cmds::open_core_dir, - cmds::get_portable_flag, - cmds::get_network_interfaces, - cmds::restart_core, - cmds::restart_app, + cmd::get_sys_proxy, + cmd::get_auto_proxy, + cmd::open_app_dir, + cmd::open_logs_dir, + cmd::open_web_url, + cmd::open_core_dir, + cmd::get_portable_flag, + cmd::get_network_interfaces, + cmd::restart_core, + cmd::restart_app, // clash - cmds::get_clash_info, - cmds::patch_clash_config, - cmds::patch_clash_mode, - cmds::change_clash_core, - cmds::get_runtime_config, - cmds::get_runtime_yaml, - cmds::get_runtime_exists, - cmds::get_runtime_logs, - cmds::uwp::invoke_uwp_tool, - cmds::copy_clash_env, + cmd::get_clash_info, + cmd::patch_clash_config, + cmd::patch_clash_mode, + cmd::change_clash_core, + cmd::get_runtime_config, + cmd::get_runtime_yaml, + cmd::get_runtime_exists, + cmd::get_runtime_logs, + cmd::invoke_uwp_tool, + cmd::copy_clash_env, // verge - cmds::get_verge_config, - cmds::patch_verge_config, - cmds::test_delay, - cmds::get_app_dir, - cmds::copy_icon_file, - cmds::download_icon_cache, - cmds::open_devtools, - cmds::exit_app, - cmds::get_network_interfaces_info, + cmd::get_verge_config, + cmd::patch_verge_config, + cmd::test_delay, + cmd::get_app_dir, + cmd::copy_icon_file, + cmd::download_icon_cache, + cmd::open_devtools, + cmd::exit_app, + cmd::get_network_interfaces_info, // profile - cmds::get_profiles, - cmds::enhance_profiles, - cmds::patch_profiles_config, - cmds::view_profile, - cmds::patch_profile, - cmds::create_profile, - cmds::import_profile, - cmds::reorder_profile, - cmds::update_profile, - cmds::delete_profile, - cmds::read_profile_file, - cmds::save_profile_file, + cmd::get_profiles, + cmd::enhance_profiles, + cmd::patch_profiles_config, + cmd::view_profile, + cmd::patch_profile, + cmd::create_profile, + cmd::import_profile, + cmd::reorder_profile, + cmd::update_profile, + cmd::delete_profile, + cmd::read_profile_file, + cmd::save_profile_file, // script validation - cmds::script_validate_notice, - cmds::validate_script_file, + cmd::script_validate_notice, + cmd::validate_script_file, // clash api - cmds::clash_api_get_proxy_delay, + cmd::clash_api_get_proxy_delay, // backup - cmds::create_webdav_backup, - cmds::save_webdav_config, - cmds::list_webdav_backup, - cmds::delete_webdav_backup, - cmds::restore_webdav_backup, + cmd::create_webdav_backup, + cmd::save_webdav_config, + cmd::list_webdav_backup, + cmd::delete_webdav_backup, + cmd::restore_webdav_backup, ]); #[cfg(debug_assertions)]