Files
Wabenhain/next.config.ts
T
Nick SzaunigandClaude Opus 5.5 4f442ec06d feat(admin): Logo und Favicon per Bild-Upload austauschbar
Neue Gruppe "Logo und Favicon" unter Admin > Inhalte mit drei Bildfeldern:
Logo (Kopfzeile), optionales Logo fuer dunkle Flaechen (Mobilmenue,
Fusszeile, Admin-Leiste; faellt sonst auf das normale Logo zurueck) und
Favicon. Leer bleibt alles wie bisher (gezeichnetes Waben-Zeichen bzw.
Standard-Icon).

- lib/brand.ts liest die Pfade und prueft sie erneut gegen das
  Upload-Muster, bevor sie in ein src-Attribut gelangen.
- Statische app/icon.svg und app/favicon.ico ersetzt durch dynamische
  app/icon.tsx (512 px PNG) und app/apple-icon.tsx (180 px auf Creme);
  lib/brand-icon.ts rendert Upload oder Standard-Icon per sharp und liest
  Dateien nur ueber resolveUploadPath. Cache-Control no-cache, damit ein
  neues Favicon ohne Tage Verzoegerung erscheint.
- /favicon.ico per Rewrite auf /icon, Manifest nutzt /icon.
- Hochgeladenes Logo mit fester Hoehe und begrenzter Breite (Kopfzeile
  mobil 56 px, ab sm 112 px), damit breite Wortmarken nichts sprengen.

Uploads bleiben auf Rasterbilder beschraenkt (kein SVG, XSS-Schutz).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-26 01:02:18 +02:00

104 lines
4.8 KiB
TypeScript

import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// Verhindert den "X-Powered-By: Next.js"-Response-Header — verrimt keinen
// funktionalen Vorteil, gibt Angreifern aber unnoetig Framework-Fingerprinting.
poweredByHeader: false,
// "standalone" buendelt eine minimale Node-Laufzeit inkl. Dependencies,
// wodurch das Docker-Image (siehe Dockerfile) deutlich kleiner bleibt.
output: "standalone",
// Pakete, die zur Laufzeit eigene Dateien ueber einen __dirname-relativen
// Pfad nachladen. Werden sie vom Bundler (Turbopack/Webpack) mitgebuendelt,
// zeigt ihr __dirname auf einen virtuellen Bundle-Pfad ("/ROOT/...") statt
// auf das echte Verzeichnis in node_modules — der Zugriff schlaegt dann mit
// ENOENT fehl. "serverExternalPackages" laesst Next.js sie stattdessen ganz
// normal per require() aus node_modules laden.
//
// - pdfkit: laedt seine Schriftart-Metadaten (.afm-Dateien).
// - ffmpeg-static: exportiert den Pfad zur mitgelieferten ffmpeg-Binary
// (siehe lib/video-transcode.ts). Gebuendelt lieferte es
// "\ROOT\node_modules\...\ffmpeg.exe" -> jeder Video-Upload ohne
// System-ffmpeg im PATH (also jede lokale Entwicklungsumgebung unter
// Windows/macOS) scheiterte mit "Das Video konnte nicht verarbeitet
// werden."
serverExternalPackages: ["pdfkit", "ffmpeg-static"],
images: {
// AVIF vor WebP. Next.js liefert das erste Format, das der Browser laut
// Accept-Header unterstuetzt; Browser ohne AVIF bekommen unveraendert
// WebP, ganz alte das Original.
//
// Am Hero-Bild der Startseite nachgemessen (1920x1280), mit den
// Einstellungen aus next/dist/server/image-optimizer.js — WebP q75 gegen
// AVIF q47 (Next.js skaliert die Qualitaet mit 50/80) bei effort 3:
// 640 px -43 %, 828 px -42 %, 1080 px -40 %, 1920 px -37 %.
//
// Die Kodierdauer spricht nicht dagegen: bei effort 3 lag AVIF in
// derselben Messung gleichauf oder schneller als WebP (1920 px: 60 ms
// gegen 122 ms). Die frueher hier vermutete teure Erstkodierung gibt es
// in dieser Einstellung nicht.
formats: ["image/avif", "image/webp"],
// Ein Jahr statt der Next.js-Vorgabe von vier Wochen. Hochgeladene Bilder
// sind unveraenderlich: lib/uploads.ts legt jede Datei unter einem eigenen
// Namen ab, ein ausgetauschtes Produktbild bekommt also eine neue URL.
// Ein kurzes TTL wuerde hier nur brauchbare Ergebnisse wegwerfen.
minimumCacheTTL: 60 * 60 * 24 * 365,
// Entspricht der Vorgabe von Next.js 16, die andere Werte bereits ablehnt
// (gegen die laufende Seite geprueft: ?q=42 antwortet mit 400) — aendert
// das Verhalten also nicht. Ausgeschrieben, damit eine spaetere
// Erweiterung der Liste eine bewusste Entscheidung ist: jeder zusaetzlich
// erlaubte Wert oeffnet einen eigenen Cache-Eintrag und eine eigene
// Kodierung je Bild, von aussen ueber die URL ausloesbar.
qualities: [75],
// Ebenfalls die Vorgabe, ausdruecklich festgehalten: ein SVG ist ein
// Dokument, das Skripte enthalten kann — same-origin ausgeliefert waere
// das ein XSS-Vektor. Diese Zeile soll verhindern, dass der Schalter
// spaeter beilaeufig umgelegt wird.
dangerouslyAllowSVG: false,
},
// Turbopacks Dateisystem-Caches (.next/dev/cache & .next/cache/turbopack)
// kollidieren in diesem OneDrive-synchronisierten Arbeitsverzeichnis
// wiederholt mit der Cloud-Synchronisierung ("Failed to open database" /
// mmap-Timeout bei next dev, "Failed to compact database" beim
// next-build-Shutdown). Beide Caches werden daher deaktiviert.
experimental: {
turbopackFileSystemCacheForDev: false,
turbopackFileSystemCacheForBuild: false,
},
// Das Favicon ist im Admin austauschbar und wird von app/icon.tsx erzeugt;
// eine statische app/favicon.ico gibt es deshalb nicht mehr. Browser und
// Crawler, die ohne <link rel="icon"> direkt /favicon.ico abfragen,
// bekommen dasselbe Bild.
async rewrites() {
return [{ source: "/favicon.ico", destination: "/icon" }];
},
async headers() {
// Content-Security-Policy wird bewusst NICHT hier gesetzt: sie braucht
// eine pro Request neue Nonce (siehe proxy.ts), was ein statischer
// headers()-Eintrag hier nicht kann.
return [
{
source: "/(.*)",
headers: [
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-Frame-Options", value: "DENY" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), payment=(self)" },
// Wirkt nur ueber HTTPS; im lokalen HTTP-Betrieb ignoriert der Browser den Header.
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains" },
],
},
];
},
};
export default nextConfig;