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

add pdf-converter app #9279

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
1 change: 1 addition & 0 deletions apps/pdf-converter/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
2 changes: 2 additions & 0 deletions apps/pdf-converter/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/dist/**
/src/client-library/**
7 changes: 7 additions & 0 deletions apps/pdf-converter/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
extends: '../../.eslintrc.js',
rules: {
'no-useless-constructor': 'off',
'@typescript-eslint/consistent-type-imports': 'off',
Copy link
Contributor Author

@arafubeatbox arafubeatbox Oct 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ts.ed で DI している場所で @typescript-eslint/consistent-type-imports がオンになっていると、DI 対象の import が type のみの import に強要されますが、その結果 DI された対象が何であっても実体のない [Function: anonymous] となってしまいます。
例えば DI された hoge に対して hogeMethod を実行すると TypeError: hoge.hogeMethod is not a function
となってしまいます。

多分 ts.ed の DI では、DI 対象の実装本体を import する必要があるっぽいです。

},
};
2 changes: 2 additions & 0 deletions apps/pdf-converter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/dist
/specs
83 changes: 83 additions & 0 deletions apps/pdf-converter/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
########################################################################
# deps-resolver
########################################################################
FROM node:20-slim AS deps-resolver

WORKDIR /app

COPY package.json yarn.lock ./

RUN yarn --frozen-lockfile

RUN tar -cf node_modules.tar \
node_modules \
&& rm -rf node_modules


########################################################################
# deps-resolver-prod
########################################################################
FROM deps-resolver AS deps-resolver-prod

WORKDIR /app

RUN yarn --production

RUN tar -cf node_modules.tar \
node_modules \
&& rm -rf node_modules


########################################################################
# builder
########################################################################
FROM node:20-slim AS builder

WORKDIR /app

COPY package.json yarn.lock tsconfig.json tsconfig.build.json .eslintrc.js ./
COPY src ./src
COPY --from=deps-resolver /app/node_modules.tar ./

RUN tar -xf node_modules.tar \
&& rm node_modules.tar \
&& yarn build \
&& tar -cf packages.tar \
package.json \
yarn.lock \
dist \
&& rm -rf node_modules


########################################################################
# production
########################################################################
FROM node:20-slim

ENV NODE_ENV=production
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
ENV LANG=ja_JP.UTF-8

RUN apt-get update && apt-get install -y tini chromium locales fonts-ipafont fonts-ipaexfont fonts-ipafont-gothic fonts-ipafont-mincho && apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& echo "ja_JP UTF-8" > /etc/locale.gen && locale-gen

USER node
WORKDIR /app

COPY --from=deps-resolver-prod --chown=node:node \
/app/node_modules.tar ./
COPY --from=builder --chown=node:node \
/app/packages.tar ./

RUN tar -xf node_modules.tar \
&& tar -xf packages.tar \
&& rm node_modules.tar packages.tar

# change permission for shared volume
RUN mkdir -p /tmp/page-bulk-export && chmod -R 777 /tmp/page-bulk-export

EXPOSE 3010

ENTRYPOINT ["/usr/bin/tini", "-e", "143", "--"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tini は不要
https://tips.weseek.co.jp/6151b1832b337100519281e7

terminus と連携して解決するのが望ましい
https://tsed.io/tutorials/terminus.html

後続ストーリーで対応をお願いしたい

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ストーリー作成しました。 https://redmine.weseek.co.jp/issues/156179

CMD ["node", "dist/index.js"]
6 changes: 6 additions & 0 deletions apps/pdf-converter/orval.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
'client-library': {
input: './specs/v3/docs/swagger.yaml',
output: './src/client-library/index.ts',
},
};
37 changes: 37 additions & 0 deletions apps/pdf-converter/package.json
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この app は type: module にできないだろうか?
Ts.ED が未対応というケースもあるだろうが調査をしてほしい(後続ストーリーでOK)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://redmine.weseek.co.jp/issues/156177 ストーリー作成しました

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@growi/pdf-converter",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "MIT",
"private": true,
"scripts": {
"dev:pdf-converter": "nodemon --watch \"src/**/*.ts\" --ignore \"node_modules/**/*\" --exec ts-node -r \"dotenv-flow/config\" src/index.ts",
"lint": "yarn eslint **/*.{js,ts}",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsconfig を細かくチェックはしていないのだけれど、build での tsc は typecheck はせずにスピードを出すのが望ましい。
代わりに lint で typecheck のための tsc を走らせる。
後続ストーリーでよい。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://redmine.weseek.co.jp/issues/156178 ストーリー作成しました。

