From 2576604a4a4b7c0e337e366d2b7a0891092de6a5 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Mon, 17 Nov 2025 00:44:10 +0100 Subject: [PATCH] CodeStyle, Compose verbesserungen, Docker Healthcheck --- Dockerfile | 16 +- Watcher/Controllers/HomeController.cs | 20 +- Watcher/Controllers/MonitoringController.cs | 90 ++-- Watcher/Controllers/ServerController.cs | 96 +++-- ...meServerPropertiesToPascalCase.Designer.cs | 392 ++++++++++++++++++ ...3330_RenameServerPropertiesToPascalCase.cs | 158 +++++++ .../Migrations/AppDbContextModelSnapshot.cs | 52 +-- Watcher/Models/Container.cs | 8 +- Watcher/Models/Server.cs | 32 +- Watcher/Program.cs | 7 + Watcher/Services/DashboardStore.cs | 4 +- Watcher/Services/DatabaseCheck.cs | 4 +- Watcher/Services/IDashboardStore.cs | 4 +- Watcher/Services/ISystemStore.cs | 4 +- Watcher/Services/NetworkCheck.cs | 14 +- Watcher/Services/SystemMangement.cs | 12 +- Watcher/Services/SystemStore.cs | 4 +- Watcher/ViewModels/DashboardViewModel.cs | 4 +- Watcher/ViewModels/EditServerViewModel.cs | 28 +- Watcher/ViewModels/ServerDetailsViewModel.cs | 4 +- Watcher/Views/Server/EditServer.cshtml | 84 ++-- Watcher/Watcher.csproj | 1 + docker-compose.yaml | 44 ++ 23 files changed, 861 insertions(+), 221 deletions(-) create mode 100644 Watcher/Migrations/20251116233330_RenameServerPropertiesToPascalCase.Designer.cs create mode 100644 Watcher/Migrations/20251116233330_RenameServerPropertiesToPascalCase.cs diff --git a/Dockerfile b/Dockerfile index 0ef5a36..1cc1e0f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,19 +18,29 @@ FROM mcr.microsoft.com/dotnet/aspnet:8.0 # Build-Argument für Version (wird zur Build-Zeit vom CI/CD gesetzt) ARG VERSION=latest +# Install curl for health checks +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* + +# Create non-root user +RUN groupadd -r watcher -g 1000 && useradd -r -g watcher -u 1000 watcher + WORKDIR /app COPY --from=build /app/publish . -# Stelle sicher, dass Verzeichnisse existieren -RUN mkdir -p /app/persistence /app/wwwroot/downloads/sqlite /app/logs +# Stelle sicher, dass Verzeichnisse existieren und Berechtigungen gesetzt sind +RUN mkdir -p /app/persistence /app/wwwroot/downloads/sqlite /app/logs && \ + chown -R watcher:watcher /app # Volumes VOLUME ["/app/persistence", "/app/wwwroot/downloads/sqlite", "/app/logs"] +# Switch to non-root user +USER watcher + # Expose Port 5000 EXPOSE 5000 ENV ASPNETCORE_URLS=http://*:5000 -ENV ASPNETCORE_ENVIRONMENT=Development +ENV ASPNETCORE_ENVIRONMENT=Production # Version als Environment Variable setzen ENV WATCHER_VERSION=${VERSION} diff --git a/Watcher/Controllers/HomeController.cs b/Watcher/Controllers/HomeController.cs index f2ba7a9..49b2ac9 100644 --- a/Watcher/Controllers/HomeController.cs +++ b/Watcher/Controllers/HomeController.cs @@ -19,18 +19,18 @@ namespace Watcher.Controllers private readonly ILogger _logger; // Daten der Backgroundchecks abrufen - private IDashboardStore _DashboardStore; + private IDashboardStore _dashboardStore; // System Store für Konfigurationen - private ISystemStore _SystemStore; + private ISystemStore _systemStore; // HomeController Constructor public HomeController(AppDbContext context, ILogger logger, IDashboardStore dashboardStore, ISystemStore systemStore) { _context = context; _logger = logger; - _DashboardStore = dashboardStore; - _SystemStore = systemStore; + _dashboardStore = dashboardStore; + _systemStore = systemStore; } @@ -62,9 +62,9 @@ namespace Watcher.Controllers Containers = await _context.Containers .OrderBy(s => s.Name) .ToListAsync(), - NetworkStatus = _DashboardStore.NetworkStatus, - DatabaseStatus = _DashboardStore.DatabaseStatus, - RefreshIntervalMilliseconds = _SystemStore.FrontendRefreshIntervalMilliseconds + NetworkStatus = _dashboardStore.NetworkStatus, + DatabaseStatus = _dashboardStore.DatabaseStatus, + RefreshIntervalMilliseconds = _systemStore.FrontendRefreshIntervalMilliseconds }; //ViewBag.NetworkConnection = _NetworkCheckStore.NetworkStatus; return View(viewModel); @@ -95,14 +95,14 @@ namespace Watcher.Controllers Containers = await _context.Containers .OrderBy(s => s.Name) .ToListAsync(), - NetworkStatus = _DashboardStore.NetworkStatus, - DatabaseStatus = _DashboardStore.DatabaseStatus + NetworkStatus = _dashboardStore.NetworkStatus, + DatabaseStatus = _dashboardStore.DatabaseStatus }; return PartialView("_DashboardStats", model); } - public String ReturnNetworkStatus() + public string ReturnNetworkStatus() { return "OK"; } diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 429e2b6..3fda28c 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -187,28 +187,28 @@ public class MonitoringController : Controller if (server != null) { // neues Metric-Objekt erstellen - var NewMetric = new Metric + var newMetric = new Metric { Timestamp = DateTime.UtcNow, ServerId = dto.ServerId, - CPU_Load = sanitizeInput(dto.CPU_Load), - CPU_Temp = sanitizeInput(dto.CPU_Temp), - GPU_Load = sanitizeInput(dto.GPU_Load), - GPU_Temp = sanitizeInput(dto.GPU_Temp), - GPU_Vram_Size = calculateGigabyte(dto.GPU_Vram_Size), - GPU_Vram_Usage = sanitizeInput(dto.GPU_Vram_Load), - RAM_Load = sanitizeInput(dto.RAM_Load), - RAM_Size = calculateGigabyte(dto.RAM_Size), - DISK_Size = calculateGigabyte(dto.DISK_Size), - DISK_Usage = calculateGigabyte(dto.DISK_Usage), - DISK_Temp = sanitizeInput(dto.DISK_Temp), - NET_In = calculateMegabit(dto.NET_In), - NET_Out = calculateMegabit(dto.NET_Out) + CPU_Load = SanitizeInput(dto.CPU_Load), + CPU_Temp = SanitizeInput(dto.CPU_Temp), + GPU_Load = SanitizeInput(dto.GPU_Load), + GPU_Temp = SanitizeInput(dto.GPU_Temp), + GPU_Vram_Size = CalculateGigabyte(dto.GPU_Vram_Size), + GPU_Vram_Usage = SanitizeInput(dto.GPU_Vram_Load), + RAM_Load = SanitizeInput(dto.RAM_Load), + RAM_Size = CalculateGigabyte(dto.RAM_Size), + DISK_Size = CalculateGigabyte(dto.DISK_Size), + DISK_Usage = CalculateGigabyte(dto.DISK_Usage), + DISK_Temp = SanitizeInput(dto.DISK_Temp), + NET_In = CalculateMegabit(dto.NET_In), + NET_Out = CalculateMegabit(dto.NET_Out) }; try { // Metric Objekt in Datenbank einfügen - _context.Metrics.Add(NewMetric); + _context.Metrics.Add(newMetric); await _context.SaveChangesAsync(); _logger.LogInformation("Monitoring-Daten für '{server}' empfangen", server.Name); @@ -256,16 +256,16 @@ public class MonitoringController : Controller JsonSerializer.Deserialize>(dto.Containers.GetRawText()) ?? new List(); - foreach (Container c in newContainers) + foreach (Container container in newContainers) { - c.ServerId = dto.Server_id; + container.ServerId = dto.Server_id; // Debug Logs // TODO entfernen wenn fertig getestet Console.WriteLine("---------"); - Console.WriteLine("ServerId: " + c.ServerId); - Console.WriteLine("ContainerId: " + c.ContainerId); - Console.WriteLine("Name: " + c.Name); - Console.WriteLine("Image: " + c.Image); + Console.WriteLine("ServerId: " + container.ServerId); + Console.WriteLine("ContainerId: " + container.ContainerId); + Console.WriteLine("Name: " + container.Name); + Console.WriteLine("Image: " + container.Image); Console.WriteLine("---------"); } @@ -277,21 +277,21 @@ public class MonitoringController : Controller // Logik, um Container, die mit dem Payload kamen zu verarbeiten - foreach (Container c in newContainers) + foreach (Container container in newContainers) { // Überprüfen, ob ein übergebener Container bereits für den Host registriert ist // Wichtig: Vergleich über ContainerId, nicht über Objektreferenz! var existingContainer = existingContainers - .FirstOrDefault(ec => ec.ContainerId == c.ContainerId); + .FirstOrDefault(ec => ec.ContainerId == container.ContainerId); if (existingContainer != null) { // Container existiert bereits, nur Daten aktualisieren falls sich etwas geändert hat - existingContainer.Name = c.Name; - existingContainer.Image = c.Image; + existingContainer.Name = container.Name; + existingContainer.Image = container.Image; existingContainer.IsRunning = true; - _logger.LogInformation("Container '{containerName}' (ID: {containerId}) already exists for Server {serverId}, updated.", c.Name, c.ContainerId, dto.Server_id); + _logger.LogInformation("Container '{containerName}' (ID: {containerId}) already exists for Server {serverId}, updated.", container.Name, container.ContainerId, dto.Server_id); } // Container auf einen Host/Server registrieren else @@ -299,35 +299,35 @@ public class MonitoringController : Controller // Container in Datenbank einlesen try { - _context.Containers.Add(c); + _context.Containers.Add(container); await _context.SaveChangesAsync(); - _logger.LogInformation("Container '{containerName}' (ID: {containerId}) added for Server {serverId}", c.Name, c.ContainerId, c.ServerId); + _logger.LogInformation("Container '{containerName}' (ID: {containerId}) added for Server {serverId}", container.Name, container.ContainerId, container.ServerId); } catch (SqliteException e) { - _logger.LogError("Error writing new Container '{containerName}' to Database: {error}", c.Name, e.Message); + _logger.LogError("Error writing new Container '{containerName}' to Database: {error}", container.Name, e.Message); } } } // Logik um abgeschaltene Container aus der Datenbank zu entfernen - foreach (Container c in existingContainers) + foreach (Container existingContainer in existingContainers) { // Abfrage, ob bereits vorhandener Container im Payload vorhanden war // Wichtig: Vergleich über ContainerId, nicht über Objektreferenz! var stillRunning = newContainers - .Any(nc => nc.ContainerId == c.ContainerId); + .Any(nc => nc.ContainerId == existingContainer.ContainerId); if (!stillRunning) { // Container entfernen - _context.Containers.Remove(c); + _context.Containers.Remove(existingContainer); await _context.SaveChangesAsync(); // Metrics für den Container entfernen //Todo - _logger.LogInformation("Container '{containerName}' (DB-ID: {id}, ContainerID: {containerId}) on Server {serverId} was removed from the database.", c.Name, c.Id, c.ContainerId, c.ServerId); + _logger.LogInformation("Container '{containerName}' (DB-ID: {id}, ContainerID: {containerId}) on Server {serverId} was removed from the database.", existingContainer.Name, existingContainer.Id, existingContainer.ContainerId, existingContainer.ServerId); } } @@ -454,39 +454,39 @@ public class MonitoringController : Controller } // Metric Input Byte zu Gigabyte umwandeln - public static double calculateGigabyte(double metric_input) + public static double CalculateGigabyte(double metricInput) { // *10^-9 um auf Gigabyte zu kommen - double calculatedValue = metric_input * Math.Pow(10, -9); + double calculatedValue = metricInput * Math.Pow(10, -9); // Auf 2 Nachkommastellen runden - double calculatedValue_s = sanitizeInput(calculatedValue); + double calculatedValueSanitized = SanitizeInput(calculatedValue); - return calculatedValue_s; + return calculatedValueSanitized; } // Metric Input Byte/s zu Megabit/s umrechnen //TODO - public static double calculateMegabit(double metric_input) + public static double CalculateMegabit(double metricInput) { // *10^-9 um auf Gigabyte zu kommen - double calculatedValue = metric_input * Math.Pow(10, -9); + double calculatedValue = metricInput * Math.Pow(10, -9); // Auf 2 Nachkommastellen runden - double calculatedValue_s = sanitizeInput(calculatedValue); + double calculatedValueSanitized = SanitizeInput(calculatedValue); - return calculatedValue_s; + return calculatedValueSanitized; } // Degree Input auf zwei Nachkommastellen runden - public static double sanitizeInput(double metric_input) + public static double SanitizeInput(double metricInput) { - Math.Round(metric_input, 2); + Math.Round(metricInput, 2); - return metric_input; + return metricInput; } - private List ParseServiceDiscoveryInput(int server_id, List containers) + private List ParseServiceDiscoveryInput(int serverId, List containers) { List containerList = new List(); diff --git a/Watcher/Controllers/ServerController.cs b/Watcher/Controllers/ServerController.cs index 720f3bd..8170df1 100644 --- a/Watcher/Controllers/ServerController.cs +++ b/Watcher/Controllers/ServerController.cs @@ -28,13 +28,13 @@ public class ServerController : Controller [HttpGet("Overview")] public async Task Overview() { - var vm = new ServerOverviewViewModel + var viewModel = new ServerOverviewViewModel { Servers = await _context.Servers.OrderBy(s => s.Id).ToListAsync(), RefreshIntervalMilliseconds = _systemStore.FrontendRefreshIntervalMilliseconds }; - return View(vm); + return View(viewModel); } @@ -47,17 +47,17 @@ public class ServerController : Controller // POST: Server/AddServer [HttpPost("AddServer")] [ValidateAntiForgeryToken] - public async Task AddServer(AddServerViewModel vm) + public async Task AddServer(AddServerViewModel viewModel) { if (!ModelState.IsValid) - return View(vm); + return View(viewModel); var server = new Server { - Name = vm.Name, - IPAddress = vm.IPAddress, - Type = vm.Type, - IsOnline = vm.IsOnline, + Name = viewModel.Name, + IPAddress = viewModel.IPAddress, + Type = viewModel.Type, + IsOnline = viewModel.IsOnline, }; _context.Servers.Add(server); @@ -96,25 +96,39 @@ public class ServerController : Controller var server = await _context.Servers.FindAsync(id); if (server == null) return NotFound(); - var vm = new EditServerViewModel + var viewModel = new EditServerViewModel { Name = server.Name, IPAddress = server.IPAddress, - Type = server.Type + Type = server.Type, + CpuLoadWarning = server.CpuLoadWarning, + CpuLoadCritical = server.CpuLoadCritical, + CpuTempWarning = server.CpuTempWarning, + CpuTempCritical = server.CpuTempCritical, + RamLoadWarning = server.RamLoadWarning, + RamLoadCritical = server.RamLoadCritical, + GpuLoadWarning = server.GpuLoadWarning, + GpuLoadCritical = server.GpuLoadCritical, + GpuTempWarning = server.GpuTempWarning, + GpuTempCritical = server.GpuTempCritical, + DiskUsageWarning = server.DiskUsageWarning, + DiskUsageCritical = server.DiskUsageCritical, + DiskTempWarning = server.DiskTempWarning, + DiskTempCritical = server.DiskTempCritical }; - return View(vm); + return View(viewModel); } // POST: Server/Edit/5 [HttpPost("EditServer/{id}")] [ValidateAntiForgeryToken] - public async Task EditServer(int id, EditServerViewModel vm) + public async Task EditServer(int id, EditServerViewModel viewModel) { if (!ModelState.IsValid) { - return View(vm); + return View(viewModel); } var server = await _context.Servers.FindAsync(id); @@ -123,9 +137,23 @@ public class ServerController : Controller return NotFound(); } - server.Name = vm.Name; - server.IPAddress = vm.IPAddress; - server.Type = vm.Type; + server.Name = viewModel.Name; + server.IPAddress = viewModel.IPAddress; + server.Type = viewModel.Type; + server.CpuLoadWarning = viewModel.CpuLoadWarning; + server.CpuLoadCritical = viewModel.CpuLoadCritical; + server.CpuTempWarning = viewModel.CpuTempWarning; + server.CpuTempCritical = viewModel.CpuTempCritical; + server.RamLoadWarning = viewModel.RamLoadWarning; + server.RamLoadCritical = viewModel.RamLoadCritical; + server.GpuLoadWarning = viewModel.GpuLoadWarning; + server.GpuLoadCritical = viewModel.GpuLoadCritical; + server.GpuTempWarning = viewModel.GpuTempWarning; + server.GpuTempCritical = viewModel.GpuTempCritical; + server.DiskUsageWarning = viewModel.DiskUsageWarning; + server.DiskUsageCritical = viewModel.DiskUsageCritical; + server.DiskTempWarning = viewModel.DiskTempWarning; + server.DiskTempCritical = viewModel.DiskTempCritical; try { @@ -137,7 +165,7 @@ public class ServerController : Controller { _logger.LogError(ex, "Fehler beim Speichern des Servers"); ModelState.AddModelError("", "Fehler beim Speichern"); - return View(vm); + return View(viewModel); } } @@ -147,28 +175,28 @@ public class ServerController : Controller public async Task Details(int id) { - var s = await _context.Servers.FindAsync(id); - if (s == null) return NotFound(); + var server = await _context.Servers.FindAsync(id); + if (server == null) return NotFound(); - var vm = new ServerDetailsViewModel + var viewModel = 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, + Id = server.Id, + Name = server.Name, + IPAddress = server.IPAddress, + Type = server.Type, + Description = server.Description, + CpuType = server.CpuType, + CpuCores = server.CpuCores, + GpuType = server.GpuType, + RamSize = server.RamSize, + CreatedAt = server.CreatedAt, + IsOnline = server.IsOnline, + LastSeen = server.LastSeen, + IsVerified = server.IsVerified, RefreshIntervalMilliseconds = _systemStore.FrontendRefreshIntervalMilliseconds }; - return View(vm); + return View(viewModel); } diff --git a/Watcher/Migrations/20251116233330_RenameServerPropertiesToPascalCase.Designer.cs b/Watcher/Migrations/20251116233330_RenameServerPropertiesToPascalCase.Designer.cs new file mode 100644 index 0000000..eb5efa8 --- /dev/null +++ b/Watcher/Migrations/20251116233330_RenameServerPropertiesToPascalCase.Designer.cs @@ -0,0 +1,392 @@ +// +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("20251116233330_RenameServerPropertiesToPascalCase")] + partial class RenameServerPropertiesToPascalCase + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContainerId") + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "id"); + + b.Property("Image") + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "image"); + + b.Property("ImageId") + .HasColumnType("INTEGER"); + + b.Property("IsRunning") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "name"); + + b.Property("ServerId") + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "Server_id"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("ServerId"); + + b.HasIndex("TagId"); + + b.ToTable("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.ContainerMetric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load") + .HasColumnType("REAL"); + + b.Property("CPU_Temp") + .HasColumnType("REAL"); + + b.Property("ContainerId") + .HasColumnType("INTEGER"); + + b.Property("RAM_Load") + .HasColumnType("REAL"); + + b.Property("RAM_Size") + .HasColumnType("REAL"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ContainerMetrics"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Tag") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContainerId") + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("TEXT"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("ServerId"); + + b.ToTable("LogEvents"); + }); + + modelBuilder.Entity("Watcher.Models.Metric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load") + .HasColumnType("REAL"); + + b.Property("CPU_Temp") + .HasColumnType("REAL"); + + b.Property("DISK_Size") + .HasColumnType("REAL"); + + b.Property("DISK_Temp") + .HasColumnType("REAL"); + + b.Property("DISK_Usage") + .HasColumnType("REAL"); + + b.Property("GPU_Load") + .HasColumnType("REAL"); + + b.Property("GPU_Temp") + .HasColumnType("REAL"); + + b.Property("GPU_Vram_Size") + .HasColumnType("REAL"); + + b.Property("GPU_Vram_Usage") + .HasColumnType("REAL"); + + b.Property("NET_In") + .HasColumnType("REAL"); + + b.Property("NET_Out") + .HasColumnType("REAL"); + + b.Property("RAM_Load") + .HasColumnType("REAL"); + + b.Property("RAM_Size") + .HasColumnType("REAL"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Metrics"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CpuCores") + .HasColumnType("INTEGER"); + + b.Property("CpuLoadCritical") + .HasColumnType("REAL"); + + b.Property("CpuLoadWarning") + .HasColumnType("REAL"); + + b.Property("CpuTempCritical") + .HasColumnType("REAL"); + + b.Property("CpuTempWarning") + .HasColumnType("REAL"); + + b.Property("CpuType") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DiskSpace") + .HasColumnType("TEXT"); + + b.Property("DiskTempCritical") + .HasColumnType("REAL"); + + b.Property("DiskTempWarning") + .HasColumnType("REAL"); + + b.Property("DiskUsageCritical") + .HasColumnType("REAL"); + + b.Property("DiskUsageWarning") + .HasColumnType("REAL"); + + b.Property("GpuLoadCritical") + .HasColumnType("REAL"); + + b.Property("GpuLoadWarning") + .HasColumnType("REAL"); + + b.Property("GpuTempCritical") + .HasColumnType("REAL"); + + b.Property("GpuTempWarning") + .HasColumnType("REAL"); + + b.Property("GpuType") + .HasColumnType("TEXT"); + + b.Property("IPAddress") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsOnline") + .HasColumnType("INTEGER"); + + b.Property("IsVerified") + .HasColumnType("INTEGER"); + + b.Property("LastSeen") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RamLoadCritical") + .HasColumnType("REAL"); + + b.Property("RamLoadWarning") + .HasColumnType("REAL"); + + b.Property("RamSize") + .HasColumnType("REAL"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TagId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("Watcher.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("LastLogin") + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + 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.Server", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Containers") + .HasForeignKey("TagId"); + + b.Navigation("Server"); + }); + + 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.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 + } + } +} diff --git a/Watcher/Migrations/20251116233330_RenameServerPropertiesToPascalCase.cs b/Watcher/Migrations/20251116233330_RenameServerPropertiesToPascalCase.cs new file mode 100644 index 0000000..dbfdde3 --- /dev/null +++ b/Watcher/Migrations/20251116233330_RenameServerPropertiesToPascalCase.cs @@ -0,0 +1,158 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Watcher.Migrations +{ + /// + public partial class RenameServerPropertiesToPascalCase : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "RAM_Load_Warning", + table: "Servers", + newName: "RamLoadWarning"); + + migrationBuilder.RenameColumn( + name: "RAM_Load_Critical", + table: "Servers", + newName: "RamLoadCritical"); + + migrationBuilder.RenameColumn( + name: "GPU_Temp_Warning", + table: "Servers", + newName: "GpuTempWarning"); + + migrationBuilder.RenameColumn( + name: "GPU_Temp_Critical", + table: "Servers", + newName: "GpuTempCritical"); + + migrationBuilder.RenameColumn( + name: "GPU_Load_Warning", + table: "Servers", + newName: "GpuLoadWarning"); + + migrationBuilder.RenameColumn( + name: "GPU_Load_Critical", + table: "Servers", + newName: "GpuLoadCritical"); + + migrationBuilder.RenameColumn( + name: "Disk_Usage_Warning", + table: "Servers", + newName: "DiskUsageWarning"); + + migrationBuilder.RenameColumn( + name: "Disk_Usage_Critical", + table: "Servers", + newName: "DiskUsageCritical"); + + migrationBuilder.RenameColumn( + name: "DISK_Temp_Warning", + table: "Servers", + newName: "DiskTempWarning"); + + migrationBuilder.RenameColumn( + name: "DISK_Temp_Critical", + table: "Servers", + newName: "DiskTempCritical"); + + migrationBuilder.RenameColumn( + name: "CPU_Temp_Warning", + table: "Servers", + newName: "CpuTempWarning"); + + migrationBuilder.RenameColumn( + name: "CPU_Temp_Critical", + table: "Servers", + newName: "CpuTempCritical"); + + migrationBuilder.RenameColumn( + name: "CPU_Load_Warning", + table: "Servers", + newName: "CpuLoadWarning"); + + migrationBuilder.RenameColumn( + name: "CPU_Load_Critical", + table: "Servers", + newName: "CpuLoadCritical"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "RamLoadWarning", + table: "Servers", + newName: "RAM_Load_Warning"); + + migrationBuilder.RenameColumn( + name: "RamLoadCritical", + table: "Servers", + newName: "RAM_Load_Critical"); + + migrationBuilder.RenameColumn( + name: "GpuTempWarning", + table: "Servers", + newName: "GPU_Temp_Warning"); + + migrationBuilder.RenameColumn( + name: "GpuTempCritical", + table: "Servers", + newName: "GPU_Temp_Critical"); + + migrationBuilder.RenameColumn( + name: "GpuLoadWarning", + table: "Servers", + newName: "GPU_Load_Warning"); + + migrationBuilder.RenameColumn( + name: "GpuLoadCritical", + table: "Servers", + newName: "GPU_Load_Critical"); + + migrationBuilder.RenameColumn( + name: "DiskUsageWarning", + table: "Servers", + newName: "Disk_Usage_Warning"); + + migrationBuilder.RenameColumn( + name: "DiskUsageCritical", + table: "Servers", + newName: "Disk_Usage_Critical"); + + migrationBuilder.RenameColumn( + name: "DiskTempWarning", + table: "Servers", + newName: "DISK_Temp_Warning"); + + migrationBuilder.RenameColumn( + name: "DiskTempCritical", + table: "Servers", + newName: "DISK_Temp_Critical"); + + migrationBuilder.RenameColumn( + name: "CpuTempWarning", + table: "Servers", + newName: "CPU_Temp_Warning"); + + migrationBuilder.RenameColumn( + name: "CpuTempCritical", + table: "Servers", + newName: "CPU_Temp_Critical"); + + migrationBuilder.RenameColumn( + name: "CpuLoadWarning", + table: "Servers", + newName: "CPU_Load_Warning"); + + migrationBuilder.RenameColumn( + name: "CpuLoadCritical", + table: "Servers", + newName: "CPU_Load_Critical"); + } + } +} diff --git a/Watcher/Migrations/AppDbContextModelSnapshot.cs b/Watcher/Migrations/AppDbContextModelSnapshot.cs index 99d7352..bc651b5 100644 --- a/Watcher/Migrations/AppDbContextModelSnapshot.cs +++ b/Watcher/Migrations/AppDbContextModelSnapshot.cs @@ -197,55 +197,55 @@ namespace Watcher.Migrations .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("CPU_Load_Critical") - .HasColumnType("REAL"); - - b.Property("CPU_Load_Warning") - .HasColumnType("REAL"); - - b.Property("CPU_Temp_Critical") - .HasColumnType("REAL"); - - b.Property("CPU_Temp_Warning") - .HasColumnType("REAL"); - b.Property("CpuCores") .HasColumnType("INTEGER"); + b.Property("CpuLoadCritical") + .HasColumnType("REAL"); + + b.Property("CpuLoadWarning") + .HasColumnType("REAL"); + + b.Property("CpuTempCritical") + .HasColumnType("REAL"); + + b.Property("CpuTempWarning") + .HasColumnType("REAL"); + b.Property("CpuType") .HasColumnType("TEXT"); b.Property("CreatedAt") .HasColumnType("TEXT"); - b.Property("DISK_Temp_Critical") - .HasColumnType("REAL"); - - b.Property("DISK_Temp_Warning") - .HasColumnType("REAL"); - b.Property("Description") .HasColumnType("TEXT"); b.Property("DiskSpace") .HasColumnType("TEXT"); - b.Property("Disk_Usage_Critical") + b.Property("DiskTempCritical") .HasColumnType("REAL"); - b.Property("Disk_Usage_Warning") + b.Property("DiskTempWarning") .HasColumnType("REAL"); - b.Property("GPU_Load_Critical") + b.Property("DiskUsageCritical") .HasColumnType("REAL"); - b.Property("GPU_Load_Warning") + b.Property("DiskUsageWarning") .HasColumnType("REAL"); - b.Property("GPU_Temp_Critical") + b.Property("GpuLoadCritical") .HasColumnType("REAL"); - b.Property("GPU_Temp_Warning") + b.Property("GpuLoadWarning") + .HasColumnType("REAL"); + + b.Property("GpuTempCritical") + .HasColumnType("REAL"); + + b.Property("GpuTempWarning") .HasColumnType("REAL"); b.Property("GpuType") @@ -268,10 +268,10 @@ namespace Watcher.Migrations .IsRequired() .HasColumnType("TEXT"); - b.Property("RAM_Load_Critical") + b.Property("RamLoadCritical") .HasColumnType("REAL"); - b.Property("RAM_Load_Warning") + b.Property("RamLoadWarning") .HasColumnType("REAL"); b.Property("RamSize") diff --git a/Watcher/Models/Container.cs b/Watcher/Models/Container.cs index 826dcbe..ac70ac3 100644 --- a/Watcher/Models/Container.cs +++ b/Watcher/Models/Container.cs @@ -13,15 +13,15 @@ public class Container public Server Server { get; set; } = null!; [JsonPropertyName("id")] - public String? ContainerId { get; set; } + public string? ContainerId { get; set; } [JsonPropertyName("image")] - public String? Image { get; set; } + public string? Image { get; set; } [JsonPropertyName("name")] - public String? Name { get; set; } + public string? Name { get; set; } // keine Variable, die vom Agent übergeben wird. Ein container ist immer Running, die Variable dient nur für die Übersicht // auf dem Dashboard. - public Boolean IsRunning { get; set; } = true; + public bool IsRunning { get; set; } = true; } diff --git a/Watcher/Models/Server.cs b/Watcher/Models/Server.cs index d29aa3c..87e2003 100644 --- a/Watcher/Models/Server.cs +++ b/Watcher/Models/Server.cs @@ -29,32 +29,32 @@ public class Server // Hardware Measurment Warning/Crit Values - public double CPU_Load_Warning { get; set; } = 75.0; - public double CPU_Load_Critical { get; set; } = 90.0; - public double CPU_Temp_Warning { get; set; } = 80.0; - public double CPU_Temp_Critical { get; set; } = 90.0; + public double CpuLoadWarning { get; set; } = 75.0; + public double CpuLoadCritical { get; set; } = 90.0; + public double CpuTempWarning { get; set; } = 80.0; + public double CpuTempCritical { get; set; } = 90.0; - public double RAM_Load_Warning { get; set; } = 85.0; - public double RAM_Load_Critical { get; set; } = 95.0; + public double RamLoadWarning { get; set; } = 85.0; + public double RamLoadCritical { get; set; } = 95.0; - public double GPU_Load_Warning { get; set; } = 75.0; - public double GPU_Load_Critical { get; set; } = 90.0; - public double GPU_Temp_Warning { get; set; } = 70.0; - public double GPU_Temp_Critical { get; set; } = 80.0; + public double GpuLoadWarning { get; set; } = 75.0; + public double GpuLoadCritical { get; set; } = 90.0; + public double GpuTempWarning { get; set; } = 70.0; + public double GpuTempCritical { get; set; } = 80.0; - public double Disk_Usage_Warning { get; set; } = 75.0; - public double Disk_Usage_Critical { get; set; } = 90.0; - public double DISK_Temp_Warning { get; set; } = 34.0; - public double DISK_Temp_Critical { get; set; } = 36.0; + public double DiskUsageWarning { get; set; } = 75.0; + public double DiskUsageCritical { get; set; } = 90.0; + public double DiskTempWarning { get; set; } = 34.0; + public double DiskTempCritical { get; set; } = 36.0; // Database public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - public Boolean IsOnline { get; set; } = false; + public bool IsOnline { get; set; } = false; public DateTime LastSeen { get; set; } - public Boolean IsVerified { get; set; } = false; + public bool IsVerified { get; set; } = false; } diff --git a/Watcher/Program.cs b/Watcher/Program.cs index 7538ab6..27685da 100644 --- a/Watcher/Program.cs +++ b/Watcher/Program.cs @@ -29,6 +29,10 @@ builder.Host.UseSerilog(); // Add services to the container. builder.Services.AddControllersWithViews(); +// Health Checks +builder.Services.AddHealthChecks() + .AddDbContextCheck("database"); + // HttpContentAccessor builder.Services.AddHttpContextAccessor(); @@ -159,6 +163,9 @@ app.UseSwaggerUI(options => options.RoutePrefix = "api/v1/swagger"; }); +// 🔹 Health Check Endpoint +app.MapHealthChecks("/health"); + // 🔹 Authentifizierung & Autorisierung app.UseAuthentication(); app.UseAuthorization(); diff --git a/Watcher/Services/DashboardStore.cs b/Watcher/Services/DashboardStore.cs index 1dc1559..962549c 100644 --- a/Watcher/Services/DashboardStore.cs +++ b/Watcher/Services/DashboardStore.cs @@ -2,7 +2,7 @@ namespace Watcher.Services; public class DashboardStore : IDashboardStore { - public String? NetworkStatus { get; set; } + public string? NetworkStatus { get; set; } - public String? DatabaseStatus { get; set; } + public string? DatabaseStatus { get; set; } } \ No newline at end of file diff --git a/Watcher/Services/DatabaseCheck.cs b/Watcher/Services/DatabaseCheck.cs index d09d598..c55ac5d 100644 --- a/Watcher/Services/DatabaseCheck.cs +++ b/Watcher/Services/DatabaseCheck.cs @@ -21,14 +21,14 @@ public class DatabaseCheck : BackgroundService while (await timer.WaitForNextTickAsync(stoppingToken)) { // Hintergrundprozess abwarten - await checkDatabaseIntegrity(); + await CheckDatabaseIntegrity(); // 5 Sekdunden Offset await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); } } // String sqliteConnectionString als Argument übergeben - public Task checkDatabaseIntegrity() + public Task CheckDatabaseIntegrity() { using var conn = new SqliteConnection("Data Source=./persistence/watcher.db"); _logger.LogInformation("Sqlite Integrity-Check started..."); diff --git a/Watcher/Services/IDashboardStore.cs b/Watcher/Services/IDashboardStore.cs index b74336e..0538e50 100644 --- a/Watcher/Services/IDashboardStore.cs +++ b/Watcher/Services/IDashboardStore.cs @@ -2,6 +2,6 @@ namespace Watcher.Services; public interface IDashboardStore { - String? NetworkStatus { get; set; } - String? DatabaseStatus { get; set; } + string? NetworkStatus { get; set; } + string? DatabaseStatus { get; set; } } diff --git a/Watcher/Services/ISystemStore.cs b/Watcher/Services/ISystemStore.cs index c2be934..58e1b48 100644 --- a/Watcher/Services/ISystemStore.cs +++ b/Watcher/Services/ISystemStore.cs @@ -2,9 +2,9 @@ namespace Watcher.Services; public interface ISystemStore { - Boolean NewVersionAvailable { get; set; } + bool NewVersionAvailable { get; set; } - Double DatabaseSize { get; set; } + double DatabaseSize { get; set; } int FrontendRefreshIntervalMilliseconds { get; set; } diff --git a/Watcher/Services/NetworkCheck.cs b/Watcher/Services/NetworkCheck.cs index 7c45fc5..fc6f391 100644 --- a/Watcher/Services/NetworkCheck.cs +++ b/Watcher/Services/NetworkCheck.cs @@ -5,12 +5,12 @@ public class NetworkCheck : BackgroundService { private readonly ILogger _logger; - private IDashboardStore _DashboardStore; + private IDashboardStore _dashboardStore; - public NetworkCheck(ILogger logger, IDashboardStore DashboardStore) + public NetworkCheck(ILogger logger, IDashboardStore dashboardStore) { _logger = logger; - _DashboardStore = DashboardStore; + _dashboardStore = dashboardStore; } @@ -21,14 +21,14 @@ public class NetworkCheck : BackgroundService while (await timer.WaitForNextTickAsync(stoppingToken)) { // Hintergrundprozess abwarten - await checkConnectionAsync(); + await CheckConnectionAsync(); // 5 Sekdunden Offset await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); } } - public Task checkConnectionAsync() + public Task CheckConnectionAsync() { _logger.LogInformation("Networkcheck started."); @@ -40,13 +40,13 @@ public class NetworkCheck : BackgroundService PingReply reply = p.Send(host, 3000); if (reply.Status == IPStatus.Success) { - _DashboardStore.NetworkStatus = "online"; + _dashboardStore.NetworkStatus = "online"; _logger.LogInformation("Ping successfull. Watcher is online."); } } catch { - _DashboardStore.NetworkStatus = "offline"; + _dashboardStore.NetworkStatus = "offline"; _logger.LogError("Ping failed. Watcher appears to have no network connection."); // LogEvent erstellen diff --git a/Watcher/Services/SystemMangement.cs b/Watcher/Services/SystemMangement.cs index efd8dcc..77312a0 100644 --- a/Watcher/Services/SystemMangement.cs +++ b/Watcher/Services/SystemMangement.cs @@ -6,12 +6,12 @@ public class SystemManagement : BackgroundService { private readonly ILogger _logger; - private ISystemStore _SystemStore; + private ISystemStore _systemStore; - public SystemManagement(ILogger logger, ISystemStore SystemStore) + public SystemManagement(ILogger logger, ISystemStore systemStore) { _logger = logger; - _SystemStore = SystemStore; + _systemStore = systemStore; } @@ -23,19 +23,19 @@ public class SystemManagement : BackgroundService while (await timer.WaitForNextTickAsync(stoppingToken)) { // Hintergrundprozess abwarten - await checkForNewDockerImageVersion(); + await CheckForNewDockerImageVersion(); // 5 Sekdunden Offset await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); } } - public Task checkForNewDockerImageVersion() + public Task CheckForNewDockerImageVersion() { return Task.CompletedTask; } - public Task createDailySqliteBackup() + public Task CreateDailySqliteBackup() { return Task.CompletedTask; } diff --git a/Watcher/Services/SystemStore.cs b/Watcher/Services/SystemStore.cs index 5f67bbd..3707589 100644 --- a/Watcher/Services/SystemStore.cs +++ b/Watcher/Services/SystemStore.cs @@ -2,9 +2,9 @@ namespace Watcher.Services; public class SystemStore: ISystemStore { - public Boolean NewVersionAvailable { get; set; } + public bool NewVersionAvailable { get; set; } - public Double DatabaseSize { get; set; } + public double DatabaseSize { get; set; } public int FrontendRefreshIntervalMilliseconds { get; set; } diff --git a/Watcher/ViewModels/DashboardViewModel.cs b/Watcher/ViewModels/DashboardViewModel.cs index c05755e..5f69cb6 100644 --- a/Watcher/ViewModels/DashboardViewModel.cs +++ b/Watcher/ViewModels/DashboardViewModel.cs @@ -14,8 +14,8 @@ namespace Watcher.ViewModels public List RecentEvents { get; set; } = new(); public List Containers { get; set; } = new(); - public String? NetworkStatus { get; set; } = "?"; - public String? DatabaseStatus { get; set; } = "?"; + public string? NetworkStatus { get; set; } = "?"; + public string? DatabaseStatus { get; set; } = "?"; public int RefreshIntervalMilliseconds { get; set; } = 30000; diff --git a/Watcher/ViewModels/EditServerViewModel.cs b/Watcher/ViewModels/EditServerViewModel.cs index d708a48..024556b 100644 --- a/Watcher/ViewModels/EditServerViewModel.cs +++ b/Watcher/ViewModels/EditServerViewModel.cs @@ -17,22 +17,22 @@ namespace Watcher.ViewModels public required string Type { get; set; } // Hardware Measurment Warning/Crit Values - public double CPU_Load_Warning { get; set; } = 75.0; - public double CPU_Load_Critical { get; set; } = 90.0; - public double CPU_Temp_Warning { get; set; } = 80.0; - public double CPU_Temp_Critical { get; set; } = 90.0; + public double CpuLoadWarning { get; set; } = 75.0; + public double CpuLoadCritical { get; set; } = 90.0; + public double CpuTempWarning { get; set; } = 80.0; + public double CpuTempCritical { get; set; } = 90.0; - public double RAM_Load_Warning { get; set; } = 85.0; - public double RAM_Load_Critical { get; set; } = 95.0; + public double RamLoadWarning { get; set; } = 85.0; + public double RamLoadCritical { get; set; } = 95.0; - public double GPU_Load_Warning { get; set; } = 75.0; - public double GPU_Load_Critical { get; set; } = 90.0; - public double GPU_Temp_Warning { get; set; } = 70.0; - public double GPU_Temp_Critical { get; set; } = 80.0; + public double GpuLoadWarning { get; set; } = 75.0; + public double GpuLoadCritical { get; set; } = 90.0; + public double GpuTempWarning { get; set; } = 70.0; + public double GpuTempCritical { get; set; } = 80.0; - public double Disk_Usage_Warning { get; set; } = 75.0; - public double Disk_Usage_Critical { get; set; } = 90.0; - public double DISK_Temp_Warning { get; set; } = 34.0; - public double DISK_Temp_Critical { get; set; } = 36.0; + public double DiskUsageWarning { get; set; } = 75.0; + public double DiskUsageCritical { get; set; } = 90.0; + public double DiskTempWarning { get; set; } = 34.0; + public double DiskTempCritical { get; set; } = 36.0; } } \ No newline at end of file diff --git a/Watcher/ViewModels/ServerDetailsViewModel.cs b/Watcher/ViewModels/ServerDetailsViewModel.cs index efb7852..34f2765 100644 --- a/Watcher/ViewModels/ServerDetailsViewModel.cs +++ b/Watcher/ViewModels/ServerDetailsViewModel.cs @@ -31,11 +31,11 @@ public class ServerDetailsViewModel // Database public DateTime CreatedAt { get; set; } = DateTime.UtcNow; - public Boolean IsOnline { get; set; } = false; + public bool IsOnline { get; set; } = false; public DateTime LastSeen { get; set; } - public Boolean IsVerified { get; set; } = false; + public bool IsVerified { get; set; } = false; public int RefreshIntervalMilliseconds { get; set; } = 30000; } \ No newline at end of file diff --git a/Watcher/Views/Server/EditServer.cshtml b/Watcher/Views/Server/EditServer.cshtml index 5587b6b..5162c5b 100644 --- a/Watcher/Views/Server/EditServer.cshtml +++ b/Watcher/Views/Server/EditServer.cshtml @@ -54,32 +54,32 @@
- - - + + +
- - - + + +
- - - + + +
- - - + + +
@@ -90,16 +90,16 @@
- - - + + +
- - - + + +
@@ -110,32 +110,32 @@
- - - + + +
- - - + + +
- - - + + +
- - - + + +
@@ -146,32 +146,32 @@
- - - + + +
- - - + + +
- - - + + +
- - - + + +
diff --git a/Watcher/Watcher.csproj b/Watcher/Watcher.csproj index b150629..4a75b31 100644 --- a/Watcher/Watcher.csproj +++ b/Watcher/Watcher.csproj @@ -13,6 +13,7 @@ + diff --git a/docker-compose.yaml b/docker-compose.yaml index 9e0fc40..18b2d9f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,27 +2,71 @@ services: watcher: image: git.triggermeelmo.com/watcher/watcher-server:${IMAGE_VERSION:-latest} container_name: watcher + + # Resource Management deploy: resources: limits: memory: 200M + cpus: '0.5' + reservations: + memory: 100M + cpus: '0.25' + restart: unless-stopped + + # Security + user: "1000:1000" + + # Health Check + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + # Environment environment: + # Timezone + - TZ=Europe/Berlin + # Application Version (wird aus Image-Tag übernommen) - WATCHER_VERSION=${IMAGE_VERSION:-latest} + + # ASP.NET Core + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://+:5000 + # 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 + + # 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.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" + - "com.watcher.description=Server Monitoring Application" + - "com.watcher.version=${IMAGE_VERSION:-latest}"