basic Dashboard

This commit is contained in:
2025-06-14 20:35:42 +02:00
parent 30758bbd88
commit 1597409365
8 changed files with 409 additions and 26 deletions

View File

@@ -1,33 +1,41 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using Watcher.Data;
using Watcher.Models;
using Watcher.ViewModels;
namespace Watcher.Controllers;
[Authorize]
public class HomeController : Controller
namespace Watcher.Controllers
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
[Authorize]
public class HomeController : Controller
{
_logger = logger;
}
private readonly AppDbContext _context;
public IActionResult Index()
{
return View();
}
public HomeController(AppDbContext context)
{
_context = context;
}
public IActionResult Privacy()
{
return View();
}
public async Task<IActionResult> Index()
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
var user = await _context.Users
.Where(u => u.PocketId == userId)
.FirstOrDefaultAsync();
var viewModel = new DashboardViewModel
{
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),
LastLogin = user?.LastLogin ?? DateTime.MinValue
};
return View(viewModel);
}
}
}

View File

@@ -0,0 +1,284 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Watcher.Data;
#nullable disable
namespace Watcher.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20250614183243_Server-Container-IsRunning-Value")]
partial class ServerContainerIsRunningValue
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.6")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("Watcher.Models.Container", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Hostname")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("ImageId")
.HasColumnType("int");
b.Property<bool>("IsRunning")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("TagId")
.HasColumnType("int");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ImageId");
b.HasIndex("TagId");
b.ToTable("Containers");
});
modelBuilder.Entity("Watcher.Models.Image", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Name")
.HasColumnType("longtext");
b.Property<string>("Tag")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("Images");
});
modelBuilder.Entity("Watcher.Models.LogEvent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int?>("ContainerId")
.HasColumnType("int");
b.Property<string>("Level")
.HasColumnType("longtext");
b.Property<string>("Message")
.HasColumnType("longtext");
b.Property<int?>("ServerId")
.HasColumnType("int");
b.Property<DateTime>("Timestamp")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("ContainerId");
b.HasIndex("ServerId");
b.ToTable("LogEvents");
});
modelBuilder.Entity("Watcher.Models.Metric", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int?>("ContainerId")
.HasColumnType("int");
b.Property<int?>("ServerId")
.HasColumnType("int");
b.Property<DateTime>("Timestamp")
.HasColumnType("datetime(6)");
b.Property<string>("Type")
.HasColumnType("longtext");
b.Property<double>("Value")
.HasColumnType("double");
b.HasKey("Id");
b.HasIndex("ContainerId");
b.HasIndex("ServerId");
b.ToTable("Metrics");
});
modelBuilder.Entity("Watcher.Models.Server", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Hostname")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsOnline")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("TagId")
.HasColumnType("int");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("TagId");
b.ToTable("Servers");
});
modelBuilder.Entity("Watcher.Models.Tag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Name")
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("Tags");
});
modelBuilder.Entity("Watcher.Models.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Email")
.HasColumnType("longtext");
b.Property<DateTime>("LastLogin")
.HasColumnType("datetime(6)");
b.Property<string>("PocketId")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("PreferredUsername")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("Watcher.Models.Container", b =>
{
b.HasOne("Watcher.Models.Image", null)
.WithMany("Containers")
.HasForeignKey("ImageId");
b.HasOne("Watcher.Models.Tag", null)
.WithMany("Containers")
.HasForeignKey("TagId");
});
modelBuilder.Entity("Watcher.Models.LogEvent", b =>
{
b.HasOne("Watcher.Models.Container", "Container")
.WithMany()
.HasForeignKey("ContainerId");
b.HasOne("Watcher.Models.Server", "Server")
.WithMany()
.HasForeignKey("ServerId");
b.Navigation("Container");
b.Navigation("Server");
});
modelBuilder.Entity("Watcher.Models.Metric", b =>
{
b.HasOne("Watcher.Models.Container", "Container")
.WithMany()
.HasForeignKey("ContainerId");
b.HasOne("Watcher.Models.Server", "Server")
.WithMany()
.HasForeignKey("ServerId");
b.Navigation("Container");
b.Navigation("Server");
});
modelBuilder.Entity("Watcher.Models.Server", b =>
{
b.HasOne("Watcher.Models.Tag", null)
.WithMany("Servers")
.HasForeignKey("TagId");
});
modelBuilder.Entity("Watcher.Models.Image", b =>
{
b.Navigation("Containers");
});
modelBuilder.Entity("Watcher.Models.Tag", b =>
{
b.Navigation("Containers");
b.Navigation("Servers");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Watcher.Migrations
{
/// <inheritdoc />
public partial class ServerContainerIsRunningValue : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsOnline",
table: "Servers",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "IsRunning",
table: "Containers",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsOnline",
table: "Servers");
migrationBuilder.DropColumn(
name: "IsRunning",
table: "Containers");
}
}
}

