File size: 3,179 Bytes
521a9b6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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),
    });
  }
}