6 Commits

Author SHA1 Message Date
a2c6071960 aa
All checks were successful
Gitea CI/CD / dotnet-build-and-test (push) Successful in 10m4s
Gitea CI/CD / Set Tag Name (push) Successful in 5s
Gitea CI/CD / docker-build-and-push (push) Successful in 11m22s
Gitea CI/CD / Create Tag (push) Successful in 6s
2026-01-09 18:09:43 +01:00
4523867a61 Added Container_Card to dashboard
All checks were successful
Gitea CI/CD / dotnet-build-and-test (push) Successful in 10m2s
Gitea CI/CD / Set Tag Name (push) Successful in 5s
Gitea CI/CD / docker-build-and-push (push) Successful in 11m30s
Gitea CI/CD / Create Tag (push) Successful in 5s
2026-01-09 12:57:48 +01:00
8727aff861 AddServer Call for Development 2026-01-09 12:41:32 +01:00
301d2309c9 get-server api call returns serverlist 2026-01-09 11:57:02 +01:00
7a096ee29c removed possible null reference 2026-01-09 11:38:31 +01:00
1d734f2951 Swagger Configration and move Agent API Calls to APIController 2026-01-09 10:36:47 +01:00
12 changed files with 296 additions and 125 deletions

View File

@@ -1,13 +1,16 @@
// Get Methoden um Metrics abzugreifen
// Get Methden um Informationen über den Status des Servers einzuholen
using System.Diagnostics; using System.Diagnostics;
using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks;
using watcher_monitoring.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using watcher_monitoring.Models;
using watcher_monitoring.Data; using watcher_monitoring.Data;
using watcher_monitoring.Attributes; using watcher_monitoring.Attributes;
using System.Threading.Tasks; using watcher_monitoring.Payloads;
using Microsoft.EntityFrameworkCore; using System.Net;
namespace watcher_monitoring.Controllers; namespace watcher_monitoring.Controllers;
@@ -26,19 +29,68 @@ public class APIController : Controller
_logger = logger; _logger = logger;
} }
// Server Calls // API Calls
[HttpGet("get-server")] [HttpGet("get-server")]
public async Task<IActionResult> Servers() public async Task<IActionResult> Servers()
{ {
List<Server> servers = await _context.Servers.ToListAsync(); List<Server> servers = await _context.Servers.ToListAsync();
return Ok(); return Ok(servers);
} }
// DEVELOPMENT ONLY
[HttpPost("add-server")] [HttpPost("add-server")]
public async Task<IActionResult> AddServer() public async Task<IActionResult> AddServer([FromBody] Server serverDto)
{ {
return Ok(); // payload check
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
_logger.LogError("Invalid server payload");
return BadRequest(new { error = "Invalid server payload", details = errors });
}
try
{
// Check if server with same IP already exists
var existingServer = await _context.Servers
.FirstOrDefaultAsync(s => s.IPAddress == serverDto.IPAddress);
if (existingServer != null)
{
_logger.LogWarning("Server mit IP-Adresse {ip} existiert bereits", serverDto.IPAddress);
return BadRequest(new { error = "Server mit dieser IP-Adresse existiert bereits" });
}
Server server = new Server
{
Name = serverDto.Name,
IPAddress = serverDto.IPAddress,
CpuType = serverDto.CpuType,
CpuCores = serverDto.CpuCores,
GpuType = serverDto.GpuType,
RamSize = serverDto.RamSize,
DiskSpace = serverDto.DiskSpace,
IsOnline = serverDto.IsOnline,
IsVerified = serverDto.IsVerified
};
_context.Servers.Add(server);
await _context.SaveChangesAsync();
_logger.LogInformation("Server '{name}' mit IP {ip} erfolgreich hinzugefügt", server.Name, server.IPAddress);
return Ok(new { message = "Server erfolgreich hinzugefügt", serverId = server.Id });
}
catch (Exception ex)
{
_logger.LogError(ex, "Fehler beim Hinzufügen des Servers");
return BadRequest(new { error = "Fehler beim Hinzufügen des Servers", details = ex.Message });
}
} }
[HttpDelete("delete-server/{id}")] [HttpDelete("delete-server/{id}")]
@@ -71,7 +123,7 @@ public class APIController : Controller
public async Task<IActionResult> Containers() public async Task<IActionResult> Containers()
{ {
List<Container> containers = await _context.Containers.ToListAsync(); List<Container> containers = await _context.Containers.ToListAsync();
return Ok(); return Ok(containers);
} }
[HttpDelete("delete-container")] [HttpDelete("delete-container")]
@@ -95,4 +147,135 @@ public class APIController : Controller
return BadRequest(); return BadRequest();
} }
} }
// Agent Calls
// Registration Endpoint for watcher-agent
[HttpPost("agent-register")]
public async Task<IActionResult> Register([FromBody] RegistrationDto dto)
{
// payload check
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
_logger.LogError("Invalid registration payload");
return BadRequest(new { error = "Invalid registration payload", details = errors });
}
try {
Server newServer = new Server
{
Name = dto.hostName,
IPAddress = dto.ipAddress
};
_context.Servers.Add(newServer);
await _context.SaveChangesAsync();
var server = await _context.Servers.FindAsync(dto.ipAddress);
return Ok(server.Id);
} catch (Exception ex)
{
Console.WriteLine(ex.Message);
_logger.LogError(ex.Message);
return BadRequest();
}
}
// Hardware Configuration Endpoint for watcher-agent
[HttpPost("agent-hardware")]
public async Task<IActionResult> HardwareConfiguration ([FromBody] HardwareDto dto)
{
// payload check
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
_logger.LogError("Invalid hardware configuration");
return BadRequest(new { error = "Invalid Hardware Configuration Payload", details = errors });
}
try
{
// Find Server in Database
var server = await _context.Servers.FindAsync(dto.id);
if (server == null)
{
_logger.LogError("Server not found");
return BadRequest("Server not found");
}
// Add Hardware Configuration
server.CpuType = dto.cpuType;
server.CpuCores = dto.cpuCores;
server.GpuType = dto.gpuType;
server.RamSize = dto.ramSize;
// TODO: Diskspace fehlt
await _context.SaveChangesAsync();
_logger.LogInformation("Harware configuration successfull for server {server}", server.Name);
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return BadRequest(ex.Message);
}
return Ok();
}
// Server-Metrics endpoint for watcher-agent
[HttpPost("agent-server-metrics/{id}")]
public async Task<IActionResult> ServerMetrics ([FromBody] MetricDto dto)
{
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
_logger.LogError("Invalid monitoring payload");
return BadRequest(new { error = "Invalid monitoring payload", details = errors });
}
var server = await _context.Servers.FindAsync(dto.id);
if (server != null)
{
// neues Objekt mit Typ Metric anlegen
// Metric in Datenbank eintragen
return Ok();
}
else
{
_logger.LogError("metric cannot be added to database");
return BadRequest();
}
}
// Service-Detection endpoint for watcher-agent
[HttpPost("agent-container-detection")]
public async Task<IActionResult> ContainerDetection ([FromBody] HardwareDto dto)
{
return Ok();
}
// Container-Metrics endpoint for watcher-agent
[HttpPost("agent-container-metrics")]
public async Task<IActionResult> ContainerMetrics ([FromBody] HardwareDto dto)
{
return Ok();
}
} }

