From d3437311dc332009e82f5c5e1b985a643eb26d79 Mon Sep 17 00:00:00 2001 From: wangziqi Date: Tue, 7 Jul 2026 09:56:52 +0800 Subject: [PATCH] refactor: remove Puppeteer, delete old PDF download endpoint --- apps/server/package.json | 1 - .../src/archive/archive-report.service.ts | 53 ---- apps/server/src/archive/archive.controller.ts | 23 +- package-lock.json | 227 ------------------ 4 files changed, 1 insertion(+), 303 deletions(-) diff --git a/apps/server/package.json b/apps/server/package.json index 91ffe4e..d40c28b 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -44,7 +44,6 @@ "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "pdfkit": "^0.18.0", - "puppeteer": "^25.3.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "typeorm": "^0.3.28" diff --git a/apps/server/src/archive/archive-report.service.ts b/apps/server/src/archive/archive-report.service.ts index 09da77f..5e58672 100644 --- a/apps/server/src/archive/archive-report.service.ts +++ b/apps/server/src/archive/archive-report.service.ts @@ -1,8 +1,6 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import puppeteer from 'puppeteer'; -import { Response } from 'express'; import { StudentProfile } from '../entities/student-profile.entity'; import { StudentEnrollment } from '../entities/student-enrollment.entity'; import { ExamScore } from '../entities/exam-score.entity'; @@ -33,57 +31,6 @@ export class ArchiveReportService { @InjectRepository(Student) private studentRepo: Repository, ) {} - async generateReport(studentId: number, res: Response): Promise { - const [student, profile, enrollments, exams, learnings, result, attendances] = - await Promise.all([ - this.studentRepo.findOne({ where: { id: studentId } }), - this.profileRepo.findOne({ where: { studentId } }), - this.enrollmentRepo.find({ where: { studentId }, order: { startDate: 'ASC' } }), - this.examRepo.find({ where: { studentId }, order: { examDate: 'ASC' } }), - this.learningRepo.find({ where: { studentId }, order: { recordDate: 'DESC' } }), - this.resultRepo.findOne({ where: { studentId } }), - this.attendanceRepo.find({ where: { studentId }, order: { attendanceDate: 'ASC' } }), - ]); - - if (!student) throw new Error('学生不存在'); - - const data: ReportData = { - student, - profile, - enrollments, - exams, - learnings, - result, - attendances, - }; - - const html = this.buildHtml(data); - - const browser = await puppeteer.launch({ - headless: true, - args: ['--no-sandbox', '--disable-setuid-sandbox'], - }); - - try { - const page = await browser.newPage(); - await page.setContent(html, { waitUntil: 'load' }); - const pdf = await page.pdf({ - format: 'A4', - printBackground: true, - margin: { top: 0, right: 0, bottom: 0, left: 0 }, - }); - - res.setHeader('Content-Type', 'application/pdf'); - res.setHeader( - 'Content-Disposition', - `attachment; filename=student_report_${studentId}.pdf`, - ); - res.end(pdf); - } finally { - await browser.close(); - } - } - async generateReportHtml(studentId: number): Promise { const [student, profile, enrollments, exams, learnings, result, attendances] = await Promise.all([ diff --git a/apps/server/src/archive/archive.controller.ts b/apps/server/src/archive/archive.controller.ts index 0a885fa..8788908 100644 --- a/apps/server/src/archive/archive.controller.ts +++ b/apps/server/src/archive/archive.controller.ts @@ -8,12 +8,11 @@ import { Param, UseGuards, Request, - Res, UseInterceptors, UploadedFile, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; -import type { Request as ExpressRequest, Response } from 'express'; +import type { Request as ExpressRequest } from 'express'; import { ArchiveReportService } from './archive-report.service'; import { ArchiveService } from './archive.service'; import { @@ -340,26 +339,6 @@ export class ArchiveController { return result; } - @Get(':studentId/report') - @RequirePermission('student:view') - async generateReport( - @Param('studentId') studentId: string, - @Res() res: Response, - @Request() req: AuthenticatedRequest, - ) { - const { ipAddress, userAgent } = extractRequestInfo(req); - await this.logService.log({ - userId: req.user?.id, - username: req.user?.username, - module: 'archive', - action: 'generate_report', - targetId: +studentId, - targetType: 'student', - ipAddress, - userAgent, - }); - return this.reportService.generateReport(+studentId, res); - } @Get(':studentId/report-html') @RequirePermission('student:view') diff --git a/package-lock.json b/package-lock.json index f02628a..9b15792 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,7 +70,6 @@ "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "pdfkit": "^0.18.0", - "puppeteer": "^25.3.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "typeorm": "^0.3.28" @@ -4204,103 +4203,6 @@ "integrity": "sha512-nctNujXL3FC1v99FktaTMSugSD9ZOZekEpahUSafkU2TSvW+XGKNkQZbokuJtiWvPBK208dwMJva8UfBkChqpw==", "license": "MIT" }, - "node_modules/@puppeteer/browsers": { - "version": "3.0.6", - "resolved": "https://registry.npmmirror.com/@puppeteer/browsers/-/browsers-3.0.6.tgz", - "integrity": "sha512-B/gKoqlFkzhvzsI6jo9K1cZz9o5ypviVv/xu8CwA4grZzyVwN+XfkT+tu8T1zrauuEXv6VhS2oGX+6NL95WcKA==", - "license": "Apache-2.0", - "dependencies": { - "modern-tar": "^0.7.6", - "yargs": "^18.0.0" - }, - "bin": { - "browsers": "lib/main-cli.js" - }, - "engines": { - "node": ">=22.12.0" - }, - "peerDependencies": { - "proxy-agent": ">=8.0.1", - "yauzl": "^2.10.0 || ^3.4.0" - }, - "peerDependenciesMeta": { - "proxy-agent": { - "optional": true - }, - "yauzl": { - "optional": true - } - } - }, - "node_modules/@puppeteer/browsers/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@puppeteer/browsers/node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmmirror.com/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@puppeteer/browsers/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmmirror.com/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, "node_modules/@rc-component/async-validator": { "version": "6.0.0", "resolved": "https://registry.npmmirror.com/@rc-component/async-validator/-/async-validator-6.0.0.tgz", @@ -7761,31 +7663,6 @@ "node": ">=6.0" } }, - "node_modules/chromium-bidi": { - "version": "16.0.1", - "resolved": "https://registry.npmmirror.com/chromium-bidi/-/chromium-bidi-16.0.1.tgz", - "integrity": "sha512-J63PGu/9PpeCwLIcKYyzWP6yaVL5pxuBc0shlYCYM8BaAkmlwiQboXO1iNbOgSDbVklEyYFfNEcHD8oOAWacUA==", - "license": "Apache-2.0", - "dependencies": { - "mitt": "^3.0.1", - "zod": "^3.24.1" - }, - "engines": { - "node": ">=20.19.0 <22.0.0 || >=22.12.0" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/chromium-bidi/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/ci-info": { "version": "4.4.0", "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-4.4.0.tgz", @@ -8476,12 +8353,6 @@ "node": ">=8" } }, - "node_modules/devtools-protocol": { - "version": "0.0.1638949", - "resolved": "https://registry.npmmirror.com/devtools-protocol/-/devtools-protocol-0.0.1638949.tgz", - "integrity": "sha512-mXwg4Fqnv0WR4iuAT/gYUmctNkjILwXFHyZ+m7Ty1dfr0ezZt2U3gnrrJTfRobJTHoXf+IbuFvFITzLrLFjwJA==", - "license": "BSD-3-Clause" - }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmmirror.com/dezalgo/-/dezalgo-1.0.4.tgz", @@ -12012,18 +11883,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/linebreak": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/linebreak/-/linebreak-1.1.0.tgz", @@ -12718,12 +12577,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "license": "MIT" - }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", @@ -12743,15 +12596,6 @@ "license": "MIT", "optional": true }, - "node_modules/modern-tar": { - "version": "0.7.6", - "resolved": "https://registry.npmmirror.com/modern-tar/-/modern-tar-0.7.6.tgz", - "integrity": "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", @@ -13734,44 +13578,6 @@ "node": ">=6" } }, - "node_modules/puppeteer": { - "version": "25.3.0", - "resolved": "https://registry.npmmirror.com/puppeteer/-/puppeteer-25.3.0.tgz", - "integrity": "sha512-O1tx8S315aw8eI99HZ5ZNcVEzJ9+jKF//eO5UvfZ3cXJ6okZ5sX3Y50u7DJaM+ewEK4LqXP068tBhfRaWikj+g==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "3.0.6", - "chromium-bidi": "16.0.1", - "devtools-protocol": "0.0.1638949", - "lilconfig": "^3.1.3", - "puppeteer-core": "25.3.0", - "typed-query-selector": "^2.12.2" - }, - "bin": { - "puppeteer": "lib/puppeteer/node/cli.js" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/puppeteer-core": { - "version": "25.3.0", - "resolved": "https://registry.npmmirror.com/puppeteer-core/-/puppeteer-core-25.3.0.tgz", - "integrity": "sha512-fm+wpUr2oigH1PXZvwgATrM2tYWHMDG8ASzTEe9uukCye4X5Ldx1K5BTHPFKITrIWvQQAQ256d1NpbEveBcKjA==", - "license": "Apache-2.0", - "dependencies": { - "@puppeteer/browsers": "3.0.6", - "chromium-bidi": "16.0.1", - "devtools-protocol": "0.0.1638949", - "typed-query-selector": "^2.12.2", - "webdriver-bidi-protocol": "0.4.2", - "ws": "^8.21.0" - }, - "engines": { - "node": ">=22.12.0" - } - }, "node_modules/pure-rand": { "version": "7.0.1", "resolved": "https://registry.npmmirror.com/pure-rand/-/pure-rand-7.0.1.tgz", @@ -15787,12 +15593,6 @@ "node": ">= 0.4" } }, - "node_modules/typed-query-selector": { - "version": "2.12.2", - "resolved": "https://registry.npmmirror.com/typed-query-selector/-/typed-query-selector-2.12.2.tgz", - "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", - "license": "MIT" - }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz", @@ -16465,12 +16265,6 @@ "defaults": "^1.0.3" } }, - "node_modules/webdriver-bidi-protocol": { - "version": "0.4.2", - "resolved": "https://registry.npmmirror.com/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.2.tgz", - "integrity": "sha512-VSV+fzfChirL3e7jay2yUC7B4HQCGtEWEg/MSSQbK+qWbqeGlRLlXTzPpYr3XGUvbpDHumWZBJxgesg4N7dbtA==", - "license": "Apache-2.0" - }, "node_modules/webpack": { "version": "5.108.3", "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.108.3.tgz", @@ -16831,27 +16625,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmmirror.com/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz",