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:
243
OTSSignsOrchestrator.Desktop/ViewModels/HostsViewModel.cs
Normal file
243
OTSSignsOrchestrator.Desktop/ViewModels/HostsViewModel.cs
Normal file
@@ -0,0 +1,243 @@
|
||||
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.Desktop.Services;
|
||||
|
||||
namespace OTSSignsOrchestrator.Desktop.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel for managing SSH host connections.
|
||||
/// Allows adding, editing, testing, and removing remote Docker Swarm hosts.
|
||||
/// </summary>
|
||||
public partial class HostsViewModel : ObservableObject
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
[ObservableProperty] private ObservableCollection<SshHost> _hosts = new();
|
||||
[ObservableProperty] private SshHost? _selectedHost;
|
||||
[ObservableProperty] private bool _isEditing;
|
||||
[ObservableProperty] private string _statusMessage = string.Empty;
|
||||
[ObservableProperty] private bool _isBusy;
|
||||
|
||||
// Edit form fields
|
||||
[ObservableProperty] private string _editLabel = string.Empty;
|
||||
[ObservableProperty] private string _editHost = string.Empty;
|
||||
[ObservableProperty] private int _editPort = 22;
|
||||
[ObservableProperty] private string _editUsername = string.Empty;
|
||||
[ObservableProperty] private string _editPrivateKeyPath = string.Empty;
|
||||
[ObservableProperty] private string _editKeyPassphrase = string.Empty;
|
||||
[ObservableProperty] private string _editPassword = string.Empty;
|
||||
[ObservableProperty] private bool _editUseKeyAuth = true;
|
||||
private Guid? _editingHostId;
|
||||
|
||||
public HostsViewModel(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
_ = LoadHostsAsync();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task LoadHostsAsync()
|
||||
{
|
||||
IsBusy = true;
|
||||
try
|
||||
{
|
||||
using var scope = _services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<XiboContext>();
|
||||
var hosts = await db.SshHosts.OrderBy(h => h.Label).ToListAsync();
|
||||
|
||||
Hosts = new ObservableCollection<SshHost>(hosts);
|
||||
StatusMessage = $"Loaded {hosts.Count} host(s).";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"Error loading hosts: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NewHost()
|
||||
{
|
||||
_editingHostId = null;
|
||||
EditLabel = string.Empty;
|
||||
EditHost = string.Empty;
|
||||
EditPort = 22;
|
||||
EditUsername = string.Empty;
|
||||
EditPrivateKeyPath = string.Empty;
|
||||
EditKeyPassphrase = string.Empty;
|
||||
EditPassword = string.Empty;
|
||||
EditUseKeyAuth = true;
|
||||
IsEditing = true;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void EditSelectedHost()
|
||||
{
|
||||
if (SelectedHost == null) return;
|
||||
|
||||
_editingHostId = SelectedHost.Id;
|
||||
EditLabel = SelectedHost.Label;
|
||||
EditHost = SelectedHost.Host;
|
||||
EditPort = SelectedHost.Port;
|
||||
EditUsername = SelectedHost.Username;
|
||||
EditPrivateKeyPath = SelectedHost.PrivateKeyPath ?? string.Empty;
|
||||
EditKeyPassphrase = string.Empty; // Don't show existing passphrase
|
||||
EditPassword = string.Empty; // Don't show existing password
|
||||
EditUseKeyAuth = SelectedHost.UseKeyAuth;
|
||||
IsEditing = true;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CancelEdit()
|
||||
{
|
||||
IsEditing = false;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SaveHostAsync()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(EditLabel) || string.IsNullOrWhiteSpace(EditHost) || string.IsNullOrWhiteSpace(EditUsername))
|
||||
{
|
||||
StatusMessage = "Label, Host, and Username are required.";
|
||||
return;
|
||||
}
|
||||
|
||||
IsBusy = true;
|
||||
try
|
||||
{
|
||||
using var scope = _services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<XiboContext>();
|
||||
|
||||
SshHost host;
|
||||
if (_editingHostId.HasValue)
|
||||
{
|
||||
host = await db.SshHosts.FindAsync(_editingHostId.Value)
|
||||
?? throw new KeyNotFoundException("Host not found.");
|
||||
|
||||
host.Label = EditLabel;
|
||||
host.Host = EditHost;
|
||||
host.Port = EditPort;
|
||||
host.Username = EditUsername;
|
||||
host.PrivateKeyPath = string.IsNullOrWhiteSpace(EditPrivateKeyPath) ? null : EditPrivateKeyPath;
|
||||
host.UseKeyAuth = EditUseKeyAuth;
|
||||
host.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
if (!string.IsNullOrEmpty(EditKeyPassphrase))
|
||||
host.KeyPassphrase = EditKeyPassphrase;
|
||||
if (!string.IsNullOrEmpty(EditPassword))
|
||||
host.Password = EditPassword;
|
||||
}
|
||||
else
|
||||
{
|
||||
host = new SshHost
|
||||
{
|
||||
Label = EditLabel,
|
||||
Host = EditHost,
|
||||
Port = EditPort,
|
||||
Username = EditUsername,
|
||||
PrivateKeyPath = string.IsNullOrWhiteSpace(EditPrivateKeyPath) ? null : EditPrivateKeyPath,
|
||||
KeyPassphrase = string.IsNullOrEmpty(EditKeyPassphrase) ? null : EditKeyPassphrase,
|
||||
Password = string.IsNullOrEmpty(EditPassword) ? null : EditPassword,
|
||||
UseKeyAuth = EditUseKeyAuth
|
||||
};
|
||||
db.SshHosts.Add(host);
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
IsEditing = false;
|
||||
StatusMessage = $"Host '{host.Label}' saved.";
|
||||
await LoadHostsAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"Error saving host: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteHostAsync()
|
||||
{
|
||||
if (SelectedHost == null) return;
|
||||
|
||||
IsBusy = true;
|
||||
try
|
||||
{
|
||||
using var scope = _services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<XiboContext>();
|
||||
|
||||
var host = await db.SshHosts.FindAsync(SelectedHost.Id);
|
||||
if (host != null)
|
||||
{
|
||||
db.SshHosts.Remove(host);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// Disconnect if connected
|
||||
var ssh = _services.GetRequiredService<SshConnectionService>();
|
||||
ssh.Disconnect(SelectedHost.Id);
|
||||
|
||||
StatusMessage = $"Host '{SelectedHost.Label}' deleted.";
|
||||
await LoadHostsAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"Error deleting host: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task TestConnectionAsync()
|
||||
{
|
||||
if (SelectedHost == null) return;
|
||||
|
||||
IsBusy = true;
|
||||
StatusMessage = $"Testing connection to {SelectedHost.Label}...";
|
||||
try
|
||||
{
|
||||
var ssh = _services.GetRequiredService<SshConnectionService>();
|
||||
var (success, message) = await ssh.TestConnectionAsync(SelectedHost);
|
||||
|
||||
// Update DB
|
||||
using var scope = _services.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<XiboContext>();
|
||||
var host = await db.SshHosts.FindAsync(SelectedHost.Id);
|
||||
if (host != null)
|
||||
{
|
||||
host.LastTestedAt = DateTime.UtcNow;
|
||||
host.LastTestSuccess = success;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
StatusMessage = success
|
||||
? $"✓ {SelectedHost.Label}: {message}"
|
||||
: $"✗ {SelectedHost.Label}: {message}";
|
||||
|
||||
await LoadHostsAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"Connection test error: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user