11import { Injectable , Logger } from '@nestjs/common' ;
22import { Cron , CronExpression } from '@nestjs/schedule' ;
3+ import { InjectRepository } from '@nestjs/typeorm' ;
4+ import { Repository , LessThan } from 'typeorm' ;
5+ import { Counter } from 'prom-client' ;
6+ import { AuditLog } from '../audit-log.entity' ;
37import { AuditLogService } from '../audit-log.service' ;
8+ import { MetricsCollectionService } from '../../monitoring/metrics/metrics-collection.service' ;
9+ import { ConfigService } from '@nestjs/config' ;
410
5- /**
6- * Provides audit Retention Task behavior.
7- */
811@Injectable ( )
912export class AuditRetentionTask {
1013 private readonly logger = new Logger ( AuditRetentionTask . name ) ;
11- constructor ( private readonly auditLogService : AuditLogService ) { }
12- /**
13- * Run retention policy daily at 2 AM
14- */
14+ private readonly retentionDays : number ;
15+ private readonly batchSize = 1000 ;
16+ private deletedCounter : Counter < 'table' > ;
17+
18+ constructor (
19+ @InjectRepository ( AuditLog )
20+ private readonly auditLogRepo : Repository < AuditLog > ,
21+ private readonly auditLogService : AuditLogService ,
22+ private readonly configService : ConfigService ,
23+ private readonly metrics : MetricsCollectionService ,
24+ ) {
25+ this . retentionDays = this . configService . get < number > ( 'AUDIT_LOG_RETENTION_DAYS' , 730 ) ;
26+ const registry = this . metrics . getRegistry ( ) ;
27+ this . deletedCounter =
28+ ( registry . getSingleMetric ( 'deleted_count' ) as Counter < 'table' > ) ??
29+ new Counter ( {
30+ name : 'deleted_count' ,
31+ help : 'Number of rows deleted by data retention policies' ,
32+ labelNames : [ 'table' ] as const ,
33+ registers : [ registry ] ,
34+ } ) ;
35+ }
36+
1537 @Cron ( CronExpression . EVERY_DAY_AT_2AM )
1638 async handleDailyRetention ( ) : Promise < void > {
1739 this . logger . log ( 'Starting daily audit log retention policy...' ) ;
40+ let totalDeleted = 0 ;
1841 try {
19- const deletedCount = await this . auditLogService . applyRetentionPolicy ( ) ;
20- this . logger . log ( `Daily retention policy completed. Deleted ${ deletedCount } old audit logs.` ) ;
42+ const cutoff = new Date ( ) ;
43+ cutoff . setDate ( cutoff . getDate ( ) - this . retentionDays ) ;
44+
45+ let deleted = 0 ;
46+ do {
47+ const logsToDelete = await this . auditLogRepo . find ( {
48+ select : [ 'id' ] ,
49+ where : { timestamp : LessThan ( cutoff ) } ,
50+ take : this . batchSize ,
51+ } ) ;
52+
53+ if ( logsToDelete . length === 0 ) {
54+ deleted = 0 ;
55+ break ;
56+ }
57+
58+ const idValues = logsToDelete . map ( ( l ) => l . id ) ;
59+ const result = await this . auditLogRepo
60+ . createQueryBuilder ( )
61+ . delete ( )
62+ . from ( AuditLog )
63+ . whereInIds ( idValues )
64+ . execute ( ) ;
65+ deleted = result . affected || 0 ;
66+ totalDeleted += deleted ;
67+ } while ( deleted >= this . batchSize ) ;
68+
69+ this . deletedCounter . inc ( { table : 'audit_logs' } , totalDeleted ) ;
70+ this . logger . log ( `Daily retention policy completed. Deleted ${ totalDeleted } old audit logs.` ) ;
2171 } catch ( error ) {
2272 this . logger . error ( 'Failed to apply retention policy:' , error ) ;
2373 }
2474 }
25- /**
26- * Generate weekly report every Monday at 3 AM
27- */
28- @Cron ( '0 3 * * 1' ) // Every Monday at 3 AM
75+
76+ @Cron ( '0 3 * * 1' )
2977 async handleWeeklyReport ( ) : Promise < void > {
3078 this . logger . log ( 'Generating weekly audit report...' ) ;
3179 try {
@@ -38,8 +86,6 @@ export class AuditRetentionTask {
3886 criticalEvents : report . eventsBySeverity [ 'CRITICAL' ] || 0 ,
3987 errorEvents : report . eventsBySeverity [ 'ERROR' ] || 0 ,
4088 } ) ;
41- // In a real implementation, you might send this report via email
42- // or store it for compliance purposes
4389 } catch ( error ) {
4490 this . logger . error ( 'Failed to generate weekly report:' , error ) ;
4591 }
0 commit comments