diff --git a/.gitea/workflows/development-build.yaml b/.gitea/workflows/development-build.yaml index 97e5dcb..c369463 100644 --- a/.gitea/workflows/development-build.yaml +++ b/.gitea/workflows/development-build.yaml @@ -1,4 +1,4 @@ -name: Development Build and Release +name: Development Build on: push: @@ -10,6 +10,7 @@ env: DOCKER_IMAGE_NAME: 'watcher-server' REGISTRY_URL: 'git.triggermeelmo.com/watcher' DOCKER_PLATFORMS: 'linux/amd64,linux/arm64' + RUNNER_TOOL_CACHE: /toolcache # Runner Tool Cache jobs: build-and-test: @@ -47,11 +48,11 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Login to Gitea Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: - registry: git.triggermeelmo.com - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + registry: ${{ env.REGISTRY_URL}} + username: ${{ secrets.AUTOMATION_USERNAME }} + password: ${{ secrets.AUTOMATION_PASSWORD }} - name: Build and Push Multi-Arch Docker Image run: | diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index c4b4ce2..0000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -# Set the OS, Python version, and other tools you might need -build: - os: ubuntu-24.04 - tools: - python: "3.13" - -# Build documentation in the "docs/" directory with Sphinx -sphinx: - configuration: docs/conf.py - -# Optionally, but recommended, -# declare the Python requirements required to build your documentation -# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html -# python: -# install: -# - requirements: docs/requirements.txt - \ No newline at end of file diff --git a/Watcher/Controllers/ApiController.cs b/Watcher/Controllers/ApiController.cs new file mode 100644 index 0000000..56385a8 --- /dev/null +++ b/Watcher/Controllers/ApiController.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Watcher.Data; + +[Route("[controller]/v1")] +public class ApiController : Controller +{ + private readonly AppDbContext _context; + private readonly ILogger _logger; + + + public ApiController(AppDbContext context, ILogger logger) + { + _context = context; + _logger = logger; + } + + [HttpGet("reference")] + public IActionResult ApiReference() + { + return View(); + } + + [HttpGet("servers")] + public async Task GetAllServers() + { + var Servers = await _context.Servers.OrderBy(s => s.Id).ToListAsync(); + return Ok(); + } +} \ No newline at end of file diff --git a/Watcher/Controllers/DatabaseController.cs b/Watcher/Controllers/DatabaseController.cs index d6cb17f..856edf4 100644 --- a/Watcher/Controllers/DatabaseController.cs +++ b/Watcher/Controllers/DatabaseController.cs @@ -116,7 +116,7 @@ namespace Watcher.Controllers TempData["DumpMessage"] = "SQLite-Dump erfolgreich erstellt."; _logger.LogInformation("SQLite-Dump erfolgreich erstellt."); - return RedirectToAction("UserSettings", "User"); + return RedirectToAction("Settings", "System"); } catch (Exception ex) { diff --git a/Watcher/Controllers/HomeController.cs b/Watcher/Controllers/HomeController.cs index f043b7c..2032eb2 100644 --- a/Watcher/Controllers/HomeController.cs +++ b/Watcher/Controllers/HomeController.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; // Local Namespaces using Watcher.Data; +using Watcher.Models; using Watcher.ViewModels; namespace Watcher.Controllers @@ -41,7 +42,17 @@ namespace Watcher.Controllers OfflineServers = await _context.Servers.CountAsync(s => !s.IsOnline), RunningContainers = await _context.Containers.CountAsync(c => c.IsRunning), FailedContainers = await _context.Containers.CountAsync(c => !c.IsRunning), - LastLogin = user?.LastLogin ?? DateTime.MinValue + LastLogin = user?.LastLogin ?? DateTime.MinValue, + Servers = await _context.Servers + .OrderBy(s => s.Name) + .ToListAsync(), + RecentEvents = await _context.LogEvents + .OrderByDescending(e => e.Timestamp) + .Take(20) + .ToListAsync(), + Containers = await _context.Containers + .OrderBy(s => s.Name) + .ToListAsync() }; return View(viewModel); @@ -49,7 +60,7 @@ namespace Watcher.Controllers // Funktion für /Views/Home/Index.cshtml um das DashboardStats-Partial neu zu laden. // Die Funktion wird nicht direkt aufgerufen, sondern nur der /Home/DashboardStats Endpoint angefragt. - public IActionResult DashboardStats() + public async Task DashboardStats() { var servers = _context.Servers.ToList(); var containers = _context.Containers.ToList(); @@ -58,14 +69,20 @@ namespace Watcher.Controllers var model = new DashboardViewModel { - ActiveServers = servers.Count(s => (now - s.LastSeen).TotalSeconds <= 120), - OfflineServers = servers.Count(s => (now - s.LastSeen).TotalSeconds > 120), - - //TODO: anwendbar, wenn Container implementiert wurden. - //RunningContainers = containers.Count(c => (now - c.LastSeen).TotalSeconds <= 120), - //FailedContainers = containers.Count(c => (now - c.LastSeen).TotalSeconds > 120), - - LastLogin = DateTime.Now + ActiveServers = await _context.Servers.CountAsync(s => s.IsOnline), + OfflineServers = await _context.Servers.CountAsync(s => !s.IsOnline), + RunningContainers = await _context.Containers.CountAsync(c => c.IsRunning), + FailedContainers = await _context.Containers.CountAsync(c => !c.IsRunning), + Servers = await _context.Servers + .OrderBy(s => s.Name) + .ToListAsync(), + RecentEvents = await _context.LogEvents + .OrderByDescending(e => e.Timestamp) + .Take(20) + .ToListAsync(), + Containers = await _context.Containers + .OrderBy(s => s.Name) + .ToListAsync() }; return PartialView("_DashboardStats", model); diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 3357bf2..5ab60ff 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -55,26 +55,26 @@ public class MetricDto public double GPU_Temp { get; set; } // deg C - public double GPU_Vram_Size { get; set; } // GB + public double GPU_Vram_Size { get; set; } // Bytes - public double GPU_Vram_Usage { get; set; } // % + public double GPU_Vram_Load { get; set; } // % // RAM - public double RAM_Size { get; set; } // GB + public double RAM_Size { get; set; } // Bytes public double RAM_Load { get; set; } // % // Disks - public double DISK_Size { get; set; } // GB + public double DISK_Size { get; set; } // Bytes - public double DISK_Usage { get; set; } // % + public double DISK_Usage { get; set; } // Bytes - public double DISK_Temp { get; set; } // deg C + public double DISK_Temp { get; set; } // deg C (if available) // Network - public double NET_In { get; set; } // Bit + public double NET_In { get; set; } // Bytes/s - public double NET_Out { get; set; } // Bit + public double NET_Out { get; set; } // Bytes/s } @@ -173,26 +173,28 @@ public class MonitoringController : Controller if (server != null) { + // neues Metric-Objekt erstellen var NewMetric = new Metric { Timestamp = DateTime.UtcNow, ServerId = dto.ServerId, - CPU_Load = dto.CPU_Load, - CPU_Temp = dto.CPU_Temp, - GPU_Load = dto.GPU_Load, - GPU_Temp = dto.GPU_Temp, - GPU_Vram_Size = dto.GPU_Vram_Size, - GPU_Vram_Usage = dto.GPU_Vram_Usage, - RAM_Load = dto.RAM_Load, - RAM_Size = dto.RAM_Size, - DISK_Size = dto.RAM_Size, - DISK_Usage = dto.DISK_Usage, - DISK_Temp = dto.DISK_Temp, - NET_In = dto.NET_In, - NET_Out = 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); await _context.SaveChangesAsync(); @@ -281,4 +283,40 @@ public class MonitoringController : Controller return Ok(data); } + + // Metric Input Byte zu Gigabyte umwandeln + public static double calculateGigabyte(double metric_input) + { + // *10^-9 um auf Gigabyte zu kommen + double calculatedValue = metric_input * Math.Pow(10, -9); + + // Auf 2 Nachkommastellen runden + double calculatedValue_s = sanitizeInput(calculatedValue); + + return calculatedValue_s; + } + + // Metric Input Byte/s zu Megabit/s umrechnen + //TODO + public static double calculateMegabit(double metric_input) + { + // *10^-9 um auf Gigabyte zu kommen + double calculatedValue = metric_input * Math.Pow(10, -9); + + // Auf 2 Nachkommastellen runden + double calculatedValue_s = sanitizeInput(calculatedValue); + + return calculatedValue_s; + } + + // Degree Input auf zwei Nachkommastellen runden + public static double sanitizeInput(double metric_input) + { + Math.Round(metric_input, 2); + + return metric_input; + } + + + } \ No newline at end of file diff --git a/Watcher/Controllers/SystemController.cs b/Watcher/Controllers/SystemController.cs new file mode 100644 index 0000000..e39ff2c --- /dev/null +++ b/Watcher/Controllers/SystemController.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Watcher.Data; +using Watcher.ViewModels; + +namespace Watcher.Controllers; + +[Authorize] +[Route("[controller]")] +public class SystemController : Controller +{ + private readonly AppDbContext _context; + private readonly ILogger _logger; + + public SystemController(AppDbContext context, ILogger logger) + { + _context = context; + _logger = logger; + } + + // Edit-Form anzeigen + [HttpGet("Settings")] + //public async Task Settings() + public IActionResult Settings() + { + ViewBag.DbProvider = "Microsoft.EntityFrameworkCore.Sqlite"; + ViewBag.mail = "test@mail.com"; + ViewBag.IdentityProvider = "Local"; + ViewBag.ServerVersion = "v0.1.0"; + return View(); + } + + // HttpPost + // public IActionResult UpdateNotifications(){} + +} diff --git a/Watcher/Controllers/UserController.cs b/Watcher/Controllers/UserController.cs index a9700c7..b227618 100644 --- a/Watcher/Controllers/UserController.cs +++ b/Watcher/Controllers/UserController.cs @@ -55,6 +55,7 @@ public class UserController : Controller var user = _context.Users.FirstOrDefault(u => u.Username == username); if (user == null) return NotFound(); + var model = new EditUserViewModel { Username = user.Username @@ -90,31 +91,6 @@ public class UserController : Controller return RedirectToAction("Index", "Home"); } - - // Edit-Form anzeigen - [Authorize] - [HttpGet] - public IActionResult UserSettings() - { - var username = User.Identity?.Name; - Console.WriteLine("gefundener User: " + username); - var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToList(); - - var user = _context.Users.FirstOrDefault(u => u.Username == username); - if (user == null) return NotFound(); - - var DbProvider = _context.Database.ProviderName; - var mail = user.Email; - - ViewBag.Name = username; - ViewBag.mail = mail; - ViewBag.Claims = claims; - ViewBag.IdentityProvider = user.IdentityProvider; - ViewBag.DbProvider = DbProvider; - - return View(); - } - // Edit speichern [Authorize] [HttpPost] diff --git a/Watcher/Models/HealthStatus.cs b/Watcher/Models/HealthStatus.cs new file mode 100644 index 0000000..9e0012b --- /dev/null +++ b/Watcher/Models/HealthStatus.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Watcher.Models +{ + public class HealthStatus + { + public DateTime Timestamp { get; set; } + + public bool NetworkOk { get; set; } + public bool DatabaseOk { get; set; } + public List Issues { get; set; } = new List(); + // Optional weitere Checks + public bool ApiOk { get; set; } + public bool DiskOk { get; set; } + } +} diff --git a/Watcher/Program.cs b/Watcher/Program.cs index 0c4e404..c20d108 100644 --- a/Watcher/Program.cs +++ b/Watcher/Program.cs @@ -1,14 +1,12 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Sqlite; -using Microsoft.IdentityModel.Tokens; using Serilog; using Watcher.Data; using Watcher.Models; +//using Watcher.Services; +//using Watcher.Workers; var builder = WebApplication.CreateBuilder(args); @@ -32,7 +30,6 @@ builder.Host.UseSerilog(); // Add services to the container. builder.Services.AddControllersWithViews(); - // HttpContentAccessor builder.Services.AddHttpContextAccessor(); @@ -112,7 +109,11 @@ builder.Services.AddAuthentication() var db = ctx.HttpContext.RequestServices.GetRequiredService(); var principal = ctx.Principal; +#pragma warning disable CS8602 // Dereference of a possibly null reference. + var pocketId = principal.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value; +#pragma warning restore CS8602 // Dereference of a possibly null reference. + var preferredUsername = principal.FindFirst("preferred_username")?.Value; var email = principal.FindFirst("email")?.Value; diff --git a/Watcher/ViewModels/DashboardViewModel.cs b/Watcher/ViewModels/DashboardViewModel.cs index 02bf40f..624c9bd 100644 --- a/Watcher/ViewModels/DashboardViewModel.cs +++ b/Watcher/ViewModels/DashboardViewModel.cs @@ -1,3 +1,5 @@ +using Watcher.Models; + namespace Watcher.ViewModels { public class DashboardViewModel @@ -7,5 +9,10 @@ namespace Watcher.ViewModels public int RunningContainers { get; set; } public int FailedContainers { get; set; } public DateTime LastLogin { get; set; } + + public List Servers { get; set; } = new(); + public List RecentEvents { get; set; } = new(); + public List Containers { get; set; } = new(); + } } diff --git a/Watcher/Views/API/ApiReference.cshtml b/Watcher/Views/API/ApiReference.cshtml new file mode 100644 index 0000000..11a76d5 --- /dev/null +++ b/Watcher/Views/API/ApiReference.cshtml @@ -0,0 +1 @@ +

