basic Dashboard
This commit is contained in:
@@ -1,33 +1,41 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Watcher.Data;
|
||||||
using Watcher.Models;
|
using Watcher.Models;
|
||||||
|
using Watcher.ViewModels;
|
||||||
|
|
||||||
namespace Watcher.Controllers;
|
namespace Watcher.Controllers
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
public class HomeController : Controller
|
|
||||||
{
|
{
|
||||||
private readonly ILogger<HomeController> _logger;
|
[Authorize]
|
||||||
|
public class HomeController : Controller
|
||||||
public HomeController(ILogger<HomeController> logger)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
private readonly AppDbContext _context;
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult Index()
|
public HomeController(AppDbContext context)
|
||||||
{
|
{
|
||||||
return View();
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IActionResult Privacy()
|
public async Task<IActionResult> Index()
|
||||||
{
|
{
|
||||||
return View();
|
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
}
|
|
||||||
|
|
||||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
var user = await _context.Users
|
||||||
public IActionResult Error()
|
.Where(u => u.PocketId == userId)
|
||||||
{
|
.FirstOrDefaultAsync();
|
||||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
284
Watcher/Migrations/20250614183243_Server-Container-IsRunning-Value.Designer.cs
generated
Normal file
284
Watcher/Migrations/20250614183243_Server-Container-IsRunning-Value.Designer.cs
generated
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -35,6 +35,9 @@ namespace Watcher.Migrations
|
|||||||
b.Property<int?>("ImageId")
|
b.Property<int?>("ImageId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<bool>("IsRunning")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
@@ -149,6 +152,9 @@ namespace Watcher.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("IsOnline")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
@@ -13,4 +13,6 @@ public class Container
|
|||||||
public string Hostname { get; set; } = string.Empty;
|
public string Hostname { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string Type { get; set; } = "docker"; // z.B. "docker", "vm", "lxc", etc.
|
public string Type { get; set; } = "docker"; // z.B. "docker", "vm", "lxc", etc.
|
||||||
|
|
||||||
|
public Boolean IsRunning { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
@@ -14,4 +14,6 @@ public class Server
|
|||||||
|
|
||||||
// z.B. "VPS", "standalone", "VM", etc.
|
// z.B. "VPS", "standalone", "VM", etc.
|
||||||
public string Type { get; set; } = "VPS";
|
public string Type { get; set; } = "VPS";
|
||||||
|
|
||||||
|
public Boolean IsOnline { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
11
Watcher/ViewModels/DashboardViewModel.cs
Normal file
11
Watcher/ViewModels/DashboardViewModel.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,36 @@
|
|||||||
@{
|
@model Watcher.ViewModels.DashboardViewModel
|
||||||
|
@{
|
||||||
ViewData["Title"] = "Dashboard";
|
ViewData["Title"] = "Dashboard";
|
||||||
}
|
}
|
||||||
|
|
||||||
<h1>Dashboard</h1>
|
<h1 class="text-2xl font-bold mb-4">Dashboard</h1>
|
||||||
<p>Willkommen im Watcher Monitoring Interface!</p>
|
|
||||||
|
<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>
|
||||||
|
Reference in New Issue
Block a user