a79d8282dc
- 15 Controller-Klassen ersetzen Minimal APIs in Program.cs - Repository Pattern mit Interfaces + Implementierungen (Project, Task, Activity, User) - AuthService verwendet jetzt IUserRepository statt direktem DbContext-Zugriff - SecurityHeadersMiddleware als eigenständige Middleware-Klasse - PathSecurityHelper als gemeinsamer Helper für Pfadvalidierung - DTOs in eigenem Namespace Nexus.Api.DTOs - EF-Entities in Nexus.Api.Data (vorher Nexus.Api.Domain) - Program.cs auf DI-Registrierung + Middleware reduziert - Alle 43 Endpoints unverändert erhalten - Build + 3/3 Tests erfolgreich
101 lines
3.4 KiB
C#
101 lines
3.4 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using Nexus.Api.Helpers;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Nexus.Api.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/v1/incidents")]
|
|
public class IncidentsController : ControllerBase
|
|
{
|
|
[HttpGet]
|
|
public async Task<IResult> GetAll()
|
|
{
|
|
var basePath = "/mnt/workspace-iris/memory/incidents";
|
|
if (!Directory.Exists(basePath))
|
|
return Results.Ok(Array.Empty<object>());
|
|
|
|
var incidents = new List<object>();
|
|
foreach (var file in Directory.GetFiles(basePath, "*.md").OrderByDescending(f => f).Take(50))
|
|
{
|
|
var fi = new FileInfo(file);
|
|
if (fi.Length > 1_000_000) continue;
|
|
var name = Path.GetFileNameWithoutExtension(file);
|
|
var content = await System.IO.File.ReadAllTextAsync(file);
|
|
|
|
var title = name;
|
|
var titleMatch = Regex.Match(content, @"^#\s+(.+)$", RegexOptions.Multiline);
|
|
if (titleMatch.Success)
|
|
title = titleMatch.Groups[1].Value.Trim();
|
|
|
|
var date = (string?)null;
|
|
var dateMatch = Regex.Match(name, @"^(\d{4}-\d{2}-\d{2})");
|
|
if (dateMatch.Success)
|
|
date = dateMatch.Groups[1].Value;
|
|
|
|
var severity = "unknown";
|
|
var severityMatch = Regex.Match(content, @"\*\*Severity:\*\*\s*(.+)$", RegexOptions.Multiline);
|
|
if (severityMatch.Success)
|
|
severity = severityMatch.Groups[1].Value.Trim();
|
|
|
|
var excerptEnd = content.IndexOf("\n## ", StringComparison.Ordinal);
|
|
var excerpt = excerptEnd > 0
|
|
? content[..excerptEnd].Trim()
|
|
: content[..Math.Min(300, content.Length)].Trim();
|
|
if (excerpt.Length > 200)
|
|
excerpt = excerpt[..200] + "\u2026";
|
|
|
|
incidents.Add(new
|
|
{
|
|
name = Path.GetFileName(file),
|
|
title,
|
|
date,
|
|
severity,
|
|
excerpt,
|
|
size = fi.Length
|
|
});
|
|
}
|
|
|
|
return Results.Ok(incidents);
|
|
}
|
|
|
|
[HttpGet("{name}")]
|
|
public async Task<IResult> GetOne(string name)
|
|
{
|
|
var basePath = "/mnt/workspace-iris/memory/incidents";
|
|
if (!PathSecurityHelper.TryResolveSafePath(basePath, name, out var filePath))
|
|
return Results.BadRequest("Invalid filename.");
|
|
|
|
if (!System.IO.File.Exists(filePath!))
|
|
{
|
|
if (!name.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
|
|
filePath = Path.Combine(basePath, name + ".md");
|
|
if (!System.IO.File.Exists(filePath!))
|
|
return Results.NotFound();
|
|
}
|
|
|
|
var content = await System.IO.File.ReadAllTextAsync(filePath!);
|
|
var fi = new FileInfo(filePath!);
|
|
var fileName = Path.GetFileName(filePath!);
|
|
|
|
var title = fileName;
|
|
var titleMatch = Regex.Match(content, @"^#\s+(.+)$", RegexOptions.Multiline);
|
|
if (titleMatch.Success)
|
|
title = titleMatch.Groups[1].Value.Trim();
|
|
|
|
var date = (string?)null;
|
|
var dateMatch = Regex.Match(fileName, @"^(\d{4}-\d{2}-\d{2})");
|
|
if (dateMatch.Success)
|
|
date = dateMatch.Groups[1].Value;
|
|
|
|
return Results.Ok(new
|
|
{
|
|
name = fileName,
|
|
title,
|
|
date,
|
|
content,
|
|
size = fi.Length
|
|
});
|
|
}
|
|
}
|