fix: Krea-Bild-URLs auf /api/images/file/ korrigieren, Thumbnail-Endpoint hinzufügen

- ui.js: [Krea: datei] URLs von /api/skills/krea-api/output/ auf /api/images/file/ geändert
- KreaEndpoint: GET /api/images/file/{filename}/thumb mit gecachten WebP-Thumbnails (320px)
- Thumbnail-Cache in data/cache/images/, analog zum SkillEndpoint
This commit is contained in:
Meleki committed 2026-03-06 15:10:26 +01:00
1 parent 20f96f69d0
commit 1f5f49228b
2 files changed
+81 -2

No files matched your search

+79
View File
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
@@ -5,6 +7,9 @@ using Gott.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Formats.Webp;
namespace Gott.Endpoints
{
@@ -15,6 +20,7 @@ namespace Gott.Endpoints
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);
}
@@ -108,5 +114,78 @@ namespace Gott.Endpoints
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;
}
// Cache-Verzeichnis: data/cache/images/
var imagesDir = Path.GetDirectoryName(originalPath);
var dataDir = Path.GetDirectoryName(imagesDir);
var cacheDir = Path.Combine(dataDir, "cache", "images");
Directory.CreateDirectory(cacheDir);
var thumbFilename = Path.GetFileNameWithoutExtension(filename) + ".webp";
var thumbPath = Path.Combine(cacheDir, thumbFilename);
if (!File.Exists(thumbPath) || File.GetLastWriteTimeUtc(originalPath) > File.GetLastWriteTimeUtc(thumbPath))
{
try
{
using var image = await Image.LoadAsync(originalPath);
var maxSize = 320;
image.Mutate(x => x.Resize(new ResizeOptions
{
Size = new Size(maxSize, maxSize),
Mode = ResizeMode.Max
}));
var encoder = new WebpEncoder { Quality = 75 };
await image.SaveAsync(thumbPath, encoder);
}
catch
{
// Fallback: Original ausliefern
var contentType = ext switch
{
".png" => "image/png",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".gif" => "image/gif",
_ => "image/webp",
};
res.ContentType = contentType;
res.Headers["Cache-Control"] = "private, max-age=3600";
await res.SendFileAsync(originalPath);
return;
}
}
res.ContentType = "image/webp";
res.Headers["Cache-Control"] = "private, max-age=86400";
await res.SendFileAsync(thumbPath);
}
}
}
+2 -2
View File
@@ -170,8 +170,8 @@ function renderMarkdown(text) {
html = html.replace(/\[Krea:\s*([^\]]+)\]/g, function(match, filename) {
var fn = filename.trim();
var safeFn = esc(fn);
var thumbUrl = '/api/skills/krea-api/output/' + encodeURIComponent(fn) + '/thumb';
var fullUrl = '/api/skills/krea-api/output/' + encodeURIComponent(fn);
var thumbUrl = '/api/images/file/' + encodeURIComponent(fn) + '/thumb';
var fullUrl = '/api/images/file/' + encodeURIComponent(fn);
return '<span class="krea-ref" data-filename="' + safeFn + '" data-fullurl="' + fullUrl + '" title="Klicken für Vollbild">'
+ '<img class="krea-thumb" src="' + thumbUrl + '" alt="' + safeFn + '" loading="lazy">'
+ '</span>';