feat: Add main application views and structure
Some checks failed
Build and Publish Docker Image / build-and-push (push) Has been cancelled
Some checks failed
Build and Publish Docker Image / build-and-push (push) Has been cancelled
- 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.
This commit is contained in:
186
OTSSignsOrchestrator.Desktop/ViewModels/InstancesViewModel.cs
Normal file
186
OTSSignsOrchestrator.Desktop/ViewModels/InstancesViewModel.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user