refactor: wip
This commit is contained in:
parent
3076fd19c1
commit
b8ad328cde
@ -1,12 +1,11 @@
|
||||
use crate::{
|
||||
core::{ClashInfo, Core, PrfItem, PrfOption, Profiles, VergeConfig},
|
||||
utils::{dirs, sysopt::SysProxyConfig},
|
||||
utils::{dirs, help, sysopt::SysProxyConfig},
|
||||
};
|
||||
use crate::{log_if_err, ret_err, wrap_err};
|
||||
use anyhow::Result;
|
||||
use serde_yaml::Mapping;
|
||||
use std::process::Command;
|
||||
use tauri::{api, Manager, State};
|
||||
use tauri::{api, State};
|
||||
|
||||
type CmdResult<T = ()> = Result<T, String>;
|
||||
|
||||
@ -17,11 +16,10 @@ pub fn get_profiles(core: State<'_, Core>) -> CmdResult<Profiles> {
|
||||
Ok(profiles.clone())
|
||||
}
|
||||
|
||||
/// synchronize data irregularly
|
||||
/// manually exec enhanced profile
|
||||
#[tauri::command]
|
||||
pub fn sync_profiles(core: State<'_, Core>) -> CmdResult {
|
||||
let mut profiles = core.profiles.lock();
|
||||
wrap_err!(profiles.sync_file())
|
||||
pub fn enhance_profiles(core: State<'_, Core>) -> CmdResult {
|
||||
wrap_err!(core.activate_enhanced(false))
|
||||
}
|
||||
|
||||
/// import the profile from url
|
||||
@ -102,23 +100,18 @@ pub fn select_profile(index: String, core: State<'_, Core>) -> CmdResult {
|
||||
|
||||
drop(profiles);
|
||||
|
||||
log_if_err!(core.activate_enhanced(false));
|
||||
|
||||
Ok(())
|
||||
wrap_err!(core.activate_enhanced(false))
|
||||
}
|
||||
|
||||
/// change the profile chain
|
||||
#[tauri::command]
|
||||
pub fn change_profile_chain(chain: Option<Vec<String>>, core: State<'_, Core>) -> CmdResult {
|
||||
dbg!("change profile chain");
|
||||
let mut profiles = core.profiles.lock();
|
||||
profiles.put_chain(chain);
|
||||
dbg!("change profile chain finish");
|
||||
|
||||
drop(profiles);
|
||||
|
||||
log_if_err!(core.activate_enhanced(false));
|
||||
|
||||
Ok(())
|
||||
wrap_err!(core.activate_enhanced(false))
|
||||
}
|
||||
|
||||
/// change the profile valid fields
|
||||
@ -126,18 +119,10 @@ pub fn change_profile_chain(chain: Option<Vec<String>>, core: State<'_, Core>) -
|
||||
pub fn change_profile_valid(valid: Option<Vec<String>>, core: State<Core>) -> CmdResult {
|
||||
let mut profiles = core.profiles.lock();
|
||||
profiles.put_valid(valid);
|
||||
|
||||
drop(profiles);
|
||||
|
||||
log_if_err!(core.activate_enhanced(false));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// manually exec enhanced profile
|
||||
#[tauri::command]
|
||||
pub fn enhance_profiles(core: State<'_, Core>) -> CmdResult {
|
||||
log_if_err!(core.activate_enhanced(false));
|
||||
Ok(())
|
||||
wrap_err!(core.activate_enhanced(false))
|
||||
}
|
||||
|
||||
/// delete profile item
|
||||
@ -147,7 +132,7 @@ pub fn delete_profile(index: String, core: State<'_, Core>) -> CmdResult {
|
||||
|
||||
if wrap_err!(profiles.delete_item(index))? {
|
||||
drop(profiles);
|
||||
// std::mem::drop(profiles);
|
||||
|
||||
log_if_err!(core.activate_enhanced(false));
|
||||
}
|
||||
|
||||
@ -178,32 +163,7 @@ pub fn view_profile(index: String, core: State<'_, Core>) -> CmdResult {
|
||||
ret_err!("the file not found");
|
||||
}
|
||||
|
||||
// use vscode first
|
||||
if let Ok(code) = which::which("code") {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
if let Err(err) = Command::new(code)
|
||||
.creation_flags(0x08000000)
|
||||
.arg(path)
|
||||
.spawn()
|
||||
{
|
||||
log::error!("failed to open file by VScode for {err}");
|
||||
return Err("failed to open file by VScode".into());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
if let Err(err) = Command::new(code).arg(path).spawn() {
|
||||
log::error!("failed to open file by VScode for {err}");
|
||||
return Err("failed to open file by VScode".into());
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
wrap_err!(open::that(path))
|
||||
wrap_err!(help::open_file(path))
|
||||
}
|
||||
|
||||
/// read the profile item file data
|
||||
@ -233,12 +193,6 @@ pub fn save_profile_file(
|
||||
wrap_err!(item.save_file(file_data.unwrap()))
|
||||
}
|
||||
|
||||
/// restart the sidecar
|
||||
#[tauri::command]
|
||||
pub fn restart_sidecar(core: State<'_, Core>) -> CmdResult {
|
||||
wrap_err!(core.restart_clash())
|
||||
}
|
||||
|
||||
/// get the clash core info from the state
|
||||
/// the caller can also get the infomation by clash's api
|
||||
#[tauri::command]
|
||||
@ -255,29 +209,11 @@ pub fn patch_clash_config(payload: Mapping, core: State<'_, Core>) -> CmdResult
|
||||
wrap_err!(core.patch_clash(payload))
|
||||
}
|
||||
|
||||
/// get the system proxy
|
||||
#[tauri::command]
|
||||
pub fn get_sys_proxy() -> Result<SysProxyConfig, String> {
|
||||
wrap_err!(SysProxyConfig::get_sys())
|
||||
}
|
||||
|
||||
/// get the current proxy config
|
||||
/// which may not the same as system proxy
|
||||
#[tauri::command]
|
||||
pub fn get_cur_proxy(core: State<'_, Core>) -> CmdResult<Option<SysProxyConfig>> {
|
||||
let verge = core.verge.lock();
|
||||
Ok(verge.cur_sysproxy.clone())
|
||||
}
|
||||
|
||||
/// get the verge config
|
||||
#[tauri::command]
|
||||
pub fn get_verge_config(core: State<'_, Core>) -> CmdResult<VergeConfig> {
|
||||
let verge = core.verge.lock();
|
||||
let mut config = verge.config.clone();
|
||||
|
||||
if config.system_proxy_bypass.is_none() && verge.cur_sysproxy.is_some() {
|
||||
config.system_proxy_bypass = Some(verge.cur_sysproxy.clone().unwrap().bypass)
|
||||
}
|
||||
let config = verge.config.clone();
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
@ -290,41 +226,35 @@ pub fn patch_verge_config(
|
||||
app_handle: tauri::AppHandle,
|
||||
core: State<'_, Core>,
|
||||
) -> Result<(), String> {
|
||||
let tun_mode = payload.enable_tun_mode.clone();
|
||||
let system_proxy = payload.enable_system_proxy.clone();
|
||||
wrap_err!(core.patch_verge(payload, &app_handle))
|
||||
}
|
||||
|
||||
let mut verge = core.verge.lock();
|
||||
wrap_err!(verge.patch_config(payload))?;
|
||||
|
||||
// change system tray
|
||||
if system_proxy.is_some() || tun_mode.is_some() {
|
||||
verge.update_systray(&app_handle).unwrap();
|
||||
}
|
||||
|
||||
// change tun mode
|
||||
if tun_mode.is_some() {
|
||||
#[cfg(target_os = "windows")]
|
||||
if *tun_mode.as_ref().unwrap() {
|
||||
let wintun_dll = dirs::app_home_dir().join("wintun.dll");
|
||||
if !wintun_dll.exists() {
|
||||
log::error!("failed to enable TUN for missing `wintun.dll`");
|
||||
return Err("failed to enable TUN for missing `wintun.dll`".into());
|
||||
}
|
||||
}
|
||||
|
||||
std::mem::drop(verge);
|
||||
log_if_err!(core.activate_enhanced(false));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
/// restart the sidecar
|
||||
#[tauri::command]
|
||||
pub fn restart_sidecar(core: State<'_, Core>) -> CmdResult {
|
||||
wrap_err!(core.restart_clash())
|
||||
}
|
||||
|
||||
/// kill all sidecars when update app
|
||||
#[tauri::command]
|
||||
pub fn kill_sidecars() {
|
||||
pub fn kill_sidecar() {
|
||||
api::process::kill_children();
|
||||
}
|
||||
|
||||
/// get the system proxy
|
||||
#[tauri::command]
|
||||
pub fn get_sys_proxy() -> Result<SysProxyConfig, String> {
|
||||
wrap_err!(SysProxyConfig::get_sys())
|
||||
}
|
||||
|
||||
/// get the current proxy config
|
||||
/// which may not the same as system proxy
|
||||
#[tauri::command]
|
||||
pub fn get_cur_proxy(core: State<'_, Core>) -> CmdResult<Option<SysProxyConfig>> {
|
||||
let sysopt = core.sysopt.lock();
|
||||
wrap_err!(sysopt.get_sysproxy())
|
||||
}
|
||||
|
||||
/// open app config dir
|
||||
#[tauri::command]
|
||||
pub fn open_app_dir() -> Result<(), String> {
|
||||
|
@ -1,520 +0,0 @@
|
||||
use super::{PrfEnhancedResult, Profiles, Verge, VergeConfig};
|
||||
use crate::log_if_err;
|
||||
use crate::utils::{config, dirs, help};
|
||||
use anyhow::{bail, Result};
|
||||
use reqwest::header::HeaderMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml::{Mapping, Value};
|
||||
use std::{collections::HashMap, time::Duration};
|
||||
use tauri::api::process::{Command, CommandChild, CommandEvent};
|
||||
use tauri::Window;
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct ClashInfo {
|
||||
/// clash sidecar status
|
||||
pub status: String,
|
||||
|
||||
/// clash core port
|
||||
pub port: Option<String>,
|
||||
|
||||
/// same as `external-controller`
|
||||
pub server: Option<String>,
|
||||
|
||||
/// clash secret
|
||||
pub secret: Option<String>,
|
||||
}
|
||||
|
||||
pub struct Clash {
|
||||
/// maintain the clash config
|
||||
pub config: Mapping,
|
||||
|
||||
/// some info
|
||||
pub info: ClashInfo,
|
||||
|
||||
/// clash sidecar
|
||||
pub sidecar: Option<CommandChild>,
|
||||
|
||||
/// save the main window
|
||||
pub window: Option<Window>,
|
||||
}
|
||||
|
||||
impl Clash {
|
||||
pub fn new() -> Clash {
|
||||
let config = Clash::read_config();
|
||||
let info = Clash::get_info(&config);
|
||||
|
||||
Clash {
|
||||
config,
|
||||
info,
|
||||
sidecar: None,
|
||||
window: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// get clash config
|
||||
fn read_config() -> Mapping {
|
||||
config::read_yaml::<Mapping>(dirs::clash_path())
|
||||
}
|
||||
|
||||
/// save the clash config
|
||||
fn save_config(&self) -> Result<()> {
|
||||
config::save_yaml(
|
||||
dirs::clash_path(),
|
||||
&self.config,
|
||||
Some("# Default Config For Clash Core\n\n"),
|
||||
)
|
||||
}
|
||||
|
||||
/// parse the clash's config.yaml
|
||||
/// get some information
|
||||
fn get_info(clash_config: &Mapping) -> ClashInfo {
|
||||
let key_port_1 = Value::from("port");
|
||||
let key_port_2 = Value::from("mixed-port");
|
||||
let key_server = Value::from("external-controller");
|
||||
let key_secret = Value::from("secret");
|
||||
|
||||
let port = match clash_config.get(&key_port_1) {
|
||||
Some(value) => match value {
|
||||
Value::String(val_str) => Some(val_str.clone()),
|
||||
Value::Number(val_num) => Some(val_num.to_string()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
let port = match port {
|
||||
Some(_) => port,
|
||||
None => match clash_config.get(&key_port_2) {
|
||||
Some(value) => match value {
|
||||
Value::String(val_str) => Some(val_str.clone()),
|
||||
Value::Number(val_num) => Some(val_num.to_string()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
};
|
||||
|
||||
let server = match clash_config.get(&key_server) {
|
||||
Some(value) => match value {
|
||||
Value::String(val_str) => {
|
||||
// `external-controller` could be
|
||||
// "127.0.0.1:9090" or ":9090"
|
||||
// Todo: maybe it could support single port
|
||||
let server = val_str.clone();
|
||||
let server = match server.starts_with(":") {
|
||||
true => format!("127.0.0.1{server}"),
|
||||
false => server,
|
||||
};
|
||||
|
||||
Some(server)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
let secret = match clash_config.get(&key_secret) {
|
||||
Some(value) => match value {
|
||||
Value::String(val_str) => Some(val_str.clone()),
|
||||
Value::Bool(val_bool) => Some(val_bool.to_string()),
|
||||
Value::Number(val_num) => Some(val_num.to_string()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
ClashInfo {
|
||||
status: "init".into(),
|
||||
port,
|
||||
server,
|
||||
secret,
|
||||
}
|
||||
}
|
||||
|
||||
/// save the main window
|
||||
pub fn set_window(&mut self, win: Option<Window>) {
|
||||
self.window = win;
|
||||
}
|
||||
|
||||
/// run clash sidecar
|
||||
pub fn run_sidecar(&mut self, profiles: &Profiles, delay: bool) -> Result<()> {
|
||||
let app_dir = dirs::app_home_dir();
|
||||
let app_dir = app_dir.as_os_str().to_str().unwrap();
|
||||
|
||||
let cmd = Command::new_sidecar("clash")?;
|
||||
let (mut rx, cmd_child) = cmd.args(["-d", app_dir]).spawn()?;
|
||||
|
||||
self.sidecar = Some(cmd_child);
|
||||
|
||||
// clash log
|
||||
tauri::async_runtime::spawn(async move {
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
CommandEvent::Stdout(line) => log::info!("[clash]: {}", line),
|
||||
CommandEvent::Stderr(err) => log::error!("[clash]: {}", err),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// activate profile
|
||||
log_if_err!(self.activate(&profiles));
|
||||
log_if_err!(self.activate_enhanced(&profiles, delay, true));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// drop clash sidecar
|
||||
pub fn drop_sidecar(&mut self) -> Result<()> {
|
||||
if let Some(sidecar) = self.sidecar.take() {
|
||||
sidecar.kill()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// restart clash sidecar
|
||||
/// should reactivate profile after restart
|
||||
pub fn restart_sidecar(&mut self, profiles: &mut Profiles) -> Result<()> {
|
||||
self.update_config();
|
||||
self.drop_sidecar()?;
|
||||
self.run_sidecar(profiles, false)
|
||||
}
|
||||
|
||||
/// update the clash info
|
||||
pub fn update_config(&mut self) {
|
||||
self.config = Clash::read_config();
|
||||
self.info = Clash::get_info(&self.config);
|
||||
}
|
||||
|
||||
/// patch update the clash config
|
||||
pub fn patch_config(
|
||||
&mut self,
|
||||
patch: Mapping,
|
||||
verge: &mut Verge,
|
||||
profiles: &mut Profiles,
|
||||
) -> Result<()> {
|
||||
let mix_port_key = Value::from("mixed-port");
|
||||
let mut port = None;
|
||||
|
||||
for (key, value) in patch.into_iter() {
|
||||
let value = value.clone();
|
||||
|
||||
// check whether the mix_port is changed
|
||||
if key == mix_port_key {
|
||||
if value.is_number() {
|
||||
port = value.as_i64().as_ref().map(|n| n.to_string());
|
||||
} else {
|
||||
port = value.as_str().as_ref().map(|s| s.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
self.config.insert(key.clone(), value);
|
||||
}
|
||||
|
||||
self.save_config()?;
|
||||
|
||||
if let Some(port) = port {
|
||||
self.restart_sidecar(profiles)?;
|
||||
verge.init_sysproxy(Some(port));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// revise the `tun` and `dns` config
|
||||
fn _tun_mode(mut config: Mapping, enable: bool) -> Mapping {
|
||||
macro_rules! revise {
|
||||
($map: expr, $key: expr, $val: expr) => {
|
||||
let ret_key = Value::String($key.into());
|
||||
$map.insert(ret_key, Value::from($val));
|
||||
};
|
||||
}
|
||||
|
||||
// if key not exists then append value
|
||||
macro_rules! append {
|
||||
($map: expr, $key: expr, $val: expr) => {
|
||||
let ret_key = Value::String($key.into());
|
||||
if !$map.contains_key(&ret_key) {
|
||||
$map.insert(ret_key, Value::from($val));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// tun config
|
||||
let tun_val = config.get(&Value::from("tun"));
|
||||
let mut new_tun = Mapping::new();
|
||||
|
||||
if tun_val.is_some() && tun_val.as_ref().unwrap().is_mapping() {
|
||||
new_tun = tun_val.as_ref().unwrap().as_mapping().unwrap().clone();
|
||||
}
|
||||
|
||||
revise!(new_tun, "enable", enable);
|
||||
|
||||
if enable {
|
||||
append!(new_tun, "stack", "gvisor");
|
||||
append!(new_tun, "dns-hijack", vec!["198.18.0.2:53"]);
|
||||
append!(new_tun, "auto-route", true);
|
||||
append!(new_tun, "auto-detect-interface", true);
|
||||
}
|
||||
|
||||
revise!(config, "tun", new_tun);
|
||||
|
||||
// dns config
|
||||
let dns_val = config.get(&Value::from("dns"));
|
||||
let mut new_dns = Mapping::new();
|
||||
|
||||
if dns_val.is_some() && dns_val.as_ref().unwrap().is_mapping() {
|
||||
new_dns = dns_val.as_ref().unwrap().as_mapping().unwrap().clone();
|
||||
}
|
||||
|
||||
// 借鉴cfw的默认配置
|
||||
revise!(new_dns, "enable", enable);
|
||||
|
||||
if enable {
|
||||
append!(new_dns, "enhanced-mode", "fake-ip");
|
||||
append!(
|
||||
new_dns,
|
||||
"nameserver",
|
||||
vec!["114.114.114.114", "223.5.5.5", "8.8.8.8"]
|
||||
);
|
||||
append!(new_dns, "fallback", vec![] as Vec<&str>);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
append!(
|
||||
new_dns,
|
||||
"fake-ip-filter",
|
||||
vec![
|
||||
"dns.msftncsi.com",
|
||||
"www.msftncsi.com",
|
||||
"www.msftconnecttest.com"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
revise!(config, "dns", new_dns);
|
||||
config
|
||||
}
|
||||
|
||||
/// activate the profile
|
||||
/// generate a new profile to the temp_dir
|
||||
/// then put the path to the clash core
|
||||
fn _activate(info: ClashInfo, config: Mapping, window: Option<Window>) -> Result<()> {
|
||||
let verge_config = VergeConfig::new();
|
||||
let tun_enable = verge_config.enable_tun_mode.unwrap_or(false);
|
||||
|
||||
let config = Clash::_tun_mode(config, tun_enable);
|
||||
|
||||
let temp_path = dirs::profiles_temp_path();
|
||||
config::save_yaml(temp_path.clone(), &config, Some("# Clash Verge Temp File"))?;
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
if info.server.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let server = info.server.unwrap();
|
||||
let server = format!("http://{server}/configs");
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("Content-Type", "application/json".parse().unwrap());
|
||||
|
||||
if let Some(secret) = info.secret.as_ref() {
|
||||
let secret = format!("Bearer {}", secret.clone()).parse().unwrap();
|
||||
headers.insert("Authorization", secret);
|
||||
}
|
||||
|
||||
let mut data = HashMap::new();
|
||||
data.insert("path", temp_path.as_os_str().to_str().unwrap());
|
||||
|
||||
// retry 5 times
|
||||
for _ in 0..5 {
|
||||
match reqwest::ClientBuilder::new().no_proxy().build() {
|
||||
Ok(client) => {
|
||||
let builder = client.put(&server).headers(headers.clone()).json(&data);
|
||||
|
||||
match builder.send().await {
|
||||
Ok(resp) => {
|
||||
if resp.status() != 204 {
|
||||
log::error!("failed to activate clash for status \"{}\"", resp.status());
|
||||
}
|
||||
|
||||
// emit the window to update something
|
||||
if let Some(window) = window {
|
||||
window.emit("verge://refresh-clash-config", "yes").unwrap();
|
||||
}
|
||||
|
||||
// do not retry
|
||||
break;
|
||||
}
|
||||
Err(err) => log::error!("failed to activate for `{err}`"),
|
||||
}
|
||||
}
|
||||
Err(err) => log::error!("failed to activate for `{err}`"),
|
||||
}
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// enhanced profiles mode
|
||||
/// - (sync) refresh config if enhance chain is null
|
||||
/// - (async) enhanced config
|
||||
pub fn activate_enhanced(&self, profiles: &Profiles, delay: bool, skip: bool) -> Result<()> {
|
||||
if self.window.is_none() {
|
||||
bail!("failed to get the main window");
|
||||
}
|
||||
|
||||
let event_name = help::get_uid("e");
|
||||
let event_name = format!("enhanced-cb-{event_name}");
|
||||
|
||||
// generate the payload
|
||||
let payload = profiles.gen_enhanced(event_name.clone())?;
|
||||
|
||||
let info = self.info.clone();
|
||||
|
||||
// do not run enhanced
|
||||
if payload.chain.len() == 0 {
|
||||
if skip {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut config = self.config.clone();
|
||||
let filter_data = Clash::strict_filter(payload.current);
|
||||
|
||||
for (key, value) in filter_data.into_iter() {
|
||||
config.insert(key, value);
|
||||
}
|
||||
|
||||
return Clash::_activate(info, config, self.window.clone());
|
||||
}
|
||||
|
||||
let window = self.window.clone().unwrap();
|
||||
let window_move = self.window.clone();
|
||||
|
||||
window.once(&event_name, move |event| {
|
||||
if let Some(result) = event.payload() {
|
||||
let result: PrfEnhancedResult = serde_json::from_str(result).unwrap();
|
||||
|
||||
if let Some(data) = result.data {
|
||||
let mut config = Clash::read_config();
|
||||
let filter_data = Clash::loose_filter(data); // loose filter
|
||||
|
||||
for (key, value) in filter_data.into_iter() {
|
||||
config.insert(key, value);
|
||||
}
|
||||
|
||||
log_if_err!(Clash::_activate(info, config, window_move));
|
||||
log::info!("profile enhanced status {}", result.status);
|
||||
}
|
||||
|
||||
result.error.map(|err| log::error!("{err}"));
|
||||
}
|
||||
});
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// wait the window setup during resolve app
|
||||
if delay {
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
window.emit("script-handler", payload).unwrap();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// activate the profile
|
||||
/// auto activate enhanced profile
|
||||
pub fn activate(&self, profiles: &Profiles) -> Result<()> {
|
||||
let data = profiles.gen_activate()?;
|
||||
let data = Clash::strict_filter(data);
|
||||
|
||||
let info = self.info.clone();
|
||||
let mut config = self.config.clone();
|
||||
|
||||
for (key, value) in data.into_iter() {
|
||||
config.insert(key, value);
|
||||
}
|
||||
|
||||
Clash::_activate(info, config, self.window.clone())
|
||||
}
|
||||
|
||||
/// only 5 default fields available (clash config fields)
|
||||
/// convert to lowercase
|
||||
fn strict_filter(config: Mapping) -> Mapping {
|
||||
// Only the following fields are allowed:
|
||||
// proxies/proxy-providers/proxy-groups/rule-providers/rules
|
||||
let valid_keys = vec![
|
||||
"proxies",
|
||||
"proxy-providers",
|
||||
"proxy-groups",
|
||||
"rules",
|
||||
"rule-providers",
|
||||
];
|
||||
|
||||
let mut new_config = Mapping::new();
|
||||
|
||||
for (key, value) in config.into_iter() {
|
||||
key.as_str().map(|key_str| {
|
||||
// change to lowercase
|
||||
let mut key_str = String::from(key_str);
|
||||
key_str.make_ascii_lowercase();
|
||||
|
||||
// filter
|
||||
if valid_keys.contains(&&*key_str) {
|
||||
new_config.insert(Value::String(key_str), value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
new_config
|
||||
}
|
||||
|
||||
/// more clash config fields available
|
||||
/// convert to lowercase
|
||||
fn loose_filter(config: Mapping) -> Mapping {
|
||||
// all of these can not be revised by script or merge
|
||||
// http/https/socks port should be under control
|
||||
let not_allow = vec![
|
||||
"port",
|
||||
"socks-port",
|
||||
"mixed-port",
|
||||
"allow-lan",
|
||||
"mode",
|
||||
"external-controller",
|
||||
"secret",
|
||||
"log-level",
|
||||
];
|
||||
|
||||
let mut new_config = Mapping::new();
|
||||
|
||||
for (key, value) in config.into_iter() {
|
||||
key.as_str().map(|key_str| {
|
||||
// change to lowercase
|
||||
let mut key_str = String::from(key_str);
|
||||
key_str.make_ascii_lowercase();
|
||||
|
||||
// filter
|
||||
if !not_allow.contains(&&*key_str) {
|
||||
new_config.insert(Value::String(key_str), value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
new_config
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Clash {
|
||||
fn default() -> Self {
|
||||
Clash::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Clash {
|
||||
fn drop(&mut self) {
|
||||
if let Err(err) = self.drop_sidecar() {
|
||||
log::error!("{err}");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
use self::notice::Notice;
|
||||
use self::service::Service;
|
||||
use self::sysopt::Sysopt;
|
||||
use crate::core::enhance::PrfEnhancedResult;
|
||||
use crate::log_if_err;
|
||||
use crate::utils::help;
|
||||
use crate::utils::{dirs, help};
|
||||
use anyhow::{bail, Result};
|
||||
use parking_lot::Mutex;
|
||||
use serde_yaml::Mapping;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tauri::Window;
|
||||
use tauri::{AppHandle, Manager, Window};
|
||||
use tokio::time::sleep;
|
||||
|
||||
mod clash;
|
||||
@ -17,6 +18,7 @@ mod notice;
|
||||
mod prfitem;
|
||||
mod profiles;
|
||||
mod service;
|
||||
mod sysopt;
|
||||
mod timer;
|
||||
mod verge;
|
||||
|
||||
@ -35,6 +37,8 @@ pub struct Core {
|
||||
|
||||
pub service: Arc<Mutex<Service>>,
|
||||
|
||||
pub sysopt: Arc<Mutex<Sysopt>>,
|
||||
|
||||
pub window: Arc<Mutex<Option<Window>>>,
|
||||
}
|
||||
|
||||
@ -50,10 +54,12 @@ impl Core {
|
||||
verge: Arc::new(Mutex::new(verge)),
|
||||
profiles: Arc::new(Mutex::new(profiles)),
|
||||
service: Arc::new(Mutex::new(service)),
|
||||
sysopt: Arc::new(Mutex::new(Sysopt::new())),
|
||||
window: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
/// initialize the core state
|
||||
pub fn init(&self, app_handle: tauri::AppHandle) {
|
||||
let mut service = self.service.lock();
|
||||
log_if_err!(service.start());
|
||||
@ -62,26 +68,30 @@ impl Core {
|
||||
log_if_err!(self.activate());
|
||||
|
||||
let clash = self.clash.lock();
|
||||
let mut verge = self.verge.lock();
|
||||
let verge = self.verge.lock();
|
||||
|
||||
let hide = verge.config.enable_silent_start.clone().unwrap_or(false);
|
||||
let silent_start = verge.config.enable_silent_start.clone();
|
||||
let auto_launch = verge.config.enable_auto_launch.clone();
|
||||
|
||||
// silent start
|
||||
if hide {
|
||||
if silent_start.unwrap_or(false) {
|
||||
let window = self.window.lock();
|
||||
window.as_ref().map(|win| {
|
||||
win.hide().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
verge.init_sysproxy(clash.info.port.clone());
|
||||
let mut sysopt = self.sysopt.lock();
|
||||
|
||||
log_if_err!(verge.init_launch());
|
||||
log_if_err!(verge.update_systray(&app_handle));
|
||||
sysopt.init_sysproxy(clash.info.port.clone(), &verge);
|
||||
|
||||
drop(clash);
|
||||
drop(verge);
|
||||
|
||||
log_if_err!(sysopt.init_launch(auto_launch));
|
||||
|
||||
log_if_err!(self.update_systray(&app_handle));
|
||||
|
||||
// wait the window setup during resolve app
|
||||
let core = self.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
@ -106,6 +116,7 @@ impl Core {
|
||||
self.activate_enhanced(true)
|
||||
}
|
||||
|
||||
/// Patch Clash
|
||||
/// handle the clash config changed
|
||||
pub fn patch_clash(&self, patch: Mapping) -> Result<()> {
|
||||
let (changed, port) = {
|
||||
@ -123,13 +134,85 @@ impl Core {
|
||||
self.activate()?;
|
||||
self.activate_enhanced(true)?;
|
||||
|
||||
let mut verge = self.verge.lock();
|
||||
verge.init_sysproxy(port);
|
||||
let mut sysopt = self.sysopt.lock();
|
||||
let verge = self.verge.lock();
|
||||
sysopt.init_sysproxy(port, &verge);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Patch Verge
|
||||
pub fn patch_verge(&self, patch: VergeConfig, app_handle: &AppHandle) -> Result<()> {
|
||||
let tun_mode = patch.enable_tun_mode.clone();
|
||||
let auto_launch = patch.enable_auto_launch.clone();
|
||||
let system_proxy = patch.enable_system_proxy.clone();
|
||||
let proxy_bypass = patch.system_proxy_bypass.clone();
|
||||
let proxy_guard = patch.enable_proxy_guard.clone();
|
||||
|
||||
if auto_launch.is_some() {
|
||||
let mut sysopt = self.sysopt.lock();
|
||||
sysopt.update_launch(auto_launch)?;
|
||||
}
|
||||
|
||||
if system_proxy.is_some() || proxy_bypass.is_some() {
|
||||
let mut sysopt = self.sysopt.lock();
|
||||
sysopt.update_sysproxy(system_proxy.clone(), proxy_bypass)?;
|
||||
sysopt.guard_proxy();
|
||||
}
|
||||
|
||||
if proxy_guard.unwrap_or(false) {
|
||||
let sysopt = self.sysopt.lock();
|
||||
sysopt.guard_proxy();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
if tun_mode.is_some() && *tun_mode.as_ref().unwrap_or(&false) {
|
||||
let wintun_dll = dirs::app_home_dir().join("wintun.dll");
|
||||
if !wintun_dll.exists() {
|
||||
bail!("failed to enable TUN for missing `wintun.dll`");
|
||||
}
|
||||
}
|
||||
|
||||
// save the patch
|
||||
let mut verge = self.verge.lock();
|
||||
verge.patch_config(patch)?;
|
||||
drop(verge);
|
||||
|
||||
if system_proxy.is_some() || tun_mode.is_some() {
|
||||
self.update_systray(app_handle)?;
|
||||
}
|
||||
|
||||
if tun_mode.is_some() {
|
||||
self.activate_enhanced(false)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// update the system tray state
|
||||
pub fn update_systray(&self, app_handle: &AppHandle) -> Result<()> {
|
||||
let verge = self.verge.lock();
|
||||
let tray = app_handle.tray_handle();
|
||||
|
||||
let system_proxy = verge.config.enable_system_proxy.as_ref();
|
||||
let tun_mode = verge.config.enable_tun_mode.as_ref();
|
||||
|
||||
tray
|
||||
.get_item("system_proxy")
|
||||
.set_selected(*system_proxy.unwrap_or(&false))?;
|
||||
tray
|
||||
.get_item("tun_mode")
|
||||
.set_selected(*tun_mode.unwrap_or(&false))?;
|
||||
|
||||
// update verge config
|
||||
let window = app_handle.get_window("main");
|
||||
let notice = Notice::from(window);
|
||||
notice.refresh_verge();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// activate the profile
|
||||
/// auto activate enhanced profile
|
||||
pub fn activate(&self) -> Result<()> {
|
||||
@ -165,6 +248,7 @@ impl Core {
|
||||
service.set_config(info, config, notice)
|
||||
}
|
||||
|
||||
/// Enhanced
|
||||
/// enhanced profiles mode
|
||||
pub fn activate_enhanced(&self, skip: bool) -> Result<()> {
|
||||
let window = self.window.lock();
|
||||
@ -235,10 +319,6 @@ impl Core {
|
||||
result.error.map(|err| log::error!("{err}"));
|
||||
});
|
||||
|
||||
// if delay {
|
||||
// sleep(Duration::from_secs(2)).await;
|
||||
// }
|
||||
|
||||
window.emit("script-handler", payload).unwrap();
|
||||
|
||||
Ok(())
|
||||
|
@ -60,7 +60,7 @@ pub struct PrfExtra {
|
||||
pub expire: usize,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||
pub struct PrfOption {
|
||||
/// for `remote` profile's http request
|
||||
/// see issue #13
|
||||
|
@ -47,6 +47,7 @@ impl Profiles {
|
||||
profiles.items = Some(vec![]);
|
||||
}
|
||||
|
||||
// compatiable with the old old old version
|
||||
profiles.items.as_mut().map(|items| {
|
||||
for mut item in items.iter_mut() {
|
||||
if item.uid.is_none() {
|
||||
@ -67,19 +68,6 @@ impl Profiles {
|
||||
)
|
||||
}
|
||||
|
||||
/// sync the config between file and memory
|
||||
pub fn sync_file(&mut self) -> Result<()> {
|
||||
let data = Self::read_file();
|
||||
if data.current.is_none() && data.items.is_none() {
|
||||
bail!("failed to read profiles.yaml");
|
||||
}
|
||||
|
||||
self.current = data.current;
|
||||
self.chain = data.chain;
|
||||
self.items = data.items;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// get the current uid
|
||||
pub fn get_current(&self) -> Option<String> {
|
||||
self.current.clone()
|
||||
@ -94,11 +82,9 @@ impl Profiles {
|
||||
let items = self.items.as_ref().unwrap();
|
||||
let some_uid = Some(uid.clone());
|
||||
|
||||
for each in items.iter() {
|
||||
if each.uid == some_uid {
|
||||
self.current = some_uid;
|
||||
return self.save_file();
|
||||
}
|
||||
if items.iter().find(|&each| each.uid == some_uid).is_some() {
|
||||
self.current = some_uid;
|
||||
return self.save_file();
|
||||
}
|
||||
|
||||
bail!("invalid uid \"{uid}\"");
|
||||
@ -162,7 +148,7 @@ impl Profiles {
|
||||
self.save_file()
|
||||
}
|
||||
|
||||
/// update the item's value
|
||||
/// update the item value
|
||||
pub fn patch_item(&mut self, uid: String, item: PrfItem) -> Result<()> {
|
||||
let mut items = self.items.take().unwrap_or(vec![]);
|
||||
|
||||
@ -197,7 +183,7 @@ impl Profiles {
|
||||
// find the item
|
||||
let _ = self.get_item(&uid)?;
|
||||
|
||||
self.items.as_mut().map(|items| {
|
||||
if let Some(items) = self.items.as_mut() {
|
||||
let some_uid = Some(uid.clone());
|
||||
|
||||
for mut each in items.iter_mut() {
|
||||
@ -217,15 +203,15 @@ impl Profiles {
|
||||
let path = dirs::app_profiles_dir().join(&file);
|
||||
|
||||
fs::File::create(path)
|
||||
.unwrap()
|
||||
.context(format!("failed to create file \"{}\"", file))?
|
||||
.write(file_data.as_bytes())
|
||||
.unwrap();
|
||||
.context(format!("failed to write to file \"{}\"", file))?;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
self.save_file()
|
||||
}
|
||||
|
@ -57,6 +57,8 @@ impl Service {
|
||||
self.start()
|
||||
}
|
||||
|
||||
/// update clash config
|
||||
/// using PUT methods
|
||||
pub fn set_config(&self, info: ClashInfo, config: Mapping, notice: Notice) -> Result<()> {
|
||||
if self.sidecar.is_none() {
|
||||
bail!("did not start sidecar");
|
||||
|
207
src-tauri/src/core/sysopt.rs
Normal file
207
src-tauri/src/core/sysopt.rs
Normal file
@ -0,0 +1,207 @@
|
||||
use super::{Clash, Verge};
|
||||
use crate::{log_if_err, utils::sysopt::SysProxyConfig};
|
||||
use anyhow::{bail, Result};
|
||||
use auto_launch::{AutoLaunch, AutoLaunchBuilder};
|
||||
// use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
use tauri::{async_runtime::Mutex, utils::platform::current_exe};
|
||||
|
||||
pub struct Sysopt {
|
||||
/// current system proxy setting
|
||||
cur_sysproxy: Option<SysProxyConfig>,
|
||||
|
||||
/// record the original system proxy
|
||||
/// recover it when exit
|
||||
old_sysproxy: Option<SysProxyConfig>,
|
||||
|
||||
/// helps to auto launch the app
|
||||
auto_launch: Option<AutoLaunch>,
|
||||
|
||||
/// record whether the guard async is running or not
|
||||
guard_state: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl Sysopt {
|
||||
pub fn new() -> Sysopt {
|
||||
Sysopt {
|
||||
cur_sysproxy: None,
|
||||
old_sysproxy: None,
|
||||
auto_launch: None,
|
||||
guard_state: Arc::new(Mutex::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
/// init the sysproxy
|
||||
pub fn init_sysproxy(&mut self, port: Option<String>, verge: &Verge) {
|
||||
if let Some(port) = port {
|
||||
let enable = verge.config.enable_system_proxy.clone().unwrap_or(false);
|
||||
|
||||
self.old_sysproxy = match SysProxyConfig::get_sys() {
|
||||
Ok(proxy) => Some(proxy),
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let bypass = verge.config.system_proxy_bypass.clone();
|
||||
let sysproxy = SysProxyConfig::new(enable, port, bypass);
|
||||
|
||||
if enable {
|
||||
if let Err(err) = sysproxy.set_sys() {
|
||||
log::error!("failed to set system proxy for `{err}`");
|
||||
}
|
||||
}
|
||||
|
||||
self.cur_sysproxy = Some(sysproxy);
|
||||
}
|
||||
|
||||
// launchs the system proxy guard
|
||||
self.guard_proxy();
|
||||
}
|
||||
|
||||
/// update the system proxy
|
||||
/// when the verge config is changed
|
||||
pub fn update_sysproxy(&mut self, enable: Option<bool>, bypass: Option<String>) -> Result<()> {
|
||||
let sysproxy = self.cur_sysproxy.take();
|
||||
|
||||
if sysproxy.is_none() {
|
||||
bail!("unhandle error for sysproxy is none");
|
||||
}
|
||||
|
||||
let mut sysproxy = sysproxy.unwrap();
|
||||
|
||||
if let Some(enable) = enable {
|
||||
sysproxy.enable = enable;
|
||||
}
|
||||
|
||||
if let Some(bypass) = bypass {
|
||||
sysproxy.bypass = bypass;
|
||||
}
|
||||
|
||||
self.cur_sysproxy = Some(sysproxy);
|
||||
|
||||
if self.cur_sysproxy.as_ref().unwrap().set_sys().is_err() {
|
||||
bail!("failed to set system proxy");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// reset the sysproxy
|
||||
pub fn reset_sysproxy(&mut self) {
|
||||
if let Some(sysproxy) = self.old_sysproxy.take() {
|
||||
match sysproxy.set_sys() {
|
||||
Ok(_) => self.cur_sysproxy = None,
|
||||
Err(_) => log::error!("failed to reset proxy"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// get current proxy
|
||||
pub fn get_sysproxy(&self) -> Result<Option<SysProxyConfig>> {
|
||||
Ok(self.cur_sysproxy.clone())
|
||||
}
|
||||
|
||||
/// init the auto launch
|
||||
pub fn init_launch(&mut self, enable: Option<bool>) -> Result<()> {
|
||||
let app_exe = current_exe().unwrap();
|
||||
let app_exe = dunce::canonicalize(app_exe).unwrap();
|
||||
let app_name = app_exe.file_stem().unwrap().to_str().unwrap();
|
||||
let app_path = app_exe.as_os_str().to_str().unwrap();
|
||||
|
||||
// fix issue #26
|
||||
#[cfg(target_os = "windows")]
|
||||
let app_path = format!("\"{app_path}\"");
|
||||
#[cfg(target_os = "windows")]
|
||||
let app_path = app_path.as_str();
|
||||
|
||||
let auto = AutoLaunchBuilder::new()
|
||||
.set_app_name(app_name)
|
||||
.set_app_path(app_path)
|
||||
.build();
|
||||
|
||||
if let Some(enable) = enable {
|
||||
// fix issue #26
|
||||
if enable {
|
||||
auto.enable()?;
|
||||
}
|
||||
}
|
||||
|
||||
self.auto_launch = Some(auto);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// update the startup
|
||||
pub fn update_launch(&mut self, enable: Option<bool>) -> Result<()> {
|
||||
if enable.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let enable = enable.unwrap();
|
||||
let auto_launch = self.auto_launch.as_ref().unwrap();
|
||||
|
||||
match enable {
|
||||
true => auto_launch.enable()?,
|
||||
false => auto_launch.disable()?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// launch a system proxy guard
|
||||
/// read config from file directly
|
||||
pub fn guard_proxy(&self) {
|
||||
use tokio::time::{sleep, Duration};
|
||||
|
||||
let guard_state = self.guard_state.clone();
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// if it is running, exit
|
||||
let mut state = guard_state.lock().await;
|
||||
if *state {
|
||||
return;
|
||||
}
|
||||
*state = true;
|
||||
drop(state);
|
||||
|
||||
// default duration is 10s
|
||||
let mut wait_secs = 10u64;
|
||||
|
||||
loop {
|
||||
sleep(Duration::from_secs(wait_secs)).await;
|
||||
|
||||
log::debug!("guard heartbeat detection");
|
||||
|
||||
let verge = Verge::new();
|
||||
|
||||
let enable_proxy = verge.config.enable_system_proxy.unwrap_or(false);
|
||||
let enable_guard = verge.config.enable_proxy_guard.unwrap_or(false);
|
||||
let guard_duration = verge.config.proxy_guard_duration.unwrap_or(10);
|
||||
|
||||
// update duration
|
||||
wait_secs = guard_duration;
|
||||
|
||||
// stop loop
|
||||
if !enable_guard || !enable_proxy {
|
||||
break;
|
||||
}
|
||||
|
||||
log::info!("try to guard proxy");
|
||||
|
||||
let clash = Clash::new();
|
||||
|
||||
match &clash.info.port {
|
||||
Some(port) => {
|
||||
let bypass = verge.config.system_proxy_bypass.clone();
|
||||
let sysproxy = SysProxyConfig::new(true, port.clone(), bypass);
|
||||
|
||||
log_if_err!(sysproxy.set_sys());
|
||||
}
|
||||
None => log::error!("failed to parse clash port"),
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = guard_state.lock().await;
|
||||
*state = false;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,14 +1,7 @@
|
||||
use crate::log_if_err;
|
||||
use crate::{
|
||||
core::Clash,
|
||||
utils::{config, dirs, sysopt::SysProxyConfig},
|
||||
};
|
||||
use crate::utils::{config, dirs};
|
||||
use anyhow::{bail, Result};
|
||||
use auto_launch::{AutoLaunch, AutoLaunchBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use tauri::{async_runtime::Mutex, utils::platform::current_exe};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
/// ### `verge.yaml` schema
|
||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||
@ -87,19 +80,6 @@ impl VergeConfig {
|
||||
pub struct Verge {
|
||||
/// manage the verge config
|
||||
pub config: VergeConfig,
|
||||
|
||||
/// current system proxy setting
|
||||
pub cur_sysproxy: Option<SysProxyConfig>,
|
||||
|
||||
/// record the original system proxy
|
||||
/// recover it when exit
|
||||
old_sysproxy: Option<SysProxyConfig>,
|
||||
|
||||
/// helps to auto launch the app
|
||||
auto_launch: Option<AutoLaunch>,
|
||||
|
||||
/// record whether the guard async is running or not
|
||||
guard_state: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl Default for Verge {
|
||||
@ -112,100 +92,11 @@ impl Verge {
|
||||
pub fn new() -> Self {
|
||||
Verge {
|
||||
config: VergeConfig::new(),
|
||||
old_sysproxy: None,
|
||||
cur_sysproxy: None,
|
||||
auto_launch: None,
|
||||
guard_state: Arc::new(Mutex::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
/// init the sysproxy
|
||||
pub fn init_sysproxy(&mut self, port: Option<String>) {
|
||||
if let Some(port) = port {
|
||||
let enable = self.config.enable_system_proxy.clone().unwrap_or(false);
|
||||
|
||||
self.old_sysproxy = match SysProxyConfig::get_sys() {
|
||||
Ok(proxy) => Some(proxy),
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let bypass = self.config.system_proxy_bypass.clone();
|
||||
let sysproxy = SysProxyConfig::new(enable, port, bypass);
|
||||
|
||||
if enable {
|
||||
if sysproxy.set_sys().is_err() {
|
||||
log::error!("failed to set system proxy");
|
||||
}
|
||||
}
|
||||
|
||||
self.cur_sysproxy = Some(sysproxy);
|
||||
}
|
||||
|
||||
// launchs the system proxy guard
|
||||
Verge::guard_proxy(self.guard_state.clone());
|
||||
}
|
||||
|
||||
/// reset the sysproxy
|
||||
pub fn reset_sysproxy(&mut self) {
|
||||
if let Some(sysproxy) = self.old_sysproxy.take() {
|
||||
match sysproxy.set_sys() {
|
||||
Ok(_) => self.cur_sysproxy = None,
|
||||
Err(_) => log::error!("failed to reset proxy"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// init the auto launch
|
||||
pub fn init_launch(&mut self) -> Result<()> {
|
||||
let app_exe = current_exe().unwrap();
|
||||
let app_exe = dunce::canonicalize(app_exe).unwrap();
|
||||
let app_name = app_exe.file_stem().unwrap().to_str().unwrap();
|
||||
let app_path = app_exe.as_os_str().to_str().unwrap();
|
||||
|
||||
// fix issue #26
|
||||
#[cfg(target_os = "windows")]
|
||||
let app_path = format!("\"{app_path}\"");
|
||||
#[cfg(target_os = "windows")]
|
||||
let app_path = app_path.as_str();
|
||||
|
||||
let auto = AutoLaunchBuilder::new()
|
||||
.set_app_name(app_name)
|
||||
.set_app_path(app_path)
|
||||
.build();
|
||||
|
||||
if let Some(enable) = self.config.enable_auto_launch.as_ref() {
|
||||
// fix issue #26
|
||||
if *enable {
|
||||
auto.enable()?;
|
||||
}
|
||||
}
|
||||
|
||||
self.auto_launch = Some(auto);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// update the startup
|
||||
fn update_launch(&mut self, enable: bool) -> Result<()> {
|
||||
let conf_enable = self.config.enable_auto_launch.clone().unwrap_or(false);
|
||||
|
||||
if enable == conf_enable {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let auto_launch = self.auto_launch.clone().unwrap();
|
||||
|
||||
match enable {
|
||||
true => auto_launch.enable()?,
|
||||
false => auto_launch.disable()?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// patch verge config
|
||||
/// There should be only one update at a time here
|
||||
/// so call the save_file at the end is savely
|
||||
/// only save to file
|
||||
pub fn patch_config(&mut self, patch: VergeConfig) -> Result<()> {
|
||||
// only change it
|
||||
if patch.language.is_some() {
|
||||
@ -217,62 +108,28 @@ impl Verge {
|
||||
if patch.theme_blur.is_some() {
|
||||
self.config.theme_blur = patch.theme_blur;
|
||||
}
|
||||
if patch.traffic_graph.is_some() {
|
||||
self.config.traffic_graph = patch.traffic_graph;
|
||||
}
|
||||
if patch.enable_silent_start.is_some() {
|
||||
self.config.enable_silent_start = patch.enable_silent_start;
|
||||
}
|
||||
if patch.theme_setting.is_some() {
|
||||
self.config.theme_setting = patch.theme_setting;
|
||||
}
|
||||
if patch.traffic_graph.is_some() {
|
||||
self.config.traffic_graph = patch.traffic_graph;
|
||||
}
|
||||
|
||||
// should update system startup
|
||||
// system setting
|
||||
if patch.enable_silent_start.is_some() {
|
||||
self.config.enable_silent_start = patch.enable_silent_start;
|
||||
}
|
||||
if patch.enable_auto_launch.is_some() {
|
||||
let enable = patch.enable_auto_launch.unwrap();
|
||||
self.update_launch(enable)?;
|
||||
self.config.enable_auto_launch = Some(enable);
|
||||
self.config.enable_auto_launch = patch.enable_auto_launch;
|
||||
}
|
||||
|
||||
// should update system proxy
|
||||
// proxy
|
||||
if patch.enable_system_proxy.is_some() {
|
||||
let enable = patch.enable_system_proxy.unwrap();
|
||||
|
||||
if let Some(mut sysproxy) = self.cur_sysproxy.take() {
|
||||
sysproxy.enable = enable;
|
||||
if sysproxy.set_sys().is_err() {
|
||||
self.cur_sysproxy = Some(sysproxy);
|
||||
|
||||
bail!("failed to set system proxy");
|
||||
}
|
||||
self.cur_sysproxy = Some(sysproxy);
|
||||
}
|
||||
self.config.enable_system_proxy = Some(enable);
|
||||
self.config.enable_system_proxy = patch.enable_system_proxy;
|
||||
}
|
||||
|
||||
// should update system proxy too
|
||||
if patch.system_proxy_bypass.is_some() {
|
||||
let bypass = patch.system_proxy_bypass.unwrap();
|
||||
|
||||
if let Some(mut sysproxy) = self.cur_sysproxy.take() {
|
||||
if sysproxy.enable {
|
||||
sysproxy.bypass = bypass.clone();
|
||||
|
||||
if sysproxy.set_sys().is_err() {
|
||||
self.cur_sysproxy = Some(sysproxy);
|
||||
|
||||
bail!("failed to set system proxy");
|
||||
}
|
||||
}
|
||||
|
||||
self.cur_sysproxy = Some(sysproxy);
|
||||
}
|
||||
|
||||
self.config.system_proxy_bypass = Some(bypass);
|
||||
self.config.system_proxy_bypass = patch.system_proxy_bypass;
|
||||
}
|
||||
|
||||
// proxy guard
|
||||
// only change it
|
||||
if patch.enable_proxy_guard.is_some() {
|
||||
self.config.enable_proxy_guard = patch.enable_proxy_guard;
|
||||
}
|
||||
@ -280,103 +137,11 @@ impl Verge {
|
||||
self.config.proxy_guard_duration = patch.proxy_guard_duration;
|
||||
}
|
||||
|
||||
// relaunch the guard
|
||||
if patch.enable_system_proxy.is_some() || patch.enable_proxy_guard.is_some() {
|
||||
Verge::guard_proxy(self.guard_state.clone());
|
||||
}
|
||||
|
||||
// handle the tun mode
|
||||
// tun mode
|
||||
if patch.enable_tun_mode.is_some() {
|
||||
self.config.enable_tun_mode = patch.enable_tun_mode;
|
||||
}
|
||||
|
||||
self.config.save_file()
|
||||
}
|
||||
|
||||
/// update the system tray state
|
||||
pub fn update_systray(&self, app_handle: &AppHandle) -> Result<()> {
|
||||
// system proxy
|
||||
let system_proxy = self.config.enable_system_proxy.as_ref();
|
||||
system_proxy.map(|system_proxy| {
|
||||
app_handle
|
||||
.tray_handle()
|
||||
.get_item("system_proxy")
|
||||
.set_selected(*system_proxy)
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
// tun mode
|
||||
let tun_mode = self.config.enable_tun_mode.as_ref();
|
||||
tun_mode.map(|tun_mode| {
|
||||
app_handle
|
||||
.tray_handle()
|
||||
.get_item("tun_mode")
|
||||
.set_selected(*tun_mode)
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
// update verge config
|
||||
let window = app_handle.get_window("main").unwrap();
|
||||
window.emit("verge://refresh-verge-config", "yes").unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Verge {
|
||||
/// launch a system proxy guard
|
||||
/// read config from file directly
|
||||
pub fn guard_proxy(guard_state: Arc<Mutex<bool>>) {
|
||||
use tokio::time::{sleep, Duration};
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// if it is running, exit
|
||||
let mut state = guard_state.lock().await;
|
||||
if *state {
|
||||
return;
|
||||
}
|
||||
*state = true;
|
||||
std::mem::drop(state);
|
||||
|
||||
// default duration is 10s
|
||||
let mut wait_secs = 10u64;
|
||||
|
||||
loop {
|
||||
sleep(Duration::from_secs(wait_secs)).await;
|
||||
|
||||
log::debug!("guard heartbeat detection");
|
||||
|
||||
let verge = Verge::new();
|
||||
|
||||
let enable_proxy = verge.config.enable_system_proxy.unwrap_or(false);
|
||||
let enable_guard = verge.config.enable_proxy_guard.unwrap_or(false);
|
||||
let guard_duration = verge.config.proxy_guard_duration.unwrap_or(10);
|
||||
|
||||
// update duration
|
||||
wait_secs = guard_duration;
|
||||
|
||||
// stop loop
|
||||
if !enable_guard || !enable_proxy {
|
||||
break;
|
||||
}
|
||||
|
||||
log::info!("try to guard proxy");
|
||||
|
||||
let clash = Clash::new();
|
||||
|
||||
match &clash.info.port {
|
||||
Some(port) => {
|
||||
let bypass = verge.config.system_proxy_bypass.clone();
|
||||
let sysproxy = SysProxyConfig::new(true, port.clone(), bypass);
|
||||
|
||||
log_if_err!(sysproxy.set_sys());
|
||||
}
|
||||
None => log::error!("fail to parse clash port"),
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = guard_state.lock().await;
|
||||
*state = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -51,33 +51,33 @@ fn main() -> std::io::Result<()> {
|
||||
}
|
||||
"system_proxy" => {
|
||||
let core = app_handle.state::<core::Core>();
|
||||
let mut verge = core.verge.lock();
|
||||
|
||||
let old_value = verge.config.enable_system_proxy.clone().unwrap_or(false);
|
||||
let new_value = !old_value;
|
||||
let new_value = {
|
||||
let verge = core.verge.lock();
|
||||
!verge.config.enable_system_proxy.clone().unwrap_or(false)
|
||||
};
|
||||
|
||||
match verge.patch_config(VergeConfig {
|
||||
let patch = VergeConfig {
|
||||
enable_system_proxy: Some(new_value),
|
||||
..VergeConfig::default()
|
||||
}) {
|
||||
Ok(_) => verge.update_systray(app_handle).unwrap(),
|
||||
Err(err) => log::error!("{err}"),
|
||||
}
|
||||
};
|
||||
|
||||
crate::log_if_err!(core.patch_verge(patch, app_handle));
|
||||
}
|
||||
"tun_mode" => {
|
||||
let core = app_handle.state::<core::Core>();
|
||||
let mut verge = core.verge.lock();
|
||||
|
||||
let old_value = verge.config.enable_tun_mode.clone().unwrap_or(false);
|
||||
let new_value = !old_value;
|
||||
let new_value = {
|
||||
let verge = core.verge.lock();
|
||||
!verge.config.enable_tun_mode.clone().unwrap_or(false)
|
||||
};
|
||||
|
||||
match verge.patch_config(VergeConfig {
|
||||
let patch = VergeConfig {
|
||||
enable_tun_mode: Some(new_value),
|
||||
..VergeConfig::default()
|
||||
}) {
|
||||
Ok(_) => verge.update_systray(app_handle).unwrap(),
|
||||
Err(err) => log::error!("{err}"),
|
||||
}
|
||||
};
|
||||
|
||||
crate::log_if_err!(core.patch_verge(patch, app_handle));
|
||||
}
|
||||
"restart_clash" => {
|
||||
let core = app_handle.state::<core::Core>();
|
||||
@ -104,7 +104,7 @@ fn main() -> std::io::Result<()> {
|
||||
cmds::restart_sidecar,
|
||||
cmds::get_sys_proxy,
|
||||
cmds::get_cur_proxy,
|
||||
cmds::kill_sidecars,
|
||||
cmds::kill_sidecar,
|
||||
cmds::open_app_dir,
|
||||
cmds::open_logs_dir,
|
||||
// clash
|
||||
@ -122,7 +122,6 @@ fn main() -> std::io::Result<()> {
|
||||
cmds::delete_profile,
|
||||
cmds::select_profile,
|
||||
cmds::get_profiles,
|
||||
cmds::sync_profiles,
|
||||
cmds::enhance_profiles,
|
||||
cmds::change_profile_chain,
|
||||
cmds::change_profile_valid,
|
||||
|
@ -1,4 +1,7 @@
|
||||
use anyhow::{bail, Result};
|
||||
use nanoid::nanoid;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
@ -41,6 +44,38 @@ pub fn parse_str<T: FromStr>(target: &str, key: &str) -> Option<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// open file
|
||||
/// use vscode by default
|
||||
pub fn open_file(path: PathBuf) -> Result<()> {
|
||||
// use vscode first
|
||||
if let Ok(code) = which::which("code") {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
if let Err(err) = Command::new(code)
|
||||
.creation_flags(0x08000000)
|
||||
.arg(path)
|
||||
.spawn()
|
||||
{
|
||||
bail!(format!("failed to open file by VScode for `{err}`"));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
if let Err(err) = Command::new(code).arg(path).spawn() {
|
||||
bail!(format!("failed to open file by VScode for `{err}`"));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match open::that(path) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => bail!(format!("failed to open file for `{err}`")),
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log_if_err {
|
||||
($result: expr) => {
|
||||
|
@ -21,9 +21,9 @@ pub fn resolve_setup(app: &App) {
|
||||
/// reset system proxy
|
||||
pub fn resolve_reset(app_handle: &AppHandle) {
|
||||
let core = app_handle.state::<Core>();
|
||||
let mut verge = core.verge.lock();
|
||||
let mut sysopt = core.sysopt.lock();
|
||||
|
||||
verge.reset_sysproxy();
|
||||
sysopt.reset_sysproxy();
|
||||
}
|
||||
|
||||
/// customize the window theme
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
} from "@mui/material";
|
||||
import { relaunch } from "@tauri-apps/api/process";
|
||||
import { checkUpdate, installUpdate } from "@tauri-apps/api/updater";
|
||||
import { killSidecars, restartSidecar } from "../../services/cmds";
|
||||
import { killSidecar, restartSidecar } from "../../services/cmds";
|
||||
import { atomUpdateState } from "../../services/states";
|
||||
import Notice from "../base/base-notice";
|
||||
|
||||
@ -41,7 +41,7 @@ const UpdateDialog = (props: Props) => {
|
||||
setUpdateState(true);
|
||||
|
||||
try {
|
||||
await killSidecars();
|
||||
await killSidecar();
|
||||
await installUpdate();
|
||||
await relaunch();
|
||||
} catch (err: any) {
|
||||
|
@ -6,10 +6,6 @@ export async function getProfiles() {
|
||||
return invoke<CmdType.ProfilesConfig>("get_profiles");
|
||||
}
|
||||
|
||||
export async function syncProfiles() {
|
||||
return invoke<void>("sync_profiles");
|
||||
}
|
||||
|
||||
export async function enhanceProfiles() {
|
||||
return invoke<void>("enhance_profiles");
|
||||
}
|
||||
@ -94,8 +90,8 @@ export async function restartSidecar() {
|
||||
return invoke<void>("restart_sidecar");
|
||||
}
|
||||
|
||||
export async function killSidecars() {
|
||||
return invoke<any>("kill_sidecars");
|
||||
export async function killSidecar() {
|
||||
return invoke<any>("kill_sidecar");
|
||||
}
|
||||
|
||||
export async function openAppDir() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user