feat: File-Explorer für Agent-Ordner im Agent-Formular
This commit is contained in:
1 parent
8e585443e2
commit
58c5ccb79b
4 files changed
+192
-4
No files matched your search
@@ -40,6 +40,9 @@ public static class WorkspaceEndpoint
|
||||
app.MapGet("/api/workspace/file", HandleFile);
|
||||
app.MapPut("/api/workspace/file", HandleFileSave);
|
||||
app.MapGet("/api/workspace/browse", HandleBrowse);
|
||||
app.MapGet("/api/workspace/agent/tree", HandleAgentTree);
|
||||
app.MapGet("/api/workspace/agent/file", HandleAgentFile);
|
||||
app.MapPut("/api/workspace/agent/file", HandleAgentFileSave);
|
||||
}
|
||||
|
||||
private static async Task HandleTree(HttpContext ctx)
|
||||
@@ -253,4 +256,169 @@ public static class WorkspaceEndpoint
|
||||
or "procfile" or "vagrantfile" or "license" or "readme"
|
||||
or "changelog" or "authors" or "contributors";
|
||||
}
|
||||
|
||||
private static string GetAgentBasePath(HttpContext ctx, out string error)
|
||||
{
|
||||
error = null;
|
||||
var agentId = ctx.Request.Query["agentId"].FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(agentId))
|
||||
{
|
||||
error = "agentId fehlt";
|
||||
return null;
|
||||
}
|
||||
|
||||
var memoryService = ctx.RequestServices.GetRequiredService<Gott.Services.MemoryService>();
|
||||
var basePath = memoryService.GetAgentMemoryDir(agentId);
|
||||
|
||||
if (!Directory.Exists(basePath))
|
||||
Directory.CreateDirectory(basePath);
|
||||
|
||||
return basePath;
|
||||
}
|
||||
|
||||
private static async Task HandleAgentTree(HttpContext ctx)
|
||||
{
|
||||
var basePath = GetAgentBasePath(ctx, out var error);
|
||||
if (basePath == null)
|
||||
{
|
||||
ctx.Response.StatusCode = 400;
|
||||
await ctx.Response.WriteAsJsonAsync(new { error });
|
||||
return;
|
||||
}
|
||||
|
||||
var subPath = ctx.Request.Query["path"].FirstOrDefault() ?? "";
|
||||
var targetPath = Path.GetFullPath(Path.Combine(basePath, subPath));
|
||||
|
||||
if (!targetPath.StartsWith(basePath))
|
||||
{
|
||||
ctx.Response.StatusCode = 403;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(targetPath))
|
||||
{
|
||||
ctx.Response.StatusCode = 404;
|
||||
await ctx.Response.WriteAsJsonAsync(new { error = "Verzeichnis nicht gefunden" });
|
||||
return;
|
||||
}
|
||||
|
||||
var entries = new List<object>();
|
||||
|
||||
foreach (var dir in Directory.GetDirectories(targetPath).OrderBy(d => Path.GetFileName(d), StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
var name = Path.GetFileName(dir);
|
||||
if (IgnoredDirs.Contains(name)) continue;
|
||||
var relPath = Path.GetRelativePath(basePath, dir).Replace('\\', '/');
|
||||
entries.Add(new { name, path = relPath, type = "dir" });
|
||||
}
|
||||
|
||||
foreach (var file in Directory.GetFiles(targetPath).OrderBy(f => Path.GetFileName(f), StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
var name = Path.GetFileName(file);
|
||||
var ext = Path.GetExtension(file);
|
||||
var relPath = Path.GetRelativePath(basePath, file).Replace('\\', '/');
|
||||
var size = new FileInfo(file).Length;
|
||||
var isText = TextExtensions.Contains(ext) || IsLikelyTextFile(name);
|
||||
entries.Add(new { name, path = relPath, type = "file", ext, size, isText });
|
||||
}
|
||||
|
||||
await ctx.Response.WriteAsJsonAsync(entries);
|
||||
}
|
||||
|
||||
private static async Task HandleAgentFile(HttpContext ctx)
|
||||
{
|
||||
var basePath = GetAgentBasePath(ctx, out var error);
|
||||
if (basePath == null)
|
||||
{
|
||||
ctx.Response.StatusCode = 400;
|
||||
await ctx.Response.WriteAsJsonAsync(new { error });
|
||||
return;
|
||||
}
|
||||
|
||||
var filePath = ctx.Request.Query["path"].FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
ctx.Response.StatusCode = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
var targetPath = Path.GetFullPath(Path.Combine(basePath, filePath));
|
||||
if (!targetPath.StartsWith(basePath))
|
||||
{
|
||||
ctx.Response.StatusCode = 403;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(targetPath))
|
||||
{
|
||||
ctx.Response.StatusCode = 404;
|
||||
return;
|
||||
}
|
||||
|
||||
var info = new FileInfo(targetPath);
|
||||
if (info.Length > 2 * 1024 * 1024)
|
||||
{
|
||||
ctx.Response.StatusCode = 413;
|
||||
await ctx.Response.WriteAsJsonAsync(new { error = "Datei zu groß (max. 2MB)" });
|
||||
return;
|
||||
}
|
||||
|
||||
var content = await File.ReadAllTextAsync(targetPath);
|
||||
var lineCount = content.Split('\n').Length;
|
||||
|
||||
await ctx.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
path = filePath,
|
||||
name = info.Name,
|
||||
size = info.Length,
|
||||
lineCount,
|
||||
content
|
||||
});
|
||||
}
|
||||
|
||||
private static async Task HandleAgentFileSave(HttpContext ctx)
|
||||
{
|
||||
var basePath = GetAgentBasePath(ctx, out var error);
|
||||
if (basePath == null)
|
||||
{
|
||||
ctx.Response.StatusCode = 400;
|
||||
await ctx.Response.WriteAsJsonAsync(new { error });
|
||||
return;
|
||||
}
|
||||
|
||||
var filePath = ctx.Request.Query["path"].FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
ctx.Response.StatusCode = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
var targetPath = Path.GetFullPath(Path.Combine(basePath, filePath));
|
||||
if (!targetPath.StartsWith(basePath))
|
||||
{
|
||||
ctx.Response.StatusCode = 403;
|
||||
return;
|
||||
}
|
||||
|
||||
// Verzeichnis erstellen falls nötig
|
||||
var dir = Path.GetDirectoryName(targetPath);
|
||||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
using var reader = new StreamReader(ctx.Request.Body);
|
||||
var content = await reader.ReadToEndAsync();
|
||||
|
||||
await File.WriteAllTextAsync(targetPath, content);
|
||||
|
||||
var info = new FileInfo(targetPath);
|
||||
var lineCount = content.Split('\n').Length;
|
||||
|
||||
await ctx.Response.WriteAsJsonAsync(new
|
||||
{
|
||||
path = filePath,
|
||||
name = info.Name,
|
||||
size = info.Length,
|
||||
lineCount
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user