verge.rs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. use crate::{
  2. core::Clash,
  3. utils::{config, dirs, sysopt::SysProxyConfig},
  4. };
  5. use auto_launch::{AutoLaunch, AutoLaunchBuilder};
  6. use serde::{Deserialize, Serialize};
  7. use std::sync::Arc;
  8. use tauri::{api::path::resource_dir, async_runtime::Mutex};
  9. /// ### `verge.yaml` schema
  10. #[derive(Default, Debug, Clone, Deserialize, Serialize)]
  11. pub struct VergeConfig {
  12. /// `light` or `dark`
  13. pub theme_mode: Option<String>,
  14. /// enable blur mode
  15. /// maybe be able to set the alpha
  16. pub theme_blur: Option<bool>,
  17. /// enable traffic graph default is true
  18. pub traffic_graph: Option<bool>,
  19. /// can the app auto startup
  20. pub enable_auto_launch: Option<bool>,
  21. /// set system proxy
  22. pub enable_system_proxy: Option<bool>,
  23. /// enable proxy guard
  24. pub enable_proxy_guard: Option<bool>,
  25. /// set system proxy bypass
  26. pub system_proxy_bypass: Option<String>,
  27. }
  28. static VERGE_CONFIG: &str = "verge.yaml";
  29. impl VergeConfig {
  30. pub fn new() -> Self {
  31. config::read_yaml::<VergeConfig>(dirs::app_home_dir().join(VERGE_CONFIG))
  32. }
  33. /// Save Verge App Config
  34. pub fn save_file(&self) -> Result<(), String> {
  35. config::save_yaml(
  36. dirs::app_home_dir().join(VERGE_CONFIG),
  37. self,
  38. Some("# The Config for Clash Verge App\n\n"),
  39. )
  40. }
  41. }
  42. /// Verge App abilities
  43. #[derive(Debug)]
  44. pub struct Verge {
  45. pub config: VergeConfig,
  46. pub old_sysproxy: Option<SysProxyConfig>,
  47. pub cur_sysproxy: Option<SysProxyConfig>,
  48. pub auto_launch: Option<AutoLaunch>,
  49. /// record whether the guard async is running or not
  50. guard_state: Arc<Mutex<bool>>,
  51. }
  52. impl Default for Verge {
  53. fn default() -> Self {
  54. Verge::new()
  55. }
  56. }
  57. impl Verge {
  58. pub fn new() -> Self {
  59. Verge {
  60. config: VergeConfig::new(),
  61. old_sysproxy: None,
  62. cur_sysproxy: None,
  63. auto_launch: None,
  64. guard_state: Arc::new(Mutex::new(false)),
  65. }
  66. }
  67. /// init the sysproxy
  68. pub fn init_sysproxy(&mut self, port: Option<String>) {
  69. if let Some(port) = port {
  70. let enable = self.config.enable_system_proxy.clone().unwrap_or(false);
  71. self.old_sysproxy = match SysProxyConfig::get_sys() {
  72. Ok(proxy) => Some(proxy),
  73. Err(_) => None,
  74. };
  75. let bypass = self.config.system_proxy_bypass.clone();
  76. let sysproxy = SysProxyConfig::new(enable, port, bypass);
  77. if enable {
  78. if sysproxy.set_sys().is_err() {
  79. log::error!("failed to set system proxy");
  80. }
  81. }
  82. self.cur_sysproxy = Some(sysproxy);
  83. }
  84. // launchs the system proxy guard
  85. Verge::guard_proxy(10, self.guard_state.clone());
  86. }
  87. /// reset the sysproxy
  88. pub fn reset_sysproxy(&mut self) {
  89. if let Some(sysproxy) = self.old_sysproxy.take() {
  90. match sysproxy.set_sys() {
  91. Ok(_) => self.cur_sysproxy = None,
  92. Err(_) => log::error!("failed to reset proxy for"),
  93. }
  94. }
  95. }
  96. /// init the auto launch
  97. pub fn init_launch(&mut self, package_info: &tauri::PackageInfo) {
  98. let app_name = "clash-verge";
  99. let app_path = get_app_path(app_name);
  100. let app_path = resource_dir(package_info, &tauri::Env::default())
  101. .unwrap()
  102. .join(app_path);
  103. let app_path = app_path.as_os_str().to_str().unwrap();
  104. let auto = AutoLaunchBuilder::new()
  105. .set_app_name(app_name)
  106. .set_app_path(app_path)
  107. .build();
  108. self.auto_launch = Some(auto);
  109. }
  110. /// sync the startup when run the app
  111. pub fn sync_launch(&self) -> Result<(), String> {
  112. let enable = self.config.enable_auto_launch.clone().unwrap_or(false);
  113. if !enable {
  114. return Ok(());
  115. }
  116. if self.auto_launch.is_none() {
  117. return Err("should init the auto launch first".into());
  118. }
  119. let auto_launch = self.auto_launch.clone().unwrap();
  120. let is_enabled = auto_launch.is_enabled().unwrap_or(false);
  121. if !is_enabled {
  122. if let Err(_) = auto_launch.enable() {
  123. return Err("failed to enable auto-launch".into());
  124. }
  125. }
  126. Ok(())
  127. }
  128. /// update the startup
  129. fn update_launch(&mut self, enable: bool) -> Result<(), String> {
  130. let conf_enable = self.config.enable_auto_launch.clone().unwrap_or(false);
  131. if enable == conf_enable {
  132. return Ok(());
  133. }
  134. let auto_launch = self.auto_launch.clone().unwrap();
  135. let result = match enable {
  136. true => auto_launch.enable(),
  137. false => auto_launch.disable(),
  138. };
  139. match result {
  140. Ok(_) => Ok(()),
  141. Err(err) => {
  142. log::error!("{err}");
  143. Err("failed to set system startup info".into())
  144. }
  145. }
  146. }
  147. /// patch verge config
  148. /// There should be only one update at a time here
  149. /// so call the save_file at the end is savely
  150. pub fn patch_config(&mut self, patch: VergeConfig) -> Result<(), String> {
  151. // only change it
  152. if patch.theme_mode.is_some() {
  153. self.config.theme_mode = patch.theme_mode;
  154. }
  155. if patch.theme_blur.is_some() {
  156. self.config.theme_blur = patch.theme_blur;
  157. }
  158. if patch.traffic_graph.is_some() {
  159. self.config.traffic_graph = patch.traffic_graph;
  160. }
  161. // should update system startup
  162. if patch.enable_auto_launch.is_some() {
  163. let enable = patch.enable_auto_launch.unwrap();
  164. self.update_launch(enable)?;
  165. self.config.enable_auto_launch = Some(enable);
  166. }
  167. // should update system proxy
  168. if patch.enable_system_proxy.is_some() {
  169. let enable = patch.enable_system_proxy.unwrap();
  170. if let Some(mut sysproxy) = self.cur_sysproxy.take() {
  171. sysproxy.enable = enable;
  172. if sysproxy.set_sys().is_err() {
  173. self.cur_sysproxy = Some(sysproxy);
  174. log::error!("failed to set system proxy");
  175. return Err("failed to set system proxy".into());
  176. }
  177. self.cur_sysproxy = Some(sysproxy);
  178. }
  179. self.config.enable_system_proxy = Some(enable);
  180. }
  181. // should update system proxy too
  182. if patch.system_proxy_bypass.is_some() {
  183. let bypass = patch.system_proxy_bypass.unwrap();
  184. if let Some(mut sysproxy) = self.cur_sysproxy.take() {
  185. if sysproxy.enable {
  186. sysproxy.bypass = bypass.clone();
  187. if sysproxy.set_sys().is_err() {
  188. self.cur_sysproxy = Some(sysproxy);
  189. log::error!("failed to set system proxy");
  190. return Err("failed to set system proxy".into());
  191. }
  192. }
  193. self.cur_sysproxy = Some(sysproxy);
  194. }
  195. self.config.system_proxy_bypass = Some(bypass);
  196. }
  197. // proxy guard
  198. // only change it
  199. if patch.enable_proxy_guard.is_some() {
  200. self.config.enable_proxy_guard = patch.enable_proxy_guard;
  201. }
  202. // relaunch the guard
  203. if patch.enable_system_proxy.is_some() || patch.enable_proxy_guard.is_some() {
  204. Verge::guard_proxy(10, self.guard_state.clone());
  205. }
  206. self.config.save_file()
  207. }
  208. }
  209. impl Verge {
  210. /// launch a system proxy guard
  211. /// read config from file directly
  212. pub fn guard_proxy(wait_secs: u64, guard_state: Arc<Mutex<bool>>) {
  213. use tokio::time::{sleep, Duration};
  214. tauri::async_runtime::spawn(async move {
  215. // if it is running, exit
  216. let mut state = guard_state.lock().await;
  217. if *state {
  218. return;
  219. }
  220. *state = true;
  221. std::mem::drop(state);
  222. loop {
  223. sleep(Duration::from_secs(wait_secs)).await;
  224. log::debug!("[Guard]: heartbeat detection");
  225. let verge = Verge::new();
  226. let enable_proxy = verge.config.enable_system_proxy.unwrap_or(false);
  227. let enable_guard = verge.config.enable_proxy_guard.unwrap_or(false);
  228. // stop loop
  229. if !enable_guard || !enable_proxy {
  230. break;
  231. }
  232. log::info!("[Guard]: try to guard proxy");
  233. let clash = Clash::new();
  234. match &clash.info.port {
  235. Some(port) => {
  236. let bypass = verge.config.system_proxy_bypass.clone();
  237. let sysproxy = SysProxyConfig::new(true, port.clone(), bypass);
  238. if let Err(err) = sysproxy.set_sys() {
  239. log::error!("[Guard]: {err}");
  240. log::error!("[Guard]: fail to set system proxy");
  241. }
  242. }
  243. None => log::error!("[Guard]: fail to parse clash port"),
  244. }
  245. }
  246. let mut state = guard_state.lock().await;
  247. *state = false;
  248. });
  249. }
  250. }
  251. // Get the target app_path
  252. fn get_app_path(app_name: &str) -> String {
  253. #[cfg(target_os = "linux")]
  254. let ext = "";
  255. #[cfg(target_os = "macos")]
  256. let ext = "";
  257. #[cfg(target_os = "windows")]
  258. let ext = ".exe";
  259. String::from(app_name) + ext
  260. }