mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 08:45:41 +08:00
refactor: adjust dirs structure
This commit is contained in:
29
src-tauri/src/utils/clash.rs
Normal file
29
src-tauri/src/utils/clash.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
extern crate log;
|
||||
|
||||
use crate::utils::app_home_dir;
|
||||
use tauri::api::process::{Command, CommandEvent};
|
||||
|
||||
/// Run the clash bin
|
||||
pub fn run_clash_bin() {
|
||||
let app_dir = app_home_dir();
|
||||
|
||||
let (mut rx, _sidecar) = Command::new_sidecar("clash")
|
||||
.expect("failed to create clash binary")
|
||||
.args(["-d", &app_dir.as_os_str().to_str().unwrap()])
|
||||
.spawn()
|
||||
.expect("failed to spawn sidecar");
|
||||
|
||||
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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
18
src-tauri/src/utils/dirs.rs
Normal file
18
src-tauri/src/utils/dirs.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use tauri::{
|
||||
api::path::{home_dir, resource_dir},
|
||||
PackageInfo,
|
||||
};
|
||||
|
||||
/// get the verge app home dir
|
||||
pub fn app_home_dir() -> PathBuf {
|
||||
home_dir()
|
||||
.unwrap()
|
||||
.join(Path::new(".config"))
|
||||
.join(Path::new("clash-verge"))
|
||||
}
|
||||
|
||||
/// get the resources dir
|
||||
pub fn app_resources_dir(package_info: &PackageInfo) -> PathBuf {
|
||||
resource_dir(package_info).unwrap().join("resources")
|
||||
}
|
||||
130
src-tauri/src/utils/import.rs
Normal file
130
src-tauri/src/utils/import.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
extern crate reqwest;
|
||||
|
||||
use crate::config::{read_profiles, save_profiles, ProfileExtra, ProfileItem};
|
||||
use crate::utils::app_home_dir;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
/// parse the string
|
||||
fn parse_string<'a>(target: &'a str, key: &'a str) -> Option<&'a str> {
|
||||
match target.find(key) {
|
||||
Some(idx) => {
|
||||
let idx = idx + key.len();
|
||||
let value = &target[idx..];
|
||||
match value.split(';').nth(0) {
|
||||
Some(value) => Some(value.trim()),
|
||||
None => Some(value.trim()),
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Todo: log
|
||||
/// Import the Profile from url
|
||||
/// save to the `verge.yaml` file
|
||||
pub async fn import_profile(profile_url: &str) -> Result<(), reqwest::Error> {
|
||||
let resp = reqwest::get(profile_url).await?;
|
||||
let header = resp.headers().clone();
|
||||
|
||||
// parse the Subscription Userinfo
|
||||
let extra = {
|
||||
let sub_info = header
|
||||
.get("Subscription-Userinfo")
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap();
|
||||
|
||||
ProfileExtra {
|
||||
upload: parse_string(sub_info, "upload=")
|
||||
.unwrap_or("0")
|
||||
.parse()
|
||||
.unwrap_or(0u64),
|
||||
download: parse_string(sub_info, "download=")
|
||||
.unwrap_or("0")
|
||||
.parse()
|
||||
.unwrap_or(0u64),
|
||||
total: parse_string(sub_info, "total=")
|
||||
.unwrap_or("0")
|
||||
.parse()
|
||||
.unwrap_or(0u64),
|
||||
expire: parse_string(sub_info, "expire=")
|
||||
.unwrap_or("0")
|
||||
.parse()
|
||||
.unwrap_or(0u64),
|
||||
}
|
||||
};
|
||||
|
||||
// parse the file name
|
||||
let file_name = {
|
||||
let file_name = header.get("Content-Disposition").unwrap().to_str().unwrap();
|
||||
let file_name = parse_string(file_name, "filename=");
|
||||
|
||||
match file_name {
|
||||
Some(f) => f.to_string(),
|
||||
None => {
|
||||
let cur_time = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
format!("{}.yaml", cur_time)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// save file
|
||||
let file_data = resp.text_with_charset("utf-8").await?;
|
||||
let file_path = app_home_dir().join("profiles").join(&file_name);
|
||||
File::create(file_path)
|
||||
.unwrap()
|
||||
.write(file_data.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
// update profiles.yaml
|
||||
let mut profiles = read_profiles();
|
||||
let mut items = match profiles.items {
|
||||
Some(p) => p,
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
let profile = ProfileItem {
|
||||
name: Some(file_name.clone()),
|
||||
file: Some(file_name.clone()),
|
||||
mode: Some(String::from("rule")),
|
||||
url: Some(String::from(profile_url)),
|
||||
selected: Some(vec![]),
|
||||
extra: Some(extra),
|
||||
};
|
||||
|
||||
let target_index = items
|
||||
.iter()
|
||||
.position(|x| x.name.is_some() && x.name.as_ref().unwrap().as_str() == file_name.as_str());
|
||||
|
||||
match target_index {
|
||||
Some(idx) => items[idx] = profile,
|
||||
None => items.push(profile),
|
||||
};
|
||||
|
||||
profiles.items = Some(items);
|
||||
save_profiles(&profiles);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_value() {
|
||||
let test_1 = "upload=111; download=2222; total=3333; expire=444";
|
||||
let test_2 = "attachment; filename=Clash.yaml";
|
||||
|
||||
assert_eq!(parse_string(test_1, "upload="), Some("111"));
|
||||
assert_eq!(parse_string(test_1, "download="), Some("2222"));
|
||||
assert_eq!(parse_string(test_1, "total="), Some("3333"));
|
||||
assert_eq!(parse_string(test_1, "expire="), Some("444"));
|
||||
assert_eq!(parse_string(test_2, "filename="), Some("Clash.yaml"));
|
||||
|
||||
assert_eq!(parse_string(test_1, "aaa="), None);
|
||||
assert_eq!(parse_string(test_1, "upload1="), None);
|
||||
assert_eq!(parse_string(test_1, "expire1="), None);
|
||||
assert_eq!(parse_string(test_2, "attachment="), None);
|
||||
}
|
||||
106
src-tauri/src/utils/init.rs
Normal file
106
src-tauri/src/utils/init.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
extern crate serde_yaml;
|
||||
|
||||
use log::LevelFilter;
|
||||
use log4rs::append::console::ConsoleAppender;
|
||||
use log4rs::append::file::FileAppender;
|
||||
use log4rs::config::{Appender, Config, Root};
|
||||
use log4rs::encode::pattern::PatternEncoder;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tauri::PackageInfo;
|
||||
|
||||
use crate::utils::{app_home_dir, app_resources_dir};
|
||||
|
||||
/// initialize this instance's log file
|
||||
fn init_log(log_dir: &PathBuf) {
|
||||
let log_time = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
let log_file = format!("log-{:?}", log_time);
|
||||
let log_file = log_dir.join(log_file);
|
||||
|
||||
let stdout = ConsoleAppender::builder().build();
|
||||
let tofile = FileAppender::builder()
|
||||
.encoder(Box::new(PatternEncoder::new(
|
||||
"{d(%Y-%m-%d %H:%M:%S)} - {m}{n}",
|
||||
)))
|
||||
.build(log_file)
|
||||
.unwrap();
|
||||
|
||||
let config = Config::builder()
|
||||
.appender(Appender::builder().build("stdout", Box::new(stdout)))
|
||||
.appender(Appender::builder().build("file", Box::new(tofile)))
|
||||
.build(
|
||||
Root::builder()
|
||||
.appenders(["stdout", "file"])
|
||||
.build(LevelFilter::Debug),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
log4rs::init_config(config).unwrap();
|
||||
}
|
||||
|
||||
/// Initialize all the files from resources
|
||||
fn init_config_file(app_dir: &PathBuf, res_dir: &PathBuf) {
|
||||
// target path
|
||||
let clash_path = app_dir.join("config.yaml");
|
||||
let verge_path = app_dir.join("verge.yaml");
|
||||
let profile_path = app_dir.join("profiles.yaml");
|
||||
let mmdb_path = app_dir.join("Country.mmdb");
|
||||
|
||||
// template path
|
||||
let clash_tmpl = res_dir.join("config_tmp.yaml");
|
||||
let verge_tmpl = res_dir.join("verge_tmp.yaml");
|
||||
let profiles_tmpl = res_dir.join("profiles_tmp.yaml");
|
||||
let mmdb_tmpl = res_dir.join("Country.mmdb");
|
||||
|
||||
if !clash_path.exists() {
|
||||
if clash_tmpl.exists() {
|
||||
fs::copy(clash_tmpl, clash_path).unwrap();
|
||||
} else {
|
||||
// make sure that the config.yaml not null
|
||||
let content = "mixed-port: 7890\nallow-lan: false\n".as_bytes();
|
||||
fs::File::create(clash_path)
|
||||
.unwrap()
|
||||
.write(content)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// only copy it
|
||||
if !verge_path.exists() && verge_tmpl.exists() {
|
||||
fs::copy(verge_tmpl, verge_path).unwrap();
|
||||
}
|
||||
if !profile_path.exists() && profiles_tmpl.exists() {
|
||||
fs::copy(profiles_tmpl, profile_path).unwrap();
|
||||
}
|
||||
if !mmdb_path.exists() && mmdb_tmpl.exists() {
|
||||
fs::copy(mmdb_tmpl, mmdb_path).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// initialize app
|
||||
pub fn init_app(package_info: &PackageInfo) {
|
||||
// create app dir
|
||||
let app_dir = app_home_dir();
|
||||
let log_dir = app_dir.join("logs");
|
||||
let profiles_dir = app_dir.join("profiles");
|
||||
|
||||
let res_dir = app_resources_dir(package_info);
|
||||
|
||||
if !app_dir.exists() {
|
||||
fs::create_dir(&app_dir).unwrap();
|
||||
}
|
||||
if !log_dir.exists() {
|
||||
fs::create_dir(&log_dir).unwrap();
|
||||
}
|
||||
if !profiles_dir.exists() {
|
||||
fs::create_dir(&profiles_dir).unwrap();
|
||||
}
|
||||
|
||||
init_log(&log_dir);
|
||||
init_config_file(&app_dir, &res_dir);
|
||||
}
|
||||
7
src-tauri/src/utils/mod.rs
Normal file
7
src-tauri/src/utils/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
mod dirs;
|
||||
pub use self::dirs::*;
|
||||
|
||||
pub mod clash;
|
||||
pub mod import;
|
||||
pub mod init;
|
||||
pub mod sysopt;
|
||||
71
src-tauri/src/utils/sysopt.rs
Normal file
71
src-tauri/src/utils/sysopt.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct SysProxyConfig {
|
||||
enable: bool,
|
||||
server: String,
|
||||
bypass: String,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
mod win {
|
||||
use super::*;
|
||||
use winreg::enums::*;
|
||||
use winreg::RegKey;
|
||||
|
||||
/// Get the windows system proxy config
|
||||
pub fn get_proxy_config() -> io::Result<SysProxyConfig> {
|
||||
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
||||
let cur_var = hkcu.open_subkey_with_flags(
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
|
||||
KEY_READ,
|
||||
)?;
|
||||
|
||||
Ok(SysProxyConfig {
|
||||
enable: cur_var.get_value::<u32, _>("ProxyEnable")? == 1u32,
|
||||
server: cur_var.get_value("ProxyServer")?,
|
||||
bypass: cur_var.get_value("ProxyOverride")?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the windows system proxy config
|
||||
pub fn set_proxy_config(config: &SysProxyConfig) -> io::Result<()> {
|
||||
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
||||
let cur_var = hkcu.open_subkey_with_flags(
|
||||
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
|
||||
KEY_SET_VALUE,
|
||||
)?;
|
||||
|
||||
let enable: u32 = if config.enable { 1u32 } else { 0u32 };
|
||||
|
||||
cur_var.set_value("ProxyEnable", &enable)?;
|
||||
cur_var.set_value("ProxyServer", &config.server)?;
|
||||
cur_var.set_value("ProxyOverride", &config.bypass)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos {
|
||||
use super::*;
|
||||
|
||||
pub fn get_proxy_config() -> io::Result<SysProxyConfig> {
|
||||
Ok(SysProxyConfig {
|
||||
enable: false,
|
||||
server: "server".into(),
|
||||
bypass: "bypass".into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_proxy_config(config: &SysProxyConfig) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use win::*;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub use macos::*;
|
||||
Reference in New Issue
Block a user