first commit

This commit is contained in:
Isuru Bimsara
2026-08-14 22:46:02 +05:30
commit 81d0e65686
132 changed files with 24398 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
/**
* Copyright (c) 2026 Niolla
* All rights reserved.
*/
// app/utils/pdfGenerator.js
const path = require("path");
const ejs = require("ejs");
const puppeteer = require("puppeteer");
const fs = require("fs");
const { imageToBase64 } = require("./imageToBase64.utill");
const { log, runningEnvironment } = require("./consoleLog.utill");
let browser;
let executablePath = null;
const isProd = runningEnvironment();
if (isProd) {
executablePath = "/usr/bin/chromium";
} else {
executablePath = puppeteer.executablePath();
}
/**
* Launch or reuse Puppeteer browser safely
*/
async function getBrowser() {
if (!browser || !browser.isConnected?.()) {
console.log("🚀 Launching Chromium from Docker system path...");
browser = await puppeteer.launch({
headless: "new",
executablePath: executablePath,
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--disable-gpu",
"--no-zygote"
],
});
console.log("✅ Browser launched");
}
return browser;
}
/**
* Close browser safely
*/
async function closeBrowser() {
if (browser) {
try {
await browser.close();
} catch (e) {}
browser = null;
}
}
/**
* Core PDF renderer
*/
async function renderPdf(templateFile, data = {}, opts = {}) {
const templatePath = path.join(__dirname, "../templates", templateFile);
console.log("📝 Rendering:", templateFile);
// Convert image to base64 if needed
if (data.logoUrl && !data.logoBase64) {
try {
data.logoBase64 = await imageToBase64(data.logoUrl);
} catch (err) {
console.error("❌ Logo conversion failed:", err.message);
data.logoBase64 = null;
}
}
const html = await ejs.renderFile(templatePath, data);
const browserInstance = await getBrowser();
const page = await browserInstance.newPage();
try {
// Safety timeouts
await page.setDefaultTimeout(60000);
await page.setDefaultNavigationTimeout(60000);
await page.setViewport({ width: 1200, height: 800 });
console.log("📄 Loading HTML...");
await page.setContent(html, { waitUntil: "networkidle0" });
await page.emulateMediaType("print");
// Wait for fonts
await page.evaluate(() => document.fonts?.ready || Promise.resolve());
const margin = {
top: 20,
bottom: 20,
left: 10,
right: 10,
...opts.margin,
};
// Optional: measure content (kept for logging only)
const contentHeightPx = await page.evaluate(() => {
const body = document.body;
const htmlEl = document.documentElement;
return Math.max(
body.scrollHeight,
body.offsetHeight,
htmlEl.scrollHeight,
htmlEl.offsetHeight
);
});
console.log("📏 Content height:", contentHeightPx, "px");
// ❗ REMOVED scaling logic (causes PDF distortion in production)
const showPageNumbers = opts.showPageNumbers === true;
console.log("⚙️ Generating PDF...");
const pdfBuffer = await page.pdf({
format: "A4",
printBackground: true,
preferCSSPageSize: true,
margin: {
top: `${margin.top}mm`,
bottom: `${margin.bottom}mm`,
left: `${margin.left}mm`,
right: `${margin.right}mm`,
},
displayHeaderFooter: showPageNumbers,
headerTemplate: `<div></div>`,
footerTemplate: showPageNumbers
? `
<div style="font-size:10px;width:100%;text-align:center;">
Page <span class="pageNumber"></span> / <span class="totalPages"></span>
</div>`
: `<div></div>`,
});
console.log("✅ PDF generated");
return pdfBuffer;
} catch (err) {
console.error("❌ PDF generation error:", err.message);
throw err;
} finally {
await page.close().catch(() => {});
}
}
/**
* Helpers
*/
const generatePdfFromPreCost = (data) =>
renderPdf("documents/precost/precost.ejs", data);
const generatePdfFromInvoice = (data) =>
renderPdf("documents/invoice/invoice.ejs", data);
const generatePdfFromDispatchNote = (data) =>
renderPdf("documents/dispatchNote/dispatchNote.ejs", data);
const generatePdfFromDeliveryNote = (data) =>
renderPdf("documents/deliveryNote/deliveryNote.ejs", data);
const generatePdfFromPO = (data) =>
renderPdf("documents/po/po.ejs", data);
const generatePdfFromCustomDocument = (data) =>
renderPdf("documents/customDocument/customDocument.ejs", data);
const generatePdfFromCreditNote = (data) =>
renderPdf("documents/creditNote/creditNote.ejs", data);
module.exports = {
generatePdfFromPreCost,
generatePdfFromInvoice,
generatePdfFromDispatchNote,
generatePdfFromDeliveryNote,
generatePdfFromPO,
generatePdfFromCustomDocument,
generatePdfFromCreditNote,
renderPdf,
closeBrowser,
};