134 lines
5.2 KiB
C#
134 lines
5.2 KiB
C#
using Elwig.Helpers;
|
|
using Elwig.Helpers.Export;
|
|
using Microsoft.Win32;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Input;
|
|
|
|
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 { }
|
|
}
|
|
}
|
|
|
|
public static async Task RestoreDatabase() {
|
|
try {
|
|
var d = new OpenFileDialog() {
|
|
Title = "Datenbank wiederherstellen - Elwig",
|
|
DefaultDirectory = App.Config.BackupPath,
|
|
InitialDirectory = App.Config.BackupPath,
|
|
DefaultExt = "sql.zip",
|
|
Filter = "SQLite-Datenbank (*.sqlite3, *.sqlite3.zip, *.sql, *.sql.zip)|*.sqlite3;*.sqlite3.zip;*.sql;*.sql.zip",
|
|
};
|
|
if (d.ShowDialog() == true) {
|
|
if (!InteractionService.AskContinue("Datenbank wiederherstellen", "Soll die Datenbank wirklich unwiederruflich durch die wiederhergestellte Version ersetzt werden?"))
|
|
return;
|
|
Mouse.OverrideCursor = Cursors.Wait;
|
|
await App.ReplaceDatabase(d.FileName);
|
|
}
|
|
} catch (Exception exc) {
|
|
InteractionService.ShowException(exc);
|
|
}
|
|
Mouse.OverrideCursor = null;
|
|
}
|
|
}
|
|
}
|