2025-09-20 00:04:46 +08:00
|
|
|
|
cfg_if::cfg_if! {
|
|
|
|
|
|
if #[cfg(not(feature = "tauri-dev"))] {
|
2025-09-21 01:31:08 +08:00
|
|
|
|
use crate::utils::logging::{console_colored_format, file_format, NoExternModule};
|
2025-09-20 00:04:46 +08:00
|
|
|
|
use flexi_logger::{Cleanup, Criterion, Duplicate, FileSpec, LogSpecification, Logger};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-13 12:51:20 +08:00
|
|
|
|
use crate::{
|
|
|
|
|
|
config::*,
|
|
|
|
|
|
core::handle,
|
2025-08-30 17:58:26 +08:00
|
|
|
|
logging,
|
|
|
|
|
|
process::AsyncHandler,
|
|
|
|
|
|
utils::{dirs, help, logging::Type},
|
2025-03-13 12:51:20 +08:00
|
|
|
|
};
|
2022-07-30 23:32:52 +08:00
|
|
|
|
use anyhow::Result;
|
2023-11-28 07:49:44 +08:00
|
|
|
|
use chrono::{Local, TimeZone};
|
2025-08-26 01:49:51 +08:00
|
|
|
|
use std::{path::PathBuf, str::FromStr};
|
2024-09-02 19:33:17 +08:00
|
|
|
|
use tauri_plugin_shell::ShellExt;
|
2025-08-26 01:49:51 +08:00
|
|
|
|
use tokio::fs;
|
|
|
|
|
|
use tokio::fs::DirEntry;
|
2021-12-08 23:40:52 +08:00
|
|
|
|
|
|
|
|
|
|
/// initialize this instance's log file
|
2025-09-20 00:04:46 +08:00
|
|
|
|
#[cfg(not(feature = "tauri-dev"))]
|
|
|
|
|
|
pub async fn init_logger() -> Result<()> {
|
2025-09-22 16:31:38 +08:00
|
|
|
|
// TODO 提供 runtime 级别实时修改
|
|
|
|
|
|
let (log_level, log_max_size, log_max_count) = {
|
|
|
|
|
|
let verge_guard = Config::verge().await;
|
|
|
|
|
|
let verge = verge_guard.latest_ref();
|
|
|
|
|
|
(
|
|
|
|
|
|
verge.get_log_level(),
|
|
|
|
|
|
verge.app_log_max_size.unwrap_or(128),
|
|
|
|
|
|
verge.app_log_max_count.unwrap_or(8),
|
|
|
|
|
|
)
|
|
|
|
|
|
};
|
2023-07-22 08:53:37 +08:00
|
|
|
|
|
2025-09-20 00:04:46 +08:00
|
|
|
|
let log_dir = dirs::app_logs_dir()?;
|
|
|
|
|
|
let logger = Logger::with(LogSpecification::from(log_level))
|
|
|
|
|
|
.log_to_file(FileSpec::default().directory(log_dir).basename(""))
|
|
|
|
|
|
.duplicate_to_stdout(Duplicate::Debug)
|
|
|
|
|
|
.format(console_colored_format)
|
|
|
|
|
|
.format_for_files(file_format)
|
|
|
|
|
|
.rotate(
|
2025-09-22 16:31:38 +08:00
|
|
|
|
Criterion::Size(log_max_size * 1024),
|
2025-09-20 00:04:46 +08:00
|
|
|
|
flexi_logger::Naming::TimestampsCustomFormat {
|
|
|
|
|
|
current_infix: Some("latest"),
|
|
|
|
|
|
format: "%Y-%m-%d_%H-%M-%S",
|
|
|
|
|
|
},
|
2025-09-22 16:31:38 +08:00
|
|
|
|
Cleanup::KeepLogFiles(log_max_count),
|
2025-09-21 01:31:08 +08:00
|
|
|
|
)
|
|
|
|
|
|
.filter(Box::new(NoExternModule));
|
2021-12-08 23:40:52 +08:00
|
|
|
|
|
2025-09-20 00:04:46 +08:00
|
|
|
|
let _handle = logger.start()?;
|
2021-12-08 23:40:52 +08:00
|
|
|
|
|
2025-09-20 00:04:46 +08:00
|
|
|
|
// TODO 全局 logger handle 控制
|
|
|
|
|
|
// GlobalLoggerProxy::global().set_inner(handle);
|
|
|
|
|
|
// TODO 提供前端设置等级,热更新等级
|
|
|
|
|
|
// logger.parse_new_spec(spec)
|
2022-07-30 23:32:52 +08:00
|
|
|
|
|
2022-11-12 11:37:23 +08:00
|
|
|
|
Ok(())
|
2021-12-08 23:40:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-20 00:04:46 +08:00
|
|
|
|
// TODO flexi_logger 提供了最大保留天数,或许我们应该用内置删除log文件
|
2023-11-02 20:12:46 +08:00
|
|
|
|
/// 删除log文件
|
2025-08-26 01:49:51 +08:00
|
|
|
|
pub async fn delete_log() -> Result<()> {
|
2023-11-02 20:12:46 +08:00
|
|
|
|
let log_dir = dirs::app_logs_dir()?;
|
|
|
|
|
|
if !log_dir.exists() {
|
|
|
|
|
|
return Ok(());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let auto_log_clean = {
|
2025-08-26 01:49:51 +08:00
|
|
|
|
let verge = Config::verge().await;
|
2025-07-04 22:43:23 +08:00
|
|
|
|
let verge = verge.latest_ref();
|
2024-01-10 17:36:35 +08:00
|
|
|
|
verge.auto_log_clean.unwrap_or(0)
|
2023-11-02 20:12:46 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-08-18 19:12:53 +08:00
|
|
|
|
// 1: 1天, 2: 7天, 3: 30天, 4: 90天
|
2023-11-02 20:12:46 +08:00
|
|
|
|
let day = match auto_log_clean {
|
2025-08-18 19:12:53 +08:00
|
|
|
|
1 => 1,
|
|
|
|
|
|
2 => 7,
|
|
|
|
|
|
3 => 30,
|
|
|
|
|
|
4 => 90,
|
2023-11-02 20:12:46 +08:00
|
|
|
|
_ => return Ok(()),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-08-30 17:58:26 +08:00
|
|
|
|
logging!(
|
|
|
|
|
|
info,
|
|
|
|
|
|
Type::Setup,
|
|
|
|
|
|
true,
|
|
|
|
|
|
"try to delete log files, day: {}",
|
|
|
|
|
|
day
|
|
|
|
|
|
);
|
2023-11-02 20:12:46 +08:00
|
|
|
|
|
|
|
|
|
|
// %Y-%m-%d to NaiveDateTime
|
|
|
|
|
|
let parse_time_str = |s: &str| {
|
|
|
|
|
|
let sa: Vec<&str> = s.split('-').collect();
|
|
|
|
|
|
if sa.len() != 4 {
|
|
|
|
|
|
return Err(anyhow::anyhow!("invalid time str"));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let year = i32::from_str(sa[0])?;
|
|
|
|
|
|
let month = u32::from_str(sa[1])?;
|
|
|
|
|
|
let day = u32::from_str(sa[2])?;
|
|
|
|
|
|
let time = chrono::NaiveDate::from_ymd_opt(year, month, day)
|
|
|
|
|
|
.ok_or(anyhow::anyhow!("invalid time str"))?
|
|
|
|
|
|
.and_hms_opt(0, 0, 0)
|
|
|
|
|
|
.ok_or(anyhow::anyhow!("invalid time str"))?;
|
|
|
|
|
|
Ok(time)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-08-26 01:49:51 +08:00
|
|
|
|
let process_file = async move |file: DirEntry| -> Result<()> {
|
2023-11-02 20:12:46 +08:00
|
|
|
|
let file_name = file.file_name();
|
|
|
|
|
|
let file_name = file_name.to_str().unwrap_or_default();
|
|
|
|
|
|
|
|
|
|
|
|
if file_name.ends_with(".log") {
|
|
|
|
|
|
let now = Local::now();
|
|
|
|
|
|
let created_time = parse_time_str(&file_name[0..file_name.len() - 4])?;
|
2023-12-13 10:59:07 +08:00
|
|
|
|
let file_time = Local
|
|
|
|
|
|
.from_local_datetime(&created_time)
|
|
|
|
|
|
.single()
|
|
|
|
|
|
.ok_or(anyhow::anyhow!("invalid local datetime"))?;
|
2023-11-02 20:12:46 +08:00
|
|
|
|
|
|
|
|
|
|
let duration = now.signed_duration_since(file_time);
|
|
|
|
|
|
if duration.num_days() > day {
|
|
|
|
|
|
let file_path = file.path();
|
2025-08-26 01:49:51 +08:00
|
|
|
|
let _ = fs::remove_file(file_path).await;
|
2025-08-30 17:58:26 +08:00
|
|
|
|
logging!(info, Type::Setup, true, "delete log file: {}", file_name);
|
2023-11-02 20:12:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-08-26 01:49:51 +08:00
|
|
|
|
let mut log_read_dir = fs::read_dir(&log_dir).await?;
|
|
|
|
|
|
while let Some(entry) = log_read_dir.next_entry().await? {
|
|
|
|
|
|
std::mem::drop(process_file(entry).await);
|
2023-11-02 20:12:46 +08:00
|
|
|
|
}
|
2024-03-31 21:44:34 +08:00
|
|
|
|
|
|
|
|
|
|
let service_log_dir = log_dir.join("service");
|
2025-08-26 01:49:51 +08:00
|
|
|
|
let mut service_log_read_dir = fs::read_dir(service_log_dir).await?;
|
|
|
|
|
|
while let Some(entry) = service_log_read_dir.next_entry().await? {
|
|
|
|
|
|
std::mem::drop(process_file(entry).await);
|
2024-03-20 20:31:00 +08:00
|
|
|
|
}
|
2024-03-31 21:44:34 +08:00
|
|
|
|
|
2023-11-02 20:12:46 +08:00
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-08 11:25:00 +08:00
|
|
|
|
/// 初始化DNS配置文件
|
2025-08-26 01:49:51 +08:00
|
|
|
|
async fn init_dns_config() -> Result<()> {
|
2025-08-30 02:24:47 +08:00
|
|
|
|
use serde_yaml_ng::Value;
|
2025-03-08 11:25:00 +08:00
|
|
|
|
|
2025-05-11 22:55:31 +08:00
|
|
|
|
// 创建DNS子配置
|
2025-08-30 02:24:47 +08:00
|
|
|
|
let dns_config = serde_yaml_ng::Mapping::from_iter([
|
2025-03-08 11:25:00 +08:00
|
|
|
|
("enable".into(), Value::Bool(true)),
|
|
|
|
|
|
("listen".into(), Value::String(":53".into())),
|
|
|
|
|
|
("enhanced-mode".into(), Value::String("fake-ip".into())),
|
2025-03-13 12:51:20 +08:00
|
|
|
|
(
|
|
|
|
|
|
"fake-ip-range".into(),
|
|
|
|
|
|
Value::String("198.18.0.1/16".into()),
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
"fake-ip-filter-mode".into(),
|
|
|
|
|
|
Value::String("blacklist".into()),
|
|
|
|
|
|
),
|
2025-03-08 11:25:00 +08:00
|
|
|
|
("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)),
|
2025-03-13 12:51:20 +08:00
|
|
|
|
(
|
|
|
|
|
|
"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![
|
2025-04-03 14:06:08 +08:00
|
|
|
|
Value::String("system".into()),
|
2025-03-13 12:51:20 +08:00
|
|
|
|
Value::String("223.6.6.6".into()),
|
|
|
|
|
|
Value::String("8.8.8.8".into()),
|
2025-04-12 23:16:31 +08:00
|
|
|
|
Value::String("2400:3200::1".into()),
|
|
|
|
|
|
Value::String("2001:4860:4860::8888".into()),
|
2025-03-13 12:51:20 +08:00
|
|
|
|
]),
|
|
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
"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()),
|
|
|
|
|
|
]),
|
|
|
|
|
|
),
|
2025-04-03 14:06:08 +08:00
|
|
|
|
("fallback".into(), Value::Sequence(vec![])),
|
2025-03-13 12:51:20 +08:00
|
|
|
|
(
|
|
|
|
|
|
"nameserver-policy".into(),
|
2025-08-30 02:24:47 +08:00
|
|
|
|
Value::Mapping(serde_yaml_ng::Mapping::new()),
|
2025-03-13 12:51:20 +08:00
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
"proxy-server-nameserver".into(),
|
|
|
|
|
|
Value::Sequence(vec![
|
|
|
|
|
|
Value::String("https://doh.pub/dns-query".into()),
|
|
|
|
|
|
Value::String("https://dns.alidns.com/dns-query".into()),
|
2025-04-03 14:06:08 +08:00
|
|
|
|
Value::String("tls://223.5.5.5".into()),
|
2025-03-13 12:51:20 +08:00
|
|
|
|
]),
|
|
|
|
|
|
),
|
2025-03-08 11:25:00 +08:00
|
|
|
|
("direct-nameserver".into(), Value::Sequence(vec![])),
|
|
|
|
|
|
("direct-nameserver-follow-policy".into(), Value::Bool(false)),
|
2025-03-13 12:51:20 +08:00
|
|
|
|
(
|
|
|
|
|
|
"fallback-filter".into(),
|
2025-08-30 02:24:47 +08:00
|
|
|
|
Value::Mapping(serde_yaml_ng::Mapping::from_iter([
|
2025-03-13 12:51:20 +08:00
|
|
|
|
("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()),
|
|
|
|
|
|
]),
|
|
|
|
|
|
),
|
2025-03-08 11:25:00 +08:00
|
|
|
|
])),
|
2025-03-13 12:51:20 +08:00
|
|
|
|
),
|
2025-03-08 11:25:00 +08:00
|
|
|
|
]);
|
|
|
|
|
|
|
2025-05-11 22:55:31 +08:00
|
|
|
|
// 获取默认DNS和host配置
|
2025-08-30 02:24:47 +08:00
|
|
|
|
let default_dns_config = serde_yaml_ng::Mapping::from_iter([
|
2025-05-11 22:55:31 +08:00
|
|
|
|
("dns".into(), Value::Mapping(dns_config)),
|
2025-08-30 02:24:47 +08:00
|
|
|
|
(
|
|
|
|
|
|
"hosts".into(),
|
|
|
|
|
|
Value::Mapping(serde_yaml_ng::Mapping::new()),
|
|
|
|
|
|
),
|
2025-05-11 22:55:31 +08:00
|
|
|
|
]);
|
|
|
|
|
|
|
2025-03-08 11:25:00 +08:00
|
|
|
|
// 检查DNS配置文件是否存在
|
|
|
|
|
|
let app_dir = dirs::app_home_dir()?;
|
|
|
|
|
|
let dns_path = app_dir.join("dns_config.yaml");
|
2025-03-13 12:51:20 +08:00
|
|
|
|
|
2025-03-08 11:25:00 +08:00
|
|
|
|
if !dns_path.exists() {
|
2025-08-30 17:58:26 +08:00
|
|
|
|
logging!(info, Type::Setup, true, "Creating default DNS config file");
|
2025-03-13 12:51:20 +08:00
|
|
|
|
help::save_yaml(
|
|
|
|
|
|
&dns_path,
|
|
|
|
|
|
&default_dns_config,
|
|
|
|
|
|
Some("# Clash Verge DNS Config"),
|
2025-08-26 01:49:51 +08:00
|
|
|
|
)
|
|
|
|
|
|
.await?;
|
2025-03-08 11:25:00 +08:00
|
|
|
|
}
|
2025-03-13 12:51:20 +08:00
|
|
|
|
|
2025-03-08 11:25:00 +08:00
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-30 17:58:26 +08:00
|
|
|
|
/// 确保目录结构存在
|
|
|
|
|
|
async fn ensure_directories() -> Result<()> {
|
|
|
|
|
|
let directories = [
|
|
|
|
|
|
("app_home", dirs::app_home_dir()?),
|
|
|
|
|
|
("app_profiles", dirs::app_profiles_dir()?),
|
|
|
|
|
|
("app_logs", dirs::app_logs_dir()?),
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
for (name, dir) in directories {
|
|
|
|
|
|
if !dir.exists() {
|
|
|
|
|
|
fs::create_dir_all(&dir).await.map_err(|e| {
|
|
|
|
|
|
anyhow::anyhow!("Failed to create {} directory {:?}: {}", name, dir, e)
|
|
|
|
|
|
})?;
|
|
|
|
|
|
logging!(
|
|
|
|
|
|
info,
|
|
|
|
|
|
Type::Setup,
|
|
|
|
|
|
true,
|
|
|
|
|
|
"Created {} directory: {:?}",
|
|
|
|
|
|
name,
|
|
|
|
|
|
dir
|
|
|
|
|
|
);
|
2022-11-18 09:35:05 +08:00
|
|
|
|
}
|
2025-08-30 17:58:26 +08:00
|
|
|
|
}
|
2022-11-18 09:35:05 +08:00
|
|
|
|
|
2025-08-30 17:58:26 +08:00
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
2022-11-22 22:01:19 +08:00
|
|
|
|
|
2025-08-30 17:58:26 +08:00
|
|
|
|
/// 初始化配置文件
|
|
|
|
|
|
async fn initialize_config_files() -> Result<()> {
|
edition 2024 (#4702)
* feat: update Cargo.toml for 2024 edition and optimize release profiles
* feat: refactor environment variable settings for Linux and improve code organization
* Refactor conditional statements to use `&&` for improved readability
- Updated multiple files to combine nested `if let` statements using `&&` for better clarity and conciseness.
- This change enhances the readability of the code by reducing indentation levels and making the conditions more straightforward.
- Affected files include: media_unlock_checker.rs, profile.rs, clash.rs, profiles.rs, async_proxy_query.rs, core.rs, handle.rs, hotkey.rs, service.rs, timer.rs, tray/mod.rs, merge.rs, seq.rs, config.rs, proxy.rs, window.rs, general.rs, dirs.rs, i18n.rs, init.rs, network.rs, and window.rs in the resolve module.
* refactor: streamline conditional checks using `&&` for improved readability
* fix: update release profile settings for panic behavior and optimization
* fix: adjust optimization level in Cargo.toml and reorder imports in lightweight.rs
2025-09-10 09:49:06 +08:00
|
|
|
|
if let Ok(path) = dirs::clash_path()
|
|
|
|
|
|
&& !path.exists()
|
|
|
|
|
|
{
|
|
|
|
|
|
let template = IClashTemp::template().0;
|
|
|
|
|
|
help::save_yaml(&path, &template, Some("# Clash Verge"))
|
|
|
|
|
|
.await
|
|
|
|
|
|
.map_err(|e| anyhow::anyhow!("Failed to create clash config: {}", e))?;
|
|
|
|
|
|
logging!(
|
|
|
|
|
|
info,
|
|
|
|
|
|
Type::Setup,
|
|
|
|
|
|
true,
|
|
|
|
|
|
"Created clash config at {:?}",
|
|
|
|
|
|
path
|
|
|
|
|
|
);
|
2025-08-26 01:49:51 +08:00
|
|
|
|
}
|
2022-11-22 22:01:19 +08:00
|
|
|
|
|
edition 2024 (#4702)
* feat: update Cargo.toml for 2024 edition and optimize release profiles
* feat: refactor environment variable settings for Linux and improve code organization
* Refactor conditional statements to use `&&` for improved readability
- Updated multiple files to combine nested `if let` statements using `&&` for better clarity and conciseness.
- This change enhances the readability of the code by reducing indentation levels and making the conditions more straightforward.
- Affected files include: media_unlock_checker.rs, profile.rs, clash.rs, profiles.rs, async_proxy_query.rs, core.rs, handle.rs, hotkey.rs, service.rs, timer.rs, tray/mod.rs, merge.rs, seq.rs, config.rs, proxy.rs, window.rs, general.rs, dirs.rs, i18n.rs, init.rs, network.rs, and window.rs in the resolve module.
* refactor: streamline conditional checks using `&&` for improved readability
* fix: update release profile settings for panic behavior and optimization
* fix: adjust optimization level in Cargo.toml and reorder imports in lightweight.rs
2025-09-10 09:49:06 +08:00
|
|
|
|
if let Ok(path) = dirs::verge_path()
|
|
|
|
|
|
&& !path.exists()
|
|
|
|
|
|
{
|
|
|
|
|
|
let template = IVerge::template();
|
|
|
|
|
|
help::save_yaml(&path, &template, Some("# Clash Verge"))
|
|
|
|
|
|
.await
|
|
|
|
|
|
.map_err(|e| anyhow::anyhow!("Failed to create verge config: {}", e))?;
|
|
|
|
|
|
logging!(
|
|
|
|
|
|
info,
|
|
|
|
|
|
Type::Setup,
|
|
|
|
|
|
true,
|
|
|
|
|
|
"Created verge config at {:?}",
|
|
|
|
|
|
path
|
|
|
|
|
|
);
|
2025-08-26 01:49:51 +08:00
|
|
|
|
}
|
2022-11-22 22:01:19 +08:00
|
|
|
|
|
edition 2024 (#4702)
* feat: update Cargo.toml for 2024 edition and optimize release profiles
* feat: refactor environment variable settings for Linux and improve code organization
* Refactor conditional statements to use `&&` for improved readability
- Updated multiple files to combine nested `if let` statements using `&&` for better clarity and conciseness.
- This change enhances the readability of the code by reducing indentation levels and making the conditions more straightforward.
- Affected files include: media_unlock_checker.rs, profile.rs, clash.rs, profiles.rs, async_proxy_query.rs, core.rs, handle.rs, hotkey.rs, service.rs, timer.rs, tray/mod.rs, merge.rs, seq.rs, config.rs, proxy.rs, window.rs, general.rs, dirs.rs, i18n.rs, init.rs, network.rs, and window.rs in the resolve module.
* refactor: streamline conditional checks using `&&` for improved readability
* fix: update release profile settings for panic behavior and optimization
* fix: adjust optimization level in Cargo.toml and reorder imports in lightweight.rs
2025-09-10 09:49:06 +08:00
|
|
|
|
if let Ok(path) = dirs::profiles_path()
|
|
|
|
|
|
&& !path.exists()
|
|
|
|
|
|
{
|
|
|
|
|
|
let template = IProfiles::template();
|
|
|
|
|
|
help::save_yaml(&path, &template, Some("# Clash Verge"))
|
|
|
|
|
|
.await
|
|
|
|
|
|
.map_err(|e| anyhow::anyhow!("Failed to create profiles config: {}", e))?;
|
|
|
|
|
|
logging!(
|
|
|
|
|
|
info,
|
|
|
|
|
|
Type::Setup,
|
|
|
|
|
|
true,
|
|
|
|
|
|
"Created profiles config at {:?}",
|
|
|
|
|
|
path
|
|
|
|
|
|
);
|
2025-08-26 01:49:51 +08:00
|
|
|
|
}
|
2021-12-08 23:40:52 +08:00
|
|
|
|
|
2025-08-30 17:58:26 +08:00
|
|
|
|
// 验证并修正verge配置
|
|
|
|
|
|
IVerge::validate_and_fix_config()
|
|
|
|
|
|
.await
|
|
|
|
|
|
.map_err(|e| anyhow::anyhow!("Failed to validate verge config: {}", e))?;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Initialize all the config files
|
|
|
|
|
|
/// before tauri setup
|
|
|
|
|
|
pub async fn init_config() -> Result<()> {
|
2025-09-20 00:04:46 +08:00
|
|
|
|
// We do not need init_portable_flag here anymore due to lib.rs will to the things
|
|
|
|
|
|
// let _ = dirs::init_portable_flag();
|
2025-08-30 17:58:26 +08:00
|
|
|
|
|
2025-09-20 00:04:46 +08:00
|
|
|
|
// We do not need init_log here anymore due to resolve will to the things
|
|
|
|
|
|
// if let Err(e) = init_log().await {
|
|
|
|
|
|
// eprintln!("Failed to initialize logging: {}", e);
|
|
|
|
|
|
// }
|
2025-08-30 17:58:26 +08:00
|
|
|
|
|
|
|
|
|
|
ensure_directories().await?;
|
|
|
|
|
|
|
|
|
|
|
|
initialize_config_files().await?;
|
|
|
|
|
|
|
|
|
|
|
|
AsyncHandler::spawn(|| async {
|
|
|
|
|
|
if let Err(e) = delete_log().await {
|
|
|
|
|
|
logging!(warn, Type::Setup, true, "Failed to clean old logs: {}", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
logging!(info, Type::Setup, true, "后台日志清理任务完成");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if let Err(e) = init_dns_config().await {
|
|
|
|
|
|
logging!(
|
|
|
|
|
|
warn,
|
|
|
|
|
|
Type::Setup,
|
|
|
|
|
|
true,
|
|
|
|
|
|
"DNS config initialization failed: {}",
|
|
|
|
|
|
e
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-03-08 11:25:00 +08:00
|
|
|
|
|
2022-11-12 11:37:23 +08:00
|
|
|
|
Ok(())
|
2021-12-08 23:40:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-22 09:18:54 +08:00
|
|
|
|
/// initialize app resources
|
|
|
|
|
|
/// after tauri setup
|
2025-08-26 01:49:51 +08:00
|
|
|
|
pub async fn init_resources() -> Result<()> {
|
2022-11-18 09:35:05 +08:00
|
|
|
|
let app_dir = dirs::app_home_dir()?;
|
2023-12-14 12:50:45 +08:00
|
|
|
|
let res_dir = dirs::app_resources_dir()?;
|
2022-02-18 23:57:13 +08:00
|
|
|
|
|
2022-11-22 22:01:19 +08:00
|
|
|
|
if !app_dir.exists() {
|
2025-08-26 01:49:51 +08:00
|
|
|
|
std::mem::drop(fs::create_dir_all(&app_dir).await);
|
2022-11-22 22:01:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
if !res_dir.exists() {
|
2025-08-26 01:49:51 +08:00
|
|
|
|
std::mem::drop(fs::create_dir_all(&res_dir).await);
|
2022-11-22 22:01:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-14 12:50:45 +08:00
|
|
|
|
let file_list = ["Country.mmdb", "geoip.dat", "geosite.dat"];
|
|
|
|
|
|
|
|
|
|
|
|
// copy the resource file
|
|
|
|
|
|
// if the source file is newer than the destination file, copy it over
|
|
|
|
|
|
for file in file_list.iter() {
|
|
|
|
|
|
let src_path = res_dir.join(file);
|
|
|
|
|
|
let dest_path = app_dir.join(file);
|
|
|
|
|
|
|
2025-08-26 01:49:51 +08:00
|
|
|
|
let handle_copy = |src: PathBuf, dest: PathBuf, file: String| async move {
|
|
|
|
|
|
match fs::copy(&src, &dest).await {
|
2025-08-30 17:58:26 +08:00
|
|
|
|
Ok(_) => {
|
|
|
|
|
|
logging!(debug, Type::Setup, true, "resources copied '{}'", file);
|
|
|
|
|
|
}
|
2023-12-14 12:50:45 +08:00
|
|
|
|
Err(err) => {
|
2025-08-30 17:58:26 +08:00
|
|
|
|
logging!(
|
|
|
|
|
|
error,
|
|
|
|
|
|
Type::Setup,
|
|
|
|
|
|
true,
|
|
|
|
|
|
"failed to copy resources '{}' to '{:?}', {}",
|
|
|
|
|
|
file,
|
|
|
|
|
|
dest,
|
|
|
|
|
|
err
|
|
|
|
|
|
);
|
2023-12-14 12:50:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if src_path.exists() && !dest_path.exists() {
|
2025-08-26 01:49:51 +08:00
|
|
|
|
handle_copy(src_path.clone(), dest_path.clone(), file.to_string()).await;
|
2023-12-14 12:50:45 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-26 01:49:51 +08:00
|
|
|
|
let src_modified = fs::metadata(&src_path).await.and_then(|m| m.modified());
|
|
|
|
|
|
let dest_modified = fs::metadata(&dest_path).await.and_then(|m| m.modified());
|
2023-07-22 09:18:54 +08:00
|
|
|
|
|
|
|
|
|
|
match (src_modified, dest_modified) {
|
|
|
|
|
|
(Ok(src_modified), Ok(dest_modified)) => {
|
|
|
|
|
|
if src_modified > dest_modified {
|
2025-08-26 01:49:51 +08:00
|
|
|
|
handle_copy(src_path.clone(), dest_path.clone(), file.to_string()).await;
|
2023-07-22 09:18:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
2025-08-30 17:58:26 +08:00
|
|
|
|
logging!(
|
|
|
|
|
|
debug,
|
|
|
|
|
|
Type::Setup,
|
|
|
|
|
|
true,
|
|
|
|
|
|
"failed to get modified '{}'",
|
|
|
|
|
|
file
|
|
|
|
|
|
);
|
2025-08-26 01:49:51 +08:00
|
|
|
|
handle_copy(src_path.clone(), dest_path.clone(), file.to_string()).await;
|
2023-07-22 09:18:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
2022-03-18 11:49:00 +08:00
|
|
|
|
}
|
2022-11-18 09:35:05 +08:00
|
|
|
|
|
|
|
|
|
|
Ok(())
|
2021-12-08 23:40:52 +08:00
|
|
|
|
}
|
2024-01-09 21:57:06 +08:00
|
|
|
|
|
|
|
|
|
|
/// initialize url scheme
|
|
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
|
|
pub fn init_scheme() -> Result<()> {
|
|
|
|
|
|
use tauri::utils::platform::current_exe;
|
edition 2024 (#4702)
* feat: update Cargo.toml for 2024 edition and optimize release profiles
* feat: refactor environment variable settings for Linux and improve code organization
* Refactor conditional statements to use `&&` for improved readability
- Updated multiple files to combine nested `if let` statements using `&&` for better clarity and conciseness.
- This change enhances the readability of the code by reducing indentation levels and making the conditions more straightforward.
- Affected files include: media_unlock_checker.rs, profile.rs, clash.rs, profiles.rs, async_proxy_query.rs, core.rs, handle.rs, hotkey.rs, service.rs, timer.rs, tray/mod.rs, merge.rs, seq.rs, config.rs, proxy.rs, window.rs, general.rs, dirs.rs, i18n.rs, init.rs, network.rs, and window.rs in the resolve module.
* refactor: streamline conditional checks using `&&` for improved readability
* fix: update release profile settings for panic behavior and optimization
* fix: adjust optimization level in Cargo.toml and reorder imports in lightweight.rs
2025-09-10 09:49:06 +08:00
|
|
|
|
use winreg::{RegKey, enums::*};
|
2024-01-09 21:57:06 +08:00
|
|
|
|
|
|
|
|
|
|
let app_exe = current_exe()?;
|
|
|
|
|
|
let app_exe = dunce::canonicalize(app_exe)?;
|
2024-01-10 17:36:35 +08:00
|
|
|
|
let app_exe = app_exe.to_string_lossy().into_owned();
|
2024-01-09 21:57:06 +08:00
|
|
|
|
|
|
|
|
|
|
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
|
|
|
|
|
let (clash, _) = hkcu.create_subkey("Software\\Classes\\Clash")?;
|
|
|
|
|
|
clash.set_value("", &"Clash Verge")?;
|
|
|
|
|
|
clash.set_value("URL Protocol", &"Clash Verge URL Scheme Protocol")?;
|
|
|
|
|
|
let (default_icon, _) = hkcu.create_subkey("Software\\Classes\\Clash\\DefaultIcon")?;
|
2024-01-10 17:36:35 +08:00
|
|
|
|
default_icon.set_value("", &app_exe)?;
|
2024-01-09 21:57:06 +08:00
|
|
|
|
let (command, _) = hkcu.create_subkey("Software\\Classes\\Clash\\Shell\\Open\\Command")?;
|
|
|
|
|
|
command.set_value("", &format!("{app_exe} \"%1\""))?;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
|
pub fn init_scheme() -> Result<()> {
|
2024-01-10 13:03:34 +08:00
|
|
|
|
let output = std::process::Command::new("xdg-mime")
|
|
|
|
|
|
.arg("default")
|
2024-01-10 16:34:35 +08:00
|
|
|
|
.arg("clash-verge.desktop")
|
2024-01-10 13:03:34 +08:00
|
|
|
|
.arg("x-scheme-handler/clash")
|
|
|
|
|
|
.output()?;
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
|
return Err(anyhow::anyhow!(
|
|
|
|
|
|
"failed to set clash scheme, {}",
|
|
|
|
|
|
String::from_utf8_lossy(&output.stderr)
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
2024-01-09 21:57:06 +08:00
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
|
|
pub fn init_scheme() -> Result<()> {
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
2024-01-17 15:06:16 +08:00
|
|
|
|
|
2024-09-23 16:31:58 +08:00
|
|
|
|
pub async fn startup_script() -> Result<()> {
|
2025-10-01 09:30:28 +08:00
|
|
|
|
let app_handle = handle::Handle::app_handle();
|
2024-09-02 19:33:17 +08:00
|
|
|
|
let script_path = {
|
2025-08-26 01:49:51 +08:00
|
|
|
|
let verge = Config::verge().await;
|
2025-07-04 22:43:23 +08:00
|
|
|
|
let verge = verge.latest_ref();
|
2024-01-17 15:06:16 +08:00
|
|
|
|
verge.startup_script.clone().unwrap_or("".to_string())
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-09-02 19:33:17 +08:00
|
|
|
|
if script_path.is_empty() {
|
|
|
|
|
|
return Ok(());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let shell_type = if script_path.ends_with(".sh") {
|
|
|
|
|
|
"bash"
|
|
|
|
|
|
} else if script_path.ends_with(".ps1") || script_path.ends_with(".bat") {
|
|
|
|
|
|
"powershell"
|
|
|
|
|
|
} else {
|
2024-09-04 07:53:16 +08:00
|
|
|
|
return Err(anyhow::anyhow!(
|
|
|
|
|
|
"unsupported script extension: {}",
|
|
|
|
|
|
script_path
|
|
|
|
|
|
));
|
2024-09-02 19:33:17 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let script_dir = PathBuf::from(&script_path);
|
|
|
|
|
|
if !script_dir.exists() {
|
|
|
|
|
|
return Err(anyhow::anyhow!("script not found: {}", script_path));
|
2024-01-17 15:06:16 +08:00
|
|
|
|
}
|
2024-09-02 19:33:17 +08:00
|
|
|
|
|
|
|
|
|
|
let parent_dir = script_dir.parent();
|
|
|
|
|
|
let working_dir = parent_dir.unwrap_or(script_dir.as_ref());
|
|
|
|
|
|
|
|
|
|
|
|
app_handle
|
|
|
|
|
|
.shell()
|
|
|
|
|
|
.command(shell_type)
|
2024-09-24 20:06:25 +08:00
|
|
|
|
.current_dir(working_dir)
|
2024-09-02 19:33:17 +08:00
|
|
|
|
.args(&[script_path])
|
2024-09-04 07:53:16 +08:00
|
|
|
|
.output()
|
|
|
|
|
|
.await?;
|
2024-09-02 19:33:17 +08:00
|
|
|
|
|
2024-01-17 15:06:16 +08:00
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|