Files
scan-receipts/next.config.ts
Timo 84b9987c49 Add full application: receipt scanning, auth, billing, and account deletion
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>
2026-08-19 20:59:04 +02:00

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;