mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
chore: update
This commit is contained in:
@@ -11,6 +11,7 @@ use serde_yaml::Mapping;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use sysproxy::{Autoproxy, Sysproxy};
|
||||
type CmdResult<T = ()> = Result<T, String>;
|
||||
use reqwest_dav::list_cmd::ListFile;
|
||||
use tauri::Manager;
|
||||
|
||||
#[tauri::command]
|
||||
@@ -375,6 +376,37 @@ pub async fn exit_app() {
|
||||
feat::quit(Some(0));
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn save_webdav_config(url: String, username: String, password: String) -> CmdResult<()> {
|
||||
let patch = IVerge {
|
||||
webdav_url: Some(url),
|
||||
webdav_username: Some(username),
|
||||
webdav_password: Some(password),
|
||||
..IVerge::default()
|
||||
};
|
||||
Config::verge().draft().patch_config(patch.clone());
|
||||
Config::verge().apply();
|
||||
Config::verge()
|
||||
.data()
|
||||
.save_file()
|
||||
.map_err(|err| err.to_string())?;
|
||||
backup::WebDavClient::global().reset();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn create_webdav_backup() -> CmdResult<()> {
|
||||
feat::create_backup_and_upload_webdav()
|
||||
.await
|
||||
.map_err(|err| err.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_webdav_backup() -> CmdResult<Vec<ListFile>> {
|
||||
feat::list_wevdav_backup().await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub mod service {
|
||||
use super::*;
|
||||
use crate::core::service;
|
||||
|
||||
@@ -147,6 +147,10 @@ pub struct IVerge {
|
||||
pub verge_port: Option<u16>,
|
||||
|
||||
pub verge_http_enabled: Option<bool>,
|
||||
|
||||
pub webdav_url: Option<String>,
|
||||
pub webdav_username: Option<String>,
|
||||
pub webdav_password: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||
@@ -304,6 +308,10 @@ impl IVerge {
|
||||
patch!(proxy_layout_column);
|
||||
patch!(test_list);
|
||||
patch!(auto_log_clean);
|
||||
|
||||
patch!(webdav_url);
|
||||
patch!(webdav_username);
|
||||
patch!(webdav_password);
|
||||
}
|
||||
|
||||
/// 在初始化前尝试拿到单例端口的值
|
||||
|
||||
118
src-tauri/src/core/backup.rs
Normal file
118
src-tauri/src/core/backup.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
use crate::config::Config;
|
||||
use crate::utils::dirs;
|
||||
use anyhow::Error;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use reqwest_dav::list_cmd::{ListEntity, ListFile};
|
||||
use std::env::{consts::OS, temp_dir};
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use zip::write::SimpleFileOptions;
|
||||
|
||||
pub struct WebDavClient {
|
||||
client: Arc<Mutex<Option<reqwest_dav::Client>>>,
|
||||
}
|
||||
|
||||
impl WebDavClient {
|
||||
pub fn global() -> &'static WebDavClient {
|
||||
static WEBDAV_CLIENT: OnceCell<WebDavClient> = OnceCell::new();
|
||||
WEBDAV_CLIENT.get_or_init(|| WebDavClient {
|
||||
client: Arc::new(Mutex::new(None)),
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_client(&self) -> Result<reqwest_dav::Client, Error> {
|
||||
if self.client.lock().is_none() {
|
||||
let verge = Config::verge().latest().clone();
|
||||
if verge.webdav_url.is_none()
|
||||
|| verge.webdav_username.is_none()
|
||||
|| verge.webdav_password.is_none()
|
||||
{
|
||||
let msg =
|
||||
"Unable to create web dav client, please make sure the webdav config is correct"
|
||||
.to_string();
|
||||
log::error!(target: "app","{}",msg);
|
||||
return Err(anyhow::Error::msg(msg));
|
||||
}
|
||||
|
||||
let url = verge.webdav_url.unwrap_or_default();
|
||||
let username = verge.webdav_username.unwrap_or_default();
|
||||
let password = verge.webdav_password.unwrap_or_default();
|
||||
|
||||
let client = reqwest_dav::ClientBuilder::new()
|
||||
.set_host(url.to_owned())
|
||||
.set_auth(reqwest_dav::Auth::Basic(
|
||||
username.to_owned(),
|
||||
password.to_owned(),
|
||||
))
|
||||
.build()?;
|
||||
*self.client.lock() = Some(client.clone());
|
||||
}
|
||||
Ok(self.client.lock().clone().unwrap())
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
if !self.client.lock().is_none() {
|
||||
self.client.lock().take();
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn upload(&self, file_path: PathBuf, file_name: String) -> Result<(), Error> {
|
||||
let client = self.get_client().await?;
|
||||
if client.get(dirs::BACKUP_DIR).await.is_err() {
|
||||
client.mkcol(dirs::BACKUP_DIR).await?;
|
||||
}
|
||||
|
||||
let webdav_path: String = format!("{}/{}", dirs::BACKUP_DIR, file_name);
|
||||
client
|
||||
.put(webdav_path.as_ref(), fs::read(file_path)?)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list_files(&self) -> Result<Vec<ListFile>, Error> {
|
||||
let client = self.get_client().await?;
|
||||
let files = client
|
||||
.list(dirs::BACKUP_DIR, reqwest_dav::Depth::Number(1))
|
||||
.await?;
|
||||
let mut final_files = Vec::new();
|
||||
for file in files {
|
||||
if let ListEntity::File(file) = file {
|
||||
final_files.push(file);
|
||||
}
|
||||
}
|
||||
Ok(final_files)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_backup() -> Result<(String, PathBuf), Error> {
|
||||
let now = chrono::Local::now().format("%Y-%m-%d_%H-%M-%S").to_string();
|
||||
let zip_file_name = format!("{}-backup-{}.zip", OS, now);
|
||||
let zip_path = temp_dir().join(&zip_file_name);
|
||||
|
||||
let file = fs::File::create(&zip_path)?;
|
||||
let mut zip = zip::ZipWriter::new(file);
|
||||
zip.add_directory("profiles/", SimpleFileOptions::default())?;
|
||||
let options = SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
|
||||
if let Ok(entries) = fs::read_dir(dirs::app_profiles_dir()?) {
|
||||
for entry in entries {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
let backup_path = format!("profiles/{}", entry.file_name().to_str().unwrap());
|
||||
zip.start_file(backup_path, options)?;
|
||||
zip.write_all(fs::read(path).unwrap().as_slice())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
zip.start_file(dirs::CLASH_CONFIG, options)?;
|
||||
zip.write_all(fs::read(dirs::clash_path()?)?.as_slice())?;
|
||||
zip.start_file(dirs::VERGE_CONFIG, options)?;
|
||||
zip.write_all(fs::read(dirs::verge_path()?)?.as_slice())?;
|
||||
zip.start_file(dirs::PROFILE_YAML, options)?;
|
||||
zip.write_all(fs::read(dirs::profiles_path()?)?.as_slice())?;
|
||||
zip.finish()?;
|
||||
Ok((zip_file_name, zip_path))
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod backup;
|
||||
pub mod clash_api;
|
||||
#[allow(clippy::module_inception)]
|
||||
mod core;
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::core::*;
|
||||
use crate::log_err;
|
||||
use crate::utils::resolve;
|
||||
use anyhow::{bail, Result};
|
||||
use reqwest_dav::list_cmd::ListFile;
|
||||
use serde_yaml::{Mapping, Value};
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
|
||||
@@ -401,3 +402,42 @@ pub async fn test_delay(url: String) -> Result<u32> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_backup_and_upload_webdav() -> Result<()> {
|
||||
if let Err(err) = async {
|
||||
let (file_name, temp_file_path) = backup::create_backup().map_err(|err| {
|
||||
log::error!(target: "app", "Failed to create backup: {:#?}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
backup::WebDavClient::global()
|
||||
.upload(temp_file_path.clone(), file_name)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log::error!(target: "app", "Failed to upload to WebDAV: {:#?}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
std::fs::remove_file(&temp_file_path).map_err(|err| {
|
||||
log::warn!(target: "app", "Failed to remove temp file: {:#?}", err);
|
||||
err
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.await
|
||||
{
|
||||
return Err(err);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list_wevdav_backup() -> Result<Vec<ListFile>> {
|
||||
backup::WebDavClient::global()
|
||||
.list_files()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log::error!(target: "app", "Failed to list WebDAV backup files: {:#?}", err);
|
||||
err
|
||||
})
|
||||
}
|
||||
|
||||
@@ -123,7 +123,11 @@ pub fn run() {
|
||||
// service mode
|
||||
cmds::service::check_service,
|
||||
// clash api
|
||||
cmds::clash_api_get_proxy_delay
|
||||
cmds::clash_api_get_proxy_delay,
|
||||
// backup
|
||||
cmds::create_webdav_backup,
|
||||
cmds::save_webdav_config,
|
||||
cmds::list_webdav_backup,
|
||||
]);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
||||
@@ -6,14 +6,19 @@ use tauri::Manager;
|
||||
|
||||
#[cfg(not(feature = "verge-dev"))]
|
||||
pub static APP_ID: &str = "io.github.clash-verge-rev.clash-verge-rev";
|
||||
#[cfg(not(feature = "verge-dev"))]
|
||||
pub static BACKUP_DIR: &str = "clash-verge-rev-backup";
|
||||
|
||||
#[cfg(feature = "verge-dev")]
|
||||
pub static APP_ID: &str = "io.github.clash-verge-rev.clash-verge-rev.dev";
|
||||
#[cfg(feature = "verge-dev")]
|
||||
pub static BACKUP_DIR: &str = "clash-verge-rev-backup-dev";
|
||||
|
||||
pub static PORTABLE_FLAG: OnceCell<bool> = OnceCell::new();
|
||||
|
||||
static CLASH_CONFIG: &str = "config.yaml";
|
||||
static VERGE_CONFIG: &str = "verge.yaml";
|
||||
static PROFILE_YAML: &str = "profiles.yaml";
|
||||
pub static CLASH_CONFIG: &str = "config.yaml";
|
||||
pub static VERGE_CONFIG: &str = "verge.yaml";
|
||||
pub static PROFILE_YAML: &str = "profiles.yaml";
|
||||
|
||||
/// init portable flag
|
||||
pub fn init_portable_flag() -> Result<()> {
|
||||
|
||||
Reference in New Issue
Block a user