-
Notifications
You must be signed in to change notification settings - Fork 218
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
add pdf-converter app #9279
Changes from all commits
e1f8e1f
7b6135f
5d0d7ab
38ef835
d0ab9ae
db522ae
79da525
12d78c7
dadcf07
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/dist/** | ||
/src/client-library/** |
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', | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/dist | ||
/specs |
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", "--"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tini は不要 terminus と連携して解決するのが望ましい 後続ストーリーで対応をお願いしたい There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ストーリー作成しました。 https://redmine.weseek.co.jp/issues/156179 |
||
CMD ["node", "dist/index.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', | ||
}, | ||
}; |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. この app は There was a problem hiding this comment. Choose a reason for hiding this commentThe 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}", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tsconfig を細かくチェックはしていないのだけれど、build での tsc は typecheck はせずにスピードを出すのが望ましい。 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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" | ||
} | ||
} |
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], | ||
}); |
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> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './pdf'; |
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; |
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(); |
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; |
There was a problem hiding this comment.
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 する必要があるっぽいです。