mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-04 20:07:32 +08:00
chore: git hooks for linter and formatter
This commit is contained in:
parent
124934b012
commit
b57c6e408a
@ -1 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
pnpm pretty-quick --staged
|
||||
|
||||
# 运行 clippy fmt
|
||||
cargo fmt --manifest-path ./src-tauri/Cargo.toml
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "rustfmt failed to format the code. Please fix the issues and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git add .
|
||||
|
||||
# 允许提交
|
||||
exit 0
|
||||
|
13
.husky/pre-push
Normal file
13
.husky/pre-push
Normal file
@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 运行 clippy
|
||||
cargo clippy --manifest-path ./src-tauri/Cargo.toml --fix
|
||||
|
||||
# 如果 clippy 失败,阻止 push
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Clippy found issues in sub_crate. Please fix them before pushing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 允许 push
|
||||
exit 0
|
1
src-tauri/.clippy.toml
Normal file
1
src-tauri/.clippy.toml
Normal file
@ -0,0 +1 @@
|
||||
avoid-breaking-exported-api = true
|
@ -75,40 +75,41 @@ pub fn get_app_dir() -> CmdResult<String> {
|
||||
pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String> {
|
||||
let icon_cache_dir = wrap_err!(dirs::app_home_dir())?.join("icons").join("cache");
|
||||
let icon_path = icon_cache_dir.join(&name);
|
||||
|
||||
|
||||
// 如果文件已存在,直接返回路径
|
||||
if icon_path.exists() {
|
||||
return Ok(icon_path.to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
|
||||
// 确保缓存目录存在
|
||||
if !icon_cache_dir.exists() {
|
||||
let _ = std::fs::create_dir_all(&icon_cache_dir);
|
||||
}
|
||||
|
||||
|
||||
// 使用临时文件名来下载
|
||||
let temp_path = icon_cache_dir.join(format!("{}.downloading", &name));
|
||||
|
||||
|
||||
// 下载文件到临时位置
|
||||
let response = wrap_err!(reqwest::get(&url).await)?;
|
||||
|
||||
|
||||
// 检查内容类型是否为图片
|
||||
let content_type = response.headers()
|
||||
let content_type = response
|
||||
.headers()
|
||||
.get(reqwest::header::CONTENT_TYPE)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("");
|
||||
|
||||
|
||||
let is_image = content_type.starts_with("image/");
|
||||
|
||||
|
||||
// 获取响应内容
|
||||
let content = wrap_err!(response.bytes().await)?;
|
||||
|
||||
|
||||
// 检查内容是否为HTML (针对CDN错误页面)
|
||||
let is_html = content.len() > 15 &&
|
||||
(content.starts_with(b"<!DOCTYPE html") ||
|
||||
content.starts_with(b"<html") ||
|
||||
content.starts_with(b"<?xml"));
|
||||
|
||||
let is_html = content.len() > 15
|
||||
&& (content.starts_with(b"<!DOCTYPE html")
|
||||
|| content.starts_with(b"<html")
|
||||
|| content.starts_with(b"<?xml"));
|
||||
|
||||
// 只有当内容确实是图片时才保存
|
||||
if is_image && !is_html {
|
||||
{
|
||||
@ -122,14 +123,14 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String>
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?;
|
||||
}
|
||||
|
||||
|
||||
// 再次检查目标文件是否已存在,避免重命名覆盖其他线程已完成的文件
|
||||
if !icon_path.exists() {
|
||||
match std::fs::rename(&temp_path, &icon_path) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
let _ = std::fs::remove_file(&temp_path);
|
||||
if icon_path.exists() {
|
||||
@ -140,11 +141,11 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String>
|
||||
} else {
|
||||
let _ = std::fs::remove_file(&temp_path);
|
||||
}
|
||||
|
||||
|
||||
Ok(icon_path.to_string_lossy().to_string())
|
||||
} else {
|
||||
let _ = std::fs::remove_file(&temp_path);
|
||||
Err(format!("下载的内容不是有效图片: {}", url).into())
|
||||
Err(format!("下载的内容不是有效图片: {}", url))
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,8 +159,7 @@ pub struct IconInfo {
|
||||
/// 复制图标文件
|
||||
#[tauri::command]
|
||||
pub fn copy_icon_file(path: String, icon_info: IconInfo) -> CmdResult<String> {
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
let file_path = Path::new(&path);
|
||||
|
||||
|
@ -24,7 +24,8 @@ pub async fn patch_clash_config(payload: Mapping) -> CmdResult {
|
||||
/// 修改Clash模式
|
||||
#[tauri::command]
|
||||
pub async fn patch_clash_mode(payload: String) -> CmdResult {
|
||||
Ok(feat::change_clash_mode(payload))
|
||||
feat::change_clash_mode(payload);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 切换Clash核心
|
||||
@ -98,9 +99,11 @@ pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
|
||||
/// 应用或撤销DNS配置
|
||||
#[tauri::command]
|
||||
pub fn apply_dns_config(apply: bool) -> CmdResult {
|
||||
use crate::config::Config;
|
||||
use crate::core::{handle, CoreManager};
|
||||
use crate::utils::dirs;
|
||||
use crate::{
|
||||
config::Config,
|
||||
core::{handle, CoreManager},
|
||||
utils::dirs,
|
||||
};
|
||||
use tauri::async_runtime;
|
||||
|
||||
// 使用spawn来处理异步操作
|
||||
|
@ -4,29 +4,29 @@ use anyhow::Result;
|
||||
pub type CmdResult<T = ()> = Result<T, String>;
|
||||
|
||||
// Command modules
|
||||
pub mod profile;
|
||||
pub mod validate;
|
||||
pub mod uwp;
|
||||
pub mod webdav;
|
||||
pub mod app;
|
||||
pub mod network;
|
||||
pub mod clash;
|
||||
pub mod verge;
|
||||
pub mod network;
|
||||
pub mod profile;
|
||||
pub mod proxy;
|
||||
pub mod runtime;
|
||||
pub mod save_profile;
|
||||
pub mod system;
|
||||
pub mod proxy;
|
||||
pub mod uwp;
|
||||
pub mod validate;
|
||||
pub mod verge;
|
||||
pub mod webdav;
|
||||
|
||||
// Re-export all command functions for backwards compatibility
|
||||
pub use profile::*;
|
||||
pub use validate::*;
|
||||
pub use uwp::*;
|
||||
pub use webdav::*;
|
||||
pub use app::*;
|
||||
pub use network::*;
|
||||
pub use clash::*;
|
||||
pub use verge::*;
|
||||
pub use network::*;
|
||||
pub use profile::*;
|
||||
pub use proxy::*;
|
||||
pub use runtime::*;
|
||||
pub use save_profile::*;
|
||||
pub use system::*;
|
||||
pub use proxy::*;
|
||||
pub use uwp::*;
|
||||
pub use validate::*;
|
||||
pub use verge::*;
|
||||
pub use webdav::*;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::wrap_err;
|
||||
use super::CmdResult;
|
||||
use sysproxy::{Autoproxy, Sysproxy};
|
||||
use serde_yaml::Mapping;
|
||||
use crate::wrap_err;
|
||||
use network_interface::NetworkInterface;
|
||||
use serde_yaml::Mapping;
|
||||
use sysproxy::{Autoproxy, Sysproxy};
|
||||
|
||||
/// get the system proxy
|
||||
#[tauri::command]
|
||||
@ -46,8 +46,7 @@ pub fn get_network_interfaces() -> Vec<String> {
|
||||
/// 获取网络接口详细信息
|
||||
#[tauri::command]
|
||||
pub fn get_network_interfaces_info() -> CmdResult<Vec<NetworkInterface>> {
|
||||
use network_interface::NetworkInterface;
|
||||
use network_interface::NetworkInterfaceConfig;
|
||||
use network_interface::{NetworkInterface, NetworkInterfaceConfig};
|
||||
|
||||
let names = get_network_interfaces();
|
||||
let interfaces = wrap_err!(NetworkInterface::show())?;
|
||||
|
@ -1,11 +1,11 @@
|
||||
use super::CmdResult;
|
||||
use crate::{
|
||||
config::*,
|
||||
core::*,
|
||||
feat,
|
||||
feat, log_err, ret_err,
|
||||
utils::{dirs, help},
|
||||
log_err, ret_err, wrap_err,
|
||||
wrap_err,
|
||||
};
|
||||
use super::CmdResult;
|
||||
|
||||
/// 获取配置文件列表
|
||||
#[tauri::command]
|
||||
@ -31,7 +31,7 @@ pub async fn enhance_profiles() -> CmdResult {
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[enhance_profiles] 更新过程发生错误: {}", e);
|
||||
handle::Handle::notice_message("config_validate::process_terminated", &e.to_string());
|
||||
handle::Handle::notice_message("config_validate::process_terminated", e.to_string());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -76,19 +76,17 @@ pub async fn delete_profile(index: String) -> CmdResult {
|
||||
|
||||
/// 修改profiles的配置
|
||||
#[tauri::command]
|
||||
pub async fn patch_profiles_config(
|
||||
profiles: IProfiles
|
||||
) -> CmdResult<bool> {
|
||||
pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
|
||||
println!("[cmd配置patch] 开始修改配置文件");
|
||||
|
||||
|
||||
// 保存当前配置,以便在验证失败时恢复
|
||||
let current_profile = Config::profiles().latest().current.clone();
|
||||
println!("[cmd配置patch] 当前配置: {:?}", current_profile);
|
||||
|
||||
|
||||
// 更新profiles配置
|
||||
println!("[cmd配置patch] 正在更新配置草稿");
|
||||
wrap_err!({ Config::profiles().draft().patch_config(profiles) })?;
|
||||
|
||||
|
||||
// 更新配置并进行验证
|
||||
match CoreManager::global().update_config().await {
|
||||
Ok((true, _)) => {
|
||||
@ -102,7 +100,7 @@ pub async fn patch_profiles_config(
|
||||
Ok((false, error_msg)) => {
|
||||
println!("[cmd配置patch] 配置验证失败: {}", error_msg);
|
||||
Config::profiles().discard();
|
||||
|
||||
|
||||
// 如果验证失败,恢复到之前的配置
|
||||
if let Some(prev_profile) = current_profile {
|
||||
println!("[cmd配置patch] 尝试恢复到之前的配置: {}", prev_profile);
|
||||
@ -124,7 +122,7 @@ pub async fn patch_profiles_config(
|
||||
Err(e) => {
|
||||
println!("[cmd配置patch] 更新过程发生错误: {}", e);
|
||||
Config::profiles().discard();
|
||||
handle::Handle::notice_message("config_validate::boot_error", &e.to_string());
|
||||
handle::Handle::notice_message("config_validate::boot_error", e.to_string());
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
@ -134,9 +132,12 @@ pub async fn patch_profiles_config(
|
||||
#[tauri::command]
|
||||
pub async fn patch_profiles_config_by_profile_index(
|
||||
_app_handle: tauri::AppHandle,
|
||||
profile_index: String
|
||||
profile_index: String,
|
||||
) -> CmdResult<bool> {
|
||||
let profiles = IProfiles{current: Some(profile_index), items: None};
|
||||
let profiles = IProfiles {
|
||||
current: Some(profile_index),
|
||||
items: None,
|
||||
};
|
||||
patch_profiles_config(profiles).await
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
use crate::{
|
||||
config::*,
|
||||
wrap_err,
|
||||
};
|
||||
use super::CmdResult;
|
||||
use crate::{config::*, wrap_err};
|
||||
use anyhow::Context;
|
||||
use std::collections::HashMap;
|
||||
use serde_yaml::Mapping;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 获取运行时配置
|
||||
#[tauri::command]
|
||||
|
@ -1,10 +1,5 @@
|
||||
use crate::{
|
||||
config::*,
|
||||
core::*,
|
||||
utils::dirs,
|
||||
wrap_err,
|
||||
};
|
||||
use super::CmdResult;
|
||||
use crate::{config::*, core::*, utils::dirs, wrap_err};
|
||||
use std::fs;
|
||||
|
||||
/// 保存profiles的配置
|
||||
@ -20,7 +15,7 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
||||
let profiles_guard = profiles.latest();
|
||||
let item = wrap_err!(profiles_guard.get_item(&index))?;
|
||||
// 确定是否为merge类型文件
|
||||
let is_merge = item.itype.as_ref().map_or(false, |t| t == "merge");
|
||||
let is_merge = item.itype.as_ref().is_some_and(|t| t == "merge");
|
||||
let content = wrap_err!(item.read_file())?;
|
||||
let path = item.file.clone().ok_or("file field is null")?;
|
||||
let profiles_dir = wrap_err!(dirs::app_profiles_dir())?;
|
||||
@ -29,14 +24,20 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
||||
|
||||
// 保存新的配置文件
|
||||
wrap_err!(fs::write(&file_path, file_data.clone().unwrap()))?;
|
||||
|
||||
|
||||
let file_path_str = file_path.to_string_lossy().to_string();
|
||||
println!("[cmd配置save] 开始验证配置文件: {}, 是否为merge文件: {}", file_path_str, is_merge_file);
|
||||
|
||||
println!(
|
||||
"[cmd配置save] 开始验证配置文件: {}, 是否为merge文件: {}",
|
||||
file_path_str, is_merge_file
|
||||
);
|
||||
|
||||
// 对于 merge 文件,只进行语法验证,不进行后续内核验证
|
||||
if is_merge_file {
|
||||
println!("[cmd配置save] 检测到merge文件,只进行语法验证");
|
||||
match CoreManager::global().validate_config_file(&file_path_str, Some(true)).await {
|
||||
match CoreManager::global()
|
||||
.validate_config_file(&file_path_str, Some(true))
|
||||
.await
|
||||
{
|
||||
Ok((true, _)) => {
|
||||
println!("[cmd配置save] merge文件语法验证通过");
|
||||
// 成功后尝试更新整体配置
|
||||
@ -63,9 +64,12 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 非merge文件使用完整验证流程
|
||||
match CoreManager::global().validate_config_file(&file_path_str, None).await {
|
||||
match CoreManager::global()
|
||||
.validate_config_file(&file_path_str, None)
|
||||
.await
|
||||
{
|
||||
Ok((true, _)) => {
|
||||
println!("[cmd配置save] 验证成功");
|
||||
Ok(())
|
||||
@ -74,16 +78,17 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
||||
println!("[cmd配置save] 验证失败: {}", error_msg);
|
||||
// 恢复原始配置文件
|
||||
wrap_err!(fs::write(&file_path, original_content))?;
|
||||
|
||||
|
||||
// 智能判断错误类型
|
||||
let is_script_error = file_path_str.ends_with(".js") ||
|
||||
error_msg.contains("Script syntax error") ||
|
||||
error_msg.contains("Script must contain a main function") ||
|
||||
error_msg.contains("Failed to read script file");
|
||||
|
||||
if error_msg.contains("YAML syntax error") ||
|
||||
error_msg.contains("Failed to read file:") ||
|
||||
(!file_path_str.ends_with(".js") && !is_script_error) {
|
||||
let is_script_error = file_path_str.ends_with(".js")
|
||||
|| error_msg.contains("Script syntax error")
|
||||
|| error_msg.contains("Script must contain a main function")
|
||||
|| error_msg.contains("Failed to read script file");
|
||||
|
||||
if error_msg.contains("YAML syntax error")
|
||||
|| error_msg.contains("Failed to read file:")
|
||||
|| (!file_path_str.ends_with(".js") && !is_script_error)
|
||||
{
|
||||
// 普通YAML错误使用YAML通知处理
|
||||
println!("[cmd配置save] YAML配置文件验证失败,发送通知");
|
||||
let result = (false, error_msg.clone());
|
||||
@ -98,7 +103,7 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
||||
println!("[cmd配置save] 其他类型验证失败,发送一般通知");
|
||||
handle::Handle::notice_message("config_validate::error", &error_msg);
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -1,8 +1,10 @@
|
||||
use super::CmdResult;
|
||||
use crate::core::handle;
|
||||
use crate::module::sysinfo::PlatformSpecification;
|
||||
use crate::{
|
||||
core::{self, handle, service, CoreManager},
|
||||
module::sysinfo::PlatformSpecification,
|
||||
wrap_err,
|
||||
};
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
use crate::{core::{self, CoreManager, service}, wrap_err};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn export_diagnostic_info() -> CmdResult<()> {
|
||||
@ -11,8 +13,7 @@ pub async fn export_diagnostic_info() -> CmdResult<()> {
|
||||
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
let cliboard = app_handle.clipboard();
|
||||
|
||||
if let Err(_) = cliboard.write_text(info) {
|
||||
if cliboard.write_text(info).is_err() {
|
||||
log::error!(target: "app", "Failed to write to clipboard");
|
||||
}
|
||||
Ok(())
|
||||
|
@ -4,8 +4,7 @@ use super::CmdResult;
|
||||
#[cfg(windows)]
|
||||
mod platform {
|
||||
use super::CmdResult;
|
||||
use crate::core::win_uwp;
|
||||
use crate::wrap_err;
|
||||
use crate::{core::win_uwp, wrap_err};
|
||||
|
||||
pub async fn invoke_uwp_tool() -> CmdResult {
|
||||
wrap_err!(win_uwp::invoke_uwptools().await)
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::core::*;
|
||||
use super::CmdResult;
|
||||
use crate::core::*;
|
||||
|
||||
/// 发送脚本验证通知消息
|
||||
#[tauri::command]
|
||||
@ -13,7 +13,7 @@ pub async fn script_validate_notice(status: String, msg: String) -> CmdResult {
|
||||
pub fn handle_script_validation_notice(result: &(bool, String), file_type: &str) {
|
||||
if !result.0 {
|
||||
let error_msg = &result.1;
|
||||
|
||||
|
||||
// 根据错误消息内容判断错误类型
|
||||
let status = if error_msg.starts_with("File not found:") {
|
||||
"config_validate::file_not_found"
|
||||
@ -27,7 +27,7 @@ pub fn handle_script_validation_notice(result: &(bool, String), file_type: &str)
|
||||
// 如果是其他类型错误,作为一般脚本错误处理
|
||||
"config_validate::script_error"
|
||||
};
|
||||
|
||||
|
||||
log::warn!(target: "app", "{} 验证失败: {}", file_type, error_msg);
|
||||
handle::Handle::notice_message(status, error_msg);
|
||||
}
|
||||
@ -37,12 +37,15 @@ pub fn handle_script_validation_notice(result: &(bool, String), file_type: &str)
|
||||
#[tauri::command]
|
||||
pub async fn validate_script_file(file_path: String) -> CmdResult<bool> {
|
||||
log::info!(target: "app", "验证脚本文件: {}", file_path);
|
||||
|
||||
match CoreManager::global().validate_config_file(&file_path, None).await {
|
||||
|
||||
match CoreManager::global()
|
||||
.validate_config_file(&file_path, None)
|
||||
.await
|
||||
{
|
||||
Ok(result) => {
|
||||
handle_script_validation_notice(&result, "脚本文件");
|
||||
Ok(result.0) // 返回验证结果布尔值
|
||||
},
|
||||
Ok(result.0) // 返回验证结果布尔值
|
||||
}
|
||||
Err(e) => {
|
||||
let error_msg = e.to_string();
|
||||
log::error!(target: "app", "验证脚本文件过程发生错误: {}", error_msg);
|
||||
@ -58,10 +61,10 @@ pub fn handle_yaml_validation_notice(result: &(bool, String), file_type: &str) {
|
||||
if !result.0 {
|
||||
let error_msg = &result.1;
|
||||
println!("[通知] 处理{}验证错误: {}", file_type, error_msg);
|
||||
|
||||
|
||||
// 检查是否为merge文件
|
||||
let is_merge_file = file_type.contains("合并");
|
||||
|
||||
|
||||
// 根据错误消息内容判断错误类型
|
||||
let status = if error_msg.starts_with("File not found:") {
|
||||
"config_validate::file_not_found"
|
||||
@ -93,7 +96,7 @@ pub fn handle_yaml_validation_notice(result: &(bool, String), file_type: &str) {
|
||||
"config_validate::yaml_error"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
log::warn!(target: "app", "{} 验证失败: {}", file_type, error_msg);
|
||||
println!("[通知] 发送通知: status={}, msg={}", status, error_msg);
|
||||
handle::Handle::notice_message(status, error_msg);
|
||||
|
@ -1,9 +1,5 @@
|
||||
use crate::{
|
||||
config::*,
|
||||
feat,
|
||||
wrap_err,
|
||||
};
|
||||
use super::CmdResult;
|
||||
use crate::{config::*, feat, wrap_err};
|
||||
|
||||
/// 获取Verge配置
|
||||
#[tauri::command]
|
||||
|
@ -1,10 +1,5 @@
|
||||
use crate::{
|
||||
core,
|
||||
config::*,
|
||||
feat,
|
||||
wrap_err,
|
||||
};
|
||||
use super::CmdResult;
|
||||
use crate::{config::*, core, feat, wrap_err};
|
||||
use reqwest_dav::list_cmd::ListFile;
|
||||
|
||||
/// 保存 WebDAV 配置
|
||||
|
@ -1,9 +1,9 @@
|
||||
use super::{Draft, IClashTemp, IProfiles, IRuntime, IVerge};
|
||||
use crate::{
|
||||
config::PrfItem,
|
||||
core::{handle, CoreManager},
|
||||
enhance,
|
||||
utils::{dirs, help},
|
||||
core::{handle, CoreManager},
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
@ -73,14 +73,17 @@ impl Config {
|
||||
// 生成运行时配置文件并验证
|
||||
let config_result = Self::generate_file(ConfigType::Run);
|
||||
|
||||
let validation_result = if let Ok(_) = config_result {
|
||||
let validation_result = if config_result.is_ok() {
|
||||
// 验证配置文件
|
||||
println!("[首次启动] 开始验证配置");
|
||||
|
||||
|
||||
match CoreManager::global().validate_config().await {
|
||||
Ok((is_valid, error_msg)) => {
|
||||
if !is_valid {
|
||||
println!("[首次启动] 配置验证失败,使用默认最小配置启动: {}", error_msg);
|
||||
println!(
|
||||
"[首次启动] 配置验证失败,使用默认最小配置启动: {}",
|
||||
error_msg
|
||||
);
|
||||
CoreManager::global()
|
||||
.use_default_config("config_validate::boot_error", &error_msg)
|
||||
.await?;
|
||||
@ -101,10 +104,7 @@ impl Config {
|
||||
} else {
|
||||
println!("[首次启动] 生成配置文件失败,使用默认配置");
|
||||
CoreManager::global()
|
||||
.use_default_config(
|
||||
"config_validate::error",
|
||||
"",
|
||||
)
|
||||
.use_default_config("config_validate::error", "")
|
||||
.await?;
|
||||
Some(("config_validate::error", String::new()))
|
||||
};
|
||||
|
@ -8,14 +8,9 @@ mod profiles;
|
||||
mod runtime;
|
||||
mod verge;
|
||||
|
||||
pub use self::clash::*;
|
||||
pub use self::config::*;
|
||||
pub use self::draft::*;
|
||||
pub use self::encrypt::*;
|
||||
pub use self::prfitem::*;
|
||||
pub use self::profiles::*;
|
||||
pub use self::runtime::*;
|
||||
pub use self::verge::*;
|
||||
pub use self::{
|
||||
clash::*, config::*, draft::*, encrypt::*, prfitem::*, profiles::*, runtime::*, verge::*,
|
||||
};
|
||||
|
||||
pub const DEFAULT_PAC: &str = r#"function FindProxyForURL(url, host) {
|
||||
return "PROXY 127.0.0.1:%mixed-port%; SOCKS5 127.0.0.1:%mixed-port%; DIRECT;";
|
||||
|
@ -234,10 +234,10 @@ impl PrfItem {
|
||||
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 with_proxy = opt_ref.is_some_and(|o| o.with_proxy.unwrap_or(false));
|
||||
let self_proxy = opt_ref.is_some_and(|o| o.self_proxy.unwrap_or(false));
|
||||
let accept_invalid_certs =
|
||||
opt_ref.map_or(false, |o| o.danger_accept_invalid_certs.unwrap_or(false));
|
||||
opt_ref.is_some_and(|o| o.danger_accept_invalid_certs.unwrap_or(false));
|
||||
let user_agent = opt_ref.and_then(|o| o.user_agent.clone());
|
||||
let update_interval = opt_ref.and_then(|o| o.update_interval);
|
||||
let mut merge = opt_ref.and_then(|o| o.merge.clone());
|
||||
|
@ -472,15 +472,17 @@ impl IProfiles {
|
||||
|
||||
/// 获取所有的profiles(uid,名称)
|
||||
pub fn all_profile_uid_and_name(&self) -> Option<Vec<(String, String)>> {
|
||||
match self.items.as_ref() {
|
||||
Some(items) => Some(items.iter().filter_map(|e| {
|
||||
if let (Some(uid), Some(name)) = (e.uid.clone(), e.name.clone()) {
|
||||
Some((uid, name))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect()),
|
||||
None => None,
|
||||
}
|
||||
self.items.as_ref().map(|items| {
|
||||
items
|
||||
.iter()
|
||||
.filter_map(|e| {
|
||||
if let (Some(uid), Some(name)) = (e.uid.clone(), e.name.clone()) {
|
||||
Some((uid, name))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::config::DEFAULT_PAC;
|
||||
use crate::config::{deserialize_encrypted, serialize_encrypted};
|
||||
use crate::utils::i18n;
|
||||
use crate::utils::{dirs, help};
|
||||
use crate::{
|
||||
config::{deserialize_encrypted, serialize_encrypted, DEFAULT_PAC},
|
||||
utils::{dirs, help, i18n},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use log::LevelFilter;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -101,7 +101,7 @@ pub struct IVerge {
|
||||
/// hotkey map
|
||||
/// format: {func},{key}
|
||||
pub hotkeys: Option<Vec<String>>,
|
||||
|
||||
|
||||
/// enable global hotkey
|
||||
pub enable_global_hotkey: Option<bool>,
|
||||
|
||||
|
@ -1,16 +1,17 @@
|
||||
use crate::config::Config;
|
||||
use crate::utils::dirs;
|
||||
use crate::{config::Config, utils::dirs};
|
||||
use anyhow::Error;
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use reqwest_dav::list_cmd::{ListEntity, ListFile};
|
||||
use std::collections::HashMap;
|
||||
use std::env::{consts::OS, temp_dir};
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env::{consts::OS, temp_dir},
|
||||
fs,
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::time::timeout;
|
||||
use zip::write::SimpleFileOptions;
|
||||
|
||||
|
@ -1,16 +1,17 @@
|
||||
use crate::config::*;
|
||||
#[cfg(target_os = "macos")]
|
||||
use crate::core::tray::Tray;
|
||||
use crate::core::{handle, service};
|
||||
use crate::log_err;
|
||||
use crate::module::mihomo::MihomoManager;
|
||||
use crate::utils::{dirs, help};
|
||||
use crate::{
|
||||
config::*,
|
||||
core::{handle, service},
|
||||
log_err,
|
||||
module::mihomo::MihomoManager,
|
||||
utils::{dirs, help},
|
||||
};
|
||||
use anyhow::{bail, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::time::sleep;
|
||||
use tokio::{sync::Mutex, time::sleep};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CoreManager {
|
||||
|
@ -1,13 +1,10 @@
|
||||
use crate::core::handle;
|
||||
use crate::{config::Config, feat, log_err};
|
||||
use crate::utils::resolve;
|
||||
use crate::{config::Config, core::handle, feat, log_err, utils::resolve};
|
||||
use anyhow::{bail, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tauri::Manager;
|
||||
use tauri::{async_runtime, Manager};
|
||||
use tauri_plugin_global_shortcut::{Code, GlobalShortcutExt, ShortcutState};
|
||||
use tauri::async_runtime;
|
||||
|
||||
pub struct Hotkey {
|
||||
current: Arc<Mutex<Vec<String>>>, // 保存当前的热键设置
|
||||
@ -26,7 +23,10 @@ impl Hotkey {
|
||||
let verge = Config::verge();
|
||||
let enable_global_hotkey = verge.latest().enable_global_hotkey.unwrap_or(true);
|
||||
|
||||
println!("Initializing hotkeys, global hotkey enabled: {}", enable_global_hotkey);
|
||||
println!(
|
||||
"Initializing hotkeys, global hotkey enabled: {}",
|
||||
enable_global_hotkey
|
||||
);
|
||||
log::info!(target: "app", "Initializing hotkeys, global hotkey enabled: {}", enable_global_hotkey);
|
||||
|
||||
// 如果全局热键被禁用,则不注册热键
|
||||
@ -85,11 +85,17 @@ impl Hotkey {
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
let manager = app_handle.global_shortcut();
|
||||
|
||||
println!("Attempting to register hotkey: {} for function: {}", hotkey, func);
|
||||
println!(
|
||||
"Attempting to register hotkey: {} for function: {}",
|
||||
hotkey, func
|
||||
);
|
||||
log::info!(target: "app", "Attempting to register hotkey: {} for function: {}", hotkey, func);
|
||||
|
||||
if manager.is_registered(hotkey) {
|
||||
println!("Hotkey {} was already registered, unregistering first", hotkey);
|
||||
println!(
|
||||
"Hotkey {} was already registered, unregistering first",
|
||||
hotkey
|
||||
);
|
||||
log::info!(target: "app", "Hotkey {} was already registered, unregistering first", hotkey);
|
||||
manager.unregister(hotkey)?;
|
||||
}
|
||||
@ -101,12 +107,12 @@ impl Hotkey {
|
||||
|| {
|
||||
println!("=== Hotkey Dashboard Window Operation Start ===");
|
||||
log::info!(target: "app", "=== Hotkey Dashboard Window Operation Start ===");
|
||||
|
||||
|
||||
// 使用 spawn_blocking 来确保在正确的线程上执行
|
||||
async_runtime::spawn_blocking(|| {
|
||||
println!("Toggle dashboard window visibility");
|
||||
log::info!(target: "app", "Toggle dashboard window visibility");
|
||||
|
||||
|
||||
// 检查窗口是否存在
|
||||
if let Some(window) = handle::Handle::global().get_window() {
|
||||
// 如果窗口可见,则隐藏它
|
||||
@ -131,11 +137,11 @@ impl Hotkey {
|
||||
resolve::create_window();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
println!("=== Hotkey Dashboard Window Operation End ===");
|
||||
log::info!(target: "app", "=== Hotkey Dashboard Window Operation End ===");
|
||||
}
|
||||
},
|
||||
}
|
||||
"clash_mode_rule" => || feat::change_clash_mode("rule".into()),
|
||||
"clash_mode_global" => || feat::change_clash_mode("global".into()),
|
||||
"clash_mode_direct" => || feat::change_clash_mode("direct".into()),
|
||||
@ -169,11 +175,14 @@ impl Hotkey {
|
||||
// 直接执行函数,不做任何状态检查
|
||||
println!("Executing function directly");
|
||||
log::info!(target: "app", "Executing function directly");
|
||||
|
||||
|
||||
// 获取轻量模式状态和全局热键状态
|
||||
let is_lite_mode = Config::verge().latest().enable_lite_mode.unwrap_or(false);
|
||||
let is_enable_global_hotkey = Config::verge().latest().enable_global_hotkey.unwrap_or(true);
|
||||
|
||||
let is_enable_global_hotkey = Config::verge()
|
||||
.latest()
|
||||
.enable_global_hotkey
|
||||
.unwrap_or(true);
|
||||
|
||||
// 在轻量模式下或配置了全局热键时,始终执行热键功能
|
||||
if is_lite_mode || is_enable_global_hotkey {
|
||||
f();
|
||||
@ -181,7 +190,7 @@ impl Hotkey {
|
||||
// 非轻量模式且未启用全局热键时,只在窗口可见且有焦点的情况下响应热键
|
||||
let is_visible = window.is_visible().unwrap_or(false);
|
||||
let is_focused = window.is_focused().unwrap_or(false);
|
||||
|
||||
|
||||
if is_focused && is_visible {
|
||||
f();
|
||||
}
|
||||
|
@ -1,10 +1,7 @@
|
||||
use crate::config::Config;
|
||||
use crate::utils::dirs;
|
||||
use crate::{config::Config, utils::dirs};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::{env::current_exe, process::Command as StdCommand};
|
||||
use std::{collections::HashMap, env::current_exe, path::PathBuf, process::Command as StdCommand};
|
||||
use tokio::time::Duration;
|
||||
|
||||
// Windows only
|
||||
@ -154,11 +151,13 @@ pub async fn reinstall_service() -> Result<()> {
|
||||
|
||||
let install_shell: String = install_path.to_string_lossy().into_owned();
|
||||
let uninstall_shell: String = uninstall_path.to_string_lossy().into_owned();
|
||||
|
||||
|
||||
// 获取提示文本,如果 i18n 失败则使用硬编码默认值
|
||||
let prompt = crate::utils::i18n::t("Service Administrator Prompt");
|
||||
let prompt = if prompt == "Service Administrator Prompt" {
|
||||
if Config::verge().latest().language.as_deref() == Some("zh") || Config::verge().latest().language.is_none() {
|
||||
if Config::verge().latest().language.as_deref() == Some("zh")
|
||||
|| Config::verge().latest().language.is_none()
|
||||
{
|
||||
"Clash Verge 需要使用管理员权限来重新安装系统服务"
|
||||
} else {
|
||||
"Clash Verge needs administrator privileges to reinstall the system service"
|
||||
@ -166,7 +165,7 @@ pub async fn reinstall_service() -> Result<()> {
|
||||
} else {
|
||||
&prompt
|
||||
};
|
||||
|
||||
|
||||
let command = format!(
|
||||
r#"do shell script "sudo '{uninstall_shell}' && sudo '{install_shell}'" with administrator privileges with prompt "{prompt}""#
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::core::handle::Handle;
|
||||
use crate::{
|
||||
config::{Config, IVerge},
|
||||
core::handle::Handle,
|
||||
log_err,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -126,8 +126,7 @@ impl Sysopt {
|
||||
if !sys_enable {
|
||||
return self.reset_sysproxy().await;
|
||||
}
|
||||
use crate::core::handle::Handle;
|
||||
use crate::utils::dirs;
|
||||
use crate::{core::handle::Handle, utils::dirs};
|
||||
use anyhow::bail;
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
|
||||
@ -185,8 +184,7 @@ impl Sysopt {
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use crate::core::handle::Handle;
|
||||
use crate::utils::dirs;
|
||||
use crate::{core::handle::Handle, utils::dirs};
|
||||
use anyhow::bail;
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
|
||||
@ -305,8 +303,7 @@ impl Sysopt {
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use crate::core::handle::Handle;
|
||||
use crate::utils::dirs;
|
||||
use crate::{core::handle::Handle, utils::dirs};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
|
||||
let app_handle = Handle::global().app_handle().unwrap();
|
||||
|
@ -1,12 +1,9 @@
|
||||
use crate::config::Config;
|
||||
use crate::feat;
|
||||
use crate::core::CoreManager;
|
||||
use crate::{config::Config, core::CoreManager, feat};
|
||||
use anyhow::{Context, Result};
|
||||
use delay_timer::prelude::{DelayTimer, DelayTimerBuilder, TaskBuilder};
|
||||
use once_cell::sync::OnceCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
type TaskID = u64;
|
||||
|
||||
@ -195,16 +192,14 @@ impl Timer {
|
||||
log::info!(target: "app", "Running timer task `{}`", uid);
|
||||
|
||||
match feat::update_profile(uid.clone(), None).await {
|
||||
Ok(_) => {
|
||||
match CoreManager::global().update_config().await {
|
||||
Ok(_) => {
|
||||
log::info!(target: "app", "Timer task completed successfully for uid: {}", uid);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(target: "app", "Timer task refresh error for uid {}: {}", uid, e);
|
||||
}
|
||||
Ok(_) => match CoreManager::global().update_config().await {
|
||||
Ok(_) => {
|
||||
log::info!(target: "app", "Timer task completed successfully for uid: {}", uid);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(target: "app", "Timer task refresh error for uid {}: {}", uid, e);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!(target: "app", "Timer task update error for uid {}: {}", uid, e);
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ impl Tray {
|
||||
let profile_uid_and_name = Config::profiles()
|
||||
.data()
|
||||
.all_profile_uid_and_name()
|
||||
.unwrap_or(Vec::new());
|
||||
.unwrap_or_default();
|
||||
|
||||
let tray = app_handle.tray_by_id("main").unwrap();
|
||||
let _ = tray.set_menu(Some(create_tray_menu(
|
||||
@ -408,8 +408,8 @@ fn create_tray_menu(
|
||||
.is_current_profile_index(profile_uid.to_string());
|
||||
CheckMenuItem::with_id(
|
||||
app_handle,
|
||||
&format!("profiles_{}", profile_uid),
|
||||
t(&profile_name),
|
||||
format!("profiles_{}", profile_uid),
|
||||
t(profile_name),
|
||||
true,
|
||||
is_current_profile,
|
||||
None::<&str>,
|
||||
|
@ -1,16 +1,15 @@
|
||||
use crate::module::mihomo::Rate;
|
||||
use crate::module::mihomo::MihomoManager;
|
||||
use crate::utils::help::format_bytes_speed;
|
||||
use crate::{
|
||||
module::mihomo::{MihomoManager, Rate},
|
||||
utils::help::format_bytes_speed,
|
||||
};
|
||||
use ab_glyph::FontArc;
|
||||
use anyhow::Result;
|
||||
use futures::Stream;
|
||||
use image::{GenericImageView, Rgba, RgbaImage};
|
||||
use imageproc::drawing::draw_text_mut;
|
||||
use parking_lot::Mutex;
|
||||
use std::io::Cursor;
|
||||
use std::sync::Arc;
|
||||
use tokio_tungstenite::tungstenite::http;
|
||||
use tokio_tungstenite::tungstenite::Message;
|
||||
use std::{io::Cursor, sync::Arc};
|
||||
use tokio_tungstenite::tungstenite::{http, Message};
|
||||
use tungstenite::client::IntoClientRequest;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpeedRate {
|
||||
|
@ -15,7 +15,7 @@ impl MihomoManager {
|
||||
proxies: serde_json::Value::Null,
|
||||
providers_proxies: serde_json::Value::Null,
|
||||
})),
|
||||
headers: headers,
|
||||
headers,
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ impl MihomoManager {
|
||||
client_response.text().await.map(|text| json!(text))
|
||||
}
|
||||
.map_err(|e| e.to_string())?;
|
||||
return Ok(response);
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn refresh_proxies(&self) -> Result<&Self, String> {
|
||||
@ -129,6 +129,6 @@ impl MihomoManager {
|
||||
self.mihomo_server, name, test_url, timeout
|
||||
);
|
||||
let response = self.send_request("GET", url, None).await?;
|
||||
return Ok(response);
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
@ -5,17 +5,10 @@ mod script;
|
||||
pub mod seq;
|
||||
mod tun;
|
||||
|
||||
use self::chain::*;
|
||||
use self::field::*;
|
||||
use self::merge::*;
|
||||
use self::script::*;
|
||||
use self::seq::*;
|
||||
use self::tun::*;
|
||||
use crate::config::Config;
|
||||
use crate::utils::tmpl;
|
||||
use self::{chain::*, field::*, merge::*, script::*, seq::*, tun::*};
|
||||
use crate::{config::Config, utils::tmpl};
|
||||
use serde_yaml::Mapping;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
type ResultLog = Vec<(String, String)>;
|
||||
|
||||
@ -267,11 +260,11 @@ pub async fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
|
||||
if enable_dns_settings {
|
||||
use crate::utils::dirs;
|
||||
use std::fs;
|
||||
|
||||
|
||||
// 尝试读取dns_config.yaml
|
||||
if let Ok(app_dir) = dirs::app_home_dir() {
|
||||
let dns_path = app_dir.join("dns_config.yaml");
|
||||
|
||||
|
||||
if dns_path.exists() {
|
||||
if let Ok(dns_yaml) = fs::read_to_string(&dns_path) {
|
||||
if let Ok(dns_config) = serde_yaml::from_str::<serde_yaml::Mapping>(&dns_yaml) {
|
||||
|
@ -108,7 +108,7 @@ proxy-groups:
|
||||
- "proxy1"
|
||||
"#;
|
||||
let mut config: Mapping = serde_yaml::from_str(config_str).unwrap();
|
||||
|
||||
|
||||
let seq = SeqMap {
|
||||
prepend: Sequence::new(),
|
||||
append: Sequence::new(),
|
||||
@ -121,16 +121,32 @@ proxy-groups:
|
||||
let proxies = config.get("proxies").unwrap().as_sequence().unwrap();
|
||||
assert_eq!(proxies.len(), 1);
|
||||
assert_eq!(
|
||||
proxies[0].as_mapping().unwrap().get("name").unwrap().as_str().unwrap(),
|
||||
proxies[0]
|
||||
.as_mapping()
|
||||
.unwrap()
|
||||
.get("name")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap(),
|
||||
"proxy2"
|
||||
);
|
||||
|
||||
// Check if proxy1 is removed from all groups
|
||||
let groups = config.get("proxy-groups").unwrap().as_sequence().unwrap();
|
||||
let group1_proxies = groups[0].as_mapping().unwrap()
|
||||
.get("proxies").unwrap().as_sequence().unwrap();
|
||||
let group2_proxies = groups[1].as_mapping().unwrap()
|
||||
.get("proxies").unwrap().as_sequence().unwrap();
|
||||
let group1_proxies = groups[0]
|
||||
.as_mapping()
|
||||
.unwrap()
|
||||
.get("proxies")
|
||||
.unwrap()
|
||||
.as_sequence()
|
||||
.unwrap();
|
||||
let group2_proxies = groups[1]
|
||||
.as_mapping()
|
||||
.unwrap()
|
||||
.get("proxies")
|
||||
.unwrap()
|
||||
.as_sequence()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(group1_proxies.len(), 1);
|
||||
assert_eq!(group1_proxies[0].as_str().unwrap(), "proxy2");
|
||||
|
@ -24,7 +24,7 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
|
||||
let mut tun_val = tun_val.map_or(Mapping::new(), |val| {
|
||||
val.as_mapping().cloned().unwrap_or(Mapping::new())
|
||||
});
|
||||
|
||||
|
||||
if enable {
|
||||
// 读取DNS配置
|
||||
let dns_key = Value::from("dns");
|
||||
@ -40,20 +40,20 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
|
||||
|
||||
// 检查现有的 enhanced-mode 设置
|
||||
let current_mode = dns_val
|
||||
.get(&Value::from("enhanced-mode"))
|
||||
.get(Value::from("enhanced-mode"))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("fake-ip");
|
||||
|
||||
// 只有当 enhanced-mode 是 fake-ip 或未设置时才修改 DNS 配置
|
||||
if current_mode == "fake-ip" || !dns_val.contains_key(&Value::from("enhanced-mode")) {
|
||||
if current_mode == "fake-ip" || !dns_val.contains_key(Value::from("enhanced-mode")) {
|
||||
revise!(dns_val, "enable", true);
|
||||
revise!(dns_val, "ipv6", ipv6_val);
|
||||
|
||||
if !dns_val.contains_key(&Value::from("enhanced-mode")) {
|
||||
|
||||
if !dns_val.contains_key(Value::from("enhanced-mode")) {
|
||||
revise!(dns_val, "enhanced-mode", "fake-ip");
|
||||
}
|
||||
|
||||
if !dns_val.contains_key(&Value::from("fake-ip-range")) {
|
||||
|
||||
if !dns_val.contains_key(Value::from("fake-ip-range")) {
|
||||
revise!(dns_val, "fake-ip-range", "198.18.0.1/16");
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
|
||||
crate::utils::resolve::set_public_dns("223.6.6.6".to_string()).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 当TUN启用时,将修改后的DNS配置写回
|
||||
revise!(config, "dns", dns_val);
|
||||
} else {
|
||||
@ -75,6 +75,6 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
|
||||
// 更新TUN配置
|
||||
revise!(tun_val, "enable", enable);
|
||||
revise!(config, "tun", tun_val);
|
||||
|
||||
|
||||
config
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
use crate::config::{Config, IVerge};
|
||||
use crate::core::backup;
|
||||
use crate::log_err;
|
||||
use crate::utils::dirs::app_home_dir;
|
||||
use crate::{
|
||||
config::{Config, IVerge},
|
||||
core::backup,
|
||||
log_err,
|
||||
utils::dirs::app_home_dir,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use reqwest_dav::list_cmd::ListFile;
|
||||
use std::fs;
|
||||
|
@ -1,8 +1,10 @@
|
||||
use crate::config::Config;
|
||||
use crate::core::{handle, tray, CoreManager};
|
||||
use crate::log_err;
|
||||
use crate::module::mihomo::MihomoManager;
|
||||
use crate::utils::resolve;
|
||||
use crate::{
|
||||
config::Config,
|
||||
core::{handle, tray, CoreManager},
|
||||
log_err,
|
||||
module::mihomo::MihomoManager,
|
||||
utils::resolve,
|
||||
};
|
||||
use serde_yaml::{Mapping, Value};
|
||||
use tauri::Manager;
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
use crate::config::{Config, IVerge};
|
||||
use crate::core::{handle, hotkey, sysopt, tray, CoreManager};
|
||||
use crate::log_err;
|
||||
use crate::utils::resolve;
|
||||
use crate::{
|
||||
config::{Config, IVerge},
|
||||
core::{handle, hotkey, sysopt, tray, CoreManager},
|
||||
log_err,
|
||||
utils::resolve,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use serde_yaml::Mapping;
|
||||
use tauri::Manager;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::cmd;
|
||||
use crate::config::{Config, PrfItem, PrfOption};
|
||||
use crate::core::handle;
|
||||
use crate::core::CoreManager;
|
||||
use crate::core::*;
|
||||
use crate::{
|
||||
cmd,
|
||||
config::{Config, PrfItem, PrfOption},
|
||||
core::{handle, CoreManager, *},
|
||||
};
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
/// Toggle proxy profile
|
||||
@ -29,7 +29,7 @@ pub async fn update_profile(uid: String, option: Option<PrfOption>) -> Result<()
|
||||
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");
|
||||
let is_remote = item.itype.as_ref().is_some_and(|s| s == "remote");
|
||||
|
||||
if !is_remote {
|
||||
println!("[订阅更新] {} 不是远程订阅,跳过更新", uid);
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::config::Config;
|
||||
use crate::config::IVerge;
|
||||
use crate::core::handle;
|
||||
use crate::{
|
||||
config::{Config, IVerge},
|
||||
core::handle,
|
||||
};
|
||||
use std::env;
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
|
||||
@ -72,10 +73,16 @@ pub fn copy_clash_env() {
|
||||
};
|
||||
|
||||
let export_text = match env_type.as_str() {
|
||||
"bash" => format!("export https_proxy={http_proxy} http_proxy={http_proxy} all_proxy={socks5_proxy}"),
|
||||
"bash" => format!(
|
||||
"export https_proxy={http_proxy} http_proxy={http_proxy} all_proxy={socks5_proxy}"
|
||||
),
|
||||
"cmd" => format!("set http_proxy={http_proxy}\r\nset https_proxy={http_proxy}"),
|
||||
"powershell" => format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\""),
|
||||
"nushell" => format!("load-env {{ http_proxy: \"{http_proxy}\", https_proxy: \"{http_proxy}\" }}"),
|
||||
"powershell" => {
|
||||
format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\"")
|
||||
}
|
||||
"nushell" => {
|
||||
format!("load-env {{ http_proxy: \"{http_proxy}\", https_proxy: \"{http_proxy}\" }}")
|
||||
}
|
||||
"fish" => format!("set -x http_proxy {http_proxy}; set -x https_proxy {http_proxy}"),
|
||||
_ => {
|
||||
log::error!(target: "app", "copy_clash_env: Invalid env type! {env_type}");
|
||||
@ -83,7 +90,7 @@ pub fn copy_clash_env() {
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(_) = cliboard.write_text(export_text) {
|
||||
if cliboard.write_text(export_text).is_err() {
|
||||
log::error!(target: "app", "Failed to write to clipboard");
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::config::Config;
|
||||
use crate::core::handle;
|
||||
use crate::core::{sysopt, CoreManager};
|
||||
use crate::module::mihomo::MihomoManager;
|
||||
use crate::utils::resolve;
|
||||
use futures;
|
||||
use crate::{
|
||||
config::Config,
|
||||
core::{handle, sysopt, CoreManager},
|
||||
module::mihomo::MihomoManager,
|
||||
utils::resolve,
|
||||
};
|
||||
use tauri::Manager;
|
||||
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
|
||||
|
||||
|
@ -3,17 +3,19 @@ mod config;
|
||||
mod core;
|
||||
mod enhance;
|
||||
mod feat;
|
||||
mod utils;
|
||||
mod module;
|
||||
use crate::core::hotkey;
|
||||
use crate::utils::{resolve, resolve::resolve_scheme, server};
|
||||
mod utils;
|
||||
use crate::{
|
||||
core::hotkey,
|
||||
utils::{resolve, resolve::resolve_scheme, server},
|
||||
};
|
||||
use config::Config;
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use std::sync::{Mutex, Once};
|
||||
use tauri::AppHandle;
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::Manager;
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
|
||||
/// A global singleton handle to the application.
|
||||
pub struct AppHandleManager {
|
||||
@ -57,7 +59,7 @@ impl AppHandleManager {
|
||||
let _ = app_handle.set_activation_policy(tauri::ActivationPolicy::Regular);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn set_activation_policy_accessory(&self) {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
@ -66,7 +68,7 @@ impl AppHandleManager {
|
||||
let _ = app_handle.set_activation_policy(tauri::ActivationPolicy::Accessory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn set_activation_policy_prohibited(&self) {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
@ -77,6 +79,7 @@ impl AppHandleManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::panic)]
|
||||
pub fn run() {
|
||||
// 单例检测
|
||||
let app_exists: bool = tauri::async_runtime::block_on(async move {
|
||||
@ -219,12 +222,18 @@ pub fn run() {
|
||||
AppHandleManager::global().init(app_handle.clone());
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let main_window = AppHandleManager::global().get_handle().get_webview_window("main").unwrap();
|
||||
let main_window = AppHandleManager::global()
|
||||
.get_handle()
|
||||
.get_webview_window("main")
|
||||
.unwrap();
|
||||
let _ = main_window.set_title("Clash Verge");
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
tauri::RunEvent::Reopen { has_visible_windows, .. } => {
|
||||
tauri::RunEvent::Reopen {
|
||||
has_visible_windows,
|
||||
..
|
||||
} => {
|
||||
if !has_visible_windows {
|
||||
AppHandleManager::global().set_activation_policy_regular();
|
||||
}
|
||||
@ -259,8 +268,11 @@ pub fn run() {
|
||||
{
|
||||
log_err!(hotkey::Hotkey::global().register("Control+Q", "quit"));
|
||||
};
|
||||
{
|
||||
let is_enable_global_hotkey = Config::verge().latest().enable_global_hotkey.unwrap_or(true);
|
||||
{
|
||||
let is_enable_global_hotkey = Config::verge()
|
||||
.latest()
|
||||
.enable_global_hotkey
|
||||
.unwrap_or(true);
|
||||
if !is_enable_global_hotkey {
|
||||
log_err!(hotkey::Hotkey::global().init())
|
||||
}
|
||||
@ -275,8 +287,11 @@ pub fn run() {
|
||||
{
|
||||
log_err!(hotkey::Hotkey::global().unregister("Control+Q"));
|
||||
};
|
||||
{
|
||||
let is_enable_global_hotkey = Config::verge().latest().enable_global_hotkey.unwrap_or(true);
|
||||
{
|
||||
let is_enable_global_hotkey = Config::verge()
|
||||
.latest()
|
||||
.enable_global_hotkey
|
||||
.unwrap_or(true);
|
||||
if !is_enable_global_hotkey {
|
||||
log_err!(hotkey::Hotkey::global().reset())
|
||||
}
|
||||
|
@ -65,6 +65,6 @@ impl MihomoManager {
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let token = http::header::HeaderValue::from_str(&auth).unwrap();
|
||||
return (ws_url, token);
|
||||
(ws_url, token)
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
pub mod mihomo;
|
||||
pub mod sysinfo;
|
||||
pub mod mihomo;
|
@ -43,7 +43,6 @@ impl PlatformSpecification {
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
Self {
|
||||
system_name,
|
||||
system_version,
|
||||
|
@ -1,8 +1,7 @@
|
||||
use crate::core::handle;
|
||||
use anyhow::Result;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, path::PathBuf};
|
||||
use tauri::Manager;
|
||||
|
||||
#[cfg(not(feature = "verge-dev"))]
|
||||
|
@ -207,7 +207,7 @@ macro_rules! t {
|
||||
/// # Examples
|
||||
/// ```not_run
|
||||
/// format_bytes_speed(1000) // returns "1000B/s"
|
||||
/// format_bytes_speed(1024) // returns "1.0KB/s"
|
||||
/// format_bytes_speed(1024) // returns "1.0KB/s"
|
||||
/// format_bytes_speed(1024 * 1024) // returns "1.0MB/s"
|
||||
/// ```
|
||||
/// ```
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::config::Config;
|
||||
use crate::utils::dirs;
|
||||
use crate::{config::Config, utils::dirs};
|
||||
use once_cell::sync::Lazy;
|
||||
use serde_json::Value;
|
||||
use std::{collections::HashMap, fs, path::PathBuf};
|
||||
|
@ -1,16 +1,21 @@
|
||||
use crate::config::*;
|
||||
use crate::core::handle;
|
||||
use crate::utils::{dirs, help};
|
||||
use crate::{
|
||||
config::*,
|
||||
core::handle,
|
||||
utils::{dirs, help},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use chrono::{Local, TimeZone};
|
||||
use log::LevelFilter;
|
||||
use log4rs::append::console::ConsoleAppender;
|
||||
use log4rs::append::file::FileAppender;
|
||||
use log4rs::config::{Appender, Logger, Root};
|
||||
use log4rs::encode::pattern::PatternEncoder;
|
||||
use std::fs::{self, DirEntry};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use log4rs::{
|
||||
append::{console::ConsoleAppender, file::FileAppender},
|
||||
config::{Appender, Logger, Root},
|
||||
encode::pattern::PatternEncoder,
|
||||
};
|
||||
use std::{
|
||||
fs::{self, DirEntry},
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
|
||||
/// initialize this instance's log file
|
||||
@ -142,69 +147,106 @@ fn init_dns_config() -> Result<()> {
|
||||
("enable".into(), Value::Bool(true)),
|
||||
("listen".into(), Value::String(":53".into())),
|
||||
("enhanced-mode".into(), Value::String("fake-ip".into())),
|
||||
("fake-ip-range".into(), Value::String("198.18.0.1/16".into())),
|
||||
("fake-ip-filter-mode".into(), Value::String("blacklist".into())),
|
||||
(
|
||||
"fake-ip-range".into(),
|
||||
Value::String("198.18.0.1/16".into()),
|
||||
),
|
||||
(
|
||||
"fake-ip-filter-mode".into(),
|
||||
Value::String("blacklist".into()),
|
||||
),
|
||||
("prefer-h3".into(), Value::Bool(false)),
|
||||
("respect-rules".into(), Value::Bool(false)),
|
||||
("use-hosts".into(), Value::Bool(false)),
|
||||
("use-system-hosts".into(), Value::Bool(false)),
|
||||
("fake-ip-filter".into(), Value::Sequence(vec![
|
||||
Value::String("*.lan".into()),
|
||||
Value::String("*.local".into()),
|
||||
Value::String("*.arpa".into()),
|
||||
Value::String("time.*.com".into()),
|
||||
Value::String("ntp.*.com".into()),
|
||||
Value::String("time.*.com".into()),
|
||||
Value::String("+.market.xiaomi.com".into()),
|
||||
Value::String("localhost.ptlogin2.qq.com".into()),
|
||||
Value::String("*.msftncsi.com".into()),
|
||||
Value::String("www.msftconnecttest.com".into()),
|
||||
])),
|
||||
("default-nameserver".into(), Value::Sequence(vec![
|
||||
Value::String("223.6.6.6".into()),
|
||||
Value::String("8.8.8.8".into()),
|
||||
])),
|
||||
("nameserver".into(), Value::Sequence(vec![
|
||||
Value::String("8.8.8.8".into()),
|
||||
Value::String("https://doh.pub/dns-query".into()),
|
||||
Value::String("https://dns.alidns.com/dns-query".into()),
|
||||
])),
|
||||
("fallback".into(), Value::Sequence(vec![
|
||||
Value::String("https://dns.alidns.com/dns-query".into()),
|
||||
Value::String("https://dns.google/dns-query".into()),
|
||||
Value::String("https://cloudflare-dns.com/dns-query".into()),
|
||||
])),
|
||||
("nameserver-policy".into(), Value::Mapping(serde_yaml::Mapping::new())),
|
||||
("proxy-server-nameserver".into(), Value::Sequence(vec![
|
||||
Value::String("https://doh.pub/dns-query".into()),
|
||||
Value::String("https://dns.alidns.com/dns-query".into()),
|
||||
])),
|
||||
(
|
||||
"fake-ip-filter".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::String("*.lan".into()),
|
||||
Value::String("*.local".into()),
|
||||
Value::String("*.arpa".into()),
|
||||
Value::String("time.*.com".into()),
|
||||
Value::String("ntp.*.com".into()),
|
||||
Value::String("time.*.com".into()),
|
||||
Value::String("+.market.xiaomi.com".into()),
|
||||
Value::String("localhost.ptlogin2.qq.com".into()),
|
||||
Value::String("*.msftncsi.com".into()),
|
||||
Value::String("www.msftconnecttest.com".into()),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"default-nameserver".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::String("223.6.6.6".into()),
|
||||
Value::String("8.8.8.8".into()),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"nameserver".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::String("8.8.8.8".into()),
|
||||
Value::String("https://doh.pub/dns-query".into()),
|
||||
Value::String("https://dns.alidns.com/dns-query".into()),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"fallback".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::String("https://dns.alidns.com/dns-query".into()),
|
||||
Value::String("https://dns.google/dns-query".into()),
|
||||
Value::String("https://cloudflare-dns.com/dns-query".into()),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"nameserver-policy".into(),
|
||||
Value::Mapping(serde_yaml::Mapping::new()),
|
||||
),
|
||||
(
|
||||
"proxy-server-nameserver".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::String("https://doh.pub/dns-query".into()),
|
||||
Value::String("https://dns.alidns.com/dns-query".into()),
|
||||
]),
|
||||
),
|
||||
("direct-nameserver".into(), Value::Sequence(vec![])),
|
||||
("direct-nameserver-follow-policy".into(), Value::Bool(false)),
|
||||
("fallback-filter".into(), Value::Mapping(serde_yaml::Mapping::from_iter([
|
||||
("geoip".into(), Value::Bool(true)),
|
||||
("geoip-code".into(), Value::String("CN".into())),
|
||||
("ipcidr".into(), Value::Sequence(vec![
|
||||
Value::String("240.0.0.0/4".into()),
|
||||
Value::String("0.0.0.0/32".into()),
|
||||
(
|
||||
"fallback-filter".into(),
|
||||
Value::Mapping(serde_yaml::Mapping::from_iter([
|
||||
("geoip".into(), Value::Bool(true)),
|
||||
("geoip-code".into(), Value::String("CN".into())),
|
||||
(
|
||||
"ipcidr".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::String("240.0.0.0/4".into()),
|
||||
Value::String("0.0.0.0/32".into()),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"domain".into(),
|
||||
Value::Sequence(vec![
|
||||
Value::String("+.google.com".into()),
|
||||
Value::String("+.facebook.com".into()),
|
||||
Value::String("+.youtube.com".into()),
|
||||
]),
|
||||
),
|
||||
])),
|
||||
("domain".into(), Value::Sequence(vec![
|
||||
Value::String("+.google.com".into()),
|
||||
Value::String("+.facebook.com".into()),
|
||||
Value::String("+.youtube.com".into()),
|
||||
])),
|
||||
]))),
|
||||
),
|
||||
]);
|
||||
|
||||
// 检查DNS配置文件是否存在
|
||||
let app_dir = dirs::app_home_dir()?;
|
||||
let dns_path = app_dir.join("dns_config.yaml");
|
||||
|
||||
|
||||
if !dns_path.exists() {
|
||||
log::info!(target: "app", "Creating default DNS config file");
|
||||
help::save_yaml(&dns_path, &default_dns_config, Some("# Clash Verge DNS Config"))?;
|
||||
help::save_yaml(
|
||||
&dns_path,
|
||||
&default_dns_config,
|
||||
Some("# Clash Verge DNS Config"),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -323,8 +365,7 @@ pub fn init_resources() -> Result<()> {
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn init_scheme() -> Result<()> {
|
||||
use tauri::utils::platform::current_exe;
|
||||
use winreg::enums::*;
|
||||
use winreg::RegKey;
|
||||
use winreg::{enums::*, RegKey};
|
||||
|
||||
let app_exe = current_exe()?;
|
||||
let app_exe = dunce::canonicalize(app_exe)?;
|
||||
|
@ -1,8 +1,8 @@
|
||||
pub mod dirs;
|
||||
pub mod error;
|
||||
pub mod help;
|
||||
pub mod i18n;
|
||||
pub mod init;
|
||||
pub mod resolve;
|
||||
pub mod server;
|
||||
pub mod tmpl;
|
||||
pub mod i18n;
|
||||
|
@ -1,9 +1,12 @@
|
||||
use crate::config::IVerge;
|
||||
use crate::utils::error;
|
||||
use crate::{config::Config, config::PrfItem, core::*, utils::init, utils::server};
|
||||
use crate::{log_err, wrap_err};
|
||||
#[cfg(target_os = "macos")]
|
||||
use crate::AppHandleManager;
|
||||
use crate::{
|
||||
config::{Config, IVerge, PrfItem},
|
||||
core::*,
|
||||
log_err,
|
||||
utils::{error, init, server},
|
||||
wrap_err,
|
||||
};
|
||||
use anyhow::{bail, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
use percent_encoding::percent_decode_str;
|
||||
@ -201,7 +204,7 @@ pub fn create_window() {
|
||||
log::info!(target: "app", "Window created successfully, attempting to show");
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
|
||||
|
||||
// 设置窗口状态监控,实时保存窗口位置和大小
|
||||
crate::feat::setup_window_state_monitor(&app_handle);
|
||||
}
|
||||
@ -315,8 +318,7 @@ fn resolve_random_port_config() -> Result<()> {
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub async fn set_public_dns(dns_server: String) {
|
||||
use crate::core::handle;
|
||||
use crate::utils::dirs;
|
||||
use crate::{core::handle, utils::dirs};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
|
||||
@ -352,8 +354,7 @@ pub async fn set_public_dns(dns_server: String) {
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub async fn restore_public_dns() {
|
||||
use crate::core::handle;
|
||||
use crate::utils::dirs;
|
||||
use crate::{core::handle, utils::dirs};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
log::info!(target: "app", "try to unset system dns");
|
||||
|
@ -1,8 +1,10 @@
|
||||
extern crate warp;
|
||||
|
||||
use super::resolve;
|
||||
use crate::config::{Config, IVerge, DEFAULT_PAC};
|
||||
use crate::log_err;
|
||||
use crate::{
|
||||
config::{Config, IVerge, DEFAULT_PAC},
|
||||
log_err,
|
||||
};
|
||||
use anyhow::{bail, Result};
|
||||
use port_scanner::local_port_available;
|
||||
use std::convert::Infallible;
|
||||
|
@ -39,7 +39,7 @@ export default defineConfig({
|
||||
}),
|
||||
],
|
||||
build: {
|
||||
outDir: "../dist",
|
||||
outDir: "dist",
|
||||
emptyOutDir: true,
|
||||
},
|
||||
resolve: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user