fix(files): report success cancellation and errors with safe next actions (U02)

This commit is contained in:
2026-11-02 06:23:59 +01:00
parent a3c1bee09e
commit 5e0ad197f0
15 changed files with 288 additions and 200 deletions
@@ -1,3 +1,4 @@
using Duempelkas.App.Services.Models;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Duempelkas.App.Services;
@@ -9,30 +10,35 @@ public class BackupService : IBackupService
{
private readonly DatabaseLocation location;
public BackupService(DatabaseLocation location) => this.location = location;
public async Task<string> CreateBackupAsync()
private readonly IFileDialogService dialogs;
public BackupService(DatabaseLocation location, IFileDialogService? dialogs = null)
{
try
{
var path = await ShowSaveDialogAsync($"duempelkas-backup-{DateTime.Now:yyyyMMdd-HHmmss}.bak");
if (string.IsNullOrWhiteSpace(path)) return "Sicherung wurde abgebrochen.";
await CreateBackupToAsync(path);
return $"Sicherung gespeichert: {path}";
}
catch (Exception ex) { return $"Sicherung fehlgeschlagen: {ex.Message}"; }
this.location = location;
this.dialogs = dialogs ?? new FileDialogService();
}
public async Task<string> RestoreBackupAsync()
public async Task<FileOperationResult> CreateBackupAsync()
{
try
{
var path = await ShowOpenDialogAsync();
if (string.IsNullOrWhiteSpace(path)) return "Wiederherstellung wurde abgebrochen.";
var fallback = await RestoreFromAsync(path);
return $"Wiederherstellung erfolgreich. Rückfallsicherung: {fallback}";
var path = await dialogs.SaveAsync($"duempelkas-backup-{DateTime.Now:yyyyMMdd-HHmmss}.bak");
if (string.IsNullOrWhiteSpace(path)) return FileOperationResult.Cancelled();
await CreateBackupToAsync(path);
return new(FileOperationStatus.Success, "Sicherung gespeichert.", path);
}
catch (Exception ex) { return $"Wiederherstellung fehlgeschlagen: {ex.Message}"; }
catch (Exception ex) { return FileOperationResult.Error(ex); }
}
public async Task<FileOperationResult> RestoreBackupAsync()
{
try
{
var path = await dialogs.OpenBackupAsync();
if (string.IsNullOrWhiteSpace(path)) return FileOperationResult.Cancelled();
var fallback = await RestoreFromAsync(path);
return new(FileOperationStatus.Success, "Sicherung zurückgespielt. Die bisherigen Daten liegen in der Rückfallsicherung:", fallback);
}
catch (Exception ex) { return FileOperationResult.Error(ex); }
}
public async Task CreateBackupToAsync(string path)
@@ -124,74 +130,4 @@ public class BackupService : IBackupService
_ = db.TransferLinks.ToArray();
}
private static Task<string?> 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<string?> 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;
}
}