WEB-3: lokale Webseiten-Vorschau - GET /api/preview/{**path} (rendert live, korrekte Content-Types, nur Veroeffentlichte) + /webseite/vorschau (iframe, Seiten-Wechsler, Smartphone/Desktop-Umschalter, Neu laden) + Einstiege auf der Uebersicht + Fix: pages.ts rief /pages statt /api/pages (live-404, vom Mock kaschiert) + 1 Backend-Roundtrip + 4 e2e-Specs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 12:18:07 +02:00
parent aa345aa796
commit d5544412bd
10 changed files with 398 additions and 8 deletions

View File

@@ -32,6 +32,24 @@ namespace GerbilManagerWebAPI.Endpoints
return TypedResults.Ok(files.Select(kv => new { path = kv.Key, size = kv.Value.Length }).ToList());
});
// ---- WEB-3: lokale Vorschau — rendert live (nur veröffentlichte Seiten)
// und liefert die Datei mit passendem Content-Type aus. Relative
// Links/CSS der gerenderten Seite funktionieren dadurch im
// Vorschau-iframe genauso wie später auf der echten Webseite. ----
api.MapGet("/preview/{**path}", async (string? path, ApplicationContext db) =>
{
var snapshot = await new SiteSnapshotService(db).BuildAsync();
var files = SiteRenderer.Render(snapshot);
var key = string.IsNullOrWhiteSpace(path) ? "index.html" : path.TrimEnd('/');
if (!files.TryGetValue(key, out var content) &&
!files.TryGetValue($"{key}/index.html", out content))
{
return Results.NotFound();
}
return Results.Content(content, PreviewContentType(key));
});
// ---- pages ----
api.MapGet("/pages", async (ApplicationContext db) =>
TypedResults.Ok(await db.Pages.AsNoTracking().OrderBy(p => p.Slug)
@@ -151,6 +169,13 @@ namespace GerbilManagerWebAPI.Endpoints
return app;
}
/// <summary>WEB-3: Content-Type der Vorschau-Dateien (Renderer erzeugt HTML + CSS).</summary>
private static string PreviewContentType(string path) =>
path.EndsWith(".css", StringComparison.OrdinalIgnoreCase) ? "text/css; charset=utf-8"
: path.EndsWith(".xml", StringComparison.OrdinalIgnoreCase) ? "application/xml; charset=utf-8"
: path.EndsWith(".txt", StringComparison.OrdinalIgnoreCase) ? "text/plain; charset=utf-8"
: "text/html; charset=utf-8";
private static BlockDto ToBlockDto(Block b) =>
new(b.Id, b.Order, b.Type, JsonNode.Parse(string.IsNullOrWhiteSpace(b.Data) ? "{}" : b.Data));