112 lines
4.0 KiB
C#
112 lines
4.0 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.Runtime.Versioning;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
using WebhookServer.Core.Ipc;
|
|
using WebhookServer.Gui.Services;
|
|
|
|
namespace WebhookServer.Gui.ViewModels;
|
|
|
|
[SupportedOSPlatform("windows")]
|
|
public sealed partial class ConfigCheckpointsViewModel : ObservableObject
|
|
{
|
|
private readonly AdminPipeClient _client;
|
|
|
|
public ObservableCollection<BackupEntry> Checkpoints { get; } = new();
|
|
|
|
[ObservableProperty] private BackupEntry? _selected;
|
|
[ObservableProperty] private string _statusMessage = "";
|
|
|
|
public ConfigCheckpointsViewModel(AdminPipeClient client)
|
|
{
|
|
_client = client;
|
|
}
|
|
|
|
[RelayCommand]
|
|
public async Task RefreshAsync()
|
|
{
|
|
try
|
|
{
|
|
var list = await _client.ListBackupsAsync().ConfigureAwait(false);
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
{
|
|
Checkpoints.Clear();
|
|
foreach (var b in list) Checkpoints.Add(b);
|
|
StatusMessage = list.Count == 0
|
|
? "No checkpoints yet. Save the config or click Take Checkpoint Now."
|
|
: $"{list.Count} checkpoint{(list.Count == 1 ? "" : "s")}.";
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Application.Current.Dispatcher.Invoke(() => StatusMessage = $"Could not load: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
[RelayCommand]
|
|
private async Task TakeCheckpointAsync()
|
|
{
|
|
// Prompt for an optional description on the UI thread.
|
|
string? description = null;
|
|
var prompted = Application.Current.Dispatcher.Invoke(() =>
|
|
{
|
|
var dlg = new Views.TakeCheckpointDialog { Owner = Application.Current.MainWindow };
|
|
if (dlg.ShowDialog() != true) return false;
|
|
description = string.IsNullOrWhiteSpace(dlg.Description) ? null : dlg.Description;
|
|
return true;
|
|
});
|
|
if (!prompted) return;
|
|
|
|
try
|
|
{
|
|
var entry = await _client.CreateCheckpointAsync(description).ConfigureAwait(false);
|
|
await RefreshAsync().ConfigureAwait(false);
|
|
if (entry is not null)
|
|
{
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
{
|
|
Selected = Checkpoints.FirstOrDefault(c => c.FileName == entry.FileName);
|
|
StatusMessage = $"Created {entry.FileName}";
|
|
});
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
MessageBox.Show(ex.Message, "Take checkpoint failed", MessageBoxButton.OK, MessageBoxImage.Error));
|
|
}
|
|
}
|
|
|
|
[RelayCommand]
|
|
private async Task RollbackAsync()
|
|
{
|
|
if (Selected is null) return;
|
|
|
|
// Capture before the refresh; the ObservableCollection.Clear() in
|
|
// RefreshAsync nulls Selected (the original instance is gone from the
|
|
// collection so the SelectedItem binding clears).
|
|
var fileName = Selected.FileName;
|
|
var savedAt = Selected.SavedAt;
|
|
|
|
var ok = MessageBox.Show(
|
|
$"Roll the configuration back to the checkpoint from {savedAt.ToLocalTime():yyyy-MM-dd HH:mm:ss}?\n\nThe current configuration is automatically saved as a new checkpoint first, so you can roll forward again.",
|
|
"Confirm rollback",
|
|
MessageBoxButton.OKCancel,
|
|
MessageBoxImage.Warning);
|
|
if (ok != MessageBoxResult.OK) return;
|
|
|
|
try
|
|
{
|
|
await _client.RestoreBackupAsync(fileName).ConfigureAwait(false);
|
|
await RefreshAsync().ConfigureAwait(false);
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
StatusMessage = $"Rolled back to {fileName}.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
MessageBox.Show(ex.Message, "Rollback failed", MessageBoxButton.OK, MessageBoxImage.Error));
|
|
}
|
|
}
|
|
}
|