Spaces:
Sleeping
Sleeping
| import { config } from '../config'; | |
| import { logger } from '../utils/logger'; | |
| import { getExpiredReportJobs, updateJobFields } from '../db/jobs'; | |
| import { deleteReportPdf } from '../db/storage'; | |
| import { insertJobEvent } from '../db/events'; | |
| /** | |
| * Periodic cleanup of expired PDF reports. | |
| * Runs every CLEANUP_INTERVAL minutes. | |
| * | |
| * Deletes report and Digital Receipt PDFs after their independent expiry time. | |
| * Deleted paths remain available in job events for the audit trail. | |
| */ | |
| export async function runCleanupReportsCron(): Promise<void> { | |
| const cronLog = logger.child({ cron: 'cleanup-reports' }); | |
| cronLog.info('Starting report cleanup cron'); | |
| try { | |
| const expiredJobs = await getExpiredReportJobs(); | |
| if (expiredJobs.length === 0) { | |
| cronLog.info('No expired reports to clean up'); | |
| return; | |
| } | |
| cronLog.info(`Found ${expiredJobs.length} expired reports to clean up`); | |
| let deleted = 0; | |
| let failed = 0; | |
| for (const job of expiredJobs) { | |
| const now = Date.now(); | |
| if ( | |
| job.output_pdf_path && | |
| job.output_pdf_expires_at && | |
| new Date(job.output_pdf_expires_at).getTime() <= now | |
| ) { | |
| try { | |
| await deleteReportPdf(job.output_pdf_path); | |
| deleted++; | |
| await updateJobFields(job.id, { output_pdf_path: null }); | |
| await insertJobEvent({ | |
| job_id: job.id, | |
| identity_id: null, | |
| level: 'info', | |
| step: 'report_expired_deleted', | |
| message: `PDF report deleted after expiry: ${job.output_pdf_path}`, | |
| metadata: { expired_path: job.output_pdf_path }, | |
| }); | |
| cronLog.info(`Deleted expired report for job ${job.id}`); | |
| } catch (err) { | |
| failed++; | |
| cronLog.error(`Failed to delete report for job ${job.id}`, { | |
| error: err instanceof Error ? err.message : String(err), | |
| path: job.output_pdf_path, | |
| }); | |
| } | |
| } | |
| if ( | |
| job.receipt_pdf_path && | |
| job.receipt_pdf_expires_at && | |
| new Date(job.receipt_pdf_expires_at).getTime() <= now | |
| ) { | |
| try { | |
| await deleteReportPdf(job.receipt_pdf_path); | |
| deleted++; | |
| await updateJobFields(job.id, { receipt_pdf_path: null }); | |
| await insertJobEvent({ | |
| job_id: job.id, | |
| identity_id: null, | |
| level: 'info', | |
| step: 'receipt_expired_deleted', | |
| message: `Digital Receipt deleted after expiry: ${job.receipt_pdf_path}`, | |
| metadata: { expired_path: job.receipt_pdf_path }, | |
| }); | |
| cronLog.info(`Deleted expired Digital Receipt for job ${job.id}`); | |
| } catch (err) { | |
| failed++; | |
| cronLog.error(`Failed to delete Digital Receipt for job ${job.id}`, { | |
| error: err instanceof Error ? err.message : String(err), | |
| path: job.receipt_pdf_path, | |
| }); | |
| } | |
| } | |
| } | |
| cronLog.info('Report cleanup cron completed', { deleted, failed }); | |
| } catch (err) { | |
| cronLog.error('Report cleanup cron failed', { | |
| error: err instanceof Error ? err.message : String(err), | |
| }); | |
| } | |
| } | |