From e369311fc261ce4ebbd7ddb7e17caa441e7f5b34 Mon Sep 17 00:00:00 2001
From: GyDi <segydi@foxmail.com>
Date: Fri, 7 Jan 2022 23:29:20 +0800
Subject: [PATCH] refactor: impl structs methods

---
 src-tauri/src/cmds.rs            | 238 +++++++++++++++++++++++++++++++
 src-tauri/src/cmds/mod.rs        |   2 -
 src-tauri/src/cmds/profile.rs    | 121 ----------------
 src-tauri/src/cmds/some.rs       | 166 ---------------------
 src-tauri/src/config/clash.rs    | 155 +++++++++++++++++---
 src-tauri/src/config/profiles.rs | 114 ++++++++++++++-
 src-tauri/src/config/verge.rs    |  76 ++++++++++
 src-tauri/src/events/emit.rs     |  26 ----
 src-tauri/src/events/mod.rs      |   2 -
 src-tauri/src/events/state.rs    |  22 ---
 src-tauri/src/main.rs            |  63 ++++----
 src-tauri/src/states.rs          |  11 ++
 src-tauri/src/utils/clash.rs     | 168 ----------------------
 src-tauri/src/utils/config.rs    | 104 +++-----------
 src-tauri/src/utils/init.rs      |   7 +-
 src-tauri/src/utils/mod.rs       |   5 +-
 src-tauri/src/utils/resolve.rs   |  75 +++-------
 src-tauri/src/utils/sysopt.rs    |  70 ++++-----
 src/services/api.ts              |   6 +-
 src/services/cmds.ts             |   2 +-
 src/services/types.ts            |   5 +-
 21 files changed, 676 insertions(+), 762 deletions(-)
 create mode 100644 src-tauri/src/cmds.rs
 delete mode 100644 src-tauri/src/cmds/mod.rs
 delete mode 100644 src-tauri/src/cmds/profile.rs
 delete mode 100644 src-tauri/src/cmds/some.rs
 delete mode 100644 src-tauri/src/events/emit.rs
 delete mode 100644 src-tauri/src/events/mod.rs
 delete mode 100644 src-tauri/src/events/state.rs
 create mode 100644 src-tauri/src/states.rs
 delete mode 100644 src-tauri/src/utils/clash.rs

diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs
new file mode 100644
index 0000000..602b9f1
--- /dev/null
+++ b/src-tauri/src/cmds.rs
@@ -0,0 +1,238 @@
+use crate::{
+  config::{ClashInfo, ProfileItem, ProfilesConfig, VergeConfig},
+  states::{ClashState, ProfilesState, VergeState},
+  utils::{
+    config::{read_clash, save_clash},
+    fetch::fetch_profile,
+    sysopt::SysProxyConfig,
+  },
+};
+use serde_yaml::Mapping;
+use tauri::State;
+
+/// get all profiles from `profiles.yaml`
+/// do not acquire the lock of ProfileLock
+#[tauri::command]
+pub fn get_profiles(profiles: State<'_, ProfilesState>) -> Result<ProfilesConfig, String> {
+  match profiles.0.lock() {
+    Ok(profiles) => Ok(profiles.clone()),
+    Err(_) => Err("failed to get profiles lock".into()),
+  }
+}
+
+/// synchronize data irregularly
+#[tauri::command]
+pub fn sync_profiles(profiles: State<'_, ProfilesState>) -> Result<(), String> {
+  match profiles.0.lock() {
+    Ok(mut profiles) => profiles.sync_file(),
+    Err(_) => Err("failed to get profiles lock".into()),
+  }
+}
+
+/// Import the profile from url
+/// and save to `profiles.yaml`
+#[tauri::command]
+pub async fn import_profile(url: String, profiles: State<'_, ProfilesState>) -> Result<(), String> {
+  match fetch_profile(&url).await {
+    Some(result) => {
+      let mut profiles = profiles.0.lock().unwrap();
+      profiles.import_from_url(url, result)
+    }
+    None => Err(format!("failed to fetch profile from `{}`", url)),
+  }
+}
+
+/// Update the profile
+/// and save to `profiles.yaml`
+/// http request firstly
+/// then acquire the lock of `profiles.yaml`
+#[tauri::command]
+pub async fn update_profile(
+  index: usize,
+  clash: State<'_, ClashState>,
+  profiles: State<'_, ProfilesState>,
+) -> Result<(), String> {
+  // maybe we can get the url from the web app directly
+  let url = match profiles.0.lock() {
+    Ok(mut profile) => {
+      let items = profile.items.take().unwrap_or(vec![]);
+      if index >= items.len() {
+        return Err("the index out of bound".into());
+      }
+      let url = match &items[index].url {
+        Some(u) => u.clone(),
+        None => return Err("failed to update profile for `invalid url`".into()),
+      };
+      profile.items = Some(items);
+      url
+    }
+    Err(_) => return Err("failed to get profiles lock".into()),
+  };
+
+  match fetch_profile(&url).await {
+    Some(result) => match profiles.0.lock() {
+      Ok(mut profiles) => {
+        profiles.update_item(index, result)?;
+
+        // reactivate the profile
+        let current = profiles.current.clone().unwrap_or(0);
+        if current == index {
+          let clash = clash.0.lock().unwrap();
+          profiles.activate(clash.info.clone())
+        } else {
+          Ok(())
+        }
+      }
+      Err(_) => Err("failed to get profiles lock".into()),
+    },
+    None => Err(format!("failed to fetch profile from `{}`", url)),
+  }
+}
+
+/// change the current profile
+#[tauri::command]
+pub fn select_profile(
+  index: usize,
+  clash: State<'_, ClashState>,
+  profiles: State<'_, ProfilesState>,
+) -> Result<(), String> {
+  let mut profiles = profiles.0.lock().unwrap();
+
+  match profiles.put_current(index) {
+    Ok(()) => {
+      let clash = clash.0.lock().unwrap();
+      profiles.activate(clash.info.clone())
+    }
+    Err(err) => Err(err),
+  }
+}
+
+/// todo: need to check
+/// delete profile item
+#[tauri::command]
+pub fn delete_profile(index: usize, profiles: State<'_, ProfilesState>) -> Result<(), String> {
+  match profiles.0.lock() {
+    Ok(mut profiles) => profiles.delete_item(index),
+    Err(_) => Err("can not get profiles lock".into()),
+  }
+}
+
+/// patch the profile config
+#[tauri::command]
+pub fn patch_profile(
+  index: usize,
+  profile: ProfileItem,
+  profiles: State<'_, ProfilesState>,
+) -> Result<(), String> {
+  match profiles.0.lock() {
+    Ok(mut profiles) => profiles.patch_item(index, profile),
+    Err(_) => Err("can not get profiles lock".into()),
+  }
+}
+
+/// restart the sidecar
+#[tauri::command]
+pub fn restart_sidecar(clash_state: State<'_, ClashState>) {
+  let mut clash_arc = clash_state.0.lock().unwrap();
+  if let Err(err) = clash_arc.restart_sidecar() {
+    log::error!("{}", err);
+  }
+}
+
+/// get the clash core info from the state
+/// the caller can also get the infomation by clash's api
+#[tauri::command]
+pub fn get_clash_info(clash_state: State<'_, ClashState>) -> Result<ClashInfo, String> {
+  match clash_state.0.lock() {
+    Ok(arc) => Ok(arc.info.clone()),
+    Err(_) => Err("failed to get clash lock".into()),
+  }
+}
+
+/// todo: need refactor
+/// update the clash core config
+/// after putting the change to the clash core
+/// then we should save the latest config
+#[tauri::command]
+pub fn patch_clash_config(payload: Mapping) -> Result<(), String> {
+  let mut config = read_clash();
+  for (key, value) in payload.iter() {
+    if config.contains_key(key) {
+      config[key] = value.clone();
+    } else {
+      config.insert(key.clone(), value.clone());
+    }
+  }
+  save_clash(&config)
+}
+
+/// set the system proxy
+/// Tips: only support windows now
+#[tauri::command]
+pub fn set_sys_proxy(enable: bool, verge_state: State<'_, VergeState>) -> Result<(), String> {
+  let mut verge = verge_state.0.lock().unwrap();
+
+  if let Some(mut sysproxy) = verge.cur_sysproxy.take() {
+    sysproxy.enable = enable;
+    if sysproxy.set_sys().is_err() {
+      log::error!("failed to set system proxy");
+    }
+    verge.cur_sysproxy = Some(sysproxy);
+  }
+  Ok(())
+}
+
+/// get the system proxy
+/// Tips: only support windows now
+#[tauri::command]
+pub fn get_sys_proxy() -> Result<SysProxyConfig, String> {
+  match SysProxyConfig::get_sys() {
+    Ok(value) => Ok(value),
+    Err(err) => Err(err.to_string()),
+  }
+}
+
+#[tauri::command]
+pub fn get_cur_proxy(verge_state: State<'_, VergeState>) -> Result<Option<SysProxyConfig>, String> {
+  match verge_state.0.lock() {
+    Ok(verge) => Ok(verge.cur_sysproxy.clone()),
+    Err(_) => Err("failed to get verge lock".into()),
+  }
+}
+
+/// get the verge config
+#[tauri::command]
+pub fn get_verge_config(verge_state: State<'_, VergeState>) -> Result<VergeConfig, String> {
+  match verge_state.0.lock() {
+    Ok(arc) => Ok(arc.config.clone()),
+    Err(_) => Err("failed to get verge lock".into()),
+  }
+}
+
+/// patch the verge config
+/// this command only save the config and not responsible for other things
+#[tauri::command]
+pub async fn patch_verge_config(
+  payload: VergeConfig,
+  verge_state: State<'_, VergeState>,
+) -> Result<(), String> {
+  let mut verge = verge_state.0.lock().unwrap();
+
+  if payload.theme_mode.is_some() {
+    verge.config.theme_mode = payload.theme_mode;
+  }
+
+  if payload.enable_self_startup.is_some() {
+    verge.config.enable_self_startup = payload.enable_self_startup;
+  }
+
+  if payload.enable_system_proxy.is_some() {
+    verge.config.enable_system_proxy = payload.enable_system_proxy;
+  }
+
+  if payload.system_proxy_bypass.is_some() {
+    verge.config.system_proxy_bypass = payload.system_proxy_bypass;
+  }
+
+  verge.config.save_file()
+}
diff --git a/src-tauri/src/cmds/mod.rs b/src-tauri/src/cmds/mod.rs
deleted file mode 100644
index c5be45b..0000000
--- a/src-tauri/src/cmds/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod profile;
-pub mod some;
diff --git a/src-tauri/src/cmds/profile.rs b/src-tauri/src/cmds/profile.rs
deleted file mode 100644
index 576805c..0000000
--- a/src-tauri/src/cmds/profile.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-use crate::{
-  config::{ProfileItem, ProfilesConfig},
-  events::state::{ClashInfoState, ProfilesState},
-  utils::{clash, fetch},
-};
-use tauri::State;
-
-/// get all profiles from `profiles.yaml`
-/// do not acquire the lock of ProfileLock
-#[tauri::command]
-pub fn get_profiles(profiles: State<'_, ProfilesState>) -> Result<ProfilesConfig, String> {
-  match profiles.0.lock() {
-    Ok(profiles) => Ok(profiles.clone()),
-    Err(_) => Err("can not get profiles lock".into()),
-  }
-}
-
-/// synchronize data irregularly
-#[tauri::command]
-pub fn sync_profiles(profiles: State<'_, ProfilesState>) -> Result<(), String> {
-  match profiles.0.lock() {
-    Ok(mut profiles) => profiles.sync_file(),
-    Err(_) => Err("can not get profiles lock".into()),
-  }
-}
-
-/// Import the profile from url
-/// and save to `profiles.yaml`
-#[tauri::command]
-pub async fn import_profile(url: String, profiles: State<'_, ProfilesState>) -> Result<(), String> {
-  let result = match fetch::fetch_profile(&url).await {
-    Some(r) => r,
-    None => return Err(format!("failed to fetch profile from `{}`", url)),
-  };
-
-  match profiles.0.lock() {
-    Ok(mut profiles) => profiles.import_from_url(url, result),
-    Err(_) => Err("can not get profiles lock".into()),
-  }
-}
-
-/// Update the profile
-/// and save to `profiles.yaml`
-/// http request firstly
-/// then acquire the lock of `profiles.yaml`
-#[tauri::command]
-pub async fn update_profile(
-  index: usize,
-  profiles: State<'_, ProfilesState>,
-) -> Result<(), String> {
-  // maybe we can get the url from the web app directly
-  let url = {
-    match profiles.0.lock() {
-      Ok(mut profile) => {
-        let items = profile.items.take().unwrap_or(vec![]);
-        if index >= items.len() {
-          return Err("the index out of bound".into());
-        }
-        let url = match &items[index].url {
-          Some(u) => u.clone(),
-          None => return Err("failed to update profile for `invalid url`".into()),
-        };
-        profile.items = Some(items);
-        url
-      }
-      Err(_) => return Err("can not get profiles lock".into()),
-    }
-  };
-
-  let result = match fetch::fetch_profile(&url).await {
-    Some(r) => r,
-    None => return Err(format!("failed to fetch profile from `{}`", url)),
-  };
-
-  match profiles.0.lock() {
-    Ok(mut profiles) => profiles.update_item(index, result),
-    Err(_) => Err("can not get profiles lock".into()),
-  }
-}
-
-/// change the current profile
-#[tauri::command]
-pub async fn select_profile(
-  index: usize,
-  profiles: State<'_, ProfilesState>,
-  clash_info: State<'_, ClashInfoState>,
-) -> Result<(), String> {
-  match profiles.0.lock() {
-    Ok(mut profiles) => profiles.put_current(index)?,
-    Err(_) => return Err("can not get profiles lock".into()),
-  };
-
-  let arc = match clash_info.0.lock() {
-    Ok(arc) => arc.clone(),
-    _ => return Err("can not get clash info lock".into()),
-  };
-
-  clash::put_clash_profile(&arc).await
-}
-
-/// delete profile item
-#[tauri::command]
-pub fn delete_profile(index: usize, profiles: State<'_, ProfilesState>) -> Result<(), String> {
-  match profiles.0.lock() {
-    Ok(mut profiles) => profiles.delete_item(index),
-    Err(_) => Err("can not get profiles lock".into()),
-  }
-}
-
-/// patch the profile config
-#[tauri::command]
-pub fn patch_profile(
-  index: usize,
-  profile: ProfileItem,
-  profiles: State<'_, ProfilesState>,
-) -> Result<(), String> {
-  match profiles.0.lock() {
-    Ok(mut profiles) => profiles.patch_item(index, profile),
-    Err(_) => Err("can not get profiles lock".into()),
-  }
-}
diff --git a/src-tauri/src/cmds/some.rs b/src-tauri/src/cmds/some.rs
deleted file mode 100644
index 44a89dc..0000000
--- a/src-tauri/src/cmds/some.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-use crate::{
-  config::VergeConfig,
-  events::{
-    emit::ClashInfoPayload,
-    state::{ClashInfoState, ClashSidecarState, VergeConfLock},
-  },
-  utils::{
-    clash::run_clash_bin,
-    config::{read_clash, save_clash, save_verge},
-    sysopt::{get_proxy_config, set_proxy_config, SysProxyConfig, DEFAULT_BYPASS},
-  },
-};
-use serde_yaml::Mapping;
-use tauri::{AppHandle, State};
-
-/// restart the sidecar
-#[tauri::command]
-pub fn restart_sidecar(
-  app_handle: AppHandle,
-  clash_info: State<'_, ClashInfoState>,
-  clash_sidecar: State<'_, ClashSidecarState>,
-) {
-  {
-    let mut guard = clash_sidecar.0.lock().unwrap();
-    let sidecar = guard.take();
-    if sidecar.is_some() {
-      if let Err(err) = sidecar.unwrap().kill() {
-        log::error!("failed to restart clash for \"{}\"", err);
-      }
-    }
-  }
-  let payload = run_clash_bin(&app_handle);
-
-  if let Ok(mut arc) = clash_info.0.lock() {
-    *arc = payload;
-  }
-}
-
-/// get the clash core info from the state
-/// the caller can also get the infomation by clash's api
-#[tauri::command]
-pub fn get_clash_info(clash_info: State<'_, ClashInfoState>) -> Result<ClashInfoPayload, String> {
-  match clash_info.0.lock() {
-    Ok(arc) => Ok(arc.clone()),
-    Err(_) => Err(format!("can not get clash info")),
-  }
-}
-
-/// update the clash core config
-/// after putting the change to the clash core
-/// then we should save the latest config
-#[tauri::command]
-pub fn patch_clash_config(payload: Mapping) -> Result<(), String> {
-  let mut config = read_clash();
-  for (key, value) in payload.iter() {
-    if config.contains_key(key) {
-      config[key] = value.clone();
-    } else {
-      config.insert(key.clone(), value.clone());
-    }
-  }
-  save_clash(&config)
-}
-
-/// set the system proxy
-/// Tips: only support windows now
-#[tauri::command]
-pub fn set_sys_proxy(
-  enable: bool,
-  clash_info: State<'_, ClashInfoState>,
-  verge_lock: State<'_, VergeConfLock>,
-) -> Result<(), String> {
-  let clash_info = match clash_info.0.lock() {
-    Ok(arc) => arc.clone(),
-    _ => return Err(format!("can not get clash info")),
-  };
-
-  let verge_info = match verge_lock.0.lock() {
-    Ok(arc) => arc.clone(),
-    _ => return Err(format!("can not get verge info")),
-  };
-
-  let port = match clash_info.controller {
-    Some(ctrl) => ctrl.port,
-    None => None,
-  };
-
-  if port.is_none() {
-    return Err(format!("can not get clash core's port"));
-  }
-
-  let config = if enable {
-    let server = format!("127.0.0.1:{}", port.unwrap());
-    let bypass = verge_info
-      .system_proxy_bypass
-      .unwrap_or(String::from(DEFAULT_BYPASS));
-    SysProxyConfig {
-      enable,
-      server,
-      bypass,
-    }
-  } else {
-    SysProxyConfig {
-      enable,
-      server: String::from(""),
-      bypass: String::from(""),
-    }
-  };
-
-  match set_proxy_config(&config) {
-    Ok(_) => Ok(()),
-    Err(_) => Err(format!("can not set proxy")),
-  }
-}
-
-/// get the system proxy
-/// Tips: only support windows now
-#[tauri::command]
-pub fn get_sys_proxy() -> Result<SysProxyConfig, String> {
-  match get_proxy_config() {
-    Ok(value) => Ok(value),
-    Err(err) => Err(err.to_string()),
-  }
-}
-
-/// get the verge config
-#[tauri::command]
-pub fn get_verge_config(verge_lock: State<'_, VergeConfLock>) -> Result<VergeConfig, String> {
-  match verge_lock.0.lock() {
-    Ok(arc) => Ok(arc.clone()),
-    Err(_) => Err(format!("can not get the lock")),
-  }
-}
-
-/// patch the verge config
-/// this command only save the config and not responsible for other things
-#[tauri::command]
-pub async fn patch_verge_config(
-  payload: VergeConfig,
-  verge_lock: State<'_, VergeConfLock>,
-) -> Result<(), String> {
-  let mut verge = match verge_lock.0.lock() {
-    Ok(v) => v,
-    Err(_) => return Err(format!("can not get the lock")),
-  };
-
-  if payload.theme_mode.is_some() {
-    verge.theme_mode = payload.theme_mode;
-  }
-
-  // todo
-  if payload.enable_self_startup.is_some() {
-    verge.enable_self_startup = payload.enable_self_startup;
-  }
-
-  // todo
-  if payload.enable_system_proxy.is_some() {
-    verge.enable_system_proxy = payload.enable_system_proxy;
-  }
-
-  if payload.system_proxy_bypass.is_some() {
-    verge.system_proxy_bypass = payload.system_proxy_bypass;
-  }
-
-  save_verge(&verge)
-}
diff --git a/src-tauri/src/config/clash.rs b/src-tauri/src/config/clash.rs
index 245b846..3ecf76a 100644
--- a/src-tauri/src/config/clash.rs
+++ b/src-tauri/src/config/clash.rs
@@ -1,30 +1,147 @@
+use crate::utils::{config, dirs};
 use serde::{Deserialize, Serialize};
