namespace LehrerApp.Core.Services; public record BackupInfo(string Path, DateTime CreatedAt, long SizeBytes); /// /// Rollierendes lokales Backup der LiteDB-Datei /// (<AppData>/LehrerApp/backups/lehrerapp-JJJJ-MM-TT_HH-mm-ss.db). /// Reine Dateikopie — unabhängig davon, ob die Datenbank verschlüsselt ist. /// public class BackupService { private const int DefaultKeepCount = 10; private readonly string _backupDirectory; public string BackupDirectory => _backupDirectory; public BackupService(string appDataPath) { _backupDirectory = Path.Combine(appDataPath, "backups"); Directory.CreateDirectory(_backupDirectory); } /// Erstellt ein Backup der übergebenen Datenbankdatei, falls sie existiert. /// Gibt den Pfad des neuen Backups zurück, oder null, wenn keine Datei vorhanden war. public string? CreateBackup(string databasePath, int keepCount = DefaultKeepCount) { if (!File.Exists(databasePath)) return null; // Millisekunden + Zufallssuffix, damit mehrere Backups innerhalb derselben Sekunde // (z.B. mehrfaches Klicken auf "Jetzt sichern") sich nicht gegenseitig überschreiben. var suffix = Guid.NewGuid().ToString("N")[..8]; var fileName = $"lehrerapp-{DateTime.Now:yyyy-MM-dd_HH-mm-ss-fff}-{suffix}.db"; var target = Path.Combine(_backupDirectory, fileName); File.Copy(databasePath, target, overwrite: true); PruneOldBackups(keepCount); return target; } public List ListBackups() => Directory.GetFiles(_backupDirectory, "lehrerapp-*.db") .Select(p => new BackupInfo(p, File.GetLastWriteTime(p), new FileInfo(p).Length)) .OrderByDescending(b => b.CreatedAt) .ToList(); public void PruneOldBackups(int keepCount = DefaultKeepCount) { var backups = ListBackups(); foreach (var stale in backups.Skip(keepCount)) { try { File.Delete(stale.Path); } catch { /* nächster Lauf versucht es erneut */ } } } /// Kopiert ein Backup an die Zielposition (z.B. über die aktive Datenbankdatei). /// Der Aufrufer ist dafür verantwortlich, alle offenen Verbindungen vorher zu schließen. public void RestoreBackup(string backupPath, string databasePath) { if (!File.Exists(backupPath)) throw new FileNotFoundException("Backup nicht gefunden.", backupPath); File.Copy(backupPath, databasePath, overwrite: true); } }