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
109 lines
4.1 KiB
C#
109 lines
4.1 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using Nexus.Api.Helpers;
|
|
|
|
namespace Nexus.Api.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/v1/memory")]
|
|
public class MemoryController : ControllerBase
|
|
{
|
|
[HttpGet]
|
|
public IResult GetAll()
|
|
{
|
|
var basePath = "/mnt/workspace-iris/memory";
|
|
if (!Directory.Exists(basePath))
|
|
return Results.Ok(Array.Empty<object>());
|
|
|
|
var files = Directory.GetFiles(basePath, "*.md")
|
|
.Select(f => new FileInfo(f))
|
|
.OrderByDescending(f => f.Name)
|
|
.Select(f => new
|
|
{
|
|
name = f.Name,
|
|
path = f.FullName.Replace(basePath, "").TrimStart('/'),
|
|
size = f.Length,
|
|
modifiedAt = f.LastWriteTimeUtc
|
|
})
|
|
.ToList();
|
|
|
|
var longTermPath = "/mnt/workspace-iris/MEMORY.md";
|
|
if (System.IO.File.Exists(longTermPath))
|
|
{
|
|
var fi = new FileInfo(longTermPath);
|
|
files.Insert(0, new { name = "MEMORY.md", path = "MEMORY.md", size = fi.Length, modifiedAt = fi.LastWriteTimeUtc });
|
|
}
|
|
|
|
return Results.Ok(files);
|
|
}
|
|
|
|
[HttpGet("search")]
|
|
public async Task<IResult> Search([FromQuery] string q)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(q) || q.Length < 2)
|
|
return Results.BadRequest("Query must be at least 2 characters.");
|
|
|
|
var basePath = "/mnt/workspace-iris/memory";
|
|
var results = new List<object>();
|
|
|
|
const int maxFiles = 50;
|
|
const int maxFileSize = 1_000_000;
|
|
|
|
async Task SearchDir(string dir)
|
|
{
|
|
if (!Directory.Exists(dir)) return;
|
|
var files = Directory.GetFiles(dir, "*.md").Take(maxFiles);
|
|
foreach (var file in files)
|
|
{
|
|
var fi = new FileInfo(file);
|
|
if (fi.Length > maxFileSize) continue;
|
|
string content;
|
|
using (var reader = new StreamReader(file))
|
|
content = await reader.ReadToEndAsync();
|
|
if (content.Contains(q, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var idx = content.IndexOf(q, StringComparison.OrdinalIgnoreCase);
|
|
var start = Math.Max(0, idx - 60);
|
|
var excerpt = (start > 0 ? "\u2026" : "") + content.Substring(start, Math.Min(200, content.Length - start)) + "\u2026";
|
|
results.Add(new { name = Path.GetFileName(file), path = file.Replace(basePath, "").TrimStart('/'), excerpt, size = fi.Length });
|
|
}
|
|
}
|
|
}
|
|
|
|
await SearchDir(basePath);
|
|
|
|
var longTermPath = "/mnt/workspace-iris/MEMORY.md";
|
|
if (System.IO.File.Exists(longTermPath))
|
|
{
|
|
string content;
|
|
using (var reader = new StreamReader(longTermPath))
|
|
content = await reader.ReadToEndAsync();
|
|
if (content.Contains(q, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var idx = content.IndexOf(q, StringComparison.OrdinalIgnoreCase);
|
|
var start = Math.Max(0, idx - 60);
|
|
var excerpt = (start > 0 ? "\u2026" : "") + content.Substring(start, Math.Min(200, content.Length - start)) + "\u2026";
|
|
results.Insert(0, new { name = "MEMORY.md", path = "MEMORY.md", excerpt, size = content.Length });
|
|
}
|
|
}
|
|
|
|
return Results.Ok(results);
|
|
}
|
|
|
|
[HttpGet("{name}")]
|
|
public async Task<IResult> GetFile(string name)
|
|
{
|
|
if (!PathSecurityHelper.TryResolveSafePath("/mnt/workspace-iris/memory", name, out var filePath))
|
|
return Results.BadRequest("Invalid filename.");
|
|
|
|
var longTermPath = "/mnt/workspace-iris/MEMORY.md";
|
|
if (name.Equals("MEMORY.md", StringComparison.OrdinalIgnoreCase))
|
|
filePath = longTermPath;
|
|
|
|
if (!System.IO.File.Exists(filePath!))
|
|
return Results.NotFound();
|
|
|
|
var content = await System.IO.File.ReadAllTextAsync(filePath!);
|
|
return Results.Ok(new { name, path = name, content, size = content.Length, modifiedAt = System.IO.File.GetLastWriteTimeUtc(filePath!) });
|
|
}
|
|
}
|