View File

@@ -25,11 +25,14 @@ public class HomeController : Controller
public async Task<IActionResult> Index() public async Task<IActionResult> Index()
{ {
List<Server> servers = await _context.Servers.ToListAsync(); List<Server> servers = await _context.Servers.ToListAsync();
List<Container> containers = await _context.Containers.ToListAsync();
ViewBag.Containers = containers;
ViewBag.ContainerCount = containers.Count();
ViewBag.TotalServers = servers.Count; ViewBag.TotalServers = servers.Count;
ViewBag.OnlineServers = servers.Count(s => s.IsOnline); ViewBag.OnlineServers = servers.Count(s => s.IsOnline);
ViewBag.OfflineServers = servers.Count(s => !s.IsOnline); ViewBag.OfflineServers = servers.Count(s => !s.IsOnline);
ViewBag.ServiceCount = 8;
ViewBag.Servers = servers; ViewBag.Servers = servers;
return View(); return View();

View File

@@ -1,12 +1,11 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using watcher_monitoring.Payloads;
using watcher_monitoring.Data; using watcher_monitoring.Data;
using watcher_monitoring.Models;
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace watcher_monitoring.Controllers; namespace watcher_monitoring.Controllers;
[Authorize]
[Route("[controller]")] [Route("[controller]")]
public class MonitoringController : Controller public class MonitoringController : Controller
{ {
@@ -20,104 +19,10 @@ public class MonitoringController : Controller
_logger = logger; _logger = logger;
} }
// Registration Endpoint for watcher-agent [HttpGet("container")]
[HttpPost("register")] public async Task<IActionResult> ContainerIndex()
public async Task<IActionResult> Register([FromBody] RegistrationDto dto)
{ {
// payload check return View();
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
_logger.LogError("Invalid registration payload");
return BadRequest(new { error = "Invalid registration payload", details = errors });
}
try {
Server server = new Server
{
Name = "test",
IPAddress = dto.IpAddress
};
_context.Servers.Add(server);
await _context.SaveChangesAsync();
return Ok();
} catch (Exception ex)
{
Console.WriteLine(ex.Message);
_logger.LogError(ex.Message);
return BadRequest();
}
}
// Hardware Configuration Endpoint for watcher-agent
[HttpPost("hardware-configuration")]
public async Task<IActionResult> HardwareConfiguration ([FromBody] HardwareDto dto)
{
// payload check
if (!ModelState.IsValid)
{
var errors = ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage)
.ToList();
_logger.LogError("Invalid hardware configuration");
return BadRequest(new { error = "Invalid Hardware Configuration Payload", details = errors });
}
try
{
// Find Server in Database
var server = await _context.Servers.FindAsync(dto.Id);
// Add Hardware Configuration
try
{
server.CpuType = dto.CpuType;
server.CpuCores = dto.CpuCores;
server.GpuType = dto.GpuType;
server.RamSize = dto.RamSize;
// Diskspace fehlt
_logger.LogInformation("Harware configuration successfull for server {server}", server.Name);
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
return Ok();
}
// Server-Metrics endpoint for watcher-agent
[HttpPost("server-metrics/{id}")]
public async Task<IActionResult> ServerMetrics ([FromBody] HardwareDto dto)
{
return Ok();
}
// Service-Detection endpoint for watcher-agent
[HttpPost("container-detection")]
public async Task<IActionResult> ContainerDetection ([FromBody] HardwareDto dto)
{
return Ok();
}
// Container-Metrics endpoint for watcher-agent
[HttpPost("container-metrics/{id}")]
public async Task<IActionResult> ContainerMetrics ([FromBody] HardwareDto dto)
{
return Ok();
} }
} }