+use serde_yaml::{Mapping, Value};
+use tauri::api::process::{Command, CommandChild, CommandEvent};
 
-/// ### `config.yaml` schema
-/// here should contain all configuration options.
-/// See: https://github.com/Dreamacro/clash/wiki/configuration for details
 #[derive(Default, Debug, Clone, Deserialize, Serialize)]
-pub struct ClashConfig {
-  pub port: Option<u32>,
+pub struct ClashInfo {
+  /// clash sidecar status
+  pub status: String,
 
-  /// alias to `mixed-port`
-  pub mixed_port: Option<u32>,
-
-  /// alias to `allow-lan`
-  pub allow_lan: Option<bool>,
-
-  /// alias to `external-controller`
-  pub external_ctrl: Option<String>,
-
-  pub secret: Option<String>,
-}
-
-#[derive(Default, Debug, Clone, Deserialize, Serialize)]
-pub struct ClashController {
   /// clash core port
   pub port: Option<String>,
 
   /// same as `external-controller`
   pub server: Option<String>,
+
+  /// clash secret
   pub secret: Option<String>,
 }
+
+#[derive(Debug)]
+pub struct Clash {
+  /// some info
+  pub info: ClashInfo,
+
+  /// clash sidecar
+  pub sidecar: Option<CommandChild>,
+}
+
+static CLASH_CONFIG: &str = "config.yaml";
+
+// todo: be able to change config field
+impl Clash {
+  pub fn new() -> Clash {
+    let clash_config = config::read_yaml::<Mapping>(dirs::app_home_dir().join(CLASH_CONFIG));
+
+    let key_port_1 = Value::String("port".to_string());
+    let key_port_2 = Value::String("mixed-port".to_string());
+    let key_server = Value::String("external-controller".to_string());
+    let key_secret = Value::String("secret".to_string());
+
+    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) => Some(val_str.clone()),
+        _ => 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,
+    };
+
+    Clash {
+      info: ClashInfo {
+        status: "init".into(),
+        port,
+        server,
+        secret,
+      },
+      sidecar: None,
+    }
+  }
+
+  /// run clash sidecar
+  pub fn run_sidecar(&mut self) -> Result<(), String> {
+    let app_dir = dirs::app_home_dir();
+    let app_dir = app_dir.as_os_str().to_str().unwrap();
+
+    match Command::new_sidecar("clash") {
+      Ok(cmd) => match cmd.args(["-d", app_dir]).spawn() {
+        Ok((mut rx, cmd_child)) => {
+          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!("[stdout]: {}", line),
+                CommandEvent::Stderr(err) => log::error!("[stderr]: {}", err),
+                _ => {}
+              }
+            }
+          });
+          Ok(())
+        }
+        Err(err) => Err(err.to_string()),
+      },
+      Err(err) => Err(err.to_string()),
+    }
+  }
+
+  /// drop clash sidecar
+  pub fn drop_sidecar(&mut self) -> Result<(), String> {
+    if let Some(sidecar) = self.sidecar.take() {
+      if let Err(err) = sidecar.kill() {
+        return Err(format!("failed to drop clash for \"{}\"", err));
+      }
+    }
+    Ok(())
+  }
+
+  /// restart clash sidecar
+  pub fn restart_sidecar(&mut self) -> Result<(), String> {
+    self.drop_sidecar()?;
+    self.run_sidecar()
+  }
+}
+
+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);
+    }
+  }
+}
diff --git a/src-tauri/src/config/profiles.rs b/src-tauri/src/config/profiles.rs
index 878590b..a493d2f 100644
--- a/src-tauri/src/config/profiles.rs
+++ b/src-tauri/src/config/profiles.rs
@@ -1,9 +1,15 @@
-use crate::utils::{app_home_dir, config};
+use crate::utils::{config, dirs};
+use reqwest::header::HeaderMap;
 use serde::{Deserialize, Serialize};
