templates.js and renderer.js content extracted via direct line-slicing (not retyped) given their size (~660-line HTML/CSS template functions) -- avoids transcription risk on template-literal-heavy code. Verified via smoke-test harness (28/28 match). server.js: 4492 -> 3784 lines (6838 originally -- 45% reduction so far).
66 lines
2.2 KiB
JavaScript
66 lines
2.2 KiB
JavaScript
// PDF Export: lazy puppeteer loader + response cache + renderer
|
|
|
|
// ── PDF Export: lazy puppeteer loader ────────────────────────────────────────
|
|
let _puppeteerBrowser = null;
|
|
let _puppeteerLoading = false;
|
|
async function getPuppeteerBrowser() {
|
|
if (_puppeteerBrowser) return _puppeteerBrowser;
|
|
if (_puppeteerLoading) {
|
|
// Wait for in-flight launch
|
|
await new Promise(r => setTimeout(r, 2000));
|
|
return _puppeteerBrowser;
|
|
}
|
|
_puppeteerLoading = true;
|
|
try {
|
|
const puppeteer = require("puppeteer");
|
|
_puppeteerBrowser = await puppeteer.launch({
|
|
headless: true,
|
|
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", "--disable-gpu"]
|
|
});
|
|
console.log("[PDF] Puppeteer browser launched");
|
|
_puppeteerBrowser.on("disconnected", () => {
|
|
console.log("[PDF] Browser disconnected — will relaunch on next request");
|
|
_puppeteerBrowser = null;
|
|
_puppeteerLoading = false;
|
|
});
|
|
} catch (e) {
|
|
console.error("[PDF] Puppeteer unavailable:", e.message);
|
|
_puppeteerBrowser = null;
|
|
}
|
|
_puppeteerLoading = false;
|
|
return _puppeteerBrowser;
|
|
}
|
|
|
|
// PDF response cache: key = "asn:format" or "compare:asn1:asn2" → {buf, ts}
|
|
const pdfCache = new Map();
|
|
const PDF_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
|
|
function pdfCacheGet(key) {
|
|
const e = pdfCache.get(key);
|
|
if (e && (Date.now() - e.ts) < PDF_CACHE_TTL) return e.buf;
|
|
return null;
|
|
}
|
|
function pdfCacheSet(key, buf) {
|
|
if (pdfCache.size > 100) pdfCache.delete(pdfCache.keys().next().value);
|
|
pdfCache.set(key, { buf, ts: Date.now() });
|
|
}
|
|
|
|
async function renderHtmlToPdf(html) {
|
|
const browser = await getPuppeteerBrowser();
|
|
if (!browser) throw new Error('PDF renderer unavailable (puppeteer not installed)');
|
|
const page = await browser.newPage();
|
|
try {
|
|
await page.setContent(html, { waitUntil: 'domcontentloaded', timeout: 15000 });
|
|
const buf = await page.pdf({
|
|
format: 'A4',
|
|
printBackground: true,
|
|
margin: { top: '18mm', bottom: '18mm', left: '16mm', right: '16mm' },
|
|
});
|
|
return buf;
|
|
} finally {
|
|
await page.close();
|
|
}
|
|
}
|
|
|
|
module.exports = { getPuppeteerBrowser, pdfCacheGet, pdfCacheSet, renderHtmlToPdf };
|