Files
bookorbit/server/src/modules/audit/audit.service.ts
T
neon 1418957b6f chore: cleanup project structure and resolve client type errors
- resolve all 69 client type-checking errors for stricter TS compliance
- remove obsolete type-check baseline scripts and files
- overhaul project root with updated README.md and new DEVELOPMENT.md
- migrate to unified baseline migration strategy and cleanup docker configs
- remove redundant production and dev-specific shell scripts
2026-03-25 07:14:51 -06:00

71 lines
2.3 KiB
TypeScript

import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { AUDIT_EVENT, AuditEventPayload, AuditEventsService } from './audit-events.service';
import { AuditRepository, AuditLogQuery } from './audit.repository';
import { AppSettingsService } from '../app-settings/app-settings.service';
const AUDIT_RETENTION_DAYS_KEY = 'audit_retention_days';
const DEFAULT_RETENTION_DAYS = 90;
@Injectable()
export class AuditService implements OnModuleInit, OnModuleDestroy {
private readonly logger = new Logger(AuditService.name);
private readonly boundHandler: (payload: AuditEventPayload) => void;
constructor(
private readonly auditEvents: AuditEventsService,
private readonly auditRepository: AuditRepository,
private readonly appSettings: AppSettingsService,
) {
this.boundHandler = this.handleAuditEvent.bind(this);
}
onModuleInit() {
this.auditEvents.on(AUDIT_EVENT, this.boundHandler);
}
onModuleDestroy() {
this.auditEvents.off(AUDIT_EVENT, this.boundHandler);
}
private handleAuditEvent(payload: AuditEventPayload): void {
this.auditRepository
.insert({
userId: payload.userId,
actorUsername: payload.actorUsername,
action: payload.action,
resource: payload.resource ?? null,
resourceId: payload.resourceId ?? null,
description: payload.description,
ip: payload.ip ?? null,
meta: payload.meta ?? null,
})
.catch((err: unknown) => {
this.logger.error('Failed to write audit log', err);
});
}
getAuditLogs(query: AuditLogQuery) {
return this.auditRepository.findAll(query);
}
async getRetentionDays(): Promise<number> {
try {
const value = await this.appSettings.getValue(AUDIT_RETENTION_DAYS_KEY);
if (!value) return DEFAULT_RETENTION_DAYS;
const parsed = parseInt(value, 10);
return isNaN(parsed) || parsed <= 0 ? DEFAULT_RETENTION_DAYS : parsed;
} catch {
return DEFAULT_RETENTION_DAYS;
}
}
async runRetentionCleanup(): Promise<void> {
const days = await this.getRetentionDays();
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - days);
await this.auditRepository.deleteOlderThan(cutoff);
this.logger.log(`Audit log cleanup: deleted records older than ${days} days`);
}
}