271 lines
10 KiB
JavaScript
271 lines
10 KiB
JavaScript
"use strict"
|
|
|
|
const puppeteer = require("puppeteer")
|
|
//const fetch = require('node-fetch');
|
|
const fs = require("fs")
|
|
|
|
console.log("Script grafana_pdf.js started...")
|
|
|
|
/*
|
|
const url = process.argv[2];
|
|
const auth_string = process.argv[3];
|
|
let outfile = process.argv[4];
|
|
*/
|
|
const url =
|
|
"https://bma-dashboard.frappet.synology.me/d/5EwyjelSk/1408ef66-0081-5b3f-aa00-5e70aa9bdbf1?orgId=1&kiosk=true"
|
|
const auth_string = "admin:xxx"
|
|
let outfile = "./url_gf.pdf"
|
|
|
|
const width_px = parseInt(process.env.PDF_WIDTH_PX, 10) || 1200
|
|
console.log("PDF width set to:", width_px)
|
|
|
|
const auth_header = "Basic " + Buffer.from(auth_string).toString("base64")
|
|
|
|
;(async () => {
|
|
try {
|
|
console.log("URL provided:", url)
|
|
console.log("Checking URL accessibility...")
|
|
const response = await fetch(url, {
|
|
method: "GET",
|
|
headers: { Authorization: auth_header },
|
|
})
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Unable to access URL. HTTP status: ${response.status}`)
|
|
}
|
|
|
|
const contentType = response.headers.get("content-type")
|
|
if (!contentType || !contentType.includes("text/html")) {
|
|
throw new Error("The URL provided is not a valid Grafana instance.")
|
|
}
|
|
|
|
let finalUrl = url
|
|
if (process.env.FORCE_KIOSK_MODE === "true") {
|
|
console.log("Checking if kiosk mode is enabled.")
|
|
if (!finalUrl.includes("&kiosk")) {
|
|
console.log("Kiosk mode not enabled. Enabling it.")
|
|
finalUrl += "&kiosk=true"
|
|
}
|
|
console.log("Kiosk mode enabled.")
|
|
}
|
|
|
|
console.log("Starting browser...")
|
|
const browser = await puppeteer.launch({
|
|
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH,
|
|
headless: true,
|
|
// args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']
|
|
})
|
|
|
|
const page = await browser.newPage()
|
|
console.log("Browser started...")
|
|
|
|
/*
|
|
await page.setExtraHTTPHeaders({'Authorization': auth_header});
|
|
await page.setDefaultNavigationTimeout(process.env.PUPPETEER_NAVIGATION_TIMEOUT || 120000);
|
|
|
|
await page.setViewport({
|
|
width: width_px,
|
|
height: 800,
|
|
deviceScaleFactor: 2,
|
|
isMobile: false
|
|
});
|
|
*/
|
|
console.log("Navigating to URL...")
|
|
await page.goto(finalUrl, { waitUntil: "networkidle0" })
|
|
console.log("Page loaded...")
|
|
/*
|
|
await page.evaluate(() => {
|
|
let infoCorners = document.getElementsByClassName('panel-info-corner');
|
|
for (let el of infoCorners) {
|
|
el.hidden = true;
|
|
}
|
|
let resizeHandles = document.getElementsByClassName('react-resizable-handle');
|
|
for (let el of resizeHandles) {
|
|
el.hidden = true;
|
|
}
|
|
});
|
|
*/
|
|
let dashboardName = "output_grafana"
|
|
let date = new Date().toISOString().split("T")[0]
|
|
let addRandomStr = false
|
|
/*
|
|
if (process.env.EXTRACT_DATE_AND_DASHBOARD_NAME_FROM_HTML_PANEL_ELEMENTS === 'true') {
|
|
console.log("Extracting dashboard name and date from the HTML page...");
|
|
let scrapedDashboardName = await page.evaluate(() => {
|
|
const dashboardElement = document.getElementById('display_actual_dashboard_title');
|
|
return dashboardElement ? dashboardElement.innerText.trim() : null;
|
|
});
|
|
|
|
let scrapedDate = await page.evaluate(() => {
|
|
const dateElement = document.getElementById('display_actual_date');
|
|
return dateElement ? dateElement.innerText.trim() : null;
|
|
});
|
|
|
|
let scrapedPanelName = await page.evaluate(() => {
|
|
const scrapedPanelName = document.querySelectorAll('h6');
|
|
if (scrapedPanelName.length > 1) { // Multiple panels detected
|
|
console.log("Multiple panels detected. Unable to fetch a unique panel name. Using default value.")
|
|
return null;
|
|
}
|
|
if (scrapedPanelName[0] && scrapedPanelName[0].innerText.trim() === '') {
|
|
console.log("Empty panel name detected. Using default value.")
|
|
return null;
|
|
}
|
|
return scrapedPanelName[0] ? scrapedPanelName[0].innerText.trim() : null;
|
|
});
|
|
|
|
if (scrapedPanelName && !scrapedDashboardName) {
|
|
console.log("Panel name fetched:", scrapedPanelName);
|
|
dashboardName = scrapedPanelName;
|
|
addRandomStr = false;
|
|
} else if (!scrapedDashboardName) {
|
|
console.log("Dashboard name not found. Using default value.");
|
|
addRandomStr = true;
|
|
} else {
|
|
console.log("Dashboard name fetched:", scrapedDashboardName);
|
|
dashboardName = scrapedDashboardName;
|
|
}
|
|
|
|
if (scrapedPanelName && !scrapedDate) {
|
|
const urlParts = new URL(url);
|
|
const from = urlParts.searchParams.get('from');
|
|
const to = urlParts.searchParams.get('to');
|
|
if (from && to) {
|
|
const fromDate = isNaN(from) ? from.replace(/[^\w\s-]/g, '_') : new Date(parseInt(from)).toISOString().split('T')[0];
|
|
const toDate = isNaN(to) ? to.replace(/[^\w\s-]/g, '_') : new Date(parseInt(to)).toISOString().split('T')[0];
|
|
date = `${fromDate}_to_${toDate}`;
|
|
} else {
|
|
// using date in URL
|
|
date = new Date().toISOString().split('T')[0];
|
|
}
|
|
} else if (!scrapedDate) {
|
|
console.log("Date not found. Using default value.");
|
|
} else {
|
|
console.log("Date fetched:", date);
|
|
date = scrapedDate;
|
|
}
|
|
} else {
|
|
console.log("Extracting dashboard name and date from the URL...");
|
|
const urlParts = new URL(url);
|
|
const pathSegments = urlParts.pathname.split('/');
|
|
dashboardName = pathSegments[pathSegments.length - 1] || dashboardName;
|
|
const from = urlParts.searchParams.get('from');
|
|
const to = urlParts.searchParams.get('to');
|
|
if (from && to) {
|
|
const fromDate = isNaN(from) ? from.replace(/[^\w\s-]/g, '_') : new Date(parseInt(from)).toISOString().split('T')[0];
|
|
const toDate = isNaN(to) ? to.replace(/[^\w\s-]/g, '_') : new Date(parseInt(to)).toISOString().split('T')[0];
|
|
date = `${fromDate}_to_${toDate}`;
|
|
} else {
|
|
date = new Date().toISOString().split('T')[0];
|
|
}
|
|
console.log("Dashboard name fetched from URL:", dashboardName);
|
|
console.log("Trying to fetch the panel name from the page...")
|
|
let scrapedPanelName = await page.evaluate(() => {
|
|
const scrapedPanelName = document.querySelectorAll('h6');
|
|
console.log(scrapedPanelName)
|
|
if (scrapedPanelName.length > 1) { // Multiple panels detected
|
|
console.log("Multiple panels detected. Unable to fetch a unique panel name. Using default value.")
|
|
return null;
|
|
}
|
|
if (scrapedPanelName[0] && scrapedPanelName[0].innerText.trim() === '') {
|
|
console.log("Empty panel name detected. Using default value.")
|
|
return null;
|
|
}
|
|
return scrapedPanelName[0] ? scrapedPanelName[0].innerText.trim() : null;
|
|
});
|
|
|
|
if (scrapedPanelName) {
|
|
console.log("Panel name fetched:", scrapedPanelName);
|
|
dashboardName = scrapedPanelName;
|
|
addRandomStr = false;
|
|
}
|
|
|
|
console.log("Date fetched from URL:", date);
|
|
}
|
|
|
|
//outfile = `./${dashboardName.replace(/\s+/g, '_')}_${date.replace(/\s+/g, '_')}${addRandomStr ? '_' + Math.random().toString(36).substring(7) : ''}.pdf`;
|
|
|
|
const loginPageDetected = await page.evaluate(() => {
|
|
const resetPasswordButton = document.querySelector('a[href*="reset-email"]');
|
|
return !!resetPasswordButton;
|
|
})
|
|
|
|
if (loginPageDetected) {
|
|
throw new Error("Login page detected. Check your credentials.");
|
|
}
|
|
|
|
if(process.env.DEBUG_MODE === 'true') {
|
|
const documentHTML = await page.evaluate(() => {
|
|
return document.querySelector("*").outerHTML;
|
|
});
|
|
if (!fs.existsSync('./debug')) {
|
|
fs.mkdirSync('./debug');
|
|
}
|
|
const filename = `./debug/debug_${dashboardName.replace(/\s+/g, '_')}_${date.replace(/\s+/g, '_')}${'_' + Math.random().toString(36).substring(7)}.html`;
|
|
fs.writeFileSync(filename, documentHTML);
|
|
console.log("Debug HTML file saved at:", filename);
|
|
|
|
}
|
|
*/
|
|
const totalHeight = await page.evaluate(() => {
|
|
const scrollableSection = document.querySelector(".scrollbar-view")
|
|
return scrollableSection
|
|
? scrollableSection.firstElementChild.scrollHeight
|
|
: null
|
|
})
|
|
|
|
if (!totalHeight) {
|
|
throw new Error(
|
|
"Unable to determine the page height. The selector '.scrollbar-view' might be incorrect or missing."
|
|
)
|
|
} else {
|
|
console.log("Page height adjusted to:", totalHeight)
|
|
}
|
|
|
|
let x = await page.evaluate(async () => {
|
|
const scrollableSection = document.querySelector(".scrollbar-view")
|
|
if (scrollableSection) {
|
|
const childElement = scrollableSection.firstElementChild
|
|
let scrollPosition = 0
|
|
let viewportHeight = window.innerHeight
|
|
|
|
while (scrollPosition < childElement.scrollHeight) {
|
|
scrollableSection.scrollBy(0, viewportHeight)
|
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
scrollPosition += viewportHeight
|
|
}
|
|
return scrollPosition
|
|
}
|
|
return 0
|
|
})
|
|
console.log("scrollPosition=" + x)
|
|
|
|
await page.setViewport({
|
|
width: width_px,
|
|
height: totalHeight,
|
|
deviceScaleFactor: 2,
|
|
isMobile: false,
|
|
})
|
|
|
|
console.log("Generating PDF...")
|
|
await page.pdf({
|
|
path: outfile,
|
|
width: width_px + "px",
|
|
height: totalHeight + "px",
|
|
printBackground: true,
|
|
scale: 1,
|
|
displayHeaderFooter: false,
|
|
margin: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
})
|
|
console.log(`PDF generated: ${outfile}`)
|
|
|
|
await browser.close()
|
|
console.log("Browser closed.")
|
|
|
|
//process.send({ success: true, path: outfile });
|
|
} catch (error) {
|
|
console.error("Error during PDF generation:", error.message)
|
|
//process.send({ success: false, error: error.message });
|
|
process.exit(1)
|
|
}
|
|
})()
|