+use serde_yaml::{Mapping, Value};
+use std::collections::HashMap;
+use std::env::temp_dir;
 use std::fs::File;
 use std::io::Write;
 use std::time::{SystemTime, UNIX_EPOCH};
 
+use super::ClashInfo;
+
 /// Define the `profiles.yaml` schema
 #[derive(Default, Debug, Clone, Deserialize, Serialize)]
 pub struct ProfilesConfig {
@@ -56,17 +62,18 @@ pub struct ProfileResponse {
 }
 
 static PROFILE_YAML: &str = "profiles.yaml";
+static PROFILE_TEMP: &str = "clash-verge-runtime.yaml";
 
 impl ProfilesConfig {
   /// read the config from the file
   pub fn read_file() -> Self {
-    config::read_yaml::<ProfilesConfig>(app_home_dir().join(PROFILE_YAML))
+    config::read_yaml::<ProfilesConfig>(dirs::app_home_dir().join(PROFILE_YAML))
   }
 
   /// save the config to the file
   pub fn save_file(&self) -> Result<(), String> {
     config::save_yaml(
-      app_home_dir().join(PROFILE_YAML),
+      dirs::app_home_dir().join(PROFILE_YAML),
       self,
       Some("# Profiles Config for Clash Verge\n\n"),
     )
@@ -74,7 +81,7 @@ impl ProfilesConfig {
 
   /// sync the config between file and memory
   pub fn sync_file(&mut self) -> Result<(), String> {
-    let data = config::read_yaml::<Self>(app_home_dir().join(PROFILE_YAML));
+    let data = config::read_yaml::<Self>(dirs::app_home_dir().join(PROFILE_YAML));
     if data.current.is_none() {
       Err("failed to read profiles.yaml".into())
     } else {
@@ -88,7 +95,7 @@ impl ProfilesConfig {
   /// and update the config file
   pub fn import_from_url(&mut self, url: String, result: ProfileResponse) -> Result<(), String> {
     // save the profile file
-    let path = app_home_dir().join("profiles").join(&result.file);
+    let path = dirs::app_home_dir().join("profiles").join(&result.file);
     let file_data = result.data.as_bytes();
     File::create(path).unwrap().write(file_data).unwrap();
 
@@ -145,7 +152,7 @@ impl ProfilesConfig {
 
     // update file
     let file_path = &items[index].file.as_ref().unwrap();
-    let file_path = app_home_dir().join("profiles").join(file_path);
+    let file_path = dirs::app_home_dir().join("profiles").join(file_path);
     let file_data = result.data.as_bytes();
     File::create(file_path).unwrap().write(file_data).unwrap();
 
@@ -206,4 +213,99 @@ impl ProfilesConfig {
     self.current = Some(current);
     self.save_file()
   }
+
+  /// activate current profile
+  pub fn activate(&self, clash_config: ClashInfo) -> Result<(), String> {
+    let current = self.current.unwrap_or(0);
+    match self.items.clone() {
+      Some(items) => {
+        if current >= items.len() {
+          return Err("the index out of bound".into());
+        }
+
+        let profile = items[current].clone();
+        tauri::async_runtime::spawn(async move {
+          let mut count = 5; // retry times
+          let mut err = String::from("");
+          while count > 0 {
+            match activate_profile(&profile, &clash_config).await {
+              Ok(_) => return,
+              Err(e) => err = e,
+            }
+            count -= 1;
+          }
+          log::error!("failed to activate for `{}`", err);
+        });
+
+        Ok(())
+      }
+      None => Err("empty profiles".into()),
+    }
+  }
+}
+
+/// put the profile to clash
+pub async fn activate_profile(profile_item: &ProfileItem, info: &ClashInfo) -> Result<(), String> {
+  // temp profile's path
+  let temp_path = temp_dir().join(PROFILE_TEMP);
+
+  // generate temp profile
+  {
+    let file_name = match profile_item.file.clone() {
+      Some(file_name) => file_name,
+      None => return Err("profile item should have `file` field".into()),
+    };
+
+    let file_path = dirs::app_home_dir().join("profiles").join(file_name);
+    if !file_path.exists() {
+      return Err(format!("profile `{:?}` not exists", file_path));
+    }
+
+    // Only the following fields are allowed:
+    // proxies/proxy-providers/proxy-groups/rule-providers/rules
+    let config = config::read_yaml::<Mapping>(file_path.clone());
+    let mut new_config = Mapping::new();
+    vec![
+      "proxies",
+      "proxy-providers",
+      "proxy-groups",
+      "rule-providers",
+      "rules",
+    ]
+    .iter()
+    .map(|item| Value::String(item.to_string()))
+    .for_each(|key| {
+      if config.contains_key(&key) {
+        let value = config[&key].clone();
+        new_config.insert(key, value);
+      }
+    });
+
+    config::save_yaml(
+      temp_path.clone(),
+      &new_config,
+      Some("# Clash Verge Temp File"),
+    )?
+  };
+
+  let server = format!("http://{}/configs", info.server.clone().unwrap());
+
+  let mut headers = HeaderMap::new();
+  headers.insert("Content-Type", "application/json".parse().unwrap());
+
+  if let Some(secret) = info.secret.clone() {
+    headers.insert(
+      "Authorization",
+      format!("Bearer {}", secret).parse().unwrap(),
+    );
+  }
+
+  let mut data = HashMap::new();
+  data.insert("path", temp_path.as_os_str().to_str().unwrap());
+
+  let client = reqwest::Client::new();
+  match client.put(server).headers(headers).json(&data).send().await {
+    Ok(_) => Ok(()),
+    Err(err) => Err(format!("request failed `{}`", err.to_string())),
+  }
 }
diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs
index e322185..751d05b 100644
--- a/src-tauri/src/config/verge.rs
+++ b/src-tauri/src/config/verge.rs
@@ -1,3 +1,4 @@
+use crate::utils::{config, dirs, sysopt::SysProxyConfig};
 use serde::{Deserialize, Serialize};
 
 /// ### `verge.yaml` schema
@@ -15,3 +16,78 @@ pub struct VergeConfig {
   /// set system proxy bypass
   pub system_proxy_bypass: Option<String>,
 }
+
+static VERGE_CONFIG: &str = "verge.yaml";
+
+impl VergeConfig {
+  pub fn new() -> Self {
+    config::read_yaml::<VergeConfig>(dirs::app_home_dir().join(VERGE_CONFIG))
+  }
+
+  /// Save Verge App Config
+  pub fn save_file(&self) -> Result<(), String> {
+    config::save_yaml(
+      dirs::app_home_dir().join(VERGE_CONFIG),
+      self,
+      Some("# The Config for Clash Verge App\n\n"),
+    )
+  }
+}
+
+#[derive(Debug)]
+pub struct Verge {
+  pub config: VergeConfig,
+
+  pub old_sysproxy: Option<SysProxyConfig>,
+
+  pub cur_sysproxy: Option<SysProxyConfig>,
+}
+
+impl Default for Verge {
+  fn default() -> Self {
+    Verge::new()
+  }
+}
+
+impl Verge {
+  pub fn new() -> Self {
+    Verge {
+      config: VergeConfig::new(),
+      old_sysproxy: None,
+      cur_sysproxy: None,
+    }
+  }
+
+  /// 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);
+    }
+  }
+
+  /// 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 for"),
+      }
+    }
+  }
+}
diff --git a/src-tauri/src/events/emit.rs b/src-tauri/src/events/emit.rs
deleted file mode 100644
index 029cc35..0000000
--- a/src-tauri/src/events/emit.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use serde::{Deserialize, Serialize};
-use tauri::{AppHandle, Manager};
-
-use crate::config::ClashController;
-
-#[derive(Default, Debug, Clone, Deserialize, Serialize)]
-pub struct ClashInfoPayload {
-  /// value between `success` and `error`
-  pub status: String,
-
-  /// the clash core's external controller infomation
-  pub controller: Option<ClashController>,
-
-  /// some message
-  pub message: Option<String>,
-}
-
-/// emit `clash_runtime` to the main windows
-pub fn clash_start(app_handle: &AppHandle, payload: &ClashInfoPayload) {
-  match app_handle.get_window("main") {
-    Some(main_win) => {
-      main_win.emit("clash_start", payload).unwrap();
-    }
-    _ => {}
-  };
-}
diff --git a/src-tauri/src/events/mod.rs b/src-tauri/src/events/mod.rs
deleted file mode 100644
index c382b2f..0000000
--- a/src-tauri/src/events/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod emit;
-pub mod state;
diff --git a/src-tauri/src/events/state.rs b/src-tauri/src/events/state.rs
deleted file mode 100644
index cfda63b..0000000
--- a/src-tauri/src/events/state.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use super::emit::ClashInfoPayload;
-use crate::{
-  config::{ProfilesConfig, VergeConfig},
-  utils::sysopt::SysProxyConfig,
-};
-use std::sync::{Arc, Mutex};
-use tauri::api::process::CommandChild;
-
-#[derive(Default)]
-pub struct ClashInfoState(pub Arc<Mutex<ClashInfoPayload>>);
-
-#[derive(Default)]
-pub struct ProfilesState(pub Arc<Mutex<ProfilesConfig>>);
-
-#[derive(Default)]
-pub struct VergeConfLock(pub Arc<Mutex<VergeConfig>>);
-
-#[derive(Default)]
-pub struct SomthingState(pub Arc<Mutex<Option<SysProxyConfig>>>);
-
-#[derive(Default)]
-pub struct ClashSidecarState(pub Arc<Mutex<Option<CommandChild>>>);
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index debbe79..6338971 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -7,13 +7,10 @@ extern crate tauri;
 
 mod cmds;
 mod config;
-mod events;
+mod states;
 mod utils;
 
-use crate::{
-  events::state,
-  utils::{clash, resolve, server},
-};
+use crate::utils::{resolve, server};
 use tauri::{
   api, CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem,
 };
@@ -31,11 +28,9 @@ fn main() -> std::io::Result<()> {
     .add_item(CustomMenuItem::new("quit", "退出").accelerator("CmdOrControl+Q"));
 
   tauri::Builder::default()
-    .manage(state::ClashSidecarState::default())
-    .manage(state::VergeConfLock::default())
-    .manage(state::ClashInfoState::default())
-    .manage(state::SomthingState::default())
-    .manage(state::ProfilesState::default())
+    .manage(states::VergeState::default())
+    .manage(states::ClashState::default())
+    .manage(states::ProfilesState::default())
     .setup(|app| Ok(resolve::resolve_setup(app)))
     .system_tray(SystemTray::new().with_menu(menu))
     .on_system_tray_event(move |app_handle, event| match event {
@@ -46,22 +41,11 @@ fn main() -> std::io::Result<()> {
           window.set_focus().unwrap();
         }
         "restart_clash" => {
-          {
-            let state = app_handle.state::<state::ClashSidecarState>();
-            let mut guard = state.0.lock().unwrap();
-            let sidecar = guard.take();
-            if sidecar.is_some() {
-              if let Err(err) = sidecar.unwrap().kill() {
-                log::error!("failed to restart clash for \"{}\"", err);
-              }
-            }
+          let clash_state = app_handle.state::<states::ClashState>();
+          let mut clash_arc = clash_state.0.lock().unwrap();
+          if let Err(err) = clash_arc.restart_sidecar() {
+            log::error!("{}", err);
           }
-
-          let payload = clash::run_clash_bin(&app_handle);
-          let state = app_handle.state::<state::ClashInfoState>();
-          if let Ok(mut arc) = state.0.lock() {
-            *arc = payload;
-          };
         }
         "quit" => {
           api::process::kill_children();
@@ -78,20 +62,21 @@ fn main() -> std::io::Result<()> {
       _ => {}
     })
     .invoke_handler(tauri::generate_handler![
-      cmds::some::restart_sidecar,
-      cmds::some::set_sys_proxy,
-      cmds::some::get_sys_proxy,
-      cmds::some::get_clash_info,
-      cmds::some::patch_clash_config,
-      cmds::some::get_verge_config,
-      cmds::some::patch_verge_config,
-      cmds::profile::import_profile,
-      cmds::profile::update_profile,
-      cmds::profile::delete_profile,
-      cmds::profile::select_profile,
-      cmds::profile::patch_profile,
-      cmds::profile::sync_profiles,
-      cmds::profile::get_profiles,
+      cmds::restart_sidecar,
+      cmds::set_sys_proxy,
+      cmds::get_sys_proxy,
+      cmds::get_cur_proxy,
+      cmds::get_clash_info,
+      cmds::patch_clash_config,
+      cmds::get_verge_config,
+      cmds::patch_verge_config,
+      cmds::import_profile,
+      cmds::update_profile,
+      cmds::delete_profile,
+      cmds::select_profile,
+      cmds::patch_profile,
+      cmds::sync_profiles,
+      cmds::get_profiles,
     ])
     .build(tauri::generate_context!())
     .expect("error while running tauri application")
diff --git a/src-tauri/src/states.rs b/src-tauri/src/states.rs
new file mode 100644
index 0000000..3f9941b
--- /dev/null
+++ b/src-tauri/src/states.rs
@@ -0,0 +1,11 @@
+use crate::config::{Clash, ProfilesConfig, Verge};
+use std::sync::{Arc, Mutex};
+
+#[derive(Default)]
+pub struct ProfilesState(pub Arc<Mutex<ProfilesConfig>>);
+
+#[derive(Default)]
+pub struct ClashState(pub Arc<Mutex<Clash>>);
+
+#[derive(Default)]
+pub struct VergeState(pub Arc<Mutex<Verge>>);
diff --git a/src-tauri/src/utils/clash.rs b/src-tauri/src/utils/clash.rs
deleted file mode 100644
index 33e5cb4..0000000
--- a/src-tauri/src/utils/clash.rs
+++ /dev/null
@@ -1,168 +0,0 @@
-extern crate log;
-
-use crate::{
-  config::ProfilesConfig,
-  events::{
-    emit::{clash_start, ClashInfoPayload},
-    state,
-  },
-  utils::{app_home_dir, clash, config},
-};
-use reqwest::header::HeaderMap;
-use serde_yaml::{Mapping, Value};
-use std::{collections::HashMap, env::temp_dir};
-use tauri::{
-  api::process::{Command, CommandEvent},
-  AppHandle, Manager,
-};
-
-/// Run the clash bin
-pub fn run_clash_bin(app_handle: &AppHandle) -> ClashInfoPayload {
-  let app_dir = app_home_dir();
-  let app_dir = app_dir.as_os_str().to_str().unwrap();
-
-  let mut payload = ClashInfoPayload {
-    status: "success".to_string(),
-    controller: None,
-    message: None,
-  };
-
-  let result = match Command::new_sidecar("clash") {
-    Ok(cmd) => match cmd.args(["-d", app_dir]).spawn() {
-      Ok(res) => Ok(res),
-      Err(err) => Err(err.to_string()),
-    },
-    Err(err) => Err(err.to_string()),
-  };
-
-  match result {
-    Ok((mut rx, cmd_child)) => {
-      log::info!("Successfully execute clash sidecar");
-      payload.controller = Some(config::read_clash_controller());
-
-      if let Ok(mut state) = app_handle.state::<state::ClashSidecarState>().0.lock() {
-        *state = Some(cmd_child);
-      };
-
-      tauri::async_runtime::spawn(async move {
-        while let Some(event) = rx.recv().await {
-          match event {
-            CommandEvent::Stdout(line) => log::info!("[stdout]: {}", line),
-            CommandEvent::Stderr(err) => log::error!("[stderr]: {}", err),
-            _ => {}
-          }
-        }
-      });
-
-      // update the profile
-      let payload_ = payload.clone();
-      tauri::async_runtime::spawn(async move {
-        let mut count = 5; // retry times
-        let mut err = String::from("");
-        while count > 0 {
-          match clash::put_clash_profile(&payload_).await {
-            Ok(_) => return,
-            Err(e) => err = e,
-          }
-          count -= 1;
-        }
-        log::error!("failed to put config for `{}`", err);
-      });
-    }
-    Err(err) => {
-      log::error!("Failed to execute clash sidecar for \"{}\"", err);
-      payload.status = "error".to_string();
-      payload.message = Some(err.to_string());
-    }
-  };
-
-  clash_start(app_handle, &payload);
-  payload
-}
-
-/// Update the clash profile firstly
-pub async fn put_clash_profile(payload: &ClashInfoPayload) -> Result<(), String> {
-  let profile = {
-    let profiles = ProfilesConfig::read_file();
-    let current = profiles.current.unwrap_or(0) as usize;
-    match profiles.items {
-      Some(items) => {
-        if items.len() == 0 {
-          return Err("can not read profiles".to_string());
-        }
-        let idx = if current < items.len() { current } else { 0 };
-        items[idx].clone()
-      }
-      None => {
-        return Err("can not read profiles".to_string());
-      }
-    }
-  };
-
-  // temp profile's path
-  let temp_path = temp_dir().join("clash-verge-runtime.yaml");
-
-  // generate temp profile
-  {
-    let file_name = match profile.file {
-      Some(file_name) => file_name.clone(),
-      None => return Err(format!("profile item should have `file` field")),
-    };
-
-    let file_path = app_home_dir().join("profiles").join(file_name);
-    if !file_path.exists() {
-      return Err(format!("profile `{:?}` not exists", file_path));
-    }
-
-    // Only the following fields are allowed:
-    // proxies/proxy-providers/proxy-groups/rule-providers/rules
-    let config = config::read_yaml::<Mapping>(file_path.clone());
-    let mut new_config = Mapping::new();
-    vec![
-      "proxies",
-      "proxy-providers",
-      "proxy-groups",
-      "rule-providers",
-      "rules",
-    ]
-    .iter()
-    .map(|item| Value::String(item.to_string()))
-    .for_each(|key| {
-      if config.contains_key(&key) {
-        let value = config[&key].clone();
-        new_config.insert(key, value);
-      }
-    });
-
-    match config::save_yaml(
-      temp_path.clone(),
-      &new_config,
-      Some("# Clash Verge Temp File"),
-    ) {
-      Err(err) => return Err(err),
-      _ => {}
-    };
-  }
-
-  let ctrl = payload.controller.clone().unwrap();
-  let server = format!("http://{}/configs", ctrl.server.unwrap());
-
-  let mut headers = HeaderMap::new();
-  headers.insert("Content-Type", "application/json".parse().unwrap());
-
-  if let Some(secret) = ctrl.secret {
-    headers.insert(
-      "Authorization",
-      format!("Bearer {}", secret).parse().unwrap(),
-    );
-  }
-
-  let mut data = HashMap::new();
-  data.insert("path", temp_path.as_os_str().to_str().unwrap());
-
-  let client = reqwest::Client::new();
-  match client.put(server).headers(headers).json(&data).send().await {
-    Ok(_) => Ok(()),
-    Err(err) => Err(format!("request failed `{}`", err.to_string())),
-  }
-}
diff --git a/src-tauri/src/utils/config.rs b/src-tauri/src/utils/config.rs
index 78b8030..4d8e13e 100644
--- a/src-tauri/src/utils/config.rs
+++ b/src-tauri/src/utils/config.rs
@@ -1,9 +1,6 @@
-use crate::{
-  config::{ClashController, VergeConfig},
-  utils::app_home_dir,
-};
+use crate::utils::dirs;
 use serde::{de::DeserializeOwned, Serialize};
-use serde_yaml::{Mapping, Value};
+use serde_yaml::Mapping;
 use std::{fs, path::PathBuf};
 
 /// read data from yaml as struct T
@@ -19,100 +16,33 @@ pub fn save_yaml<T: Serialize>(
   data: &T,
   prefix: Option<&str>,
 ) -> Result<(), String> {
-  if let Ok(data_str) = serde_yaml::to_string(data) {
-    let yaml_str = if prefix.is_some() {
-      prefix.unwrap().to_string() + &data_str
-    } else {
-      data_str
-    };
-
-    if fs::write(path.clone(), yaml_str.as_bytes()).is_err() {
-      Err(format!("can not save file `{:?}`", path))
-    } else {
-      Ok(())
+  match serde_yaml::to_string(data) {
+    Ok(data_str) => {
+      let yaml_str = match prefix {
+        Some(prefix) => format!("{}{}", prefix, data_str),
+        None => data_str,
+      };
+
+      let path_str = path.as_os_str().to_string_lossy().to_string();
+      match fs::write(path, yaml_str.as_bytes()) {
+        Ok(_) => Ok(()),
+        Err(_) => Err(format!("can not save file `{}`", path_str)),
+      }
     }
-  } else {
-    Err(String::from("can not convert the data to yaml"))
+    Err(_) => Err("can not convert the data to yaml".into()),
   }
 }
 
 /// Get Clash Core Config `config.yaml`
 pub fn read_clash() -> Mapping {
-  read_yaml::<Mapping>(app_home_dir().join("config.yaml"))
+  read_yaml::<Mapping>(dirs::app_home_dir().join("config.yaml"))
 }
 
 /// Save the clash core Config `config.yaml`
 pub fn save_clash(config: &Mapping) -> Result<(), String> {
   save_yaml(
-    app_home_dir().join("config.yaml"),
+    dirs::app_home_dir().join("config.yaml"),
     config,
     Some("# Default Config For Clash Core\n\n"),
   )
 }
-
-/// Get infomation of the clash's `external-controller` and `secret`
-pub fn read_clash_controller() -> ClashController {
-  let config = read_clash();
-
-  let key_port_1 = Value::String("port".to_string());
-  let key_port_2 = Value::String("mixed-port".to_string());
-  let key_server = Value::String("external-controller".to_string());
-  let key_secret = Value::String("secret".to_string());
-
-  let port = match 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 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 config.get(&key_server) {
-    Some(value) => match value {
-      Value::String(val_str) => Some(val_str.clone()),
-      _ => None,
-    },
-    _ => None,
-  };
-  let secret = match 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,
-  };
-
-  ClashController {
-    port,
-    server,
-    secret,
-  }
-}
-
-/// Get the `verge.yaml`
-pub fn read_verge() -> VergeConfig {
-  read_yaml::<VergeConfig>(app_home_dir().join("verge.yaml"))
-}
-
-/// Save Verge App Config
-pub fn save_verge(verge: &VergeConfig) -> Result<(), String> {
-  save_yaml(
-    app_home_dir().join("verge.yaml"),
-    verge,
-    Some("# The Config for Clash Verge App\n\n"),
-  )
-}
diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs
index e0cf06b..7171b1f 100644
--- a/src-tauri/src/utils/init.rs
+++ b/src-tauri/src/utils/init.rs
@@ -1,5 +1,6 @@
 extern crate serde_yaml;
 
+use crate::utils::dirs;
 use chrono::Local;
 use log::LevelFilter;
 use log4rs::append::console::ConsoleAppender;
@@ -11,8 +12,6 @@ use std::io::Write;
 use std::path::PathBuf;
 use tauri::PackageInfo;
 
-use crate::utils::{app_home_dir, app_resources_dir};
-
 /// initialize this instance's log file
 fn init_log(log_dir: &PathBuf) {
   let local_time = Local::now().format("%Y-%m-%d-%H%M%S").to_string();
@@ -85,11 +84,11 @@ fn init_config_file(app_dir: &PathBuf, res_dir: &PathBuf) {
 /// initialize app
 pub fn init_app(package_info: &PackageInfo) {
   // create app dir
-  let app_dir = app_home_dir();
+  let app_dir = dirs::app_home_dir();
   let log_dir = app_dir.join("logs");
   let profiles_dir = app_dir.join("profiles");
 
-  let res_dir = app_resources_dir(package_info);
+  let res_dir = dirs::app_resources_dir(package_info);
 
   if !app_dir.exists() {
     fs::create_dir(&app_dir).unwrap();
diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs
index 3a09fe4..a5fe22b 100644
--- a/src-tauri/src/utils/mod.rs
+++ b/src-tauri/src/utils/mod.rs
@@ -1,8 +1,5 @@
-mod dirs;
-pub use self::dirs::*;
-
-pub mod clash;
 pub mod config;
+pub mod dirs;
 pub mod fetch;
 pub mod init;
 pub mod resolve;
diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs
index 8d5727b..416b80e 100644
--- a/src-tauri/src/utils/resolve.rs
+++ b/src-tauri/src/utils/resolve.rs
@@ -1,5 +1,5 @@
-use super::{clash, config, init, server, sysopt};
-use crate::{config::ProfilesConfig, events::state};
+use super::{init, server};
+use crate::{config::ProfilesConfig, states};
 use tauri::{App, AppHandle, Manager};
 
 /// handle something when start app
@@ -10,68 +10,31 @@ pub fn resolve_setup(app: &App) {
   // init app config
   init::init_app(app.package_info());
 
-  // run clash sidecar
-  let info = clash::run_clash_bin(&app.handle());
+  // init states
+  let clash_state = app.state::<states::ClashState>();
+  let verge_state = app.state::<states::VergeState>();
+  let profiles_state = app.state::<states::ProfilesState>();
 
-  // resolve the verge config - enable system proxy
-  let mut original: Option<sysopt::SysProxyConfig> = None;
-  let verge = config::read_verge();
-  let enable = verge.enable_system_proxy.unwrap_or(false);
+  let mut clash = clash_state.0.lock().unwrap();
+  let mut verge = verge_state.0.lock().unwrap();
+  let mut profiles = profiles_state.0.lock().unwrap();
 
-  if enable && info.controller.is_some() {
-    if let Ok(original_conf) = sysopt::get_proxy_config() {
-      original = Some(original_conf)
-    };
-    let ctl = info.controller.clone().unwrap();
-    if ctl.port.is_some() {
-      let server = format!("127.0.0.1:{}", ctl.port.unwrap());
-      let bypass = verge
-        .system_proxy_bypass
-        .clone()
-        .unwrap_or(String::from(sysopt::DEFAULT_BYPASS));
-      let config = sysopt::SysProxyConfig {
-        enable,
-        server,
-        bypass,
-      };
-      if let Err(err) = sysopt::set_proxy_config(&config) {
-        log::error!("can not set system proxy for `{}`", err);
-      }
-    }
+  if let Err(err) = clash.run_sidecar() {
+    log::error!("{}", err);
   }
 
-  // update state
-  let profiles_state = app.state::<state::ProfilesState>();
-  let mut profiles = profiles_state.0.lock().unwrap();
   *profiles = ProfilesConfig::read_file();
+  if let Err(err) = profiles.activate(clash.info.clone()) {
+    log::error!("{}", err);
+  }
 
-  let verge_state = app.state::<state::VergeConfLock>();
-  let mut verge_arc = verge_state.0.lock().unwrap();
-  *verge_arc = verge;
-
-  let clash_state = app.state::<state::ClashInfoState>();
-  let mut clash_arc = clash_state.0.lock().unwrap();
-  *clash_arc = info;
-
-  let some_state = app.state::<state::SomthingState>();
-  let mut some_arc = some_state.0.lock().unwrap();
-  *some_arc = original;
+  verge.init_sysproxy(clash.info.port.clone());
 }
 
 /// reset system proxy
 pub fn resolve_reset(app_handle: &AppHandle) {
-  let state = app_handle.try_state::<state::SomthingState>();
-  if state.is_none() {
-    return;
-  }
-  match state.unwrap().0.lock() {
-    Ok(arc) => {
-      if arc.is_some() {
-        if let Err(err) = sysopt::set_proxy_config(arc.as_ref().unwrap()) {
-          log::error!("failed to reset proxy for `{}`", err);
-        }
-      }
-    }
-    _ => {}
-  };
+  let verge_state = app_handle.state::<states::VergeState>();
+  let mut verge_arc = verge_state.0.lock().unwrap();
+
+  verge_arc.reset_sysproxy();
 }
diff --git a/src-tauri/src/utils/sysopt.rs b/src-tauri/src/utils/sysopt.rs
index 46250e3..c47ab13 100644
--- a/src-tauri/src/utils/sysopt.rs
+++ b/src-tauri/src/utils/sysopt.rs
@@ -1,6 +1,8 @@
 use serde::{Deserialize, Serialize};
 use std::io;
 
+static DEFAULT_BYPASS: &str = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*;<local>";
+
 #[derive(Debug, Deserialize, Serialize, Clone)]
 pub struct SysProxyConfig {
   pub enable: bool,
@@ -18,16 +20,21 @@ impl Default for SysProxyConfig {
   }
 }
 
-pub static DEFAULT_BYPASS: &str = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*;<local>";
-
-#[cfg(target_os = "windows")]
-mod win {
-  use super::*;
-  use winreg::enums::*;
-  use winreg::RegKey;
+impl SysProxyConfig {
+  pub fn new(enable: bool, port: String, bypass: Option<String>) -> Self {
+    SysProxyConfig {
+      enable,
+      server: format!("127.0.0.1:{}", port),
+      bypass: bypass.unwrap_or(DEFAULT_BYPASS.into()),
+    }
+  }
 
   /// Get the windows system proxy config
-  pub fn get_proxy_config() -> io::Result<SysProxyConfig> {
+  #[cfg(target_os = "windows")]
+  pub fn get_sys() -> io::Result<Self> {
+    use winreg::enums::*;
+    use winreg::RegKey;
+
     let hkcu = RegKey::predef(HKEY_CURRENT_USER);
     let cur_var = hkcu.open_subkey_with_flags(
       "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
@@ -41,43 +48,38 @@ mod win {
     })
   }
 
+  #[cfg(target_os = "windows")]
   /// Set the windows system proxy config
-  pub fn set_proxy_config(config: &SysProxyConfig) -> io::Result<()> {
+  pub fn set_sys(&self) -> io::Result<()> {
+    use winreg::enums::*;
+    use winreg::RegKey;
     let hkcu = RegKey::predef(HKEY_CURRENT_USER);
     let cur_var = hkcu.open_subkey_with_flags(
       "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
       KEY_SET_VALUE,
     )?;
 
-    let enable: u32 = if config.enable { 1u32 } else { 0u32 };
+    let enable: u32 = if self.enable { 1u32 } else { 0u32 };
 
     cur_var.set_value("ProxyEnable", &enable)?;
-    cur_var.set_value("ProxyServer", &config.server)?;
-    cur_var.set_value("ProxyOverride", &config.bypass)?;
-
-    Ok(())
+    cur_var.set_value("ProxyServer", &self.server)?;
+    cur_var.set_value("ProxyOverride", &self.bypass)
   }
 }
 
-#[cfg(target_os = "macos")]
-mod macos {
-  use super::*;
-
-  pub fn get_proxy_config() -> io::Result<SysProxyConfig> {
-    Ok(SysProxyConfig {
-      enable: false,
-      server: "server".into(),
-      bypass: "bypass".into(),
-    })
-  }
-
-  pub fn set_proxy_config(config: &SysProxyConfig) -> io::Result<()> {
-    Ok(())
-  }
-}
+// #[cfg(target_os = "macos")]
+// mod macos {
+//   use super::*;
 
-#[cfg(target_os = "windows")]
-pub use win::*;
+//   pub fn get_proxy_config() -> io::Result<SysProxyConfig> {
+//     Ok(SysProxyConfig {
+//       enable: false,
+//       server: "server".into(),
+//       bypass: "bypass".into(),
+//     })
+//   }
 
-#[cfg(target_os = "macos")]
-pub use macos::*;
+//   pub fn set_proxy_config(config: &SysProxyConfig) -> io::Result<()> {
+//     Ok(())
+//   }
+// }
diff --git a/src/services/api.ts b/src/services/api.ts
index 52d4cb1..8e0273f 100644
--- a/src/services/api.ts
+++ b/src/services/api.ts
@@ -13,8 +13,8 @@ export async function getAxios() {
   try {
     const info = await getClashInfo();
 
-    if (info?.controller?.server) server = info?.controller?.server;
-    if (info?.controller?.secret) secret = info?.controller?.secret;
+    if (info?.server) server = info?.server;
+    if (info?.secret) secret = info?.secret;
   } catch {}
 
   axiosIns = axios.create({
@@ -29,7 +29,7 @@ export async function getAxios() {
 export async function getInfomation() {
   if (server) return { server, secret };
   const info = await getClashInfo();
-  return info?.controller!;
+  return info!;
 }
 
 /// Get Version
diff --git a/src/services/cmds.ts b/src/services/cmds.ts
index 132cfb2..308ff63 100644
--- a/src/services/cmds.ts
+++ b/src/services/cmds.ts
@@ -2,7 +2,7 @@ import { invoke } from "@tauri-apps/api/tauri";
 import { ApiType, CmdType } from "./types";
 
 export async function getProfiles() {
-  return (await invoke<CmdType.ProfilesConfig>("get_profiles")) ?? {};
+  return invoke<CmdType.ProfilesConfig>("get_profiles");
 }
 
 export async function syncProfiles() {
diff --git a/src/services/types.ts b/src/services/types.ts
index 3544c42..e7c91f5 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -80,8 +80,9 @@ export namespace ApiType {
 export namespace CmdType {
   export interface ClashInfo {
     status: string;
-    controller?: { server?: string; secret?: string };
-    message?: string;
+    port?: string;
+    server?: string;
+    secret?: string;
   }
 
   export interface ProfileItem {
-- 
GitLab