Files
gott/Endpoints/KreaEndpoint.cs
T
Meleki dcedbaa074 refactor: Backend deduplizieren – 13 Dateien, -528 Zeilen netto
ChatContext: 3 identische Context-Klassen (PreparerContext, ReviewContext,
FinalizerContext) zu einer SubAgentContext-Klasse zusammengefasst.

ChatService: 3x40 Zeilen Sub-Agent-Setup durch BuildSubAgentContext() ersetzt.

AnthropicService: 5 Hilfsmethoden extrahiert – ExtractWebSearchSources(),
TruncateToolResult(), AddToolsToPayload(), ParseServerToolUse(), TrackUsage().

WorkspaceEndpoint: 5 Session/Agent-Endpoint-Paare durch generische Core-Handler
mit basePath-Parameter ersetzt. Ungenutzte rmdir-Endpoints entfernt.

AgentEndpoint: Doppeltes Agent-DTO-Mapping durch MapAgentList() ersetzt.

SessionEndpoint: Doppelte Session/Agent-Auflösung durch ResolvePromptContext() ersetzt.

DatabaseService: Toten Code entfernt (SetSessionTelegramChatId,
ClearSessionTelegramChatId, GetSessions, GetUserSessions). ConvertDataUrlToWebP
und ResizeDataUrlToWebP zu einer Methode zusammengefasst.

TelegramEndpoint: Bot-DTO-Mapping durch MapBotResponse() dedupliziert.

KreaEndpoint + SkillEndpoint: Thumbnail-Generierung in ImageService.GenerateThumbnail()
extrahiert, SixLabors-Imports aus Endpoints entfernt.

CronjobEndpoint: 7x ctx.Items[UserId] durch ctx.GetUserId() ersetzt.
2026-03-09 09:18:49 +01:00

170 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using Gott.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
namespace Gott.Endpoints
{
public static class KreaEndpoint
{
public static void MapKreaEndpoints(this IEndpointRouteBuilder app)
{
app.MapPost("/api/images/generate", HandleGenerate);
app.MapGet("/api/images", HandleList);
app.MapGet("/api/images/file/{filename}", HandleFile);
app.MapGet("/api/images/file/{filename}/thumb", HandleThumb);
app.MapGet("/api/images/status", HandleStatus);
}
private static async Task HandleGenerate(HttpRequest req, HttpResponse res, KreaService krea)
{
if (!krea.IsConfigured)
{
res.StatusCode = 400;
await res.WriteAsJsonAsync(new { error = "Krea API nicht konfiguriert (KreaToken fehlt)" });
return;
}
JsonElement body;
try { body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body); }
catch
{
res.StatusCode = 400;
await res.WriteAsJsonAsync(new { error = "Ungültiger JSON-Body" });
return;
}
var prompt = body.TryGetProperty("prompt", out var p) ? p.GetString() : null;
if (string.IsNullOrWhiteSpace(prompt))
{
res.StatusCode = 400;
await res.WriteAsJsonAsync(new { error = "prompt ist erforderlich" });
return;
}
var model = body.TryGetProperty("model", out var m) ? m.GetString() : "flux";
var width = body.TryGetProperty("width", out var w) ? w.GetInt32() : 1024;
var height = body.TryGetProperty("height", out var h) ? h.GetInt32() : 1024;
var steps = body.TryGetProperty("steps", out var st) ? st.GetInt32() : 25;
var guidanceScale = body.TryGetProperty("guidanceScale", out var g) ? g.GetDouble() : 3.0;
var seed = body.TryGetProperty("seed", out var sd) ? sd.GetString() : null;
var batchSize = body.TryGetProperty("batchSize", out var b) ? b.GetInt32() : 1;
try
{
var result = await krea.GenerateAsync(prompt, model, width, height, steps, guidanceScale, seed, batchSize);
await res.WriteAsJsonAsync(result);
}
catch (System.Exception ex)
{
res.StatusCode = 500;
await res.WriteAsJsonAsync(new { error = ex.Message });
}
}
private static async Task HandleList(HttpRequest req, HttpResponse res, KreaService krea)
{
var images = krea.ListImages();
await res.WriteAsJsonAsync(images);
}
private static async Task HandleFile(HttpRequest req, HttpResponse res, KreaService krea, string filename)
{
// Sicherheitscheck – kein Path Traversal
if (filename.Contains("..") || filename.Contains("/") || filename.Contains("\\"))
{
res.StatusCode = 400;
return;
}
var path = krea.GetImagePath(filename);
if (path == null)
{
res.StatusCode = 404;
return;
}
var ext = Path.GetExtension(filename).ToLower();
var contentType = ext switch
{
".png" => "image/png",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".gif" => "image/gif",
_ => "image/webp",
};
res.ContentType = contentType;
await res.SendFileAsync(path);
}
private static async Task HandleStatus(HttpRequest req, HttpResponse res, KreaService krea)
{
await res.WriteAsJsonAsync(new
{
configured = krea.IsConfigured,
models = KreaService.IMAGE_MODELS.Keys,
});
}
private static readonly HashSet<string> ThumbExtensions = new(StringComparer.OrdinalIgnoreCase)
{
".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"
};
private static async Task HandleThumb(HttpRequest req, HttpResponse res, KreaService krea, string filename)
{
if (filename.Contains("..") || filename.Contains("/") || filename.Contains("\\"))
{
res.StatusCode = 400;
return;
}
var ext = Path.GetExtension(filename);
if (!ThumbExtensions.Contains(ext))
{
res.StatusCode = 400;
return;
}
var originalPath = krea.GetImagePath(filename);
if (originalPath == null)
{
res.StatusCode = 404;
return;
}
var imagesDir = Path.GetDirectoryName(originalPath);
var dataDir = Path.GetDirectoryName(imagesDir);
var cacheDir = Path.Combine(dataDir, "cache", "images");
var thumbPath = await ImageService.GenerateThumbnail(originalPath, cacheDir);
if (thumbPath != null)
{
res.ContentType = "image/webp";
res.Headers["Cache-Control"] = "private, max-age=86400";
await res.SendFileAsync(thumbPath);
}
else
{
// Fallback: Original ausliefern
res.ContentType = ext switch
{
".png" => "image/png",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".gif" => "image/gif",
_ => "image/webp",
};
res.Headers["Cache-Control"] = "private, max-age=3600";
await res.SendFileAsync(originalPath);
}
}
}
}