/** * Template Handler Middleware * Handles injecting CSP nonces into HTML templates */ const fs = require('fs'); const path = require('path'); const winston = require('winston'); const { generateRichSnippet, escapeHtmlAttr } = require('./rich_snippet'); // Cache for HTML templates const templateCache = {}; function injectRichSnippetIntoHtml(html, snippet) { if (!snippet) return html; const metaTags = [ ``, ``, ``, ``, ``, ``, ``, ``, ``, `` ].join('\n\t\t'); // Replace the document title if present. html = html.replace(/[^<]*<\/title>/i, `<title>${escapeHtmlAttr(snippet.title)}`); // Inject tags before . if (/<\/head>/i.test(html)) { return html.replace(/<\/head>/i, `\t\t${metaTags}\n\n\t`); } return html + '\n' + metaTags; } /** * Template handler middleware * Preprocesses HTML templates and injects the CSP nonce */ function templateHandlerMiddleware(staticDir, options) { const opts = options || {}; const store = opts.store; const staticDocuments = opts.documents || {}; const config = opts.config || {}; // 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'); const isPasteRoute = req.url !== '/'; // 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 || ''; const renderAndSend = function(pasteContent) { // If the response was already sent (or the client disconnected), do nothing. if (res.headersSent || res.writableEnded) { return; } // Process the template - replace all nonce placeholders let html; try { // Replace {{cspNonce}} placeholders html = templateCache[indexPath].replace(/\{\{cspNonce\}\}/g, nonce); // If this is a paste route and we have content, inject rich snippet tags. if (isPasteRoute && pasteContent) { const key = req.url.slice(1); const snippet = generateRichSnippet({ key, content: pasteContent, req, config }); html = injectRichSnippetIntoHtml(html, snippet); } // Also ensure any