i

\ No newline at end of file diff --git a/Watcher/Views/Auth/Login.cshtml b/Watcher/Views/Auth/Login.cshtml index 49408c0..a4a822a 100644 --- a/Watcher/Views/Auth/Login.cshtml +++ b/Watcher/Views/Auth/Login.cshtml @@ -5,55 +5,10 @@ var oidc = ViewBag.oidc; } - + + + + - diff --git a/Watcher/Views/Database/ManageSqlDumps.cshtml b/Watcher/Views/Database/ManageSqlDumps.cshtml index 059aa61..6e3f147 100644 --- a/Watcher/Views/Database/ManageSqlDumps.cshtml +++ b/Watcher/Views/Database/ManageSqlDumps.cshtml @@ -3,6 +3,11 @@ ViewData["Title"] = "Datenbank-Dumps"; } + + + + +

Datenbank-Dumps

@if (TempData["Success"] != null) @@ -14,7 +19,7 @@
@TempData["Error"]
} - +
diff --git a/Watcher/Views/Home/Index.cshtml b/Watcher/Views/Home/Index.cshtml index 6b61e39..f39e9c4 100644 --- a/Watcher/Views/Home/Index.cshtml +++ b/Watcher/Views/Home/Index.cshtml @@ -3,6 +3,9 @@ ViewData["Title"] = "Dashboard"; } + + +

Dashboard

@@ -11,25 +14,6 @@ @await Html.PartialAsync("_DashboardStats", Model) -
- -
-
-

- Systeminfo -

-

- - Benutzer: @User.FindFirst("preferred_username")?.Value -

-

- - Letzter Login: @Model.LastLogin.ToString("g") -

-
-
-
- @section Scripts {
Dateiname