/** * @ai-summary Centralized audit logging service * @ai-context Provides simple API for all features to log audit events */ import { AuditLogRepository } from '../data/audit-log.repository'; import { AuditLogCategory, AuditLogSeverity, AuditLogEntry, AuditLogFilters, AuditLogPagination, AuditLogSearchResult, isValidCategory, isValidSeverity, } from './audit-log.types'; import { logger } from '../../../core/logging/logger'; export class AuditLogService { constructor(private repository: AuditLogRepository) {} /** * Log an audit event * * @param category - Event category (auth, vehicle, user, system, admin) * @param severity - Event severity (info, warning, error) * @param userId - User who performed the action (null for system actions) * @param action - Human-readable description of the action * @param resourceType - Type of resource affected (optional) * @param resourceId - ID of affected resource (optional) * @param details - Additional structured data (optional) */ async log( category: AuditLogCategory, severity: AuditLogSeverity, userId: string | null, action: string, resourceType?: string | null, resourceId?: string | null, details?: Record | null ): Promise { // Validate category if (!isValidCategory(category)) { const error = new Error(`Invalid audit log category: ${category}`); logger.error('Invalid audit log category', { category }); throw error; } // Validate severity if (!isValidSeverity(severity)) { const error = new Error(`Invalid audit log severity: ${severity}`); logger.error('Invalid audit log severity', { severity }); throw error; } try { const entry = await this.repository.create({ category, severity, userId, action, resourceType, resourceId, details, }); logger.debug('Audit log created', { id: entry.id, category, severity, action, }); return entry; } catch (error) { logger.error('Error creating audit log', { error, category, action }); throw error; } } /** * Convenience method for info-level logs */ async info( category: AuditLogCategory, userId: string | null, action: string, resourceType?: string | null, resourceId?: string | null, details?: Record | null ): Promise { return this.log(category, 'info', userId, action, resourceType, resourceId, details); } /** * Convenience method for warning-level logs */ async warning( category: AuditLogCategory, userId: string | null, action: string, resourceType?: string | null, resourceId?: string | null, details?: Record | null ): Promise { return this.log(category, 'warning', userId, action, resourceType, resourceId, details); } /** * Convenience method for error-level logs */ async error( category: AuditLogCategory, userId: string | null, action: string, resourceType?: string | null, resourceId?: string | null, details?: Record | null ): Promise { return this.log(category, 'error', userId, action, resourceType, resourceId, details); } /** * Search audit logs with filters and pagination */ async search( filters: AuditLogFilters, pagination: AuditLogPagination ): Promise { try { return await this.repository.search(filters, pagination); } catch (error) { logger.error('Error searching audit logs', { error, filters }); throw error; } } /** * Get logs for CSV export (limited to 5000 records) */ async getForExport(filters: AuditLogFilters): Promise<{ logs: AuditLogEntry[]; truncated: boolean }> { try { return await this.repository.getForExport(filters); } catch (error) { logger.error('Error getting audit logs for export', { error, filters }); throw error; } } /** * Run retention cleanup (delete logs older than specified days) */ async cleanup(olderThanDays: number = 90): Promise { try { const deletedCount = await this.repository.cleanup(olderThanDays); logger.info('Audit log cleanup completed', { olderThanDays, deletedCount }); return deletedCount; } catch (error) { logger.error('Error running audit log cleanup', { error, olderThanDays }); throw error; } } }