Merge pull request 'feature/Graphen' (#63) from feature/Graphen into development
Reviewed-on: daniel-hbn/Watcher#63
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
|||||||
# Per-Use special Files and Directories
|
# Per-Use special Files and Directories
|
||||||
/persistance/*.db
|
/persistance/*.db
|
||||||
|
/persistance/*.db-shm
|
||||||
|
/persistance/*.db-wal
|
||||||
/logs/*.log
|
/logs/*.log
|
||||||
*.env
|
*.env
|
||||||
/wwwroot/downloads/sqlite/*.sql
|
/wwwroot/downloads/sqlite/*.sql
|
||||||
|
@@ -215,7 +215,7 @@ public class MonitoringController : Controller
|
|||||||
|
|
||||||
|
|
||||||
// Durchschnittliche Werte Berechnen
|
// Durchschnittliche Werte Berechnen
|
||||||
[HttpGet]
|
[HttpGet("median")]
|
||||||
public async Task<IActionResult> CalculateMedian(string Metric, int HoursToMonitor, int ServerId)
|
public async Task<IActionResult> CalculateMedian(string Metric, int HoursToMonitor, int ServerId)
|
||||||
{
|
{
|
||||||
// Aktuelle Zeit - X Stunden = letzter Wert, der berücksichtigt werden soll
|
// Aktuelle Zeit - X Stunden = letzter Wert, der berücksichtigt werden soll
|
||||||
@@ -230,4 +230,55 @@ public class MonitoringController : Controller
|
|||||||
|
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("cpu-usage")]
|
||||||
|
public async Task<IActionResult> GetCpuUsageData(int serverId)
|
||||||
|
{
|
||||||
|
var oneDayAgo = DateTime.UtcNow.AddDays(-1);
|
||||||
|
var data = await _context.Metrics
|
||||||
|
.Where(m => m.Timestamp >= oneDayAgo && m.ServerId == serverId)
|
||||||
|
.OrderBy(m => m.Timestamp)
|
||||||
|
.Select(m => new
|
||||||
|
{
|
||||||
|
label = m.Timestamp.ToUniversalTime().ToString("o"),
|
||||||
|
data = m.CPU_Load
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("ram-usage")]
|
||||||
|
public async Task<IActionResult> GetRamUsageData(int serverId)
|
||||||
|
{
|
||||||
|
var oneDayAgo = DateTime.UtcNow.AddDays(-1);
|
||||||
|
var data = await _context.Metrics
|
||||||
|
.Where(m => m.Timestamp >= oneDayAgo && m.ServerId == serverId)
|
||||||
|
.OrderBy(m => m.Timestamp)
|
||||||
|
.Select(m => new
|
||||||
|
{
|
||||||
|
label = m.Timestamp.ToUniversalTime().ToString("o"),
|
||||||
|
data = m.RAM_Load
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("gpu-usage")]
|
||||||
|
public async Task<IActionResult> GetGpuUsageData(int serverId)
|
||||||
|
{
|
||||||
|
var oneDayAgo = DateTime.UtcNow.AddDays(-1);
|
||||||
|
var data = await _context.Metrics
|
||||||
|
.Where(m => m.Timestamp >= oneDayAgo && m.ServerId == serverId)
|
||||||
|
.OrderBy(m => m.Timestamp)
|
||||||
|
.Select(m => new
|
||||||
|
{
|
||||||
|
label = m.Timestamp.ToUniversalTime().ToString("o"),
|
||||||
|
data = m.GPU_Load
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(data);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -6,6 +6,7 @@ using Watcher.Models;
|
|||||||
using Watcher.ViewModels;
|
using Watcher.ViewModels;
|
||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
|
[Route("Server")]
|
||||||
public class ServerController : Controller
|
public class ServerController : Controller
|
||||||
{
|
{
|
||||||
private readonly AppDbContext _context;
|
private readonly AppDbContext _context;
|
||||||
@@ -18,6 +19,7 @@ public class ServerController : Controller
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("Overview")]
|
||||||
public async Task<IActionResult> Overview()
|
public async Task<IActionResult> Overview()
|
||||||
{
|
{
|
||||||
var vm = new ServerOverviewViewModel
|
var vm = new ServerOverviewViewModel
|
||||||
@@ -126,7 +128,7 @@ public class ServerController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GET: Server/Details/5
|
// GET: Server/Details/5
|
||||||
[HttpGet]
|
[HttpGet("Details")]
|
||||||
public async Task<IActionResult> Details(int id)
|
public async Task<IActionResult> Details(int id)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -153,6 +155,7 @@ public class ServerController : Controller
|
|||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("ServerCardPartial")]
|
||||||
public async Task<IActionResult> ServerCardsPartial()
|
public async Task<IActionResult> ServerCardsPartial()
|
||||||
{
|
{
|
||||||
var servers = _context.Servers.ToList();
|
var servers = _context.Servers.ToList();
|
||||||
@@ -166,5 +169,12 @@ public class ServerController : Controller
|
|||||||
return PartialView("_ServerCard", servers);
|
return PartialView("_ServerCard", servers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("ServerDetailPartial")]
|
||||||
|
public IActionResult ServerDetailPartial()
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Server Detail Seite neu geladen");
|
||||||
|
return PartialView("_ServerDetails");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,31 +4,272 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<div id="server-cards-container">
|
<div id="server-cards-container">
|
||||||
@await Html.PartialAsync("_ServerDetails")
|
|
||||||
|
<div class="container mt-4"></div>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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: 500px; width: 100%">
|
||||||
|
<canvas id="cpuUsageChart" style="width: 800px; height: 400px; border: 1px solid red;"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</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: 500px; width: 100%">
|
||||||
|
<canvas id="ramUsageChart" style="width: 800px; height: 400px; border: 1px solid red;"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4"></div>
|
||||||
|
<h6><i class="bi bi-graph-up me-1"></i>GPU Last</h6>
|
||||||
|
<div class="bg-light border rounded p-4 text-center text-muted" style="height: 500px; width: 100%">
|
||||||
|
<canvas id="gpuUsageChart" style="width: 800px; height: 400px; border: 1px solid red;"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
<script src="~/js/server_uptime.js"></script>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function loadServerStats() {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
try {
|
const ctx_cpu = document.getElementById('cpuUsageChart').getContext('2d');
|
||||||
const response = await fetch('/Server/ServerDetailsPartial');
|
const ctx_ram = document.getElementById('ramUsageChart').getContext('2d');
|
||||||
if (response.ok) {
|
const ctx_gpu = document.getElementById('gpuUsageChart').getContext('2d');
|
||||||
const html = await response.text();
|
|
||||||
document.getElementById('server-cards-container').innerHTML = html;
|
const cpuChart = new Chart(ctx_cpu, {
|
||||||
} else {
|
type: 'line',
|
||||||
console.error('Fehler beim Nachladen der Serverdaten');
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: 'CPU Last (%)',
|
||||||
|
data: [],
|
||||||
|
borderColor: 'rgb(255, 99, 132)',
|
||||||
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||||
|
fill: true,
|
||||||
|
tension: 0.3,
|
||||||
|
pointRadius: 3
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
max: 100,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Auslastung in %'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Zeit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ramChart = new Chart(ctx_ram, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: 'RAM Last (%)',
|
||||||
|
data: [],
|
||||||
|
borderColor: 'rgb(255, 99, 132)',
|
||||||
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||||
|
fill: true,
|
||||||
|
tension: 0.3,
|
||||||
|
pointRadius: 3
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
max: 100,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Prozent'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Zeit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const gpuChart = new Chart(ctx_gpu, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: 'GPU Last (%)',
|
||||||
|
data: [],
|
||||||
|
borderColor: 'rgb(255, 99, 132)',
|
||||||
|
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||||
|
fill: true,
|
||||||
|
tension: 0.3,
|
||||||
|
pointRadius: 3
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
max: 100,
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Prozent'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Zeit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadCpuData() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/monitoring/cpu-usage?serverId=@Model.Id');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP-Fehler: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
|
console.warn('Keine CPU-Daten empfangen.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuChart.data.labels = data.map(d => d.label ?? '');
|
||||||
|
cpuChart.data.datasets[0].data = data.map(d => Number(d.data));
|
||||||
|
|
||||||
|
cpuChart.update();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Netzwerkfehler beim Nachladen der Serverdaten:', err);
|
console.error('Fehler beim Laden der CPU-Daten:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial laden und dann alle 30 Sekunden
|
async function loadRamData() {
|
||||||
loadServerStats();
|
try {
|
||||||
setInterval(loadServerCards, 30000);
|
const response = await fetch('/monitoring/ram-usage?serverId=@Model.Id');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP-Fehler: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
|
console.warn('Keine RAM-Daten empfangen.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ramChart.data.labels = data.map(d => d.label ?? '');
|
||||||
|
ramChart.data.datasets[0].data = data.map(d => Number(d.data));
|
||||||
|
|
||||||
|
ramChart.update();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Fehler beim Laden der RAM-Daten:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadGpuData() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/monitoring/gpu-usage?serverId=@Model.Id');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP-Fehler: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
|
console.warn('Keine GPU-Daten empfangen.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpuChart.data.labels = data.map(d => d.label ?? '');
|
||||||
|
gpuChart.data.datasets[0].data = data.map(d => Number(d.data));
|
||||||
|
|
||||||
|
gpuChart.update();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Fehler beim Laden der GPU-Daten:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiales Laden
|
||||||
|
loadCpuData();
|
||||||
|
loadRamData();
|
||||||
|
loadGpuData();
|
||||||
|
|
||||||
|
// Alle 30 Sekunden aktualisieren
|
||||||
|
setInterval(loadCpuData, 30000);
|
||||||
|
setInterval(loadRamData, 30000);
|
||||||
|
setInterval(loadGpuData, 30000);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
}
|
}
|
@@ -1,67 +0,0 @@
|
|||||||
<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>
|
|
@@ -17,13 +17,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
||||||
<script src="~/js/server_uptime.js"></script>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function loadServerCards() {
|
async function loadServerCards() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/Server/ServerCardsPartial');
|
const response = await fetch('/Server/ServerCardPartial');
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const html = await response.text();
|
const html = await response.text();
|
||||||
document.getElementById('server-cards-container').innerHTML = html;
|
document.getElementById('server-cards-container').innerHTML = html;
|
||||||
|
Binary file not shown.
Binary file not shown.
@@ -1,35 +0,0 @@
|
|||||||
document.addEventListener("DOMContentLoaded", () => {
|
|
||||||
const canvases = document.querySelectorAll("canvas[id^='uptimeChart-']");
|
|
||||||
|
|
||||||
canvases.forEach(canvas => {
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
// Hier kannst du Daten dynamisch anpassen, ggf. mit data-* Attributen
|
|
||||||
new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
labels: ['0h', '6h', '12h', '18h', '24h'],
|
|
||||||
datasets: [{
|
|
||||||
label: 'Uptime (%)',
|
|
||||||
data: [100, 90, 80, 100, 95], // Beispielwerte
|
|
||||||
borderColor: '#3b82f6',
|
|
||||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
|
||||||
tension: 0.3,
|
|
||||||
fill: true,
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
plugins: {
|
|
||||||
legend: { display: false }
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
max: 100
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
// Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
|
||||||
// for details on configuring this project to bundle and minify static web assets.
|
|
||||||
|
|
||||||
// Write your JavaScript code.
|
|
Reference in New Issue
Block a user