Files
hoerdle/proxy.ts
Hördle Bot 85bdbf795c Refactor: Plausible-Konfiguration aktualisiert und twitterHandle entfernt
- Defaults auf neue Domains aktualisiert (hoerdle.de statt hoerdle.elpatron.me)
- CSP in proxy.ts konfigurierbar gemacht (liest Plausible-URL aus Umgebungsvariablen)
- twitterHandle entfernt (wurde nirgendwo verwendet)
- Dokumentation aktualisiert
2025-12-01 18:29:09 +01:00

69 lines
2.4 KiB
TypeScript

import createMiddleware from 'next-intl/middleware';
import type { NextRequest } from 'next/server';
const i18nMiddleware = createMiddleware({
locales: ['en', 'de'],
defaultLocale: 'en',
// Wir nutzen überall Locale-Präfixe (`/en`, `/de`)
localePrefix: 'always'
});
export default function proxy(request: NextRequest) {
// 1. i18n-Routing
const response = i18nMiddleware(request);
// 2. Security-Header ergänzen
const headers = response.headers;
headers.set('X-Frame-Options', 'SAMEORIGIN');
headers.set('X-XSS-Protection', '1; mode=block');
headers.set('X-Content-Type-Options', 'nosniff');
headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
// Extract Plausible domain from script URL for CSP
const plausibleScriptSrc = process.env.NEXT_PUBLIC_PLAUSIBLE_SCRIPT_SRC || 'https://plausible.example.com/js/script.js';
let plausibleOrigin = 'https://plausible.example.com';
try {
const url = new URL(plausibleScriptSrc);
plausibleOrigin = url.origin;
} catch {
// If URL parsing fails, try to extract domain manually
const match = plausibleScriptSrc.match(/https?:\/\/([^/]+)/);
if (match) {
plausibleOrigin = `https://${match[1]}`;
}
}
// Get other service URLs from environment (only add to CSP if configured)
const gotifyUrl = process.env.GOTIFY_URL;
const openrouterUrl = process.env.NEXT_PUBLIC_OPENROUTER_URL || 'https://openrouter.ai';
// Build CSP dynamically based on environment variables
const connectSrcParts = ["'self'", openrouterUrl, plausibleOrigin];
if (gotifyUrl && !gotifyUrl.includes('example.com')) {
connectSrcParts.push(gotifyUrl);
}
const csp = [
"default-src 'self'",
`script-src 'self' 'unsafe-inline' 'unsafe-eval' ${plausibleOrigin}`,
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: blob:",
"font-src 'self' data:",
`connect-src ${connectSrcParts.join(' ')}`,
"media-src 'self' blob:",
"frame-ancestors 'self'",
].join('; ');
headers.set('Content-Security-Policy', csp);
return response;
}
export const config = {
// Empfohlener Matcher aus der next-intl Doku:
// alle Routen außer _next, API und statischen Dateien
matcher: ['/((?!api|_next|.*\\..*).*)']
};