feat: Krea Image Generation Endpoint

- KreaService: GenerateAsync() mit Job-Polling, Image-Download, Metadaten-Speicherung
- KreaEndpoint: POST /api/images/generate, GET /api/images, GET /api/images/file/{filename}, GET /api/images/status
- KreaImageRecord: Output-Model
- Output: data/images/<timestamp>-<index>.<ext> + data/images/<timestamp>.json
- 16 Modelle: flux, flux-kontext, flux-1.1-pro, imagen-3/4, chatgpt-image, runway-gen-4, etc.
- appsettings.json: KreaToken (Format: KEY_ID:SECRET), obsolete TelegramBotToken/WebhookSecret entfernt
This commit is contained in:
Meleki committed 2026-03-04 18:24:51 +01:00
1 parent f552b44b05
commit 26da25967d
5 files changed
+443 -1

No files matched your search

+112
View File
@@ -0,0 +1,112 @@
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/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,
});
}
}
}