next.config.ts
Rama main · réplica de lectura del repositorio original.
import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin("./i18n/request.ts");
const securityHeaders = [
{ key: "X-Frame-Options", value: "DENY" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload",
},
{
key: "Permissions-Policy",
value: "camera=(), microphone=(), geolocation=()",
},
{
key: "Strict-Transport-Security",
// Posé par l'APP et pas seulement par le reverse proxy. Deux raisons :
// • le self-host tourne sans NPM devant — sans ça, une instance
// auto-hébergée n'a aucun HSTS ;
// • défense en profondeur si la config du proxy dérive.
// Inoffensif en dev : RFC 6797 §7.2 impose aux navigateurs d'IGNORER cet
// en-tête reçu sur une connexion non sécurisée. Un doublon avec celui du
// proxy est sans effet (le premier reçu fait foi, même valeur).
value: "max-age=63072000; includeSubDomains; preload",
},
{ key: "Cross-Origin-Opener-Policy", value: "same-origin" },
{ key: "Cross-Origin-Resource-Policy", value: "same-origin" },
{
key: "Content-Security-Policy",
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: blob:",
"font-src 'self'",
"connect-src 'self'",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
"object-src 'none'",
].join("; "),
},
];
const nextConfig: NextConfig = {
output: "standalone",
poweredByHeader: false,
// Force Next à embarquer les markdown utilisateur dans la sortie standalone.
// La layout `/docs` lit `docs/documentation/*.md` via fs.readdir à chaque
// requête (sidebar) — sans cette directive, Next ne trace pas ces fichiers
// (path en dehors de l'arbre `app/`) et le runtime échoue avec ENOENT.
outputFileTracingIncludes: {
"/[locale]/docs": ["./docs/documentation/**/*"],
"/[locale]/docs/[slug]": ["./docs/documentation/**/*"],
// Modèles de workflow lus par le guide d'installation (C-0051). Même
// ENOENT sinon : les deux routes répondraient 500 dans l'image.
"/api/projects/[slug]/setup/template": ["./docs/*.modele.yml"],
"/api/projects/[slug]/setup/verify": ["./docs/*.modele.yml"],
},
async headers() {
return [
{
source: "/:path*",
headers: securityHeaders,
},
];
},
};
export default withNextIntl(nextConfig);