From 410a78ab2bcd8d3d4ccfaf6dd2cfaeaa9ca43877 Mon Sep 17 00:00:00 2001 From: ImBenji Date: Wed, 31 Dec 2025 13:30:24 +0000 Subject: [PATCH] Add request logging middleware and improve avatar/image URL handling --- api.js | 203 +++++++++++++++++++++++++++++++++++++++++++++++++---- quote.html | 64 ++++++++++------- 2 files changed, 229 insertions(+), 38 deletions(-) diff --git a/api.js b/api.js index 9ce92cf..f999101 100644 --- a/api.js +++ b/api.js @@ -16,6 +16,42 @@ if (!fs.existsSync(CACHE_DIR)) { app.use(express.json({ limit: '1gb' })); app.use(express.urlencoded({ limit: '1gb', extended: true })); +// Request logging middleware +app.use((req, res, next) => { + const timestamp = new Date().toISOString(); + console.log(`\n[${timestamp}] ${req.method} ${req.url}`); + + if (req.method === 'POST' && req.body) { + const logBody = { ...req.body }; + + // Truncate long base64 strings for readability + if (logBody.avatarUrl && logBody.avatarUrl.startsWith('data:')) { + logBody.avatarUrl = logBody.avatarUrl.substring(0, 50) + '... (base64 truncated)'; + } + if (logBody.imageUrl && logBody.imageUrl.startsWith('data:')) { + logBody.imageUrl = logBody.imageUrl.substring(0, 50) + '... (base64 truncated)'; + } + + console.log('Body:', JSON.stringify(logBody, null, 2)); + } + + if (req.method === 'GET' && Object.keys(req.query).length > 0) { + const logQuery = { ...req.query }; + + // Truncate long base64 strings for readability + if (logQuery.avatarUrl && logQuery.avatarUrl.startsWith('data:')) { + logQuery.avatarUrl = logQuery.avatarUrl.substring(0, 50) + '... (base64 truncated)'; + } + if (logQuery.imageUrl && logQuery.imageUrl.startsWith('data:')) { + logQuery.imageUrl = logQuery.imageUrl.substring(0, 50) + '... (base64 truncated)'; + } + + console.log('Query:', JSON.stringify(logQuery, null, 2)); + } + + next(); +}); + function normalizeConfig(config) { // Remove null, undefined, and empty string values const normalized = {}; @@ -115,18 +151,119 @@ function formatTimestamp(epoch) { } async function generateQuoteBuffer(config) { - const htmlTemplate = fs.readFileSync(path.join(__dirname, 'quote.html'), 'utf8'); + // Build HTML directly with values injected + const avatarHtml = config.avatarUrl + ? `` + : `
`; - const configScript = ` - - `; + const imageHtml = config.imageUrl + ? `
` + : ''; - const modifiedHtml = htmlTemplate.replace('', `${configScript}`); + const html = ` + + + + + + +
+
+
+
${avatarHtml}
+ +
+
${config.text}
+ ${imageHtml} +
${config.timestamp}
+
+
+ + +`; const image = await nodeHtmlToImage({ - html: modifiedHtml, + html: html, puppeteerArgs: { args: ['--no-sandbox'], defaultViewport: { @@ -135,7 +272,7 @@ async function generateQuoteBuffer(config) { } }, beforeScreenshot: async (page) => { - await new Promise(resolve => setTimeout(resolve, 500)); + await new Promise(resolve => setTimeout(resolve, 1000)); } }); @@ -150,9 +287,9 @@ app.get('/generate', async (req, res) => { const config = { displayName: req.query.displayName || "Anonymous", username: req.query.username || "@anonymous", - avatarUrl: req.query.avatarUrl || null, + avatarUrl: fixDataUri(req.query.avatarUrl) || null, text: req.query.text || "No text provided", - imageUrl: req.query.imageUrl || null, + imageUrl: fixDataUri(req.query.imageUrl) || null, timestamp: timestamp, viewsCount: req.query.viewsCount || "0" }; @@ -178,16 +315,54 @@ app.get('/generate', async (req, res) => { }); // POST endpoint - use request body +function detectImageType(base64String) { + // Extract base64 data + const base64Data = base64String.includes(',') ? base64String.split(',')[1] : base64String; + const buffer = Buffer.from(base64Data, 'base64'); + + // Check magic numbers + if (buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) { + return 'image/jpeg'; + } else if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4E && buffer[3] === 0x47) { + return 'image/png'; + } else if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) { + return 'image/gif'; + } else if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46) { + return 'image/webp'; + } + return 'image/png'; // default fallback +} + +function fixDataUri(dataUri) { + if (!dataUri || !dataUri.startsWith('data:')) return dataUri; + + // Extract the base64 part + const parts = dataUri.split(','); + if (parts.length !== 2) return dataUri; + + const base64Data = parts[1]; + + try { + const correctType = detectImageType(base64Data); + return `data:${correctType};base64,${base64Data}`; + } catch (error) { + console.error('Invalid base64 data:', error.message); + return null; + } +} + app.post('/generate', async (req, res) => { try { const timestamp = req.body.timestamp ? formatTimestamp(parseInt(req.body.timestamp)) : formatTimestamp(Date.now() / 1000); + const username = req.body.username?.trim(); + const config = { displayName: req.body.displayName || "Anonymous", - username: req.body.username || "@anonymous", - avatarUrl: req.body.avatarUrl || null, + username: (username && username !== "@") ? username : "@anonymous", + avatarUrl: fixDataUri(req.body.avatarUrl) || null, text: req.body.text || "No text provided", - imageUrl: req.body.imageUrl || null, + imageUrl: fixDataUri(req.body.imageUrl) || null, timestamp: timestamp, viewsCount: req.body.viewsCount || "0" }; diff --git a/quote.html b/quote.html index 3400d72..753882d 100644 --- a/quote.html +++ b/quote.html @@ -47,13 +47,17 @@ width: 40px; height: 40px; border-radius: 50%; - background-size: cover; - background-position: center; background-color: rgb(51, 54, 57); position: relative; overflow: hidden; } + .avatar img { + width: 100%; + height: 100%; + object-fit: cover; + } + .avatar-placeholder { width: 100%; height: 100%; @@ -180,6 +184,7 @@
+