Brings the working codebase (Next.js app, auth system, Stripe billing, Docker/deploy config, tests, docs) into version control on top of the placeholder initial commit, and adds account self-deletion (Danger Zone in Settings, password + typed-email confirmation, cascading DB cleanup, Stripe cancellation) per GDPR right-to-erasure. Excludes local build caches, node_modules, and internal agent scratch files; .gitignore hardened to keep those out going forward. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
109 lines
4.2 KiB
TypeScript
109 lines
4.2 KiB
TypeScript
import type { NextConfig } from "next";
|
|
|
|
// Umami's script origin, derived from NEXT_PUBLIC_UMAMI_SRC so the CSP below
|
|
// only needs the one env var to stay in sync with the <Script> tag in the
|
|
// root layouts — no separate CSP-domain variable to keep updated by hand.
|
|
const umamiOrigin = (() => {
|
|
if (!process.env.NEXT_PUBLIC_UMAMI_SRC) return null;
|
|
try {
|
|
return new URL(process.env.NEXT_PUBLIC_UMAMI_SRC).origin;
|
|
} catch {
|
|
return null;
|
|
}
|
|
})();
|
|
|
|
const nextConfig: NextConfig = {
|
|
output: "standalone",
|
|
serverExternalPackages: ["exceljs", "sharp", "pdfjs-dist", "@napi-rs/canvas", "heic-convert", "heic-decode", "libheif-js"],
|
|
// pdfjs-dist lädt @napi-rs/canvas zur Laufzeit dynamisch (createRequire),
|
|
// daher findet der Standalone-Trace das native Modul nicht von selbst. Ohne
|
|
// diesen Include fehlt @napi-rs/canvas im Docker-Image und PDF-Rasterisierung
|
|
// schlägt mit "Cannot load @napi-rs/canvas" fehl. Hier explizit einschließen.
|
|
outputFileTracingIncludes: {
|
|
"/api/scan": [
|
|
"./node_modules/@napi-rs/canvas/**/*",
|
|
"./node_modules/heic-convert/**/*",
|
|
"./node_modules/heic-decode/**/*",
|
|
"./node_modules/libheif-js/**/*",
|
|
"./node_modules/jpeg-js/**/*",
|
|
"./node_modules/pngjs/**/*",
|
|
],
|
|
},
|
|
images: {
|
|
remotePatterns: [
|
|
{
|
|
protocol: "http",
|
|
hostname: "localhost",
|
|
},
|
|
{
|
|
protocol: "https",
|
|
hostname: "localhost",
|
|
},
|
|
],
|
|
},
|
|
async redirects() {
|
|
return [
|
|
{
|
|
// The bare domain has no page of its own (only /de and /en are
|
|
// pre-rendered) — without this it 404s, which kills every backlink,
|
|
// social share, and type-in visit to the naked root URL.
|
|
source: "/",
|
|
destination: "/en",
|
|
permanent: true,
|
|
},
|
|
];
|
|
},
|
|
async headers() {
|
|
return [
|
|
{
|
|
// Unconditional security headers — applied to every response.
|
|
source: "/(.*)",
|
|
headers: [
|
|
{ key: "X-Frame-Options", value: "DENY" },
|
|
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
|
|
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), payment=()" },
|
|
{
|
|
// Static CSP, applied to every response via next.config headers().
|
|
// NOTE on `script-src 'self' 'unsafe-inline'`: the /de and /en
|
|
// landing pages are statically generated (SSG), so Next.js cannot
|
|
// inject a per-request nonce into their pre-built HTML. A strict
|
|
// `'self'`-only script-src would block Next.js's inline hydration
|
|
// scripts and break the pages. This is the pattern Next.js
|
|
// documents for statically rendered apps ("Without Nonces").
|
|
// 'unsafe-inline' is required for scripts, but every other
|
|
// directive stays strict (no eval, no external objects, etc.).
|
|
key: "Content-Security-Policy",
|
|
value: [
|
|
"default-src 'self'",
|
|
`script-src 'self' 'unsafe-inline'${umamiOrigin ? ` ${umamiOrigin}` : ""}`,
|
|
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
|
"img-src 'self' data: blob: https:",
|
|
"font-src 'self' data: https://fonts.gstatic.com",
|
|
`connect-src 'self'${umamiOrigin ? ` ${umamiOrigin}` : ""}`,
|
|
"object-src 'none'",
|
|
"base-uri 'self'",
|
|
"frame-ancestors 'none'",
|
|
"form-action 'self'",
|
|
].join("; "),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
// HSTS is emitted only when the request actually arrived over HTTPS
|
|
// (x-forwarded-proto: https). Emitting it on plain-HTTP responses would
|
|
// let an HTTP server promise an upgrade it cannot deliver and poison
|
|
// HTTP clients' upgrade expectations. The directive itself is unchanged:
|
|
// max-age=63072000 (2 years), includeSubDomains, preload.
|
|
source: "/(.*)",
|
|
has: [{ type: "header", key: "x-forwarded-proto", value: "https" }],
|
|
headers: [
|
|
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
|
|
],
|
|
},
|
|
];
|
|
},
|
|
};
|
|
|
|
export default nextConfig;
|