using System.Diagnostics; using System.Runtime.InteropServices; using Duempelkas.App.Services; using Microsoft.Data.Sqlite; namespace Duempelkas.Infrastructure.Services; public class BackupService : IBackupService { private static readonly string DbPath = Path.Combine( Path.GetDirectoryName(Environment.ProcessPath ?? AppContext.BaseDirectory) ?? AppContext.BaseDirectory, "duempelkas.db"); public async Task CreateBackupAsync() { if (!File.Exists(DbPath)) { return "Keine Datenbankdatei gefunden."; } var suggestedFileName = $"duempelkas-backup-{DateTime.Now:yyMMdd-HHmm}.bak"; var backupPath = await ShowSaveDialogAsync(suggestedFileName); if (string.IsNullOrWhiteSpace(backupPath)) { return "Backup wurde abgebrochen."; } backupPath = Path.ChangeExtension(backupPath, ".bak"); try { File.Copy(DbPath, backupPath, overwrite: true); return $"Backup gespeichert: {backupPath}"; } catch (Exception ex) { return $"Backup fehlgeschlagen: {ex.Message}"; } } public async Task RestoreBackupAsync() { var backupPath = await ShowOpenDialogAsync(); if (string.IsNullOrWhiteSpace(backupPath)) { return "Wiederherstellung wurde abgebrochen."; } if (!File.Exists(backupPath)) { return "Die gewählte Backup-Datei wurde nicht gefunden."; } try { // Ensure no pooled connection keeps stale handles to the previous DB file. SqliteConnection.ClearAllPools(); var targetDirectory = Path.GetDirectoryName(DbPath); if (!string.IsNullOrWhiteSpace(targetDirectory)) { Directory.CreateDirectory(targetDirectory); } File.Copy(backupPath, DbPath, overwrite: true); SqliteConnection.ClearAllPools(); return "Wiederherstellung erfolgreich. Die Datenbank wurde neu geladen."; } catch (Exception ex) { return $"Wiederherstellung fehlgeschlagen: {ex.Message}"; } } private static Task ShowSaveDialogAsync(string suggestedFileName) { return Task.Run(() => { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); return Path.Combine(documentsPath, suggestedFileName); } var script = $@" Add-Type -AssemblyName System.Windows.Forms $dialog = New-Object System.Windows.Forms.SaveFileDialog $dialog.Filter = 'Backup-Dateien (*.bak)|*.bak' $dialog.DefaultExt = 'bak' $dialog.AddExtension = $true $dialog.OverwritePrompt = $true $dialog.FileName = '{suggestedFileName.Replace("'", "''")}' $dialog.Title = 'Backup speichern' if ($dialog.ShowDialog() -eq 'OK') {{ $dialog.FileName }} else {{ '' }} "; return RunPowerShellDialog(script); }); } private static Task ShowOpenDialogAsync() { return Task.Run(() => { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return null; } var script = @" Add-Type -AssemblyName System.Windows.Forms $dialog = New-Object System.Windows.Forms.OpenFileDialog $dialog.Filter = 'Backup-Dateien (*.bak)|*.bak' $dialog.Multiselect = $false $dialog.Title = 'Backup auswählen' if ($dialog.ShowDialog() -eq 'OK') { $dialog.FileName } else { '' } "; return RunPowerShellDialog(script); }); } private static string? RunPowerShellDialog(string script) { var psi = new ProcessStartInfo { FileName = "powershell", Arguments = $"-NoProfile -Command \"{script.Replace("\"", "\\\"") }\"", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }; using var process = Process.Start(psi); if (process == null) { return null; } var result = process.StandardOutput.ReadToEnd().Trim(); process.WaitForExit(); return string.IsNullOrWhiteSpace(result) ? null : result; } }