| import { Router, Response } from 'express'; |
| import { authenticateUser, AuthenticatedRequest } from '../middleware/auth'; |
| import { getJobById } from '../../db/jobs'; |
| import { createSignedUrl } from '../../db/storage'; |
| import { config } from '../../config'; |
| import { logger } from '../../utils/logger'; |
|
|
| const router = Router(); |
|
|
| function buildReportDownloadName(inputFileName: string): string { |
| const base = String(inputFileName || 'report') |
| .replace(/\.[^.]+$/, '') |
| .replace(/[\\/:*?"<>|]+/g, ' ') |
| .replace(/\s+/g, ' ') |
| .trim() |
| .slice(0, 140) || 'report'; |
| return `RelVDev_${base}.pdf`; |
| } |
|
|
| function buildReceiptDownloadName(inputFileName: string): string { |
| const base = String(inputFileName || 'receipt') |
| .replace(/\.[^.]+$/, '') |
| .replace(/[\\/:*?"<>|]+/g, ' ') |
| .replace(/\s+/g, ' ') |
| .trim() |
| .slice(0, 132) || 'receipt'; |
| return `RelVDev_Receipt_${base}.pdf`; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| router.get( |
| '/api/jobs/:jobId/report-url', |
| authenticateUser, |
| async (req, res: Response): Promise<void> => { |
| const authReq = req as AuthenticatedRequest; |
| const jobId = String(authReq.params.jobId || ''); |
|
|
| try { |
| const job = await getJobById(jobId); |
|
|
| if (!job) { |
| res.status(404).json({ error: 'Job not found' }); |
| return; |
| } |
|
|
| if (job.user_id !== authReq.userId) { |
| res.status(403).json({ error: 'Forbidden' }); |
| return; |
| } |
|
|
| if (!job.output_pdf_path) { |
| res.status(404).json({ error: 'Report PDF is not available yet' }); |
| return; |
| } |
|
|
| if ( |
| job.output_pdf_expires_at && |
| new Date(job.output_pdf_expires_at).getTime() < Date.now() |
| ) { |
| res.status(410).json({ error: 'Report PDF has expired' }); |
| return; |
| } |
|
|
| const signedUrl = await createSignedUrl( |
| config.reportBucket, |
| job.output_pdf_path, |
| 300, |
| buildReportDownloadName(job.input_file_name), |
| ); |
|
|
| res.json({ |
| signedUrl, |
| fileName: buildReportDownloadName(job.input_file_name), |
| expiresIn: 300, |
| expiresAt: new Date(Date.now() + 300 * 1000).toISOString(), |
| }); |
| } catch (err: unknown) { |
| const message = err instanceof Error ? err.message : String(err); |
| logger.error('Failed to create report signed URL', { |
| jobId, |
| userId: authReq.userId, |
| error: message, |
| }); |
| res.status(500).json({ error: 'Failed to create report download URL' }); |
| } |
| }, |
| ); |
|
|
| |
| |
| |
| |
| |
| router.get( |
| '/api/jobs/:jobId/receipt-url', |
| authenticateUser, |
| async (req, res: Response): Promise<void> => { |
| const authReq = req as AuthenticatedRequest; |
| const jobId = String(authReq.params.jobId || ''); |
|
|
| try { |
| const job = await getJobById(jobId); |
|
|
| if (!job) { |
| res.status(404).json({ error: 'Job not found' }); |
| return; |
| } |
|
|
| if (job.user_id !== authReq.userId) { |
| res.status(403).json({ error: 'Forbidden' }); |
| return; |
| } |
|
|
| if (!job.receipt_pdf_path) { |
| res.status(404).json({ error: 'Digital Receipt is not available yet' }); |
| return; |
| } |
|
|
| if ( |
| job.receipt_pdf_expires_at && |
| new Date(job.receipt_pdf_expires_at).getTime() < Date.now() |
| ) { |
| res.status(410).json({ error: 'Digital Receipt has expired' }); |
| return; |
| } |
|
|
| const fileName = buildReceiptDownloadName(job.input_file_name); |
| const signedUrl = await createSignedUrl( |
| config.reportBucket, |
| job.receipt_pdf_path, |
| 300, |
| fileName, |
| ); |
|
|
| res.json({ |
| signedUrl, |
| fileName, |
| expiresIn: 300, |
| expiresAt: new Date(Date.now() + 300 * 1000).toISOString(), |
| }); |
| } catch (err: unknown) { |
| const message = err instanceof Error ? err.message : String(err); |
| logger.error('Failed to create Digital Receipt signed URL', { |
| jobId, |
| userId: authReq.userId, |
| error: message, |
| }); |
| res.status(500).json({ error: 'Failed to create Digital Receipt download URL' }); |
| } |
| }, |
| ); |
|
|
| export default router; |
|
|