const express = require('express'); const sharp = require('sharp'); const path = require('path'); const log = require('../lib/logger'); const router = express.Router(); function normalizeColor(color) { if (!color) return '#cccccc'; if (/^[0-9A-Fa-f]{6}$/.test(color)) return '#' + color; if (/^[0-9A-Fa-f]{3}$/.test(color)) return '#' + color; return color.toLowerCase(); } function escapeXml(str) { return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } async function placeholderHandler(req, res) { const { size, bg, fg, text: textFromPath } = req.params; const { text: textFromQuery, format = 'png', fontsize } = req.query; const text = textFromQuery || textFromPath || ''; const [w, h] = size.split('x').map(Number); const width = Math.min(Math.max(w || 300, 1), 4000); const height = Math.min(Math.max(h || 200, 1), 4000); const bgColor = normalizeColor(bg); const fgColor = normalizeColor(fg); const fontSize = fontsize ? parseInt(fontsize, 10) : Math.max(12, Math.floor(Math.min(width, height) / 8)); const svg = ` ${escapeXml(text)} `; try { let img = sharp(Buffer.from(svg)); if (format === 'jpg' || format === 'jpeg') { res.type('jpeg').end(await img.jpeg({ quality: 90 }).toBuffer()); } else if (format === 'webp') { res.type('webp').end(await img.webp({ quality: 90 }).toBuffer()); } else { res.type('png').end(await img.png().toBuffer()); } } catch (err) { log.error('Placeholder error', { error: err.message }); res.status(500).json({ error: 'Generation failed' }); } } // Placeholder page router.get('/', (req, res) => { res.sendFile(path.join(__dirname, '..', 'public', 'placeholder.html')); }); router.get('/:size/:bg/:fg', placeholderHandler); router.get('/:size/:bg/:fg/:text', placeholderHandler); module.exports = router;