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 = `
+
+
+
+
+
+
+
+
+
+
+
+`;
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 @@