|
|
@@ -5,6 +5,7 @@ import { pipeline as pipelinePromise } from 'node:stream/promises';
|
|
|
import { OnInit } from '@tsed/common';
|
|
|
import { Service } from '@tsed/di';
|
|
|
import { Logger } from '@tsed/logger';
|
|
|
+import type { PuppeteerNodeLaunchOptions } from 'puppeteer';
|
|
|
import { Cluster } from 'puppeteer-cluster';
|
|
|
|
|
|
interface PageInfo {
|
|
|
@@ -37,8 +38,6 @@ interface JobInfo {
|
|
|
class PdfConvertService implements OnInit {
|
|
|
private puppeteerCluster: Cluster | undefined;
|
|
|
|
|
|
- private maxConcurrency = 1;
|
|
|
-
|
|
|
private convertRetryLimit = 5;
|
|
|
|
|
|
private tmpOutputRootDir = '/tmp/page-bulk-export';
|
|
|
@@ -292,15 +291,8 @@ class PdfConvertService implements OnInit {
|
|
|
private async initPuppeteerCluster(): Promise<void> {
|
|
|
if (process.env.SKIP_PUPPETEER_INIT === 'true') return;
|
|
|
|
|
|
- this.puppeteerCluster = await Cluster.launch({
|
|
|
- concurrency: Cluster.CONCURRENCY_PAGE,
|
|
|
- maxConcurrency: this.maxConcurrency,
|
|
|
- workerCreationDelay: 10000,
|
|
|
- puppeteerOptions: {
|
|
|
- // ref) https://github.com/growilabs/growi/pull/10192
|
|
|
- args: ['--no-sandbox'],
|
|
|
- },
|
|
|
- });
|
|
|
+ const config = this.getPuppeteerClusterConfig();
|
|
|
+ this.puppeteerCluster = await Cluster.launch(config);
|
|
|
|
|
|
await this.puppeteerCluster.task(async ({ page, data: htmlString }) => {
|
|
|
await page.setContent(htmlString, { waitUntil: 'domcontentloaded' });
|
|
|
@@ -326,6 +318,51 @@ class PdfConvertService implements OnInit {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Get puppeteer cluster configuration from environment variable
|
|
|
+ * @returns merged cluster configuration
|
|
|
+ */
|
|
|
+ private getPuppeteerClusterConfig(): Record<string, any> {
|
|
|
+ // Default cluster configuration
|
|
|
+ const defaultConfig = {
|
|
|
+ concurrency: Cluster.CONCURRENCY_CONTEXT,
|
|
|
+ maxConcurrency: 1,
|
|
|
+ workerCreationDelay: 10000,
|
|
|
+ // Puppeteer options (not configurable for security reasons)
|
|
|
+ // ref) https://github.com/growilabs/growi/pull/10192
|
|
|
+ puppeteerOptions: {
|
|
|
+ args: ['--no-sandbox'],
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ // Parse configuration from environment variable
|
|
|
+ let customConfig: Record<string, any> = {};
|
|
|
+ if (process.env.PUPPETEER_CLUSTER_CONFIG) {
|
|
|
+ try {
|
|
|
+ customConfig = JSON.parse(process.env.PUPPETEER_CLUSTER_CONFIG);
|
|
|
+ } catch (err) {
|
|
|
+ this.logger.warn(
|
|
|
+ 'Failed to parse PUPPETEER_CLUSTER_CONFIG, using default values',
|
|
|
+ err,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Remove puppeteerOptions from custom config if present (not allowed for security)
|
|
|
+ if (customConfig.puppeteerOptions) {
|
|
|
+ this.logger.warn(
|
|
|
+ 'puppeteerOptions configuration is not allowed for security reasons and will be ignored',
|
|
|
+ );
|
|
|
+ delete customConfig.puppeteerOptions;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Merge configurations (customConfig overrides defaultConfig, except puppeteerOptions)
|
|
|
+ return {
|
|
|
+ ...defaultConfig,
|
|
|
+ ...customConfig,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Get parent path from given path
|
|
|
* @param path target path
|