@@ -4,6 +4,7 @@ import { createLogger } from '@/lib/logging';
44import { appendAuditLog } from '@/lib/audit' ;
55import { getCertificateById , getCertificateForDownload } from '@/services/certificate-service' ;
66import { generatePDF } from '@/services/pdf-generation' ;
7+ import { withTimeout } from '@/lib/timeout' ;
78
89const logger = createLogger ( 'certificates-download' ) ;
910
@@ -117,10 +118,22 @@ export async function GET(request: NextRequest, { params }: { params: { id: stri
117118 // Generate PDF from certificate data
118119 const html = generateCertificateHTML ( certificate ) ;
119120
120- // TODO: Add timeout protection for PDF generation
121- // Currently Puppeteer may hang on malicious HTML
122- // Implement: Promise.race(generatePDF(html), timeout(30000))
123- const pdfBuffer = await generatePDF ( html ) ;
121+ // Timeout protection for PDF generation
122+ const timeoutMs = parseInt ( process . env . PDF_TIMEOUT_MS || '30000' , 10 ) ;
123+ let pdfBuffer ;
124+ try {
125+ pdfBuffer = await withTimeout ( generatePDF ( html ) , timeoutMs , 'PDF generation timed out, please retry' ) ;
126+ } catch ( e ) {
127+ logger . error ( 'PDF generation timeout' , { context : { certificateId } } ) ;
128+ return NextResponse . json (
129+ {
130+ error : 'PDF generation timed out, please retry' ,
131+ timeout : timeoutMs ,
132+ retry_after : 5 ,
133+ } ,
134+ { status : 504 }
135+ ) ;
136+ }
124137
125138 if ( ! pdfBuffer || pdfBuffer . length === 0 ) {
126139 throw new Error ( 'PDF generation resulted in empty buffer' ) ;
@@ -161,7 +174,7 @@ export async function GET(request: NextRequest, { params }: { params: { id: stri
161174 Expires : '0' ,
162175 } ,
163176 } ) ;
164- } catch ( error ) {
177+ } catch ( error : unknown ) {
165178 logger . error ( 'Certificate download error' , {
166179 context : { certificateId, userId } ,
167180 error,
@@ -194,7 +207,15 @@ export async function GET(request: NextRequest, { params }: { params: { id: stri
194207 * The name and courseName fields have been through input validation
195208 * which stripped dangerous HTML tags and patterns.
196209 */
197- function generateCertificateHTML ( cert : any ) : string {
210+ interface Certificate {
211+ name : string ;
212+ courseName : string ;
213+ completionDate : string ;
214+ issuedAt : string ;
215+ certificateId : string ;
216+ }
217+
218+ function generateCertificateHTML ( cert : Certificate ) : string {
198219 const { name, courseName, completionDate, issuedAt } = cert ;
199220
200221 // Format dates
0 commit comments