mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 05:03:45 +08:00
refactor: rename cmds module to cmd for better consistency (#2830)
- Renamed `cmds` module to `cmd` for better naming consistency - Reorganized command modules into separate files under src/cmd/ - Updated all imports and references to use the new module name - Fixed missing dependency in webdav.rs to reference core::backup - Updated tray module to use new cmd namespace - Improved uwp.rs module structure using platform-specific implementations - Removed unnecessary imports from various command files
This commit is contained in:
parent
184fd4a1ba
commit
9ee011514a
122
src-tauri/src/cmd/app.rs
Normal file
122
src-tauri/src/cmd/app.rs
Normal file
@ -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<bool> {
|
||||||
|
Ok(*dirs::PORTABLE_FLAG.get().unwrap_or(&false))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取应用目录
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_app_dir() -> CmdResult<String> {
|
||||||
|
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<String> {
|
||||||
|
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<String> {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
79
src-tauri/src/cmd/clash.rs
Normal file
79
src-tauri/src/cmd/clash.rs
Normal file
@ -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<ClashInfo> {
|
||||||
|
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<Option<String>> {
|
||||||
|
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<String>,
|
||||||
|
timeout: i32,
|
||||||
|
) -> CmdResult<clash_api::DelayRes> {
|
||||||
|
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<u32> {
|
||||||
|
Ok(feat::test_delay(url).await.unwrap_or(10000u32))
|
||||||
|
}
|
28
src-tauri/src/cmd/mod.rs
Normal file
28
src-tauri/src/cmd/mod.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
// Common result type used by command functions
|
||||||
|
pub type CmdResult<T = ()> = Result<T, String>;
|
||||||
|
|
||||||
|
// 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::*;
|
64
src-tauri/src/cmd/network.rs
Normal file
64
src-tauri/src/cmd/network.rs
Normal file
@ -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<Mapping> {
|
||||||
|
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<Mapping> {
|
||||||
|
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<String> {
|
||||||
|
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<Vec<NetworkInterface>> {
|
||||||
|
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)
|
||||||
|
}
|
176
src-tauri/src/cmd/profile.rs
Normal file
176
src-tauri/src/cmd/profile.rs
Normal file
@ -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<IProfiles> {
|
||||||
|
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<PrfOption>) -> 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<String>) -> 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<PrfOption>) -> 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<bool> {
|
||||||
|
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<bool> {
|
||||||
|
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<String> {
|
||||||
|
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)
|
||||||
|
}
|
39
src-tauri/src/cmd/runtime.rs
Normal file
39
src-tauri/src/cmd/runtime.rs
Normal file
@ -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<Option<Mapping>> {
|
||||||
|
Ok(Config::runtime().latest().config.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取运行时YAML配置
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_runtime_yaml() -> CmdResult<String> {
|
||||||
|
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<Vec<String>> {
|
||||||
|
Ok(Config::runtime().latest().exists_keys.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取运行时日志
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_runtime_logs() -> CmdResult<HashMap<String, Vec<(String, String)>>> {
|
||||||
|
Ok(Config::runtime().latest().chain_logs.clone())
|
||||||
|
}
|
111
src-tauri/src/cmd/save_profile.rs
Normal file
111
src-tauri/src/cmd/save_profile.rs
Normal file
@ -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<String>) -> 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src-tauri/src/cmd/uwp.rs
Normal file
29
src-tauri/src/cmd/uwp.rs
Normal file
@ -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
|
||||||
|
}
|
101
src-tauri/src/cmd/validate.rs
Normal file
101
src-tauri/src/cmd/validate.rs
Normal file
@ -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<bool> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
20
src-tauri/src/cmd/verge.rs
Normal file
20
src-tauri/src/cmd/verge.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use crate::{
|
||||||
|
config::*,
|
||||||
|
feat,
|
||||||
|
wrap_err,
|
||||||
|
};
|
||||||
|
use super::CmdResult;
|
||||||
|
|
||||||
|
/// 获取Verge配置
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_verge_config() -> CmdResult<IVergeResponse> {
|
||||||
|
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)
|
||||||
|
}
|
51
src-tauri/src/cmd/webdav.rs
Normal file
51
src-tauri/src/cmd/webdav.rs
Normal file
@ -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<Vec<ListFile>> {
|
||||||
|
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)
|
||||||
|
}
|
@ -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<T = ()> = Result<T, String>;
|
|
||||||
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<IProfiles> {
|
|
||||||
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<PrfOption>) -> 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<String>) -> 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<PrfOption>) -> 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<bool> {
|
|
||||||
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<bool> {
|
|
||||||
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<String> {
|
|
||||||
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<String>) -> 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<ClashInfo> {
|
|
||||||
Ok(Config::clash().latest().get_client_info())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_runtime_config() -> CmdResult<Option<Mapping>> {
|
|
||||||
Ok(Config::runtime().latest().config.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_runtime_yaml() -> CmdResult<String> {
|
|
||||||
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<Vec<String>> {
|
|
||||||
Ok(Config::runtime().latest().exists_keys.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_runtime_logs() -> CmdResult<HashMap<String, Vec<(String, String)>>> {
|
|
||||||
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<IVergeResponse> {
|
|
||||||
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<Option<String>> {
|
|
||||||
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<Mapping> {
|
|
||||||
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<Mapping> {
|
|
||||||
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<String>,
|
|
||||||
timeout: i32,
|
|
||||||
) -> CmdResult<clash_api::DelayRes> {
|
|
||||||
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<bool> {
|
|
||||||
Ok(*dirs::PORTABLE_FLAG.get().unwrap_or(&false))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn test_delay(url: String) -> CmdResult<u32> {
|
|
||||||
Ok(feat::test_delay(url).await.unwrap_or(10000u32))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub fn get_app_dir() -> CmdResult<String> {
|
|
||||||
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<String> {
|
|
||||||
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<String> {
|
|
||||||
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<String> {
|
|
||||||
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<Vec<NetworkInterface>> {
|
|
||||||
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<Vec<ListFile>> {
|
|
||||||
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<bool> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ use once_cell::sync::OnceCell;
|
|||||||
pub mod speed_rate;
|
pub mod speed_rate;
|
||||||
use crate::core::clash_api::Rate;
|
use crate::core::clash_api::Rate;
|
||||||
use crate::{
|
use crate::{
|
||||||
cmds,
|
cmd,
|
||||||
config::Config,
|
config::Config,
|
||||||
feat, resolve,
|
feat, resolve,
|
||||||
utils::resolve::VERSION,
|
utils::resolve::VERSION,
|
||||||
@ -596,9 +596,9 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
|
|||||||
"system_proxy" => feat::toggle_system_proxy(),
|
"system_proxy" => feat::toggle_system_proxy(),
|
||||||
"tun_mode" => feat::toggle_tun_mode(None),
|
"tun_mode" => feat::toggle_tun_mode(None),
|
||||||
"copy_env" => feat::copy_clash_env(),
|
"copy_env" => feat::copy_clash_env(),
|
||||||
"open_app_dir" => crate::log_err!(cmds::open_app_dir()),
|
"open_app_dir" => crate::log_err!(cmd::open_app_dir()),
|
||||||
"open_core_dir" => crate::log_err!(cmds::open_core_dir()),
|
"open_core_dir" => crate::log_err!(cmd::open_core_dir()),
|
||||||
"open_logs_dir" => crate::log_err!(cmds::open_logs_dir()),
|
"open_logs_dir" => crate::log_err!(cmd::open_logs_dir()),
|
||||||
"restart_clash" => feat::restart_clash_core(),
|
"restart_clash" => feat::restart_clash_core(),
|
||||||
"restart_app" => feat::restart_app(),
|
"restart_app" => feat::restart_app(),
|
||||||
"quit" => {
|
"quit" => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::cmds;
|
use crate::cmd;
|
||||||
use crate::config::{Config, PrfItem, PrfOption};
|
use crate::config::{Config, PrfItem, PrfOption};
|
||||||
use crate::core::handle;
|
use crate::core::handle;
|
||||||
use crate::core::CoreManager;
|
use crate::core::CoreManager;
|
||||||
@ -9,7 +9,7 @@ use anyhow::{bail, Result};
|
|||||||
pub fn toggle_proxy_profile(profile_index: String) {
|
pub fn toggle_proxy_profile(profile_index: String) {
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
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(_) => {
|
Ok(_) => {
|
||||||
let _ = tray::Tray::global().update_menu();
|
let _ = tray::Tray::global().update_menu();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
mod cmds;
|
mod cmd;
|
||||||
mod config;
|
mod config;
|
||||||
mod core;
|
mod core;
|
||||||
mod enhance;
|
mod enhance;
|
||||||
@ -9,7 +9,7 @@ use crate::utils::{resolve, resolve::resolve_scheme, server};
|
|||||||
use config::Config;
|
use config::Config;
|
||||||
use tauri_plugin_autostart::MacosLauncher;
|
use tauri_plugin_autostart::MacosLauncher;
|
||||||
use tauri_plugin_deep_link::DeepLinkExt;
|
use tauri_plugin_deep_link::DeepLinkExt;
|
||||||
use std::{sync::{Mutex, Once}};
|
use std::sync::{Mutex, Once};
|
||||||
use tauri::AppHandle;
|
use tauri::AppHandle;
|
||||||
|
|
||||||
/// A global singleton handle to the application.
|
/// A global singleton handle to the application.
|
||||||
@ -134,61 +134,61 @@ pub fn run() {
|
|||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
// common
|
// common
|
||||||
cmds::get_sys_proxy,
|
cmd::get_sys_proxy,
|
||||||
cmds::get_auto_proxy,
|
cmd::get_auto_proxy,
|
||||||
cmds::open_app_dir,
|
cmd::open_app_dir,
|
||||||
cmds::open_logs_dir,
|
cmd::open_logs_dir,
|
||||||
cmds::open_web_url,
|
cmd::open_web_url,
|
||||||
cmds::open_core_dir,
|
cmd::open_core_dir,
|
||||||
cmds::get_portable_flag,
|
cmd::get_portable_flag,
|
||||||
cmds::get_network_interfaces,
|
cmd::get_network_interfaces,
|
||||||
cmds::restart_core,
|
cmd::restart_core,
|
||||||
cmds::restart_app,
|
cmd::restart_app,
|
||||||
// clash
|
// clash
|
||||||
cmds::get_clash_info,
|
cmd::get_clash_info,
|
||||||
cmds::patch_clash_config,
|
cmd::patch_clash_config,
|
||||||
cmds::patch_clash_mode,
|
cmd::patch_clash_mode,
|
||||||
cmds::change_clash_core,
|
cmd::change_clash_core,
|
||||||
cmds::get_runtime_config,
|
cmd::get_runtime_config,
|
||||||
cmds::get_runtime_yaml,
|
cmd::get_runtime_yaml,
|
||||||
cmds::get_runtime_exists,
|
cmd::get_runtime_exists,
|
||||||
cmds::get_runtime_logs,
|
cmd::get_runtime_logs,
|
||||||
cmds::uwp::invoke_uwp_tool,
|
cmd::invoke_uwp_tool,
|
||||||
cmds::copy_clash_env,
|
cmd::copy_clash_env,
|
||||||
// verge
|
// verge
|
||||||
cmds::get_verge_config,
|
cmd::get_verge_config,
|
||||||
cmds::patch_verge_config,
|
cmd::patch_verge_config,
|
||||||
cmds::test_delay,
|
cmd::test_delay,
|
||||||
cmds::get_app_dir,
|
cmd::get_app_dir,
|
||||||
cmds::copy_icon_file,
|
cmd::copy_icon_file,
|
||||||
cmds::download_icon_cache,
|
cmd::download_icon_cache,
|
||||||
cmds::open_devtools,
|
cmd::open_devtools,
|
||||||
cmds::exit_app,
|
cmd::exit_app,
|
||||||
cmds::get_network_interfaces_info,
|
cmd::get_network_interfaces_info,
|
||||||
// profile
|
// profile
|
||||||
cmds::get_profiles,
|
cmd::get_profiles,
|
||||||
cmds::enhance_profiles,
|
cmd::enhance_profiles,
|
||||||
cmds::patch_profiles_config,
|
cmd::patch_profiles_config,
|
||||||
cmds::view_profile,
|
cmd::view_profile,
|
||||||
cmds::patch_profile,
|
cmd::patch_profile,
|
||||||
cmds::create_profile,
|
cmd::create_profile,
|
||||||
cmds::import_profile,
|
cmd::import_profile,
|
||||||
cmds::reorder_profile,
|
cmd::reorder_profile,
|
||||||
cmds::update_profile,
|
cmd::update_profile,
|
||||||
cmds::delete_profile,
|
cmd::delete_profile,
|
||||||
cmds::read_profile_file,
|
cmd::read_profile_file,
|
||||||
cmds::save_profile_file,
|
cmd::save_profile_file,
|
||||||
// script validation
|
// script validation
|
||||||
cmds::script_validate_notice,
|
cmd::script_validate_notice,
|
||||||
cmds::validate_script_file,
|
cmd::validate_script_file,
|
||||||
// clash api
|
// clash api
|
||||||
cmds::clash_api_get_proxy_delay,
|
cmd::clash_api_get_proxy_delay,
|
||||||
// backup
|
// backup
|
||||||
cmds::create_webdav_backup,
|
cmd::create_webdav_backup,
|
||||||
cmds::save_webdav_config,
|
cmd::save_webdav_config,
|
||||||
cmds::list_webdav_backup,
|
cmd::list_webdav_backup,
|
||||||
cmds::delete_webdav_backup,
|
cmd::delete_webdav_backup,
|
||||||
cmds::restore_webdav_backup,
|
cmd::restore_webdav_backup,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user