using Nexus.Api.Helpers; using System.Text.RegularExpressions; namespace Nexus.Api.Services; public sealed partial class IncidentService : IIncidentService { private const string BasePath = "/mnt/workspace-iris/memory/incidents"; public async Task> GetAllAsync() { if (!Directory.Exists(BasePath)) return Array.Empty(); var incidents = new List(); 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 File.ReadAllTextAsync(file); var title = ExtractTitle(name, content); var date = ExtractDate(name); var severity = ExtractSeverity(content); var excerpt = ExtractExcerpt(content); incidents.Add(new IncidentSummary(Path.GetFileName(file), title, date, severity, excerpt, fi.Length)); } return incidents; } public async Task GetByNameAsync(string name) { if (!PathSecurityHelper.TryResolveSafePath(BasePath, name, out var filePath)) return null; if (!File.Exists(filePath!)) { if (!name.EndsWith(".md", StringComparison.OrdinalIgnoreCase)) filePath = Path.Combine(BasePath, name + ".md"); if (!File.Exists(filePath!)) return null; } var content = await File.ReadAllTextAsync(filePath!); var fi = new FileInfo(filePath!); var fileName = Path.GetFileName(filePath!); var title = ExtractTitle(Path.GetFileNameWithoutExtension(filePath!), content); var date = ExtractDate(fileName); return new IncidentDetail(fileName, title, date, content, fi.Length); } private static string ExtractTitle(string name, string content) { var match = TitleRegex().Match(content); return match.Success ? match.Groups[1].Value.Trim() : name; } private static string? ExtractDate(string fileName) { var match = DateRegex().Match(fileName); return match.Success ? match.Groups[1].Value : null; } private static string ExtractSeverity(string content) { var match = SeverityRegex().Match(content); return match.Success ? match.Groups[1].Value.Trim() : "unknown"; } private static string ExtractExcerpt(string content) { var excerptEnd = content.IndexOf("\n## ", StringComparison.Ordinal); var excerpt = excerptEnd > 0 ? content[..excerptEnd].Trim() : content[..Math.Min(300, content.Length)].Trim(); return excerpt.Length > 200 ? excerpt[..200] + "…" : excerpt; } [GeneratedRegex(@"^#\s+(.+)$", RegexOptions.Multiline)] private static partial Regex TitleRegex(); [GeneratedRegex(@"^(\d{4}-\d{2}-\d{2})")] private static partial Regex DateRegex(); [GeneratedRegex(@"\*\*Severity:\*\*\s*(.+)$", RegexOptions.Multiline)] private static partial Regex SeverityRegex(); }