const puppeteer = require('puppeteer'); const POOL_SIZE = parseInt(process.env.BROWSER_POOL_SIZE) || 5; const VIEWPORT_WIDTH = 3240; const VIEWPORT_HEIGHT = 3240; let browsers = []; let availableBrowsers = []; let initPromise = null; async function initPool() { if (initPromise) return initPromise; initPromise = (async () => { console.log(`Initializing browser pool with ${POOL_SIZE} instances...`); for (let i = 0; i < POOL_SIZE; i++) { const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); browsers.push(browser); availableBrowsers.push(browser); } console.log(`Browser pool ready with ${POOL_SIZE} browsers`); })(); return initPromise; } async function acquireBrowser() { await initPool(); // wait for available browser while (availableBrowsers.length === 0) { await new Promise(resolve => setTimeout(resolve, 50)); } return availableBrowsers.pop(); } function releaseBrowser(browser) { availableBrowsers.push(browser); } // renders html and returns screenshot as buffer async function renderHtml(html, waitTime = 1000) { const browser = await acquireBrowser(); let page = null; try { page = await browser.newPage(); await page.setViewport({ width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT }); await page.setContent(html, { waitUntil: ['load', 'domcontentloaded', 'networkidle0'] }); // wait for body to be ready await page.waitForSelector('body'); // wait for any animations or laoding await new Promise(resolve => setTimeout(resolve, waitTime)); const screenshot = await page.screenshot({ type: 'png', fullPage: false, encoding: 'binary' }); return Buffer.from(screenshot); } finally { if (page) await page.close(); releaseBrowser(browser); } } async function shutdownPool() { console.log('Shutting down browser pool...'); for (const browser of browsers) { await browser.close(); } browsers = []; availableBrowsers = []; initPromise = null; console.log('Browser pool shut down'); } // Graceful shutdown handlers process.on('SIGINT', async () => { await shutdownPool(); process.exit(0); }); process.on('SIGTERM', async () => { await shutdownPool(); process.exit(0); }); module.exports = { initPool, renderHtml, shutdownPool, POOL_SIZE };