import { readFileSync } from 'node:fs'; import path from 'node:path'; import type { NextConfig } from 'next'; import createNextIntlPlugin from 'next-intl/plugin'; // Bauzeit-Einbettung der Aenderungsliste (quick-260916-dcz): CHANGELOG.md aus // dem Wurzelverzeichnis wird hier gelesen und als `env.TESSERA_CHANGELOG_MD` // abgelegt. Next.js ersetzt `process.env.TESSERA_CHANGELOG_MD` fuer webpack UND // Turbopack ueber denselben Define-Mechanismus. Der Text bleibt nur im // Server-Bundle, weil ausschliesslich Server-Code `@/lib/changelog` importiert: // die Server-Seite (changelog/page.tsx) und die 'use server'-Datei // lib/release-notice-actions.ts (quick-260925-bow) — Importdisziplin, kein // Client-Chunk. const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts'); function readChangelog(): string { const file = path.resolve(__dirname, '../../CHANGELOG.md'); try { return readFileSync(file, 'utf8'); } catch (error) { const reason = error instanceof Error ? error.message : String(error); throw new Error( `CHANGELOG.md konnte nicht gelesen werden (${file}): ${reason}. ` + 'Die Datei muss im Wurzelverzeichnis liegen; im Docker-Bau braucht sie die Ausnahme ' + '`!CHANGELOG.md` in .dockerignore und die Zeile `COPY CHANGELOG.md ./` in apps/web/Dockerfile.', ); } } // Schutz-Kopfzeilen der Anwendung (quick-261009-p0m, aus dem ersten ZAP-Lauf). Sie gelten fuer // alle Pfade und stehen hier, damit sie auch ohne vorgeschalteten Proxy gesendet werden // (doppelt gesichert; setzt der Proxy dieselben Kopfzeilen, gewinnt keine Seite etwas oder // verliert etwas). Absichtlich KEINE vollstaendige Content-Security-Policy -- sie braucht eine // eigene Pruefung der Inline-Skripte von Next.js und bleibt im Sicherheitsprotokoll "offen". // `frame-ancestors 'self'` ist nur die eine Richtlinie gegen das Einrahmen durch fremde Seiten // (Gegenstueck zu X-Frame-Options fuer neuere Browser). Eingebettete FREMDE Seiten (XFrame-Kachel, // eigene Module) sind nicht betroffen: diese Kopfzeilen schuetzen nur Tessera selbst. // Permissions-Policy sperrt Kamera, Mikrofon, Standort, Zahlung und USB; die Zwischenablage // (clipboard-write fuer "Link kopieren"), Vollbild und Benachrichtigungen bleiben unberuehrt. // Cross-Origin-Opener-Policy `same-origin-allow-popups`: jeder Link in ein neues Fenster // (Nextcloud-Anmeldung, Favoriten, ...) traegt noopener, nichts nutzt window.opener/postMessage. const securityHeaders = [ { key: 'X-Content-Type-Options', value: 'nosniff' }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, { key: 'X-Frame-Options', value: 'SAMEORIGIN' }, { key: 'Content-Security-Policy', value: "frame-ancestors 'self'" }, { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=(), payment=(), usb=()', }, { key: 'Cross-Origin-Opener-Policy', value: 'same-origin-allow-popups' }, ]; const nextConfig: NextConfig = { output: 'standalone' as const, poweredByHeader: false, env: { TESSERA_CHANGELOG_MD: readChangelog(), }, async headers() { return [{ source: '/:path*', headers: securityHeaders }]; }, async rewrites() { const apiUrl = process.env.API_INTERNAL_URL || 'http://api:3001'; return [ { source: '/api-proxy/:path*', destination: `${apiUrl}/:path*`, }, ]; }, }; export default withNextIntl(nextConfig);