xpeditis2.0/apps/backend/src/application/services/audit.service.ts
David-Henri ARNAUD c5c15eb1f9 feature phase 3
2025-10-13 17:54:32 +02:00

166 lines
3.6 KiB
TypeScript

/**
* 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<string, any>;
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<void> {
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<string, any>;
ipAddress?: string;
userAgent?: string;
},
): Promise<void> {
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<string, any>;
ipAddress?: string;
userAgent?: string;
},
): Promise<void> {
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<AuditLog[]> {
return this.auditLogRepository.findByResource(resourceType, resourceId);
}
/**
* Get recent activity for an organization
*/
async getOrganizationActivity(
organizationId: string,
limit: number = 50,
): Promise<AuditLog[]> {
return this.auditLogRepository.findRecentByOrganization(organizationId, limit);
}
/**
* Get user activity history
*/
async getUserActivity(userId: string, limit: number = 50): Promise<AuditLog[]> {
return this.auditLogRepository.findByUser(userId, limit);
}
}