@@ -0,0 +1,110 @@
|
||||
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<string>();
|
||||
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 { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user