View File

@@ -6,21 +6,18 @@ namespace watcher_monitoring.Payloads;
public class HardwareDto public class HardwareDto
{ {
[Required] [Required]
public required int Id; public required int id;
[Required]
public string? IpAddress { get; set; }
// Hardware Info // Hardware Info
[Required] [Required]
public string? CpuType { get; set; } public string? cpuType { get; set; }
[Required] [Required]
public int CpuCores { get; set; } public int cpuCores { get; set; }
[Required] [Required]
public string? GpuType { get; set; } public string? gpuType { get; set; }
[Required] [Required]
public double RamSize { get; set; } public double ramSize { get; set; }
} }

View File

@@ -0,0 +1,44 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace watcher_monitoring.Payloads;
public class MetricDto
{
// Server Identity
[Required]
public int id { get; set; }
// Hardware Metrics
// CPU
public double cpuLoad { get; set; } // %
public double cpuTemp { get; set; } // deg C
// GPU
public double gpuLoad { get; set; } // %
public double gpuTemp { get; set; } // deg C
public double vRamSize { get; set; } // Bytes
public double vRamLoad { get; set; } // %
// RAM
public double ramSize { get; set; } // Bytes
public double ramLoad { get; set; } // %
// Disks
public double diskSize { get; set; } // Bytes
public double diskLoad { get; set; } // Bytes
public double diskTempp { get; set; } // deg C (if available)
// Network
public double netIn { get; set; } // Bytes/s
public double netOut { get; set; } // Bytes/s
}

