init.rs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. use crate::config::*;
  2. use crate::utils::{dirs, help};
  3. use anyhow::Result;
  4. use chrono::{Local, TimeZone};
  5. use log::LevelFilter;
  6. use log4rs::append::console::ConsoleAppender;
  7. use log4rs::append::file::FileAppender;
  8. use log4rs::config::{Appender, Logger, Root};
  9. use log4rs::encode::pattern::PatternEncoder;
  10. use std::fs::{self, DirEntry};
  11. use std::str::FromStr;
  12. /// initialize this instance's log file
  13. fn init_log() -> Result<()> {
  14. let log_dir = dirs::app_logs_dir()?;
  15. if !log_dir.exists() {
  16. let _ = fs::create_dir_all(&log_dir);
  17. }
  18. let log_level = Config::verge().data().get_log_level();
  19. if log_level == LevelFilter::Off {
  20. return Ok(());
  21. }
  22. let local_time = Local::now().format("%Y-%m-%d-%H%M").to_string();
  23. let log_file = format!("{}.log", local_time);
  24. let log_file = log_dir.join(log_file);
  25. let log_pattern = match log_level {
  26. LevelFilter::Trace => "{d(%Y-%m-%d %H:%M:%S)} {l} [{M}] - {m}{n}",
  27. _ => "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}",
  28. };
  29. let encode = Box::new(PatternEncoder::new(log_pattern));
  30. let stdout = ConsoleAppender::builder().encoder(encode.clone()).build();
  31. let tofile = FileAppender::builder().encoder(encode).build(log_file)?;
  32. let mut logger_builder = Logger::builder();
  33. let mut root_builder = Root::builder();
  34. let log_more = log_level == LevelFilter::Trace || log_level == LevelFilter::Debug;
  35. #[cfg(feature = "verge-dev")]
  36. {
  37. logger_builder = logger_builder.appenders(["file", "stdout"]);
  38. if log_more {
  39. root_builder = root_builder.appenders(["file", "stdout"]);
  40. } else {
  41. root_builder = root_builder.appenders(["stdout"]);
  42. }
  43. }
  44. #[cfg(not(feature = "verge-dev"))]
  45. {
  46. logger_builder = logger_builder.appenders(["file"]);
  47. if log_more {
  48. root_builder = root_builder.appenders(["file"]);
  49. }
  50. }
  51. let (config, _) = log4rs::config::Config::builder()
  52. .appender(Appender::builder().build("stdout", Box::new(stdout)))
  53. .appender(Appender::builder().build("file", Box::new(tofile)))
  54. .logger(logger_builder.additive(false).build("app", log_level))
  55. .build_lossy(root_builder.build(log_level));
  56. log4rs::init_config(config)?;
  57. Ok(())
  58. }
  59. /// 删除log文件
  60. pub fn delete_log() -> Result<()> {
  61. let log_dir = dirs::app_logs_dir()?;
  62. if !log_dir.exists() {
  63. return Ok(());
  64. }
  65. let auto_log_clean = {
  66. let verge = Config::verge();
  67. let verge = verge.data();
  68. verge.auto_log_clean.clone().unwrap_or(0)
  69. };
  70. let day = match auto_log_clean {
  71. 1 => 7,
  72. 2 => 30,
  73. 3 => 90,
  74. _ => return Ok(()),
  75. };
  76. log::debug!(target: "app", "try to delete log files, day: {day}");
  77. // %Y-%m-%d to NaiveDateTime
  78. let parse_time_str = |s: &str| {
  79. let sa: Vec<&str> = s.split('-').collect();
  80. if sa.len() != 4 {
  81. return Err(anyhow::anyhow!("invalid time str"));
  82. }
  83. let year = i32::from_str(sa[0])?;
  84. let month = u32::from_str(sa[1])?;
  85. let day = u32::from_str(sa[2])?;
  86. let time = chrono::NaiveDate::from_ymd_opt(year, month, day)
  87. .ok_or(anyhow::anyhow!("invalid time str"))?
  88. .and_hms_opt(0, 0, 0)
  89. .ok_or(anyhow::anyhow!("invalid time str"))?;
  90. Ok(time)
  91. };
  92. let process_file = |file: DirEntry| -> Result<()> {
  93. let file_name = file.file_name();
  94. let file_name = file_name.to_str().unwrap_or_default();
  95. if file_name.ends_with(".log") {
  96. let now = Local::now();
  97. let created_time = parse_time_str(&file_name[0..file_name.len() - 4])?;
  98. let file_time = Local
  99. .from_local_datetime(&created_time)
  100. .single()
  101. .ok_or(anyhow::anyhow!("invalid local datetime"))?;
  102. let duration = now.signed_duration_since(file_time);
  103. if duration.num_days() > day {
  104. let file_path = file.path();
  105. let _ = fs::remove_file(file_path);
  106. log::info!(target: "app", "delete log file: {file_name}");
  107. }
  108. }
  109. Ok(())
  110. };
  111. for file in fs::read_dir(&log_dir)? {
  112. if let Ok(file) = file {
  113. let _ = process_file(file);
  114. }
  115. }
  116. Ok(())
  117. }
  118. /// Initialize all the config files
  119. /// before tauri setup
  120. pub fn init_config() -> Result<()> {
  121. let _ = dirs::init_portable_flag();
  122. let _ = init_log();
  123. let _ = delete_log();
  124. crate::log_err!(dirs::app_home_dir().map(|app_dir| {
  125. if !app_dir.exists() {
  126. let _ = fs::create_dir_all(&app_dir);
  127. }
  128. }));
  129. crate::log_err!(dirs::app_profiles_dir().map(|profiles_dir| {
  130. if !profiles_dir.exists() {
  131. let _ = fs::create_dir_all(&profiles_dir);
  132. }
  133. }));
  134. crate::log_err!(dirs::clash_path().map(|path| {
  135. if !path.exists() {
  136. help::save_yaml(&path, &IClashTemp::template().0, Some("# Clash Vergeasu"))?;
  137. }
  138. <Result<()>>::Ok(())
  139. }));
  140. crate::log_err!(dirs::verge_path().map(|path| {
  141. if !path.exists() {
  142. help::save_yaml(&path, &IVerge::template(), Some("# Clash Verge"))?;
  143. }
  144. <Result<()>>::Ok(())
  145. }));
  146. crate::log_err!(dirs::profiles_path().map(|path| {
  147. if !path.exists() {
  148. help::save_yaml(&path, &IProfiles::template(), Some("# Clash Verge"))?;
  149. }
  150. <Result<()>>::Ok(())
  151. }));
  152. Ok(())
  153. }
  154. /// initialize app resources
  155. /// after tauri setup
  156. pub fn init_resources() -> Result<()> {
  157. let app_dir = dirs::app_home_dir()?;
  158. let res_dir = dirs::app_resources_dir()?;
  159. if !app_dir.exists() {
  160. let _ = fs::create_dir_all(&app_dir);
  161. }
  162. if !res_dir.exists() {
  163. let _ = fs::create_dir_all(&res_dir);
  164. }
  165. #[cfg(target_os = "windows")]
  166. let file_list = ["Country.mmdb", "geoip.dat", "geosite.dat"];
  167. #[cfg(not(target_os = "windows"))]
  168. let file_list = ["Country.mmdb", "geoip.dat", "geosite.dat"];
  169. // copy the resource file
  170. // if the source file is newer than the destination file, copy it over
  171. for file in file_list.iter() {
  172. let src_path = res_dir.join(file);
  173. let dest_path = app_dir.join(file);
  174. let handle_copy = || {
  175. match fs::copy(&src_path, &dest_path) {
  176. Ok(_) => log::debug!(target: "app", "resources copied '{file}'"),
  177. Err(err) => {
  178. log::error!(target: "app", "failed to copy resources '{file}', {err}")
  179. }
  180. };
  181. };
  182. if src_path.exists() && !dest_path.exists() {
  183. handle_copy();
  184. continue;
  185. }
  186. let src_modified = fs::metadata(&src_path).and_then(|m| m.modified());
  187. let dest_modified = fs::metadata(&dest_path).and_then(|m| m.modified());
  188. match (src_modified, dest_modified) {
  189. (Ok(src_modified), Ok(dest_modified)) => {
  190. if src_modified > dest_modified {
  191. handle_copy();
  192. } else {
  193. log::debug!(target: "app", "skipping resource copy '{file}'");
  194. }
  195. }
  196. _ => {
  197. log::debug!(target: "app", "failed to get modified '{file}'");
  198. handle_copy();
  199. }
  200. };
  201. }
  202. Ok(())
  203. }
  204. /// initialize service resources
  205. /// after tauri setup
  206. #[cfg(target_os = "windows")]
  207. pub fn init_service() -> Result<()> {
  208. let service_dir = dirs::service_dir()?;
  209. let res_dir = dirs::app_resources_dir()?;
  210. if !service_dir.exists() {
  211. let _ = fs::create_dir_all(&service_dir);
  212. }
  213. if !res_dir.exists() {
  214. let _ = fs::create_dir_all(&res_dir);
  215. }
  216. let file_list = [
  217. "clash-verge-service.exe",
  218. "install-service.exe",
  219. "uninstall-service.exe",
  220. ];
  221. // copy the resource file
  222. // if the source file is newer than the destination file, copy it over
  223. for file in file_list.iter() {
  224. let src_path = res_dir.join(file);
  225. let dest_path = service_dir.join(file);
  226. let handle_copy = || {
  227. match fs::copy(&src_path, &dest_path) {
  228. Ok(_) => log::debug!(target: "app", "resources copied '{file}'"),
  229. Err(err) => {
  230. log::error!(target: "app", "failed to copy resources '{file}', {err}")
  231. }
  232. };
  233. };
  234. if src_path.exists() && !dest_path.exists() {
  235. handle_copy();
  236. continue;
  237. }
  238. let src_modified = fs::metadata(&src_path).and_then(|m| m.modified());
  239. let dest_modified = fs::metadata(&dest_path).and_then(|m| m.modified());
  240. match (src_modified, dest_modified) {
  241. (Ok(src_modified), Ok(dest_modified)) => {
  242. if src_modified > dest_modified {
  243. handle_copy();
  244. } else {
  245. log::debug!(target: "app", "skipping resource copy '{file}'");
  246. }
  247. }
  248. _ => {
  249. log::debug!(target: "app", "failed to get modified '{file}'");
  250. handle_copy();
  251. }
  252. };
  253. }
  254. Ok(())
  255. }