Serilog konfiguriert und Code in Heartbeat- Home- und Auth-Controller aufräumen gestartet

This commit is contained in:
2025-06-25 18:01:03 +02:00
parent d1e9c18361
commit 4f8197c500
7 changed files with 93 additions and 32 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,6 @@
# Per-Use special Files and Directories # Per-Use special Files and Directories
/persistance/*.db /persistance/*.db
/logs /logs/*.log
*.env *.env
/wwwroot/downloads/sqlite/*.sql /wwwroot/downloads/sqlite/*.sql

View File

@@ -22,12 +22,19 @@ public class AuthController : Controller
private readonly AppDbContext _context; private readonly AppDbContext _context;
private readonly AppSettings _settings; private readonly AppSettings _settings;
public AuthController(AppDbContext context, IOptions<AppSettings> options) // Logging einbinden
private readonly ILogger<AuthController> _logger;
public AuthController(AppDbContext context, IOptions<AppSettings> options, ILogger<AuthController> logger)
{ {
_context = context; _context = context;
_settings = options.Value; _settings = options.Value;
_logger = logger;
} }
// Login Seite anzeigen
[HttpGet] [HttpGet]
public IActionResult Login(string? returnUrl = null) public IActionResult Login(string? returnUrl = null)
{ {
@@ -41,6 +48,7 @@ public class AuthController : Controller
} }
// Login mit lokalem User
[HttpPost] [HttpPost]
public async Task<IActionResult> Login(LoginViewModel model) public async Task<IActionResult> Login(LoginViewModel model)
{ {
@@ -55,20 +63,23 @@ public class AuthController : Controller
} }
var claims = new List<Claim> var claims = new List<Claim>
{ {
new Claim(ClaimTypes.Name, user.Username), new Claim(ClaimTypes.Name, user.Username),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
}; };
var identity = new ClaimsIdentity(claims, "local"); var identity = new ClaimsIdentity(claims, "local");
var principal = new ClaimsPrincipal(identity); var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync("Cookies", principal); await HttpContext.SignInAsync("Cookies", principal);
_logger.LogInformation("lokaler User angemeldet: " + user.Username);
return Redirect("Home/Index"); return Redirect("Home/Index");
} }
// Login mit OIDC-Provider
public IActionResult SignIn() public IActionResult SignIn()
{ {
return Challenge(new AuthenticationProperties return Challenge(new AuthenticationProperties
@@ -77,6 +88,9 @@ public class AuthController : Controller
}, "oidc"); }, "oidc");
} }
// Logout
[HttpPost] [HttpPost]
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
public async Task<IActionResult> Logout() public async Task<IActionResult> Logout()
@@ -89,26 +103,32 @@ public class AuthController : Controller
await HttpContext.SignOutAsync("Cookies"); await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc", props); await HttpContext.SignOutAsync("oidc", props);
_logger.LogInformation("User abgemeldet");
return Redirect("/"); // nur als Fallback return Redirect("/"); // nur als Fallback
} }
// Anzeigen der User-Informationen
[Authorize] [Authorize]
public IActionResult Info() public IActionResult Info()
{ {
var username = User.Identity?.Name;
Console.WriteLine("gefundener User: " + username);
var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToList(); var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToList();
var Identity_User = User.Identity?.Name;
var user = _context.Users.FirstOrDefault(u => u.Username == username); var user = _context.Users.FirstOrDefault(u => u.Username == Identity_User);
if (user == null) return NotFound(); if (user == null) return NotFound();
// Anzeigedaten
var DbProvider = _context.Database.ProviderName; var DbProvider = _context.Database.ProviderName;
var username = user.Username;
var mail = user.Email; var mail = user.Email;
var Id = user.Id; var Id = user.Id;
ViewBag.Name = username; // Anzeigedaten an View übergeben
ViewBag.Claims = claims; ViewBag.Claims = claims;
ViewBag.Name = username;
ViewBag.Mail = mail; ViewBag.Mail = mail;
ViewBag.Id = Id; ViewBag.Id = Id;
@@ -154,9 +174,12 @@ public class AuthController : Controller
// Eventuell hier das Auth-Cookie erneuern, wenn Username sich ändert // Eventuell hier das Auth-Cookie erneuern, wenn Username sich ändert
_logger.LogTrace("Passwort-Change durchgeführt");
return RedirectToAction("Index", "Home"); return RedirectToAction("Index", "Home");
} }
// Edit-Form anzeigen // Edit-Form anzeigen
[Authorize] [Authorize]
[HttpGet] [HttpGet]

View File

@@ -1,13 +1,10 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Watcher.Data; using Watcher.Data;
using Watcher.Models; using Watcher.Models;
using Watcher.ViewModels;
// eingehender Heartbeat-Payload
public class HeartbeatDto public class HeartbeatDto
{ {
public string? IpAddress { get; set; } public string? IpAddress { get; set; }
@@ -18,18 +15,27 @@ public class HeartbeatDto
public class HeartbeatController : Controller public class HeartbeatController : Controller
{ {
// Datenbankverbindung
private readonly AppDbContext _context; private readonly AppDbContext _context;
public HeartbeatController(AppDbContext context) // Logging einbinden
private readonly ILogger<HeartbeatController> _logger;
public HeartbeatController(AppDbContext context, ILogger<HeartbeatController> logger)
{ {
_context = context; _context = context;
_logger = logger;
} }
// Endpoint um Heartbeat-Payloads zu empfangen
[HttpPost("receive")] [HttpPost("receive")]
public async Task<IActionResult> Receive([FromBody] HeartbeatDto dto) public async Task<IActionResult> Receive([FromBody] HeartbeatDto dto)
{ {
_logger.LogInformation("Heartbeat empfangen");
if (string.IsNullOrWhiteSpace(dto.IpAddress)) if (string.IsNullOrWhiteSpace(dto.IpAddress))
{ {
_logger.LogWarning("Eingehender Payload enthält keine IP-Adresse");
return BadRequest("Missing IP address."); return BadRequest("Missing IP address.");
} }
@@ -40,9 +46,11 @@ public class HeartbeatController : Controller
{ {
server.LastSeen = DateTime.UtcNow; server.LastSeen = DateTime.UtcNow;
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
_logger.LogInformation("Heartbeat von {server} in Datenbank eingetragen.", server.Name);
return Ok(); return Ok();
} }
_logger.LogWarning("Eingehender Payload kann keiner bekannten IP-Adresse zugewiesen werden.");
return NotFound("No matching server found."); return NotFound("No matching server found.");
} }
} }

View File

@@ -1,9 +1,9 @@
// .NET Packages
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Security.Claims; // Local Namespaces
using Watcher.Data; using Watcher.Data;
using Watcher.Models;
using Watcher.ViewModels; using Watcher.ViewModels;
namespace Watcher.Controllers namespace Watcher.Controllers
@@ -11,13 +11,20 @@ namespace Watcher.Controllers
[Authorize] [Authorize]
public class HomeController : Controller public class HomeController : Controller
{ {
// Datenbankverbindung
private readonly AppDbContext _context; private readonly AppDbContext _context;
public HomeController(AppDbContext context) // Logging einbinden
private readonly ILogger<HomeController> _logger;
// HomeController Constructor
public HomeController(AppDbContext context, ILogger<HomeController> logger)
{ {
_context = context; _context = context;
_logger = logger;
} }
// Dashboard unter /home/index
public async Task<IActionResult> Index() public async Task<IActionResult> Index()
{ {
var preferredUserName = User.FindFirst("preferred_username")?.Value; var preferredUserName = User.FindFirst("preferred_username")?.Value;
@@ -39,9 +46,10 @@ namespace Watcher.Controllers
return View(viewModel); return View(viewModel);
} }
// 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 IActionResult DashboardStats()
{ {
Console.WriteLine("Dashboard aktualisiert");
var servers = _context.Servers.ToList(); var servers = _context.Servers.ToList();
var containers = _context.Containers.ToList(); var containers = _context.Containers.ToList();
@@ -51,9 +59,12 @@ namespace Watcher.Controllers
{ {
ActiveServers = servers.Count(s => (now - s.LastSeen).TotalSeconds <= 120), ActiveServers = servers.Count(s => (now - s.LastSeen).TotalSeconds <= 120),
OfflineServers = 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), //RunningContainers = containers.Count(c => (now - c.LastSeen).TotalSeconds <= 120),
//FailedContainers = containers.Count(c => (now - c.LastSeen).TotalSeconds > 120), //FailedContainers = containers.Count(c => (now - c.LastSeen).TotalSeconds > 120),
LastLogin = DateTime.Now // Oder was auch immer hier richtig ist
LastLogin = DateTime.Now
}; };
return PartialView("_DashboardStats", model); return PartialView("_DashboardStats", model);

View File

@@ -4,17 +4,39 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Sqlite; using Microsoft.EntityFrameworkCore.Sqlite;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Serilog;
using Watcher.Data; using Watcher.Data;
using Watcher.Models; using Watcher.Models;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Serilog konfigurieren nur Logs, die nicht von Microsoft stammen
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Warning) // <--
.Enrich.FromLogContext()
.WriteTo.File(
"logs/watcher-.log",
outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}",
rollingInterval: RollingInterval.Day
)
.CreateLogger();
builder.Host.UseSerilog();
// Add services to the container. // Add services to the container.
builder.Services.AddControllersWithViews(); builder.Services.AddControllersWithViews();
// HttpContentAccessor // HttpContentAccessor
builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpContextAccessor();
// ---------- Konfiguration ---------- // ---------- Konfiguration ----------
DotNetEnv.Env.Load(); DotNetEnv.Env.Load();
@@ -22,14 +44,14 @@ builder.Configuration
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables(); .AddEnvironmentVariables();
// Konfiguration laden // Konfiguration laden
var configuration = builder.Configuration; var configuration = builder.Configuration;
// ---------- DB-Kontext ----------
// ---------- DB-Kontext ----------
var dbProvider = configuration["Database:Provider"] ?? "MySQL"; var dbProvider = configuration["Database:Provider"] ?? "MySQL";
var connectionString = configuration["Database:ConnectionString"]; var connectionString = configuration["Database:ConnectionString"];
builder.Services.AddDbContext<AppDbContext>((serviceProvider, options) => builder.Services.AddDbContext<AppDbContext>((serviceProvider, options) =>
{ {
var config = serviceProvider.GetRequiredService<IConfiguration>(); var config = serviceProvider.GetRequiredService<IConfiguration>();
@@ -51,19 +73,21 @@ builder.Services.AddDbContext<AppDbContext>((serviceProvider, options) =>
} }
}); });
// ---------- Authentifizierung konfigurieren ---------- // ---------- Authentifizierung konfigurieren ----------
// PocketID nur konfigurieren, wenn aktiviert // PocketID nur konfigurieren, wenn aktiviert
var pocketIdSection = builder.Configuration.GetSection("Authentication:PocketID"); var pocketIdSection = builder.Configuration.GetSection("Authentication:PocketID");
var pocketIdEnabled = pocketIdSection.GetValue<bool>("Enabled"); var pocketIdEnabled = pocketIdSection.GetValue<bool>("Enabled");
var auth = builder.Services.AddAuthentication("Cookies");
var auth = builder.Services.AddAuthentication("Cookies");
auth.AddCookie("Cookies", options => auth.AddCookie("Cookies", options =>
{ {
options.LoginPath = "/Auth/Login"; options.LoginPath = "/Auth/Login";
options.AccessDeniedPath = "/Auth/AccessDenied"; options.AccessDeniedPath = "/Auth/AccessDenied";
}); });
builder.Services.AddAuthentication() builder.Services.AddAuthentication()
.AddOpenIdConnect("oidc", options => .AddOpenIdConnect("oidc", options =>
{ {
@@ -124,12 +148,9 @@ builder.Services.AddAuthentication()
}); });
var app = builder.Build(); var app = builder.Build();
// Migrationen anwenden (für SQLite oder andere DBs) // Migrationen anwenden (für SQLite oder andere DBs)
using (var scope = app.Services.CreateScope()) using (var scope = app.Services.CreateScope())
{ {
@@ -169,6 +190,7 @@ using (var scope = app.Services.CreateScope())
} }
} }
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment()) if (!app.Environment.IsDevelopment())
{ {
@@ -178,10 +200,6 @@ if (!app.Environment.IsDevelopment())
} }
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseRouting(); app.UseRouting();
@@ -190,11 +208,9 @@ app.UseAuthorization();
app.UseStaticFiles(); app.UseStaticFiles();
app.MapControllerRoute( app.MapControllerRoute(
name: "default", name: "default",
pattern: "{controller=Auth}/{action=Login}/" pattern: "{controller=Auth}/{action=Login}/"
); );
app.Run(); app.Run();

View File

@@ -47,6 +47,7 @@
try { try {
const response = await fetch('/Home/DashboardStats'); const response = await fetch('/Home/DashboardStats');
if (response.ok) { if (response.ok) {
console.log("reload")
const html = await response.text(); const html = await response.text();
document.getElementById('dashboard-stats').innerHTML = html; document.getElementById('dashboard-stats').innerHTML = html;
} else { } else {

View File

@@ -20,6 +20,8 @@
<!-- Auth via OpenID Connect + Cookies --> <!-- Auth via OpenID Connect + Cookies -->
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.3.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.6" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.6" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>