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:
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user