mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 08:45:41 +08:00
refactor: wip
This commit is contained in:
66
src-tauri/src/core/clash_api.rs
Normal file
66
src-tauri/src/core/clash_api.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use crate::{config, utils::dirs};
|
||||
use anyhow::{bail, Result};
|
||||
use reqwest::header::HeaderMap;
|
||||
use serde_yaml::Mapping;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// PUT /configs
|
||||
pub async fn put_configs() -> Result<()> {
|
||||
let (url, headers) = clash_client_info()?;
|
||||
let url = format!("{url}/configs");
|
||||
|
||||
let runtime_yaml = dirs::clash_runtime_yaml();
|
||||
let runtime_yaml = dirs::path_to_str(&runtime_yaml)?;
|
||||
|
||||
let mut data = HashMap::new();
|
||||
data.insert("path", runtime_yaml);
|
||||
|
||||
let client = reqwest::ClientBuilder::new().no_proxy().build()?;
|
||||
let builder = client.put(&url).headers(headers).json(&data);
|
||||
let response = builder.send().await?;
|
||||
|
||||
match response.status().as_u16() {
|
||||
204 => Ok(()),
|
||||
status @ _ => {
|
||||
bail!("failed to put configs with status \"{status}\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PATCH /configs
|
||||
pub async fn patch_configs(config: &Mapping) -> Result<()> {
|
||||
let (url, headers) = clash_client_info()?;
|
||||
let url = format!("{url}/configs");
|
||||
|
||||
let client = reqwest::ClientBuilder::new().no_proxy().build()?;
|
||||
let builder = client.patch(&url).headers(headers.clone()).json(config);
|
||||
builder.send().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 根据clash info获取clash服务地址和请求头
|
||||
fn clash_client_info() -> Result<(String, HeaderMap)> {
|
||||
let info = { config::ClashN::global().info.lock().clone() };
|
||||
|
||||
if info.server.is_none() {
|
||||
let status = &info.status;
|
||||
if info.port.is_none() {
|
||||
bail!("failed to parse config.yaml file with status {status}");
|
||||
} else {
|
||||
bail!("failed to parse the server with status {status}");
|
||||
}
|
||||
}
|
||||
|
||||
let server = info.server.unwrap();
|
||||
let server = format!("http://{server}");
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("Content-Type", "application/json".parse()?);
|
||||
|
||||
if let Some(secret) = info.secret.as_ref() {
|
||||
let secret = format!("Bearer {}", secret.clone()).parse()?;
|
||||
headers.insert("Authorization", secret);
|
||||
}
|
||||
|
||||
Ok((server, headers))
|
||||
}
|
||||
262
src-tauri/src/core/core.rs
Normal file
262
src-tauri/src/core/core.rs
Normal file
@@ -0,0 +1,262 @@
|
||||
use super::{clash_api, logger::Logger};
|
||||
use crate::{
|
||||
config::*,
|
||||
enhance,
|
||||
utils::{self, dirs},
|
||||
};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::{fs, io::Write, sync::Arc, time::Duration};
|
||||
use sysinfo::{Pid, PidExt, ProcessExt, System, SystemExt};
|
||||
use tauri::api::process::{Command, CommandChild, CommandEvent};
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CoreManager {
|
||||
clash_core: Arc<Mutex<String>>,
|
||||
|
||||
sidecar: Arc<Mutex<Option<CommandChild>>>,
|
||||
|
||||
#[allow(unused)]
|
||||
use_service_mode: Arc<Mutex<bool>>,
|
||||
|
||||
pub runtime_config: Arc<Mutex<RuntimeResult>>,
|
||||
}
|
||||
|
||||
impl CoreManager {
|
||||
pub fn global() -> &'static CoreManager {
|
||||
static CORE_MANAGER: OnceCell<CoreManager> = OnceCell::new();
|
||||
|
||||
CORE_MANAGER.get_or_init(|| CoreManager {
|
||||
clash_core: Arc::new(Mutex::new("clash".into())),
|
||||
sidecar: Arc::new(Mutex::new(None)),
|
||||
runtime_config: Arc::new(Mutex::new(RuntimeResult::default())),
|
||||
use_service_mode: Arc::new(Mutex::new(false)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn init(&self) -> Result<()> {
|
||||
// kill old clash process
|
||||
if let Ok(pid) = fs::read(dirs::clash_pid_path()) {
|
||||
if let Ok(pid) = String::from_utf8_lossy(&pid).parse() {
|
||||
let mut system = System::new();
|
||||
system.refresh_all();
|
||||
system.process(Pid::from_u32(pid)).map(|proc| {
|
||||
if proc.name().contains("clash") {
|
||||
proc.kill();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 使用配置的核心
|
||||
let verge = VergeN::global().config.lock();
|
||||
if let Some(verge_core) = verge.clash_core.as_ref() {
|
||||
if verge_core == "clash" || verge_core == "clash-meta" {
|
||||
let mut clash_core = self.clash_core.lock();
|
||||
*clash_core = verge_core.clone();
|
||||
}
|
||||
}
|
||||
|
||||
// 启动clash
|
||||
self.run_core()?;
|
||||
|
||||
// 更新配置
|
||||
tauri::async_runtime::spawn(async {
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
crate::log_err!(Self::global().activate_config().await);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 检查配置是否正确
|
||||
pub fn check_config(&self) -> Result<()> {
|
||||
let config_path = dirs::clash_runtime_yaml();
|
||||
let config_path = dirs::path_to_str(&config_path)?;
|
||||
|
||||
let clash_core = { self.clash_core.lock().clone() };
|
||||
|
||||
let output = Command::new_sidecar(clash_core)?
|
||||
.args(["-t", config_path])
|
||||
.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
Logger::global().set_log(output.stderr.clone());
|
||||
bail!("{}", output.stderr); // 过滤掉终端颜色值
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 启动核心
|
||||
pub fn run_core(&self) -> Result<()> {
|
||||
// 先纠正重要的配置字段
|
||||
self.correct_config()?;
|
||||
|
||||
let mut sidecar = self.sidecar.lock();
|
||||
|
||||
if let Some(child) = sidecar.take() {
|
||||
let _ = child.kill();
|
||||
}
|
||||
|
||||
let app_dir = dirs::app_home_dir();
|
||||
let app_dir = dirs::path_to_str(&app_dir)?;
|
||||
|
||||
let clash_core = { self.clash_core.lock().clone() };
|
||||
|
||||
// fix #212
|
||||
let args = match clash_core.as_str() {
|
||||
"clash-meta" => vec!["-m", "-d", app_dir],
|
||||
_ => vec!["-d", app_dir],
|
||||
};
|
||||
|
||||
let cmd = Command::new_sidecar(clash_core)?;
|
||||
let (mut rx, cmd_child) = cmd.args(args).spawn()?;
|
||||
|
||||
// 将pid写入文件中
|
||||
crate::log_err!({
|
||||
let pid = cmd_child.pid();
|
||||
let path = dirs::clash_pid_path();
|
||||
fs::File::create(path)
|
||||
.context("failed to create the pid file")?
|
||||
.write(format!("{pid}").as_bytes())
|
||||
.context("failed to write pid to the file")?;
|
||||
<Result<()>>::Ok(())
|
||||
});
|
||||
|
||||
*sidecar = Some(cmd_child);
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
CommandEvent::Stdout(line) => {
|
||||
let can_short = line.starts_with("time=") && line.len() > 33;
|
||||
let stdout = if can_short { &line[33..] } else { &line };
|
||||
log::info!(target: "app" ,"[clash]: {}", stdout);
|
||||
Logger::global().set_log(line);
|
||||
}
|
||||
CommandEvent::Stderr(err) => {
|
||||
log::error!(target: "app" ,"[clash error]: {err}");
|
||||
Logger::global().set_log(err);
|
||||
}
|
||||
CommandEvent::Error(err) => {
|
||||
log::error!(target: "app" ,"[clash error]: {err}");
|
||||
Logger::global().set_log(err);
|
||||
}
|
||||
CommandEvent::Terminated(_) => {
|
||||
log::info!(target: "app" ,"clash core Terminated");
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 停止核心运行
|
||||
pub fn stop_core(&self) -> Result<()> {
|
||||
let mut sidecar = self.sidecar.lock();
|
||||
if let Some(child) = sidecar.take() {
|
||||
let _ = child.kill();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 切换核心
|
||||
pub async fn change_core(&self, clash_core: Option<String>) -> Result<()> {
|
||||
let clash_core = clash_core.ok_or(anyhow::anyhow!("clash core is null"))?;
|
||||
|
||||
if &clash_core != "clash" && &clash_core != "clash-meta" {
|
||||
bail!("invalid clash core name \"{clash_core}\"");
|
||||
}
|
||||
|
||||
// 清掉旧日志
|
||||
Logger::global().clear_log();
|
||||
|
||||
let mut self_core = self.clash_core.lock();
|
||||
let old_core = self_core.clone(); // 保存一下旧值
|
||||
*self_core = clash_core.clone();
|
||||
drop(self_core);
|
||||
|
||||
match self.run_core() {
|
||||
Ok(_) => {
|
||||
// 更新到配置文件
|
||||
let mut verge = VergeN::global().config.lock();
|
||||
verge.clash_core = Some(clash_core);
|
||||
drop(verge);
|
||||
|
||||
let _ = VergeN::global().save_file();
|
||||
|
||||
sleep(Duration::from_millis(100)).await; // 等一会儿再更新配置
|
||||
self.activate_config().await?;
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
// 恢复旧的值
|
||||
let mut self_core = self.clash_core.lock();
|
||||
*self_core = old_core;
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 纠正一下配置
|
||||
/// 将mixed-port和external-controller都改为配置的内容
|
||||
pub fn correct_config(&self) -> Result<()> {
|
||||
// todo!()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 激活一个配置
|
||||
pub async fn activate_config(&self) -> Result<()> {
|
||||
let clash_config = { ClashN::global().config.lock().clone() };
|
||||
|
||||
let tun_mode = { VergeN::global().config.lock().enable_tun_mode.clone() };
|
||||
let tun_mode = tun_mode.unwrap_or(false);
|
||||
|
||||
let pa = { ProfilesN::global().config.lock().gen_activate()? };
|
||||
|
||||
let (config, exists_keys, logs) =
|
||||
enhance::enhance_config(clash_config, pa.current, pa.chain, pa.valid, tun_mode);
|
||||
|
||||
// 保存到文件中
|
||||
let runtime_path = dirs::clash_runtime_yaml();
|
||||
utils::config::save_yaml(runtime_path, &config, Some("# Clash Verge Runtime Config"))?;
|
||||
|
||||
// 检查配置是否正常
|
||||
self.check_config()?;
|
||||
|
||||
// todo 是否需要检查核心是否运行
|
||||
|
||||
// 发送请求 发送5次
|
||||
for i in 0..5 {
|
||||
match clash_api::put_configs().await {
|
||||
Ok(_) => break,
|
||||
Err(err) => {
|
||||
if i < 4 {
|
||||
log::error!(target: "app", "{err}");
|
||||
} else {
|
||||
bail!(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
sleep(Duration::from_millis(250)).await;
|
||||
}
|
||||
|
||||
// 保存结果
|
||||
let mut runtime = self.runtime_config.lock();
|
||||
let config_yaml = Some(serde_yaml::to_string(&config).unwrap_or("".into()));
|
||||
*runtime = RuntimeResult {
|
||||
config: Some(config),
|
||||
config_yaml,
|
||||
exists_keys,
|
||||
chain_logs: logs,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
217
src-tauri/src/core/core_service.rs
Normal file
217
src-tauri/src/core/core_service.rs
Normal file
@@ -0,0 +1,217 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use crate::utils::{config, dirs};
|
||||
use anyhow::Context;
|
||||
use deelevate::{PrivilegeLevel, Token};
|
||||
use runas::Command as RunasCommand;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::{env::current_exe, process::Command as StdCommand};
|
||||
|
||||
const SERVICE_NAME: &str = "clash_verge_service";
|
||||
|
||||
const SERVICE_URL: &str = "http://127.0.0.1:33211";
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct ResponseBody {
|
||||
pub bin_path: String,
|
||||
pub config_dir: String,
|
||||
pub log_file: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
pub struct JsonResponse {
|
||||
pub code: u64,
|
||||
pub msg: String,
|
||||
pub data: Option<ResponseBody>,
|
||||
}
|
||||
|
||||
impl Service {
|
||||
/// Install the Clash Verge Service
|
||||
/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
|
||||
pub async fn install_service() -> Result<()> {
|
||||
let binary_path = dirs::service_path();
|
||||
let install_path = binary_path.with_file_name("install-service.exe");
|
||||
|
||||
if !install_path.exists() {
|
||||
bail!("installer exe not found");
|
||||
}
|
||||
|
||||
let token = Token::with_current_process()?;
|
||||
let level = token.privilege_level()?;
|
||||
|
||||
let status = match level {
|
||||
PrivilegeLevel::NotPrivileged => RunasCommand::new(install_path).status()?,
|
||||
_ => StdCommand::new(install_path)
|
||||
.creation_flags(0x08000000)
|
||||
.status()?,
|
||||
};
|
||||
|
||||
if !status.success() {
|
||||
bail!(
|
||||
"failed to install service with status {}",
|
||||
status.code().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Uninstall the Clash Verge Service
|
||||
/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
|
||||
pub async fn uninstall_service() -> Result<()> {
|
||||
let binary_path = dirs::service_path();
|
||||
let uninstall_path = binary_path.with_file_name("uninstall-service.exe");
|
||||
|
||||
if !uninstall_path.exists() {
|
||||
bail!("uninstaller exe not found");
|
||||
}
|
||||
|
||||
let token = Token::with_current_process()?;
|
||||
let level = token.privilege_level()?;
|
||||
|
||||
let status = match level {
|
||||
PrivilegeLevel::NotPrivileged => RunasCommand::new(uninstall_path).status()?,
|
||||
_ => StdCommand::new(uninstall_path)
|
||||
.creation_flags(0x08000000)
|
||||
.status()?,
|
||||
};
|
||||
|
||||
if !status.success() {
|
||||
bail!(
|
||||
"failed to uninstall service with status {}",
|
||||
status.code().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// [deprecated]
|
||||
/// start service
|
||||
/// 该函数应该在协程或者线程中执行,避免UAC弹窗阻塞主线程
|
||||
pub async fn start_service() -> Result<()> {
|
||||
let token = Token::with_current_process()?;
|
||||
let level = token.privilege_level()?;
|
||||
|
||||
let args = ["start", SERVICE_NAME];
|
||||
|
||||
let status = match level {
|
||||
PrivilegeLevel::NotPrivileged => RunasCommand::new("sc").args(&args).status()?,
|
||||
_ => StdCommand::new("sc").args(&args).status()?,
|
||||
};
|
||||
|
||||
match status.success() {
|
||||
true => Ok(()),
|
||||
false => bail!(
|
||||
"failed to start service with status {}",
|
||||
status.code().unwrap()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// stop service
|
||||
pub async fn stop_service() -> Result<()> {
|
||||
let url = format!("{SERVICE_URL}/stop_service");
|
||||
let res = reqwest::ClientBuilder::new()
|
||||
.no_proxy()
|
||||
.build()?
|
||||
.post(url)
|
||||
.send()
|
||||
.await?
|
||||
.json::<JsonResponse>()
|
||||
.await
|
||||
.context("failed to connect to the Clash Verge Service")?;
|
||||
|
||||
if res.code != 0 {
|
||||
bail!(res.msg);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// check the windows service status
|
||||
pub async fn check_service() -> Result<JsonResponse> {
|
||||
let url = format!("{SERVICE_URL}/get_clash");
|
||||
let response = reqwest::ClientBuilder::new()
|
||||
.no_proxy()
|
||||
.build()?
|
||||
.get(url)
|
||||
.send()
|
||||
.await?
|
||||
.json::<JsonResponse>()
|
||||
.await
|
||||
.context("failed to connect to the Clash Verge Service")?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// start the clash by service
|
||||
pub(super) async fn start_clash_by_service() -> Result<()> {
|
||||
let status = Self::check_service().await?;
|
||||
|
||||
if status.code == 0 {
|
||||
Self::stop_clash_by_service().await?;
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
|
||||
let clash_core = {
|
||||
let global = Data::global();
|
||||
let verge = global.verge.lock();
|
||||
verge.clash_core.clone().unwrap_or("clash".into())
|
||||
};
|
||||
|
||||
let clash_bin = format!("{clash_core}.exe");
|
||||
let bin_path = current_exe().unwrap().with_file_name(clash_bin);
|
||||
let bin_path = bin_path.as_os_str().to_str().unwrap();
|
||||
|
||||
let config_dir = dirs::app_home_dir();
|
||||
let config_dir = config_dir.as_os_str().to_str().unwrap();
|
||||
|
||||
let log_path = dirs::service_log_file();
|
||||
let log_path = log_path.as_os_str().to_str().unwrap();
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert("bin_path", bin_path);
|
||||
map.insert("config_dir", config_dir);
|
||||
map.insert("log_file", log_path);
|
||||
|
||||
let url = format!("{SERVICE_URL}/start_clash");
|
||||
let res = reqwest::ClientBuilder::new()
|
||||
.no_proxy()
|
||||
.build()?
|
||||
.post(url)
|
||||
.json(&map)
|
||||
.send()
|
||||
.await?
|
||||
.json::<JsonResponse>()
|
||||
.await
|
||||
.context("failed to connect to the Clash Verge Service")?;
|
||||
|
||||
if res.code != 0 {
|
||||
bail!(res.msg);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// stop the clash by service
|
||||
pub(super) async fn stop_clash_by_service() -> Result<()> {
|
||||
let url = format!("{SERVICE_URL}/stop_clash");
|
||||
let res = reqwest::ClientBuilder::new()
|
||||
.no_proxy()
|
||||
.build()?
|
||||
.post(url)
|
||||
.send()
|
||||
.await?
|
||||
.json::<JsonResponse>()
|
||||
.await
|
||||
.context("failed to connect to the Clash Verge Service")?;
|
||||
|
||||
if res.code != 0 {
|
||||
bail!(res.msg);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,77 @@
|
||||
use super::tray::Tray;
|
||||
use crate::log_if_err;
|
||||
use anyhow::{bail, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
use tauri::{AppHandle, Manager, Window};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Handle {
|
||||
pub app_handle: Option<AppHandle>,
|
||||
pub app_handle: Arc<Mutex<Option<AppHandle>>>,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
pub fn set_inner(&mut self, app_handle: AppHandle) {
|
||||
self.app_handle = Some(app_handle);
|
||||
pub fn global() -> &'static Handle {
|
||||
static HANDLE: OnceCell<Handle> = OnceCell::new();
|
||||
|
||||
HANDLE.get_or_init(|| Handle {
|
||||
app_handle: Arc::new(Mutex::new(None)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn init(&self, app_handle: AppHandle) {
|
||||
*self.app_handle.lock() = Some(app_handle);
|
||||
}
|
||||
|
||||
pub fn get_window(&self) -> Option<Window> {
|
||||
self.app_handle
|
||||
.lock()
|
||||
.as_ref()
|
||||
.map_or(None, |a| a.get_window("main"))
|
||||
}
|
||||
|
||||
pub fn refresh_clash(&self) {
|
||||
if let Some(window) = self.get_window() {
|
||||
pub fn refresh_clash() {
|
||||
if let Some(window) = Self::global().get_window() {
|
||||
log_if_err!(window.emit("verge://refresh-clash-config", "yes"));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refresh_verge(&self) {
|
||||
if let Some(window) = self.get_window() {
|
||||
pub fn refresh_verge() {
|
||||
if let Some(window) = Self::global().get_window() {
|
||||
log_if_err!(window.emit("verge://refresh-verge-config", "yes"));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn refresh_profiles(&self) {
|
||||
if let Some(window) = self.get_window() {
|
||||
pub fn refresh_profiles() {
|
||||
if let Some(window) = Self::global().get_window() {
|
||||
log_if_err!(window.emit("verge://refresh-profiles-config", "yes"));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notice_message(&self, status: String, msg: String) {
|
||||
if let Some(window) = self.get_window() {
|
||||
log_if_err!(window.emit("verge://notice-message", (status, msg)));
|
||||
pub fn notice_message<S: Into<String>, M: Into<String>>(status: S, msg: M) {
|
||||
if let Some(window) = Self::global().get_window() {
|
||||
log_if_err!(window.emit("verge://notice-message", (status.into(), msg.into())));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_systray(&self) -> Result<()> {
|
||||
if self.app_handle.is_none() {
|
||||
bail!("update_systray unhandle error");
|
||||
pub fn update_systray() -> Result<()> {
|
||||
let app_handle = Self::global().app_handle.lock();
|
||||
if app_handle.is_none() {
|
||||
bail!("update_systray unhandled error");
|
||||
}
|
||||
let app_handle = self.app_handle.as_ref().unwrap();
|
||||
Tray::update_systray(app_handle)?;
|
||||
Tray::update_systray(app_handle.as_ref().unwrap())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// update the system tray state
|
||||
pub fn update_systray_part(&self) -> Result<()> {
|
||||
if self.app_handle.is_none() {
|
||||
bail!("update_systray unhandle error");
|
||||
pub fn update_systray_part() -> Result<()> {
|
||||
let app_handle = Self::global().app_handle.lock();
|
||||
if app_handle.is_none() {
|
||||
bail!("update_systray unhandled error");
|
||||
}
|
||||
let app_handle = self.app_handle.as_ref().unwrap();
|
||||
Tray::update_part(app_handle)?;
|
||||
Tray::update_part(app_handle.as_ref().unwrap())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
use crate::{data::*, feat, log_if_err};
|
||||
use crate::{config, feat, log_err};
|
||||
use anyhow::{bail, Result};
|
||||
use std::collections::HashMap;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tauri::{AppHandle, GlobalShortcutManager};
|
||||
|
||||
pub struct Hotkey {
|
||||
current: Vec<String>, // 保存当前的热键设置
|
||||
manager: Option<AppHandle>,
|
||||
current: Arc<Mutex<Vec<String>>>, // 保存当前的热键设置
|
||||
|
||||
app_handle: Arc<Mutex<Option<AppHandle>>>,
|
||||
}
|
||||
|
||||
impl Hotkey {
|
||||
pub fn new() -> Hotkey {
|
||||
Hotkey {
|
||||
current: Vec::new(),
|
||||
manager: None,
|
||||
}
|
||||
pub fn global() -> &'static Hotkey {
|
||||
static HOTKEY: OnceCell<Hotkey> = OnceCell::new();
|
||||
|
||||
HOTKEY.get_or_init(|| Hotkey {
|
||||
current: Arc::new(Mutex::new(Vec::new())),
|
||||
app_handle: Arc::new(Mutex::new(None)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn init(&mut self, app_handle: AppHandle) -> Result<()> {
|
||||
self.manager = Some(app_handle);
|
||||
let data = Data::global();
|
||||
let verge = data.verge.lock();
|
||||
pub fn init(&self, app_handle: AppHandle) -> Result<()> {
|
||||
*self.app_handle.lock() = Some(app_handle);
|
||||
|
||||
let verge = config::VergeN::global().config.lock();
|
||||
|
||||
if let Some(hotkeys) = verge.hotkeys.as_ref() {
|
||||
for hotkey in hotkeys.iter() {
|
||||
@@ -28,25 +33,26 @@ impl Hotkey {
|
||||
let key = iter.next();
|
||||
|
||||
if func.is_some() && key.is_some() {
|
||||
log_if_err!(self.register(key.unwrap(), func.unwrap()));
|
||||
log_err!(self.register(key.unwrap(), func.unwrap()));
|
||||
} else {
|
||||
log::error!(target: "app", "invalid hotkey \"{}\":\"{}\"", key.unwrap_or("None"), func.unwrap_or("None"));
|
||||
}
|
||||
}
|
||||
self.current = hotkeys.clone();
|
||||
*self.current.lock() = hotkeys.clone();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_manager(&self) -> Result<impl GlobalShortcutManager> {
|
||||
if self.manager.is_none() {
|
||||
let app_handle = self.app_handle.lock();
|
||||
if app_handle.is_none() {
|
||||
bail!("failed to get hotkey manager");
|
||||
}
|
||||
Ok(self.manager.as_ref().unwrap().global_shortcut_manager())
|
||||
Ok(app_handle.as_ref().unwrap().global_shortcut_manager())
|
||||
}
|
||||
|
||||
fn register(&mut self, hotkey: &str, func: &str) -> Result<()> {
|
||||
fn register(&self, hotkey: &str, func: &str) -> Result<()> {
|
||||
let mut manager = self.get_manager()?;
|
||||
|
||||
if manager.is_registered(hotkey)? {
|
||||
@@ -54,16 +60,16 @@ impl Hotkey {
|
||||
}
|
||||
|
||||
let f = match func.trim() {
|
||||
"clash_mode_rule" => || feat::change_clash_mode("rule"),
|
||||
"clash_mode_global" => || feat::change_clash_mode("global"),
|
||||
"clash_mode_direct" => || feat::change_clash_mode("direct"),
|
||||
"clash_mode_script" => || feat::change_clash_mode("script"),
|
||||
"toggle_system_proxy" => || feat::toggle_system_proxy(),
|
||||
"enable_system_proxy" => || feat::enable_system_proxy(),
|
||||
"disable_system_proxy" => || feat::disable_system_proxy(),
|
||||
"toggle_tun_mode" => || feat::toggle_tun_mode(),
|
||||
"enable_tun_mode" => || feat::enable_tun_mode(),
|
||||
"disable_tun_mode" => || feat::disable_tun_mode(),
|
||||
"clash_mode_rule" => || feat::change_clash_mode("rule".into()),
|
||||
"clash_mode_global" => || feat::change_clash_mode("global".into()),
|
||||
"clash_mode_direct" => || feat::change_clash_mode("direct".into()),
|
||||
"clash_mode_script" => || feat::change_clash_mode("script".into()),
|
||||
"toggle_system_proxy" => || log_err!(feat::toggle_system_proxy()),
|
||||
"enable_system_proxy" => || log_err!(feat::enable_system_proxy()),
|
||||
"disable_system_proxy" => || log_err!(feat::disable_system_proxy()),
|
||||
"toggle_tun_mode" => || log_err!(feat::toggle_tun_mode()),
|
||||
"enable_tun_mode" => || log_err!(feat::enable_tun_mode()),
|
||||
"disable_tun_mode" => || log_err!(feat::disable_tun_mode()),
|
||||
|
||||
_ => bail!("invalid function \"{func}\""),
|
||||
};
|
||||
@@ -73,14 +79,14 @@ impl Hotkey {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister(&mut self, hotkey: &str) -> Result<()> {
|
||||
fn unregister(&self, hotkey: &str) -> Result<()> {
|
||||
self.get_manager()?.unregister(&hotkey)?;
|
||||
log::info!(target: "app", "unregister hotkey {hotkey}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update(&mut self, new_hotkeys: Vec<String>) -> Result<()> {
|
||||
let current = self.current.to_owned();
|
||||
pub fn update(&self, new_hotkeys: Vec<String>) -> Result<()> {
|
||||
let mut current = self.current.lock();
|
||||
let old_map = Self::get_map_from_vec(¤t);
|
||||
let new_map = Self::get_map_from_vec(&new_hotkeys);
|
||||
|
||||
@@ -91,10 +97,10 @@ impl Hotkey {
|
||||
});
|
||||
|
||||
add.iter().for_each(|(key, func)| {
|
||||
log_if_err!(self.register(key, func));
|
||||
log_err!(self.register(key, func));
|
||||
});
|
||||
|
||||
self.current = new_hotkeys;
|
||||
*current = new_hotkeys;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
36
src-tauri/src/core/logger.rs
Normal file
36
src-tauri/src/core/logger.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::{collections::VecDeque, sync::Arc};
|
||||
|
||||
const LOGS_QUEUE_LEN: usize = 100;
|
||||
|
||||
pub struct Logger {
|
||||
log_data: Arc<Mutex<VecDeque<String>>>,
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
pub fn global() -> &'static Logger {
|
||||
static LOGGER: OnceCell<Logger> = OnceCell::new();
|
||||
|
||||
LOGGER.get_or_init(|| Logger {
|
||||
log_data: Arc::new(Mutex::new(VecDeque::with_capacity(LOGS_QUEUE_LEN + 10))),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_log(&self) -> VecDeque<String> {
|
||||
self.log_data.lock().clone()
|
||||
}
|
||||
|
||||
pub fn set_log(&self, text: String) {
|
||||
let mut logs = self.log_data.lock();
|
||||
if logs.len() > LOGS_QUEUE_LEN {
|
||||
logs.pop_front();
|
||||
}
|
||||
logs.push_back(text);
|
||||
}
|
||||
|
||||
pub fn clear_log(&self) {
|
||||
let mut logs = self.log_data.lock();
|
||||
logs.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,349 +1,347 @@
|
||||
use self::handle::Handle;
|
||||
use self::hotkey::Hotkey;
|
||||
use self::sysopt::Sysopt;
|
||||
use self::timer::Timer;
|
||||
use crate::config::enhance_config;
|
||||
use crate::data::*;
|
||||
use crate::log_if_err;
|
||||
use anyhow::{bail, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use serde_yaml::{Mapping, Value};
|
||||
use std::sync::Arc;
|
||||
// use self::handle::Handle;
|
||||
// use self::hotkey::Hotkey;
|
||||
// use self::sysopt::Sysopt;
|
||||
// use self::timer::Timer;
|
||||
// // use crate::data::*;
|
||||
// // use crate::enhance::enhance_config;
|
||||
// use crate::log_if_err;
|
||||
// use anyhow::{bail, Result};
|
||||
// use once_cell::sync::OnceCell;
|
||||
// use parking_lot::Mutex;
|
||||
// use serde_yaml::{Mapping, Value};
|
||||
// use std::sync::Arc;
|
||||
|
||||
mod handle;
|
||||
mod hotkey;
|
||||
mod service;
|
||||
mod sysopt;
|
||||
mod timer;
|
||||
pub mod clash_api;
|
||||
mod core;
|
||||
pub mod core_service;
|
||||
pub mod handle;
|
||||
pub mod hotkey;
|
||||
pub mod logger;
|
||||
// pub mod service;
|
||||
pub mod sysopt;
|
||||
pub mod timer;
|
||||
pub mod tray;
|
||||
|
||||
pub use self::service::*;
|
||||
pub use self::core::*;
|
||||
// pub use self::service::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Core {
|
||||
pub service: Arc<Mutex<Service>>,
|
||||
pub sysopt: Arc<Mutex<Sysopt>>,
|
||||
pub timer: Arc<Mutex<Timer>>,
|
||||
pub hotkey: Arc<Mutex<Hotkey>>,
|
||||
pub runtime: Arc<Mutex<RuntimeResult>>,
|
||||
pub handle: Arc<Mutex<Handle>>,
|
||||
}
|
||||
pub struct Core {}
|
||||
|
||||
impl Core {
|
||||
pub fn global() -> &'static Core {
|
||||
static CORE: OnceCell<Core> = OnceCell::new();
|
||||
// pub fn global() -> &'static Core {
|
||||
// static CORE: OnceCell<Core> = OnceCell::new();
|
||||
|
||||
CORE.get_or_init(|| Core {
|
||||
service: Arc::new(Mutex::new(Service::new())),
|
||||
sysopt: Arc::new(Mutex::new(Sysopt::new())),
|
||||
timer: Arc::new(Mutex::new(Timer::new())),
|
||||
hotkey: Arc::new(Mutex::new(Hotkey::new())),
|
||||
runtime: Arc::new(Mutex::new(RuntimeResult::default())),
|
||||
handle: Arc::new(Mutex::new(Handle::default())),
|
||||
})
|
||||
}
|
||||
// CORE.get_or_init(|| Core {
|
||||
// service: Arc::new(Mutex::new(Service::new())),
|
||||
// sysopt: Arc::new(Mutex::new(Sysopt::new())),
|
||||
// timer: Arc::new(Mutex::new(Timer::new())),
|
||||
// hotkey: Arc::new(Mutex::new(Hotkey::new())),
|
||||
// runtime: Arc::new(Mutex::new(RuntimeResult::default())),
|
||||
// handle: Arc::new(Mutex::new(Handle::default())),
|
||||
// })
|
||||
// }
|
||||
|
||||
/// initialize the core state
|
||||
pub fn init(&self, app_handle: tauri::AppHandle) {
|
||||
// kill old clash process
|
||||
Service::kill_old_clash();
|
||||
// /// initialize the core state
|
||||
// pub fn init(&self, app_handle: tauri::AppHandle) {
|
||||
// kill old clash process
|
||||
// Service::kill_old_clash();
|
||||
|
||||
let mut handle = self.handle.lock();
|
||||
handle.set_inner(app_handle.clone());
|
||||
drop(handle);
|
||||
// let mut handle = self.handle.lock();
|
||||
// handle.set_inner(app_handle.clone());
|
||||
// drop(handle);
|
||||
|
||||
let mut service = self.service.lock();
|
||||
log_if_err!(service.start());
|
||||
drop(service);
|
||||
// let mut service = self.service.lock();
|
||||
// log_if_err!(service.start());
|
||||
// drop(service);
|
||||
|
||||
log_if_err!(self.activate());
|
||||
// log_if_err!(self.activate());
|
||||
|
||||
let mut sysopt = self.sysopt.lock();
|
||||
log_if_err!(sysopt.init_launch());
|
||||
log_if_err!(sysopt.init_sysproxy());
|
||||
drop(sysopt);
|
||||
// let mut sysopt = self.sysopt.lock();
|
||||
// log_if_err!(sysopt.init_launch());
|
||||
// log_if_err!(sysopt.init_sysproxy());
|
||||
// drop(sysopt);
|
||||
|
||||
let handle = self.handle.lock();
|
||||
log_if_err!(handle.update_systray_part());
|
||||
drop(handle);
|
||||
// let handle = self.handle.lock();
|
||||
// log_if_err!(handle.update_systray_part());
|
||||
// drop(handle);
|
||||
|
||||
let mut hotkey = self.hotkey.lock();
|
||||
log_if_err!(hotkey.init(app_handle));
|
||||
drop(hotkey);
|
||||
// let mut hotkey = self.hotkey.lock();
|
||||
// log_if_err!(hotkey.init(app_handle));
|
||||
// drop(hotkey);
|
||||
|
||||
// timer initialize
|
||||
let mut timer = self.timer.lock();
|
||||
log_if_err!(timer.restore());
|
||||
}
|
||||
// // timer initialize
|
||||
// let mut timer = self.timer.lock();
|
||||
// log_if_err!(timer.restore());
|
||||
// }
|
||||
|
||||
/// restart the clash sidecar
|
||||
pub fn restart_clash(&self) -> Result<()> {
|
||||
let mut service = self.service.lock();
|
||||
service.restart()?;
|
||||
drop(service);
|
||||
self.activate()
|
||||
}
|
||||
// /// restart the clash sidecar
|
||||
// pub fn restart_clash(&self) -> Result<()> {
|
||||
// let mut service = self.service.lock();
|
||||
// service.restart()?;
|
||||
// drop(service);
|
||||
// self.activate()
|
||||
// }
|
||||
|
||||
/// change the clash core
|
||||
pub fn change_core(&self, clash_core: Option<String>) -> Result<()> {
|
||||
let clash_core = clash_core.unwrap_or("clash".into());
|
||||
// /// change the clash core
|
||||
// pub fn change_core(&self, clash_core: Option<String>) -> Result<()> {
|
||||
// let clash_core = clash_core.unwrap_or("clash".into());
|
||||
|
||||
if &clash_core != "clash" && &clash_core != "clash-meta" {
|
||||
bail!("invalid clash core name \"{clash_core}\"");
|
||||
}
|
||||
// if &clash_core != "clash" && &clash_core != "clash-meta" {
|
||||
// bail!("invalid clash core name \"{clash_core}\"");
|
||||
// }
|
||||
|
||||
let global = Data::global();
|
||||
let mut verge = global.verge.lock();
|
||||
verge.patch_config(Verge {
|
||||
clash_core: Some(clash_core.clone()),
|
||||
..Verge::default()
|
||||
})?;
|
||||
drop(verge);
|
||||
// let global = Data::global();
|
||||
// let mut verge = global.verge.lock();
|
||||
// verge.patch_config(Verge {
|
||||
// clash_core: Some(clash_core.clone()),
|
||||
// ..Verge::default()
|
||||
// })?;
|
||||
// drop(verge);
|
||||
|
||||
let mut service = self.service.lock();
|
||||
service.clear_logs();
|
||||
service.restart()?;
|
||||
drop(service);
|
||||
// let mut service = self.service.lock();
|
||||
// service.clear_logs();
|
||||
// service.restart()?;
|
||||
// drop(service);
|
||||
|
||||
self.activate()
|
||||
}
|
||||
// self.activate()
|
||||
// }
|
||||
|
||||
/// Patch Clash
|
||||
/// handle the clash config changed
|
||||
pub fn patch_clash(&self, patch: Mapping) -> Result<()> {
|
||||
let patch_cloned = patch.clone();
|
||||
let clash_mode = patch.get("mode");
|
||||
let mixed_port = patch.get("mixed-port");
|
||||
let external = patch.get("external-controller");
|
||||
let secret = patch.get("secret");
|
||||
// /// Patch Clash
|
||||
// /// handle the clash config changed
|
||||
// pub fn patch_clash(&self, patch: Mapping) -> Result<()> {
|
||||
// let patch_cloned = patch.clone();
|
||||
// let clash_mode = patch.get("mode");
|
||||
// let mixed_port = patch.get("mixed-port");
|
||||
// let external = patch.get("external-controller");
|
||||
// let secret = patch.get("secret");
|
||||
|
||||
let valid_port = {
|
||||
let global = Data::global();
|
||||
let mut clash = global.clash.lock();
|
||||
clash.patch_config(patch_cloned)?;
|
||||
clash.info.port.is_some()
|
||||
};
|
||||
// let valid_port = {
|
||||
// let global = Data::global();
|
||||
// let mut clash = global.clash.lock();
|
||||
// clash.patch_config(patch_cloned)?;
|
||||
// clash.info.port.is_some()
|
||||
// };
|
||||
|
||||
// todo: port check
|
||||
if (mixed_port.is_some() && valid_port) || external.is_some() || secret.is_some() {
|
||||
let mut service = self.service.lock();
|
||||
service.restart()?;
|
||||
drop(service);
|
||||
// // todo: port check
|
||||
// if (mixed_port.is_some() && valid_port) || external.is_some() || secret.is_some() {
|
||||
// let mut service = self.service.lock();
|
||||
// service.restart()?;
|
||||
// drop(service);
|
||||
|
||||
self.activate()?;
|
||||
// self.activate()?;
|
||||
|
||||
let mut sysopt = self.sysopt.lock();
|
||||
sysopt.init_sysproxy()?;
|
||||
}
|
||||
// let mut sysopt = self.sysopt.lock();
|
||||
// sysopt.init_sysproxy()?;
|
||||
// }
|
||||
|
||||
if clash_mode.is_some() {
|
||||
let handle = self.handle.lock();
|
||||
handle.update_systray_part()?;
|
||||
}
|
||||
// if clash_mode.is_some() {
|
||||
// let handle = self.handle.lock();
|
||||
// handle.update_systray_part()?;
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/// Patch Verge
|
||||
pub fn patch_verge(&self, patch: Verge) -> Result<()> {
|
||||
// save the patch
|
||||
let global = Data::global();
|
||||
let mut verge = global.verge.lock();
|
||||
verge.patch_config(patch.clone())?;
|
||||
drop(verge);
|
||||
// /// Patch Verge
|
||||
// pub fn patch_verge(&self, patch: Verge) -> Result<()> {
|
||||
// // save the patch
|
||||
// let global = Data::global();
|
||||
// let mut verge = global.verge.lock();
|
||||
// verge.patch_config(patch.clone())?;
|
||||
// drop(verge);
|
||||
|
||||
let tun_mode = patch.enable_tun_mode;
|
||||
let auto_launch = patch.enable_auto_launch;
|
||||
let system_proxy = patch.enable_system_proxy;
|
||||
let proxy_bypass = patch.system_proxy_bypass;
|
||||
let proxy_guard = patch.enable_proxy_guard;
|
||||
let language = patch.language;
|
||||
// let tun_mode = patch.enable_tun_mode;
|
||||
// let auto_launch = patch.enable_auto_launch;
|
||||
// let system_proxy = patch.enable_system_proxy;
|
||||
// let proxy_bypass = patch.system_proxy_bypass;
|
||||
// let proxy_guard = patch.enable_proxy_guard;
|
||||
// let language = patch.language;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let service_mode = patch.enable_service_mode;
|
||||
// #[cfg(target_os = "windows")]
|
||||
// {
|
||||
// let service_mode = patch.enable_service_mode;
|
||||
|
||||
// 重启服务
|
||||
if service_mode.is_some() {
|
||||
let mut service = self.service.lock();
|
||||
service.restart()?;
|
||||
drop(service);
|
||||
}
|
||||
// // 重启服务
|
||||
// if service_mode.is_some() {
|
||||
// let mut service = self.service.lock();
|
||||
// service.restart()?;
|
||||
// drop(service);
|
||||
// }
|
||||
|
||||
if tun_mode.is_some() && *tun_mode.as_ref().unwrap_or(&false) {
|
||||
let wintun_dll = crate::utils::dirs::app_home_dir().join("wintun.dll");
|
||||
if !wintun_dll.exists() {
|
||||
bail!("failed to enable TUN for missing `wintun.dll`");
|
||||
}
|
||||
}
|
||||
// if tun_mode.is_some() && *tun_mode.as_ref().unwrap_or(&false) {
|
||||
// let wintun_dll = crate::utils::dirs::app_home_dir().join("wintun.dll");
|
||||
// if !wintun_dll.exists() {
|
||||
// bail!("failed to enable TUN for missing `wintun.dll`");
|
||||
// }
|
||||
// }
|
||||
|
||||
if service_mode.is_some() || tun_mode.is_some() {
|
||||
self.activate()?;
|
||||
}
|
||||
}
|
||||
// if service_mode.is_some() || tun_mode.is_some() {
|
||||
// self.activate()?;
|
||||
// }
|
||||
// }
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
if tun_mode.is_some() {
|
||||
self.activate()?;
|
||||
}
|
||||
// #[cfg(not(target_os = "windows"))]
|
||||
// if tun_mode.is_some() {
|
||||
// self.activate()?;
|
||||
// }
|
||||
|
||||
let mut sysopt = self.sysopt.lock();
|
||||
// let mut sysopt = self.sysopt.lock();
|
||||
|
||||
if auto_launch.is_some() {
|
||||
sysopt.update_launch()?;
|
||||
}
|
||||
if system_proxy.is_some() || proxy_bypass.is_some() {
|
||||
sysopt.update_sysproxy()?;
|
||||
sysopt.guard_proxy();
|
||||
}
|
||||
if proxy_guard.unwrap_or(false) {
|
||||
sysopt.guard_proxy();
|
||||
}
|
||||
// if auto_launch.is_some() {
|
||||
// sysopt.update_launch()?;
|
||||
// }
|
||||
// if system_proxy.is_some() || proxy_bypass.is_some() {
|
||||
// sysopt.update_sysproxy()?;
|
||||
// sysopt.guard_proxy();
|
||||
// }
|
||||
// if proxy_guard.unwrap_or(false) {
|
||||
// sysopt.guard_proxy();
|
||||
// }
|
||||
|
||||
// 更新tray
|
||||
if language.is_some() {
|
||||
let handle = self.handle.lock();
|
||||
handle.update_systray()?;
|
||||
} else if system_proxy.is_some() || tun_mode.is_some() {
|
||||
let handle = self.handle.lock();
|
||||
handle.update_systray_part()?;
|
||||
}
|
||||
// // 更新tray
|
||||
// if language.is_some() {
|
||||
// let handle = self.handle.lock();
|
||||
// handle.update_systray()?;
|
||||
// } else if system_proxy.is_some() || tun_mode.is_some() {
|
||||
// let handle = self.handle.lock();
|
||||
// handle.update_systray_part()?;
|
||||
// }
|
||||
|
||||
if patch.hotkeys.is_some() {
|
||||
let mut hotkey = self.hotkey.lock();
|
||||
hotkey.update(patch.hotkeys.unwrap())?;
|
||||
}
|
||||
// if patch.hotkeys.is_some() {
|
||||
// let mut hotkey = self.hotkey.lock();
|
||||
// hotkey.update(patch.hotkeys.unwrap())?;
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// update rule/global/direct/script mode
|
||||
pub fn update_mode(&self, mode: &str) -> Result<()> {
|
||||
// save config to file
|
||||
let info = {
|
||||
let global = Data::global();
|
||||
let mut clash = global.clash.lock();
|
||||
clash.config.insert(Value::from("mode"), Value::from(mode));
|
||||
clash.save_config()?;
|
||||
clash.info.clone()
|
||||
};
|
||||
// /// update rule/global/direct/script mode
|
||||
// pub fn update_mode(&self, mode: &str) -> Result<()> {
|
||||
// // save config to file
|
||||
// let info = {
|
||||
// let global = Data::global();
|
||||
// let mut clash = global.clash.lock();
|
||||
// clash.config.insert(Value::from("mode"), Value::from(mode));
|
||||
// clash.save_config()?;
|
||||
// clash.info.clone()
|
||||
// };
|
||||
|
||||
let mut mapping = Mapping::new();
|
||||
mapping.insert(Value::from("mode"), Value::from(mode));
|
||||
// let mut mapping = Mapping::new();
|
||||
// mapping.insert(Value::from("mode"), Value::from(mode));
|
||||
|
||||
let handle = self.handle.clone();
|
||||
// let handle = self.handle.clone();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
log_if_err!(Service::patch_config(info, mapping.to_owned()).await);
|
||||
// tauri::async_runtime::spawn(async move {
|
||||
// log_if_err!(Service::patch_config(info, mapping.to_owned()).await);
|
||||
|
||||
// update tray
|
||||
let handle = handle.lock();
|
||||
handle.refresh_clash();
|
||||
log_if_err!(handle.update_systray_part());
|
||||
});
|
||||
// // update tray
|
||||
// let handle = handle.lock();
|
||||
// handle.refresh_clash();
|
||||
// log_if_err!(handle.update_systray_part());
|
||||
// });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/// activate the profile
|
||||
/// auto activate enhanced profile
|
||||
/// 触发clash配置更新
|
||||
pub fn activate(&self) -> Result<()> {
|
||||
let global = Data::global();
|
||||
// /// activate the profile
|
||||
// /// auto activate enhanced profile
|
||||
// /// 触发clash配置更新
|
||||
// pub fn activate(&self) -> Result<()> {
|
||||
// let global = Data::global();
|
||||
|
||||
let verge = global.verge.lock();
|
||||
let clash = global.clash.lock();
|
||||
let profiles = global.profiles.lock();
|
||||
// let verge = global.verge.lock();
|
||||
// let clash = global.clash.lock();
|
||||
// let profiles = global.profiles.lock();
|
||||
|
||||
let tun_mode = verge.enable_tun_mode.clone().unwrap_or(false);
|
||||
let profile_activate = profiles.gen_activate()?;
|
||||
// let tun_mode = verge.enable_tun_mode.clone().unwrap_or(false);
|
||||
// let profile_activate = profiles.gen_activate()?;
|
||||
|
||||
let clash_config = clash.config.clone();
|
||||
let clash_info = clash.info.clone();
|
||||
// let clash_config = clash.config.clone();
|
||||
// let clash_info = clash.info.clone();
|
||||
|
||||
drop(clash);
|
||||
drop(verge);
|
||||
drop(profiles);
|
||||
// drop(clash);
|
||||
// drop(verge);
|
||||
// drop(profiles);
|
||||
|
||||
let (config, exists_keys, logs) = enhance_config(
|
||||
clash_config,
|
||||
profile_activate.current,
|
||||
profile_activate.chain,
|
||||
profile_activate.valid,
|
||||
tun_mode,
|
||||
);
|
||||
// let (config, exists_keys, logs) = enhance_config(
|
||||
// clash_config,
|
||||
// profile_activate.current,
|
||||
// profile_activate.chain,
|
||||
// profile_activate.valid,
|
||||
// tun_mode,
|
||||
// );
|
||||
|
||||
let mut runtime = self.runtime.lock();
|
||||
*runtime = RuntimeResult {
|
||||
config: Some(config.clone()),
|
||||
config_yaml: Some(serde_yaml::to_string(&config).unwrap_or("".into())),
|
||||
exists_keys,
|
||||
chain_logs: logs,
|
||||
};
|
||||
drop(runtime);
|
||||
// let mut runtime = self.runtime.lock();
|
||||
// *runtime = RuntimeResult {
|
||||
// config: Some(config.clone()),
|
||||
// config_yaml: Some(serde_yaml::to_string(&config).unwrap_or("".into())),
|
||||
// exists_keys,
|
||||
// chain_logs: logs,
|
||||
// };
|
||||
// drop(runtime);
|
||||
|
||||
let mut service = self.service.lock();
|
||||
service.check_start()?;
|
||||
drop(service);
|
||||
// let mut service = self.service.lock();
|
||||
// service.check_start()?;
|
||||
// drop(service);
|
||||
|
||||
let handle = self.handle.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
match Service::set_config(clash_info, config).await {
|
||||
Ok(_) => {
|
||||
let handle = handle.lock();
|
||||
handle.refresh_clash();
|
||||
handle.notice_message("set_config::ok".into(), "ok".into());
|
||||
}
|
||||
Err(err) => {
|
||||
let handle = handle.lock();
|
||||
handle.notice_message("set_config::error".into(), format!("{err}"));
|
||||
log::error!(target: "app", "{err}")
|
||||
}
|
||||
}
|
||||
});
|
||||
// let handle = self.handle.clone();
|
||||
// tauri::async_runtime::spawn(async move {
|
||||
// match Service::set_config(clash_info, config).await {
|
||||
// Ok(_) => {
|
||||
// let handle = handle.lock();
|
||||
// handle.refresh_clash();
|
||||
// handle.notice_message("set_config::ok".into(), "ok".into());
|
||||
// }
|
||||
// Err(err) => {
|
||||
// let handle = handle.lock();
|
||||
// handle.notice_message("set_config::error".into(), format!("{err}"));
|
||||
// log::error!(target: "app", "{err}")
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/// Static function
|
||||
/// update profile item
|
||||
pub async fn update_profile_item(&self, uid: String, option: Option<PrfOption>) -> Result<()> {
|
||||
let global = Data::global();
|
||||
// /// Static function
|
||||
// /// update profile item
|
||||
// pub async fn update_profile_item(&self, uid: String, option: Option<PrfOption>) -> Result<()> {
|
||||
// let global = Data::global();
|
||||
|
||||
let (url, opt) = {
|
||||
let profiles = global.profiles.lock();
|
||||
let item = profiles.get_item(&uid)?;
|
||||
// let (url, opt) = {
|
||||
// let profiles = global.profiles.lock();
|
||||
// let item = profiles.get_item(&uid)?;
|
||||
|
||||
if let Some(typ) = item.itype.as_ref() {
|
||||
// maybe only valid for `local` profile
|
||||
if *typ != "remote" {
|
||||
// reactivate the config
|
||||
if Some(uid) == profiles.get_current() {
|
||||
drop(profiles);
|
||||
self.activate()?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if item.url.is_none() {
|
||||
bail!("failed to get the profile item url");
|
||||
}
|
||||
(item.url.clone().unwrap(), item.option.clone())
|
||||
};
|
||||
// if let Some(typ) = item.itype.as_ref() {
|
||||
// // maybe only valid for `local` profile
|
||||
// if *typ != "remote" {
|
||||
// // reactivate the config
|
||||
// if Some(uid) == profiles.get_current() {
|
||||
// drop(profiles);
|
||||
// self.activate()?;
|
||||
// }
|
||||
// return Ok(());
|
||||
// }
|
||||
// }
|
||||
// if item.url.is_none() {
|
||||
// bail!("failed to get the profile item url");
|
||||
// }
|
||||
// (item.url.clone().unwrap(), item.option.clone())
|
||||
// };
|
||||
|
||||
let merged_opt = PrfOption::merge(opt, option);
|
||||
let item = PrfItem::from_url(&url, None, None, merged_opt).await?;
|
||||
// let merged_opt = PrfOption::merge(opt, option);
|
||||
// let item = PrfItem::from_url(&url, None, None, merged_opt).await?;
|
||||
|
||||
let mut profiles = global.profiles.lock();
|
||||
profiles.update_item(uid.clone(), item)?;
|
||||
// let mut profiles = global.profiles.lock();
|
||||
// profiles.update_item(uid.clone(), item)?;
|
||||
|
||||
// reactivate the profile
|
||||
if Some(uid) == profiles.get_current() {
|
||||
drop(profiles);
|
||||
self.activate()?;
|
||||
}
|
||||
// // reactivate the profile
|
||||
// if Some(uid) == profiles.get_current() {
|
||||
// drop(profiles);
|
||||
// self.activate()?;
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ impl Service {
|
||||
/// update clash config
|
||||
/// using PUT methods
|
||||
pub async fn set_config(info: ClashInfo, config: Mapping) -> Result<()> {
|
||||
let temp_path = dirs::profiles_temp_path();
|
||||
let temp_path = dirs::clash_runtime_yaml();
|
||||
config::save_yaml(temp_path.clone(), &config, Some("# Clash Verge Temp File"))?;
|
||||
|
||||
let (server, headers) = Self::clash_client_info(info)?;
|
||||
@@ -222,13 +222,13 @@ impl Service {
|
||||
data.insert("path", temp_path.as_os_str().to_str().unwrap());
|
||||
|
||||
macro_rules! report_err {
|
||||
($i: expr, $e: expr) => {
|
||||
match $i {
|
||||
4 => bail!($e),
|
||||
_ => log::error!(target: "app", $e),
|
||||
($i: expr, $e: expr) => {
|
||||
match $i {
|
||||
4 => bail!($e),
|
||||
_ => log::error!(target: "app", $e),
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// retry 5 times
|
||||
for i in 0..5 {
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
use crate::{data::*, log_if_err};
|
||||
use crate::{config, log_err};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use auto_launch::{AutoLaunch, AutoLaunchBuilder};
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
use sysproxy::Sysproxy;
|
||||
use tauri::{async_runtime::Mutex, utils::platform::current_exe};
|
||||
use tauri::{async_runtime::Mutex as TokioMutex, utils::platform::current_exe};
|
||||
|
||||
pub struct Sysopt {
|
||||
/// current system proxy setting
|
||||
cur_sysproxy: Option<Sysproxy>,
|
||||
cur_sysproxy: Arc<Mutex<Option<Sysproxy>>>,
|
||||
|
||||
/// record the original system proxy
|
||||
/// recover it when exit
|
||||
old_sysproxy: Option<Sysproxy>,
|
||||
old_sysproxy: Arc<Mutex<Option<Sysproxy>>>,
|
||||
|
||||
/// helps to auto launch the app
|
||||
auto_launch: Option<AutoLaunch>,
|
||||
auto_launch: Arc<Mutex<Option<AutoLaunch>>>,
|
||||
|
||||
/// record whether the guard async is running or not
|
||||
guard_state: Arc<Mutex<bool>>,
|
||||
guard_state: Arc<TokioMutex<bool>>,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
@@ -28,44 +30,45 @@ static DEFAULT_BYPASS: &str = "localhost,127.0.0.1/8,::1";
|
||||
static DEFAULT_BYPASS: &str = "127.0.0.1,localhost,<local>";
|
||||
|
||||
impl Sysopt {
|
||||
pub fn new() -> Sysopt {
|
||||
Sysopt {
|
||||
cur_sysproxy: None,
|
||||
old_sysproxy: None,
|
||||
auto_launch: None,
|
||||
guard_state: Arc::new(Mutex::new(false)),
|
||||
}
|
||||
pub fn global() -> &'static Sysopt {
|
||||
static SYSOPT: OnceCell<Sysopt> = OnceCell::new();
|
||||
|
||||
SYSOPT.get_or_init(|| Sysopt {
|
||||
cur_sysproxy: Arc::new(Mutex::new(None)),
|
||||
old_sysproxy: Arc::new(Mutex::new(None)),
|
||||
auto_launch: Arc::new(Mutex::new(None)),
|
||||
guard_state: Arc::new(TokioMutex::new(false)),
|
||||
})
|
||||
}
|
||||
|
||||
/// init the sysproxy
|
||||
pub fn init_sysproxy(&mut self) -> Result<()> {
|
||||
let data = Data::global();
|
||||
let clash = data.clash.lock();
|
||||
let port = clash.info.port.clone();
|
||||
pub fn init_sysproxy(&self) -> Result<()> {
|
||||
let port = { config::ClashN::global().info.lock().port.clone() };
|
||||
|
||||
if port.is_none() {
|
||||
bail!("clash port is none");
|
||||
}
|
||||
|
||||
let verge = data.verge.lock();
|
||||
let port = port.unwrap().parse::<u16>()?;
|
||||
|
||||
let verge = config::VergeN::global().config.lock();
|
||||
let enable = verge.enable_system_proxy.clone().unwrap_or(false);
|
||||
let bypass = verge.system_proxy_bypass.clone();
|
||||
let bypass = bypass.unwrap_or(DEFAULT_BYPASS.into());
|
||||
|
||||
let port = port.unwrap().parse::<u16>()?;
|
||||
let host = String::from("127.0.0.1");
|
||||
|
||||
self.cur_sysproxy = Some(Sysproxy {
|
||||
let current = Sysproxy {
|
||||
enable,
|
||||
host,
|
||||
host: String::from("127.0.0.1"),
|
||||
port,
|
||||
bypass,
|
||||
});
|
||||
};
|
||||
|
||||
if enable {
|
||||
self.old_sysproxy = Sysproxy::get_system_proxy().map_or(None, |p| Some(p));
|
||||
self.cur_sysproxy.as_ref().unwrap().set_system_proxy()?;
|
||||
let old = Sysproxy::get_system_proxy().map_or(None, |p| Some(p));
|
||||
current.set_system_proxy()?;
|
||||
|
||||
*self.old_sysproxy.lock() = old;
|
||||
*self.cur_sysproxy.lock() = Some(current);
|
||||
}
|
||||
|
||||
// run the system proxy guard
|
||||
@@ -74,37 +77,44 @@ impl Sysopt {
|
||||
}
|
||||
|
||||
/// update the system proxy
|
||||
pub fn update_sysproxy(&mut self) -> Result<()> {
|
||||
if self.cur_sysproxy.is_none() || self.old_sysproxy.is_none() {
|
||||
pub fn update_sysproxy(&self) -> Result<()> {
|
||||
let mut cur_sysproxy = self.cur_sysproxy.lock();
|
||||
let old_sysproxy = self.old_sysproxy.lock();
|
||||
|
||||
if cur_sysproxy.is_none() || old_sysproxy.is_none() {
|
||||
drop(cur_sysproxy);
|
||||
drop(old_sysproxy);
|
||||
return self.init_sysproxy();
|
||||
}
|
||||
|
||||
let data = Data::global();
|
||||
let verge = data.verge.lock();
|
||||
let verge = config::VergeN::global().config.lock();
|
||||
|
||||
let enable = verge.enable_system_proxy.clone().unwrap_or(false);
|
||||
let bypass = verge.system_proxy_bypass.clone();
|
||||
let bypass = bypass.unwrap_or(DEFAULT_BYPASS.into());
|
||||
|
||||
let mut sysproxy = self.cur_sysproxy.take().unwrap();
|
||||
let mut sysproxy = cur_sysproxy.take().unwrap();
|
||||
|
||||
sysproxy.enable = enable;
|
||||
sysproxy.bypass = bypass;
|
||||
|
||||
self.cur_sysproxy = Some(sysproxy);
|
||||
self.cur_sysproxy.as_ref().unwrap().set_system_proxy()?;
|
||||
sysproxy.set_system_proxy()?;
|
||||
*cur_sysproxy = Some(sysproxy);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// reset the sysproxy
|
||||
pub fn reset_sysproxy(&mut self) -> Result<()> {
|
||||
let cur = self.cur_sysproxy.take();
|
||||
pub fn reset_sysproxy(&self) -> Result<()> {
|
||||
let mut cur_sysproxy = self.cur_sysproxy.lock();
|
||||
let mut old_sysproxy = self.old_sysproxy.lock();
|
||||
|
||||
if let Some(mut old) = self.old_sysproxy.take() {
|
||||
let cur_sysproxy = cur_sysproxy.take();
|
||||
|
||||
if let Some(mut old) = old_sysproxy.take() {
|
||||
// 如果原代理和当前代理 端口一致,就disable关闭,否则就恢复原代理设置
|
||||
// 当前没有设置代理的时候,不确定旧设置是否和当前一致,全关了
|
||||
let port_same = cur.map_or(true, |cur| old.port == cur.port);
|
||||
let port_same = cur_sysproxy.map_or(true, |cur| old.port == cur.port);
|
||||
|
||||
if old.enable && port_same {
|
||||
old.enable = false;
|
||||
@@ -114,7 +124,7 @@ impl Sysopt {
|
||||
}
|
||||
|
||||
old.set_system_proxy()?;
|
||||
} else if let Some(mut cur @ Sysproxy { enable: true, .. }) = cur {
|
||||
} else if let Some(mut cur @ Sysproxy { enable: true, .. }) = cur_sysproxy {
|
||||
// 没有原代理,就按现在的代理设置disable即可
|
||||
log::info!(target: "app", "reset proxy by disabling the current proxy");
|
||||
cur.enable = false;
|
||||
@@ -127,9 +137,8 @@ impl Sysopt {
|
||||
}
|
||||
|
||||
/// init the auto launch
|
||||
pub fn init_launch(&mut self) -> Result<()> {
|
||||
let data = Data::global();
|
||||
let verge = data.verge.lock();
|
||||
pub fn init_launch(&self) -> Result<()> {
|
||||
let verge = config::VergeN::global().config.lock();
|
||||
let enable = verge.enable_auto_launch.clone().unwrap_or(false);
|
||||
|
||||
let app_exe = current_exe()?;
|
||||
@@ -167,51 +176,41 @@ impl Sysopt {
|
||||
.set_app_path(&app_path)
|
||||
.build()?;
|
||||
|
||||
self.auto_launch = Some(auto);
|
||||
|
||||
// 避免在开发时将自启动关了
|
||||
#[cfg(feature = "verge-dev")]
|
||||
if !enable {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let auto = self.auto_launch.as_ref().unwrap();
|
||||
|
||||
// macos每次启动都更新登录项,避免重复设置登录项
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let _ = auto.disable();
|
||||
if enable {
|
||||
auto.enable()?;
|
||||
}
|
||||
}
|
||||
let _ = auto.disable();
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
match enable {
|
||||
true => auto.enable()?,
|
||||
false => auto.disable()?,
|
||||
};
|
||||
if enable {
|
||||
auto.enable()?;
|
||||
}
|
||||
*self.auto_launch.lock() = Some(auto);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// update the startup
|
||||
pub fn update_launch(&mut self) -> Result<()> {
|
||||
if self.auto_launch.is_none() {
|
||||
pub fn update_launch(&self) -> Result<()> {
|
||||
let auto_launch = self.auto_launch.lock();
|
||||
|
||||
if auto_launch.is_none() {
|
||||
drop(auto_launch);
|
||||
return self.init_launch();
|
||||
}
|
||||
|
||||
let data = Data::global();
|
||||
let verge = data.verge.lock();
|
||||
let verge = config::VergeN::global().config.lock();
|
||||
let enable = verge.enable_auto_launch.clone().unwrap_or(false);
|
||||
|
||||
let auto_launch = self.auto_launch.as_ref().unwrap();
|
||||
let auto_launch = auto_launch.as_ref().unwrap();
|
||||
|
||||
match enable {
|
||||
true => auto_launch.enable()?,
|
||||
false => crate::log_if_err!(auto_launch.disable()), // 忽略关闭的错误
|
||||
false => log_err!(auto_launch.disable()), // 忽略关闭的错误
|
||||
};
|
||||
|
||||
Ok(())
|
||||
@@ -239,8 +238,7 @@ impl Sysopt {
|
||||
loop {
|
||||
sleep(Duration::from_secs(wait_secs)).await;
|
||||
|
||||
let global = Data::global();
|
||||
let verge = global.verge.lock();
|
||||
let verge = config::VergeN::global().config.lock();
|
||||
|
||||
let enable = verge.enable_system_proxy.clone().unwrap_or(false);
|
||||
let guard = verge.enable_proxy_guard.clone().unwrap_or(false);
|
||||
@@ -256,14 +254,10 @@ impl Sysopt {
|
||||
// update duration
|
||||
wait_secs = guard_duration;
|
||||
|
||||
let clash = global.clash.lock();
|
||||
let port = clash.info.port.clone();
|
||||
let port = port.unwrap_or("".into()).parse::<u16>();
|
||||
drop(clash);
|
||||
|
||||
log::debug!(target: "app", "try to guard the system proxy");
|
||||
|
||||
match port {
|
||||
let port = { config::ClashN::global().info.lock().port.clone() };
|
||||
match port.unwrap_or("".into()).parse::<u16>() {
|
||||
Ok(port) => {
|
||||
let sysproxy = Sysproxy {
|
||||
enable: true,
|
||||
@@ -272,14 +266,17 @@ impl Sysopt {
|
||||
bypass: bypass.unwrap_or(DEFAULT_BYPASS.into()),
|
||||
};
|
||||
|
||||
log_if_err!(sysproxy.set_system_proxy());
|
||||
log_err!(sysproxy.set_system_proxy());
|
||||
}
|
||||
Err(_) => {
|
||||
log::error!(target: "app", "failed to parse clash port in guard proxy")
|
||||
}
|
||||
Err(_) => log::error!(target: "app", "failed to parse clash port"),
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = guard_state.lock().await;
|
||||
*state = false;
|
||||
drop(state);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,96 @@
|
||||
use super::Core;
|
||||
use crate::utils::help::get_now;
|
||||
use crate::{data::Data, log_if_err};
|
||||
use crate::config::{self, ProfilesN};
|
||||
use anyhow::{Context, Result};
|
||||
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
type TaskID = u64;
|
||||
|
||||
pub struct Timer {
|
||||
/// cron manager
|
||||
delay_timer: DelayTimer,
|
||||
delay_timer: Arc<Mutex<DelayTimer>>,
|
||||
|
||||
/// save the current state
|
||||
timer_map: HashMap<String, (TaskID, u64)>,
|
||||
timer_map: Arc<Mutex<HashMap<String, (TaskID, u64)>>>,
|
||||
|
||||
/// increment id
|
||||
timer_count: TaskID,
|
||||
timer_count: Arc<Mutex<TaskID>>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
pub fn new() -> Self {
|
||||
Timer {
|
||||
delay_timer: DelayTimerBuilder::default().build(),
|
||||
timer_map: HashMap::new(),
|
||||
timer_count: 1,
|
||||
}
|
||||
pub fn global() -> &'static Timer {
|
||||
static TIMER: OnceCell<Timer> = OnceCell::new();
|
||||
|
||||
TIMER.get_or_init(|| Timer {
|
||||
delay_timer: Arc::new(Mutex::new(DelayTimerBuilder::default().build())),
|
||||
timer_map: Arc::new(Mutex::new(HashMap::new())),
|
||||
timer_count: Arc::new(Mutex::new(1)),
|
||||
})
|
||||
}
|
||||
|
||||
/// restore timer
|
||||
pub fn init(&self) -> Result<()> {
|
||||
self.refresh()?;
|
||||
|
||||
let cur_timestamp = chrono::Local::now().timestamp();
|
||||
|
||||
let profiles = config::ProfilesN::global().config.lock();
|
||||
|
||||
let timer_map = self.timer_map.lock();
|
||||
let delay_timer = self.delay_timer.lock();
|
||||
|
||||
profiles.get_items().map(|items| {
|
||||
items
|
||||
.iter()
|
||||
// .filter_map(|item| {
|
||||
// item.uid.is_some() && item.updated.is_some() && item.option.is_some()
|
||||
// })
|
||||
.filter_map(|item| {
|
||||
// mins to seconds
|
||||
let interval = ((item.option.as_ref()?.update_interval?) as i64) * 60;
|
||||
let updated = item.updated? as i64;
|
||||
|
||||
if interval > 0 && cur_timestamp - updated >= interval {
|
||||
Some(item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.for_each(|item| {
|
||||
if let Some(uid) = item.uid.as_ref() {
|
||||
if let Some((task_id, _)) = timer_map.get(uid) {
|
||||
crate::log_err!(delay_timer.advance_task(*task_id));
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Correctly update all cron tasks
|
||||
pub fn refresh(&mut self) -> Result<()> {
|
||||
pub fn refresh(&self) -> Result<()> {
|
||||
let diff_map = self.gen_diff();
|
||||
|
||||
let mut timer_map = self.timer_map.lock();
|
||||
let delay_timer = self.delay_timer.lock();
|
||||
|
||||
for (uid, diff) in diff_map.into_iter() {
|
||||
match diff {
|
||||
DiffFlag::Del(tid) => {
|
||||
let _ = self.timer_map.remove(&uid);
|
||||
log_if_err!(self.delay_timer.remove_task(tid));
|
||||
let _ = timer_map.remove(&uid);
|
||||
crate::log_err!(delay_timer.remove_task(tid));
|
||||
}
|
||||
DiffFlag::Add(tid, val) => {
|
||||
let _ = self.timer_map.insert(uid.clone(), (tid, val));
|
||||
log_if_err!(self.add_task(uid, tid, val));
|
||||
let _ = timer_map.insert(uid.clone(), (tid, val));
|
||||
crate::log_err!(self.add_task(uid, tid, val));
|
||||
}
|
||||
DiffFlag::Mod(tid, val) => {
|
||||
let _ = self.timer_map.insert(uid.clone(), (tid, val));
|
||||
log_if_err!(self.delay_timer.remove_task(tid));
|
||||
log_if_err!(self.add_task(uid, tid, val));
|
||||
let _ = timer_map.insert(uid.clone(), (tid, val));
|
||||
crate::log_err!(delay_timer.remove_task(tid));
|
||||
crate::log_err!(self.add_task(uid, tid, val));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,41 +98,9 @@ impl Timer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// restore timer
|
||||
pub fn restore(&mut self) -> Result<()> {
|
||||
self.refresh()?;
|
||||
|
||||
let cur_timestamp = get_now(); // seconds
|
||||
|
||||
let global = Data::global();
|
||||
let profiles = global.profiles.lock();
|
||||
|
||||
profiles
|
||||
.get_items()
|
||||
.unwrap_or(&vec![])
|
||||
.iter()
|
||||
.filter(|item| item.uid.is_some() && item.updated.is_some() && item.option.is_some())
|
||||
.filter(|item| {
|
||||
// mins to seconds
|
||||
let interval =
|
||||
item.option.as_ref().unwrap().update_interval.unwrap_or(0) as usize * 60;
|
||||
let updated = item.updated.unwrap();
|
||||
return interval > 0 && cur_timestamp - updated >= interval;
|
||||
})
|
||||
.for_each(|item| {
|
||||
let uid = item.uid.as_ref().unwrap();
|
||||
if let Some((task_id, _)) = self.timer_map.get(uid) {
|
||||
log_if_err!(self.delay_timer.advance_task(*task_id));
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// generate a uid -> update_interval map
|
||||
fn gen_map(&self) -> HashMap<String, u64> {
|
||||
let global = Data::global();
|
||||
let profiles = global.profiles.lock();
|
||||
let profiles = config::ProfilesN::global().config.lock();
|
||||
|
||||
let mut new_map = HashMap::new();
|
||||
|
||||
@@ -107,11 +121,13 @@ impl Timer {
|
||||
}
|
||||
|
||||
/// generate the diff map for refresh
|
||||
fn gen_diff(&mut self) -> HashMap<String, DiffFlag> {
|
||||
fn gen_diff(&self) -> HashMap<String, DiffFlag> {
|
||||
let mut diff_map = HashMap::new();
|
||||
|
||||
let timer_map = self.timer_map.lock();
|
||||
|
||||
let new_map = self.gen_map();
|
||||
let cur_map = &self.timer_map;
|
||||
let cur_map = &timer_map;
|
||||
|
||||
cur_map.iter().for_each(|(uid, (tid, val))| {
|
||||
let new_val = new_map.get(uid).unwrap_or(&0);
|
||||
@@ -123,34 +139,31 @@ impl Timer {
|
||||
}
|
||||
});
|
||||
|
||||
let mut count = self.timer_count;
|
||||
let mut count = self.timer_count.lock();
|
||||
|
||||
new_map.iter().for_each(|(uid, val)| {
|
||||
if cur_map.get(uid).is_none() {
|
||||
diff_map.insert(uid.clone(), DiffFlag::Add(count, *val));
|
||||
diff_map.insert(uid.clone(), DiffFlag::Add(*count, *val));
|
||||
|
||||
count += 1;
|
||||
*count += 1;
|
||||
}
|
||||
});
|
||||
|
||||
self.timer_count = count;
|
||||
|
||||
diff_map
|
||||
}
|
||||
|
||||
/// add a cron task
|
||||
fn add_task(&self, uid: String, tid: TaskID, minutes: u64) -> Result<()> {
|
||||
let core = Core::global();
|
||||
|
||||
let task = TaskBuilder::default()
|
||||
.set_task_id(tid)
|
||||
.set_maximum_parallel_runnable_num(1)
|
||||
.set_frequency_repeated_by_minutes(minutes)
|
||||
// .set_frequency_repeated_by_seconds(minutes) // for test
|
||||
.spawn_async_routine(move || Self::async_task(core.to_owned(), uid.to_owned()))
|
||||
.spawn_async_routine(move || Self::async_task(uid.to_owned()))
|
||||
.context("failed to create timer task")?;
|
||||
|
||||
self.delay_timer
|
||||
.lock()
|
||||
.add_task(task)
|
||||
.context("failed to add timer task")?;
|
||||
|
||||
@@ -158,9 +171,9 @@ impl Timer {
|
||||
}
|
||||
|
||||
/// the task runner
|
||||
async fn async_task(core: Core, uid: String) {
|
||||
async fn async_task(uid: String) {
|
||||
log::info!(target: "app", "running timer task `{uid}`");
|
||||
log_if_err!(core.update_profile_item(uid, None).await);
|
||||
crate::log_err!(ProfilesN::global().update_item(uid, None).await);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::{data::Data, feat, utils::resolve};
|
||||
use anyhow::{Ok, Result};
|
||||
use crate::log_err;
|
||||
use crate::{config, feat, utils::resolve};
|
||||
use anyhow::Result;
|
||||
use tauri::{
|
||||
api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem,
|
||||
SystemTraySubmenu,
|
||||
@@ -9,14 +10,16 @@ pub struct Tray {}
|
||||
|
||||
impl Tray {
|
||||
pub fn tray_menu(app_handle: &AppHandle) -> SystemTrayMenu {
|
||||
let data = Data::global();
|
||||
let zh = {
|
||||
let verge = data.verge.lock();
|
||||
let verge = config::VergeN::global().config.lock();
|
||||
verge.language == Some("zh".into())
|
||||
};
|
||||
|
||||
let version = app_handle.package_info().version.to_string();
|
||||
|
||||
dbg!(&zh);
|
||||
dbg!(&version);
|
||||
|
||||
if zh {
|
||||
SystemTrayMenu::new()
|
||||
.add_item(CustomMenuItem::new("open_window", "打开面板"))
|
||||
@@ -75,13 +78,14 @@ impl Tray {
|
||||
}
|
||||
|
||||
pub fn update_part(app_handle: &AppHandle) -> Result<()> {
|
||||
let global = Data::global();
|
||||
let clash = global.clash.lock();
|
||||
let mode = clash
|
||||
.config
|
||||
.get(&serde_yaml::Value::from("mode"))
|
||||
.map(|val| val.as_str().unwrap_or("rule"))
|
||||
.unwrap_or("rule");
|
||||
let mode = {
|
||||
let clash = config::ClashN::global().config.lock();
|
||||
clash
|
||||
.get("mode")
|
||||
.map(|val| val.as_str().unwrap_or("rule"))
|
||||
.unwrap_or("rule")
|
||||
.to_owned()
|
||||
};
|
||||
|
||||
let tray = app_handle.tray_handle();
|
||||
|
||||
@@ -90,7 +94,7 @@ impl Tray {
|
||||
let _ = tray.get_item("direct_mode").set_selected(mode == "direct");
|
||||
let _ = tray.get_item("script_mode").set_selected(mode == "script");
|
||||
|
||||
let verge = global.verge.lock();
|
||||
let verge = config::VergeN::global().config.lock();
|
||||
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
|
||||
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
|
||||
|
||||
@@ -105,12 +109,12 @@ impl Tray {
|
||||
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
|
||||
mode @ ("rule_mode" | "global_mode" | "direct_mode" | "script_mode") => {
|
||||
let mode = &mode[0..mode.len() - 5];
|
||||
feat::change_clash_mode(mode);
|
||||
feat::change_clash_mode(mode.into());
|
||||
}
|
||||
|
||||
"open_window" => resolve::create_window(app_handle),
|
||||
"system_proxy" => feat::toggle_system_proxy(),
|
||||
"tun_mode" => feat::toggle_tun_mode(),
|
||||
"system_proxy" => log_err!(feat::toggle_system_proxy()),
|
||||
"tun_mode" => log_err!(feat::toggle_tun_mode()),
|
||||
"restart_clash" => feat::restart_clash_core(),
|
||||
"restart_app" => api::process::restart(&app_handle.env()),
|
||||
"quit" => {
|
||||
@@ -124,7 +128,9 @@ impl Tray {
|
||||
SystemTrayEvent::LeftClick { .. } => {
|
||||
resolve::create_window(app_handle);
|
||||
}
|
||||
_ => {}
|
||||
e @ _ => {
|
||||
dbg!("trya");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user