"gen:client-code": "tsed run generate-swagger --output ./specs && orval",
"build": "yarn gen:client-code && tsc -p tsconfig.build.json"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build 時に gen:client-code でクライアントコードも自動生成しています。

},
"dependencies": {
"@tsed/cli": "^5.4.3",
"@tsed/cli-generate-swagger": "^5.4.3",
"@tsed/common": "7.83.4",
"@tsed/components-scan": "7.83.4",
"@tsed/core": "7.83.4",
"@tsed/di": "7.83.4",
"@tsed/exceptions": "7.83.4",
"@tsed/json-mapper": "7.83.4",
"@tsed/platform-express": "7.83.4",
"@tsed/schema": "7.83.4",
"@tsed/swagger": "7.83.4",
"express": "^4.19.2",
"puppeteer": "^23.1.1",
"puppeteer-cluster": "^0.24.0"
},
"devDependencies": {
"@types/connect": "^3.4.38",
"@types/express": "^4.17.21",
"@types/multer": "^1.4.12",
"@types/node": "^22.5.4",
"orval": "^7.1.1"
}
}
9 changes: 9 additions & 0 deletions apps/pdf-converter/src/bin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CliCore } from '@tsed/cli-core';
import { GenerateSwaggerCmd } from '@tsed/cli-generate-swagger';

import Server from '../server';

CliCore.bootstrap({
server: Server,
commands: [GenerateSwaggerCmd],
});
96 changes: 96 additions & 0 deletions apps/pdf-converter/src/client-library/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Generated by orval v7.1.1 🍺
* Do not edit manually.
* Api documentation
* OpenAPI spec version: 1.0.0
*/
import axios from 'axios'
import type {
AxiosRequestConfig,
AxiosResponse
} from 'axios'
export type PdfCtrlSyncJobStatus202Status = typeof PdfCtrlSyncJobStatus202Status[keyof typeof PdfCtrlSyncJobStatus202Status];


// eslint-disable-next-line @typescript-eslint/no-redeclare
export const PdfCtrlSyncJobStatus202Status = {
HTML_EXPORT_IN_PROGRESS: 'HTML_EXPORT_IN_PROGRESS',
HTML_EXPORT_DONE: 'HTML_EXPORT_DONE',
FAILED: 'FAILED',
PDF_EXPORT_DONE: 'PDF_EXPORT_DONE',
} as const;

export type PdfCtrlSyncJobStatus202 = {
status: PdfCtrlSyncJobStatus202Status;
};

export type PdfCtrlSyncJobStatusBodyStatus = typeof PdfCtrlSyncJobStatusBodyStatus[keyof typeof PdfCtrlSyncJobStatusBodyStatus];


// eslint-disable-next-line @typescript-eslint/no-redeclare
export const PdfCtrlSyncJobStatusBodyStatus = {
HTML_EXPORT_IN_PROGRESS: 'HTML_EXPORT_IN_PROGRESS',
HTML_EXPORT_DONE: 'HTML_EXPORT_DONE',
FAILED: 'FAILED',
} as const;

export type PdfCtrlSyncJobStatusBody = {
expirationDate?: string;
jobId?: string;
status?: PdfCtrlSyncJobStatusBodyStatus;
};

export interface GenericError {
/**
* An error message
* @minLength 1
*/
message: string;
/**
* The error name
* @minLength 1
*/
name: string;
[key: string]: unknown;
}

