diff --git a/Endpoints/KreaEndpoint.cs b/Endpoints/KreaEndpoint.cs index e490a37..b5e50c3 100644 --- a/Endpoints/KreaEndpoint.cs +++ b/Endpoints/KreaEndpoint.cs @@ -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 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); + } } } diff --git a/wwwroot/js/ui.js b/wwwroot/js/ui.js index 1301b5c..83cc922 100644 --- a/wwwroot/js/ui.js +++ b/wwwroot/js/ui.js @@ -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 '' + '' + safeFn + '' + '';