new Dashboard
This commit is contained in:
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
// Local Namespaces
|
||||
using Watcher.Data;
|
||||
using Watcher.Models;
|
||||
using Watcher.ViewModels;
|
||||
|
||||
namespace Watcher.Controllers
|
||||
@@ -41,7 +42,17 @@ namespace Watcher.Controllers
|
||||
OfflineServers = await _context.Servers.CountAsync(s => !s.IsOnline),
|
||||
RunningContainers = await _context.Containers.CountAsync(c => c.IsRunning),
|
||||
FailedContainers = await _context.Containers.CountAsync(c => !c.IsRunning),
|
||||
LastLogin = user?.LastLogin ?? DateTime.MinValue
|
||||
LastLogin = user?.LastLogin ?? DateTime.MinValue,
|
||||
Servers = await _context.Servers
|
||||
.OrderBy(s => s.Name)
|
||||
.ToListAsync(),
|
||||
RecentEvents = await _context.LogEvents
|
||||
.OrderByDescending(e => e.Timestamp)
|
||||
.Take(20)
|
||||
.ToListAsync(),
|
||||
Containers = await _context.Containers
|
||||
.OrderBy(s => s.Name)
|
||||
.ToListAsync()
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
@@ -49,7 +60,7 @@ namespace Watcher.Controllers
|
||||
|
||||
// Funktion für /Views/Home/Index.cshtml um das DashboardStats-Partial neu zu laden.
|
||||
// Die Funktion wird nicht direkt aufgerufen, sondern nur der /Home/DashboardStats Endpoint angefragt.
|
||||
public IActionResult DashboardStats()
|
||||
public async Task<IActionResult> DashboardStats()
|
||||
{
|
||||
var servers = _context.Servers.ToList();
|
||||
var containers = _context.Containers.ToList();
|
||||
@@ -58,14 +69,20 @@ namespace Watcher.Controllers
|
||||
|
||||
var model = new DashboardViewModel
|
||||
{
|
||||
ActiveServers = servers.Count(s => (now - s.LastSeen).TotalSeconds <= 120),
|
||||
OfflineServers = servers.Count(s => (now - s.LastSeen).TotalSeconds > 120),
|
||||
|
||||
//TODO: anwendbar, wenn Container implementiert wurden.
|
||||
//RunningContainers = containers.Count(c => (now - c.LastSeen).TotalSeconds <= 120),
|
||||
//FailedContainers = containers.Count(c => (now - c.LastSeen).TotalSeconds > 120),
|
||||
|
||||
LastLogin = DateTime.Now
|
||||
ActiveServers = await _context.Servers.CountAsync(s => s.IsOnline),
|
||||
OfflineServers = await _context.Servers.CountAsync(s => !s.IsOnline),
|
||||
RunningContainers = await _context.Containers.CountAsync(c => c.IsRunning),
|
||||
FailedContainers = await _context.Containers.CountAsync(c => !c.IsRunning),
|
||||
Servers = await _context.Servers
|
||||
.OrderBy(s => s.Name)
|
||||
.ToListAsync(),
|
||||
RecentEvents = await _context.LogEvents
|
||||
.OrderByDescending(e => e.Timestamp)
|
||||
.Take(20)
|
||||
.ToListAsync(),
|
||||
Containers = await _context.Containers
|
||||
.OrderBy(s => s.Name)
|
||||
.ToListAsync()
|
||||
};
|
||||
|
||||
return PartialView("_DashboardStats", model);
|
||||
|
16
Watcher/Models/HealthStatus.cs
Normal file
16
Watcher/Models/HealthStatus.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Watcher.Models
|
||||
{
|
||||
public class HealthStatus
|
||||
{
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
public bool NetworkOk { get; set; }
|
||||
public bool DatabaseOk { get; set; }
|
||||
public List<string> Issues { get; set; } = new List<string>();
|
||||
// Optional weitere Checks
|
||||
public bool ApiOk { get; set; }
|
||||
public bool DiskOk { get; set; }
|
||||
}
|
||||
}
|
@@ -1,14 +1,12 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
using Serilog;
|
||||
|
||||
using Watcher.Data;
|
||||
using Watcher.Models;
|
||||
//using Watcher.Services;
|
||||
//using Watcher.Workers;
|
||||
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
@@ -32,7 +30,6 @@ builder.Host.UseSerilog();
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllersWithViews();
|
||||
|
||||
|
||||
// HttpContentAccessor
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
|
||||
@@ -112,7 +109,11 @@ builder.Services.AddAuthentication()
|
||||
var db = ctx.HttpContext.RequestServices.GetRequiredService<AppDbContext>();
|
||||
|
||||
var principal = ctx.Principal;
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
|
||||
var pocketId = principal.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value;
|
||||
#pragma warning restore CS8602 // Dereference of a possibly null reference.
|
||||
|
||||
var preferredUsername = principal.FindFirst("preferred_username")?.Value;
|
||||
var email = principal.FindFirst("email")?.Value;
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
using Watcher.Models;
|
||||
|
||||
namespace Watcher.ViewModels
|
||||
{
|
||||
public class DashboardViewModel
|
||||
@@ -7,5 +9,10 @@ namespace Watcher.ViewModels
|
||||
public int RunningContainers { get; set; }
|
||||
public int FailedContainers { get; set; }
|
||||
public DateTime LastLogin { get; set; }
|
||||
|
||||
public List<Server> Servers { get; set; } = new();
|
||||
public List<LogEvent> RecentEvents { get; set; } = new();
|
||||
public List<Container> Containers { get; set; } = new();
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -11,25 +11,6 @@
|
||||
@await Html.PartialAsync("_DashboardStats", Model)
|
||||
</div>
|
||||
|
||||
<div class="row g-4 mt-4">
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card p-3">
|
||||
<h2 class="h5">
|
||||
<i class="bi bi-person-circle me-2"></i>Systeminfo
|
||||
</h2>
|
||||
<p>
|
||||
<i class="bi bi-person-badge me-1"></i>
|
||||
Benutzer: <strong>@User.FindFirst("preferred_username")?.Value</strong>
|
||||
</p>
|
||||
<p>
|
||||
<i class="bi bi-clock me-1"></i>
|
||||
Letzter Login: <strong>@Model.LastLogin.ToString("g")</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
async function loadDashboardStats() {
|
||||
|
@@ -1,25 +1,170 @@
|
||||
@model Watcher.ViewModels.DashboardViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Dashboard";
|
||||
}
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="bg-white shadow rounded-3 p-4 h-100">
|
||||
<h2 class="h5 fw-semibold mb-2">Server</h2>
|
||||
<p>🟢 Online: <strong>@Model.ActiveServers</strong></p>
|
||||
<p>🔴 Offline: <strong>@Model.OfflineServers</strong></p>
|
||||
<a href="/Server/Overview" class="text-primary text-decoration-none mt-2 d-inline-block">
|
||||
→ Zu den Servern
|
||||
</a>
|
||||
<div class="container-fluid py-4">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="loadDashboardStats()">
|
||||
<i class="bi bi-arrow-clockwise me-1"></i> Aktualisieren
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- KPI Cards -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-12 col-sm-6 col-lg-3">
|
||||
<div class="card shadow-sm border-0 rounded-3 text-center h-100">
|
||||
<div class="card-body">
|
||||
<i class="bi bi-hdd-network text-success fs-2 mb-2"></i>
|
||||
<h6 class="text-muted">Server Online</h6>
|
||||
<h3 class="fw-bold">@Model.ActiveServers</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-lg-3">
|
||||
<div class="card shadow-sm border-0 rounded-3 text-center h-100">
|
||||
<div class="card-body">
|
||||
<i class="bi bi-hdd-network-fill text-danger fs-2 mb-2"></i>
|
||||
<h6 class="text-muted">Server Offline</h6>
|
||||
<h3 class="fw-bold">@Model.OfflineServers</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-lg-3">
|
||||
<div class="card shadow-sm border-0 rounded-3 text-center h-100">
|
||||
<div class="card-body">
|
||||
<i class="bi bi-box-seam text-primary fs-2 mb-2"></i>
|
||||
<h6 class="text-muted">Services Running</h6>
|
||||
<h3 class="fw-bold">@Model.RunningContainers</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-lg-3">
|
||||
<div class="card shadow-sm border-0 rounded-3 text-center h-100">
|
||||
<div class="card-body">
|
||||
<i class="bi bi-exclamation-triangle text-warning fs-2 mb-2"></i>
|
||||
<h6 class="text-muted">Service Warnungen</h6>
|
||||
<h3 class="fw-bold">@Model.FailedContainers</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="bg-white shadow rounded-3 p-4 h-100">
|
||||
<h2 class="h5 fw-semibold mb-2">Container</h2>
|
||||
<p>🟢 Laufend: <strong>@Model.RunningContainers</strong></p>
|
||||
<p>🔴 Fehlerhaft: <strong>@Model.FailedContainers</strong></p>
|
||||
<a href="/Container/Overview" class="text-primary text-decoration-none mt-2 d-inline-block">
|
||||
→ Zu den Containern
|
||||
</a>
|
||||
<!-- System Overview & Recent Events -->
|
||||
<div class="row g-4">
|
||||
<!-- System Health -->
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card shadow-sm border-0 rounded-3 h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="fw-bold mb-3"><i class="bi bi-heart-pulse me-2 text-danger"></i>Systemstatus</h5>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span>Netzwerk</span>
|
||||
<span class="badge bg-success">OK</span>
|
||||
</div>
|
||||
<div class="progress mb-3" style="height: 6px;">
|
||||
<div class="progress-bar bg-success w-100"></div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span>Datenbank</span>
|
||||
<span class="badge bg-success">OK</span>
|
||||
</div>
|
||||
<div class="progress mb-3" style="height: 6px;">
|
||||
<div class="progress-bar bg-success w-100"></div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span>API</span>
|
||||
<span class="badge bg-warning">Langsam</span>
|
||||
</div>
|
||||
<div class="progress" style="height: 6px;">
|
||||
<div class="progress-bar bg-warning w-75"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Events -->
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card shadow-sm border-0 rounded-3 h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="fw-bold mb-3"><i class="bi bi-activity me-2 text-primary"></i>Letzte Ereignisse</h5>
|
||||
<ul class="list-group list-group-flush small">
|
||||
@foreach (var log in Model.RecentEvents.Take(1))
|
||||
{
|
||||
<li class="list-group-item d-flex align-items-center">
|
||||
<i class="bi bi-dot fs-4 text-muted me-1"></i>
|
||||
<span class="text-muted me-2">@log.Timestamp.ToString("HH:mm:ss")</span>
|
||||
<span>@log</span>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Services -->
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="bg-white shadow rounded-3 p-4 h-100">
|
||||
<h2 class="h5 fw-semibold mb-3">Services</h2>
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach (var service in Model.Containers)
|
||||
{
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>@service.Name</span>
|
||||
<span class="badge @(service.Status == "Running" ? "bg-success" : "bg-warning")">
|
||||
@service.Status
|
||||
</span>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Server Liste -->
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="bg-white shadow rounded-3 p-4 h-100">
|
||||
<h2 class="h5 fw-semibold mb-3">Server</h2>
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach (var server in Model.Servers)
|
||||
{
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span>@server.Name</span>
|
||||
<span class="badge bg-info")">
|
||||
CPU: 30.45%
|
||||
</span>
|
||||
<span class="badge bg-info")">
|
||||
RAM: 65.09%
|
||||
</span>
|
||||
<span class="badge @(server.IsOnline ? "bg-success" : "bg-danger")">
|
||||
@(server.IsOnline ? "Online" : "Offline")
|
||||
</span>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
async function loadDashboardStats() {
|
||||
try {
|
||||
const response = await fetch('/Home/DashboardStats');
|
||||
if (response.ok) {
|
||||
const html = await response.text();
|
||||
document.getElementById('dashboard-stats').innerHTML = html;
|
||||
} else {
|
||||
console.error('Fehler beim Laden der Dashboard-Daten');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Netzwerkfehler:', err);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
|
@@ -71,10 +71,10 @@
|
||||
<a class="nav-link" href="/">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/Server/Overview">Servers</a>
|
||||
<a class="nav-link" href="#">Netzwerk</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/Container/Overview">Container</a>
|
||||
<a class="nav-link" href="/Container/Overview">Services</a>
|
||||
</li>
|
||||
<!-- Noch nicht implementiert
|
||||
<li class="nav-item"></li>
|
||||
|
Reference in New Issue
Block a user