using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Watcher.Data; using Watcher.Models; using Watcher.ViewModels; namespace Watcher.Controllers; public class RegistrationDto { // Server Identity [Required] public int Id { get; set; } [Required] public string? IpAddress { get; set; } // Hardware Info [Required] public string? CpuType { get; set; } [Required] public int CpuCores { get; set; } [Required] public string? GpuType { get; set; } [Required] public double RamSize { get; set; } } public class MetricDto { // Server Identity [Required] public int ServerId { get; set; } [Required] public string? IpAddress { get; set; } // Hardware Metrics // CPU public double CPU_Load { get; set; } // % public double CPU_Temp { get; set; } // deg C // GPU public double GPU_Load { get; set; } // % public double GPU_Temp { get; set; } // deg C public double GPU_Vram_Size { get; set; } // Bytes public double GPU_Vram_Load { get; set; } // % // RAM public double RAM_Size { get; set; } // Bytes public double RAM_Load { get; set; } // % // Disks public double DISK_Size { get; set; } // Bytes public double DISK_Usage { get; set; } // Bytes public double DISK_Temp { get; set; } // deg C (if available) // Network public double NET_In { get; set; } // Bytes/s public double NET_Out { get; set; } // Bytes/s } [ApiController] [Route("[controller]")] public class MonitoringController : Controller { private readonly AppDbContext _context; private readonly ILogger _logger; public MonitoringController(AppDbContext context, ILogger logger) { _context = context; _logger = logger; } // Endpoint, an dem sich neue Agents registrieren [HttpPost("register-agent-by-id")] public async Task Register([FromBody] RegistrationDto dto) { // Gültigkeit des Payloads prüfen if (!ModelState.IsValid) { var errors = ModelState.Values .SelectMany(v => v.Errors) .Select(e => e.ErrorMessage) .ToList(); _logger.LogError("Fehlerhafter Registrierungs-Payload."); return BadRequest(new { error = "Ungültiger Payload", details = errors }); } // Server in Datenbank finden var server = await _context.Servers .FirstOrDefaultAsync(s => s.IPAddress == dto.IpAddress); if (server != null) { // Serverdaten in Datenbank eintragen server.CpuType = dto.CpuType; server.CpuCores = dto.CpuCores; server.GpuType = dto.GpuType; server.RamSize = dto.RamSize; // Änderungen in Datenbank speichern await _context.SaveChangesAsync(); // Success _logger.LogInformation("Agent für '{server}' erfolgreich registriert.", server.Name); return Ok(); } _logger.LogError("Kein Server für Registrierung gefunden"); return NotFound("No Matching Server found."); } [HttpGet("server-id-by-ip")] public async Task GetServerIdByIp([FromQuery] string IpAddress) { var server = await _context.Servers .FirstOrDefaultAsync(s => s.IPAddress == IpAddress); if (server == null) return NotFound(); return Ok(new { id = server.Id, IpAddress = server.IPAddress }); } // Enpoint, an den Agents Ihre gesammelten Daten senden [HttpPost("metric")] public async Task Receive([FromBody] MetricDto dto) { // Gültigkeit des Payloads prüfen if (!ModelState.IsValid) { var errors = ModelState.Values .SelectMany(v => v.Errors) .Select(e => e.ErrorMessage) .ToList(); _logger.LogError("Ungültiger Monitoring-Payload."); return BadRequest(new { error = "Ungültiger Payload", details = errors }); } // Server in Datenbank finden var server = await _context.Servers .FirstOrDefaultAsync(s => s.IPAddress == dto.IpAddress); if (server != null) { // neues Metric-Objekt erstellen 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) }; try { // Metric Objekt in Datenbank einfügen _context.Metrics.Add(NewMetric); await _context.SaveChangesAsync(); _logger.LogInformation("Monitoring-Daten für '{server}' empfangen", server.Name); return Ok(); } catch { // Alert triggern _logger.LogError("Metric für {server} konnte nicht in Datenbank geschrieben werden.", server.Name); return BadRequest(); } } _logger.LogError("Kein Server für eingegangenen Monitoring-Payload gefunden"); return NotFound("No Matching Server found."); } // Durchschnittliche Werte Berechnen [HttpGet("median")] public async Task CalculateMedian(string Metric, int HoursToMonitor, int ServerId) { // Aktuelle Zeit - X Stunden = letzter Wert, der berücksichtigt werden soll DateTime TimeToMonitor = DateTime.Now.AddHours(-HoursToMonitor); // Alle Metrics von Server "ServerId" finden, die in der festgelegten Zeitspanne liegen var MetricsInTimeFrame = await _context.Metrics .Where(e => e.Timestamp >= TimeToMonitor && e.ServerId == ServerId) .ToListAsync(); // return Action (MetricsInTimeFrame) return NotFound(); } [HttpGet("cpu-usage")] public async Task 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 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 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); } // 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; } }