Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Puppeteer to gotenberg #1059

Merged
merged 3 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,13 @@ services:
networks:
- tms_dev_db

gotenberg:
container_name: gotenberg
image: gotenberg/gotenberg:8
ports:
- '2000:3000'
networks:
- tms_dev_db

networks:
tms_dev_db:
8 changes: 8 additions & 0 deletions docs/docs/assets/dev/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,13 @@ services:
networks:
- tms_dev_db

gotenberg:
container_name: gotenberg
image: gotenberg/gotenberg:8
ports:
- '2000:3000'
networks:
- tms_dev_db

networks:
tms_dev_db:
11 changes: 8 additions & 3 deletions docs/docs/assets/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ services:
image: ghcr.io/dudrie/tutor-management-system:<version>
container_name: tms-server
restart: on-failure:1
cap_add:
# This one is needed so puppeteer properly works inside the container (see: https://developers.google.com/web/tools/puppeteer/troubleshooting#tips)
- SYS_ADMIN
depends_on:
- mysql
networks:
Expand All @@ -43,6 +40,14 @@ services:
# Replace with the path to the folder containing the configuration and template files of the TMS.
- <path-to-CONFIG>:/tms/server/config

gotenberg:
container_name: gotenberg
image: gotenberg/gotenberg:8
ports:
- '2000:3000'
networks:
- tms_db

volumes:
db_data:

Expand Down
5,939 changes: 2,926 additions & 3,013 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions scripts/build-test-docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ services:
volumes:
- ./config:/tms/server/config

gotenberg:
container_name: gotenberg
image: gotenberg/gotenberg:8
ports:
- '2000:3000'
networks:
- tms_db

volumes:
db_data:

Expand Down
24 changes: 13 additions & 11 deletions server/config/development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ handbookUrl: 'https://dudrie.github.io/Tutor-Management-System/'

# Configuration for the database access. More information can be found in the documentation.
database:
host: localhost
port: 3306
databaseName: tms-big-db
maxRetries: 2
reconnectTimeout: 10000
host: localhost
port: 3306
databaseName: tms-big-db
maxRetries: 2
reconnectTimeout: 10000

defaultSettings:
canTutorExcuseStudents: true
defaultTeamSize: 3
canTutorExcuseStudents: true
defaultTeamSize: 3

# Settings used for the puppeteer instance
puppeteer:
# Timeout in ms
timeout: 30000
# Settings used for the gotenberg instance
gotenberg:
host: localhost
port: 2000
# Timeout in ms
timeout: 30000
24 changes: 13 additions & 11 deletions server/config/production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ handbookUrl: 'https://dudrie.github.io/Tutor-Management-System/'

# Configuration for the database access. More information can be found in the documentation.
database:
host: tms_sql
port: 3306
databaseName: tms-db
maxRetries: 2
reconnectTimeout: 10000
host: tms_sql
port: 3306
databaseName: tms-db
maxRetries: 2
reconnectTimeout: 10000

defaultSettings:
canTutorExcuseStudents: true
defaultTeamSize: 3
canTutorExcuseStudents: true
defaultTeamSize: 3

# Settings used for the puppeteer instance
puppeteer:
# Timeout in ms
timeout: 30000
# Settings used for the gotenberg instance
gotenberg:
host: gotenberg
port: 3000
# Timeout in ms
timeout: 30000
4 changes: 2 additions & 2 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@
"@nestjs/passport": "^9.0.0",
"@nestjs/platform-express": "^9.0.11",
"@nestjs/swagger": "^6.1.0",
"axios": "^1.7.7",
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.0",
"class-validator": "^0.13.2",
"excel4node": "^1.8.0",
"express-session": "^1.17.3",
"form-data": "^4.0.1",
"highlight.js": "^10.7.2",
"jszip": "^3.10.1",
"luxon": "^1.25.0",
Expand All @@ -48,7 +50,6 @@
"passport": "^0.6.0",
"passport-local": "^1.0.0",
"pug": "^3.0.2",
"puppeteer": "5.5.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.5.6",
Expand All @@ -73,7 +74,6 @@
"@types/passport": "^1.0.10",
"@types/passport-local": "^1.0.34",
"@types/pug": "^2.0.6",
"@types/puppeteer": "5.4.2",
"@types/supertest": "^2.0.12",
"@types/uuid": "^8.3.4",
"jest": "^29.0.0",
Expand Down
70 changes: 25 additions & 45 deletions server/src/module/pdf/subservices/PDFGenerator.core.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// noinspection HtmlRequiredLangAttribute,HtmlRequiredTitleElement

import { Logger } from '@nestjs/common';
import axios from 'axios';
import FormData from 'form-data';
import fs from 'fs';
import puppeteer from 'puppeteer';
import { StaticSettings } from '../../settings/settings.static';
import { StaticSettings } from 'module/settings/settings.static';

