feat.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. //!
  2. //! feat mod 里的函数主要用于
  3. //! - hotkey 快捷键
  4. //! - timer 定时器
  5. //! - cmds 页面调用
  6. //!
  7. use crate::config::*;
  8. use crate::core::*;
  9. use crate::log_err;
  10. use crate::utils::resolve;
  11. use anyhow::{bail, Result};
  12. use serde_yaml::{Mapping, Value};
  13. use tauri::{AppHandle, ClipboardManager};
  14. // 打开面板
  15. pub fn open_dashboard() {
  16. let handle = handle::Handle::global();
  17. let app_handle = handle.app_handle.lock();
  18. if let Some(app_handle) = app_handle.as_ref() {
  19. resolve::create_window(app_handle);
  20. }
  21. }
  22. // 重启clash
  23. pub fn restart_clash_core() {
  24. tauri::async_runtime::spawn(async {
  25. match CoreManager::global().run_core().await {
  26. Ok(_) => {
  27. handle::Handle::refresh_clash();
  28. handle::Handle::notice_message("set_config::ok", "ok");
  29. }
  30. Err(err) => {
  31. handle::Handle::notice_message("set_config::error", format!("{err}"));
  32. log::error!(target:"app", "{err}");
  33. }
  34. }
  35. });
  36. }
  37. // 切换模式 rule/global/direct/script mode
  38. pub fn change_clash_mode(mode: String) {
  39. let mut mapping = Mapping::new();
  40. mapping.insert(Value::from("mode"), mode.clone().into());
  41. tauri::async_runtime::spawn(async move {
  42. log::debug!(target: "app", "change clash mode to {mode}");
  43. match clash_api::patch_configs(&mapping).await {
  44. Ok(_) => {
  45. // 更新订阅
  46. Config::clash().data().patch_config(mapping);
  47. if Config::clash().data().save_config().is_ok() {
  48. handle::Handle::refresh_clash();
  49. log_err!(handle::Handle::update_systray_part());
  50. }
  51. }
  52. Err(err) => log::error!(target: "app", "{err}"),
  53. }
  54. });
  55. }
  56. // 切换系统代理
  57. pub fn toggle_system_proxy() {
  58. let enable = Config::verge().draft().enable_system_proxy.clone();
  59. let enable = enable.unwrap_or(false);
  60. tauri::async_runtime::spawn(async move {
  61. match patch_verge(IVerge {
  62. enable_system_proxy: Some(!enable),
  63. ..IVerge::default()
  64. })
  65. .await
  66. {
  67. Ok(_) => handle::Handle::refresh_verge(),
  68. Err(err) => log::error!(target: "app", "{err}"),
  69. }
  70. });
  71. }
  72. // 打开系统代理
  73. pub fn enable_system_proxy() {
  74. tauri::async_runtime::spawn(async {
  75. match patch_verge(IVerge {
  76. enable_system_proxy: Some(true),
  77. ..IVerge::default()
  78. })
  79. .await
  80. {
  81. Ok(_) => handle::Handle::refresh_verge(),
  82. Err(err) => log::error!(target: "app", "{err}"),
  83. }
  84. });
  85. }
  86. // 关闭系统代理
  87. pub fn disable_system_proxy() {
  88. tauri::async_runtime::spawn(async {
  89. match patch_verge(IVerge {
  90. enable_system_proxy: Some(false),
  91. ..IVerge::default()
  92. })
  93. .await
  94. {
  95. Ok(_) => handle::Handle::refresh_verge(),
  96. Err(err) => log::error!(target: "app", "{err}"),
  97. }
  98. });
  99. }
  100. // 切换tun模式
  101. pub fn toggle_tun_mode() {
  102. let enable = Config::verge().data().enable_tun_mode.clone();
  103. let enable = enable.unwrap_or(false);
  104. tauri::async_runtime::spawn(async move {
  105. match patch_verge(IVerge {
  106. enable_tun_mode: Some(!enable),
  107. ..IVerge::default()
  108. })
  109. .await
  110. {
  111. Ok(_) => handle::Handle::refresh_verge(),
  112. Err(err) => log::error!(target: "app", "{err}"),
  113. }
  114. });
  115. }
  116. // 打开tun模式
  117. pub fn enable_tun_mode() {
  118. tauri::async_runtime::spawn(async {
  119. match patch_verge(IVerge {
  120. enable_tun_mode: Some(true),
  121. ..IVerge::default()
  122. })
  123. .await
  124. {
  125. Ok(_) => handle::Handle::refresh_verge(),
  126. Err(err) => log::error!(target: "app", "{err}"),
  127. }
  128. });
  129. }
  130. // 关闭tun模式
  131. pub fn disable_tun_mode() {
  132. tauri::async_runtime::spawn(async {
  133. match patch_verge(IVerge {
  134. enable_tun_mode: Some(false),
  135. ..IVerge::default()
  136. })
  137. .await
  138. {
  139. Ok(_) => handle::Handle::refresh_verge(),
  140. Err(err) => log::error!(target: "app", "{err}"),
  141. }
  142. });
  143. }
  144. /// 修改clash的订阅
  145. pub async fn patch_clash(patch: Mapping) -> Result<()> {
  146. Config::clash().draft().patch_config(patch.clone());
  147. match {
  148. let mixed_port = patch.get("mixed-port");
  149. let enable_random_port = Config::verge().latest().enable_random_port.unwrap_or(false);
  150. if mixed_port.is_some() && !enable_random_port {
  151. let changed = mixed_port.clone().unwrap()
  152. != Config::verge()
  153. .latest()
  154. .verge_mixed_port
  155. .unwrap_or(Config::clash().data().get_mixed_port());
  156. // 检查端口占用
  157. if changed {
  158. if let Some(port) = mixed_port.clone().unwrap().as_u64() {
  159. if !port_scanner::local_port_available(port as u16) {
  160. Config::clash().discard();
  161. bail!("port already in use");
  162. }
  163. }
  164. }
  165. };
  166. // 激活订阅
  167. if mixed_port.is_some()
  168. || patch.get("secret").is_some()
  169. || patch.get("external-controller").is_some()
  170. {
  171. Config::generate()?;
  172. CoreManager::global().run_core().await?;
  173. handle::Handle::refresh_clash();
  174. }
  175. // 更新系统代理
  176. if mixed_port.is_some() {
  177. log_err!(sysopt::Sysopt::global().init_sysproxy());
  178. }
  179. if patch.get("mode").is_some() {
  180. log_err!(handle::Handle::update_systray_part());
  181. }
  182. Config::runtime().latest().patch_config(patch);
  183. <Result<()>>::Ok(())
  184. } {
  185. Ok(()) => {
  186. Config::clash().apply();
  187. Config::clash().data().save_config()?;
  188. Ok(())
  189. }
  190. Err(err) => {
  191. Config::clash().discard();
  192. Err(err)
  193. }
  194. }
  195. }
  196. /// 修改verge的订阅
  197. /// 一般都是一个个的修改
  198. pub async fn patch_verge(patch: IVerge) -> Result<()> {
  199. Config::verge().draft().patch_config(patch.clone());
  200. let tun_mode = patch.enable_tun_mode;
  201. let auto_launch = patch.enable_auto_launch;
  202. let system_proxy = patch.enable_system_proxy;
  203. let proxy_bypass = patch.system_proxy_bypass;
  204. let language = patch.language;
  205. match {
  206. #[cfg(target_os = "windows")]
  207. {
  208. let service_mode = patch.enable_service_mode;
  209. if service_mode.is_some() {
  210. log::debug!(target: "app", "change service mode to {}", service_mode.unwrap());
  211. Config::generate()?;
  212. CoreManager::global().run_core().await?;
  213. } else if tun_mode.is_some() {
  214. update_core_config().await?;
  215. }
  216. }
  217. #[cfg(not(target_os = "windows"))]
  218. if tun_mode.is_some() {
  219. update_core_config().await?;
  220. }
  221. if auto_launch.is_some() {
  222. sysopt::Sysopt::global().update_launch()?;
  223. }
  224. if system_proxy.is_some() || proxy_bypass.is_some() {
  225. sysopt::Sysopt::global().update_sysproxy()?;
  226. sysopt::Sysopt::global().guard_proxy();
  227. }
  228. if let Some(true) = patch.enable_proxy_guard {
  229. sysopt::Sysopt::global().guard_proxy();
  230. }
  231. if let Some(hotkeys) = patch.hotkeys {
  232. hotkey::Hotkey::global().update(hotkeys)?;
  233. }
  234. if language.is_some() {
  235. handle::Handle::update_systray()?;
  236. } else if system_proxy.or(tun_mode).is_some() {
  237. handle::Handle::update_systray_part()?;
  238. }
  239. <Result<()>>::Ok(())
  240. } {
  241. Ok(()) => {
  242. Config::verge().apply();
  243. Config::verge().data().save_file()?;
  244. Ok(())
  245. }
  246. Err(err) => {
  247. Config::verge().discard();
  248. Err(err)
  249. }
  250. }
  251. }
  252. /// 更新某个profile
  253. /// 如果更新当前订阅就激活订阅
  254. pub async fn update_profile(uid: String, option: Option<PrfOption>) -> Result<()> {
  255. let url_opt = {
  256. let profiles = Config::profiles();
  257. let profiles = profiles.latest();
  258. let item = profiles.get_item(&uid)?;
  259. let is_remote = item.itype.as_ref().map_or(false, |s| s == "remote");
  260. if !is_remote {
  261. None // 直接更新
  262. } else if item.url.is_none() {
  263. bail!("failed to get the profile item url");
  264. } else {
  265. Some((item.url.clone().unwrap(), item.option.clone()))
  266. }
  267. };
  268. let should_update = match url_opt {
  269. Some((url, opt)) => {
  270. let merged_opt = PrfOption::merge(opt, option);
  271. let item = PrfItem::from_url(&url, None, None, merged_opt).await?;
  272. let profiles = Config::profiles();
  273. let mut profiles = profiles.latest();
  274. profiles.update_item(uid.clone(), item)?;
  275. Some(uid) == profiles.get_current()
  276. }
  277. None => true,
  278. };
  279. if should_update {
  280. update_core_config().await?;
  281. }
  282. Ok(())
  283. }
  284. /// 更新订阅
  285. async fn update_core_config() -> Result<()> {
  286. match CoreManager::global().update_config().await {
  287. Ok(_) => {
  288. handle::Handle::refresh_clash();
  289. handle::Handle::notice_message("set_config::ok", "ok");
  290. Ok(())
  291. }
  292. Err(err) => {
  293. handle::Handle::notice_message("set_config::error", format!("{err}"));
  294. Err(err)
  295. }
  296. }
  297. }
  298. /// copy env variable
  299. pub fn copy_clash_env(app_handle: &AppHandle, option: &str) {
  300. let port = { Config::verge().latest().verge_mixed_port.unwrap_or(7890) };
  301. let http_proxy = format!("http://127.0.0.1:{}", port);
  302. let socks5_proxy = format!("socks5://127.0.0.1:{}", port);
  303. let sh =
  304. format!("export https_proxy={http_proxy} http_proxy={http_proxy} all_proxy={socks5_proxy}");
  305. let cmd: String = format!("set http_proxy={http_proxy} \n set https_proxy={http_proxy}");
  306. let ps: String = format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\"");
  307. let mut cliboard = app_handle.clipboard_manager();
  308. match option {
  309. "sh" => cliboard.write_text(sh).unwrap_or_default(),
  310. "cmd" => cliboard.write_text(cmd).unwrap_or_default(),
  311. "ps" => cliboard.write_text(ps).unwrap_or_default(),
  312. _ => log::error!(target: "app", "copy_clash_env: Invalid option! {option}"),
  313. };
  314. }