const express = require("express"); const compression = require("compression"); const path = require("path"); const app = express(); const port = process.env.PORT || 3000; // Sécurité basique app.disable("x-powered-by"); // Compression gzip/brotli app.use(compression()); // Isolation requise pour de meilleures perfs WASM/Workers (Tesseract) app.use((req, res, next) => { res.setHeader("Cross-Origin-Opener-Policy", "same-origin"); res.setHeader("Cross-Origin-Embedder-Policy", "require-corp"); res.setHeader("Cross-Origin-Resource-Policy", "same-origin"); next(); }); // Répertoire statique = dossier courant (contient index.html) const staticRootDirectory = __dirname; const nodeModulesDirectory = path.join(__dirname, "node_modules"); const vendorDirectory = path.join(__dirname, "vendor"); app.use( express.static(staticRootDirectory, { index: "index.html", maxAge: "7d", etag: true, setHeaders(res, filePath) { const ext = path.extname(filePath); if (ext === ".html") { res.setHeader("Cache-Control", "no-cache"); } if (ext === ".wasm") { res.setHeader("Content-Type", "application/wasm"); } if (ext === ".js") { res.setHeader("Content-Type", "application/javascript; charset=utf-8"); } }, }) ); // Assets Tesseract.js auto-hébergés pour éviter les erreurs MIME/CDN app.use( "/vendor/tesseract", express.static(path.join(nodeModulesDirectory, "tesseract.js/dist"), { maxAge: "7d", setHeaders(res, filePath) { const ext = path.extname(filePath); if (ext === ".wasm") res.setHeader("Content-Type", "application/wasm"); if (ext === ".js") res.setHeader("Content-Type", "application/javascript; charset=utf-8"); }, }) ); app.use( "/vendor/tesseract-core", // Le package tesseract.js-core >=4 place les fichiers à la racine du module (pas de dossier dist) express.static(path.join(nodeModulesDirectory, "tesseract.js-core"), { maxAge: "7d", setHeaders(res, filePath) { const ext = path.extname(filePath); if (ext === ".wasm") res.setHeader("Content-Type", "application/wasm"); if (ext === ".js") res.setHeader("Content-Type", "application/javascript; charset=utf-8"); }, }) ); // Traineddata (langues) auto-hébergés app.use( "/vendor/tessdata", express.static(path.join(vendorDirectory, "tessdata"), { maxAge: "30d", setHeaders(res, filePath) { const ext = path.extname(filePath); if (ext === ".gz") res.setHeader("Content-Type", "application/gzip"); if (ext === ".traineddata") res.setHeader("Content-Type", "application/octet-stream"); }, }) ); // Fallback SPA/HTML unique // Utiliser une regex pour compatibilité avec path-to-regexp (Express 5) app.get(/.*/, (_req, res) => { res.sendFile(path.join(staticRootDirectory, "index.html")); }); app.listen(port, () => { // eslint-disable-next-line no-console console.log(`Site statique servi sur http://localhost:${port}`); });