View File

@@ -7,5 +7,7 @@ namespace watcher_monitoring.Payloads;
public class RegistrationDto public class RegistrationDto
{ {
[Required] [Required]
public required string IpAddress { get; set; } public required string ipAddress { get; set; }
public required string hostName { get; set; }
} }

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
@@ -65,6 +66,17 @@ builder.Services.AddHealthChecks();
builder.Services.AddSwaggerGen(options => builder.Services.AddSwaggerGen(options =>
{ {
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Watcher-Server API", Version = "v1" }); options.SwaggerDoc("v1", new OpenApiInfo { Title = "Watcher-Server API", Version = "v1" });
// Nur API-Controller dokumentieren (mit [ApiController]-Attribut)
options.DocInclusionPredicate((docName, apiDesc) =>
{
var controllerActionDescriptor = apiDesc.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;
if (controllerActionDescriptor == null) return false;
// Nur Controller mit [ApiController]-Attribut einbeziehen
return controllerActionDescriptor.ControllerTypeInfo
.GetCustomAttributes(typeof(ApiControllerAttribute), true).Any();
});
}); });
var app = builder.Build(); var app = builder.Build();

View File

@@ -33,14 +33,14 @@
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
<div class="metric-card"> <div class="metric-card">
<div class="metric-label">Totoal Services</div> <div class="metric-label">Total Containers</div>
<div class="metric-value">@ViewBag.ServiceCount</div> <div class="metric-value">@ViewBag.ContainerCount</div>
</div> </div>
</div> </div>
</div> </div>
<div class="row g-4"> <div class="row g-4">
<div class="col-lg-8"> <div class="col-lg-4">
<div class="card"> <div class="card">
<h2 class="card-title">Monitored Servers</h2> <h2 class="card-title">Monitored Servers</h2>
<ul class="server-list"> <ul class="server-list">
@@ -68,6 +68,31 @@
</ul> </ul>
</div> </div>
</div> </div>
<div class="col-lg-4">
<div class="card">
<h2 class="card-title">Monitored Containers</h2>
<ul class="server-list">
@if (ViewBag.Containers != null && ViewBag.Containers.Count > 0)
{
@foreach (var container in ViewBag.Containers)
{
<li class="server-item">
<div class="server-info">
<span class="server-name">@container.Name</span>
<span class="server-ip">Container Image</span>
</div>
</li>
}
}
else
{
<li class="text-center py-4" style="color: var(--text-muted)">
No Containers added yet
</li>
}
</ul>
</div>
</div>
<div class="col-lg-4"> <div class="col-lg-4">
<div class="card"> <div class="card">

View File

@@ -58,7 +58,7 @@
</form> </form>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<a style="display: inline-block; vertical-align: middle; margin-right: 8px;"> <a style="display: inline-block; vertical-align: middle; margin-right: 8px;"
asp-area="" asp-controller="User" asp-action="Index"><svg width="16" height="16" asp-area="" asp-controller="User" asp-action="Index"><svg width="16" height="16"
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
style="display: inline-block; vertical-align: middle; margin-right: 8px;"> style="display: inline-block; vertical-align: middle; margin-right: 8px;">