use crate::{ config::*, core::*, feat, utils::{dirs, help}, }; use crate::{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; #[tauri::command] pub fn copy_clash_env() -> CmdResult { feat::copy_clash_env(); Ok(()) } #[tauri::command] pub fn get_profiles() -> CmdResult { Ok(Config::profiles().data().clone()) } #[tauri::command] pub async fn enhance_profiles() -> CmdResult { wrap_err!(CoreManager::global().update_config().await)?; handle::Handle::refresh_clash(); 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 { wrap_err!({ Config::profiles().draft().patch_config(profiles) })?; match CoreManager::global().update_config().await { Ok(_) => { handle::Handle::refresh_clash(); let _ = handle::Handle::update_systray_tooltip(); Config::profiles().apply(); wrap_err!(Config::profiles().data().save_file())?; Ok(()) } Err(err) => { Config::profiles().discard(); log::error!(target: "app", "{err}"); Err(format!("{err}")) } } } /// 修改某个profile item的 #[tauri::command] pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult { wrap_err!(Config::profiles().data().patch_item(index, profile))?; wrap_err!(timer::Timer::global().refresh()) } #[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) } #[tauri::command] pub fn save_profile_file(index: String, file_data: Option) -> CmdResult { if file_data.is_none() { return Ok(()); } let profiles = Config::profiles(); let profiles = profiles.latest(); let item = wrap_err!(profiles.get_item(&index))?; wrap_err!(item.save_file(file_data.unwrap())) } #[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 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).await) } #[tauri::command] pub async fn change_clash_core(clash_core: Option) -> CmdResult { wrap_err!(CoreManager::global().change_core(clash_core).await) } /// 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(()) } }