feat: manage clash config
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use super::ProfilesConfig;
|
||||
use crate::utils::{config, dirs};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml::{Mapping, Value};
|
||||
@@ -20,6 +21,9 @@ pub struct ClashInfo {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Clash {
|
||||
/// maintain the clash config
|
||||
pub config: Mapping,
|
||||
|
||||
/// some info
|
||||
pub info: ClashInfo,
|
||||
|
||||
@@ -32,17 +36,19 @@ static CLASH_CONFIG: &str = "config.yaml";
|
||||
// todo: be able to change config field
|
||||
impl Clash {
|
||||
pub fn new() -> Clash {
|
||||
let config = Clash::get_config();
|
||||
let info = Clash::get_info(&config);
|
||||
|
||||
Clash {
|
||||
info: Clash::get_info(),
|
||||
config,
|
||||
info,
|
||||
sidecar: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// parse the clash's config.yaml
|
||||
/// get some information
|
||||
fn get_info() -> ClashInfo {
|
||||
let clash_config = config::read_yaml::<Mapping>(dirs::app_home_dir().join(CLASH_CONFIG));
|
||||
|
||||
fn get_info(clash_config: &Mapping) -> ClashInfo {
|
||||
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());
|
||||
@@ -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
|
||||
pub fn run_sidecar(&mut self) -> Result<(), String> {
|
||||
let app_dir = dirs::app_home_dir();
|
||||
@@ -138,10 +138,56 @@ impl Clash {
|
||||
}
|
||||
|
||||
/// restart clash sidecar
|
||||
pub fn restart_sidecar(&mut self) -> Result<(), String> {
|
||||
self.update_info()?;
|
||||
/// should reactivate profile after restart
|
||||
pub fn restart_sidecar(&mut self, profiles: &mut ProfilesConfig) -> Result<(), String> {
|
||||
self.update_config();
|
||||
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 reqwest::header::HeaderMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -8,8 +9,6 @@ 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 {
|
||||
@@ -224,7 +223,7 @@ impl ProfilesConfig {
|
||||
}
|
||||
|
||||
/// 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);
|
||||
match self.items.clone() {
|
||||
Some(items) => {
|
||||
@@ -233,11 +232,13 @@ impl ProfilesConfig {
|
||||
}
|
||||
|
||||
let profile = items[current].clone();
|
||||
let clash_config = clash.config.clone();
|
||||
let clash_info = clash.info.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 {
|
||||
match activate_profile(&profile, &clash_config, &clash_info).await {
|
||||
Ok(_) => return,
|
||||
Err(e) => err = e,
|
||||
}
|
||||
@@ -254,7 +255,11 @@ impl ProfilesConfig {
|
||||
}
|
||||
|
||||
/// 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
|
||||
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);
|
||||
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:
|
||||
// proxies/proxy-providers/proxy-groups/rule-providers/rules
|
||||
let config = config::read_yaml::<Mapping>(file_path.clone());
|
||||
let mut new_config = Mapping::new();
|
||||
vec![
|
||||
let valid_keys = 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();
|
||||
];
|
||||
valid_keys.iter().for_each(|key| {
|
||||
let key = Value::String(key.to_string());
|
||||
if def_config.contains_key(&key) {
|
||||
let value = def_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);
|
||||
}
|
||||
});
|
||||
@@ -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();
|
||||
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(
|
||||
"Authorization",
|
||||
format!("Bearer {}", secret).parse().unwrap(),
|
||||
|
||||
Reference in New Issue
Block a user