2021-12-08 18:40:52 +03:00
|
|
|
extern crate log;
|
2021-12-04 09:31:26 +03:00
|
|
|
|
2021-12-14 13:37:03 +03:00
|
|
|
use crate::{
|
|
|
|
events::emit::{clash_start, ClashInfoPayload},
|
2021-12-17 21:29:54 +03:00
|
|
|
utils::{
|
|
|
|
app_home_dir,
|
2021-12-19 19:31:57 +03:00
|
|
|
config::{read_clash_controller, read_profiles, read_yaml, save_yaml},
|
2021-12-17 21:29:54 +03:00
|
|
|
},
|
2021-12-14 13:37:03 +03:00
|
|
|
};
|
2021-12-14 16:58:29 +03:00
|
|
|
use reqwest::header::HeaderMap;
|
2021-12-19 19:31:57 +03:00
|
|
|
use serde_yaml::{Mapping, Value};
|
|
|
|
use std::{collections::HashMap, env::temp_dir};
|
2021-12-14 13:37:03 +03:00
|
|
|
use tauri::{
|
|
|
|
api::process::{Command, CommandEvent},
|
|
|
|
AppHandle,
|
|
|
|
};
|
2021-12-04 09:31:26 +03:00
|
|
|
|
|
|
|
/// Run the clash bin
|
2021-12-14 13:37:03 +03:00
|
|
|
pub fn run_clash_bin(app_handle: &AppHandle) -> ClashInfoPayload {
|
2021-12-08 18:40:52 +03:00
|
|
|
let app_dir = app_home_dir();
|
2021-12-19 12:49:16 +03:00
|
|
|
let app_dir = app_dir.as_os_str().to_str().unwrap();
|
|
|
|
|
2021-12-14 13:37:03 +03:00
|
|
|
let mut payload = ClashInfoPayload {
|
|
|
|
status: "success".to_string(),
|
|
|
|
controller: None,
|
|
|
|
message: None,
|
|
|
|
};
|
2021-12-08 18:40:52 +03:00
|
|
|
|
2021-12-19 12:49:16 +03:00
|
|
|
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()),
|
2021-12-14 13:37:03 +03:00
|
|
|
},
|
2021-12-19 12:49:16 +03:00
|
|
|
Err(err) => Err(err.to_string()),
|
|
|
|
};
|
|
|
|
|
|
|
|
match result {
|
|
|
|
Ok((mut rx, _)) => {
|
|
|
|
log::info!("Successfully execute clash sidecar");
|
|
|
|
payload.controller = Some(read_clash_controller());
|
|
|
|
|
|
|
|
tauri::async_runtime::spawn(async move {
|
|
|
|
while let Some(event) = rx.recv().await {
|
|
|
|
match event {
|
|
|
|
CommandEvent::Stdout(line) => log::info!("{}", line),
|
|
|
|
CommandEvent::Stderr(err) => log::error!("{}", err),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2021-12-14 13:37:03 +03:00
|
|
|
Err(err) => {
|
2021-12-19 12:49:16 +03:00
|
|
|
log::error!("Failed to execute clash sidecar for \"{}\"", err);
|
2021-12-14 13:37:03 +03:00
|
|
|
payload.status = "error".to_string();
|
|
|
|
payload.message = Some(err.to_string());
|
2021-12-04 09:31:26 +03:00
|
|
|
}
|
2021-12-14 13:37:03 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
clash_start(app_handle, &payload);
|
|
|
|
payload
|
2021-12-04 09:31:26 +03:00
|
|
|
}
|
2021-12-14 16:58:29 +03:00
|
|
|
|
|
|
|
/// Update the clash profile firstly
|
|
|
|
pub async fn put_clash_profile(payload: &ClashInfoPayload) -> Result<(), String> {
|
|
|
|
let profile = {
|
|
|
|
let profiles = read_profiles();
|
2021-12-17 21:29:54 +03:00
|
|
|
let current = profiles.current.unwrap_or(0) as usize;
|
2021-12-14 16:58:29 +03:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-12-19 19:31:57 +03:00
|
|
|
// temp profile's path
|
|
|
|
let temp_path = temp_dir().join("clash-verge-runtime.yaml");
|
|
|
|
|
2021-12-14 16:58:29 +03:00
|
|
|
// generate temp profile
|
2021-12-19 19:31:57 +03:00
|
|
|
{
|
|
|
|
let file_name = match profile.file {
|
|
|
|
Some(file_name) => file_name.clone(),
|
|
|
|
None => {
|
|
|
|
return Err(format!("profile item should have `file` field"));
|
|
|
|
}
|
|
|
|
};
|
2021-12-14 16:58:29 +03:00
|
|
|
|
2021-12-19 19:31:57 +03:00
|
|
|
let file_path = app_home_dir().join("profiles").join(file_name);
|
|
|
|
if !file_path.exists() {
|
|
|
|
return Err(format!("profile `{:?}` not exists", file_path));
|
|
|
|
}
|
2021-12-14 16:58:29 +03:00
|
|
|
|
2021-12-19 19:31:57 +03:00
|
|
|
// Only the following fields are allowed:
|
|
|
|
// proxies/proxy-providers/proxy-groups/rule-providers/rules
|
|
|
|
let 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 save_yaml(
|
|
|
|
temp_path.clone(),
|
|
|
|
&new_config,
|
|
|
|
Some("# Clash Verge Temp File"),
|
|
|
|
) {
|
|
|
|
Err(err) => return Err(err),
|
|
|
|
_ => {}
|
|
|
|
};
|
2021-12-14 16:58:29 +03:00
|
|
|
}
|
|
|
|
|
2021-12-15 17:36:13 +03:00
|
|
|
let ctrl = payload.controller.clone().unwrap();
|
|
|
|
let server = format!("http://{}/configs", ctrl.server.unwrap());
|
2021-12-14 16:58:29 +03:00
|
|
|
|
|
|
|
let mut headers = HeaderMap::new();
|
|
|
|
headers.insert("Content-Type", "application/json".parse().unwrap());
|
|
|
|
|
2021-12-15 17:36:13 +03:00
|
|
|
if let Some(secret) = ctrl.secret {
|
|
|
|
headers.insert(
|
|
|
|
"Authorization",
|
|
|
|
format!("Bearer {}", secret).parse().unwrap(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-12-14 16:58:29 +03:00
|
|
|
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(()),
|
2021-12-19 19:31:57 +03:00
|
|
|
Err(err) => Err(format!("request failed `{}`", err.to_string())),
|
2021-12-14 16:58:29 +03:00
|
|
|
}
|
|
|
|
}
|