feat: refactor commands and support update profile
This commit is contained in:
parent
c1bcfc6785
commit
98378e6261
@ -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")),
|
||||
}
|
||||
}
|
2
src-tauri/src/cmds/mod.rs
Normal file
2
src-tauri/src/cmds/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod profile;
|
||||
pub mod some;
|
172
src-tauri/src/cmds/profile.rs
Normal file
172
src-tauri/src/cmds/profile.rs
Normal file
@ -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),
|
||||
}
|
||||
}
|
70
src-tauri/src/cmds/some.rs
Normal file
70
src-tauri/src/cmds/some.rs
Normal file
@ -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")),
|
||||
}
|
||||
}
|
@ -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]
|
||||
|
@ -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");
|
||||
|
@ -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 />
|
||||
|
@ -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>
|
||||
))}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user