Files
clash-proxy/src-tauri/src/feat.rs

485 lines
15 KiB
Rust
Raw Normal View History

2022-11-18 18:18:41 +08:00
//
//! feat mod 里的函数主要用于
//! - hotkey 快捷键
//! - timer 定时器
//! - cmds 页面调用
//!
2022-11-14 01:26:33 +08:00
use crate::config::*;
2022-09-14 01:19:02 +08:00
use crate::core::*;
2022-11-14 01:26:33 +08:00
use crate::log_err;
2024-11-09 23:11:02 +08:00
use crate::utils::dirs::app_home_dir;
use crate::utils::resolve;
2022-11-17 17:07:13 +08:00
use anyhow::{bail, Result};
2024-11-08 21:46:15 +08:00
use reqwest_dav::list_cmd::ListFile;
2022-11-14 01:26:33 +08:00
use serde_yaml::{Mapping, Value};
2024-11-09 23:11:02 +08:00
use std::fs;
use tauri::Manager;
2024-09-02 19:33:17 +08:00
use tauri_plugin_clipboard_manager::ClipboardExt;
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
2022-09-14 01:19:02 +08:00
// 打开面板
pub fn open_or_close_dashboard() {
if let Some(window) = handle::Handle::global().get_window() {
// 如果窗口存在,则切换其显示状态
if window.is_visible().unwrap_or(false) {
let _ = window.hide();
} else {
let _ = window.show();
let _ = window.set_focus();
}
} else {
resolve::create_window();
}
}
2022-10-28 00:40:29 +08:00
// 重启clash
pub fn restart_clash_core() {
2022-11-14 01:26:33 +08:00
tauri::async_runtime::spawn(async {
2024-09-27 00:24:05 +08:00
match CoreManager::global().restart_core().await {
2022-11-18 18:18:41 +08:00
Ok(_) => {
handle::Handle::refresh_clash();
handle::Handle::notice_message("set_config::ok", "ok");
}
Err(err) => {
handle::Handle::notice_message("set_config::error", format!("{err}"));
log::error!(target:"app", "{err}");
}
2022-11-17 20:19:40 +08:00
}
2022-11-14 01:26:33 +08:00
});
2022-10-28 00:40:29 +08:00
}
2024-11-09 23:11:02 +08:00
pub fn restart_app() {
tauri::async_runtime::spawn_blocking(|| {
tauri::async_runtime::block_on(async {
log_err!(CoreManager::global().stop_core().await);
});
resolve::resolve_reset();
let app_handle = handle::Handle::global().app_handle().unwrap();
std::thread::sleep(std::time::Duration::from_secs(1));
let _ = app_handle.save_window_state(StateFlags::default());
2024-11-09 23:11:02 +08:00
tauri::process::restart(&app_handle.env());
});
}
2022-11-14 01:26:33 +08:00
// 切换模式 rule/global/direct/script mode
pub fn change_clash_mode(mode: String) {
let mut mapping = Mapping::new();
mapping.insert(Value::from("mode"), mode.clone().into());
tauri::async_runtime::spawn(async move {
2022-11-23 16:04:25 +08:00
log::debug!(target: "app", "change clash mode to {mode}");
2022-11-14 01:26:33 +08:00
match clash_api::patch_configs(&mapping).await {
Ok(_) => {
// 更新订阅
2022-11-17 17:07:13 +08:00
Config::clash().data().patch_config(mapping);
2022-11-14 01:26:33 +08:00
2022-11-23 16:04:25 +08:00
if Config::clash().data().save_config().is_ok() {
2022-11-14 01:26:33 +08:00
handle::Handle::refresh_clash();
log_err!(handle::Handle::update_systray_part());
}
}
2022-11-18 20:15:34 +08:00
Err(err) => log::error!(target: "app", "{err}"),
2022-11-14 01:26:33 +08:00
}
});
2022-09-14 01:19:02 +08:00
}
// 切换系统代理
2022-11-17 17:07:13 +08:00
pub fn toggle_system_proxy() {
2024-01-10 17:36:35 +08:00
let enable = Config::verge().draft().enable_system_proxy;
2022-11-17 17:07:13 +08:00
let enable = enable.unwrap_or(false);
tauri::async_runtime::spawn(async move {
match patch_verge(IVerge {
enable_system_proxy: Some(!enable),
..IVerge::default()
})
.await
{
Ok(_) => handle::Handle::refresh_verge(),
Err(err) => log::error!(target: "app", "{err}"),
}
});
2022-09-14 01:19:02 +08:00
}
// 切换tun模式
2022-11-17 17:07:13 +08:00
pub fn toggle_tun_mode() {
2024-01-10 17:36:35 +08:00
let enable = Config::verge().data().enable_tun_mode;
2022-11-17 17:07:13 +08:00
let enable = enable.unwrap_or(false);
2022-11-17 17:07:13 +08:00
tauri::async_runtime::spawn(async move {
match patch_verge(IVerge {
enable_tun_mode: Some(!enable),
..IVerge::default()
})
.await
{
Ok(_) => handle::Handle::refresh_verge(),
Err(err) => log::error!(target: "app", "{err}"),
2022-11-17 17:07:13 +08:00
}
});
2022-09-14 01:19:02 +08:00
}
2024-09-25 11:47:01 +08:00
pub fn quit(code: Option<i32>) {
let app_handle = handle::Handle::global().app_handle().unwrap();
handle::Handle::global().set_is_exiting();
resolve::resolve_reset();
log_err!(handle::Handle::global().get_window().unwrap().close());
2024-09-25 11:47:01 +08:00
app_handle.exit(code.unwrap_or(0));
}
/// 修改clash的订阅
2022-11-17 17:07:13 +08:00
pub async fn patch_clash(patch: Mapping) -> Result<()> {
Config::clash().draft().patch_config(patch.clone());
2022-11-14 01:26:33 +08:00
2024-06-12 10:00:22 +08:00
let res = {
// 激活订阅
2024-07-07 18:09:53 +08:00
if patch.get("secret").is_some() || patch.get("external-controller").is_some() {
Config::generate().await?;
2024-09-27 00:24:05 +08:00
CoreManager::global().restart_core().await?;
2024-11-05 16:24:58 +08:00
} else {
if patch.get("mode").is_some() {
log_err!(handle::Handle::update_systray_part());
}
Config::runtime().latest().patch_config(patch);
CoreManager::global().update_config().await?;
2022-11-17 17:07:13 +08:00
}
handle::Handle::refresh_clash();
2022-11-17 17:07:13 +08:00
<Result<()>>::Ok(())
2024-06-12 10:00:22 +08:00
};
match res {
2022-11-17 17:07:13 +08:00
Ok(()) => {
Config::clash().apply();
Config::clash().data().save_config()?;
Ok(())
2022-11-14 01:26:33 +08:00
}
2022-11-17 17:07:13 +08:00
Err(err) => {
Config::clash().discard();
Err(err)
}
}
2022-11-14 01:26:33 +08:00
}
/// 修改verge的订阅
2022-11-14 01:26:33 +08:00
/// 一般都是一个个的修改
2022-11-17 17:07:13 +08:00
pub async fn patch_verge(patch: IVerge) -> Result<()> {
Config::verge().draft().patch_config(patch.clone());
2024-09-27 00:24:05 +08:00
2022-11-14 01:26:33 +08:00
let tun_mode = patch.enable_tun_mode;
let auto_launch = patch.enable_auto_launch;
let system_proxy = patch.enable_system_proxy;
2024-05-26 17:59:39 +08:00
let pac = patch.proxy_auto_config;
let pac_content = patch.pac_file_content;
2022-11-14 01:26:33 +08:00
let proxy_bypass = patch.system_proxy_bypass;
let language = patch.language;
2024-07-07 18:09:53 +08:00
let mixed_port = patch.verge_mixed_port;
2024-05-15 12:00:38 +08:00
#[cfg(target_os = "macos")]
let tray_icon = patch.tray_icon;
2024-09-20 17:49:31 +08:00
#[cfg(not(target_os = "macos"))]
2024-09-20 18:02:19 +08:00
let tray_icon: Option<String> = None;
2024-02-24 00:52:21 +08:00
let common_tray_icon = patch.common_tray_icon;
let sysproxy_tray_icon = patch.sysproxy_tray_icon;
let tun_tray_icon = patch.tun_tray_icon;
2024-05-09 09:01:04 +08:00
#[cfg(not(target_os = "windows"))]
let redir_enabled = patch.verge_redir_enabled;
2024-07-07 18:09:53 +08:00
#[cfg(not(target_os = "windows"))]
let redir_port = patch.verge_redir_port;
2024-05-09 09:01:04 +08:00
#[cfg(target_os = "linux")]
let tproxy_enabled = patch.verge_tproxy_enabled;
2024-07-07 18:09:53 +08:00
#[cfg(target_os = "linux")]
let tproxy_port = patch.verge_tproxy_port;
2024-05-09 09:01:04 +08:00
let socks_enabled = patch.verge_socks_enabled;
2024-07-07 18:09:53 +08:00
let socks_port = patch.verge_socks_port;
2024-05-09 09:01:04 +08:00
let http_enabled = patch.verge_http_enabled;
2024-07-07 18:09:53 +08:00
let http_port = patch.verge_port;
2024-09-27 00:24:05 +08:00
2024-09-28 12:37:01 +08:00
let res: std::result::Result<(), anyhow::Error> = {
let mut should_restart_core = false;
let mut should_update_clash_config = false;
let mut should_update_launch = false;
let mut should_update_sysproxy = false;
let mut should_update_systray_part = false;
if tun_mode.is_some() {
should_update_clash_config = true;
2022-11-17 17:07:13 +08:00
}
2024-09-28 12:37:01 +08:00
2024-05-09 09:01:04 +08:00
#[cfg(not(target_os = "windows"))]
2024-09-28 12:37:01 +08:00
if redir_enabled.is_some() || redir_port.is_some() {
should_restart_core = true;
2024-05-09 09:01:04 +08:00
}
2024-09-27 00:24:05 +08:00
2024-05-09 09:01:04 +08:00
#[cfg(target_os = "linux")]
2024-07-07 18:09:53 +08:00
if tproxy_enabled.is_some() || tproxy_port.is_some() {
2024-09-28 12:37:01 +08:00
should_restart_core = true;
2024-05-09 09:01:04 +08:00
}
2024-09-28 12:37:01 +08:00
if socks_enabled.is_some()
2024-07-07 18:09:53 +08:00
|| http_enabled.is_some()
|| socks_port.is_some()
2024-09-25 11:47:01 +08:00
|| http_port.is_some()
2024-09-28 12:37:01 +08:00
|| mixed_port.is_some()
2024-09-25 11:47:01 +08:00
{
2024-09-28 12:37:01 +08:00
should_restart_core = true;
2024-05-09 09:01:04 +08:00
}
2022-11-17 17:07:13 +08:00
if auto_launch.is_some() {
2024-09-28 12:37:01 +08:00
should_update_launch = true;
2022-11-17 17:07:13 +08:00
}
2024-05-26 17:59:39 +08:00
if system_proxy.is_some()
|| proxy_bypass.is_some()
2024-07-07 18:09:53 +08:00
|| mixed_port.is_some()
2024-05-26 17:59:39 +08:00
|| pac.is_some()
|| pac_content.is_some()
{
2024-09-28 12:37:01 +08:00
should_update_sysproxy = true;
2022-11-17 17:07:13 +08:00
}
2022-11-14 01:26:33 +08:00
2024-09-22 15:39:52 +08:00
if language.is_some()
|| system_proxy.is_some()
2024-02-24 00:52:21 +08:00
|| tun_mode.is_some()
|| common_tray_icon.is_some()
|| sysproxy_tray_icon.is_some()
|| tun_tray_icon.is_some()
|| tray_icon.is_some()
2024-02-24 00:52:21 +08:00
{
2024-09-28 12:37:01 +08:00
should_update_systray_part = true;
}
if should_restart_core {
CoreManager::global().restart_core().await?;
}
if should_update_clash_config {
CoreManager::global().update_config().await?;
handle::Handle::refresh_clash();
2024-09-28 12:37:01 +08:00
}
if should_update_launch {
sysopt::Sysopt::global().update_launch()?;
}
if should_update_sysproxy {
2024-10-04 05:27:59 +08:00
sysopt::Sysopt::global().update_sysproxy().await?;
2024-09-28 12:37:01 +08:00
}
if let Some(hotkeys) = patch.hotkeys {
hotkey::Hotkey::global().update(hotkeys)?;
}
if should_update_systray_part {
2022-11-17 17:07:13 +08:00
handle::Handle::update_systray_part()?;
}
<Result<()>>::Ok(())
2024-06-12 10:00:22 +08:00
};
match res {
2022-11-17 17:07:13 +08:00
Ok(()) => {
Config::verge().apply();
Config::verge().data().save_file()?;
2024-09-28 12:37:01 +08:00
2024-10-10 18:40:39 +08:00
Ok(())
2022-11-17 17:07:13 +08:00
}
Err(err) => {
Config::verge().discard();
2024-10-10 18:40:39 +08:00
Err(err)
2022-11-17 17:07:13 +08:00
}
}
2022-11-14 01:26:33 +08:00
}
2022-11-12 11:37:23 +08:00
2022-11-16 01:26:41 +08:00
/// 更新某个profile
/// 如果更新当前订阅就激活订阅
2022-11-16 01:26:41 +08:00
pub async fn update_profile(uid: String, option: Option<PrfOption>) -> Result<()> {
2022-11-17 17:07:13 +08:00
let url_opt = {
let profiles = Config::profiles();
let profiles = profiles.latest();
let item = profiles.get_item(&uid)?;
let is_remote = item.itype.as_ref().map_or(false, |s| s == "remote");
if !is_remote {
None // 直接更新
} else if item.url.is_none() {
bail!("failed to get the profile item url");
} else {
Some((item.url.clone().unwrap(), item.option.clone()))
}
};
let should_update = match url_opt {
Some((url, opt)) => {
let merged_opt = PrfOption::merge(opt, option);
let item = PrfItem::from_url(&url, None, None, merged_opt).await?;
let profiles = Config::profiles();
let mut profiles = profiles.latest();
profiles.update_item(uid.clone(), item)?;
Some(uid) == profiles.get_current()
}
None => true,
};
if should_update {
match CoreManager::global().update_config().await {
Ok(_) => {
handle::Handle::refresh_clash();
}
Err(err) => {
2024-11-30 07:04:09 +08:00
handle::Handle::notice_message("set_config::error", format!("{err}"));
log::error!(target: "app", "{err}");
}
2022-11-18 18:18:41 +08:00
}
}
Ok(())
2022-11-18 18:18:41 +08:00
}
/// copy env variable
pub fn copy_clash_env() {
let app_handle = handle::Handle::global().app_handle().unwrap();
2023-12-07 14:52:14 +08:00
let port = { Config::verge().latest().verge_mixed_port.unwrap_or(7897) };
2023-11-09 10:52:52 +08:00
let http_proxy = format!("http://127.0.0.1:{}", port);
let socks5_proxy = format!("socks5://127.0.0.1:{}", port);
2023-12-01 12:56:18 +08:00
let sh =
format!("export https_proxy={http_proxy} http_proxy={http_proxy} all_proxy={socks5_proxy}");
2024-05-03 12:50:04 +08:00
let cmd: String = format!("set http_proxy={http_proxy}\r\nset https_proxy={http_proxy}");
2023-11-09 10:52:52 +08:00
let ps: String = format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\"");
let nu: String =
format!("load-env {{ http_proxy: \"{http_proxy}\", https_proxy: \"{http_proxy}\" }}");
2024-09-02 19:33:17 +08:00
let cliboard = app_handle.clipboard();
let env_type = { Config::verge().latest().env_type.clone() };
let env_type = match env_type {
Some(env_type) => env_type,
None => {
#[cfg(not(target_os = "windows"))]
let default = "bash";
#[cfg(target_os = "windows")]
let default = "powershell";
default.to_string()
}
};
match env_type.as_str() {
"bash" => cliboard.write_text(sh).unwrap_or_default(),
2023-12-01 15:52:49 +08:00
"cmd" => cliboard.write_text(cmd).unwrap_or_default(),
"powershell" => cliboard.write_text(ps).unwrap_or_default(),
"nushell" => cliboard.write_text(nu).unwrap_or_default(),
_ => log::error!(target: "app", "copy_clash_env: Invalid env type! {env_type}"),
2023-12-01 15:52:49 +08:00
};
}
2024-01-17 11:02:17 +08:00
pub async fn test_delay(url: String) -> Result<u32> {
use tokio::time::{Duration, Instant};
let mut builder = reqwest::ClientBuilder::new().use_rustls_tls().no_proxy();
let port = Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
let tun_mode = Config::verge().latest().enable_tun_mode.unwrap_or(false);
2024-01-17 11:02:17 +08:00
let proxy_scheme = format!("http://127.0.0.1:{port}");
if !tun_mode {
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
2024-01-17 11:02:17 +08:00
}
let request = builder
.timeout(Duration::from_millis(10000))
.build()?
.get(url).header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0");
2024-01-17 11:02:17 +08:00
let start = Instant::now();
2024-06-05 19:17:43 +08:00
let response = request.send().await;
match response {
Ok(response) => {
log::trace!(target: "app", "test_delay response: {:#?}", response);
if response.status().is_success() {
Ok(start.elapsed().as_millis() as u32)
} else {
Ok(10000u32)
}
}
Err(err) => {
log::trace!(target: "app", "test_delay error: {:#?}", err);
Err(err.into())
}
2024-01-17 11:02:17 +08:00
}
}
2024-11-08 21:46:15 +08:00
pub async fn create_backup_and_upload_webdav() -> Result<()> {
2024-11-08 23:42:00 +08:00
let (file_name, temp_file_path) = backup::create_backup().map_err(|err| {
log::error!(target: "app", "Failed to create backup: {:#?}", err);
err
})?;
2024-11-08 21:46:15 +08:00
2024-11-08 23:42:00 +08:00
if let Err(err) = backup::WebDavClient::global()
.upload(temp_file_path.clone(), file_name)
.await
2024-11-08 21:46:15 +08:00
{
2024-11-08 23:42:00 +08:00
log::error!(target: "app", "Failed to upload to WebDAV: {:#?}", err);
2024-11-08 21:46:15 +08:00
return Err(err);
}
2024-11-08 23:42:00 +08:00
if let Err(err) = std::fs::remove_file(&temp_file_path) {
log::warn!(target: "app", "Failed to remove temp file: {:#?}", err);
}
2024-11-08 21:46:15 +08:00
Ok(())
}
pub async fn list_wevdav_backup() -> Result<Vec<ListFile>> {
2024-11-09 23:11:02 +08:00
backup::WebDavClient::global().list().await.map_err(|err| {
log::error!(target: "app", "Failed to list WebDAV backup files: {:#?}", err);
err
})
}
pub async fn delete_webdav_backup(filename: String) -> Result<()> {
2024-11-08 21:46:15 +08:00
backup::WebDavClient::global()
2024-11-09 23:11:02 +08:00
.delete(filename)
2024-11-08 21:46:15 +08:00
.await
.map_err(|err| {
2024-11-09 23:11:02 +08:00
log::error!(target: "app", "Failed to delete WebDAV backup file: {:#?}", err);
2024-11-08 21:46:15 +08:00
err
})
}
2024-11-09 23:11:02 +08:00
pub async fn restore_webdav_backup(filename: String) -> Result<()> {
let verge = Config::verge();
let verge_data = verge.data().clone();
let webdav_url = verge_data.webdav_url.clone();
let webdav_username = verge_data.webdav_username.clone();
let webdav_password = verge_data.webdav_password.clone();
2024-11-09 23:11:02 +08:00
let backup_storage_path = app_home_dir().unwrap().join(&filename);
backup::WebDavClient::global()
.download(filename, backup_storage_path.clone())
.await
.map_err(|err| {
log::error!(target: "app", "Failed to download WebDAV backup file: {:#?}", err);
err
})?;
// extract zip file
let mut zip = zip::ZipArchive::new(fs::File::open(backup_storage_path.clone())?)?;
zip.extract(app_home_dir()?)?;
log_err!(
patch_verge(IVerge {
webdav_url: webdav_url,
webdav_username: webdav_username,
webdav_password: webdav_password,
..IVerge::default()
})
.await
);
2024-11-09 23:11:02 +08:00
// 最后删除临时文件
fs::remove_file(backup_storage_path)?;
Ok(())
}