feat: manage clash config
This commit is contained in:
parent
0bd29d71be
commit
d49fd37656
@ -1,12 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{ClashInfo, ProfileItem, ProfilesConfig, VergeConfig},
|
core::{ClashInfo, ProfileItem, ProfilesConfig, VergeConfig},
|
||||||
states::{ClashState, ProfilesState, VergeState},
|
states::{ClashState, ProfilesState, VergeState},
|
||||||
utils::{
|
utils::{dirs::app_home_dir, fetch::fetch_profile, sysopt::SysProxyConfig},
|
||||||
config::{read_clash, save_clash},
|
|
||||||
dirs::app_home_dir,
|
|
||||||
fetch::fetch_profile,
|
|
||||||
sysopt::SysProxyConfig,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use serde_yaml::Mapping;
|
use serde_yaml::Mapping;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@ -82,7 +77,7 @@ pub async fn update_profile(
|
|||||||
let current = profiles.current.clone().unwrap_or(0);
|
let current = profiles.current.clone().unwrap_or(0);
|
||||||
if current == index {
|
if current == index {
|
||||||
let clash = clash.0.lock().unwrap();
|
let clash = clash.0.lock().unwrap();
|
||||||
profiles.activate(clash.info.clone())
|
profiles.activate(&clash)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -105,7 +100,7 @@ pub fn select_profile(
|
|||||||
match profiles.put_current(index) {
|
match profiles.put_current(index) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
let clash = clash.0.lock().unwrap();
|
let clash = clash.0.lock().unwrap();
|
||||||
profiles.activate(clash.info.clone())
|
profiles.activate(&clash)
|
||||||
}
|
}
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
}
|
}
|
||||||
@ -123,7 +118,7 @@ pub fn delete_profile(
|
|||||||
Ok(change) => match change {
|
Ok(change) => match change {
|
||||||
true => {
|
true => {
|
||||||
let clash = clash_state.0.lock().unwrap();
|
let clash = clash_state.0.lock().unwrap();
|
||||||
profiles.activate(clash.info.clone())
|
profiles.activate(&clash)
|
||||||
}
|
}
|
||||||
false => Ok(()),
|
false => Ok(()),
|
||||||
},
|
},
|
||||||
@ -179,18 +174,10 @@ pub fn restart_sidecar(
|
|||||||
profiles_state: State<'_, ProfilesState>,
|
profiles_state: State<'_, ProfilesState>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let mut clash = clash_state.0.lock().unwrap();
|
let mut clash = clash_state.0.lock().unwrap();
|
||||||
|
let mut profiles = profiles_state.0.lock().unwrap();
|
||||||
|
|
||||||
match clash.restart_sidecar() {
|
match clash.restart_sidecar(&mut profiles) {
|
||||||
Ok(_) => {
|
Ok(_) => Ok(()),
|
||||||
let profiles = profiles_state.0.lock().unwrap();
|
|
||||||
match profiles.activate(clash.info.clone()) {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(err) => {
|
|
||||||
log::error!("{}", err);
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("{}", err);
|
log::error!("{}", err);
|
||||||
Err(err)
|
Err(err)
|
||||||
@ -203,26 +190,23 @@ pub fn restart_sidecar(
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_clash_info(clash_state: State<'_, ClashState>) -> Result<ClashInfo, String> {
|
pub fn get_clash_info(clash_state: State<'_, ClashState>) -> Result<ClashInfo, String> {
|
||||||
match clash_state.0.lock() {
|
match clash_state.0.lock() {
|
||||||
Ok(arc) => Ok(arc.info.clone()),
|
Ok(clash) => Ok(clash.info.clone()),
|
||||||
Err(_) => Err("failed to get clash lock".into()),
|
Err(_) => Err("failed to get clash lock".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// todo: need refactor
|
|
||||||
/// update the clash core config
|
/// update the clash core config
|
||||||
/// after putting the change to the clash core
|
/// after putting the change to the clash core
|
||||||
/// then we should save the latest config
|
/// then we should save the latest config
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn patch_clash_config(payload: Mapping) -> Result<(), String> {
|
pub fn patch_clash_config(
|
||||||
let mut config = read_clash();
|
payload: Mapping,
|
||||||
for (key, value) in payload.iter() {
|
clash_state: State<'_, ClashState>,
|
||||||
if config.contains_key(key) {
|
profiles_state: State<'_, ProfilesState>,
|
||||||
config[key] = value.clone();
|
) -> Result<(), String> {
|
||||||
} else {
|
let mut clash = clash_state.0.lock().unwrap();
|
||||||
config.insert(key.clone(), value.clone());
|
let mut profiles = profiles_state.0.lock().unwrap();
|
||||||
}
|
clash.patch_config(payload, &mut profiles)
|
||||||
}
|
|
||||||
save_clash(&config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get the system proxy
|
/// get the system proxy
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use super::ProfilesConfig;
|
||||||
use crate::utils::{config, dirs};
|
use crate::utils::{config, dirs};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml::{Mapping, Value};
|
use serde_yaml::{Mapping, Value};
|
||||||
@ -20,6 +21,9 @@ pub struct ClashInfo {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Clash {
|
pub struct Clash {
|
||||||
|
/// maintain the clash config
|
||||||
|
pub config: Mapping,
|
||||||
|
|
||||||
/// some info
|
/// some info
|
||||||
pub info: ClashInfo,
|
pub info: ClashInfo,
|
||||||
|
|
||||||
@ -32,17 +36,19 @@ static CLASH_CONFIG: &str = "config.yaml";
|
|||||||
// todo: be able to change config field
|
// todo: be able to change config field
|
||||||
impl Clash {
|
impl Clash {
|
||||||
pub fn new() -> Clash {
|
pub fn new() -> Clash {
|
||||||
|
let config = Clash::get_config();
|
||||||
|
let info = Clash::get_info(&config);
|
||||||
|
|
||||||
Clash {
|
Clash {
|
||||||
info: Clash::get_info(),
|
config,
|
||||||
|
info,
|
||||||
sidecar: None,
|
sidecar: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// parse the clash's config.yaml
|
/// parse the clash's config.yaml
|
||||||
/// get some information
|
/// get some information
|
||||||
fn get_info() -> ClashInfo {
|
fn get_info(clash_config: &Mapping) -> ClashInfo {
|
||||||
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_1 = Value::String("port".to_string());
|
||||||
let key_port_2 = Value::String("mixed-port".to_string());
|
let key_port_2 = Value::String("mixed-port".to_string());
|
||||||
let key_server = Value::String("external-controller".to_string());
|
let key_server = Value::String("external-controller".to_string());
|
||||||
@ -93,12 +99,6 @@ impl Clash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// update the clash info
|
|
||||||
pub fn update_info(&mut self) -> Result<(), String> {
|
|
||||||
self.info = Clash::get_info();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// run clash sidecar
|
/// run clash sidecar
|
||||||
pub fn run_sidecar(&mut self) -> Result<(), String> {
|
pub fn run_sidecar(&mut self) -> Result<(), String> {
|
||||||
let app_dir = dirs::app_home_dir();
|
let app_dir = dirs::app_home_dir();
|
||||||
@ -138,10 +138,56 @@ impl Clash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// restart clash sidecar
|
/// restart clash sidecar
|
||||||
pub fn restart_sidecar(&mut self) -> Result<(), String> {
|
/// should reactivate profile after restart
|
||||||
self.update_info()?;
|
pub fn restart_sidecar(&mut self, profiles: &mut ProfilesConfig) -> Result<(), String> {
|
||||||
|
self.update_config();
|
||||||
self.drop_sidecar()?;
|
self.drop_sidecar()?;
|
||||||
self.run_sidecar()
|
self.run_sidecar()?;
|
||||||
|
profiles.activate(&self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// update the clash info
|
||||||
|
pub fn update_config(&mut self) {
|
||||||
|
self.config = Clash::get_config();
|
||||||
|
self.info = Clash::get_info(&self.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get clash config
|
||||||
|
fn get_config() -> Mapping {
|
||||||
|
config::read_yaml::<Mapping>(dirs::app_home_dir().join(CLASH_CONFIG))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// save the clash config
|
||||||
|
fn save_config(&self) -> Result<(), String> {
|
||||||
|
config::save_yaml(
|
||||||
|
dirs::app_home_dir().join(CLASH_CONFIG),
|
||||||
|
&self.config,
|
||||||
|
Some("# Default Config For Clash Core\n\n"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// patch update the clash config
|
||||||
|
pub fn patch_config(
|
||||||
|
&mut self,
|
||||||
|
patch: Mapping,
|
||||||
|
profiles: &mut ProfilesConfig,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
for (key, value) in patch.iter() {
|
||||||
|
let value = value.clone();
|
||||||
|
let key_str = key.as_str().clone().unwrap_or("");
|
||||||
|
|
||||||
|
// restart the clash
|
||||||
|
if key_str == "mixed-port" {
|
||||||
|
self.restart_sidecar(profiles)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.config.contains_key(key) {
|
||||||
|
self.config[key] = value.clone();
|
||||||
|
} else {
|
||||||
|
self.config.insert(key.clone(), value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.save_config()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use super::{Clash, ClashInfo};
|
||||||
use crate::utils::{config, dirs};
|
use crate::utils::{config, dirs};
|
||||||
use reqwest::header::HeaderMap;
|
use reqwest::header::HeaderMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -8,8 +9,6 @@ use std::fs::File;
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use super::ClashInfo;
|
|
||||||
|
|
||||||
/// Define the `profiles.yaml` schema
|
/// Define the `profiles.yaml` schema
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct ProfilesConfig {
|
pub struct ProfilesConfig {
|
||||||
@ -224,7 +223,7 @@ impl ProfilesConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// activate current profile
|
/// activate current profile
|
||||||
pub fn activate(&self, clash_config: ClashInfo) -> Result<(), String> {
|
pub fn activate(&self, clash: &Clash) -> Result<(), String> {
|
||||||
let current = self.current.unwrap_or(0);
|
let current = self.current.unwrap_or(0);
|
||||||
match self.items.clone() {
|
match self.items.clone() {
|
||||||
Some(items) => {
|
Some(items) => {
|
||||||
@ -233,11 +232,13 @@ impl ProfilesConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let profile = items[current].clone();
|
let profile = items[current].clone();
|
||||||
|
let clash_config = clash.config.clone();
|
||||||
|
let clash_info = clash.info.clone();
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
let mut count = 5; // retry times
|
let mut count = 5; // retry times
|
||||||
let mut err = String::from("");
|
let mut err = String::from("");
|
||||||
while count > 0 {
|
while count > 0 {
|
||||||
match activate_profile(&profile, &clash_config).await {
|
match activate_profile(&profile, &clash_config, &clash_info).await {
|
||||||
Ok(_) => return,
|
Ok(_) => return,
|
||||||
Err(e) => err = e,
|
Err(e) => err = e,
|
||||||
}
|
}
|
||||||
@ -254,7 +255,11 @@ impl ProfilesConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// put the profile to clash
|
/// put the profile to clash
|
||||||
pub async fn activate_profile(profile_item: &ProfileItem, info: &ClashInfo) -> Result<(), String> {
|
pub async fn activate_profile(
|
||||||
|
profile_item: &ProfileItem,
|
||||||
|
clash_config: &Mapping,
|
||||||
|
clash_info: &ClashInfo,
|
||||||
|
) -> Result<(), String> {
|
||||||
// temp profile's path
|
// temp profile's path
|
||||||
let temp_path = temp_dir().join(PROFILE_TEMP);
|
let temp_path = temp_dir().join(PROFILE_TEMP);
|
||||||
|
|
||||||
@ -267,25 +272,46 @@ pub async fn activate_profile(profile_item: &ProfileItem, info: &ClashInfo) -> R
|
|||||||
|
|
||||||
let file_path = dirs::app_home_dir().join("profiles").join(file_name);
|
let file_path = dirs::app_home_dir().join("profiles").join(file_name);
|
||||||
if !file_path.exists() {
|
if !file_path.exists() {
|
||||||
return Err(format!("profile `{:?}` not exists", file_path));
|
return Err(format!(
|
||||||
|
"profile `{}` not exists",
|
||||||
|
file_path.as_os_str().to_str().unwrap()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// begin to generate the new profile config
|
||||||
|
let def_config = config::read_yaml::<Mapping>(file_path.clone());
|
||||||
|
let mut new_config = Mapping::new();
|
||||||
|
|
||||||
// Only the following fields are allowed:
|
// Only the following fields are allowed:
|
||||||
// proxies/proxy-providers/proxy-groups/rule-providers/rules
|
// proxies/proxy-providers/proxy-groups/rule-providers/rules
|
||||||
let config = config::read_yaml::<Mapping>(file_path.clone());
|
let valid_keys = vec![
|
||||||
let mut new_config = Mapping::new();
|
|
||||||
vec![
|
|
||||||
"proxies",
|
"proxies",
|
||||||
"proxy-providers",
|
"proxy-providers",
|
||||||
"proxy-groups",
|
"proxy-groups",
|
||||||
"rule-providers",
|
"rule-providers",
|
||||||
"rules",
|
"rules",
|
||||||
]
|
];
|
||||||
.iter()
|
valid_keys.iter().for_each(|key| {
|
||||||
.map(|item| Value::String(item.to_string()))
|
let key = Value::String(key.to_string());
|
||||||
.for_each(|key| {
|
if def_config.contains_key(&key) {
|
||||||
if config.contains_key(&key) {
|
let value = def_config[&key].clone();
|
||||||
let value = config[&key].clone();
|
new_config.insert(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// add some of the clash `config.yaml` config to it
|
||||||
|
let valid_keys = vec![
|
||||||
|
"mixed-port",
|
||||||
|
"log-level",
|
||||||
|
"allow-lan",
|
||||||
|
"external-controller",
|
||||||
|
"secret",
|
||||||
|
"ipv6",
|
||||||
|
];
|
||||||
|
valid_keys.iter().for_each(|key| {
|
||||||
|
let key = Value::String(key.to_string());
|
||||||
|
if clash_config.contains_key(&key) {
|
||||||
|
let value = clash_config[&key].clone();
|
||||||
new_config.insert(key, value);
|
new_config.insert(key, value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -297,12 +323,12 @@ pub async fn activate_profile(profile_item: &ProfileItem, info: &ClashInfo) -> R
|
|||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let server = format!("http://{}/configs", info.server.clone().unwrap());
|
let server = format!("http://{}/configs", clash_info.server.clone().unwrap());
|
||||||
|
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
headers.insert("Content-Type", "application/json".parse().unwrap());
|
headers.insert("Content-Type", "application/json".parse().unwrap());
|
||||||
|
|
||||||
if let Some(secret) = info.secret.clone() {
|
if let Some(secret) = clash_info.secret.clone() {
|
||||||
headers.insert(
|
headers.insert(
|
||||||
"Authorization",
|
"Authorization",
|
||||||
format!("Bearer {}", secret).parse().unwrap(),
|
format!("Bearer {}", secret).parse().unwrap(),
|
||||||
|
@ -41,14 +41,11 @@ fn main() -> std::io::Result<()> {
|
|||||||
}
|
}
|
||||||
"restart_clash" => {
|
"restart_clash" => {
|
||||||
let clash_state = app_handle.state::<states::ClashState>();
|
let clash_state = app_handle.state::<states::ClashState>();
|
||||||
|
let profiles_state = app_handle.state::<states::ProfilesState>();
|
||||||
let mut clash = clash_state.0.lock().unwrap();
|
let mut clash = clash_state.0.lock().unwrap();
|
||||||
match clash.restart_sidecar() {
|
let mut profiles = profiles_state.0.lock().unwrap();
|
||||||
|
match clash.restart_sidecar(&mut profiles) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let profiles = app_handle.state::<states::ProfilesState>();
|
|
||||||
let profiles = profiles.0.lock().unwrap();
|
|
||||||
if let Err(err) = profiles.activate(clash.info.clone()) {
|
|
||||||
log::error!("{}", err);
|
|
||||||
}
|
|
||||||
let window = app_handle.get_window("main").unwrap();
|
let window = app_handle.get_window("main").unwrap();
|
||||||
window.emit("restart_clash", "yes").unwrap();
|
window.emit("restart_clash", "yes").unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use crate::utils::dirs;
|
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use serde_yaml::Mapping;
|
|
||||||
use std::{fs, path::PathBuf};
|
use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
/// read data from yaml as struct T
|
/// read data from yaml as struct T
|
||||||
@ -32,17 +30,3 @@ pub fn save_yaml<T: Serialize>(
|
|||||||
Err(_) => Err("can not convert the data to yaml".into()),
|
Err(_) => Err("can not convert the data to yaml".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get Clash Core Config `config.yaml`
|
|
||||||
pub fn read_clash() -> Mapping {
|
|
||||||
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(
|
|
||||||
dirs::app_home_dir().join("config.yaml"),
|
|
||||||
config,
|
|
||||||
Some("# Default Config For Clash Core\n\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -39,7 +39,7 @@ pub fn resolve_setup(app: &App) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
*profiles = ProfilesConfig::read_file();
|
*profiles = ProfilesConfig::read_file();
|
||||||
if let Err(err) = profiles.activate(clash.info.clone()) {
|
if let Err(err) = profiles.activate(&clash) {
|
||||||
log::error!("{}", err);
|
log::error!("{}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user