mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 05:03:45 +08:00
refactor: wip
This commit is contained in:
parent
f5f865a139
commit
84c12dee80
@ -14,8 +14,7 @@ type CmdResult<T = ()> = Result<T, String>;
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_profiles() -> CmdResult<IProfiles> {
|
pub fn get_profiles() -> CmdResult<IProfiles> {
|
||||||
let profiles = ProfilesN::global().config.lock();
|
Ok(Config::profiles().data().clone())
|
||||||
Ok(profiles.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@ -27,47 +26,80 @@ pub async fn enhance_profiles() -> CmdResult {
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult {
|
pub async fn import_profile(url: String, option: Option<PrfOption>) -> CmdResult {
|
||||||
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?;
|
let item = wrap_err!(PrfItem::from_url(&url, None, None, option).await)?;
|
||||||
let mut profiles = ProfilesN::global().config.lock();
|
wrap_err!(Config::profiles().data().append_item(item))
|
||||||
wrap_err!(profiles.append_item(item))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResult {
|
pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResult {
|
||||||
let item = wrap_err!(PrfItem::from(item, file_data).await)?;
|
let item = wrap_err!(PrfItem::from(item, file_data).await)?;
|
||||||
let mut profiles = ProfilesN::global().config.lock();
|
wrap_err!(Config::profiles().data().append_item(item))
|
||||||
wrap_err!(profiles.append_item(item))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResult {
|
pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResult {
|
||||||
wrap_err!(ProfilesN::global().update_item(index, option).await)
|
wrap_err!(feat::update_profile(index, option).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn select_profile(index: String) -> CmdResult {
|
pub async fn select_profile(index: String) -> CmdResult {
|
||||||
wrap_err!({ ProfilesN::global().config.lock().put_current(index) })?;
|
wrap_err!({ Config::profiles().draft().put_current(index) })?;
|
||||||
wrap_err!(CoreManager::global().activate_config().await)
|
|
||||||
|
match feat::handle_activate().await {
|
||||||
|
Ok(_) => {
|
||||||
|
Config::profiles().apply();
|
||||||
|
wrap_err!(Config::profiles().data().save_file())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
Config::profiles().discard();
|
||||||
|
log::error!(target: "app", "{err}");
|
||||||
|
Err(format!("{err}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// change the profile chain
|
/// change the profile chain
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn change_profile_chain(chain: Option<Vec<String>>) -> CmdResult {
|
pub async fn change_profile_chain(chain: Option<Vec<String>>) -> CmdResult {
|
||||||
wrap_err!({ ProfilesN::global().config.lock().put_chain(chain) })?;
|
wrap_err!({ Config::profiles().draft().put_chain(chain) })?;
|
||||||
wrap_err!(CoreManager::global().activate_config().await)
|
|
||||||
|
match feat::handle_activate().await {
|
||||||
|
Ok(_) => {
|
||||||
|
Config::profiles().apply();
|
||||||
|
wrap_err!(Config::profiles().data().save_file())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
Config::profiles().discard();
|
||||||
|
log::error!(target: "app", "{err}");
|
||||||
|
Err(format!("{err}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn change_profile_valid(valid: Option<Vec<String>>) -> CmdResult {
|
pub async fn change_profile_valid(valid: Option<Vec<String>>) -> CmdResult {
|
||||||
wrap_err!({ ProfilesN::global().config.lock().put_valid(valid) })?;
|
wrap_err!({ Config::profiles().draft().put_valid(valid) })?;
|
||||||
wrap_err!(CoreManager::global().activate_config().await)
|
|
||||||
|
match feat::handle_activate().await {
|
||||||
|
Ok(_) => {
|
||||||
|
Config::profiles().apply();
|
||||||
|
wrap_err!(Config::profiles().data().save_file())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
Config::profiles().discard();
|
||||||
|
log::error!(target: "app", "{err}");
|
||||||
|
Err(format!("{err}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn delete_profile(index: String) -> CmdResult {
|
pub async fn delete_profile(index: String) -> CmdResult {
|
||||||
let should_update = { wrap_err!(ProfilesN::global().config.lock().delete_item(index))? };
|
let should_update = wrap_err!({ Config::profiles().data().delete_item(index) })?;
|
||||||
|
|
||||||
if should_update {
|
if should_update {
|
||||||
wrap_err!(CoreManager::global().activate_config().await)?;
|
wrap_err!(feat::handle_activate().await)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -75,19 +107,20 @@ pub async fn delete_profile(index: String) -> CmdResult {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
|
pub fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
|
||||||
let mut profiles = ProfilesN::global().config.lock();
|
wrap_err!(Config::profiles().data().patch_item(index, profile))?;
|
||||||
wrap_err!(profiles.patch_item(index, profile))?;
|
|
||||||
drop(profiles);
|
|
||||||
|
|
||||||
wrap_err!(timer::Timer::global().refresh())
|
wrap_err!(timer::Timer::global().refresh())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn view_profile(index: String) -> CmdResult {
|
pub fn view_profile(index: String) -> CmdResult {
|
||||||
let profiles = ProfilesN::global().config.lock();
|
let file = {
|
||||||
let item = wrap_err!(profiles.get_item(&index))?;
|
wrap_err!(Config::profiles().latest().get_item(&index))?
|
||||||
|
.file
|
||||||
|
.clone()
|
||||||
|
.ok_or("the file field is null")
|
||||||
|
}?;
|
||||||
|
|
||||||
let file = item.file.clone().ok_or("the file field is null")?;
|
|
||||||
let path = dirs::app_profiles_dir().join(file);
|
let path = dirs::app_profiles_dir().join(file);
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
ret_err!("the file not found");
|
ret_err!("the file not found");
|
||||||
@ -98,7 +131,8 @@ pub fn view_profile(index: String) -> CmdResult {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn read_profile_file(index: String) -> CmdResult<String> {
|
pub fn read_profile_file(index: String) -> CmdResult<String> {
|
||||||
let profiles = ProfilesN::global().config.lock();
|
let profiles = Config::profiles();
|
||||||
|
let profiles = profiles.latest();
|
||||||
let item = wrap_err!(profiles.get_item(&index))?;
|
let item = wrap_err!(profiles.get_item(&index))?;
|
||||||
let data = wrap_err!(item.read_file())?;
|
let data = wrap_err!(item.read_file())?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
@ -110,14 +144,15 @@ pub fn save_profile_file(index: String, file_data: Option<String>) -> CmdResult
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let profiles = ProfilesN::global().config.lock();
|
let profiles = Config::profiles();
|
||||||
|
let profiles = profiles.latest();
|
||||||
let item = wrap_err!(profiles.get_item(&index))?;
|
let item = wrap_err!(profiles.get_item(&index))?;
|
||||||
wrap_err!(item.save_file(file_data.unwrap()))
|
wrap_err!(item.save_file(file_data.unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_clash_info() -> CmdResult<ClashInfoN> {
|
pub fn get_clash_info() -> CmdResult<ClashInfoN> {
|
||||||
Ok(ClashN::global().info.lock().clone())
|
wrap_err!(Config::clash().latest().get_info())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@ -153,18 +188,18 @@ pub fn get_runtime_logs() -> CmdResult<HashMap<String, Vec<(String, String)>>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn patch_clash_config(payload: Mapping) -> CmdResult {
|
pub async fn patch_clash_config(payload: Mapping) -> CmdResult {
|
||||||
wrap_err!(feat::patch_clash(payload))
|
wrap_err!(feat::patch_clash(payload).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_verge_config() -> CmdResult<IVerge> {
|
pub fn get_verge_config() -> CmdResult<IVerge> {
|
||||||
Ok(VergeN::global().config.lock().clone())
|
Ok(Config::verge().data().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn patch_verge_config(payload: IVerge) -> CmdResult {
|
pub async fn patch_verge_config(payload: IVerge) -> CmdResult {
|
||||||
wrap_err!(feat::patch_verge(payload))
|
wrap_err!(feat::patch_verge(payload).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
use crate::utils::{config, dirs};
|
use crate::utils::{config, dirs};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml::{Mapping, Value};
|
use serde_yaml::{Mapping, Value};
|
||||||
use std::{net::SocketAddr, sync::Arc};
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct IClashTemp(pub Mapping);
|
pub struct IClashTemp(pub Mapping);
|
||||||
@ -33,69 +30,6 @@ impl IClashTemp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[deprecated]
|
|
||||||
pub struct ClashN {
|
|
||||||
/// maintain the clash config
|
|
||||||
pub config: Arc<Mutex<Mapping>>,
|
|
||||||
/// some info
|
|
||||||
pub info: Arc<Mutex<ClashInfoN>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClashN {
|
|
||||||
pub fn global() -> &'static ClashN {
|
|
||||||
static DATA: OnceCell<ClashN> = OnceCell::new();
|
|
||||||
|
|
||||||
DATA.get_or_init(|| {
|
|
||||||
let config = ClashN::read_config();
|
|
||||||
let info = ClashInfoN::from(&config);
|
|
||||||
|
|
||||||
ClashN {
|
|
||||||
config: Arc::new(Mutex::new(config)),
|
|
||||||
info: Arc::new(Mutex::new(info)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get clash config
|
|
||||||
pub fn read_config() -> Mapping {
|
|
||||||
config::read_merge_mapping(dirs::clash_path())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// save the clash config
|
|
||||||
pub fn save_config(&self) -> Result<()> {
|
|
||||||
let config = self.config.lock();
|
|
||||||
|
|
||||||
config::save_yaml(
|
|
||||||
dirs::clash_path(),
|
|
||||||
&*config,
|
|
||||||
Some("# Default Config For ClashN Core\n\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 返回旧值
|
|
||||||
pub fn patch_info(&self, info: ClashInfoN) -> Result<ClashInfoN> {
|
|
||||||
let mut old_info = self.info.lock();
|
|
||||||
let old = (*old_info).to_owned();
|
|
||||||
*old_info = info;
|
|
||||||
Ok(old)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// patch update the clash config
|
|
||||||
/// if the port is changed then return true
|
|
||||||
pub fn patch_config(&self, patch: Mapping) -> Result<()> {
|
|
||||||
let mut config = self.config.lock();
|
|
||||||
|
|
||||||
for (key, value) in patch.into_iter() {
|
|
||||||
config.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(config);
|
|
||||||
|
|
||||||
self.save_config()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct ClashInfoN {
|
pub struct ClashInfoN {
|
||||||
/// clash sidecar status
|
/// clash sidecar status
|
||||||
@ -245,21 +179,3 @@ pub struct IClashFallbackFilter {
|
|||||||
pub ipcidr: Option<Vec<String>>,
|
pub ipcidr: Option<Vec<String>>,
|
||||||
pub domain: Option<Vec<String>>,
|
pub domain: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test() {
|
|
||||||
let socket = SocketAddr::new("127.0.0.1".parse().unwrap(), 9090);
|
|
||||||
|
|
||||||
let s = "[::]:8080".parse::<SocketAddr>();
|
|
||||||
|
|
||||||
dbg!(s);
|
|
||||||
|
|
||||||
// match "::8080".parse::<SocketAddr>() {
|
|
||||||
// Ok(_) => {}
|
|
||||||
// Err(err) => {
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// assert_eq!(":8080".parse(), Ok(socket));
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use super::{Draft, IClashTemp, IProfiles, IVerge};
|
use super::{Draft, IClashTemp, IProfiles, IVerge};
|
||||||
use crate::config::ClashN;
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use serde_yaml::Mapping;
|
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
clash_config: Draft<IClashTemp>,
|
clash_config: Draft<IClashTemp>,
|
||||||
@ -20,18 +18,6 @@ impl Config {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn clash<'a>() -> MappedMutexGuard<'a, IClash> {
|
|
||||||
// Self::global().clash_config.latest()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn verge<'a>() -> MappedMutexGuard<'a, IVerge> {
|
|
||||||
// Self::global().verge_config.latest()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn profiles<'a>() -> MappedMutexGuard<'a, IProfiles> {
|
|
||||||
// Self::global().profiles_config.latest()
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn clash() -> Draft<IClashTemp> {
|
pub fn clash() -> Draft<IClashTemp> {
|
||||||
Self::global().clash_config.clone()
|
Self::global().clash_config.clone()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use super::{IClash, IClashTemp, IProfiles, IVerge};
|
use super::{IClashTemp, IProfiles, IVerge};
|
||||||
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
|
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
|
||||||
use serde_yaml::Mapping;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -64,10 +63,10 @@ macro_rules! draft_define {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
draft_define!(IClash);
|
// draft_define!(IClash);
|
||||||
draft_define!(IClashTemp);
|
draft_define!(IClashTemp);
|
||||||
draft_define!(IVerge);
|
draft_define!(IVerge);
|
||||||
draft_define!(Mapping);
|
// draft_define!(Mapping);
|
||||||
draft_define!(IProfiles);
|
draft_define!(IProfiles);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -6,6 +6,8 @@ use serde_yaml::Mapping;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use sysproxy::Sysproxy;
|
use sysproxy::Sysproxy;
|
||||||
|
|
||||||
|
use super::Config;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct PrfItem {
|
pub struct PrfItem {
|
||||||
pub uid: Option<String>,
|
pub uid: Option<String>,
|
||||||
@ -192,9 +194,9 @@ impl PrfItem {
|
|||||||
|
|
||||||
// 使用软件自己的代理
|
// 使用软件自己的代理
|
||||||
if self_proxy {
|
if self_proxy {
|
||||||
let clash = super::ClashN::global();
|
let port = Config::clash().data().get_info()?.port;
|
||||||
let port = clash.info.lock().port.clone();
|
|
||||||
let port = port.ok_or(anyhow::anyhow!("failed to get clash info port"))?;
|
let port = port.ok_or(anyhow::anyhow!("failed to get clash info port"))?;
|
||||||
|
|
||||||
let proxy_scheme = format!("http://127.0.0.1:{port}");
|
let proxy_scheme = format!("http://127.0.0.1:{port}");
|
||||||
|
|
||||||
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
|
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
|
||||||
|
@ -1,82 +1,25 @@
|
|||||||
use super::{prfitem::PrfItem, ChainItem, PrfOption};
|
use super::{prfitem::PrfItem, ChainItem};
|
||||||
use crate::{
|
use crate::utils::{config, dirs, help};
|
||||||
core::CoreManager,
|
|
||||||
utils::{config, dirs, help},
|
|
||||||
};
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_yaml::Mapping;
|
use serde_yaml::Mapping;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::{fs, io::Write};
|
use std::{fs, io::Write};
|
||||||
|
|
||||||
#[deprecated]
|
|
||||||
pub struct ProfilesN {
|
|
||||||
pub config: Arc<Mutex<IProfiles>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProfilesN {
|
|
||||||
pub fn global() -> &'static ProfilesN {
|
|
||||||
static PROFILES: OnceCell<ProfilesN> = OnceCell::new();
|
|
||||||
|
|
||||||
PROFILES.get_or_init(|| ProfilesN {
|
|
||||||
config: Arc::new(Mutex::new(IProfiles::read_file())),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 更新单个配置
|
|
||||||
pub async fn update_item(&self, uid: String, option: Option<PrfOption>) -> Result<()> {
|
|
||||||
let url_opt = {
|
|
||||||
let profiles = self.config.lock();
|
|
||||||
let item = profiles.get_item(&uid)?;
|
|
||||||
let is_remote = item.itype.as_ref().map_or(false, |s| s == "remote");
|
|
||||||
|
|
||||||
if !is_remote {
|
|
||||||
None // 直接更新
|
|
||||||
} else if item.url.is_none() {
|
|
||||||
bail!("failed to get the profile item url");
|
|
||||||
} else {
|
|
||||||
Some((item.url.clone().unwrap(), item.option.clone()))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let should_update = match url_opt {
|
|
||||||
Some((url, opt)) => {
|
|
||||||
let merged_opt = PrfOption::merge(opt, option);
|
|
||||||
let item = PrfItem::from_url(&url, None, None, merged_opt).await?;
|
|
||||||
|
|
||||||
let mut profiles = self.config.lock();
|
|
||||||
profiles.update_item(uid.clone(), item)?;
|
|
||||||
|
|
||||||
Some(uid) == profiles.get_current()
|
|
||||||
}
|
|
||||||
None => true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if should_update {
|
|
||||||
CoreManager::global().activate_config().await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Define the `profiles.yaml` schema
|
/// Define the `profiles.yaml` schema
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct IProfiles {
|
pub struct IProfiles {
|
||||||
/// same as PrfConfig.current
|
/// same as PrfConfig.current
|
||||||
current: Option<String>,
|
pub current: Option<String>,
|
||||||
|
|
||||||
/// same as PrfConfig.chain
|
/// same as PrfConfig.chain
|
||||||
chain: Option<Vec<String>>,
|
pub chain: Option<Vec<String>>,
|
||||||
|
|
||||||
/// record valid fields for clash
|
/// record valid fields for clash
|
||||||
valid: Option<Vec<String>>,
|
pub valid: Option<Vec<String>>,
|
||||||
|
|
||||||
/// profile list
|
/// profile list
|
||||||
items: Option<Vec<PrfItem>>,
|
pub items: Option<Vec<PrfItem>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! patch {
|
macro_rules! patch {
|
||||||
@ -134,7 +77,7 @@ impl IProfiles {
|
|||||||
|
|
||||||
if items.iter().find(|&each| each.uid == some_uid).is_some() {
|
if items.iter().find(|&each| each.uid == some_uid).is_some() {
|
||||||
self.current = some_uid;
|
self.current = some_uid;
|
||||||
return self.save_file();
|
return self.save_file(); // todo remove
|
||||||
}
|
}
|
||||||
|
|
||||||
bail!("invalid uid \"{uid}\"");
|
bail!("invalid uid \"{uid}\"");
|
||||||
|
@ -1,53 +1,6 @@
|
|||||||
use crate::utils::{config, dirs};
|
use crate::utils::{config, dirs};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[deprecated]
|
|
||||||
pub struct VergeN {
|
|
||||||
pub config: Arc<Mutex<IVerge>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VergeN {
|
|
||||||
pub fn global() -> &'static VergeN {
|
|
||||||
static DATA: OnceCell<VergeN> = OnceCell::new();
|
|
||||||
|
|
||||||
DATA.get_or_init(|| {
|
|
||||||
let config = config::read_yaml::<IVerge>(dirs::verge_path());
|
|
||||||
VergeN {
|
|
||||||
config: Arc::new(Mutex::new(config)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save IVerge App Config
|
|
||||||
pub fn save_file(&self) -> Result<()> {
|
|
||||||
self.config.lock().save_file()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// patch verge config
|
|
||||||
/// only save to file
|
|
||||||
pub fn patch_config(&self, patch: IVerge) -> Result<()> {
|
|
||||||
{
|
|
||||||
self.config.lock().patch_config(patch);
|
|
||||||
}
|
|
||||||
self.save_file()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 在初始化前尝试拿到单例端口的值
|
|
||||||
pub fn get_singleton_port() -> u16 {
|
|
||||||
let config = config::read_yaml::<IVerge>(dirs::verge_path());
|
|
||||||
|
|
||||||
#[cfg(not(feature = "verge-dev"))]
|
|
||||||
const SERVER_PORT: u16 = 33331;
|
|
||||||
#[cfg(feature = "verge-dev")]
|
|
||||||
const SERVER_PORT: u16 = 11233;
|
|
||||||
|
|
||||||
config.app_singleton_port.unwrap_or(SERVER_PORT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ### `verge.yaml` schema
|
/// ### `verge.yaml` schema
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||||
@ -177,4 +130,16 @@ impl IVerge {
|
|||||||
patch!(auto_close_connection);
|
patch!(auto_close_connection);
|
||||||
patch!(default_latency_test);
|
patch!(default_latency_test);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 在初始化前尝试拿到单例端口的值
|
||||||
|
pub fn get_singleton_port() -> u16 {
|
||||||
|
let config = config::read_yaml::<IVerge>(dirs::verge_path());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "verge-dev"))]
|
||||||
|
const SERVER_PORT: u16 = 33331;
|
||||||
|
#[cfg(feature = "verge-dev")]
|
||||||
|
const SERVER_PORT: u16 = 11233;
|
||||||
|
|
||||||
|
config.app_singleton_port.unwrap_or(SERVER_PORT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{config, utils::dirs};
|
use crate::{config::Config, utils::dirs};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use reqwest::header::HeaderMap;
|
use reqwest::header::HeaderMap;
|
||||||
use serde_yaml::Mapping;
|
use serde_yaml::Mapping;
|
||||||
@ -40,7 +40,7 @@ pub async fn patch_configs(config: &Mapping) -> Result<()> {
|
|||||||
|
|
||||||
/// 根据clash info获取clash服务地址和请求头
|
/// 根据clash info获取clash服务地址和请求头
|
||||||
fn clash_client_info() -> Result<(String, HeaderMap)> {
|
fn clash_client_info() -> Result<(String, HeaderMap)> {
|
||||||
let info = { config::ClashN::global().info.lock().clone() };
|
let info = { Config::clash().data().get_info()? };
|
||||||
|
|
||||||
if info.server.is_none() {
|
if info.server.is_none() {
|
||||||
let status = &info.status;
|
let status = &info.status;
|
||||||
|
@ -64,12 +64,12 @@ impl Hotkey {
|
|||||||
"clash_mode_global" => || feat::change_clash_mode("global".into()),
|
"clash_mode_global" => || feat::change_clash_mode("global".into()),
|
||||||
"clash_mode_direct" => || feat::change_clash_mode("direct".into()),
|
"clash_mode_direct" => || feat::change_clash_mode("direct".into()),
|
||||||
"clash_mode_script" => || feat::change_clash_mode("script".into()),
|
"clash_mode_script" => || feat::change_clash_mode("script".into()),
|
||||||
"toggle_system_proxy" => || log_err!(feat::toggle_system_proxy()),
|
"toggle_system_proxy" => || feat::toggle_system_proxy(),
|
||||||
"enable_system_proxy" => || log_err!(feat::enable_system_proxy()),
|
"enable_system_proxy" => || feat::enable_system_proxy(),
|
||||||
"disable_system_proxy" => || log_err!(feat::disable_system_proxy()),
|
"disable_system_proxy" => || feat::disable_system_proxy(),
|
||||||
"toggle_tun_mode" => || log_err!(feat::toggle_tun_mode()),
|
"toggle_tun_mode" => || feat::toggle_tun_mode(),
|
||||||
"enable_tun_mode" => || log_err!(feat::enable_tun_mode()),
|
"enable_tun_mode" => || feat::enable_tun_mode(),
|
||||||
"disable_tun_mode" => || log_err!(feat::disable_tun_mode()),
|
"disable_tun_mode" => || feat::disable_tun_mode(),
|
||||||
|
|
||||||
_ => bail!("invalid function \"{func}\""),
|
_ => bail!("invalid function \"{func}\""),
|
||||||
};
|
};
|
||||||
|
@ -144,13 +144,8 @@ impl Sysopt {
|
|||||||
|
|
||||||
/// init the auto launch
|
/// init the auto launch
|
||||||
pub fn init_launch(&self) -> Result<()> {
|
pub fn init_launch(&self) -> Result<()> {
|
||||||
let enable = {
|
let enable = { Config::verge().latest().enable_auto_launch.clone() };
|
||||||
Config::verge()
|
let enable = enable.unwrap_or(false);
|
||||||
.latest()
|
|
||||||
.enable_auto_launch
|
|
||||||
.clone()
|
|
||||||
.unwrap_or(false)
|
|
||||||
};
|
|
||||||
|
|
||||||
let app_exe = current_exe()?;
|
let app_exe = current_exe()?;
|
||||||
let app_exe = dunce::canonicalize(app_exe)?;
|
let app_exe = dunce::canonicalize(app_exe)?;
|
||||||
@ -213,13 +208,8 @@ impl Sysopt {
|
|||||||
drop(auto_launch);
|
drop(auto_launch);
|
||||||
return self.init_launch();
|
return self.init_launch();
|
||||||
}
|
}
|
||||||
let enable = {
|
let enable = { Config::verge().latest().enable_auto_launch.clone() };
|
||||||
Config::verge()
|
let enable = enable.unwrap_or(false);
|
||||||
.latest()
|
|
||||||
.enable_auto_launch
|
|
||||||
.clone()
|
|
||||||
.unwrap_or(false)
|
|
||||||
};
|
|
||||||
let auto_launch = auto_launch.as_ref().unwrap();
|
let auto_launch = auto_launch.as_ref().unwrap();
|
||||||
|
|
||||||
match enable {
|
match enable {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::config::{self, ProfilesN};
|
use crate::config::Config;
|
||||||
|
use crate::feat;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
|
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
@ -36,12 +37,10 @@ impl Timer {
|
|||||||
|
|
||||||
let cur_timestamp = chrono::Local::now().timestamp();
|
let cur_timestamp = chrono::Local::now().timestamp();
|
||||||
|
|
||||||
let profiles = config::ProfilesN::global().config.lock();
|
|
||||||
|
|
||||||
let timer_map = self.timer_map.lock();
|
let timer_map = self.timer_map.lock();
|
||||||
let delay_timer = self.delay_timer.lock();
|
let delay_timer = self.delay_timer.lock();
|
||||||
|
|
||||||
profiles.get_items().map(|items| {
|
Config::profiles().latest().get_items().map(|items| {
|
||||||
items
|
items
|
||||||
.iter()
|
.iter()
|
||||||
// .filter_map(|item| {
|
// .filter_map(|item| {
|
||||||
@ -100,11 +99,9 @@ impl Timer {
|
|||||||
|
|
||||||
/// generate a uid -> update_interval map
|
/// generate a uid -> update_interval map
|
||||||
fn gen_map(&self) -> HashMap<String, u64> {
|
fn gen_map(&self) -> HashMap<String, u64> {
|
||||||
let profiles = config::ProfilesN::global().config.lock();
|
|
||||||
|
|
||||||
let mut new_map = HashMap::new();
|
let mut new_map = HashMap::new();
|
||||||
|
|
||||||
if let Some(items) = profiles.get_items() {
|
if let Some(items) = Config::profiles().latest().get_items() {
|
||||||
for item in items.iter() {
|
for item in items.iter() {
|
||||||
if item.option.is_some() {
|
if item.option.is_some() {
|
||||||
let option = item.option.as_ref().unwrap();
|
let option = item.option.as_ref().unwrap();
|
||||||
@ -178,7 +175,7 @@ impl Timer {
|
|||||||
/// the task runner
|
/// the task runner
|
||||||
async fn async_task(uid: String) {
|
async fn async_task(uid: String) {
|
||||||
log::info!(target: "app", "running timer task `{uid}`");
|
log::info!(target: "app", "running timer task `{uid}`");
|
||||||
crate::log_err!(ProfilesN::global().update_item(uid, None).await);
|
crate::log_err!(feat::update_profile(uid, None).await);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use crate::log_err;
|
|
||||||
use crate::{config::Config, feat, utils::resolve};
|
use crate::{config::Config, feat, utils::resolve};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tauri::{
|
use tauri::{
|
||||||
@ -109,8 +108,8 @@ impl Tray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"open_window" => resolve::create_window(app_handle),
|
"open_window" => resolve::create_window(app_handle),
|
||||||
"system_proxy" => log_err!(feat::toggle_system_proxy()),
|
"system_proxy" => feat::toggle_system_proxy(),
|
||||||
"tun_mode" => log_err!(feat::toggle_tun_mode()),
|
"tun_mode" => feat::toggle_tun_mode(),
|
||||||
"restart_clash" => feat::restart_clash_core(),
|
"restart_clash" => feat::restart_clash_core(),
|
||||||
"restart_app" => api::process::restart(&app_handle.env()),
|
"restart_app" => api::process::restart(&app_handle.env()),
|
||||||
"quit" => {
|
"quit" => {
|
||||||
|
@ -1,167 +0,0 @@
|
|||||||
use crate::utils::{config, dirs};
|
|
||||||
use anyhow::Result;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_yaml::{Mapping, Value};
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct ClashInfo {
|
|
||||||
/// clash sidecar status
|
|
||||||
pub status: String,
|
|
||||||
|
|
||||||
/// clash core port
|
|
||||||
pub port: Option<String>,
|
|
||||||
|
|
||||||
/// same as `external-controller`
|
|
||||||
pub server: Option<String>,
|
|
||||||
|
|
||||||
/// clash secret
|
|
||||||
pub secret: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClashInfo {
|
|
||||||
/// parse the clash's config.yaml
|
|
||||||
/// get some information
|
|
||||||
pub fn from(config: &Mapping) -> ClashInfo {
|
|
||||||
let key_port_1 = Value::from("mixed-port");
|
|
||||||
let key_port_2 = Value::from("port");
|
|
||||||
let key_server = Value::from("external-controller");
|
|
||||||
let key_secret = Value::from("secret");
|
|
||||||
|
|
||||||
let mut status: u32 = 0;
|
|
||||||
|
|
||||||
let port = match config.get(&key_port_1) {
|
|
||||||
Some(value) => match value {
|
|
||||||
Value::String(val_str) => Some(val_str.clone()),
|
|
||||||
Value::Number(val_num) => Some(val_num.to_string()),
|
|
||||||
_ => {
|
|
||||||
status |= 0b1;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
status |= 0b10;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let port = match port {
|
|
||||||
Some(_) => port,
|
|
||||||
None => match config.get(&key_port_2) {
|
|
||||||
Some(value) => match value {
|
|
||||||
Value::String(val_str) => Some(val_str.clone()),
|
|
||||||
Value::Number(val_num) => Some(val_num.to_string()),
|
|
||||||
_ => {
|
|
||||||
status |= 0b100;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
status |= 0b1000;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// `external-controller` could be
|
|
||||||
// "127.0.0.1:9090" or ":9090"
|
|
||||||
let server = match config.get(&key_server) {
|
|
||||||
Some(value) => match value.as_str() {
|
|
||||||
Some(val_str) => {
|
|
||||||
if val_str.starts_with(":") {
|
|
||||||
Some(format!("127.0.0.1{val_str}"))
|
|
||||||
} else if val_str.starts_with("0.0.0.0:") {
|
|
||||||
Some(format!("127.0.0.1:{}", &val_str[8..]))
|
|
||||||
} else if val_str.starts_with("[::]:") {
|
|
||||||
Some(format!("127.0.0.1:{}", &val_str[5..]))
|
|
||||||
} else {
|
|
||||||
Some(val_str.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
status |= 0b10000;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
status |= 0b100000;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let secret = match config.get(&key_secret) {
|
|
||||||
Some(value) => match value {
|
|
||||||
Value::String(val_str) => Some(val_str.clone()),
|
|
||||||
Value::Bool(val_bool) => Some(val_bool.to_string()),
|
|
||||||
Value::Number(val_num) => Some(val_num.to_string()),
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
ClashInfo {
|
|
||||||
status: format!("{status}"),
|
|
||||||
port,
|
|
||||||
server,
|
|
||||||
secret,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Clash {
|
|
||||||
/// maintain the clash config
|
|
||||||
pub config: Mapping,
|
|
||||||
|
|
||||||
/// some info
|
|
||||||
pub info: ClashInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clash {
|
|
||||||
pub fn new() -> Clash {
|
|
||||||
let config = Clash::read_config();
|
|
||||||
let info = ClashInfo::from(&config);
|
|
||||||
|
|
||||||
Clash { config, info }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get clash config
|
|
||||||
pub fn read_config() -> Mapping {
|
|
||||||
config::read_merge_mapping(dirs::clash_path())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// save the clash config
|
|
||||||
pub fn save_config(&self) -> Result<()> {
|
|
||||||
config::save_yaml(
|
|
||||||
dirs::clash_path(),
|
|
||||||
&self.config,
|
|
||||||
Some("# Default Config For Clash Core\n\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// patch update the clash config
|
|
||||||
/// if the port is changed then return true
|
|
||||||
pub fn patch_config(&mut self, patch: Mapping) -> Result<()> {
|
|
||||||
let port_key = Value::from("mixed-port");
|
|
||||||
let server_key = Value::from("external-controller");
|
|
||||||
let secret_key = Value::from("secret");
|
|
||||||
|
|
||||||
let change_info = patch.contains_key(&port_key)
|
|
||||||
|| patch.contains_key(&server_key)
|
|
||||||
|| patch.contains_key(&secret_key);
|
|
||||||
|
|
||||||
for (key, value) in patch.into_iter() {
|
|
||||||
self.config.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if change_info {
|
|
||||||
self.info = ClashInfo::from(&self.config);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.save_config()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Clash {
|
|
||||||
fn default() -> Self {
|
|
||||||
Clash::new()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
mod clash;
|
|
||||||
mod prfitem;
|
|
||||||
mod profiles;
|
|
||||||
mod verge;
|
|
||||||
|
|
||||||
pub use self::clash::*;
|
|
||||||
pub use self::prfitem::*;
|
|
||||||
pub use self::profiles::*;
|
|
||||||
pub use self::verge::*;
|
|
||||||
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Data {
|
|
||||||
pub clash: Arc<Mutex<Clash>>,
|
|
||||||
pub verge: Arc<Mutex<Verge>>,
|
|
||||||
pub profiles: Arc<Mutex<Profiles>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Data {
|
|
||||||
pub fn global() -> &'static Data {
|
|
||||||
static DATA: OnceCell<Data> = OnceCell::new();
|
|
||||||
|
|
||||||
DATA.get_or_init(|| Data {
|
|
||||||
clash: Arc::new(Mutex::new(Clash::new())),
|
|
||||||
verge: Arc::new(Mutex::new(Verge::new())),
|
|
||||||
profiles: Arc::new(Mutex::new(Profiles::new())),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,406 +0,0 @@
|
|||||||
use crate::utils::{config, dirs, help, tmpl};
|
|
||||||
use anyhow::{bail, Context, Result};
|
|
||||||
use reqwest::StatusCode;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_yaml::Mapping;
|
|
||||||
use std::fs;
|
|
||||||
use sysproxy::Sysproxy;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct PrfItem {
|
|
||||||
pub uid: Option<String>,
|
|
||||||
|
|
||||||
/// profile item type
|
|
||||||
/// enum value: remote | local | script | merge
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
pub itype: Option<String>,
|
|
||||||
|
|
||||||
/// profile name
|
|
||||||
pub name: Option<String>,
|
|
||||||
|
|
||||||
/// profile file
|
|
||||||
pub file: Option<String>,
|
|
||||||
|
|
||||||
/// profile description
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub desc: Option<String>,
|
|
||||||
|
|
||||||
/// source url
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub url: Option<String>,
|
|
||||||
|
|
||||||
/// selected infomation
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub selected: Option<Vec<PrfSelected>>,
|
|
||||||
|
|
||||||
/// subscription user info
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub extra: Option<PrfExtra>,
|
|
||||||
|
|
||||||
/// updated time
|
|
||||||
pub updated: Option<usize>,
|
|
||||||
|
|
||||||
/// some options of the item
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub option: Option<PrfOption>,
|
|
||||||
|
|
||||||
/// the file data
|
|
||||||
#[serde(skip)]
|
|
||||||
pub file_data: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct PrfSelected {
|
|
||||||
pub name: Option<String>,
|
|
||||||
pub now: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Copy, Deserialize, Serialize)]
|
|
||||||
pub struct PrfExtra {
|
|
||||||
pub upload: usize,
|
|
||||||
pub download: usize,
|
|
||||||
pub total: usize,
|
|
||||||
pub expire: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct PrfOption {
|
|
||||||
/// for `remote` profile's http request
|
|
||||||
/// see issue #13
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub user_agent: Option<String>,
|
|
||||||
|
|
||||||
/// for `remote` profile
|
|
||||||
/// use system proxy
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub with_proxy: Option<bool>,
|
|
||||||
|
|
||||||
/// for `remote` profile
|
|
||||||
/// use self proxy
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub self_proxy: Option<bool>,
|
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub update_interval: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrfOption {
|
|
||||||
pub fn merge(one: Option<Self>, other: Option<Self>) -> Option<Self> {
|
|
||||||
match (one, other) {
|
|
||||||
(Some(mut a), Some(b)) => {
|
|
||||||
a.user_agent = b.user_agent.or(a.user_agent);
|
|
||||||
a.with_proxy = b.with_proxy.or(a.with_proxy);
|
|
||||||
a.self_proxy = b.self_proxy.or(a.self_proxy);
|
|
||||||
a.update_interval = b.update_interval.or(a.update_interval);
|
|
||||||
Some(a)
|
|
||||||
}
|
|
||||||
t @ _ => t.0.or(t.1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PrfItem {
|
|
||||||
fn default() -> Self {
|
|
||||||
PrfItem {
|
|
||||||
uid: None,
|
|
||||||
itype: None,
|
|
||||||
name: None,
|
|
||||||
desc: None,
|
|
||||||
file: None,
|
|
||||||
url: None,
|
|
||||||
selected: None,
|
|
||||||
extra: None,
|
|
||||||
updated: None,
|
|
||||||
option: None,
|
|
||||||
file_data: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrfItem {
|
|
||||||
/// From partial item
|
|
||||||
/// must contain `itype`
|
|
||||||
pub async fn from(item: PrfItem, file_data: Option<String>) -> Result<PrfItem> {
|
|
||||||
if item.itype.is_none() {
|
|
||||||
bail!("type should not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
match item.itype.unwrap().as_str() {
|
|
||||||
"remote" => {
|
|
||||||
if item.url.is_none() {
|
|
||||||
bail!("url should not be null");
|
|
||||||
}
|
|
||||||
let url = item.url.as_ref().unwrap().as_str();
|
|
||||||
let name = item.name;
|
|
||||||
let desc = item.desc;
|
|
||||||
PrfItem::from_url(url, name, desc, item.option).await
|
|
||||||
}
|
|
||||||
"local" => {
|
|
||||||
let name = item.name.unwrap_or("Local File".into());
|
|
||||||
let desc = item.desc.unwrap_or("".into());
|
|
||||||
PrfItem::from_local(name, desc, file_data)
|
|
||||||
}
|
|
||||||
"merge" => {
|
|
||||||
let name = item.name.unwrap_or("Merge".into());
|
|
||||||
let desc = item.desc.unwrap_or("".into());
|
|
||||||
PrfItem::from_merge(name, desc)
|
|
||||||
}
|
|
||||||
"script" => {
|
|
||||||
let name = item.name.unwrap_or("Script".into());
|
|
||||||
let desc = item.desc.unwrap_or("".into());
|
|
||||||
PrfItem::from_script(name, desc)
|
|
||||||
}
|
|
||||||
typ @ _ => bail!("invalid profile item type \"{typ}\""),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ## Local type
|
|
||||||
/// create a new item from name/desc
|
|
||||||
pub fn from_local(name: String, desc: String, file_data: Option<String>) -> Result<PrfItem> {
|
|
||||||
let uid = help::get_uid("l");
|
|
||||||
let file = format!("{uid}.yaml");
|
|
||||||
|
|
||||||
Ok(PrfItem {
|
|
||||||
uid: Some(uid),
|
|
||||||
itype: Some("local".into()),
|
|
||||||
name: Some(name),
|
|
||||||
desc: Some(desc),
|
|
||||||
file: Some(file),
|
|
||||||
url: None,
|
|
||||||
selected: None,
|
|
||||||
extra: None,
|
|
||||||
option: None,
|
|
||||||
updated: Some(help::get_now()),
|
|
||||||
file_data: Some(file_data.unwrap_or(tmpl::ITEM_LOCAL.into())),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ## Remote type
|
|
||||||
/// create a new item from url
|
|
||||||
pub async fn from_url(
|
|
||||||
url: &str,
|
|
||||||
name: Option<String>,
|
|
||||||
desc: Option<String>,
|
|
||||||
option: Option<PrfOption>,
|
|
||||||
) -> Result<PrfItem> {
|
|
||||||
let opt_ref = option.as_ref();
|
|
||||||
let with_proxy = opt_ref.map_or(false, |o| o.with_proxy.unwrap_or(false));
|
|
||||||
let self_proxy = opt_ref.map_or(false, |o| o.self_proxy.unwrap_or(false));
|
|
||||||
let user_agent = opt_ref.map_or(None, |o| o.user_agent.clone());
|
|
||||||
|
|
||||||
let mut builder = reqwest::ClientBuilder::new().no_proxy();
|
|
||||||
|
|
||||||
// 使用软件自己的代理
|
|
||||||
if self_proxy {
|
|
||||||
let data = super::Data::global();
|
|
||||||
let port = data.clash.lock().info.port.clone();
|
|
||||||
let port = port.ok_or(anyhow::anyhow!("failed to get clash info port"))?;
|
|
||||||
let proxy_scheme = format!("http://127.0.0.1:{port}");
|
|
||||||
|
|
||||||
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
|
|
||||||
builder = builder.proxy(proxy);
|
|
||||||
}
|
|
||||||
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
|
|
||||||
builder = builder.proxy(proxy);
|
|
||||||
}
|
|
||||||
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
|
|
||||||
builder = builder.proxy(proxy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 使用系统代理
|
|
||||||
else if with_proxy {
|
|
||||||
match Sysproxy::get_system_proxy() {
|
|
||||||
Ok(p @ Sysproxy { enable: true, .. }) => {
|
|
||||||
let proxy_scheme = format!("http://{}:{}", p.host, p.port);
|
|
||||||
|
|
||||||
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
|
|
||||||
builder = builder.proxy(proxy);
|
|
||||||
}
|
|
||||||
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
|
|
||||||
builder = builder.proxy(proxy);
|
|
||||||
}
|
|
||||||
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
|
|
||||||
builder = builder.proxy(proxy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let version = unsafe { dirs::APP_VERSION };
|
|
||||||
let version = format!("clash-verge/{version}");
|
|
||||||
builder = builder.user_agent(user_agent.unwrap_or(version));
|
|
||||||
|
|
||||||
let resp = builder.build()?.get(url).send().await?;
|
|
||||||
|
|
||||||
let status_code = resp.status();
|
|
||||||
if !StatusCode::is_success(&status_code) {
|
|
||||||
bail!("failed to fetch remote profile with status {status_code}")
|
|
||||||
}
|
|
||||||
|
|
||||||
let header = resp.headers();
|
|
||||||
|
|
||||||
// parse the Subscription UserInfo
|
|
||||||
let extra = match header.get("Subscription-Userinfo") {
|
|
||||||
Some(value) => {
|
|
||||||
let sub_info = value.to_str().unwrap_or("");
|
|
||||||
|
|
||||||
Some(PrfExtra {
|
|
||||||
upload: help::parse_str(sub_info, "upload=").unwrap_or(0),
|
|
||||||
download: help::parse_str(sub_info, "download=").unwrap_or(0),
|
|
||||||
total: help::parse_str(sub_info, "total=").unwrap_or(0),
|
|
||||||
expire: help::parse_str(sub_info, "expire=").unwrap_or(0),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// parse the Content-Disposition
|
|
||||||
let filename = match header.get("Content-Disposition") {
|
|
||||||
Some(value) => {
|
|
||||||
let filename = value.to_str().unwrap_or("");
|
|
||||||
help::parse_str::<String>(filename, "filename=")
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// parse the profile-update-interval
|
|
||||||
let option = match header.get("profile-update-interval") {
|
|
||||||
Some(value) => match value.to_str().unwrap_or("").parse::<u64>() {
|
|
||||||
Ok(val) => Some(PrfOption {
|
|
||||||
update_interval: Some(val * 60), // hour -> min
|
|
||||||
..PrfOption::default()
|
|
||||||
}),
|
|
||||||
Err(_) => None,
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let uid = help::get_uid("r");
|
|
||||||
let file = format!("{uid}.yaml");
|
|
||||||
let name = name.unwrap_or(filename.unwrap_or("Remote File".into()));
|
|
||||||
let data = resp.text_with_charset("utf-8").await?;
|
|
||||||
|
|
||||||
// check the data whether the valid yaml format
|
|
||||||
let yaml = serde_yaml::from_str::<Mapping>(&data) //
|
|
||||||
.context("the remote profile data is invalid yaml")?;
|
|
||||||
|
|
||||||
if !yaml.contains_key("proxies") && !yaml.contains_key("proxy-providers") {
|
|
||||||
bail!("profile does not contain `proxies` or `proxy-providers`");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(PrfItem {
|
|
||||||
uid: Some(uid),
|
|
||||||
itype: Some("remote".into()),
|
|
||||||
name: Some(name),
|
|
||||||
desc,
|
|
||||||
file: Some(file),
|
|
||||||
url: Some(url.into()),
|
|
||||||
selected: None,
|
|
||||||
extra,
|
|
||||||
option,
|
|
||||||
updated: Some(help::get_now()),
|
|
||||||
file_data: Some(data),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ## Merge type (enhance)
|
|
||||||
/// create the enhanced item by using `merge` rule
|
|
||||||
pub fn from_merge(name: String, desc: String) -> Result<PrfItem> {
|
|
||||||
let uid = help::get_uid("m");
|
|
||||||
let file = format!("{uid}.yaml");
|
|
||||||
|
|
||||||
Ok(PrfItem {
|
|
||||||
uid: Some(uid),
|
|
||||||
itype: Some("merge".into()),
|
|
||||||
name: Some(name),
|
|
||||||
desc: Some(desc),
|
|
||||||
file: Some(file),
|
|
||||||
url: None,
|
|
||||||
selected: None,
|
|
||||||
extra: None,
|
|
||||||
option: None,
|
|
||||||
updated: Some(help::get_now()),
|
|
||||||
file_data: Some(tmpl::ITEM_MERGE.into()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ## Script type (enhance)
|
|
||||||
/// create the enhanced item by using javascript(browserjs)
|
|
||||||
pub fn from_script(name: String, desc: String) -> Result<PrfItem> {
|
|
||||||
let uid = help::get_uid("s");
|
|
||||||
let file = format!("{uid}.js"); // js ext
|
|
||||||
|
|
||||||
Ok(PrfItem {
|
|
||||||
uid: Some(uid),
|
|
||||||
itype: Some("script".into()),
|
|
||||||
name: Some(name),
|
|
||||||
desc: Some(desc),
|
|
||||||
file: Some(file),
|
|
||||||
url: None,
|
|
||||||
selected: None,
|
|
||||||
extra: None,
|
|
||||||
option: None,
|
|
||||||
updated: Some(help::get_now()),
|
|
||||||
file_data: Some(tmpl::ITEM_SCRIPT.into()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get the file data
|
|
||||||
pub fn read_file(&self) -> Result<String> {
|
|
||||||
if self.file.is_none() {
|
|
||||||
bail!("could not find the file");
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = self.file.clone().unwrap();
|
|
||||||
let path = dirs::app_profiles_dir().join(file);
|
|
||||||
fs::read_to_string(path).context("failed to read the file")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// save the file data
|
|
||||||
pub fn save_file(&self, data: String) -> Result<()> {
|
|
||||||
if self.file.is_none() {
|
|
||||||
bail!("could not find the file");
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = self.file.clone().unwrap();
|
|
||||||
let path = dirs::app_profiles_dir().join(file);
|
|
||||||
fs::write(path, data.as_bytes()).context("failed to save the file")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get the data for enhanced mode
|
|
||||||
pub fn to_enhance(&self) -> Option<ChainItem> {
|
|
||||||
let itype = self.itype.as_ref()?.as_str();
|
|
||||||
let file = self.file.clone()?;
|
|
||||||
let uid = self.uid.clone().unwrap_or("".into());
|
|
||||||
let path = dirs::app_profiles_dir().join(file);
|
|
||||||
|
|
||||||
if !path.exists() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match itype {
|
|
||||||
"script" => Some(ChainItem {
|
|
||||||
uid,
|
|
||||||
data: ChainType::Script(fs::read_to_string(path).unwrap_or("".into())),
|
|
||||||
}),
|
|
||||||
"merge" => Some(ChainItem {
|
|
||||||
uid,
|
|
||||||
data: ChainType::Merge(config::read_merge_mapping(path)),
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ChainItem {
|
|
||||||
pub uid: String,
|
|
||||||
pub data: ChainType,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ChainType {
|
|
||||||
Merge(Mapping),
|
|
||||||
Script(String),
|
|
||||||
}
|
|
@ -1,329 +0,0 @@
|
|||||||
use super::prfitem::PrfItem;
|
|
||||||
use super::ChainItem;
|
|
||||||
use crate::utils::{config, dirs, help};
|
|
||||||
use anyhow::{bail, Context, Result};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_yaml::Mapping;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::{fs, io::Write};
|
|
||||||
|
|
||||||
///
|
|
||||||
/// ## Profiles Config
|
|
||||||
///
|
|
||||||
/// Define the `profiles.yaml` schema
|
|
||||||
///
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct Profiles {
|
|
||||||
/// same as PrfConfig.current
|
|
||||||
current: Option<String>,
|
|
||||||
|
|
||||||
/// same as PrfConfig.chain
|
|
||||||
chain: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// record valid fields for clash
|
|
||||||
valid: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// profile list
|
|
||||||
items: Option<Vec<PrfItem>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! patch {
|
|
||||||
($lv: expr, $rv: expr, $key: tt) => {
|
|
||||||
if ($rv.$key).is_some() {
|
|
||||||
$lv.$key = $rv.$key;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Profiles {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Profiles::read_file()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// read the config from the file
|
|
||||||
pub fn read_file() -> Self {
|
|
||||||
let mut profiles = config::read_yaml::<Self>(dirs::profiles_path());
|
|
||||||
|
|
||||||
if profiles.items.is_none() {
|
|
||||||
profiles.items = Some(vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// compatiable with the old old old version
|
|
||||||
profiles.items.as_mut().map(|items| {
|
|
||||||
for mut item in items.iter_mut() {
|
|
||||||
if item.uid.is_none() {
|
|
||||||
item.uid = Some(help::get_uid("d"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
profiles
|
|
||||||
}
|
|
||||||
|
|
||||||
/// save the config to the file
|
|
||||||
pub fn save_file(&self) -> Result<()> {
|
|
||||||
config::save_yaml(
|
|
||||||
dirs::profiles_path(),
|
|
||||||
self,
|
|
||||||
Some("# Profiles Config for Clash Verge\n\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get the current uid
|
|
||||||
pub fn get_current(&self) -> Option<String> {
|
|
||||||
self.current.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// only change the main to the target id
|
|
||||||
pub fn put_current(&mut self, uid: String) -> Result<()> {
|
|
||||||
if self.items.is_none() {
|
|
||||||
self.items = Some(vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let items = self.items.as_ref().unwrap();
|
|
||||||
let some_uid = Some(uid.clone());
|
|
||||||
|
|
||||||
if items.iter().find(|&each| each.uid == some_uid).is_some() {
|
|
||||||
self.current = some_uid;
|
|
||||||
return self.save_file();
|
|
||||||
}
|
|
||||||
|
|
||||||
bail!("invalid uid \"{uid}\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// just change the `chain`
|
|
||||||
pub fn put_chain(&mut self, chain: Option<Vec<String>>) -> Result<()> {
|
|
||||||
self.chain = chain;
|
|
||||||
self.save_file()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// just change the `field`
|
|
||||||
pub fn put_valid(&mut self, valid: Option<Vec<String>>) -> Result<()> {
|
|
||||||
self.valid = valid;
|
|
||||||
self.save_file()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get items ref
|
|
||||||
pub fn get_items(&self) -> Option<&Vec<PrfItem>> {
|
|
||||||
self.items.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// find the item by the uid
|
|
||||||
pub fn get_item(&self, uid: &String) -> Result<&PrfItem> {
|
|
||||||
if self.items.is_some() {
|
|
||||||
let items = self.items.as_ref().unwrap();
|
|
||||||
let some_uid = Some(uid.clone());
|
|
||||||
|
|
||||||
for each in items.iter() {
|
|
||||||
if each.uid == some_uid {
|
|
||||||
return Ok(each);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bail!("failed to get the profile item \"uid:{uid}\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// append new item
|
|
||||||
/// if the file_data is some
|
|
||||||
/// then should save the data to file
|
|
||||||
pub fn append_item(&mut self, mut item: PrfItem) -> Result<()> {
|
|
||||||
if item.uid.is_none() {
|
|
||||||
bail!("the uid should not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
// save the file data
|
|
||||||
// move the field value after save
|
|
||||||
if let Some(file_data) = item.file_data.take() {
|
|
||||||
if item.file.is_none() {
|
|
||||||
bail!("the file should not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = item.file.clone().unwrap();
|
|
||||||
let path = dirs::app_profiles_dir().join(&file);
|
|
||||||
|
|
||||||
fs::File::create(path)
|
|
||||||
.context(format!("failed to create file \"{}\"", file))?
|
|
||||||
.write(file_data.as_bytes())
|
|
||||||
.context(format!("failed to write to file \"{}\"", file))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.items.is_none() {
|
|
||||||
self.items = Some(vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.items.as_mut().map(|items| items.push(item));
|
|
||||||
self.save_file()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// update the item value
|
|
||||||
pub fn patch_item(&mut self, uid: String, item: PrfItem) -> Result<()> {
|
|
||||||
let mut items = self.items.take().unwrap_or(vec![]);
|
|
||||||
|
|
||||||
for mut each in items.iter_mut() {
|
|
||||||
if each.uid == Some(uid.clone()) {
|
|
||||||
patch!(each, item, itype);
|
|
||||||
patch!(each, item, name);
|
|
||||||
patch!(each, item, desc);
|
|
||||||
patch!(each, item, file);
|
|
||||||
patch!(each, item, url);
|
|
||||||
patch!(each, item, selected);
|
|
||||||
patch!(each, item, extra);
|
|
||||||
patch!(each, item, updated);
|
|
||||||
patch!(each, item, option);
|
|
||||||
|
|
||||||
self.items = Some(items);
|
|
||||||
return self.save_file();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.items = Some(items);
|
|
||||||
bail!("failed to find the profile item \"uid:{uid}\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// be used to update the remote item
|
|
||||||
/// only patch `updated` `extra` `file_data`
|
|
||||||
pub fn update_item(&mut self, uid: String, mut item: PrfItem) -> Result<()> {
|
|
||||||
if self.items.is_none() {
|
|
||||||
self.items = Some(vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the item
|
|
||||||
let _ = self.get_item(&uid)?;
|
|
||||||
|
|
||||||
if let Some(items) = self.items.as_mut() {
|
|
||||||
let some_uid = Some(uid.clone());
|
|
||||||
|
|
||||||
for mut each in items.iter_mut() {
|
|
||||||
if each.uid == some_uid {
|
|
||||||
each.extra = item.extra;
|
|
||||||
each.updated = item.updated;
|
|
||||||
|
|
||||||
// save the file data
|
|
||||||
// move the field value after save
|
|
||||||
if let Some(file_data) = item.file_data.take() {
|
|
||||||
let file = each.file.take();
|
|
||||||
let file =
|
|
||||||
file.unwrap_or(item.file.take().unwrap_or(format!("{}.yaml", &uid)));
|
|
||||||
|
|
||||||
// the file must exists
|
|
||||||
each.file = Some(file.clone());
|
|
||||||
|
|
||||||
let path = dirs::app_profiles_dir().join(&file);
|
|
||||||
|
|
||||||
fs::File::create(path)
|
|
||||||
.context(format!("failed to create file \"{}\"", file))?
|
|
||||||
.write(file_data.as_bytes())
|
|
||||||
.context(format!("failed to write to file \"{}\"", file))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.save_file()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// delete item
|
|
||||||
/// if delete the current then return true
|
|
||||||
pub fn delete_item(&mut self, uid: String) -> Result<bool> {
|
|
||||||
let current = self.current.as_ref().unwrap_or(&uid);
|
|
||||||
let current = current.clone();
|
|
||||||
|
|
||||||
let mut items = self.items.take().unwrap_or(vec![]);
|
|
||||||
let mut index = None;
|
|
||||||
|
|
||||||
// get the index
|
|
||||||
for i in 0..items.len() {
|
|
||||||
if items[i].uid == Some(uid.clone()) {
|
|
||||||
index = Some(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(index) = index {
|
|
||||||
items.remove(index).file.map(|file| {
|
|
||||||
let path = dirs::app_profiles_dir().join(file);
|
|
||||||
if path.exists() {
|
|
||||||
let _ = fs::remove_file(path);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete the original uid
|
|
||||||
if current == uid {
|
|
||||||
self.current = match items.len() > 0 {
|
|
||||||
true => items[0].uid.clone(),
|
|
||||||
false => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
self.items = Some(items);
|
|
||||||
self.save_file()?;
|
|
||||||
Ok(current == uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// generate the current Mapping data
|
|
||||||
fn gen_current(&self) -> Result<Mapping> {
|
|
||||||
let config = Mapping::new();
|
|
||||||
|
|
||||||
if self.current.is_none() || self.items.is_none() {
|
|
||||||
return Ok(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
let current = self.current.clone().unwrap();
|
|
||||||
for item in self.items.as_ref().unwrap().iter() {
|
|
||||||
if item.uid == Some(current.clone()) {
|
|
||||||
let file_path = match item.file.clone() {
|
|
||||||
Some(file) => dirs::app_profiles_dir().join(file),
|
|
||||||
None => bail!("failed to get the file field"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !file_path.exists() {
|
|
||||||
bail!("failed to read the file \"{}\"", file_path.display());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(config::read_merge_mapping(file_path.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bail!("failed to find current profile \"uid:{current}\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// generate the data for activate clash config
|
|
||||||
pub fn gen_activate(&self) -> Result<PrfActivate> {
|
|
||||||
let current = self.gen_current()?;
|
|
||||||
let chain = match self.chain.as_ref() {
|
|
||||||
Some(chain) => chain
|
|
||||||
.iter()
|
|
||||||
.filter_map(|uid| self.get_item(uid).ok())
|
|
||||||
.filter_map(|item| item.to_enhance())
|
|
||||||
.collect::<Vec<ChainItem>>(),
|
|
||||||
None => vec![],
|
|
||||||
};
|
|
||||||
let valid = self.valid.clone().unwrap_or(vec![]);
|
|
||||||
|
|
||||||
Ok(PrfActivate {
|
|
||||||
current,
|
|
||||||
chain,
|
|
||||||
valid,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
pub struct PrfActivate {
|
|
||||||
pub current: Mapping,
|
|
||||||
pub chain: Vec<ChainItem>,
|
|
||||||
pub valid: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct RuntimeResult {
|
|
||||||
pub config: Option<Mapping>,
|
|
||||||
pub config_yaml: Option<String>,
|
|
||||||
// 记录在配置中(包括merge和script生成的)出现过的keys
|
|
||||||
// 这些keys不一定都生效
|
|
||||||
pub exists_keys: Vec<String>,
|
|
||||||
pub chain_logs: HashMap<String, Vec<(String, String)>>,
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
use crate::utils::{config, dirs};
|
|
||||||
use anyhow::Result;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// ### `verge.yaml` schema
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct Verge {
|
|
||||||
/// app listening port
|
|
||||||
/// for app singleton
|
|
||||||
pub app_singleton_port: Option<u16>,
|
|
||||||
|
|
||||||
// i18n
|
|
||||||
pub language: Option<String>,
|
|
||||||
|
|
||||||
/// `light` or `dark` or `system`
|
|
||||||
pub theme_mode: Option<String>,
|
|
||||||
|
|
||||||
/// enable blur mode
|
|
||||||
/// maybe be able to set the alpha
|
|
||||||
pub theme_blur: Option<bool>,
|
|
||||||
|
|
||||||
/// enable traffic graph default is true
|
|
||||||
pub traffic_graph: Option<bool>,
|
|
||||||
|
|
||||||
/// clash tun mode
|
|
||||||
pub enable_tun_mode: Option<bool>,
|
|
||||||
|
|
||||||
/// windows service mode
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub enable_service_mode: Option<bool>,
|
|
||||||
|
|
||||||
/// can the app auto startup
|
|
||||||
pub enable_auto_launch: Option<bool>,
|
|
||||||
|
|
||||||
/// not show the window on launch
|
|
||||||
pub enable_silent_start: Option<bool>,
|
|
||||||
|
|
||||||
/// set system proxy
|
|
||||||
pub enable_system_proxy: Option<bool>,
|
|
||||||
|
|
||||||
/// enable proxy guard
|
|
||||||
pub enable_proxy_guard: Option<bool>,
|
|
||||||
|
|
||||||
/// set system proxy bypass
|
|
||||||
pub system_proxy_bypass: Option<String>,
|
|
||||||
|
|
||||||
/// proxy guard duration
|
|
||||||
pub proxy_guard_duration: Option<u64>,
|
|
||||||
|
|
||||||
/// theme setting
|
|
||||||
pub theme_setting: Option<VergeTheme>,
|
|
||||||
|
|
||||||
/// web ui list
|
|
||||||
pub web_ui_list: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// clash core path
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub clash_core: Option<String>,
|
|
||||||
|
|
||||||
/// hotkey map
|
|
||||||
/// format: {func},{key}
|
|
||||||
pub hotkeys: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// 切换代理时自动关闭连接
|
|
||||||
pub auto_close_connection: Option<bool>,
|
|
||||||
|
|
||||||
/// 默认的延迟测试连接
|
|
||||||
pub default_latency_test: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct VergeTheme {
|
|
||||||
pub primary_color: Option<String>,
|
|
||||||
pub secondary_color: Option<String>,
|
|
||||||
pub primary_text: Option<String>,
|
|
||||||
pub secondary_text: Option<String>,
|
|
||||||
|
|
||||||
pub info_color: Option<String>,
|
|
||||||
pub error_color: Option<String>,
|
|
||||||
pub warning_color: Option<String>,
|
|
||||||
pub success_color: Option<String>,
|
|
||||||
|
|
||||||
pub font_family: Option<String>,
|
|
||||||
pub css_injection: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Verge {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
config::read_yaml::<Verge>(dirs::verge_path())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save Verge App Config
|
|
||||||
pub fn save_file(&self) -> Result<()> {
|
|
||||||
config::save_yaml(
|
|
||||||
dirs::verge_path(),
|
|
||||||
self,
|
|
||||||
Some("# The Config for Clash Verge App\n\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// patch verge config
|
|
||||||
/// only save to file
|
|
||||||
pub fn patch_config(&mut self, patch: Verge) -> Result<()> {
|
|
||||||
macro_rules! patch {
|
|
||||||
($key: tt) => {
|
|
||||||
if patch.$key.is_some() {
|
|
||||||
self.$key = patch.$key;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
patch!(language);
|
|
||||||
patch!(theme_mode);
|
|
||||||
patch!(theme_blur);
|
|
||||||
patch!(traffic_graph);
|
|
||||||
|
|
||||||
patch!(enable_tun_mode);
|
|
||||||
patch!(enable_service_mode);
|
|
||||||
patch!(enable_auto_launch);
|
|
||||||
patch!(enable_silent_start);
|
|
||||||
patch!(enable_system_proxy);
|
|
||||||
patch!(enable_proxy_guard);
|
|
||||||
patch!(system_proxy_bypass);
|
|
||||||
patch!(proxy_guard_duration);
|
|
||||||
|
|
||||||
patch!(theme_setting);
|
|
||||||
patch!(web_ui_list);
|
|
||||||
patch!(clash_core);
|
|
||||||
patch!(hotkeys);
|
|
||||||
|
|
||||||
patch!(auto_close_connection);
|
|
||||||
patch!(default_latency_test);
|
|
||||||
|
|
||||||
self.save_file()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
use crate::config::*;
|
use crate::config::*;
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
use crate::log_err;
|
use crate::log_err;
|
||||||
use anyhow::Result;
|
use anyhow::{bail, Result};
|
||||||
use serde_yaml::{Mapping, Value};
|
use serde_yaml::{Mapping, Value};
|
||||||
|
|
||||||
// 重启clash
|
// 重启clash
|
||||||
@ -22,11 +22,9 @@ pub fn change_clash_mode(mode: String) {
|
|||||||
match clash_api::patch_configs(&mapping).await {
|
match clash_api::patch_configs(&mapping).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// 更新配置
|
// 更新配置
|
||||||
let mut clash = ClashN::global().config.lock();
|
Config::clash().data().patch_config(mapping);
|
||||||
clash.insert(Value::from("mode"), mode.into());
|
|
||||||
drop(clash);
|
|
||||||
|
|
||||||
if let Ok(_) = ClashN::global().save_config() {
|
if let Ok(_) = Config::clash().data().save_config() {
|
||||||
handle::Handle::refresh_clash();
|
handle::Handle::refresh_clash();
|
||||||
log_err!(handle::Handle::update_systray_part());
|
log_err!(handle::Handle::update_systray_part());
|
||||||
}
|
}
|
||||||
@ -39,163 +37,199 @@ pub fn change_clash_mode(mode: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 切换系统代理
|
// 切换系统代理
|
||||||
pub fn toggle_system_proxy() -> Result<()> {
|
pub fn toggle_system_proxy() {
|
||||||
let enable = {
|
let enable = Config::verge().draft().enable_system_proxy.clone();
|
||||||
let verge = VergeN::global().config.lock();
|
let enable = enable.unwrap_or(false);
|
||||||
verge.enable_system_proxy.clone().unwrap_or(false)
|
|
||||||
};
|
|
||||||
patch_verge(IVerge {
|
|
||||||
enable_system_proxy: Some(!enable),
|
|
||||||
..IVerge::default()
|
|
||||||
})?;
|
|
||||||
handle::Handle::refresh_verge();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开系统代理
|
|
||||||
pub fn enable_system_proxy() -> Result<()> {
|
|
||||||
patch_verge(IVerge {
|
|
||||||
enable_system_proxy: Some(true),
|
|
||||||
..IVerge::default()
|
|
||||||
})?;
|
|
||||||
handle::Handle::refresh_verge();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭系统代理
|
|
||||||
pub fn disable_system_proxy() -> Result<()> {
|
|
||||||
patch_verge(IVerge {
|
|
||||||
enable_system_proxy: Some(false),
|
|
||||||
..IVerge::default()
|
|
||||||
})?;
|
|
||||||
handle::Handle::refresh_verge();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换tun模式
|
|
||||||
pub fn toggle_tun_mode() -> Result<()> {
|
|
||||||
let enable = {
|
|
||||||
let verge = VergeN::global().config.lock();
|
|
||||||
verge.enable_tun_mode.clone().unwrap_or(false)
|
|
||||||
};
|
|
||||||
|
|
||||||
patch_verge(IVerge {
|
|
||||||
enable_tun_mode: Some(!enable),
|
|
||||||
..IVerge::default()
|
|
||||||
})?;
|
|
||||||
handle::Handle::refresh_verge();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开tun模式
|
|
||||||
pub fn enable_tun_mode() -> Result<()> {
|
|
||||||
patch_verge(IVerge {
|
|
||||||
enable_tun_mode: Some(true),
|
|
||||||
..IVerge::default()
|
|
||||||
})?;
|
|
||||||
handle::Handle::refresh_verge();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭tun模式
|
|
||||||
pub fn disable_tun_mode() -> Result<()> {
|
|
||||||
patch_verge(IVerge {
|
|
||||||
enable_tun_mode: Some(false),
|
|
||||||
..IVerge::default()
|
|
||||||
})?;
|
|
||||||
handle::Handle::refresh_verge();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 修改clash的配置
|
|
||||||
pub fn patch_clash(patch: Mapping) -> Result<()> {
|
|
||||||
let patch_cloned = patch.clone();
|
|
||||||
let clash_mode = patch.get("mode").is_some();
|
|
||||||
let mixed_port = patch.get("mixed-port").is_some();
|
|
||||||
let external = patch.get("external-controller").is_some();
|
|
||||||
let secret = patch.get("secret").is_some();
|
|
||||||
|
|
||||||
// 更新info信息
|
|
||||||
if mixed_port || external || secret {
|
|
||||||
let mut tmp_config = { ClashN::global().config.lock().clone() };
|
|
||||||
|
|
||||||
for (key, value) in patch.into_iter() {
|
|
||||||
tmp_config.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
let old_info = ClashN::global().patch_info(ClashInfoN::from(&tmp_config))?;
|
|
||||||
|
|
||||||
if let Err(err) = CoreManager::global().run_core() {
|
|
||||||
// 恢复旧值
|
|
||||||
ClashN::global().patch_info(old_info)?;
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 存好再搞
|
|
||||||
ClashN::global().patch_config(patch_cloned)?;
|
|
||||||
|
|
||||||
// 激活配置
|
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
match handle_activate().await {
|
match patch_verge(IVerge {
|
||||||
Ok(_) => {
|
enable_system_proxy: Some(!enable),
|
||||||
// 更新系统代理
|
..IVerge::default()
|
||||||
if mixed_port {
|
})
|
||||||
log_err!(sysopt::Sysopt::global().init_sysproxy());
|
.await
|
||||||
}
|
{
|
||||||
|
Ok(_) => handle::Handle::refresh_verge(),
|
||||||
if clash_mode {
|
|
||||||
log_err!(handle::Handle::update_systray_part());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => log::error!(target: "app", "{err}"),
|
Err(err) => log::error!(target: "app", "{err}"),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Ok(())
|
}
|
||||||
|
|
||||||
|
// 打开系统代理
|
||||||
|
pub fn enable_system_proxy() {
|
||||||
|
tauri::async_runtime::spawn(async {
|
||||||
|
match patch_verge(IVerge {
|
||||||
|
enable_system_proxy: Some(true),
|
||||||
|
..IVerge::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => handle::Handle::refresh_verge(),
|
||||||
|
Err(err) => log::error!(target: "app", "{err}"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭系统代理
|
||||||
|
pub fn disable_system_proxy() {
|
||||||
|
tauri::async_runtime::spawn(async {
|
||||||
|
match patch_verge(IVerge {
|
||||||
|
enable_system_proxy: Some(false),
|
||||||
|
..IVerge::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => handle::Handle::refresh_verge(),
|
||||||
|
Err(err) => log::error!(target: "app", "{err}"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换tun模式
|
||||||
|
pub fn toggle_tun_mode() {
|
||||||
|
let enable = Config::verge().data().enable_tun_mode.clone();
|
||||||
|
let enable = enable.unwrap_or(false);
|
||||||
|
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
match patch_verge(IVerge {
|
||||||
|
enable_tun_mode: Some(!enable),
|
||||||
|
..IVerge::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => handle::Handle::refresh_verge(),
|
||||||
|
Err(err) => log::error!(target: "app", "{err}"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开tun模式
|
||||||
|
pub fn enable_tun_mode() {
|
||||||
|
tauri::async_runtime::spawn(async {
|
||||||
|
match patch_verge(IVerge {
|
||||||
|
enable_tun_mode: Some(true),
|
||||||
|
..IVerge::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => handle::Handle::refresh_verge(),
|
||||||
|
Err(err) => log::error!(target: "app", "{err}"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭tun模式
|
||||||
|
pub fn disable_tun_mode() {
|
||||||
|
tauri::async_runtime::spawn(async {
|
||||||
|
match patch_verge(IVerge {
|
||||||
|
enable_tun_mode: Some(false),
|
||||||
|
..IVerge::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => handle::Handle::refresh_verge(),
|
||||||
|
Err(err) => log::error!(target: "app", "{err}"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改clash的配置
|
||||||
|
pub async fn patch_clash(patch: Mapping) -> Result<()> {
|
||||||
|
Config::clash().draft().patch_config(patch.clone());
|
||||||
|
|
||||||
|
match {
|
||||||
|
let mixed_port = patch.get("mixed-port");
|
||||||
|
if mixed_port.is_some() {
|
||||||
|
let changed = mixed_port != Config::clash().data().0.get("mixed-port");
|
||||||
|
// 检查端口占用
|
||||||
|
if changed {
|
||||||
|
if let Some(port) = mixed_port.clone().unwrap().as_u64() {
|
||||||
|
if !port_scanner::local_port_available(port as u16) {
|
||||||
|
Config::clash().discard();
|
||||||
|
bail!("the port not available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 激活配置
|
||||||
|
handle_activate().await?;
|
||||||
|
|
||||||
|
// 更新系统代理
|
||||||
|
if mixed_port.is_some() {
|
||||||
|
log_err!(sysopt::Sysopt::global().init_sysproxy());
|
||||||
|
}
|
||||||
|
|
||||||
|
if patch.get("mode").is_some() {
|
||||||
|
log_err!(handle::Handle::update_systray_part());
|
||||||
|
}
|
||||||
|
|
||||||
|
<Result<()>>::Ok(())
|
||||||
|
} {
|
||||||
|
Ok(()) => {
|
||||||
|
Config::clash().apply();
|
||||||
|
Config::clash().data().save_config()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
Config::clash().discard();
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 修改verge的配置
|
/// 修改verge的配置
|
||||||
/// 一般都是一个个的修改
|
/// 一般都是一个个的修改
|
||||||
pub fn patch_verge(patch: IVerge) -> Result<()> {
|
pub async fn patch_verge(patch: IVerge) -> Result<()> {
|
||||||
VergeN::global().patch_config(patch.clone())?;
|
Config::verge().draft().patch_config(patch.clone());
|
||||||
|
|
||||||
let tun_mode = patch.enable_tun_mode;
|
let tun_mode = patch.enable_tun_mode;
|
||||||
let auto_launch = patch.enable_auto_launch;
|
let auto_launch = patch.enable_auto_launch;
|
||||||
let system_proxy = patch.enable_system_proxy;
|
let system_proxy = patch.enable_system_proxy;
|
||||||
let proxy_bypass = patch.system_proxy_bypass;
|
let proxy_bypass = patch.system_proxy_bypass;
|
||||||
let proxy_guard = patch.enable_proxy_guard;
|
|
||||||
let language = patch.language;
|
let language = patch.language;
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
match {
|
||||||
{}
|
#[cfg(target_os = "windows")]
|
||||||
|
{}
|
||||||
|
|
||||||
if tun_mode.is_some() {
|
if tun_mode.is_some() {
|
||||||
tauri::async_runtime::spawn(async {
|
handle_activate().await?;
|
||||||
log_err!(handle_activate().await);
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if auto_launch.is_some() {
|
if auto_launch.is_some() {
|
||||||
sysopt::Sysopt::global().update_launch()?;
|
sysopt::Sysopt::global().update_launch()?;
|
||||||
}
|
}
|
||||||
if system_proxy.is_some() || proxy_bypass.is_some() {
|
if system_proxy.is_some() || proxy_bypass.is_some() {
|
||||||
sysopt::Sysopt::global().update_sysproxy()?;
|
sysopt::Sysopt::global().update_sysproxy()?;
|
||||||
sysopt::Sysopt::global().guard_proxy();
|
sysopt::Sysopt::global().guard_proxy();
|
||||||
}
|
}
|
||||||
if proxy_guard.unwrap_or(false) {
|
|
||||||
sysopt::Sysopt::global().guard_proxy();
|
|
||||||
}
|
|
||||||
|
|
||||||
if language.is_some() {
|
if let Some(true) = patch.enable_proxy_guard {
|
||||||
handle::Handle::update_systray()?;
|
sysopt::Sysopt::global().guard_proxy();
|
||||||
} else if system_proxy.or(tun_mode).is_some() {
|
}
|
||||||
handle::Handle::update_systray_part()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if patch.hotkeys.is_some() {
|
if let Some(hotkeys) = patch.hotkeys {
|
||||||
hotkey::Hotkey::global().update(patch.hotkeys.unwrap())?;
|
hotkey::Hotkey::global().update(hotkeys)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
if language.is_some() {
|
||||||
|
handle::Handle::update_systray()?;
|
||||||
|
} else if system_proxy.or(tun_mode).is_some() {
|
||||||
|
handle::Handle::update_systray_part()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
<Result<()>>::Ok(())
|
||||||
|
} {
|
||||||
|
Ok(()) => {
|
||||||
|
Config::verge().apply();
|
||||||
|
Config::verge().data().save_file()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
Config::verge().discard();
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 激活配置
|
/// 激活配置
|
||||||
@ -216,5 +250,38 @@ pub async fn handle_activate() -> Result<()> {
|
|||||||
/// 更新某个profile
|
/// 更新某个profile
|
||||||
/// 如果更新当前配置就激活配置
|
/// 如果更新当前配置就激活配置
|
||||||
pub async fn update_profile(uid: String, option: Option<PrfOption>) -> Result<()> {
|
pub async fn update_profile(uid: String, option: Option<PrfOption>) -> Result<()> {
|
||||||
|
let url_opt = {
|
||||||
|
let profiles = Config::profiles();
|
||||||
|
let profiles = profiles.latest();
|
||||||
|
let item = profiles.get_item(&uid)?;
|
||||||
|
let is_remote = item.itype.as_ref().map_or(false, |s| s == "remote");
|
||||||
|
|
||||||
|
if !is_remote {
|
||||||
|
None // 直接更新
|
||||||
|
} else if item.url.is_none() {
|
||||||
|
bail!("failed to get the profile item url");
|
||||||
|
} else {
|
||||||
|
Some((item.url.clone().unwrap(), item.option.clone()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let should_update = match url_opt {
|
||||||
|
Some((url, opt)) => {
|
||||||
|
let merged_opt = PrfOption::merge(opt, option);
|
||||||
|
let item = PrfItem::from_url(&url, None, None, merged_opt).await?;
|
||||||
|
|
||||||
|
let profiles = Config::profiles();
|
||||||
|
let mut profiles = profiles.latest();
|
||||||
|
profiles.update_item(uid.clone(), item)?;
|
||||||
|
|
||||||
|
Some(uid) == profiles.get_current()
|
||||||
|
}
|
||||||
|
None => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_update {
|
||||||
|
handle_activate().await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
mod cmds;
|
mod cmds;
|
||||||
mod config;
|
mod config;
|
||||||
mod core;
|
mod core;
|
||||||
// mod data;
|
|
||||||
mod enhance;
|
mod enhance;
|
||||||
mod feat;
|
mod feat;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
use crate::config::Config;
|
||||||
use crate::log_err;
|
use crate::log_err;
|
||||||
use crate::{config::VergeN, core::*, utils::init, utils::server};
|
use crate::{core::*, utils::init, utils::server};
|
||||||
use tauri::{App, AppHandle, Manager};
|
use tauri::{App, AppHandle, Manager};
|
||||||
|
|
||||||
/// handle something when start app
|
/// handle something when start app
|
||||||
@ -19,10 +20,7 @@ pub fn resolve_setup(app: &mut App) {
|
|||||||
|
|
||||||
log_err!(tray::Tray::update_systray(&app.app_handle()));
|
log_err!(tray::Tray::update_systray(&app.app_handle()));
|
||||||
|
|
||||||
let silent_start = {
|
let silent_start = { Config::verge().data().enable_silent_start.clone() };
|
||||||
let verge = VergeN::global().config.lock();
|
|
||||||
verge.enable_silent_start.clone()
|
|
||||||
};
|
|
||||||
if !silent_start.unwrap_or(false) {
|
if !silent_start.unwrap_or(false) {
|
||||||
create_window(&app.app_handle());
|
create_window(&app.app_handle());
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
extern crate warp;
|
extern crate warp;
|
||||||
|
|
||||||
use super::resolve;
|
use super::resolve;
|
||||||
use crate::config::VergeN;
|
use crate::config::IVerge;
|
||||||
use port_scanner::local_port_available;
|
use port_scanner::local_port_available;
|
||||||
use tauri::AppHandle;
|
use tauri::AppHandle;
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
|
||||||
/// check whether there is already exists
|
/// check whether there is already exists
|
||||||
pub fn check_singleton() -> Result<(), ()> {
|
pub fn check_singleton() -> Result<(), ()> {
|
||||||
let port = VergeN::get_singleton_port();
|
let port = IVerge::get_singleton_port();
|
||||||
|
|
||||||
if !local_port_available(port) {
|
if !local_port_available(port) {
|
||||||
tauri::async_runtime::block_on(async {
|
tauri::async_runtime::block_on(async {
|
||||||
@ -25,7 +25,7 @@ pub fn check_singleton() -> Result<(), ()> {
|
|||||||
/// maybe it can be used as pac server later
|
/// maybe it can be used as pac server later
|
||||||
pub fn embed_server(app_handle: AppHandle) {
|
pub fn embed_server(app_handle: AppHandle) {
|
||||||
let app_handle = app_handle.clone();
|
let app_handle = app_handle.clone();
|
||||||
let port = VergeN::get_singleton_port();
|
let port = IVerge::get_singleton_port();
|
||||||
|
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
let commands = warp::path!("commands" / "visible").map(move || {
|
let commands = warp::path!("commands" / "visible").map(move || {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user