View File

@@ -35,6 +35,9 @@ namespace Watcher.Migrations
b.Property<int?>("ImageId")
.HasColumnType("int");
b.Property<bool>("IsRunning")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
@@ -149,6 +152,9 @@ namespace Watcher.Migrations
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("IsOnline")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");

View File

@@ -13,4 +13,6 @@ public class Container
public string Hostname { get; set; } = string.Empty;
public string Type { get; set; } = "docker"; // z.B. "docker", "vm", "lxc", etc.
public Boolean IsRunning { get; set; } = false;
}

View File

@@ -13,5 +13,7 @@ public class Server
public string Hostname { get; set; } = string.Empty;
// z.B. "VPS", "standalone", "VM", etc.
public string Type { get; set; } = "VPS";
public string Type { get; set; } = "VPS";
public Boolean IsOnline { get; set; } = false;
}

View File

@@ -0,0 +1,11 @@
namespace Watcher.ViewModels
{
public class DashboardViewModel
{
public int ActiveServers { get; set; }
public int OfflineServers { get; set; }
public int RunningContainers { get; set; }
public int FailedContainers { get; set; }
public DateTime LastLogin { get; set; }
}
}

View File

@@ -1,6 +1,36 @@
@{
@model Watcher.ViewModels.DashboardViewModel
@{
ViewData["Title"] = "Dashboard";
}
<h1>Dashboard</h1>
<p>Willkommen im Watcher Monitoring Interface!</p>
<h1 class="text-2xl font-bold mb-4">Dashboard</h1>
<div class="grid grid-cols-2 gap-6">
<div class="bg-white shadow rounded-2xl p-4">
<h2 class="text-xl font-semibold mb-2">Server</h2>
<p>🟢 Online: <strong>@Model.ActiveServers</strong></p>
<p>🔴 Offline: <strong>@Model.OfflineServers</strong></p>
<a href="/Servers" class="text-blue-500 hover:underline mt-2 inline-block">→ Zu den Servern</a>
</div>
<div class="bg-white shadow rounded-2xl p-4">
<h2 class="text-xl font-semibold mb-2">Container</h2>
<p>🟢 Laufend: <strong>@Model.RunningContainers</strong></p>
<p>🔴 Fehlerhaft: <strong>@Model.FailedContainers</strong></p>
<a href="/Containers" class="text-blue-500 hover:underline mt-2 inline-block">→ Zu den Containern</a>
</div>
<div class="col-span-2 bg-white shadow rounded-2xl p-4">
<h2 class="text-xl font-semibold mb-2">Uptime letzte 24h</h2>
<div class="bg-gray-100 h-32 rounded-lg flex items-center justify-center text-gray-500">
(Diagramm folgt hier)
</div>
</div>
<div class="col-span-2 bg-white shadow rounded-2xl p-4">
<h2 class="text-xl font-semibold mb-2">Systeminfo</h2>
<p>Benutzer: <strong>@User.FindFirst("preferred_username")?.Value</strong></p>
<p>Letzter Login: <strong>@Model.LastLogin.ToString("g")</strong></p>
<a href="/Auth/Info" class="text-blue-500 hover:underline mt-2 inline-block">→ Account-Verwaltung</a>
</div>
</div>