using Elwig.Helpers; using Elwig.Helpers.Export; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; namespace Elwig.Services { public static class BackupService { public static bool IsAnyBackupUpToDate { get { var lastDbWrite = File.GetLastWriteTime(App.Config.DatabaseFile); lastDbWrite += new TimeSpan(0, 0, -1); return Directory.GetFiles(App.Config.BackupPath, "????-??-??_??-??-??.*.zip") .Any(n => DateTime.ParseExact(Path.GetFileName(n)[..19], "yyyy-MM-dd_HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None) >= lastDbWrite); } } public static async Task TryDatabaseBackup() { if (IsAnyBackupUpToDate) return; await BackupDatabase(); PruneBackups(); } public static async Task BackupDatabase() { await Database.ExportSql(Path.Combine(App.Config.BackupPath, $"{File.GetLastWriteTime(App.Config.DatabaseFile):yyyy-MM-dd_HH-mm-ss}.sql.zip"), true); } public static void PruneBackups() { var retain = new HashSet(); var backups = Directory.GetFiles(App.Config.BackupPath, "????-??-??_??-??-??.*.zip") .Select(n => (FileName: n, Timestamp: DateTime.ParseExact(Path.GetFileName(n)[..19], "yyyy-MM-dd_HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None))) .OrderByDescending(t => t.Timestamp).ToArray(); DateTime now; // retain hourly backups now = DateTime.Now; for (int i = 0; i < App.Config.BackupRetainHours; i++, now = now.AddHours(-1)) { foreach (var b in backups) { if (b.Timestamp.Date == now.Date && b.Timestamp.Hour == now.Hour) { retain.Add(b.FileName); break; } } } // retain daily backups now = DateTime.Now; for (int i = 0; i < App.Config.BackupRetainDays; i++, now = now.AddDays(-1)) { foreach (var b in backups) { if (b.Timestamp.Date == now.Date) { retain.Add(b.FileName); break; } } } // retain weekly backups now = DateTime.Now; for (int i = 0; i < App.Config.BackupRetainWeeks; i++, now = now.AddDays(-7)) { foreach (var b in backups) { if (b.Timestamp.GetYearAndWeek() == now.GetYearAndWeek()) { retain.Add(b.FileName); break; } } } // retain monthly backups now = DateTime.Now; for (int i = 0; i < App.Config.BackupRetainMonths; i++, now = now.AddMonths(-1)) { foreach (var b in backups) { if (b.Timestamp.Year == now.Year && b.Timestamp.Month == now.Month) { retain.Add(b.FileName); break; } } } // retain tertial backups now = new DateTime(DateTime.Now.Year + 1, 3, 1); for (int i = 0; i < 600; i++, now = now.AddMonths(-4)) { foreach (var b in backups) { if (b.Timestamp.GetYearAndTertial() == now.GetYearAndTertial()) { retain.Add(b.FileName); break; } } } // delete non-retained files foreach (var b in backups) { if (retain.Contains(b.FileName)) continue; try { File.Delete(b.FileName); } catch { } } // delete temp files foreach (var f in Directory.GetFiles(App.Config.BackupPath, "*.tmp")) { try { File.Delete(f); } catch { } } } } }