release: v4.1.0

This commit is contained in:
Amruth Pillai
2024-05-05 14:55:06 +02:00
parent 68252c35fc
commit e87b05a93a
282 changed files with 11461 additions and 10713 deletions

View File

@ -1,30 +1,26 @@
import { HttpService } from "@nestjs/axios";
import { InternalServerErrorException, Logger } from "@nestjs/common";
import { Injectable } from "@nestjs/common";
import { Injectable, InternalServerErrorException, Logger } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import fontkit from "@pdf-lib/fontkit";
import { ResumeDto } from "@reactive-resume/dto";
import { getFontUrls } from "@reactive-resume/utils";
import { ErrorMessage } from "@reactive-resume/utils";
import { ErrorMessage, getFontUrls } from "@reactive-resume/utils";
import retry from "async-retry";
import { PDFDocument } from "pdf-lib";
import { connect } from "puppeteer";
import { Config } from "../config/schema";
import { StorageService } from "../storage/storage.service";
import { UtilsService } from "../utils/utils.service";
@Injectable()
export class PrinterService {
private readonly logger = new Logger(PrinterService.name);
private browserURL: string;
private readonly browserURL: string;
constructor(
private readonly configService: ConfigService<Config>,
private readonly storageService: StorageService,
private readonly httpService: HttpService,
private readonly utils: UtilsService,
) {
const chromeUrl = this.configService.getOrThrow<string>("CHROME_URL");
const chromeToken = this.configService.getOrThrow<string>("CHROME_TOKEN");
@ -36,21 +32,24 @@ export class PrinterService {
try {
return await connect({ browserWSEndpoint: this.browserURL });
} catch (error) {
throw new InternalServerErrorException(ErrorMessage.InvalidBrowserConnection, error.message);
throw new InternalServerErrorException(
ErrorMessage.InvalidBrowserConnection,
(error as Error).message,
);
}
}
async getVersion() {
const browser = await this.getBrowser();
const version = await browser.version();
browser.disconnect();
await browser.disconnect();
return version;
}
async printResume(resume: ResumeDto) {
const start = performance.now();
const url = await retry(() => this.generateResume(resume), {
const url = await retry<string | undefined>(() => this.generateResume(resume), {
retries: 3,
randomize: true,
onRetry: (_, attempt) => {
@ -59,9 +58,9 @@ export class PrinterService {
});
const duration = Number(performance.now() - start).toFixed(0);
const numPages = resume.data.metadata.layout.length;
const numberPages = resume.data.metadata.layout.length;
this.logger.debug(`Chrome took ${duration}ms to print ${numPages} page(s)`);
this.logger.debug(`Chrome took ${duration}ms to print ${numberPages} page(s)`);
return url;
}
@ -91,10 +90,11 @@ export class PrinterService {
const browser = await this.getBrowser();
const page = await browser.newPage();
let url = this.utils.getUrl();
const publicUrl = this.configService.getOrThrow<string>("PUBLIC_URL");
const storageUrl = this.configService.getOrThrow<string>("STORAGE_URL");
let url = publicUrl;
if ([publicUrl, storageUrl].some((url) => url.includes("localhost"))) {
// Switch client URL from `localhost` to `host.docker.internal` in development
// This is required because the browser is running in a container and the client is running on the host machine.
@ -107,15 +107,15 @@ export class PrinterService {
if (request.url().startsWith(storageUrl)) {
const modifiedUrl = request.url().replace("localhost", `host.docker.internal`);
request.continue({ url: modifiedUrl });
void request.continue({ url: modifiedUrl });
} else {
request.continue();
void request.continue();
}
});
}
// Set the data of the resume to be printed in the browser's session storage
const numPages = resume.data.metadata.layout.length;
const numberPages = resume.data.metadata.layout.length;
await page.evaluateOnNewDocument((data) => {
window.localStorage.setItem("resume", JSON.stringify(data));
@ -127,25 +127,27 @@ export class PrinterService {
const processPage = async (index: number) => {
const pageElement = await page.$(`[data-page="${index}"]`);
// eslint-disable-next-line unicorn/no-await-expression-member
const width = (await (await pageElement?.getProperty("scrollWidth"))?.jsonValue()) ?? 0;
// eslint-disable-next-line unicorn/no-await-expression-member
const height = (await (await pageElement?.getProperty("scrollHeight"))?.jsonValue()) ?? 0;
const tempHtml = await page.evaluate((element: HTMLDivElement) => {
const temporaryHtml = await page.evaluate((element: HTMLDivElement) => {
const clonedElement = element.cloneNode(true) as HTMLDivElement;
const tempHtml = document.body.innerHTML;
document.body.innerHTML = `${clonedElement.outerHTML}`;
return tempHtml;
const temporaryHtml_ = document.body.innerHTML;
document.body.innerHTML = clonedElement.outerHTML;
return temporaryHtml_;
}, pageElement);
pagesBuffer.push(await page.pdf({ width, height, printBackground: true }));
await page.evaluate((tempHtml: string) => {
document.body.innerHTML = tempHtml;
}, tempHtml);
await page.evaluate((temporaryHtml_: string) => {
document.body.innerHTML = temporaryHtml_;
}, temporaryHtml);
};
// Loop through all the pages and print them, by first displaying them, printing the PDF and then hiding them back
for (let index = 1; index <= numPages; index++) {
for (let index = 1; index <= numberPages; index++) {
await processPage(index);
}
@ -170,8 +172,8 @@ export class PrinterService {
// Embed all the fonts in the PDF
await Promise.all(fontsBuffer.map((buffer) => pdf.embedFont(buffer)));
for (let index = 0; index < pagesBuffer.length; index++) {
const page = await PDFDocument.load(pagesBuffer[index]);
for (const element of pagesBuffer) {
const page = await PDFDocument.load(element);
const [copiedPage] = await pdf.copyPages(page, [0]);
pdf.addPage(copiedPage);
}
@ -190,7 +192,7 @@ export class PrinterService {
// Close all the pages and disconnect from the browser
await page.close();
browser.disconnect();
await browser.disconnect();
return resumeUrl;
} catch (error) {
@ -202,10 +204,11 @@ export class PrinterService {
const browser = await this.getBrowser();
const page = await browser.newPage();
let url = this.utils.getUrl();
const publicUrl = this.configService.getOrThrow<string>("PUBLIC_URL");
const storageUrl = this.configService.getOrThrow<string>("STORAGE_URL");
let url = publicUrl;
if ([publicUrl, storageUrl].some((url) => url.includes("localhost"))) {
// Switch client URL from `localhost` to `host.docker.internal` in development
// This is required because the browser is running in a container and the client is running on the host machine.
@ -218,9 +221,9 @@ export class PrinterService {
if (request.url().startsWith(storageUrl)) {
const modifiedUrl = request.url().replace("localhost", `host.docker.internal`);
request.continue({ url: modifiedUrl });
void request.continue({ url: modifiedUrl });
} else {
request.continue();
void request.continue();
}
});
}
@ -248,7 +251,7 @@ export class PrinterService {
// Close all the pages and disconnect from the browser
await page.close();
browser.disconnect();
await browser.disconnect();
return previewUrl;
}