diff --git a/src-tauri/src/config/verge.rs b/src-tauri/src/config/verge.rs index 708ce61df635f4119b0236170cdd970197e718b7..961280eadae4d864e867ec3412a865a52e41bd8b 100644 --- a/src-tauri/src/config/verge.rs +++ b/src-tauri/src/config/verge.rs @@ -83,6 +83,10 @@ pub struct IVerge { /// proxy 页é¢å¸ƒå±€ 列数 pub proxy_layout_column: Option<i32>, + /// æ—¥å¿—æ¸…ç† + /// 0: 䏿¸…ç†; 1: 7天; 2: 30天; 3: 90天 + pub auto_log_clean: Option<i32>, + /// window size and position #[serde(skip_serializing_if = "Option::is_none")] pub window_size_position: Option<Vec<f64>>, @@ -137,6 +141,7 @@ impl IVerge { auto_close_connection: Some(true), enable_builtin_enhanced: Some(true), enable_clash_fields: Some(true), + auto_log_clean: Some(3), ..Self::default() } } @@ -183,7 +188,7 @@ impl IVerge { patch!(enable_builtin_enhanced); patch!(proxy_layout_column); patch!(enable_clash_fields); - + patch!(auto_log_clean); patch!(window_size_position); } diff --git a/src-tauri/src/utils/init.rs b/src-tauri/src/utils/init.rs index 2fae79d917b999d329b2c1c61d4e244ebdbfa41c..e9bcd4206e07138642d6255c64bf031920630d43 100644 --- a/src-tauri/src/utils/init.rs +++ b/src-tauri/src/utils/init.rs @@ -1,13 +1,14 @@ use crate::config::*; use crate::utils::{dirs, help}; use anyhow::Result; -use chrono::Local; +use chrono::{DateTime, Local}; 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; +use std::fs::{self, DirEntry}; +use std::str::FromStr; use tauri::PackageInfo; /// initialize this instance's log file @@ -69,6 +70,72 @@ fn init_log() -> Result<()> { Ok(()) } +/// åˆ é™¤log文件 +pub fn delete_log() -> Result<()> { + let log_dir = dirs::app_logs_dir()?; + if !log_dir.exists() { + return Ok(()); + } + + let auto_log_clean = { + let verge = Config::verge(); + let verge = verge.data(); + verge.auto_log_clean.clone().unwrap_or(0) + }; + + let day = match auto_log_clean { + 1 => 7, + 2 => 30, + 3 => 90, + _ => return Ok(()), + }; + + log::debug!(target: "app", "try to delete log files, day: {day}"); + + // %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) + }; + + let process_file = |file: DirEntry| -> Result<()> { + 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])?; + let file_time = DateTime::<Local>::from_local(created_time, now.offset().clone()); + + let duration = now.signed_duration_since(file_time); + if duration.num_days() > day { + let file_path = file.path(); + let _ = fs::remove_file(file_path); + log::info!(target: "app", "delete log file: {file_name}"); + } + } + Ok(()) + }; + + for file in fs::read_dir(&log_dir)? { + if let Ok(file) = file { + let _ = process_file(file); + } + } + Ok(()) +} + /// Initialize all the config files /// before tauri setup pub fn init_config() -> Result<()> { @@ -78,6 +145,7 @@ pub fn init_config() -> Result<()> { } let _ = init_log(); + let _ = delete_log(); crate::log_err!(dirs::app_home_dir().map(|app_dir| { if !app_dir.exists() { diff --git a/src/components/setting/mods/misc-viewer.tsx b/src/components/setting/mods/misc-viewer.tsx index cfcd1c07583c412c0c182ab7ed3bcced9acddb8b..7d6326471f9157109c62d3bbf16bb90fd5ee56aa 100644 --- a/src/components/setting/mods/misc-viewer.tsx +++ b/src/components/setting/mods/misc-viewer.tsx @@ -25,6 +25,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => { enableBuiltinEnhanced: true, proxyLayoutColumn: 6, defaultLatencyTest: "", + autoLogClean: 0, }); useImperativeHandle(ref, () => ({ @@ -37,6 +38,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => { enableBuiltinEnhanced: verge?.enable_builtin_enhanced ?? true, proxyLayoutColumn: verge?.proxy_layout_column || 6, defaultLatencyTest: verge?.default_latency_test || "", + autoLogClean: verge?.auto_log_clean || 0, }); }, close: () => setOpen(false), @@ -51,6 +53,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => { enable_builtin_enhanced: values.enableBuiltinEnhanced, proxy_layout_column: values.proxyLayoutColumn, default_latency_test: values.defaultLatencyTest, + auto_log_clean: values.autoLogClean as any, }); setOpen(false); } catch (err: any) { @@ -128,7 +131,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => { <ListItemText primary={t("Proxy Layout Column")} /> <Select size="small" - sx={{ width: 100, "> div": { py: "7.5px" } }} + sx={{ width: 135, "> div": { py: "7.5px" } }} value={values.proxyLayoutColumn} onChange={(e) => { setValues((v) => ({ @@ -148,6 +151,32 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => { </Select> </ListItem> + <ListItem sx={{ padding: "5px 2px" }}> + <ListItemText primary={t("Auto Log Clean")} /> + <Select + size="small" + sx={{ width: 135, "> div": { py: "7.5px" } }} + value={values.autoLogClean} + onChange={(e) => { + setValues((v) => ({ + ...v, + autoLogClean: e.target.value as number, + })); + }} + > + {[ + { key: "Never Clean", value: 0 }, + { key: "Retain 7 Days", value: 1 }, + { key: "Retain 30 Days", value: 2 }, + { key: "Retain 90 Days", value: 3 }, + ].map((i) => ( + <MenuItem key={i.value} value={i.value}> + {t(i.key)} + </MenuItem> + ))} + </Select> + </ListItem> + <ListItem sx={{ padding: "5px 2px" }}> <ListItemText primary={t("Default Latency Test")} /> <TextField diff --git a/src/locales/en.json b/src/locales/en.json index 3ea7b2134f42b24ee02aea236480fae4c4f7b21a..10c92eff99aef36d90b02976285bbf213325431b 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -125,5 +125,11 @@ "Enable Clash Fields Filter": "Enable Clash Fields Filter", "Enable Builtin Enhanced": "Enable Builtin Enhanced", "Proxy Layout Column": "Proxy Layout Column", - "Default Latency Test": "Default Latency Test" + "Default Latency Test": "Default Latency Test", + + "Auto Log Clean": "Auto Log Clean", + "Never Clean": "Never Clean", + "Retain 7 Days": "Retain 7 Days", + "Retain 30 Days": "Retain 30 Days", + "Retain 90 Days": "Retain 90 Days" } diff --git a/src/locales/zh.json b/src/locales/zh.json index 256ce8c07261e3862935552f3329ed244a9e0e3b..27b358ca27450530d6e7fd3bcc14592ab9a31396 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -125,5 +125,11 @@ "Enable Clash Fields Filter": "å¼€å¯Clashå—æ®µè¿‡æ»¤", "Enable Builtin Enhanced": "å¼€å¯å†…建增强功能", "Proxy Layout Column": "代ç†é¡µå¸ƒå±€åˆ—æ•°", - "Default Latency Test": "默认测试链接" + "Default Latency Test": "默认测试链接", + + "Auto Log Clean": "è‡ªåŠ¨æ¸…ç†æ—¥å¿—", + "Never Clean": "䏿¸…ç†", + "Retain 7 Days": "ä¿ç•™7天", + "Retain 30 Days": "ä¿ç•™30天", + "Retain 90 Days": "ä¿ç•™90天" } diff --git a/src/services/types.d.ts b/src/services/types.d.ts index 57e3316ee248fac2374797ffdae10040c937551a..3fe2175e736e82aeb1684e3fb8d6b36d014c95dd 100644 --- a/src/services/types.d.ts +++ b/src/services/types.d.ts @@ -186,6 +186,7 @@ interface IVergeConfig { default_latency_test?: string; enable_clash_fields?: boolean; enable_builtin_enhanced?: boolean; + auto_log_clean?: 0 | 1 | 2 | 3; proxy_layout_column?: number; }