Files
OTSSignsOrchestrator/OTSSignsOrchestrator.Desktop/ViewModels/InstancesViewModel.cs
Matt Batchelder 45c94b6536
Some checks failed
Build and Publish Docker Image / build-and-push (push) Has been cancelled
feat: Add main application views and structure
- Implemented CreateInstanceView for creating new instances.
- Added HostsView for managing SSH hosts with CRUD operations.
- Created InstancesView for displaying and managing instances.
- Developed LogsView for viewing operation logs.
- Introduced SecretsView for managing secrets associated with hosts.
- Established SettingsView for configuring application settings.
- Created MainWindow as the main application window with navigation.
- Added app manifest and configuration files for logging and settings.
2026-02-18 10:43:27 -05:00

187 lines
6.0 KiB
C#

using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using OTSSignsOrchestrator.Core.Data;
using OTSSignsOrchestrator.Core.Models.Entities;
using OTSSignsOrchestrator.Core.Services;
using OTSSignsOrchestrator.Desktop.Services;
namespace OTSSignsOrchestrator.Desktop.ViewModels;
/// <summary>
/// ViewModel for listing, viewing, and managing CMS instances.
/// </summary>
public partial class InstancesViewModel : ObservableObject
{
private readonly IServiceProvider _services;
[ObservableProperty] private ObservableCollection<CmsInstance> _instances = new();
[ObservableProperty] private CmsInstance? _selectedInstance;
[ObservableProperty] private string _statusMessage = string.Empty;
[ObservableProperty] private bool _isBusy;
[ObservableProperty] private string _filterText = string.Empty;
[ObservableProperty] private ObservableCollection<StackInfo> _remoteStacks = new();
[ObservableProperty] private ObservableCollection<ServiceInfo> _selectedServices = new();
// Available SSH hosts for the dropdown
[ObservableProperty] private ObservableCollection<SshHost> _availableHosts = new();
[ObservableProperty] private SshHost? _selectedSshHost;
public InstancesViewModel(IServiceProvider services)
{
_services = services;
_ = InitAsync();
}
private async Task InitAsync()
{
await LoadHostsAsync();
await LoadInstancesAsync();
}
[RelayCommand]
private async Task LoadHostsAsync()
{
using var scope = _services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<XiboContext>();
var hosts = await db.SshHosts.OrderBy(h => h.Label).ToListAsync();
AvailableHosts = new ObservableCollection<SshHost>(hosts);
}
[RelayCommand]
private async Task LoadInstancesAsync()
{
IsBusy = true;
try
{
using var scope = _services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<XiboContext>();
var query = db.CmsInstances.Include(i => i.SshHost).AsQueryable();
if (!string.IsNullOrWhiteSpace(FilterText))
{
query = query.Where(i =>
i.CustomerName.Contains(FilterText) ||
i.StackName.Contains(FilterText));
}
var items = await query.OrderByDescending(i => i.CreatedAt).ToListAsync();
Instances = new ObservableCollection<CmsInstance>(items);
StatusMessage = $"Loaded {items.Count} instance(s).";
}
catch (Exception ex)
{
StatusMessage = $"Error: {ex.Message}";
}
finally
{
IsBusy = false;
}
}
[RelayCommand]
private async Task RefreshRemoteStacksAsync()
{
if (SelectedSshHost == null)
{
StatusMessage = "Select an SSH host first.";
return;
}
IsBusy = true;
StatusMessage = $"Listing stacks on {SelectedSshHost.Label}...";
try
{
var dockerCli = _services.GetRequiredService<SshDockerCliService>();
dockerCli.SetHost(SelectedSshHost);
var stacks = await dockerCli.ListStacksAsync();
RemoteStacks = new ObservableCollection<StackInfo>(stacks);
StatusMessage = $"Found {stacks.Count} stack(s) on {SelectedSshHost.Label}.";
}
catch (Exception ex)
{
StatusMessage = $"Error listing stacks: {ex.Message}";
}
finally
{
IsBusy = false;
}
}
[RelayCommand]
private async Task InspectInstanceAsync()
{
if (SelectedInstance == null) return;
if (SelectedSshHost == null && SelectedInstance.SshHost == null)
{
StatusMessage = "No SSH host associated with this instance.";
return;
}
IsBusy = true;
try
{
var host = SelectedInstance.SshHost ?? SelectedSshHost!;
var dockerCli = _services.GetRequiredService<SshDockerCliService>();
dockerCli.SetHost(host);
var services = await dockerCli.InspectStackServicesAsync(SelectedInstance.StackName);
SelectedServices = new ObservableCollection<ServiceInfo>(services);
StatusMessage = $"Found {services.Count} service(s) in stack '{SelectedInstance.StackName}'.";
}
catch (Exception ex)
{
StatusMessage = $"Error inspecting: {ex.Message}";
}
finally
{
IsBusy = false;
}
}
[RelayCommand]
private async Task DeleteInstanceAsync()
{
if (SelectedInstance == null) return;
IsBusy = true;
StatusMessage = $"Deleting {SelectedInstance.StackName}...";
try
{
var host = SelectedInstance.SshHost ?? SelectedSshHost;
if (host == null)
{
StatusMessage = "No SSH host available for deletion.";
return;
}
// Wire up SSH-based docker services
var dockerCli = _services.GetRequiredService<SshDockerCliService>();
dockerCli.SetHost(host);
var dockerSecrets = _services.GetRequiredService<SshDockerSecretsService>();
dockerSecrets.SetHost(host);
using var scope = _services.CreateScope();
var instanceSvc = scope.ServiceProvider.GetRequiredService<InstanceService>();
var result = await instanceSvc.DeleteInstanceAsync(SelectedInstance.Id);
StatusMessage = result.Success
? $"Instance '{SelectedInstance.StackName}' deleted."
: $"Delete failed: {result.ErrorMessage}";
await LoadInstancesAsync();
}
catch (Exception ex)
{
StatusMessage = $"Error deleting: {ex.Message}";
}
finally
{
IsBusy = false;
}
}
}