mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 17:15:38 +08:00
refactor: convert file operations to async using tokio fs (#5267)
* refactor: convert file operations to async using tokio fs * refactor: integrate AsyncHandler for file operations in backup processes
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use crate::{config::Config, utils::dirs};
|
||||
use crate::{config::Config, process::AsyncHandler, utils::dirs};
|
||||
use anyhow::Error;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
@@ -7,13 +7,12 @@ use smartstring::alias::String;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env::{consts::OS, temp_dir},
|
||||
fs,
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::time::timeout;
|
||||
use tokio::{fs, time::timeout};
|
||||
use zip::write::SimpleFileOptions;
|
||||
|
||||
// 应用版本常量,来自 tauri.conf.json
|
||||
@@ -170,7 +169,7 @@ impl WebDavClient {
|
||||
let webdav_path: String = format!("{}/{}", dirs::BACKUP_DIR, file_name).into();
|
||||
|
||||
// 读取文件并上传,如果失败尝试一次重试
|
||||
let file_content = fs::read(&file_path)?;
|
||||
let file_content = fs::read(&file_path).await?;
|
||||
|
||||
// 添加超时保护
|
||||
let upload_result = timeout(
|
||||
@@ -212,7 +211,7 @@ impl WebDavClient {
|
||||
let fut = async {
|
||||
let response = client.get(path.as_str()).await?;
|
||||
let content = response.bytes().await?;
|
||||
fs::write(&storage_path, &content)?;
|
||||
fs::write(&storage_path, &content).await?;
|
||||
Ok::<(), Error>(())
|
||||
};
|
||||
|
||||
@@ -250,18 +249,19 @@ impl WebDavClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_backup() -> Result<(String, PathBuf), Error> {
|
||||
pub async 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: String = format!("{OS}-backup-{now}.zip").into();
|
||||
let zip_path = temp_dir().join(zip_file_name.as_str());
|
||||
|
||||
let file = fs::File::create(&zip_path)?;
|
||||
let value = zip_path.clone();
|
||||
let file = AsyncHandler::spawn_blocking(move || std::fs::File::create(&value)).await??;
|
||||
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?;
|
||||
|
||||
if let Ok(mut entries) = fs::read_dir(dirs::app_profiles_dir()?).await {
|
||||
while let Some(entry) = entries.next_entry().await? {
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
let file_name_os = entry.file_name();
|
||||
@@ -270,16 +270,16 @@ pub fn create_backup() -> Result<(String, PathBuf), Error> {
|
||||
.ok_or_else(|| anyhow::Error::msg("Invalid file name encoding"))?;
|
||||
let backup_path = format!("profiles/{}", file_name);
|
||||
zip.start_file(backup_path, options)?;
|
||||
let file_content = fs::read(&path)?;
|
||||
let file_content = fs::read(&path).await?;
|
||||
zip.write_all(&file_content)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
zip.start_file(dirs::CLASH_CONFIG, options)?;
|
||||
zip.write_all(fs::read(dirs::clash_path()?)?.as_slice())?;
|
||||
zip.write_all(fs::read(dirs::clash_path()?).await?.as_slice())?;
|
||||
|
||||
let mut verge_config: serde_json::Value =
|
||||
serde_yaml_ng::from_str(&fs::read_to_string(dirs::verge_path()?)?)?;
|
||||
let verge_text = fs::read_to_string(dirs::verge_path()?).await?;
|
||||
let mut verge_config: serde_json::Value = serde_yaml_ng::from_str(&verge_text)?;
|
||||
if let Some(obj) = verge_config.as_object_mut() {
|
||||
obj.remove("webdav_username");
|
||||
obj.remove("webdav_password");
|
||||
@@ -291,11 +291,11 @@ pub fn create_backup() -> Result<(String, PathBuf), Error> {
|
||||
let dns_config_path = dirs::app_home_dir()?.join(dirs::DNS_CONFIG);
|
||||
if dns_config_path.exists() {
|
||||
zip.start_file(dirs::DNS_CONFIG, options)?;
|
||||
zip.write_all(fs::read(&dns_config_path)?.as_slice())?;
|
||||
zip.write_all(fs::read(&dns_config_path).await?.as_slice())?;
|
||||
}
|
||||
|
||||
zip.start_file(dirs::PROFILE_YAML, options)?;
|
||||
zip.write_all(fs::read(dirs::profiles_path()?)?.as_slice())?;
|
||||
zip.write_all(fs::read(dirs::profiles_path()?).await?.as_slice())?;
|
||||
zip.finish()?;
|
||||
Ok((zip_file_name, zip_path))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use once_cell::sync::OnceCell;
|
||||
use tauri::Emitter;
|
||||
use tauri::tray::TrayIconBuilder;
|
||||
use tauri_plugin_mihomo::models::Proxies;
|
||||
use tokio::fs;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod speed_rate;
|
||||
use crate::config::PrfSelected;
|
||||
@@ -26,7 +27,6 @@ use smartstring::alias::String;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
fs,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
@@ -86,7 +86,7 @@ impl TrayState {
|
||||
let is_common_tray_icon = verge.common_tray_icon.unwrap_or(false);
|
||||
if is_common_tray_icon
|
||||
&& let Ok(Some(common_icon_path)) = find_target_icons("common")
|
||||
&& let Ok(icon_data) = fs::read(common_icon_path)
|
||||
&& let Ok(icon_data) = fs::read(common_icon_path).await
|
||||
{
|
||||
return (true, icon_data);
|
||||
}
|
||||
@@ -123,7 +123,7 @@ impl TrayState {
|
||||
let is_sysproxy_tray_icon = verge.sysproxy_tray_icon.unwrap_or(false);
|
||||
if is_sysproxy_tray_icon
|
||||
&& let Ok(Some(sysproxy_icon_path)) = find_target_icons("sysproxy")
|
||||
&& let Ok(icon_data) = fs::read(sysproxy_icon_path)
|
||||
&& let Ok(icon_data) = fs::read(sysproxy_icon_path).await
|
||||
{
|
||||
return (true, icon_data);
|
||||
}
|
||||
@@ -160,7 +160,7 @@ impl TrayState {
|
||||
let is_tun_tray_icon = verge.tun_tray_icon.unwrap_or(false);
|
||||
if is_tun_tray_icon
|
||||
&& let Ok(Some(tun_icon_path)) = find_target_icons("tun")
|
||||
&& let Ok(icon_data) = fs::read(tun_icon_path)
|
||||
&& let Ok(icon_data) = fs::read(tun_icon_path).await
|
||||
{
|
||||
return (true, icon_data);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use anyhow::Result;
|
||||
use scopeguard::defer;
|
||||
use smartstring::alias::String;
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
use tokio::fs;
|
||||
|
||||
use crate::config::{Config, ConfigType};
|
||||
use crate::core::handle;
|
||||
@@ -33,19 +33,16 @@ impl CoreConfigValidator {
|
||||
|
||||
impl CoreConfigValidator {
|
||||
/// 检查文件是否为脚本文件
|
||||
fn is_script_file<P>(path: P) -> Result<bool>
|
||||
where
|
||||
P: AsRef<Path> + std::fmt::Display,
|
||||
{
|
||||
async fn is_script_file(path: &str) -> Result<bool> {
|
||||
// 1. 先通过扩展名快速判断
|
||||
if has_ext(&path, "yaml") || has_ext(&path, "yml") {
|
||||
if has_ext(path, "yaml") || has_ext(path, "yml") {
|
||||
return Ok(false); // YAML文件不是脚本文件
|
||||
} else if has_ext(&path, "js") {
|
||||
} else if has_ext(path, "js") {
|
||||
return Ok(true); // JS文件是脚本文件
|
||||
}
|
||||
|
||||
// 2. 读取文件内容
|
||||
let content = match std::fs::read_to_string(&path) {
|
||||
let content = match fs::read_to_string(path).await {
|
||||
Ok(content) => content,
|
||||
Err(err) => {
|
||||
logging!(
|
||||
@@ -115,11 +112,11 @@ impl CoreConfigValidator {
|
||||
}
|
||||
|
||||
/// 只进行文件语法检查,不进行完整验证
|
||||
fn validate_file_syntax(config_path: &str) -> Result<(bool, String)> {
|
||||
async fn validate_file_syntax(config_path: &str) -> Result<(bool, String)> {
|
||||
logging!(info, Type::Validate, "开始检查文件: {}", config_path);
|
||||
|
||||
// 读取文件内容
|
||||
let content = match std::fs::read_to_string(config_path) {
|
||||
let content = match fs::read_to_string(config_path).await {
|
||||
Ok(content) => content,
|
||||
Err(err) => {
|
||||
let error_msg = format!("Failed to read file: {err}").into();
|
||||
@@ -144,9 +141,9 @@ impl CoreConfigValidator {
|
||||
}
|
||||
|
||||
/// 验证脚本文件语法
|
||||
fn validate_script_file(path: &str) -> Result<(bool, String)> {
|
||||
async fn validate_script_file(path: &str) -> Result<(bool, String)> {
|
||||
// 读取脚本内容
|
||||
let content = match std::fs::read_to_string(path) {
|
||||
let content = match fs::read_to_string(path).await {
|
||||
Ok(content) => content,
|
||||
Err(err) => {
|
||||
let error_msg = format!("Failed to read script file: {err}").into();
|
||||
@@ -216,14 +213,14 @@ impl CoreConfigValidator {
|
||||
"检测到Merge文件,仅进行语法检查: {}",
|
||||
config_path
|
||||
);
|
||||
return Self::validate_file_syntax(config_path);
|
||||
return Self::validate_file_syntax(config_path).await;
|
||||
}
|
||||
|
||||
// 检查是否为脚本文件
|
||||
let is_script = if config_path.ends_with(".js") {
|
||||
true
|
||||
} else {
|
||||
match Self::is_script_file(config_path) {
|
||||
match Self::is_script_file(config_path).await {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
// 如果无法确定文件类型,尝试使用Clash内核验证
|
||||
@@ -246,7 +243,7 @@ impl CoreConfigValidator {
|
||||
"检测到脚本文件,使用JavaScript验证: {}",
|
||||
config_path
|
||||
);
|
||||
return Self::validate_script_file(config_path);
|
||||
return Self::validate_script_file(config_path).await;
|
||||
}
|
||||
|
||||
// 对YAML配置文件使用Clash内核验证
|
||||
|
||||
Reference in New Issue
Block a user