Initial commit: Kalendartool (Next.js, Prisma, Docker)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Kühn
2026-10-08 14:38:04 +02:00
commit bc49c3074e
94 changed files with 12812 additions and 0 deletions

73
next.config.mjs Normal file
View File

@@ -0,0 +1,73 @@
/** @type {import('next').NextConfig} */
// MultiToolApp-Plattform (module-plan.md):
// HUB_URL – Basis-URL des Hubs (JWKS-Abruf für Token-Validierung)
// HUB_BASE_PATH – Pfad, unter dem der Hub diese App routet (z. B. /apps/kalender).
// Setzt Next.js basePath → alle Routen hängen darunter.
// Ohne HUB_BASE_PATH läuft die App standalone (basePath: '').
const hubUrl = (process.env.HUB_URL ?? '').replace(/\/$/, '');
const hubBasePath = (process.env.HUB_BASE_PATH ?? '').replace(/\/$/, '');
const nextConfig = {
// Standalone-Ausgabe für einen schlanken Docker-Container
output: 'standalone',
reactStrictMode: true,
// Proxy-Modus: App läuft unter /apps/<slug> innerhalb der Hub-Domain.
// basePath '' (falsy) = Standalone-Betrieb ohne Pfad-Präfix.
...(hubBasePath ? { basePath: hubBasePath } : {}),
// Manifest-Route: /.well-known/app-manifest → /api/manifest
// (Next.js App Router routed keine Dot-Ordner; mit beforeFiles-Phase
// fangen wir den Pfad ab, bevor basePath zuschlägt.)
async rewrites() {
return {
beforeFiles: [
{
source: '/.well-known/app-manifest',
destination: '/api/manifest',
},
],
};
},
// Security-Header (Next.js setzt keine davon by default):
// - X-Content-Type-Options: verhindert MIME-Sniffing
// - Referrer-Policy: keine URLs/Token an Dritte leaken
// - Permissions-Policy: Browser-Features, die die App nicht nutzt
// - CSP: Skripte nur 'self' + Inline (Next.js Hydration-Snippet);
// style-src 'unsafe-inline' ist fuer Tailwind noetig.
// - frame-ancestors: nur der Hub darf einbetten (statt X-Frame-Options,
// das Subdomain-Embedding blockieren wuerde – platform-plan.md §4).
async headers() {
return [
{
source: '/:path*',
headers: [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()',
},
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data:",
"font-src 'self'",
// JWKS-Abruf vom Hub (Token-Validierung in /auth/hub)
hubUrl ? `connect-src 'self' ${hubUrl}` : "connect-src 'self'",
// Proxy-Modus: kein Framing mehr nötig – aber der Hub darf
// weiterhin einbetten (Rückwärtskompatibilität).
hubUrl ? `frame-ancestors ${hubUrl}` : "frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
].join('; '),
},
],
},
];
},
};
export default nextConfig;