using ITNexusAgent.Models; using ITNexusAgent.Services; using Newtonsoft.Json; using System.Diagnostics; using System.Windows; using System.Windows.Threading; using WinForms = System.Windows.Forms; using DrawingIcon = System.Drawing.Icon; using DrawingSystemIcons = System.Drawing.SystemIcons; using MessageBox = System.Windows.MessageBox; using SolidColorBrush = System.Windows.Media.SolidColorBrush; using MediaColor = System.Windows.Media.Color; using ProgressBar = System.Windows.Controls.ProgressBar; namespace ITNexusAgent.UI; public partial class DashboardWindow : Window { private const string StatusPath = @"C:\ProgramData\IT Nexus Agent\status.json"; private const string LogPath = @"C:\ProgramData\IT Nexus Agent\agent.log"; private readonly DispatcherTimer _timer; private AgentConfig? _config; private string? _adminToken; private WinForms.NotifyIcon? _trayIcon; private List _allSoftware = []; private bool _softwarePlaceholderActive = true; public DashboardWindow() { InitializeComponent(); try { _config = AgentConfig.Load(@"C:\ProgramData\IT Nexus Agent\config.json"); } catch { } InitTrayIcon(); InitSoftwareSearch(); VersionLabel.Text = $"IT Nexus Agent v2.0.0 · Cereda Systems GmbH"; _timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) }; _timer.Tick += (_, _) => RefreshStatus(); _timer.Start(); RefreshStatus(); } private void InitSoftwareSearch() { SoftwareSearch.Text = "Software suchen..."; SoftwareSearch.Foreground = new SolidColorBrush(MediaColor.FromRgb(82, 82, 91)); SoftwareSearch.GotFocus += (_, _) => { if (_softwarePlaceholderActive) { _softwarePlaceholderActive = false; SoftwareSearch.Text = ""; SoftwareSearch.Foreground = new SolidColorBrush(MediaColor.FromRgb(250, 250, 250)); } }; SoftwareSearch.LostFocus += (_, _) => { if (string.IsNullOrWhiteSpace(SoftwareSearch.Text)) { _softwarePlaceholderActive = true; SoftwareSearch.Text = "Software suchen..."; SoftwareSearch.Foreground = new SolidColorBrush(MediaColor.FromRgb(82, 82, 91)); ApplySoftwareFilter(""); } }; } private void RefreshStatus() { try { if (!File.Exists(StatusPath)) { SetOffline("Noch kein Check-in"); return; } var json = File.ReadAllText(StatusPath); var status = JsonConvert.DeserializeObject(json); if (status == null) return; HostnameLabel.Text = $"{status.Hostname} · {status.LastUser}"; var ago = DateTime.Now - status.LastCheckin; var agoText = ago.TotalSeconds < 60 ? $"vor {(int)ago.TotalSeconds} Sekunden" : ago.TotalMinutes < 60 ? $"vor {(int)ago.TotalMinutes} Minuten" : $"vor {(int)ago.TotalHours} Stunden"; if (status.Online && ago.TotalMinutes < 3) { StatusDot.Fill = new SolidColorBrush(MediaColor.FromRgb(34, 197, 94)); StatusLabel.Text = "Online"; } else { StatusDot.Fill = new SolidColorBrush(MediaColor.FromRgb(239, 68, 68)); StatusLabel.Text = "Offline"; } // Metrics var cpu = status.CpuUsage ?? 0; CpuLabel.Text = $"{cpu:0}%"; CpuBar.Value = cpu; SetBarColor(CpuBar, cpu); if (status.RamTotal > 0) { var ramPct = (status.RamUsed ?? 0) / status.RamTotal.Value * 100; RamLabel.Text = $"{status.RamUsed:0.0}/{status.RamTotal:0} GB"; RamBar.Value = ramPct; SetBarColor(RamBar, ramPct); } if (status.DiskTotal > 0) { var diskUsed = status.DiskTotal.Value - (status.DiskFree ?? 0); var diskPct = diskUsed / status.DiskTotal.Value * 100; DiskLabel.Text = $"{diskUsed:0}/{status.DiskTotal:0} GB"; DiskBar.Value = diskPct; SetBarColor(DiskBar, diskPct); } // Security BitlockerLabel.Text = status.BitlockerStatus switch { "encrypted" => "✓ Verschlüsselt", "off" => "✗ Nicht aktiv", _ => "? Unbekannt" }; BitlockerLabel.Foreground = status.BitlockerStatus == "encrypted" ? new SolidColorBrush(MediaColor.FromRgb(22, 163, 74)) : new SolidColorBrush(MediaColor.FromRgb(239, 68, 68)); DefenderLabel.Text = status.DefenderEnabled ? status.DefenderSignaturesAge <= 3 ? "✓ Aktiv · Signaturen aktuell" : $"⚠ Aktiv · Signaturen {status.DefenderSignaturesAge}d alt" : "✗ Inaktiv"; DefenderLabel.Foreground = status.DefenderEnabled ? new SolidColorBrush(MediaColor.FromRgb(22, 163, 74)) : new SolidColorBrush(MediaColor.FromRgb(239, 68, 68)); UpdatesLabel.Text = status.PendingUpdates == 0 ? "✓ Aktuell" : $"⚠ {status.PendingUpdates} ausstehend"; UpdatesLabel.Foreground = status.PendingUpdates == 0 ? new SolidColorBrush(MediaColor.FromRgb(22, 163, 74)) : new SolidColorBrush(MediaColor.FromRgb(245, 158, 11)); SerialLabel.Text = string.IsNullOrEmpty(status.HardwareSerial) ? "—" : status.HardwareSerial; LastCheckinLabel.Text = $"Letzter Check-in: {agoText}"; // Software-Liste nur aktualisieren wenn sich die Anzahl geändert hat if (status.InstalledSoftware.Count != _allSoftware.Count || (status.InstalledSoftware.Count > 0 && _allSoftware.Count == 0)) { _allSoftware = status.InstalledSoftware; var filterText = _softwarePlaceholderActive ? "" : SoftwareSearch.Text; ApplySoftwareFilter(filterText); } } catch { SetOffline("Fehler beim Laden"); } } private void ApplySoftwareFilter(string filter) { var filtered = string.IsNullOrWhiteSpace(filter) ? _allSoftware : _allSoftware.Where(s => s.Contains(filter, StringComparison.OrdinalIgnoreCase)).ToList(); SoftwareList.ItemsSource = filtered; SoftwareCountLabel.Text = _allSoftware.Count > 0 ? $"{filtered.Count} / {_allSoftware.Count}" : ""; } private void SoftwareSearch_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) { if (_softwarePlaceholderActive) return; ApplySoftwareFilter(SoftwareSearch.Text); } private void SetOffline(string reason) { StatusDot.Fill = new SolidColorBrush(MediaColor.FromRgb(156, 163, 175)); StatusLabel.Text = reason; } private static void SetBarColor(System.Windows.Controls.ProgressBar bar, double pct) { var color = pct > 90 ? MediaColor.FromRgb(239, 68, 68) : pct > 70 ? MediaColor.FromRgb(245, 158, 11) : MediaColor.FromRgb(59, 130, 246); bar.Foreground = new SolidColorBrush(color); } private void WebButton_Click(object sender, RoutedEventArgs e) { var url = _config?.ServerUrl ?? "https://it-nexus.cereda-systems.de"; Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); } private async void MessageButton_Click(object sender, RoutedEventArgs e) { var dlg = new MessageDialog(); if (dlg.ShowDialog() != true || string.IsNullOrWhiteSpace(dlg.Message)) return; try { if (_config != null) { var api = new ApiService(_config.ServerUrl, _config.AgentKey); await api.SendMessageAsync(Environment.MachineName, dlg.Message); MessageBox.Show("Nachricht wurde gesendet.", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Information); } } catch { MessageBox.Show("Fehler beim Senden.", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Warning); } } private async void RebootButton_Click(object sender, RoutedEventArgs e) { var result = MessageBox.Show( "Möchtest du einen Neustart bei der IT anfordern?\nDein Rechner wird nicht sofort neu gestartet.", "Neustart anfordern", MessageBoxButton.YesNo, MessageBoxImage.Question); if (result != MessageBoxResult.Yes) return; try { if (_config != null) { var api = new ApiService(_config.ServerUrl, _config.AgentKey); await api.SendRebootRequestAsync(Environment.MachineName); MessageBox.Show("Neustart wurde angefordert. Die IT wird sich darum kümmern.", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Information); } } catch { } } private void AdminLoginButton_Click(object sender, RoutedEventArgs e) { if (_adminToken != null) { _adminToken = null; AdminPanel.Visibility = Visibility.Collapsed; AdminLoginButton.Content = "Admin-Modus"; return; } AdminUsernameBox.Text = ""; AdminPasswordBox.Password = ""; AdminErrorLabel.Visibility = Visibility.Collapsed; AdminOverlay.Visibility = Visibility.Visible; AdminUsernameBox.Focus(); } private void AdminCancelModal_Click(object sender, RoutedEventArgs e) => AdminOverlay.Visibility = Visibility.Collapsed; private async void AdminLoginModal_Click(object sender, RoutedEventArgs e) { var username = AdminUsernameBox.Text.Trim(); var password = AdminPasswordBox.Password; if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) return; AdminLoginBtn.IsEnabled = false; AdminErrorLabel.Visibility = Visibility.Collapsed; try { using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(10) }; var body = Newtonsoft.Json.JsonConvert.SerializeObject(new { username, password }); var resp = await http.PostAsync($"{_config?.ServerUrl?.TrimEnd('/')}/api/auth/login", new StringContent(body, Encoding.UTF8, "application/json")); var json = await resp.Content.ReadAsStringAsync(); var result = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(json, new { token = "" }); if (resp.IsSuccessStatusCode && !string.IsNullOrEmpty(result?.token)) { _adminToken = result.token; AdminOverlay.Visibility = Visibility.Collapsed; if (_pendingExit) { _pendingExit = false; DoExit(); return; } AdminPanel.Visibility = Visibility.Visible; AdminUserLabel.Text = $"Angemeldet als {username}"; AdminLoginButton.Content = "Admin-Modus beenden"; } else { AdminErrorLabel.Text = "Ungültige Anmeldedaten"; AdminErrorLabel.Visibility = Visibility.Visible; } } catch { AdminErrorLabel.Text = "Verbindung fehlgeschlagen"; AdminErrorLabel.Visibility = Visibility.Visible; } finally { AdminLoginBtn.IsEnabled = true; } } private void AdminPasswordBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) { if (e.Key == System.Windows.Input.Key.Enter) AdminLoginModal_Click(sender, new RoutedEventArgs()); } private void AdminCheckin_Click(object sender, RoutedEventArgs e) { try { Process.Start(new ProcessStartInfo("net.exe", "stop \"IT Nexus Agent\"") { CreateNoWindow = true, UseShellExecute = false })?.WaitForExit(); Process.Start(new ProcessStartInfo("net.exe", "start \"IT Nexus Agent\"") { CreateNoWindow = true, UseShellExecute = false }); MessageBox.Show("Agent wird neu gestartet — Check-in in ~5 Sekunden.", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Information); } catch (Exception ex) { MessageBox.Show($"Fehler: {ex.Message}", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Error); } } private void AdminUpdates_Click(object sender, RoutedEventArgs e) { Process.Start(new ProcessStartInfo("UsoClient.exe", "StartScan") { CreateNoWindow = true }); MessageBox.Show("Update-Scan gestartet.", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Information); } private void AdminLog_Click(object sender, RoutedEventArgs e) { var dlg = new LogWindow(); dlg.Show(); } private void AdminRestart_Click(object sender, RoutedEventArgs e) { try { Process.Start(new ProcessStartInfo("net.exe", "stop \"IT Nexus Agent\"") { CreateNoWindow = true, UseShellExecute = false })?.WaitForExit(); Process.Start(new ProcessStartInfo("net.exe", "start \"IT Nexus Agent\"") { CreateNoWindow = true, UseShellExecute = false }); MessageBox.Show("Agent-Service wird neu gestartet.", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Information); } catch (Exception ex) { MessageBox.Show($"Fehler: {ex.Message}", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Error); } } private void InitTrayIcon() { try { var iconPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "icon.ico"); var icon = File.Exists(iconPath) ? new DrawingIcon(iconPath) : DrawingSystemIcons.Application; _trayIcon = new WinForms.NotifyIcon { Icon = icon, Text = "IT Nexus Agent", Visible = true }; var menu = new WinForms.ContextMenuStrip(); menu.Items.Add("Dashboard öffnen", null, (s, e) => ShowDashboard()); menu.Items.Add("-"); menu.Items.Add("IT Nexus Web", null, (s, e) => { var url = _config?.ServerUrl ?? "https://it-nexus.cereda-systems.de"; Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); }); menu.Items.Add("-"); menu.Items.Add("Beenden", null, (s, e) => ExitWithAuth()); _trayIcon.ContextMenuStrip = menu; _trayIcon.DoubleClick += (s, e) => ShowDashboard(); } catch { } } private void ShowDashboard() { Show(); WindowState = WindowState.Normal; Activate(); Focus(); } protected override void OnClosing(System.ComponentModel.CancelEventArgs e) { e.Cancel = true; Hide(); } private void ExitWithAuth() { Dispatcher.Invoke(() => { if (_adminToken != null) { DoExit(); return; } AdminUsernameBox.Text = ""; AdminPasswordBox.Password = ""; AdminErrorLabel.Visibility = Visibility.Collapsed; AdminOverlay.Visibility = Visibility.Visible; ShowDashboard(); _pendingExit = true; AdminUsernameBox.Focus(); }); } private bool _pendingExit = false; private void DoExit() { _trayIcon!.Visible = false; System.Windows.Application.Current.Shutdown(); } private void TitleBar_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed) DragMove(); } private void Close_Click(object sender, RoutedEventArgs e) => Close(); private bool _isMaximized = false; private double _restoreLeft, _restoreTop, _restoreWidth, _restoreHeight; private void Minimize_Click(object sender, RoutedEventArgs e) => WindowState = WindowState.Minimized; private void Maximize_Click(object sender, RoutedEventArgs e) { if (_isMaximized) RestoreWindow(); else MaximizeWindow(); } private void MaximizeWindow() { _restoreLeft = Left; _restoreTop = Top; _restoreWidth = Width; _restoreHeight = Height; var wa = SystemParameters.WorkArea; Left = wa.Left; Top = wa.Top; Width = wa.Width; Height = wa.Height; _isMaximized = true; OuterBorder.CornerRadius = new CornerRadius(0); OuterBorder.Margin = new Thickness(0); OuterBorder.Effect = null; TitleBarBorder.CornerRadius = new CornerRadius(0); } private void RestoreWindow() { Left = _restoreLeft; Top = _restoreTop; Width = _restoreWidth; Height = _restoreHeight; _isMaximized = false; OuterBorder.CornerRadius = new CornerRadius(14); OuterBorder.Margin = new Thickness(16); OuterBorder.Effect = new System.Windows.Media.Effects.DropShadowEffect { Color = System.Windows.Media.Colors.Black, Opacity = 0.4, BlurRadius = 20, ShadowDepth = 0 }; TitleBarBorder.CornerRadius = new CornerRadius(14, 14, 0, 0); } private void Window_StateChanged(object sender, EventArgs e) { } protected override void OnClosed(EventArgs e) { _timer.Stop(); base.OnClosed(e); } }