export interface InternalServerError {
/** A list of related errors */
errors?: GenericError[];
/**
* An error message
* @minLength 1
*/
message: string;
/**
* The error name
* @minLength 1
*/
name: string;
/** The stack trace (only in development mode) */
stack?: string;
/** The status code of the exception */
status: number;
}





/**
*
Sync job pdf convert status with GROWI.
Register or update job inside pdf-converter with given jobId, expirationDate, and status.
Return resulting status of job to GROWI.

*/
export const pdfCtrlSyncJobStatus = <TData = AxiosResponse<PdfCtrlSyncJobStatus202>>(
pdfCtrlSyncJobStatusBody?: PdfCtrlSyncJobStatusBody, options?: AxiosRequestConfig
): Promise<TData> => {
return axios.post(
`/pdf/sync-job`,
pdfCtrlSyncJobStatusBody,options
);
}

export type PdfCtrlSyncJobStatusResult = AxiosResponse<PdfCtrlSyncJobStatus202>
1 change: 1 addition & 0 deletions apps/pdf-converter/src/controllers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './pdf';
51 changes: 51 additions & 0 deletions apps/pdf-converter/src/controllers/pdf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { BodyParams, Logger } from '@tsed/common';
import { Controller, Inject } from '@tsed/di';
import { InternalServerError } from '@tsed/exceptions';
import {
Post, Returns, Enum, Description,
} from '@tsed/schema';

import PdfConvertService, { JobStatusSharedWithGrowi, JobStatus } from '../service/pdf-convert';

@Controller('/pdf')
class PdfCtrl {

@Inject()
logger: Logger;

constructor(private readonly pdfConvertService: PdfConvertService) {}

@Post('/sync-job')
@(Returns(202).ContentType('application/json').Schema({
type: 'object',
properties: {
status: { type: 'string', enum: Object.values(JobStatus) },
},
required: ['status'],
}))
@Returns(500)
@Description(`
Sync job pdf convert status with GROWI.
Register or update job inside pdf-converter with given jobId, expirationDate, and status.
Return resulting status of job to GROWI.
`)
async syncJobStatus(
@BodyParams('jobId') jobId: string,
@BodyParams('expirationDate') expirationDateStr: string,
@BodyParams('status') @Enum(Object.values(JobStatusSharedWithGrowi)) growiJobStatus: JobStatusSharedWithGrowi,
): Promise<{ status: JobStatus }> {
const expirationDate = new Date(expirationDateStr);
try {
await this.pdfConvertService.registerOrUpdateJob(jobId, expirationDate, growiJobStatus);
this.pdfConvertService.cleanUpJobList();
return { status: this.pdfConvertService.getJobStatus(jobId) };
}
catch (err) {
this.logger.error('Failed to register or update job', err);
throw new InternalServerError(err);
}
}

}

export default PdfCtrl;
19 changes: 19 additions & 0 deletions apps/pdf-converter/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { $log } from '@tsed/common';
import { PlatformExpress } from '@tsed/platform-express';

import Server from './server';

async function bootstrap() {
try {
$log.debug('Start server...');
const platform = await PlatformExpress.bootstrap(Server);

await platform.listen();
$log.debug('Server initialized');
}
catch (error) {
$log.error(error);
}
}

bootstrap();
37 changes: 37 additions & 0 deletions apps/pdf-converter/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { PlatformApplication } from '@tsed/common';
import { Configuration, Inject } from '@tsed/di';
import express from 'express';
import '@tsed/swagger';

import * as Controllers from './controllers';

import '@tsed/platform-express';

const PORT = Number(process.env.PORT || 3010);

@Configuration({
port: PORT,
acceptMimes: ['application/json'],
mount: {
'/': [...Object.values(Controllers)],
},
middlewares: [
'json-parser',
express.json({ limit: '50mb' }),
express.urlencoded({ extended: true, limit: '50mb' }),
],
swagger: [
{
path: '/v3/docs',
specVersion: '3.0.1',
},
],
})
class Server {

@Inject()
app: PlatformApplication;

}

export default Server;
Loading