feat(app): wire main window, DI services, and demo dataset

This commit is contained in:
dualfroz committed 2026-08-27 09:55:47 +02:00
1 parent c3fc019994
commit 90d047c29a
4 files changed
+156

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
using Vantage.Core.Alerts;
using Vantage.Core.Logs;
using Vantage.Core.Models;
namespace Vantage.App;
public static class DemoData
{
public static VantageConfig Build() => new()
{
RefreshIntervalSeconds = 3,
HistoryLength = 120,
AlertRules =
{
new AlertRule { Name = "CPU saturated", Metric = "cpu", Comparison = AlertComparison.GreaterOrEqual, Threshold = 80, For = TimeSpan.FromSeconds(30), Severity = AlertSeverity.Warning },
new AlertRule { Name = "Memory pressure", Metric = "memory", Comparison = AlertComparison.GreaterOrEqual, Threshold = 90, For = TimeSpan.FromMinutes(2), Severity = AlertSeverity.Critical },
new AlertRule { Name = "Disk almost full", Metric = "disk", Comparison = AlertComparison.GreaterOrEqual, Threshold = 90, For = TimeSpan.Zero, Severity = AlertSeverity.Critical }
},
LogSources =
{
LogSource.Journal("docker", "Docker"),
LogSource.Journal("ssh", "SSH"),
LogSource.File("/var/log/syslog", "syslog")
},
Hosts =
{
new HostConfig { Name = "edge-01", Address = "10.10.0.11", Username = "root", AuthMethod = SshAuthMethod.PrivateKey, PrivateKeyPath = "~/.ssh/id_ed25519" },
new HostConfig { Name = "storage-01", Address = "10.10.0.21", Username = "ops", AuthMethod = SshAuthMethod.PrivateKey, PrivateKeyPath = "~/.ssh/id_ed25519" },
new HostConfig { Name = "vps-main", Address = "vps.example.net", Username = "deploy", AuthMethod = SshAuthMethod.PrivateKey, PrivateKeyPath = "~/.ssh/id_ed25519" }
},
HealthChecks =
{
new HealthCheckConfig { Name = "Gateway HTTP", Kind = HealthKind.Http, Target = "https://example.com" },
new HealthCheckConfig { Name = "Local SSH", Kind = HealthKind.Tcp, Target = "127.0.0.1:22" },
new HealthCheckConfig { Name = "DNS TCP", Kind = HealthKind.Tcp, Target = "1.1.1.1:53" }
}
};
}
+31
View File
@@ -0,0 +1,31 @@
using Microsoft.Extensions.DependencyInjection;
using Vantage.App.ViewModels;
using Vantage.Core;
namespace Vantage.App;
public static class VantageAppServices
{
public static IServiceCollection AddVantageApp(this IServiceCollection services, bool useMockProviders) =>
services.AddVantageApp(useMockProviders ? VantageProviderMode.Mock : VantageProviderMode.Ssh);
public static IServiceCollection AddVantageApp(this IServiceCollection services, VantageProviderMode mode)
{
services.AddVantageCore(mode);
services.AddTransient<MainWindowViewModel>();
return services;
}
public static bool ResolveMockMode() => ResolveProviderMode() == VantageProviderMode.Mock;
public static VantageProviderMode ResolveProviderMode()
{
var mode = Environment.GetEnvironmentVariable("VANTAGE_PROVIDERS");
return mode?.ToLowerInvariant() switch
{
"ssh" => VantageProviderMode.Ssh,
"prometheus" => VantageProviderMode.Prometheus,
_ => VantageProviderMode.Mock
};
}
}
+78
View File
@@ -0,0 +1,78 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Vantage.App.ViewModels"
xmlns:views="clr-namespace:Vantage.App.Views"
xmlns:conv="clr-namespace:Vantage.App.Converters"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="Vantage.App.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
x:CompileBindings="True"
Width="1080" Height="720"
MinWidth="820" MinHeight="560"
Background="#12151B"
Title="Vantage - homelab dashboard">
<Grid ColumnDefinitions="260,*" RowDefinitions="*,Auto">
<Border Grid.Row="0" Grid.Column="0" Background="#161A22" BorderBrush="#2C323D" BorderThickness="0,0,1,0">
<Grid RowDefinitions="Auto,*">
<StackPanel Grid.Row="0" Margin="16,16,16,8" Spacing="2">
<TextBlock Text="VANTAGE" FontSize="18" FontWeight="Bold" Foreground="{DynamicResource AccentBrush}" />
<TextBlock Text="Hosts" Foreground="#8891A0" FontSize="12" />
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding Hosts}" SelectedItem="{Binding SelectedHost}"
Background="Transparent" Margin="8,0">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:HostViewModel">
<Grid ColumnDefinitions="Auto,*" Margin="4,6">
<Ellipse Grid.Column="0" Width="10" Height="10" VerticalAlignment="Center" Margin="0,0,10,0"
Fill="{Binding IsOnline, Converter={x:Static conv:StateBrushConverters.RunningBrush}}" />
<StackPanel Grid.Column="1">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" />
<TextBlock Text="{Binding Endpoint}" Foreground="#8891A0" FontSize="11" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Border>
<Grid Grid.Row="0" Grid.Column="1" RowDefinitions="Auto,*" Margin="16">
<DockPanel Grid.Row="0" Margin="0,0,0,12">
<Button DockPanel.Dock="Right" Content="Refresh now" Command="{Binding RefreshSelectedHostCommand}" />
<StackPanel Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
<TextBlock Text="Monitoring" Foreground="#8891A0" VerticalAlignment="Center" />
<Ellipse Width="9" Height="9" VerticalAlignment="Center"
Fill="{Binding IsMonitoring, Converter={x:Static conv:StateBrushConverters.RunningBrush}}" />
</StackPanel>
</DockPanel>
<TabControl Grid.Row="1">
<TabItem Header="Fleet">
<views:FleetView DataContext="{Binding}" />
</TabItem>
<TabItem Header="Host">
<views:HostMetricsView DataContext="{Binding SelectedHost}" />
</TabItem>
<TabItem Header="Alerts">
<views:AlertsView DataContext="{Binding}" />
</TabItem>
<TabItem Header="Docker">
<views:DockerView DataContext="{Binding Docker}" />
</TabItem>
<TabItem Header="Health">
<views:HealthView DataContext="{Binding}" />
</TabItem>
</TabControl>
</Grid>
<Border Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Background="#161A22" BorderBrush="#2C323D" BorderThickness="0,1,0,0" Padding="16,6">
<TextBlock Text="{Binding StatusLine}" Foreground="#8891A0" FontSize="12" />
</Border>
</Grid>
</Window>
@@ -0,0 +1,9 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Vantage.App.Views;
public partial class MainWindow : Window
{
public MainWindow() => AvaloniaXamlLoader.Load(this);
}