/**
* @param T Type of the options passed to `generatePDF`.
Expand All @@ -29,52 +30,31 @@ export abstract class PDFGenerator<T = Record<string, unknown>> {
*/
protected async generatePDFFromBodyContent(body: string): Promise<Buffer> {
const html = await this.putBodyInHTML(body);
let browser: puppeteer.Browser | undefined;

this.logger.debug('Starting browser...');
this.logger.debug(`\tExec path: ${process.env.TMS_PUPPETEER_EXEC_PATH}`);
const gotenbergConfig = StaticSettings.getService().getGotenbergConfiguration();

try {
browser = await puppeteer.launch({
args: ['--disable-dev-shm-usage'],
executablePath: process.env.TMS_PUPPETEER_EXEC_PATH,
timeout: StaticSettings.getService().getPuppeteerConfiguration()?.timeout,
});

this.logger.debug('Browser started.');

const page = await browser.newPage();
this.logger.debug('Page created.');

await page.setContent(html, { waitUntil: 'domcontentloaded' });
this.logger.debug('Page content loaded');

const buffer = await page.pdf({
format: 'A4',
margin: {
top: '1cm',
right: '1cm',
bottom: '1cm',
left: '1cm',
},
// Fixes CSS 'background' not being respected in printed PDFs.
printBackground: true,
});

this.logger.debug('PDF created.');

await browser.close();

this.logger.debug('Browser closed');

return buffer;
const form = new FormData();
form.append('files', html, { filename: 'index.html', contentType: 'text/html' });

this.logger.debug('Sending request to Gotenberg for PDF generation...');

const response = await axios.post(
`http://${gotenbergConfig?.host}:${gotenbergConfig?.port}/forms/chromium/convert/html`,
form,
{
headers: {
...form.getHeaders(),
},
timeout: gotenbergConfig?.timeout,
responseType: 'arraybuffer',
}
);

this.logger.debug('PDF generated successfully from Gotenberg.');

return Buffer.from(response.data);
} catch (err) {
if (browser) {
await browser.close();
}

Logger.error(JSON.stringify(err, null, 2));

this.logger.error('Failed to generate PDF:', err);
throw err;
}
}
Expand Down
6 changes: 3 additions & 3 deletions server/src/module/settings/model/ApplicationConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Type } from 'class-transformer';
import { IsNumber, IsOptional, IsString, IsUrl, Min, ValidateNested } from 'class-validator';
import { ClientSettingsDTO } from '../settings.dto';
import { DatabaseConfiguration } from './DatabaseConfiguration';
import { PuppeteerConfiguration } from './PuppeteerConfiguration';
import { GotenbergConfiguration } from './GotenbergConfiguration';

export class ApplicationConfiguration {
@IsOptional()
Expand All @@ -28,7 +28,7 @@ export class ApplicationConfiguration {
readonly defaultSettings: ClientSettingsDTO | undefined;

@IsOptional()
@Type(() => PuppeteerConfiguration)
@Type(() => GotenbergConfiguration)
@ValidateNested()
readonly puppeteer: PuppeteerConfiguration | undefined;
readonly gotenberg: GotenbergConfiguration | undefined;
}
14 changes: 14 additions & 0 deletions server/src/module/settings/model/GotenbergConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IsNumber, IsString, Min } from 'class-validator';

export class GotenbergConfiguration {
@IsString()
readonly host!: string;

@IsNumber({}, { always: true })
@Min(0)
readonly timeout!: number;

@IsNumber({}, { always: true })
@Min(0)
readonly port!: number;
}
7 changes: 0 additions & 7 deletions server/src/module/settings/model/PuppeteerConfiguration.ts

This file was deleted.

8 changes: 4 additions & 4 deletions server/src/module/settings/settings.static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
DatabaseConnectionOptions,
} from './model/DatabaseConfiguration';
import { ENV_VARIABLE_NAMES, EnvironmentConfig } from './model/EnvironmentConfig';
import { PuppeteerConfiguration } from './model/PuppeteerConfiguration';
import { GotenbergConfiguration } from './model/GotenbergConfiguration';

export class StaticSettings {
private static service: StaticSettings = new StaticSettings();
Expand Down Expand Up @@ -74,10 +74,10 @@ export class StaticSettings {
}

/**
* @returns Configuration for the puppeteer instance. Can be `undefined`.
* @returns Configuration for the gotenberg instance. Can be `undefined`.
*/
getPuppeteerConfiguration(): PuppeteerConfiguration | undefined {
return this.config.puppeteer;
getGotenbergConfiguration(): GotenbergConfiguration | undefined {
return this.config.gotenberg;
}

/**
Expand Down
Loading