Files
alpenwerk-hr/next.config.ts
Maximilian Stubhan 61ccce5456 Revert "Make the build fit Vercel without breaking the container"
This reverts commit ecbda3f. The deployment goes to a Linux server instead,
so the two accommodations no longer earn their place: output: "standalone"
returns to unconditional, which is what the Dockerfile wants, and
/api/import goes back to 120 seconds — the free-tier ceiling that forced 60
does not apply outside a serverless platform, and a large import benefits
from the headroom.

The Vercel section in DEPLOYMENT.md goes with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 08:21:58 +02:00

69 lines
2.8 KiB
TypeScript

import type { NextConfig } from "next";
// Report-only rather than enforcing, deliberately: the policy is derived from
// what this app is known to load — its own bundle, the self-hosted Nunito
// files from next/font, and nothing else — but an unenforced policy that logs
// violations is worth more than a guessed one that blanks the app for every
// HR user. Promote it to `Content-Security-Policy` once the reports come back
// clean.
//
// 'unsafe-inline' on script-src is not laziness: Next.js inlines its
// bootstrap and hydration payload as inline <script> tags, so a nonce-based
// policy means threading a per-request nonce through proxy.ts — a separate
// change, and the reason this starts in report-only.
function contentSecurityPolicy(): string {
// Die Anmeldung schickt ein Formular an /api/auth/signin/…, und von dort
// geht es per Weiterleitung zum Anmeldeserver. `form-action` gilt auch für
// die Weiterleitungen nach einem Formularversand — steht der Aussteller
// nicht darin, bricht der Browser die Anmeldung ab. Genau hier wäre die
// Nur-Bericht-Fassung später eine böse Überraschung.
let issuerOrigin = "";
try {
issuerOrigin = new URL(process.env.AUTH_MICROSOFT_ENTRA_ID_ISSUER ?? "").origin;
} catch {
issuerOrigin = "";
}
const formAction = ["'self'", issuerOrigin].filter(Boolean).join(" ");
return [
"default-src 'self'",
"script-src 'self' 'unsafe-inline'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: blob:",
"font-src 'self'",
// Die Anwendung spricht im Browser mit niemandem ausser sich selbst: die
// Datenbank erreicht nur der Server, und seit die API-Schicht weg ist,
// gibt es keine Gegenstelle mehr, die von aussen angesprochen würde.
"connect-src 'self'",
"frame-ancestors 'self'",
"base-uri 'self'",
`form-action ${formAction}`,
"object-src 'none'",
].join("; ");
}
const nextConfig: NextConfig = {
// Emits a self-contained .next/standalone server (only the deps actually
// used at runtime, no full node_modules) - what the Dockerfile copies in.
output: "standalone",
// Baseline security headers (clickjacking, MIME-sniffing, referrer leakage,
// browser feature access) plus the report-only CSP described above.
async headers() {
return [
{
source: "/:path*",
headers: [
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains" },
{ key: "Content-Security-Policy-Report-Only", value: contentSecurityPolicy() },
],
},
];
},
};
export default nextConfig;