/** * Audit Service * * Provides centralized audit logging functionality * Tracks all important actions for security and compliance */ import { Injectable, Logger, Inject } from '@nestjs/common'; import { v4 as uuidv4 } from 'uuid'; import { AuditLog, AuditAction, AuditStatus, } from '../../domain/entities/audit-log.entity'; import { AuditLogRepository, AUDIT_LOG_REPOSITORY, AuditLogFilters, } from '../../domain/ports/out/audit-log.repository'; export interface LogAuditInput { action: AuditAction; status: AuditStatus; userId: string; userEmail: string; organizationId: string; resourceType?: string; resourceId?: string; resourceName?: string; metadata?: Record; ipAddress?: string; userAgent?: string; errorMessage?: string; } @Injectable() export class AuditService { private readonly logger = new Logger(AuditService.name); constructor( @Inject(AUDIT_LOG_REPOSITORY) private readonly auditLogRepository: AuditLogRepository, ) {} /** * Log an audit event */ async log(input: LogAuditInput): Promise { try { const auditLog = AuditLog.create({ id: uuidv4(), ...input, }); await this.auditLogRepository.save(auditLog); this.logger.log( `Audit log created: ${input.action} by ${input.userEmail} (${input.status})`, ); } catch (error: any) { // Never throw on audit logging failure - log the error and continue this.logger.error( `Failed to create audit log: ${error?.message || 'Unknown error'}`, error?.stack, ); } } /** * Log successful action */ async logSuccess( action: AuditAction, userId: string, userEmail: string, organizationId: string, options?: { resourceType?: string; resourceId?: string; resourceName?: string; metadata?: Record; ipAddress?: string; userAgent?: string; }, ): Promise { await this.log({ action, status: AuditStatus.SUCCESS, userId, userEmail, organizationId, ...options, }); } /** * Log failed action */ async logFailure( action: AuditAction, userId: string, userEmail: string, organizationId: string, errorMessage: string, options?: { resourceType?: string; resourceId?: string; metadata?: Record; ipAddress?: string; userAgent?: string; }, ): Promise { await this.log({ action, status: AuditStatus.FAILURE, userId, userEmail, organizationId, errorMessage, ...options, }); } /** * Get audit logs with filters */ async getAuditLogs(filters: AuditLogFilters): Promise<{ logs: AuditLog[]; total: number; }> { const [logs, total] = await Promise.all([ this.auditLogRepository.findByFilters(filters), this.auditLogRepository.count(filters), ]); return { logs, total }; } /** * Get audit trail for a specific resource */ async getResourceAuditTrail( resourceType: string, resourceId: string, ): Promise { return this.auditLogRepository.findByResource(resourceType, resourceId); } /** * Get recent activity for an organization */ async getOrganizationActivity( organizationId: string, limit: number = 50, ): Promise { return this.auditLogRepository.findRecentByOrganization(organizationId, limit); } /** * Get user activity history */ async getUserActivity(userId: string, limit: number = 50): Promise { return this.auditLogRepository.findByUser(userId, limit); } }