refactor: adjust dirs structure

This commit is contained in:
GyDi
2021-12-14 16:07:15 +08:00
parent db802e959d
commit 4719649bf4
9 changed files with 35 additions and 20 deletions

View 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);
}
_ => {}
}
}
});
}

View 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")
}

View 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
View 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);
}

View File

@@ -0,0 +1,7 @@
mod dirs;
pub use self::dirs::*;
pub mod clash;
pub mod import;
pub mod init;
pub mod sysopt;

View 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::*;