Merge pull request 'feature/Server-Details-Page' (#53) from feature/Server-Details-Page into development
Reviewed-on: daniel-hbn/Watcher#53
This commit is contained in:
@@ -29,32 +29,4 @@ public class ContainerController : Controller
|
|||||||
return View(viewModel);
|
return View(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public IActionResult AddContainer()
|
|
||||||
{
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<IActionResult> AddContainer(AddContainerViewModel vm)
|
|
||||||
{
|
|
||||||
if (!ModelState.IsValid)
|
|
||||||
return View(vm);
|
|
||||||
|
|
||||||
var container = new Container
|
|
||||||
{
|
|
||||||
Name = vm.Name,
|
|
||||||
Image = vm.Image,
|
|
||||||
Status = vm.Status,
|
|
||||||
Hostname = vm.Hostname,
|
|
||||||
IsRunning = vm.IsRunning,
|
|
||||||
CreatedAt = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
_context.Containers.Add(container);
|
|
||||||
await _context.SaveChangesAsync();
|
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -116,13 +115,41 @@ public class ServerController : Controller
|
|||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET: Server/Details/5
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Details(int id)
|
||||||
|
{
|
||||||
|
|
||||||
|
var s = await _context.Servers.FindAsync(id);
|
||||||
|
if (s == null) return NotFound();
|
||||||
|
|
||||||
|
var vm = new ServerDetailsViewModel
|
||||||
|
{
|
||||||
|
Id = s.Id,
|
||||||
|
Name = s.Name,
|
||||||
|
IPAddress = s.IPAddress,
|
||||||
|
Type = s.Type,
|
||||||
|
Description = s.Description,
|
||||||
|
CpuType = s.CpuType,
|
||||||
|
CpuCores = s.CpuCores,
|
||||||
|
GpuType = s.GpuType,
|
||||||
|
RamSize = s.RamSize,
|
||||||
|
CreatedAt = s.CreatedAt,
|
||||||
|
IsOnline = s.IsOnline,
|
||||||
|
LastSeen = s.LastSeen,
|
||||||
|
IsVerified = s.IsVerified
|
||||||
|
};
|
||||||
|
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> ServerCardsPartial()
|
public async Task<IActionResult> ServerCardsPartial()
|
||||||
{
|
{
|
||||||
var servers = _context.Servers.ToList();
|
var servers = _context.Servers.ToList();
|
||||||
|
|
||||||
foreach (var server in servers)
|
foreach (var server in servers)
|
||||||
{
|
{
|
||||||
server.IsOnline = (DateTime.UtcNow - server.LastSeen).TotalSeconds <= 120;
|
server.IsOnline = (DateTime.UtcNow - server.LastSeen).TotalSeconds <= 120;
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,17 +4,22 @@ public class Container
|
|||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
public string Name { get; set; } = string.Empty;
|
// Container Details
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
public int ExposedPort { get; set; }
|
||||||
|
|
||||||
|
public int InternalPort { get; set; }
|
||||||
|
|
||||||
public string Status { get; set; } = string.Empty;
|
public string Status { get; set; } = string.Empty;
|
||||||
|
|
||||||
public Image? Image { get; set; }
|
public string Health { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string? Image { get; set; }
|
||||||
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
public string Hostname { get; set; } = string.Empty;
|
public Boolean IsRunning { get; set; } = true;
|
||||||
|
|
||||||
public string Type { get; set; } = "docker"; // z.B. "docker", "vm", "lxc", etc.
|
public Server? HostServer { get; set; }
|
||||||
|
|
||||||
public Boolean IsRunning { get; set; } = false;
|
|
||||||
}
|
}
|
||||||
|
41
Watcher/ViewModels/ServerDetailsViewModel.cs
Normal file
41
Watcher/ViewModels/ServerDetailsViewModel.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Watcher.ViewModels;
|
||||||
|
|
||||||
|
public class ServerDetailsViewModel
|
||||||
|
{
|
||||||
|
// System Infos
|
||||||
|
[Required]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public required string Name { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public required string IPAddress { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public required string Type { get; set; }
|
||||||
|
|
||||||
|
public string? Description { get; set; } = String.Empty;
|
||||||
|
|
||||||
|
|
||||||
|
// Hardware Infos
|
||||||
|
public string? CpuType { get; set; } = string.Empty;
|
||||||
|
public int CpuCores { get; set; } = 0;
|
||||||
|
public string? GpuType { get; set; } = string.Empty;
|
||||||
|
public double RamSize { get; set; } = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// Database
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
|
public Boolean IsOnline { get; set; } = false;
|
||||||
|
|
||||||
|
public DateTime LastSeen { get; set; }
|
||||||
|
|
||||||
|
public Boolean IsVerified { get; set; } = false;
|
||||||
|
}
|
@@ -1,22 +0,0 @@
|
|||||||
@model Watcher.ViewModels.AddContainerViewModel
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Neuen Container hinzufügen";
|
|
||||||
}
|
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold mb-4">Neuen Container hinzufügen</h1>
|
|
||||||
|
|
||||||
<form asp-action="Add" method="post" class="space-y-4 max-w-xl">
|
|
||||||
<div>
|
|
||||||
<label asp-for="Name" class="block font-medium">Name</label>
|
|
||||||
<input asp-for="Name" class="w-full border rounded px-3 py-2" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label asp-for="Image" class="block font-medium">Image</label>
|
|
||||||
<input asp-for="Image" class="w-full border rounded px-3 py-2" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label asp-for="ServerName" class="block font-medium">Server</label>
|
|
||||||
<input asp-for="ServerName" class="w-full border rounded px-3 py-2" />
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700">Speichern</button>
|
|
||||||
</form>
|
|
@@ -3,33 +3,14 @@
|
|||||||
ViewData["Title"] = "Containerübersicht";
|
ViewData["Title"] = "Containerübersicht";
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="flex items-center justify-between mb-6">
|
|
||||||
<h1 class="text-3xl font-bold">Containerübersicht</h1>
|
|
||||||
<a asp-action="AddContainer" class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700">
|
|
||||||
+ Container hinzufügen
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
@foreach (var container in Model.Containers)
|
@foreach (var container in Model.Containers)
|
||||||
{
|
{
|
||||||
<div class="bg-white shadow-md rounded-xl p-5 border border-gray-200 hover:shadow-lg transition duration-200">
|
<div class="bg-white shadow-md rounded-xl p-5 border border-gray-200 hover:shadow-lg transition duration-200">
|
||||||
<h2 class="text-xl font-semibold mb-1">@container.Name</h2>
|
<h2 class="text-xl font-semibold mb-1">@container.Name</h2>
|
||||||
<p class="text-sm text-gray-600 mb-2"><strong>Image:</strong> @container.Image</p>
|
<p class="text-sm text-gray-600 mb-2"><strong>Image:</strong> @container.Image</p>
|
||||||
<p class="text-sm text-gray-600"><strong>Hostname:</strong> @container.Hostname</p>
|
|
||||||
<p class="text-sm text-gray-600"><strong>Status:</strong> @container.Status</p>
|
<p class="text-sm text-gray-600"><strong>Status:</strong> @(container.IsRunning ? "online" : "offline")</p>
|
||||||
<p class="text-sm text-gray-600 mb-3">
|
|
||||||
<strong>Läuft:</strong>
|
|
||||||
<span class="@(container.IsRunning ? "text-green-600" : "text-red-600")">
|
|
||||||
@(container.IsRunning ? "Ja" : "Nein")
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<div class="flex justify-end gap-2">
|
|
||||||
<a asp-action="Edit" asp-route-id="@container.Id"
|
|
||||||
class="text-blue-600 hover:underline text-sm">Bearbeiten</a>
|
|
||||||
<a asp-action="Delete" asp-route-id="@container.Id"
|
|
||||||
class="text-red-600 hover:underline text-sm">Löschen</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -12,17 +12,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-4 mt-4">
|
<div class="row g-4 mt-4">
|
||||||
<div class="col-12">
|
|
||||||
<div class="card p-3">
|
|
||||||
<h2 class="h5">
|
|
||||||
<i class="bi bi-graph-up me-2"></i>Uptime letzte 24h
|
|
||||||
</h2>
|
|
||||||
<div class="bg-secondary rounded p-5 text-center text-muted">
|
|
||||||
<i class="bi bi-bar-chart-line" style="font-size: 2rem;"></i><br />
|
|
||||||
(Diagramm folgt hier)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card p-3">
|
<div class="card p-3">
|
||||||
|
34
Watcher/Views/Server/Details.cshtml
Normal file
34
Watcher/Views/Server/Details.cshtml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
@model Watcher.ViewModels.ServerDetailsViewModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Serverübersicht";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div id="server-cards-container">
|
||||||
|
@await Html.PartialAsync("_ServerDetails")
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<script src="~/js/server_uptime.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function loadServerStats() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/Server/ServerDetailsPartial');
|
||||||
|
if (response.ok) {
|
||||||
|
const html = await response.text();
|
||||||
|
document.getElementById('server-cards-container').innerHTML = html;
|
||||||
|
} else {
|
||||||
|
console.error('Fehler beim Nachladen der Serverdaten');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Netzwerkfehler beim Nachladen der Serverdaten:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial laden und dann alle 30 Sekunden
|
||||||
|
loadServerStats();
|
||||||
|
setInterval(loadServerCards, 30000);
|
||||||
|
</script>
|
||||||
|
}
|
@@ -30,29 +30,18 @@
|
|||||||
<div><i class="bi bi-memory me-1"></i><strong>RAM:</strong> @(s.RamSize) </div>
|
<div><i class="bi bi-memory me-1"></i><strong>RAM:</strong> @(s.RamSize) </div>
|
||||||
<div><i class="bi bi-hdd me-1"></i><strong>Disk Space:</strong> ... </div>
|
<div><i class="bi bi-hdd me-1"></i><strong>Disk Space:</strong> ... </div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-7">
|
|
||||||
<div class="card bg-secondary text-white p-3 h-100 d-flex flex-column justify-content-center align-items-center">
|
|
||||||
<h6 class="mb-3"><i class="bi bi-graph-up me-2"></i>Metrics letzte 24h</h6>
|
|
||||||
<div style="width: 100%; height: 250px;">
|
|
||||||
<canvas id="uptimeChart-@s.Id"></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex flex-wrap gap-2">
|
<div class="d-flex flex-wrap gap-2">
|
||||||
<a href="/Download/File/Linux/heartbeat" class="btn btn-success">
|
|
||||||
🖥️ Linux Agent
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a asp-action="#" asp-route-id="@s.Id" class="btn btn-outline-primary">
|
|
||||||
<i class="bi bi-pencil-square me-1"></i> Metrics
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a asp-action="EditServer" asp-route-id="@s.Id" class="btn btn-outline-primary">
|
<a asp-action="EditServer" asp-route-id="@s.Id" class="btn btn-outline-primary">
|
||||||
<i class="bi bi-pencil-square me-1"></i> Bearbeiten
|
<i class="bi bi-pencil-square me-1"></i> Bearbeiten
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a asp-asp-controller="Server" asp-action="Details" asp-route-id="@s.Id" class="btn btn-outline-primary">
|
||||||
|
<i class="bi bi-bar-chart-fill me-1"></i> Metrics
|
||||||
|
</a>
|
||||||
|
|
||||||
<form asp-action="Delete" asp-route-id="@s.Id" method="post" onsubmit="return confirm('Diesen Server wirklich löschen?');" class="m-0">
|
<form asp-action="Delete" asp-route-id="@s.Id" method="post" onsubmit="return confirm('Diesen Server wirklich löschen?');" class="m-0">
|
||||||
<button type="submit" class="btn btn-outline-danger">
|
<button type="submit" class="btn btn-outline-danger">
|
||||||
<i class="bi bi-trash me-1"></i> Löschen
|
<i class="bi bi-trash me-1"></i> Löschen
|
||||||
|
67
Watcher/Views/Server/_ServerDetails.cshtml
Normal file
67
Watcher/Views/Server/_ServerDetails.cshtml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<div class="container mt-4">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0">
|
||||||
|
<i class="bi bi-hdd-network me-2 text-primary"></i>Serverdetails: @Model.Name
|
||||||
|
</h5>
|
||||||
|
<span class="badge @(Model.IsOnline ? "bg-success" : "bg-danger")">
|
||||||
|
<i class="bi @(Model.IsOnline ? "bi-check-circle" : "bi-x-circle") me-1"></i>
|
||||||
|
@(Model.IsOnline ? "Online" : "Offline")
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<dl class="row mb-0">
|
||||||
|
<dt class="col-sm-3">ID</dt>
|
||||||
|
<dd class="col-sm-9">@Model.Id</dd>
|
||||||
|
|
||||||
|
<dt class="col-sm-3">IP-Adresse</dt>
|
||||||
|
<dd class="col-sm-9">@Model.IPAddress</dd>
|
||||||
|
|
||||||
|
<dt class="col-sm-3">Typ</dt>
|
||||||
|
<dd class="col-sm-9">@Model.Type</dd>
|
||||||
|
|
||||||
|
<dt class="col-sm-3">Erstellt am</dt>
|
||||||
|
<dd class="col-sm-9">@Model.CreatedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")</dd>
|
||||||
|
|
||||||
|
<dt class="col-sm-3">Zuletzt gesehen</dt>
|
||||||
|
<dd class="col-sm-9">@Model.LastSeen.ToLocalTime().ToString("dd.MM.yyyy HH:mm")</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-end">
|
||||||
|
<a href="/Download/File/Linux/heartbeat" class="btn btn-success">
|
||||||
|
🖥️ Linux Agent
|
||||||
|
</a>
|
||||||
|
<a asp-action="EditServer" asp-route-id="@Model.Id" class="btn btn-outline-primary me-2">
|
||||||
|
<i class="bi bi-pencil"></i> Bearbeiten
|
||||||
|
</a>
|
||||||
|
<form asp-action="Delete" asp-route-id="@Model.Id" method="post" class="d-inline"
|
||||||
|
onsubmit="return confirm('Diesen Server wirklich löschen?');">
|
||||||
|
<button type="submit" class="btn btn-outline-danger">
|
||||||
|
<i class="bi bi-trash"></i> Löschen
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Optional: Bereich für Logs, Diagramme oder weitere Details -->
|
||||||
|
<div class="mt-4">
|
||||||
|
<h6><i class="bi bi-graph-up me-1"></i>Uptime letzte 24h</h6>
|
||||||
|
<div class="bg-light border rounded p-4 text-center text-muted" style="height: 250px;">
|
||||||
|
<canvas id="uptimeChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<h6><i class="bi bi-graph-up me-1"></i>CPU Last</h6>
|
||||||
|
<div class="bg-light border rounded p-4 text-center text-muted" style="height: 250px;">
|
||||||
|
<canvas id="uptimeChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4"></div>
|
||||||
|
<h6><i class="bi bi-graph-up me-1"></i>RAM Last</h6>
|
||||||
|
<div class="bg-light border rounded p-4 text-center text-muted" style="height: 250px;">
|
||||||
|
<canvas id="uptimeChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -73,10 +73,10 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/Server/Overview">Servers</a>
|
<a class="nav-link" href="/Server/Overview">Servers</a>
|
||||||
</li>
|
</li>
|
||||||
<!-- Noch nicht implementiert
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/Container/Overview">Container</a>
|
<a class="nav-link" href="/Container/Overview">Container</a>
|
||||||
</li>
|
</li>
|
||||||
|
<!-- Noch nicht implementiert
|
||||||
<li class="nav-item"></li>
|
<li class="nav-item"></li>
|
||||||
<a class="nav-link" href="/Uptime/Overview">Uptime</a>
|
<a class="nav-link" href="/Uptime/Overview">Uptime</a>
|
||||||
</li>
|
</li>
|
||||||
|
Reference in New Issue
Block a user