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]>
104 lines
4.8 KiB
TypeScript
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;
|