diff --git a/src-tauri/src/cmd.rs b/src-tauri/src/cmd.rs
deleted file mode 100644
index 421c724ece06f596953ae506a721d9f6311ecb4d..0000000000000000000000000000000000000000
--- a/src-tauri/src/cmd.rs
+++ /dev/null
@@ -1,215 +0,0 @@
-use crate::{
-  config::{read_profiles, save_profiles, ProfileItem, ProfilesConfig},
-  events::{
-    emit::ClashInfoPayload,
-    state::{ClashInfoState, ProfileLock},
-  },
-  utils::{
-    app_home_dir,
-    clash::{self, put_clash_profile},
-    fetch::fetch_profile,
-    sysopt::{set_proxy_config, SysProxyConfig},
-  },
-};
-use std::fs::File;
-use std::io::Write;
-use tauri::{api::process::kill_children, AppHandle, State};
-
-#[tauri::command]
-pub fn restart_sidebar(app_handle: AppHandle, clash_info: State<'_, ClashInfoState>) {
-  kill_children();
-  let payload = clash::run_clash_bin(&app_handle);
-
-  if let Ok(mut arc) = clash_info.0.lock() {
-    *arc = payload;
-  }
-}
-
-#[tauri::command]
-pub fn get_clash_info(clash_info: State<'_, ClashInfoState>) -> Option<ClashInfoPayload> {
-  match clash_info.0.lock() {
-    Ok(arc) => Some(arc.clone()),
-    _ => None,
-  }
-}
-
-/// Import the Profile from url and
-/// save to the `profiles.yaml` file
-#[tauri::command]
-pub async fn import_profile(url: String, lock: State<'_, ProfileLock>) -> Result<String, String> {
-  let result = match fetch_profile(&url).await {
-    Some(r) => r,
-    None => {
-      log::error!("failed to fetch profile from `{}`", url);
-      return Err(format!("failed"));
-    }
-  };
-
-  let path = app_home_dir().join("profiles").join(&result.file);
-  File::create(path)
-    .unwrap()
-    .write(result.data.as_bytes())
-    .unwrap();
-
-  // get lock
-  match lock.0.lock() {
-    Ok(_) => {}
-    Err(_) => return Err(format!("can not get file locked")),
-  };
-
-  // update profiles.yaml
-  let mut profiles = read_profiles();
-  let mut items = match profiles.items {
-    Some(p) => p,
-    None => vec![],
-  };
-
-  let profile = ProfileItem {
-    name: Some(result.name),
-    file: Some(result.file),
-    mode: Some(format!("rule")),
-    url: Some(url),
-    selected: Some(vec![]), // Todo: parse the selected list
-    extra: Some(result.extra),
-  };
-
-  items.push(profile);
-  profiles.items = Some(items);
-  save_profiles(&profiles);
-
-  Ok(format!("success"))
-}
-
-#[tauri::command]
-pub fn get_profiles(lock: State<'_, ProfileLock>) -> Option<ProfilesConfig> {
-  match lock.0.lock() {
-    Ok(_) => Some(read_profiles()),
-    Err(_) => None,
-  }
-}
-
-#[tauri::command]
-/// update the profile config
-pub fn set_profiles(
-  current: usize,
-  profile: ProfileItem,
-  lock: State<'_, ProfileLock>,
-) -> Result<(), String> {
-  match lock.0.lock() {
-    Ok(_) => {}
-    Err(_) => return Err(format!("can not get file locked")),
-  };
-
-  let mut profiles = read_profiles();
-  let mut items = match profiles.items {
-    Some(p) => p,
-    None => vec![],
-  };
-
-  if current >= items.len() {
-    return Err(format!("out of profiles bound"));
-  }
-
-  let mut origin = items[current].clone();
-
-  if profile.name.is_some() {
-    origin.name = profile.name;
-  }
-  if profile.file.is_some() {
-    origin.file = profile.file;
-  }
-  if profile.mode.is_some() {
-    origin.mode = profile.mode;
-  }
-  if profile.url.is_some() {
-    origin.url = profile.url;
-  }
-  if profile.selected.is_some() {
-    origin.selected = profile.selected;
-  }
-  if profile.extra.is_some() {
-    origin.extra = profile.extra;
-  }
-
-  items[current] = origin;
-  profiles.items = Some(items);
-  save_profiles(&profiles);
-
-  Ok(())
-}
-
-#[tauri::command]
-/// change to target profile
-pub async fn put_profiles(
-  current: usize,
-  lock: State<'_, ProfileLock>,
-  clash_info: State<'_, ClashInfoState>,
-) -> Result<(), String> {
-  match lock.0.lock() {
-    Ok(_) => {}
-    Err(_) => return Err(format!("can not get file locked")),
-  };
-
-  let clash_info = match clash_info.0.lock() {
-    Ok(arc) => arc.clone(),
-    _ => return Err(format!("can not get clash info")),
-  };
-
-  let mut profiles = read_profiles();
-  let items_len = match &profiles.items {
-    Some(p) => p.len(),
-    None => 0,
-  };
-
-  if current >= items_len {
-    return Err(format!(
-      "failed to change profile to the index `{}`",
-      current
-    ));
-  }
-
-  profiles.current = Some(current as u32);
-  save_profiles(&profiles);
-  put_clash_profile(&clash_info).await
-}
-
-#[tauri::command]
-/// set system proxy
-pub fn set_sys_proxy(enable: bool, clash_info: State<'_, ClashInfoState>) -> Result<(), String> {
-  let clash_info = match clash_info.0.lock() {
-    Ok(arc) => arc.clone(),
-    _ => return Err(format!("can not get clash 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());
-    // todo
-    let bypass = String::from("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.*");
-
-    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")),
-  }
-}
diff --git a/src-tauri/src/cmds/mod.rs b/src-tauri/src/cmds/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c5be45b733a18c5b3f3d6e0974e6898663cce9d9
--- /dev/null
+++ b/src-tauri/src/cmds/mod.rs
@@ -0,0 +1,2 @@
+pub mod profile;
+pub mod some;
diff --git a/src-tauri/src/cmds/profile.rs b/src-tauri/src/cmds/profile.rs
new file mode 100644
index 0000000000000000000000000000000000000000..26d672c83868b07eedb1912aac076c570f2f8695
--- /dev/null
+++ b/src-tauri/src/cmds/profile.rs
@@ -0,0 +1,172 @@
+use crate::{
+  config::{read_profiles, save_profiles, ProfileItem, ProfilesConfig},
+  events::state::{ClashInfoState, ProfileLock},
+  utils::{app_home_dir, clash::put_clash_profile, fetch::fetch_profile},
+};
+use std::fs::File;
+use std::io::Write;
+use tauri::State;
+
+/// Import the profile from url
+/// and save to `profiles.yaml`
+#[tauri::command]
+pub async fn import_profile(url: String, lock: State<'_, ProfileLock>) -> Result<(), String> {
+  let result = match fetch_profile(&url).await {
+    Some(r) => r,
+    None => {
+      log::error!("failed to fetch profile from `{}`", url);
+      return Err(format!("failed to fetch profile from `{}`", url));
+    }
+  };
+
+  // get lock
+  if lock.0.lock().is_err() {
+    return Err(format!("can not get file lock"));
+  }
+
+  // save the profile file
+  let path = app_home_dir().join("profiles").join(&result.file);
+  let file_data = result.data.as_bytes();
+  File::create(path).unwrap().write(file_data).unwrap();
+
+  // update `profiles.yaml`
+  let mut profiles = read_profiles();
+  let mut items = profiles.items.unwrap_or(vec![]);
+
+  items.push(ProfileItem {
+    name: Some(result.name),
+    file: Some(result.file),
+    mode: Some(format!("rule")),
+    url: Some(url),
+    selected: Some(vec![]),
+    extra: Some(result.extra),
+  });
+  profiles.items = Some(items);
+  save_profiles(&profiles)
+}
+
+/// 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, lock: State<'_, ProfileLock>) -> Result<(), String> {
+  // get lock
+  if lock.0.lock().is_err() {
+    return Err(format!("can not get file lock"));
+  }
+
+  // update `profiles.yaml`
+  let mut profiles = read_profiles();
+  let mut items = profiles.items.unwrap_or(vec![]);
+
+  if index >= items.len() {
+    return Err(format!("the index out of bound"));
+  }
+
+  let url = match &items[index].url {
+    Some(u) => u,
+    None => return Err(format!("invalid url")),
+  };
+
+  let result = match fetch_profile(&url).await {
+    Some(r) => r,
+    None => {
+      log::error!("failed to fetch profile from `{}`", url);
+      return Err(format!("failed to fetch profile from `{}`", url));
+    }
+  };
+
+  // update file
+  let file_path = &items[index].file.as_ref().unwrap();
+  let file_path = app_home_dir().join("profiles").join(file_path);
+  let file_data = result.data.as_bytes();
+  File::create(file_path).unwrap().write(file_data).unwrap();
+
+  items[index].name = Some(result.name);
+  items[index].extra = Some(result.extra);
+  profiles.items = Some(items);
+  save_profiles(&profiles)
+}
+
+/// get all profiles from `profiles.yaml`
+/// do not acquire the lock of ProfileLock
+#[tauri::command]
+pub fn get_profiles() -> Result<ProfilesConfig, String> {
+  Ok(read_profiles())
+}
+
+/// patch the profile config
+#[tauri::command]
+pub fn set_profiles(
+  index: usize,
+  profile: ProfileItem,
+  lock: State<'_, ProfileLock>,
+) -> Result<(), String> {
+  // get lock
+  if lock.0.lock().is_err() {
+    return Err(format!("can not get file lock"));
+  }
+
+  let mut profiles = read_profiles();
+  let mut items = profiles.items.unwrap_or(vec![]);
+
+  if index >= items.len() {
+    return Err(format!("the index out of bound"));
+  }
+
+  if profile.name.is_some() {
+    items[index].name = profile.name;
+  }
+  if profile.file.is_some() {
+    items[index].file = profile.file;
+  }
+  if profile.mode.is_some() {
+    items[index].mode = profile.mode;
+  }
+  if profile.url.is_some() {
+    items[index].url = profile.url;
+  }
+  if profile.selected.is_some() {
+    items[index].selected = profile.selected;
+  }
+  if profile.extra.is_some() {
+    items[index].extra = profile.extra;
+  }
+
+  profiles.items = Some(items);
+  save_profiles(&profiles)
+}
+
+/// change the current profile
+#[tauri::command]
+pub async fn put_profiles(
+  current: usize,
+  lock: State<'_, ProfileLock>,
+  clash_info: State<'_, ClashInfoState>,
+) -> Result<(), String> {
+  if lock.0.lock().is_err() {
+    return Err(format!("can not get file lock"));
+  }
+
+  let clash_info = match clash_info.0.lock() {
+    Ok(arc) => arc.clone(),
+    _ => return Err(format!("can not get clash info")),
+  };
+
+  let mut profiles = read_profiles();
+  let items_len = match &profiles.items {
+    Some(list) => list.len(),
+    None => 0,
+  };
+
+  if current >= items_len {
+    return Err(format!("the index out of bound"));
+  }
+
+  profiles.current = Some(current as u32);
+  match save_profiles(&profiles) {
+    Ok(_) => put_clash_profile(&clash_info).await,
+    Err(err) => Err(err),
+  }
+}
diff --git a/src-tauri/src/cmds/some.rs b/src-tauri/src/cmds/some.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9b7c556b56e4757647f16a9eb672d33b02af7d76
--- /dev/null
+++ b/src-tauri/src/cmds/some.rs
@@ -0,0 +1,70 @@
+use crate::{
+  events::{emit::ClashInfoPayload, state::ClashInfoState},
+  utils::{
+    clash::run_clash_bin,
+    sysopt::{set_proxy_config, SysProxyConfig},
+  },
+};
+use tauri::{api::process::kill_children, AppHandle, State};
+
+/// restart the sidecar
+#[tauri::command]
+pub fn restart_sidecar(app_handle: AppHandle, clash_info: State<'_, ClashInfoState>) {
+  kill_children();
+  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
+#[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")),
+  }
+}
+
+/// set the system proxy
+/// Tips: only support windows now
+#[tauri::command]
+pub fn set_sys_proxy(enable: bool, clash_info: State<'_, ClashInfoState>) -> Result<(), String> {
+  let clash_info = match clash_info.0.lock() {
+    Ok(arc) => arc.clone(),
+    _ => return Err(format!("can not get clash 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());
+    // todo
+    let bypass = String::from("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.*");
+
+    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")),
+  }
+}
diff --git a/src-tauri/src/config/operate.rs b/src-tauri/src/config/operate.rs
index 388ed7406946e04f075307967f7b699ce3961efe..b060fd50c68086a4bd434e7e700a242c4ec13a0a 100644
--- a/src-tauri/src/config/operate.rs
+++ b/src-tauri/src/config/operate.rs
@@ -99,13 +99,12 @@ pub fn read_profiles() -> ProfilesConfig {
 }
 
 /// Save Verge App Config
-pub fn save_profiles(profiles: &ProfilesConfig) {
+pub fn save_profiles(profiles: &ProfilesConfig) -> Result<(), String> {
   save_yaml(
     app_home_dir().join("profiles.yaml"),
     profiles,
     Some("# Profiles Config for Clash Verge\n\n"),
   )
-  .unwrap();
 }
 
 #[test]
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 3177d69c4d60e90364a303e934f248e69fa6e429..271769e16930b760b3e4324b9ca1acd20f9ea7cd 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -5,7 +5,7 @@
 
 extern crate tauri;
 
-mod cmd;
+mod cmds;
 mod config;
 mod events;
 mod utils;
@@ -57,13 +57,14 @@ fn main() -> std::io::Result<()> {
       _ => {}
     })
     .invoke_handler(tauri::generate_handler![
-      cmd::restart_sidebar,
-      cmd::get_clash_info,
-      cmd::import_profile,
-      cmd::get_profiles,
-      cmd::set_profiles,
-      cmd::put_profiles,
-      cmd::set_sys_proxy,
+      cmds::some::restart_sidecar,
+      cmds::some::get_clash_info,
+      cmds::some::set_sys_proxy,
+      cmds::profile::import_profile,
+      cmds::profile::update_profile,
+      cmds::profile::get_profiles,
+      cmds::profile::set_profiles,
+      cmds::profile::put_profiles,
     ])
     .build(tauri::generate_context!())
     .expect("error while running tauri application");
diff --git a/src/components/profile-item.tsx b/src/components/profile-item.tsx
index a6c19975349a4600cb2c8b320099d26c6ae528d4..5b004474d4c42dc4d106919b05487e2c9a6576b9 100644
--- a/src/components/profile-item.tsx
+++ b/src/components/profile-item.tsx
@@ -28,10 +28,11 @@ interface Props {
   selected: boolean;
   itemData: ProfileItem;
   onClick: () => void;
+  onUpdate: () => void;
 }
 
 const ProfileItemComp: React.FC<Props> = (props) => {
-  const { selected, itemData, onClick } = props;
+  const { selected, itemData, onClick, onUpdate } = props;
 
   const { name = "Profile", extra } = itemData;
   const { upload = 0, download = 0, total = 0 } = extra ?? {};
@@ -87,6 +88,7 @@ const ProfileItemComp: React.FC<Props> = (props) => {
           color="inherit"
           onClick={(e) => {
             e.stopPropagation();
+            onUpdate();
           }}
         >
           <MenuRounded />
diff --git a/src/pages/rules.tsx b/src/pages/rules.tsx
index 030a0b8772f06cf800117d086d8c0daa132dd38c..37b6b16543ec3d71ab749661112fb4c6ae95ad97 100644
--- a/src/pages/rules.tsx
+++ b/src/pages/rules.tsx
@@ -1,7 +1,12 @@
 import { useState } from "react";
 import useSWR, { useSWRConfig } from "swr";
 import { Box, Button, Grid, TextField, Typography } from "@mui/material";
-import { getProfiles, importProfile, putProfiles } from "../services/command";
+import {
+  getProfiles,
+  importProfile,
+  putProfiles,
+  updateProfile,
+} from "../services/command";
 import ProfileItemComp from "../components/profile-item";
 import useNotice from "../utils/use-notice";
 
@@ -33,6 +38,16 @@ const RulesPage = () => {
       });
   };
 
+  const onUpdateProfile = (index: number) => {
+    updateProfile(index)
+      .then(() => {
+        mutate("getProfiles");
+      })
+      .catch((err) => {
+        console.error(err);
+      });
+  };
+
   return (
     <Box sx={{ width: 0.9, maxWidth: "850px", mx: "auto", mb: 2 }}>
       <Typography variant="h4" component="h1" sx={{ py: 2, mb: 1 }}>
@@ -66,6 +81,7 @@ const RulesPage = () => {
               selected={profiles.current === idx}
               itemData={item}
               onClick={() => onProfileChange(idx)}
+              onUpdate={() => onUpdateProfile(idx)}
             />
           </Grid>
         ))}
diff --git a/src/services/command.ts b/src/services/command.ts
index 7489a45e0b522b690ce84e9c2e87dfbb2374dff3..84045b2612f431d8c62258537966d003f3709b8f 100644
--- a/src/services/command.ts
+++ b/src/services/command.ts
@@ -1,7 +1,7 @@
 import { invoke } from "@tauri-apps/api/tauri";
 
 export async function restartSidecar() {
-  return invoke<void>("restart_sidebar");
+  return invoke<void>("restart_sidecar");
 }
 
 export interface ClashInfo {
@@ -15,7 +15,11 @@ export async function getClashInfo() {
 }
 
 export async function importProfile(url: string) {
-  return invoke<string>("import_profile", { url });
+  return invoke<void>("import_profile", { url });
+}
+
+export async function updateProfile(index: number) {
+  return invoke<void>("update_profile", { index });
 }
 
 export interface ProfileItem {