/** * Template Handler Middleware * Handles injecting CSP nonces into HTML templates */ const fs = require('fs'); const path = require('path'); const winston = require('winston'); // Cache for HTML templates const templateCache = {}; /** * Template handler middleware * Preprocesses HTML templates and injects the CSP nonce */ function templateHandlerMiddleware(staticDir) { // Load the index.html template at startup to avoid reading from disk on each request try { const indexPath = path.join(staticDir, 'index.html'); templateCache[indexPath] = fs.readFileSync(indexPath, 'utf8'); winston.debug('Loaded template: ' + indexPath); } catch (err) { winston.error('Failed to load index.html template at startup:', err); } return function(req, res, next) { // Only process specific URLs if (req.url === '/' || req.url.match(/^\/[a-zA-Z0-9_-]+$/)) { const indexPath = path.join(staticDir, 'index.html'); // If template is not in cache, try to load it if (!templateCache[indexPath]) { try { templateCache[indexPath] = fs.readFileSync(indexPath, 'utf8'); winston.debug('Loaded template on demand: ' + indexPath); } catch (err) { winston.error('Error reading index.html template:', err); return next(); } } // Get the CSP nonce from the request (set by CSP middleware) const nonce = req.cspNonce || ''; // Add debug log console.log('Template handler processing with nonce:', nonce); // Process the template - replace all nonce placeholders let html; try { html = templateCache[indexPath].replace(/\{\{cspNonce\}\}/g, nonce); } catch (err) { winston.error('Error processing template:', err); return next(); } // Set response headers for HTML content res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.setHeader('Content-Length', Buffer.byteLength(html)); // Send the response res.statusCode = 200; res.end(html); // Don't proceed to other middleware return; } // Continue to next middleware for non-index requests next(); }; } module.exports = templateHandlerMiddleware;