Owner decision pending (STATE.md DC-100 tick): adopt/ship/park. Preserved from fragile git stash to named branch 2026-08-23. NOTE: requires a Windows build machine (WinUI XAML compiler + MSIX do not cross-build on Linux) — see WINDOWS_APP_BUILD.md in this tree. Secret-scanned clean 2026-08-23 (no keys/tokens/PEM in tree).
166 lines
5.6 KiB
C#
166 lines
5.6 KiB
C#
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using DashCaddy.Desktop.Models;
|
|
using DashCaddy.Desktop.Services;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Generic;
|
|
|
|
namespace DashCaddy.Desktop.ViewModels;
|
|
|
|
public partial class MainViewModel : ObservableObject
|
|
{
|
|
private readonly DockerService _dockerService;
|
|
private readonly ApiClient _apiClient;
|
|
private readonly CaddyConfigGenerator _caddyGenerator;
|
|
private readonly DnsClient _dnsClient;
|
|
|
|
[ObservableProperty]
|
|
private ObservableCollection<ServiceViewModel> _services = new();
|
|
|
|
[ObservableProperty]
|
|
private int _servicesHealthy;
|
|
|
|
[ObservableProperty]
|
|
private int _servicesTotal;
|
|
|
|
[ObservableProperty]
|
|
private bool _dnsHealthy = true;
|
|
|
|
[ObservableProperty]
|
|
private bool _certsHealthy = true;
|
|
|
|
[ObservableProperty]
|
|
private int _certsDaysRemaining = 45;
|
|
|
|
// For dialogs
|
|
public ObservableCollection<ServiceTemplate> Templates { get; } = new(TemplateRegistry.GetAll());
|
|
public List<VariableViewModel> TemplateVariables { get; set; } = new();
|
|
public Dictionary<string, string> TemplateVariablesDict { get; set; } = new();
|
|
public ServiceTemplate SelectedTemplate { get; set; }
|
|
public string SelectedComposeFile { get; set; }
|
|
public object CustomService { get; set; }
|
|
|
|
public string ServicesSummary => $"{ServicesHealthy}/{ServicesTotal} running";
|
|
public string CertsSummary => $"{CertsDaysRemaining} days";
|
|
public string DashboardUrl => "https://status.local";
|
|
|
|
public MainViewModel(DockerService dockerService, ApiClient apiClient, CaddyConfigGenerator caddyGenerator, DnsClient dnsClient)
|
|
{
|
|
_dockerService = dockerService;
|
|
_apiClient = apiClient;
|
|
_caddyGenerator = caddyGenerator;
|
|
_dnsClient = dnsClient;
|
|
}
|
|
|
|
public async Task InitializeAsync()
|
|
{
|
|
await RefreshServicesAsync();
|
|
await CheckHealthAsync();
|
|
}
|
|
|
|
public async Task RefreshServicesAsync()
|
|
{
|
|
try
|
|
{
|
|
var services = await _apiClient.GetServicesAsync();
|
|
Services.Clear();
|
|
foreach (var svc in services)
|
|
{
|
|
Services.Add(new ServiceViewModel
|
|
{
|
|
Id = svc.Id,
|
|
Name = svc.Name,
|
|
Type = svc.Type,
|
|
Url = svc.Url,
|
|
Health = Enum.TryParse<ServiceHealth>(svc.Health, true, out var h) ? h : ServiceHealth.Unknown,
|
|
IsRunning = svc.State == "running",
|
|
Port = svc.Port,
|
|
Host = svc.Host
|
|
});
|
|
}
|
|
ServicesHealthy = Services.Count(s => s.Health == ServiceHealth.Healthy);
|
|
ServicesTotal = Services.Count;
|
|
}
|
|
catch
|
|
{
|
|
await RefreshFromDockerAsync();
|
|
}
|
|
}
|
|
|
|
private async Task RefreshFromDockerAsync()
|
|
{
|
|
var containers = await _dockerService.GetContainersAsync("dashcaddy");
|
|
Services.Clear();
|
|
foreach (var c in containers)
|
|
{
|
|
Services.Add(new ServiceViewModel
|
|
{
|
|
Id = c.ID[..12],
|
|
Name = c.Names.FirstOrDefault()?.TrimStart('/') ?? "unknown",
|
|
Type = "docker",
|
|
Url = $"http://localhost:{c.Ports.FirstOrDefault()?.PublicPort ?? 0}",
|
|
Health = c.State == "running" ? ServiceHealth.Healthy : ServiceHealth.Unhealthy,
|
|
IsRunning = c.State == "running"
|
|
});
|
|
}
|
|
ServicesHealthy = Services.Count(s => s.Health == ServiceHealth.Healthy);
|
|
ServicesTotal = Services.Count;
|
|
}
|
|
|
|
public async Task CheckHealthAsync()
|
|
{
|
|
try
|
|
{
|
|
var health = await _apiClient.GetHealthAsync();
|
|
DnsHealthy = health.Dns == "healthy";
|
|
CertsHealthy = health.Certs == "healthy";
|
|
CertsDaysRemaining = health.CertsDaysRemaining;
|
|
}
|
|
catch
|
|
{
|
|
DnsHealthy = false;
|
|
CertsHealthy = false;
|
|
}
|
|
}
|
|
|
|
public async Task StartServiceAsync(string id) => await _dockerService.StartContainerAsync(id);
|
|
public async Task StopServiceAsync(string id) => await _dockerService.StopContainerAsync(id);
|
|
public async Task RestartServiceAsync(string id) => await _dockerService.RestartContainerAsync(id);
|
|
|
|
public async Task RemoveServiceAsync(string id)
|
|
{
|
|
await _dockerService.RemoveContainerAsync(id);
|
|
await _apiClient.RemoveServiceAsync(id);
|
|
_caddyGenerator.RegenerateConfig(Services.Select(s => s.ToServiceModel()).ToList());
|
|
await _caddyGenerator.ReloadCaddyAsync();
|
|
}
|
|
|
|
public async Task ImportComposeAsync(string filePath)
|
|
{
|
|
var json = await File.ReadAllTextAsync(filePath);
|
|
var services = ComposeParser.ParseJson(json);
|
|
|
|
foreach (var svc in services)
|
|
{
|
|
await _apiClient.CreateServiceAsync(svc);
|
|
await _dockerService.CreateContainerAsync(svc);
|
|
}
|
|
|
|
_caddyGenerator.RegenerateConfig(Services.Select(s => s.ToServiceModel()).ToList());
|
|
await _caddyGenerator.ReloadCaddyAsync();
|
|
}
|
|
|
|
public async Task<ServiceModel> CreateServiceFromTemplateAsync()
|
|
{
|
|
if (SelectedTemplate == null) return null;
|
|
|
|
var service = SelectedTemplate.Instantiate(TemplateVariablesDict);
|
|
|
|
await _apiClient.CreateServiceAsync(service);
|
|
await _dockerService.CreateContainerAsync(service);
|
|
|
|
_caddyGenerator.RegenerateConfig(Services.Select(s => s.ToServiceModel()).ToList());
|
|
await _caddyGenerator.ReloadCaddyAsync();
|
|
|
|
return service;
|
|
}
|
|
} |