Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 851985a9d0 | |||
| 48469b03db | |||
| 29860bd098 | |||
| f820c641b4 |
@@ -2,48 +2,21 @@ services:
|
||||
watcher:
|
||||
image: git.triggermeelmo.com/triggermeelmo/watcher-server:latest
|
||||
container_name: watcher
|
||||
|
||||
# Resource Management
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 200M
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
# Security - User/Group ID aus Umgebungsvariablen
|
||||
user: "${USER_UID:-1000}:${USER_GID:-1000}"
|
||||
|
||||
# Health Check
|
||||
user: "1000:1000"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Environment
|
||||
environment:
|
||||
# Non-Root User
|
||||
- USER_UID=1000 # Standard 1000
|
||||
- USER_GID=1000 # Standard 1000
|
||||
|
||||
# Timezone
|
||||
- TZ=Europe/Berlin
|
||||
|
||||
# Update Check
|
||||
- UPDATE_CHECK_ENABLED=true
|
||||
- UPDATE_CHECK_INTERVAL_HOURS=24
|
||||
- UPDATE_CHECK_REPOSITORY_URL=https://git.triggermeelmo.com/api/v1/repos/Watcher/watcher/releases/latest
|
||||
|
||||
# Data Retention Policy
|
||||
- METRIC_RETENTION_DAYS=30
|
||||
- METRIC_CLEANUP_INTERVAL_HOURS=24
|
||||
- METRIC_CLEANUP_ENABLED=true
|
||||
|
||||
# Aktualisierungsrate Frontend
|
||||
- FRONTEND_REFRESH_INTERVAL_SECONDS=30
|
||||
|
||||
# OIDC-Authentifizierung (Optional)
|
||||
# - OIDC_ENABLED=true
|
||||
# - OIDC_AUTHORITY=https://auth.example.com/realms/myrealm
|
||||
@@ -54,23 +27,19 @@ services:
|
||||
# - OIDC_CLAIM_USERNAME=preferred_username
|
||||
# - OIDC_CLAIM_EMAIL=email
|
||||
# - OIDC_AUTO_PROVISION_USERS=true
|
||||
|
||||
# Ports
|
||||
ports:
|
||||
- "5000:5000"
|
||||
|
||||
# Volumes
|
||||
volumes:
|
||||
- ./data/db:/app/persistence
|
||||
- ./data/dumps:/app/wwwroot/downloads/sqlite
|
||||
- ./data/logs:/app/logs
|
||||
|
||||
# Labels (Traefik Integration)
|
||||
labels:
|
||||
# Traefik konfiguration
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.watcher.rule=Host(`watcher.example.com`)"
|
||||
- "traefik.http.routers.watcher.entrypoints=websecure"
|
||||
- "traefik.http.routers.watcher.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.watcher.loadbalancer.server.port=5000"
|
||||
# Labels, die von watcher-agent verwendet werden
|
||||
- "com.watcher.description=Server Monitoring Application"
|
||||
- "com.watcher.version=${IMAGE_VERSION:-latest}"
|
||||
|
||||
@@ -75,7 +75,7 @@ public class APIController : Controller
|
||||
GpuType = serverDto.GpuType,
|
||||
RamSize = serverDto.RamSize,
|
||||
DiskSpace = serverDto.DiskSpace,
|
||||
IsOnline = serverDto.IsOnline,
|
||||
State = serverDto.State,
|
||||
IsVerified = serverDto.IsVerified
|
||||
};
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ public class HomeController : Controller
|
||||
servers = _servers,
|
||||
containers = _containers,
|
||||
serversCount = _servers.Count,
|
||||
serversOnline = (from server in _servers where server.IsOnline select server).Count(),
|
||||
serversOffline = _servers.Count - (from server in _servers where server.IsOnline select server).Count()
|
||||
serversOnline = (from server in _servers where server.State=="online" select server).Count(),
|
||||
serversOffline = (from server in _servers where server.State=="offline" select server).Count()
|
||||
};
|
||||
|
||||
return View(homeVm);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using watcher_monitoring.Models;
|
||||
|
||||
using watcher_monitoring.Data;
|
||||
using watcher_monitoring.ViewModels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace watcher_monitoring.Controllers;
|
||||
|
||||
@@ -28,7 +32,12 @@ public class MonitoringController : Controller
|
||||
[HttpGet("server")]
|
||||
public async Task <IActionResult> ServerIndex()
|
||||
{
|
||||
return View();
|
||||
var ServerIndexViewModel = new ServerIndexViewModel
|
||||
{
|
||||
servers = await _context.Servers.ToListAsync()
|
||||
};
|
||||
|
||||
return View(ServerIndexViewModel);
|
||||
}
|
||||
|
||||
}
|
||||
186
watcher-monitoring/Migrations/20260121112553_changedServerAndContainerAttributeState.Designer.cs
generated
Normal file
186
watcher-monitoring/Migrations/20260121112553_changedServerAndContainerAttributeState.Designer.cs
generated
Normal file
@@ -0,0 +1,186 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using watcher_monitoring.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace watcher_monitoring.Migrations
|
||||
{
|
||||
[DbContext(typeof(WatcherDbContext))]
|
||||
[Migration("20260121112553_changedServerAndContainerAttributeState")]
|
||||
partial class changedServerAndContainerAttributeState
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.6");
|
||||
|
||||
modelBuilder.Entity("watcher_monitoring.Models.ApiKey", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("ExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("LastUsedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("ApiKeys");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("watcher_monitoring.Models.Container", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ContainerName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("State")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Containers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("watcher_monitoring.Models.Server", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("CpuCores")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CpuType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DiskSpace")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("GpuType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("IPAddress")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsVerified")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("LastSeen")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<double>("RamSize")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<string>("State")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Servers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("watcher_monitoring.Models.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("LastLogin")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OidcSubject")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("watcher_monitoring.Models.ApiKey", b =>
|
||||
{
|
||||
b.HasOne("watcher_monitoring.Models.User", "User")
|
||||
.WithMany("ApiKeys")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("watcher_monitoring.Models.User", b =>
|
||||
{
|
||||
b.Navigation("ApiKeys");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace watcher_monitoring.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class changedServerAndContainerAttributeState : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsOnline",
|
||||
table: "Servers");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "State",
|
||||
table: "Servers",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "State",
|
||||
table: "Containers",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "State",
|
||||
table: "Servers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "State",
|
||||
table: "Containers");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsOnline",
|
||||
table: "Servers",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,10 @@ namespace watcher_monitoring.Migrations
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("State")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Containers");
|
||||
@@ -99,9 +103,6 @@ namespace watcher_monitoring.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsOnline")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsVerified")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -115,6 +116,10 @@ namespace watcher_monitoring.Migrations
|
||||
b.Property<double>("RamSize")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<string>("State")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Servers");
|
||||
|
||||
@@ -13,4 +13,6 @@ public class Container
|
||||
[StringLength(50)]
|
||||
public required string ContainerName { get; set; } = null!;
|
||||
|
||||
public required string State { get; set; }
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class Server
|
||||
// Metadata
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public bool IsOnline { get; set; } = false;
|
||||
public string State { get; set; }
|
||||
|
||||
public DateTime LastSeen { get; set; }
|
||||
|
||||
|
||||
10
watcher-monitoring/ViewModels/ServerIndexViewModel.cs
Normal file
10
watcher-monitoring/ViewModels/ServerIndexViewModel.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using watcher_monitoring.Data;
|
||||
using watcher_monitoring.Models;
|
||||
|
||||
namespace watcher_monitoring.ViewModels;
|
||||
|
||||
public class ServerIndexViewModel
|
||||
{
|
||||
public List<Server> servers { get; set; } = new();
|
||||
|
||||
}
|
||||
@@ -14,16 +14,20 @@
|
||||
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-md-3">
|
||||
<a href="/monitoring/server" style="text-decoration: none;">
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">Total Servers</div>
|
||||
<div class="metric-value">@Model.serversCount</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<a href="/monitoring/container" style="text-decoration: none;">
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">Online</div>
|
||||
<div class="metric-value" style="color: var(--success)">@Model.serversOnline</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="metric-card">
|
||||
@@ -42,23 +46,26 @@
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<h2 class="card-title"><a href="/Monitoring/server" style="text-decoration: none;">Monitored Servers</a></h2>
|
||||
<h2 class="card-title"><a href="/Monitoring/server" style="text-decoration: none;">Server Issues</a></h2>
|
||||
<ul class="server-list">
|
||||
@if (Model.servers != null && Model.servers.Count > 0)
|
||||
{
|
||||
@foreach (var server in Model.servers)
|
||||
{
|
||||
@if (server.State != "online")
|
||||
{
|
||||
<li class="server-item">
|
||||
<div class="server-info">
|
||||
<span class="server-name">@server.Name</span>
|
||||
<span class="server-ip">@server.IPAddress</span>
|
||||
</div>
|
||||
<span class="status-badge @(server.IsOnline ? "status-online" : "status-offline")">
|
||||
@(server.IsOnline ? "Online" : "Offline")
|
||||
<span class="status-badge @(server.State=="warning" ? "status-warning" : "status-offline")">
|
||||
@(server.State=="warning" ? "Warning" : "Offline")
|
||||
</span>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="text-center py-4" style="color: var(--text-muted)">
|
||||
@@ -70,12 +77,13 @@
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<h2 class="card-title"><a href="/Monitoring/server" style="text-decoration: none;">Monitored Containers</a></h2>
|
||||
<h2 class="card-title"><a href="/Monitoring/server" style="text-decoration: none;">Container Issues</a></h2>
|
||||
<ul class="server-list">
|
||||
@if (Model.containers != null && Model.containers.Count > 0)
|
||||
{
|
||||
@foreach (var container in Model.containers)
|
||||
{
|
||||
@if (container.State != "online") {
|
||||
<li class="server-item">
|
||||
<div class="server-info">
|
||||
<span class="server-name">@container.Name</span>
|
||||
@@ -84,6 +92,7 @@
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="text-center py-4" style="color: var(--text-muted)">
|
||||
|
||||
@@ -11,15 +11,16 @@
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Server Card 1 -->
|
||||
@foreach (var server in Model.servers)
|
||||
{
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card server-detail-card">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div>
|
||||
<h2 class="card-title mb-1">test</h2>
|
||||
<span class="server-ip">192.168.1.100</span>
|
||||
<h2 class="card-title mb-1">@server.Name</h2>
|
||||
<span class="server-ip">@server.IPAddress</span>
|
||||
</div>
|
||||
<span class="status-badge status-online">Online</span>
|
||||
<span class="status-badge status-online">@server.State</span>
|
||||
</div>
|
||||
|
||||
<div class="server-metrics">
|
||||
@@ -51,211 +52,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Server Card 2 -->
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card server-detail-card">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div>
|
||||
<h2 class="card-title mb-1">test</h2>
|
||||
<span class="server-ip">192.168.1.101</span>
|
||||
</div>
|
||||
<span class="status-badge status-online">Online</span>
|
||||
</div>
|
||||
|
||||
<div class="server-metrics">
|
||||
<!-- CPU Load -->
|
||||
<div class="metric-section mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="metric-label-small">CPU Load</span>
|
||||
<span class="metric-value-small">400%</span>
|
||||
</div>
|
||||
<canvas id="cpuChart2" class="chart-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- RAM Load -->
|
||||
<div class="metric-section mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="metric-label-small">RAM Load</span>
|
||||
<span class="metric-value-small">400 MB</span>
|
||||
</div>
|
||||
<canvas id="ramChart2" class="chart-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Uptime -->
|
||||
<div class="metric-section">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="metric-label-small">Uptime</span>
|
||||
<span class="metric-value-small">400 days</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Server Card 3 -->
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card server-detail-card">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div>
|
||||
<h2 class="card-title mb-1">test</h2>
|
||||
<span class="server-ip">192.168.1.102</span>
|
||||
</div>
|
||||
<span class="status-badge status-warning">Warning</span>
|
||||
</div>
|
||||
|
||||
<div class="server-metrics">
|
||||
<!-- CPU Load -->
|
||||
<div class="metric-section mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="metric-label-small">CPU Load</span>
|
||||
<span class="metric-value-small">400%</span>
|
||||
</div>
|
||||
<canvas id="cpuChart3" class="chart-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- RAM Load -->
|
||||
<div class="metric-section mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="metric-label-small">RAM Load</span>
|
||||
<span class="metric-value-small">400 MB</span>
|
||||
</div>
|
||||
<canvas id="ramChart3" class="chart-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Uptime -->
|
||||
<div class="metric-section">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="metric-label-small">Uptime</span>
|
||||
<span class="metric-value-small">400 days</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Server Card 4 -->
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card server-detail-card">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div>
|
||||
<h2 class="card-title mb-1">test</h2>
|
||||
<span class="server-ip">192.168.1.103</span>
|
||||
</div>
|
||||
<span class="status-badge status-online">Online</span>
|
||||
</div>
|
||||
|
||||
<div class="server-metrics">
|
||||
<!-- CPU Load -->
|
||||
<div class="metric-section mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="metric-label-small">CPU Load</span>
|
||||
<span class="metric-value-small">400%</span>
|
||||
</div>
|
||||
<canvas id="cpuChart4" class="chart-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- RAM Load -->
|
||||
<div class="metric-section mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="metric-label-small">RAM Load</span>
|
||||
<span class="metric-value-small">400 MB</span>
|
||||
</div>
|
||||
<canvas id="ramChart4" class="chart-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Uptime -->
|
||||
<div class="metric-section">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="metric-label-small">Uptime</span>
|
||||
<span class="metric-value-small">400 days</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Server Card 5 -->
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card server-detail-card">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div>
|
||||
<h2 class="card-title mb-1">test</h2>
|
||||
<span class="server-ip">192.168.1.104</span>
|
||||
</div>
|
||||
<span class="status-badge status-offline">Offline</span>
|
||||
</div>
|
||||
|
||||
<div class="server-metrics">
|
||||
<!-- CPU Load -->
|
||||
<div class="metric-section mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="metric-label-small">CPU Load</span>
|
||||
<span class="metric-value-small">400%</span>
|
||||
</div>
|
||||
<canvas id="cpuChart5" class="chart-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- RAM Load -->
|
||||
<div class="metric-section mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="metric-label-small">RAM Load</span>
|
||||
<span class="metric-value-small">400 MB</span>
|
||||
</div>
|
||||
<canvas id="ramChart5" class="chart-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Uptime -->
|
||||
<div class="metric-section">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="metric-label-small">Uptime</span>
|
||||
<span class="metric-value-small">400 days</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Server Card 6 -->
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card server-detail-card">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div>
|
||||
<h2 class="card-title mb-1">test</h2>
|
||||
<span class="server-ip">192.168.1.105</span>
|
||||
</div>
|
||||
<span class="status-badge status-online">Online</span>
|
||||
</div>
|
||||
|
||||
<div class="server-metrics">
|
||||
<!-- CPU Load -->
|
||||
<div class="metric-section mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="metric-label-small">CPU Load</span>
|
||||
<span class="metric-value-small">400%</span>
|
||||
</div>
|
||||
<canvas id="cpuChart6" class="chart-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- RAM Load -->
|
||||
<div class="metric-section mb-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="metric-label-small">RAM Load</span>
|
||||
<span class="metric-value-small">400 MB</span>
|
||||
</div>
|
||||
<canvas id="ramChart6" class="chart-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Uptime -->
|
||||
<div class="metric-section">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="metric-label-small">Uptime</span>
|
||||
<span class="metric-value-small">400 days</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user