Files
litlyx/dashboard/server/api/project/generate_pdf_adv.ts
2025-11-28 16:49:20 +01:00

865 lines
32 KiB
TypeScript

import { ProjectModel, TProject } from "~/shared/schema/project/ProjectSchema";
import puppeteer from 'puppeteer'
import { PDFDocument } from 'pdf-lib';
import fs from 'fs';
import path from "path";
import { VisitModel } from "~/shared/schema/metrics/VisitSchema";
import { formatNumberK, formatTime } from "~/utils/numberFormatter";
import { visitController } from "~/server/controllers/VisitController";
import { durationController } from "~/server/controllers/DurationController";
import { bouncingController } from "~/server/controllers/BouncingController";
import { AiService } from "~/server/services/ai/AiService";
import referrers from "../data/referrers";
type BaseOptions = {
target: TProject,
resourcesPath: string,
domain: string,
from: Date,
to: Date,
customLogo: string
}
function getDistribution(array: any[], key: string, current: number) {
const count = array.reduce((a, e) => a + e[key], 0);
return (100 / count * array[current][key]).toFixed(2);
}
export async function getPage1(options: BaseOptions) {
const page1_raw = fs.readFileSync(path.join(options.resourcesPath, 'pages/page1.html'), 'utf-8');
const page1 = page1_raw
.replace('%PROJECT_NAME%', options.target.name)
.replace('%DOMAIN%', options.domain)
.replace('%FROM%', options.from.toLocaleString())
.replace('%TO%', options.to.toLocaleString())
.replace('%LOGO%', options.customLogo)
return page1;
}
export async function getPage2(options: BaseOptions) {
const page2_raw = fs.readFileSync(path.join(options.resourcesPath, 'pages/page2.html'), 'utf-8');
const [visit_count, session_count, pages, countries, devices, referrers, session_duration, bouncing_rate] = await Promise.all([
VisitModel.countDocuments({
project_id: options.target._id,
created_at: {
$gte: options.from,
$lte: options.to
},
website: options.domain
}),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{
$group: {
_id: null,
uniqueSessions: { $addToSet: "$session" }
}
},
{
$project: {
_id: 0,
uniqueSessionCount: { $size: "$uniqueSessions" }
}
}
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{ $group: { _id: "$page", count: { $sum: 1, } } },
{ $sort: { count: -1 } },
{ $limit: 3 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{ $group: { _id: "$country", count: { $sum: 1, } } },
{ $sort: { count: -1 } },
{ $limit: 3 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: {
$gte: options.from,
$lte: options.to
},
website: options.domain
}
},
{ $group: { _id: "$device", count: { $sum: 1, } } },
{ $sort: { count: -1 } },
{ $limit: 3 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: {
$gte: options.from,
$lte: options.to
},
website: options.domain
}
},
{ $group: { _id: "$referrer", count: { $sum: 1, } } },
{ $sort: { count: -1 } },
{ $limit: 3 }
]),
durationController.executeDynamic({
project_id: options.target._id.toString(),
from: options.from.getTime(), to: options.to.getTime(),
slice: 'day', domain: options.domain
}),
await bouncingController.executeDynamic({
project_id: options.target._id.toString(),
from: options.from.getTime(), to: options.to.getTime(),
slice: 'day', domain: options.domain
})
]);
const ai = AiService.init();
const data: any[] = [
`- Visits: ${visit_count}`,
`- Sessions: ${session_count[0].uniqueSessionCount}`,
`- Most viewed pages: ${JSON.stringify(pages)}`,
`- Most viewed from countries: ${JSON.stringify(countries)}`,
`- Most viewed from devices: ${JSON.stringify(devices)}`,
`- Date of analytics: ${options.from.toISOString()} to ${options.to.toISOString()}`,
`- Domain of analytics: ${options.domain}`,
]
const aiResponse = await ai.responses.create({
model: 'gpt-5-nano',
input: `Generate an insight of the current website analytics data (max 600 characters):\n${data.join('\n')}`
})
const page2 = page2_raw
.replace('%VISITS%', formatNumberK(visit_count ?? 0, 1))
.replace('%SESSIONS%', formatNumberK(session_count[0].uniqueSessionCount ?? 0, 1))
.replace('%BOUNCING_RATE%', (bouncing_rate.data.reduce((a, e) => a + e.count, 0) / bouncing_rate.data.length).toFixed(2) + '%')
.replace('%SESSION_DURATION%', formatTime(session_duration.data.reduce((a, e) => a + e.count, 0) * 1000))
.replace('%PAGE_0%', pages[0]?._id)
.replace('%PAGE_1%', pages[1]?._id)
.replace('%PAGE_2%', pages[2]?._id)
.replace('%COUNTRY_0%', countries[0]?._id)
.replace('%COUNTRY_1%', countries[1]?._id)
.replace('%COUNTRY_2%', countries[2]?._id)
.replace('%DEVICE_0%', devices[0]?._id)
.replace('%DEVICE_1%', devices[1]?._id)
.replace('%DEVICE_2%', devices[2]?._id)
.replace('%REFERRER_0%', referrers[0]?._id)
.replace('%REFERRER_1%', referrers[1]?._id)
.replace('%REFERRER_2%', referrers[2]?._id)
.replace('%AI_0%', aiResponse.output_text)
.replace('%LOGO%', options.customLogo)
return page2;
}
export async function getPage3(options: BaseOptions) {
const page3_raw = fs.readFileSync(path.join(options.resourcesPath, 'pages/page3.html'), 'utf-8');
const [visits, sessions, devices, visits_timeline] = await Promise.all([
VisitModel.countDocuments({
project_id: options.target._id,
created_at: {
$gte: options.from,
$lte: options.to
},
website: options.domain
}),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: {
$gte: options.from,
$lte: options.to
},
website: options.domain
}
},
{ $group: { _id: "$session" } },
{ $count: "count" }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: {
$gte: options.from,
$lte: options.to
},
website: options.domain
}
},
{ $group: { _id: "$device", count: { $sum: 1, } } },
{ $sort: { count: -1 } },
{ $limit: 3 }
]),
visitController.executeDynamic({
project_id: options.target._id.toString(),
from: options.from.getTime(), to: options.to.getTime(),
slice: 'day', domain: options.domain
})
]);
const ai = AiService.init();
const [ai0, ai1] = await Promise.all([
ai.responses.create({
model: 'gpt-5-nano',
input: `Generate an insight of the current website analytics data (max 220 characters):\n- Visits: ${JSON.stringify(visits)}\n- Sessions: ${JSON.stringify(sessions)}`
}),
ai.responses.create({
model: 'gpt-5-nano',
input: `Generate an insight of the current website analytics data (max 500 characters):\n- Visits: ${JSON.stringify(visits_timeline)}`
})
])
const page3 = page3_raw
.replace('%VISITS%', visits.toString())
.replace('%SESSIONS%', sessions[0].count)
.replace('%DEVICE_0%', `${devices[0]._id} - ${devices[0].count}`)
.replace('%DEVICE_1%', `${devices[1]._id} - ${devices[1].count}`)
.replace('%DEVICE_2%', `${devices[2]._id} - ${devices[2].count}`)
.replace('VAR_LABELS_TRAFFIC', `[${visits_timeline.data.map(e => `"${e.count}"`).join(',')}]`)
.replace('VAR_DATA_TRAFFIC', `[${visits_timeline.data.map(e => e.count).join(',')}]`)
.replace('VAR_LABELS_PIE', `[${devices.map(e => `"${e._id}"`).join(',')}]`)
.replace('VAR_DATA_PIE', `[${devices.map(e => e.count).join(',')}]`)
.replace('%AI_0%', ai0.output_text)
.replace('%AI_1%', ai1.output_text)
.replace('%LOGO%', options.customLogo)
return page3;
}
export async function getPage4(options: BaseOptions) {
const page4_raw = fs.readFileSync(path.join(options.resourcesPath, 'pages/page4.html'), 'utf-8');
const [referrers, utm_sources, utm_campaign, utm_medium] = await Promise.all([
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{ $group: { _id: "$referrer", count: { $sum: 1 } } },
{ $sort: { count: -1 } },
{ $limit: 5 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain,
utm_source: { $ne: null }
}
},
{
$group: {
_id: '$utm_source',
count: { $sum: 1 }
}
},
{ $sort: { count: -1 } },
{ $limit: 3 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain,
utm_campaign: { $ne: null }
}
},
{
$group: {
_id: '$utm_campaign',
count: { $sum: 1 }
}
},
{ $sort: { count: -1 } },
{ $limit: 3 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain,
utm_medium: { $ne: null }
}
},
{
$group: {
_id: '$utm_medium',
count: { $sum: 1 }
}
},
{ $sort: { count: -1 } },
{ $limit: 3 }
])
]);
const ai = AiService.init();
const [ai0, ai1, ai2, ai3] = await Promise.all([
ai.responses.create({
model: 'gpt-5-nano',
input: `Generate an insight of the current website analytics data (max 220 characters):\n- Referrers: ${JSON.stringify(referrers)}`
}),
ai.responses.create({
model: 'gpt-5-nano',
input: `Generate an insight of the current website analytics data (max 220 characters):\n- utm_sources: ${JSON.stringify(utm_sources)}`
}),
ai.responses.create({
model: 'gpt-5-nano',
input: `Generate an insight of the current website analytics data (max 220 characters):\n- utm_campaign: ${JSON.stringify(utm_campaign)}`
}),
ai.responses.create({
model: 'gpt-5-nano',
input: `Generate an insight of the current website analytics data (max 220 characters):\n- utm_medium: ${JSON.stringify(utm_medium)}`
})
])
const page4 = page4_raw
.replace('%REFERRER_0%', `${referrers[0]._id} - ${referrers[0].count}`)
.replace('%REFERRER_1%', `${referrers[1]._id} - ${referrers[1].count}`)
.replace('%REFERRER_2%', `${referrers[2]._id} - ${referrers[2].count}`)
.replace('%REFERRER_3%', `${referrers[3]._id} - ${referrers[3].count}`)
.replace('%REFERRER_4%', `${referrers[4]._id} - ${referrers[4].count}`)
.replace('%UTMSOURCE_0%', `${utm_sources[0]?._id} -> ${utm_sources[0]?.count}`)
.replace('%UTMSOURCE_1%', `${utm_sources[1]?._id} -> ${utm_sources[1]?.count}`)
.replace('%UTMSOURCE_2%', `${utm_sources[2]?._id} -> ${utm_sources[2]?.count}`)
.replace('%UTMCAMPAIGN_0%', `${utm_campaign[0]?._id} -> ${utm_campaign[0]?.count}`)
.replace('%UTMCAMPAIGN_1%', `${utm_campaign[1]?._id} -> ${utm_campaign[1]?.count}`)
.replace('%UTMCAMPAIGN_2%', `${utm_campaign[2]?._id} -> ${utm_campaign[2]?.count}`)
.replace('%UTMMEDIUM_0%', `${utm_medium[0]?._id} -> ${utm_medium[0]?.count}`)
.replace('%UTMMEDIUM_1%', `${utm_medium[1]?._id} -> ${utm_medium[1]?.count}`)
.replace('%UTMMEDIUM_2%', `${utm_medium[2]?._id} -> ${utm_medium[2]?.count}`)
.replace('%AI_0%', ai0.output_text)
.replace('%AI_1%', ai1.output_text)
.replace('%AI_2%', ai2.output_text)
.replace('%AI_3%', ai3.output_text)
.replace('%LOGO%', options.customLogo)
return page4;
}
export async function getPage5(options: BaseOptions) {
const page5_raw = fs.readFileSync(path.join(options.resourcesPath, 'pages/page5.html'), 'utf-8');
const [referrers, utm_term, utm_content] = await Promise.all([
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{ $group: { _id: "$referrer", count: { $sum: 1 } } },
{ $sort: { count: -1 } },
{ $limit: 5 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain,
utm_term: { $ne: null }
}
},
{
$group: {
_id: '$utm_term',
count: { $sum: 1 }
}
},
{ $sort: { count: -1 } },
{ $limit: 3 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain,
utm_content: { $ne: null }
}
},
{
$group: {
_id: '$utm_content',
count: { $sum: 1 }
}
},
{ $sort: { count: -1 } },
{ $limit: 3 }
]),
]);
const ai = AiService.init();
const [ai0, ai1] = await Promise.all([
ai.responses.create({
model: 'gpt-5-nano',
input: `Generate an insight of the current website analytics data (max 220 characters):\n- utm_term: ${JSON.stringify(utm_term)}`
}),
ai.responses.create({
model: 'gpt-5-nano',
input: `Generate an insight of the current website analytics data (max 220 characters):\n- utm_content: ${JSON.stringify(utm_content)}`
}),
])
let page5 = page5_raw
for (let i = 0; i < 5; i++) {
page5 = page5.replace(`%REFERRER_${i}_NAME%`, `${referrers[i]?._id}`)
.replace(`%REFERRER_${i}_COUNT%`, `${referrers[i]?.count}`)
.replace(`%REFERRER_${i}_PERCENT%`, `${getDistribution(referrers, 'count', i)} %`)
}
page5 = page5.replace(`%UTMTERM_0%`, `${utm_term[0]?._id}${utm_term[0]?.count}`)
page5 = page5.replace(`%UTMTERM_1%`, `${utm_term[1]?._id}${utm_term[1]?.count}`)
page5 = page5.replace(`%UTMTERM_2%`, `${utm_term[2]?._id}${utm_term[2]?.count}`)
page5 = page5.replace(`%UTMCONTENT_0%`, `${utm_content[0]?._id}${utm_content[0]?.count}`)
page5 = page5.replace(`%UTMCONTENT_1%`, `${utm_content[1]?._id}${utm_content[1]?.count}`)
page5 = page5.replace(`%UTMCONTENT_2%`, `${utm_content[2]?._id}${utm_content[2]?.count}`)
page5 = page5.replace(`%AI_0%`, ai0.output_text)
page5 = page5.replace(`%AI_1%`, ai1.output_text)
return page5;
}
export async function getPage6(options: BaseOptions) {
const page6_raw = fs.readFileSync(path.join(options.resourcesPath, 'pages/page6.html'), 'utf-8');
const [pages, entry_pages, exit_pages] = await Promise.all([
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{ $group: { _id: "$page", count: { $sum: 1, } } },
{ $sort: { count: -1 } },
{ $limit: 5 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{ $sort: { session: 1, created_at: 1 } },
{ $group: { _id: "$session", entryPage: { $first: "$page" } } },
{ $group: { _id: "$entryPage", count: { $sum: 1 } } },
{ $sort: { count: -1 } },
{ $limit: 5 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{ $sort: { session: 1, created_at: 1 } },
{ $group: { _id: "$session", exitPage: { $last: "$page" } } },
{ $group: { _id: "$exitPage", count: { $sum: 1 } } },
{ $sort: { count: -1 } },
{ $limit: 5 }
])
])
const page6 = page6_raw
.replace('%PAGE_0%', `${pages[0]?._id}${pages[0]?.count}`)
.replace('%PAGE_1%', `${pages[1]?._id}${pages[1]?.count}`)
.replace('%PAGE_2%', `${pages[2]?._id}${pages[2]?.count}`)
.replace('%PAGE_3%', `${pages[3]?._id}${pages[3]?.count}`)
.replace('%PAGE_4%', `${pages[4]?._id}${pages[4]?.count}`)
.replace('%ENTRY_PAGE_0%', `${entry_pages[0]?._id}${entry_pages[0]?.count}`)
.replace('%ENTRY_PAGE_1%', `${entry_pages[1]?._id}${entry_pages[1]?.count}`)
.replace('%ENTRY_PAGE_2%', `${entry_pages[2]?._id}${entry_pages[2]?.count}`)
.replace('%ENTRY_PAGE_3%', `${entry_pages[3]?._id}${entry_pages[3]?.count}`)
.replace('%ENTRY_PAGE_4%', `${entry_pages[4]?._id}${entry_pages[4]?.count}`)
.replace('%EXIT_PAGE_0%', `${exit_pages[0]?._id}${exit_pages[0]?.count}`)
.replace('%EXIT_PAGE_1%', `${exit_pages[1]?._id}${exit_pages[1]?.count}`)
.replace('%EXIT_PAGE_2%', `${exit_pages[2]?._id}${exit_pages[2]?.count}`)
.replace('%EXIT_PAGE_3%', `${exit_pages[3]?._id}${exit_pages[3]?.count}`)
.replace('%EXIT_PAGE_4%', `${exit_pages[4]?._id}${exit_pages[4]?.count}`)
return page6;
}
export async function getPage7(options: BaseOptions) {
const page7_raw = fs.readFileSync(path.join(options.resourcesPath, 'pages/page7.html'), 'utf-8');
const [pages, sessions] = await Promise.all([
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{
$setWindowFields: {
partitionBy: "$session",
sortBy: { created_at: 1 },
output: {
nextCreatedAt: { $shift: { output: "$created_at", by: 1 } },
nextPage: { $shift: { output: "$page", by: 1 } }
}
}
},
{
$project: {
page: 1,
created_at: 1,
nextCreatedAt: 1,
durationMs: {
$cond: [
{ $ne: ["$nextCreatedAt", null] },
{ $subtract: ["$nextCreatedAt", "$created_at"] },
null
]
}
}
},
{
$match: {
durationMs: { $ne: null, $gt: 0, $lte: 1000 * 60 * 60 * 1 }
}
},
{
$group: {
_id: "$page",
count: { $sum: 1 },
avgMs: { $avg: "$durationMs" }
}
},
{
$project: {
_id: 1,
count: 1,
avgSeconds: { $round: [{ $divide: ["$avgMs", 1000] }, 0] },
}
},
{ $sort: { count: -1 } },
{ $limit: 8 },
]),
durationController.executeDynamic({
project_id: options.target._id.toString(),
from: options.from.getTime(), to: options.to.getTime(),
slice: 'day', domain: options.domain
})
])
let page7 = page7_raw
for (let i = 0; i < 8; i++) {
page7 = page7
.replace(`%ITEM_${i}_A%`, `${pages[i]?._id}`)
.replace(`%ITEM_${i}_B%`, `${pages[i]?.count}`)
.replace(`%ITEM_${i}_C%`, `${formatTime(pages[i]?.avgSeconds * 1000)}`)
.replace(`%ITEM_${i}_D%`, `${getDistribution(pages, 'count', i)} %`)
.replace(`%AVG_PAGE_TIME%`, `${formatTime((pages.reduce((a, e) => a + e.avgSeconds, 0) / pages.length) * 1000)}`)
.replace(`%AVG_SESSION_TIME%`, `${formatTime((sessions.data.reduce((a, e) => a + e.count, 0) / sessions.data.length) * 1000)}`)
.replace('%LOGO%', options.customLogo)
}
return page7;
}
export async function getPage8(options: BaseOptions) {
const page8_raw = fs.readFileSync(path.join(options.resourcesPath, 'pages/page8.html'), 'utf-8');
const [continents, countries] = await Promise.all([
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{ $group: { _id: "$continent", count: { $sum: 1, } } },
{ $sort: { count: -1 } },
{ $limit: 4 }
]),
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{ $group: { _id: "$country", count: { $sum: 1, } } },
{ $sort: { count: -1 } },
{ $limit: 5 }
])
])
let page8 = page8_raw
for (let i = 0; i < 4; i++) {
page8 = page8
.replace(`%CONTINENT_${i}_A%`, `${continents[i]?._id}`)
.replace(`%CONTINENT_${i}_B%`, `${continents[i]?.count}`)
.replace(`%CONTINENT_${i}_C%`, `${getDistribution(continents, 'count', i)}%`)
}
for (let i = 0; i < 5; i++) {
page8 = page8
.replace(`%COUNTRY_${i}_A%`, `${countries[i]?._id}`)
.replace(`%COUNTRY_${i}_B%`, `${countries[i]?.count}`)
.replace(`%COUNTRY_${i}_C%`, `${getDistribution(countries, 'count', i)}%`)
}
page8 = page8.replace('%LOGO%', options.customLogo)
return page8;
}
export async function getPage9(options: BaseOptions) {
const page9_raw = fs.readFileSync(path.join(options.resourcesPath, 'pages/page9.html'), 'utf-8');
const [cities] = await Promise.all([
VisitModel.aggregate([
{
$match: {
project_id: options.target._id,
created_at: { $gte: options.from, $lte: options.to },
website: options.domain
}
},
{ $group: { _id: "$city", count: { $sum: 1, } } },
{ $sort: { count: -1 } },
{ $limit: 5 }
]),
])
let page9 = page9_raw
for (let i = 0; i < 5; i++) {
page9 = page9
.replace(`%CITY_${i}_A%`, `${cities[i]?._id ?? 'Unknown'}`)
.replace(`%CITY_${i}_B%`, `${cities[i]?.count}`)
.replace(`%CITY_${i}_C%`, `${getDistribution(cities, 'count', i)}%`)
}
page9 = page9.replace('%LOGO%', options.customLogo)
return page9;
}
function getResourcePath() {
if (isSelfhosted()) return '/home/app/public/pdf/';
return process.dev ? './public/pdf/' : './.output/public/pdf/';
}
export default defineEventHandler(async event => {
const ctx = await getRequestContext(event, 'pid', 'range');
const project = await ProjectModel.findById(ctx.project_id);
if (!project) return setResponseStatus(event, 400, 'Project not found');
const { customLogo } = await readBody(event);
const target = project;
const from = new Date(ctx.from);
const to = new Date(ctx.to);
const query = getQuery(event);
const domain = query.domain as string;
const resourcesPath = getResourcePath();
const [page1, page2, page3, page4, page5, page6, page7, page8, page9] = await Promise.all([
getPage1({ resourcesPath, domain, from, to, target, customLogo: customLogo ?? 'https://dashboard.litlyx.com/pdf/pdf_images/logo-black.png' }),
getPage2({ resourcesPath, domain, from, to, target, customLogo: customLogo ?? 'https://dashboard.litlyx.com/pdf/pdf_images/logo-black.png' }),
getPage3({ resourcesPath, domain, from, to, target, customLogo: customLogo ?? 'https://dashboard.litlyx.com/pdf/pdf_images/logo-black.png' }),
getPage4({ resourcesPath, domain, from, to, target, customLogo: customLogo ?? 'https://dashboard.litlyx.com/pdf/pdf_images/logo-black.png' }),
getPage5({ resourcesPath, domain, from, to, target, customLogo: customLogo ?? 'https://dashboard.litlyx.com/pdf/pdf_images/logo-black.png' }),
getPage6({ resourcesPath, domain, from, to, target, customLogo: customLogo ?? 'https://dashboard.litlyx.com/pdf/pdf_images/logo-black.png' }),
getPage7({ resourcesPath, domain, from, to, target, customLogo: customLogo ?? 'https://dashboard.litlyx.com/pdf/pdf_images/logo-black.png' }),
getPage8({ resourcesPath, domain, from, to, target, customLogo: customLogo ?? 'https://dashboard.litlyx.com/pdf/pdf_images/logo-black.png' }),
getPage9({ resourcesPath, domain, from, to, target, customLogo: customLogo ?? 'https://dashboard.litlyx.com/pdf/pdf_images/logo-black.png' }),
])
const CUSTOM_CHROMIUM = fs.existsSync('/usr/bin/chromium-browser');
const browser = await puppeteer.launch({
executablePath: CUSTOM_CHROMIUM ? '/usr/bin/chromium-browser' : undefined,
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-features=site-per-process',
'--aggressive-cache-discard',
'--disable-cache',
'--disable-application-cache',
'--disable-offline-load-stale-cache',
'--disable-gpu-shader-disk-cache',
'--media-cache-size=0',
'--disk-cache-size=0',
],
});
const page = await browser.newPage();
await page.setContent(page1, { waitUntil: "load" });
const page1buffer = await page.pdf({ format: "A4", printBackground: true });
await page.setContent(page2, { waitUntil: "load" });
const page2buffer = await page.pdf({ format: "A4", printBackground: true });
await page.setContent(page3, { waitUntil: "load" });
await new Promise(e => setTimeout(e, 1000));
const page3buffer = await page.pdf({ format: "A4", printBackground: true });
await page.setContent(page4, { waitUntil: "load" });
const page4buffer = await page.pdf({ format: "A4", printBackground: true });
await page.setContent(page5, { waitUntil: "load" });
const page5buffer = await page.pdf({ format: "A4", printBackground: true });
await page.setContent(page6, { waitUntil: "load" });
const page6buffer = await page.pdf({ format: "A4", printBackground: true });
await page.setContent(page7, { waitUntil: "load" });
const page7buffer = await page.pdf({ format: "A4", printBackground: true });
await page.setContent(page8, { waitUntil: "load" });
const page8buffer = await page.pdf({ format: "A4", printBackground: true });
await page.setContent(page9, { waitUntil: "load" });
const page9buffer = await page.pdf({ format: "A4", printBackground: true });
await browser.close();
const mergedPdf = await PDFDocument.create();
const pdf1 = await PDFDocument.load(page1buffer);
const copiedPages1 = await mergedPdf.copyPages(pdf1, pdf1.getPageIndices());
copiedPages1.forEach((p) => mergedPdf.addPage(p));
const pdf2 = await PDFDocument.load(page2buffer);
const copiedPages2 = await mergedPdf.copyPages(pdf2, pdf2.getPageIndices());
copiedPages2.forEach((p) => mergedPdf.addPage(p));
const pdf3 = await PDFDocument.load(page3buffer);
const copiedPages3 = await mergedPdf.copyPages(pdf3, pdf3.getPageIndices());
copiedPages3.forEach((p) => mergedPdf.addPage(p));
const pdf4 = await PDFDocument.load(page4buffer);
const copiedPages4 = await mergedPdf.copyPages(pdf4, pdf4.getPageIndices());
copiedPages4.forEach((p) => mergedPdf.addPage(p));
const pdf5 = await PDFDocument.load(page5buffer);
const copiedPages5 = await mergedPdf.copyPages(pdf5, pdf5.getPageIndices());
copiedPages5.forEach((p) => mergedPdf.addPage(p));
const pdf6 = await PDFDocument.load(page6buffer);
const copiedPages6 = await mergedPdf.copyPages(pdf6, pdf6.getPageIndices());
copiedPages6.forEach((p) => mergedPdf.addPage(p));
const pdf7 = await PDFDocument.load(page7buffer);
const copiedPages7 = await mergedPdf.copyPages(pdf7, pdf7.getPageIndices());
copiedPages7.forEach((p) => mergedPdf.addPage(p));
const pdf8 = await PDFDocument.load(page8buffer);
const copiedPages8 = await mergedPdf.copyPages(pdf8, pdf8.getPageIndices());
copiedPages8.forEach((p) => mergedPdf.addPage(p));
const pdf9 = await PDFDocument.load(page9buffer);
const copiedPages9 = await mergedPdf.copyPages(pdf9, pdf9.getPageIndices());
copiedPages9.forEach((p) => mergedPdf.addPage(p));
const finalPdfBytes = await mergedPdf.save();
setResponseHeaders(event, {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="AdvancedReport.pdf"',
});
return finalPdfBytes;
});