UNPKG

@kitstack/nest-powertools

Version:

A comprehensive collection of NestJS powertools, decorators, and utilities to supercharge your backend development

198 lines 7.81 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var MongoAuditStorage_1, InMemoryAuditStorage_1, AuditInterceptor_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuditService = exports.Audit = exports.AuditInterceptor = exports.InMemoryAuditStorage = exports.MongoAuditStorage = void 0; const common_1 = require("@nestjs/common"); const common_2 = require("@nestjs/common"); const operators_1 = require("rxjs/operators"); const rxjs_1 = require("rxjs"); let MongoAuditStorage = MongoAuditStorage_1 = class MongoAuditStorage { constructor() { this.logger = new common_1.Logger(MongoAuditStorage_1.name); } async save(entry) { try { this.logger.log(`Audit log saved: ${entry.action} by user ${entry.userId}`); console.log('Audit Entry:', JSON.stringify(entry, null, 2)); } catch (error) { this.logger.error('Failed to save audit log', error); } } async find(filters) { return []; } async findById(id) { return null; } }; exports.MongoAuditStorage = MongoAuditStorage; exports.MongoAuditStorage = MongoAuditStorage = MongoAuditStorage_1 = __decorate([ (0, common_1.Injectable)() ], MongoAuditStorage); let InMemoryAuditStorage = InMemoryAuditStorage_1 = class InMemoryAuditStorage { constructor() { this.logs = []; this.logger = new common_1.Logger(InMemoryAuditStorage_1.name); } async save(entry) { entry.id = Date.now().toString(); this.logs.push(entry); this.logger.log(`Audit log saved: ${entry.action} by user ${entry.userId}`); } async find(filters) { return this.logs.filter((log) => { return Object.entries(filters).every(([key, value]) => log[key] === value); }); } async findById(id) { return this.logs.find((log) => log.id === id) || null; } getAllLogs() { return [...this.logs]; } clearLogs() { this.logs = []; } }; exports.InMemoryAuditStorage = InMemoryAuditStorage; exports.InMemoryAuditStorage = InMemoryAuditStorage = InMemoryAuditStorage_1 = __decorate([ (0, common_1.Injectable)() ], InMemoryAuditStorage); let AuditInterceptor = AuditInterceptor_1 = class AuditInterceptor { constructor(auditStorage) { this.auditStorage = auditStorage; this.logger = new common_1.Logger(AuditInterceptor_1.name); } intercept(context, next) { const auditConfig = this.getAuditConfig(context); if (!auditConfig) { return next.handle(); } const startTime = Date.now(); const request = context.switchToHttp().getRequest(); const user = request.user; const auditEntry = { userId: user?.id || user?.sub, userEmail: user?.email, action: auditConfig.action, resource: auditConfig.resource || this.extractResourceFromContext(context), resourceId: this.extractResourceId(request), ipAddress: request.ip || request.connection?.remoteAddress, userAgent: request.headers['user-agent'], timestamp: new Date(), endpoint: request.url, method: request.method, requestBody: auditConfig.includeRequestBody ? this.sanitizeData(request.body, auditConfig.excludeFields) : undefined, metadata: auditConfig.customMetadata ? auditConfig.customMetadata(context) : {}, }; return next.handle().pipe((0, operators_1.tap)((response) => { auditEntry.duration = Date.now() - startTime; auditEntry.responseStatus = context .switchToHttp() .getResponse().statusCode; if (auditConfig.includeResponseBody) { auditEntry.metadata = { ...auditEntry.metadata, responseBody: this.sanitizeData(response, auditConfig.excludeFields), }; } this.auditStorage.save(auditEntry).catch((error) => { this.logger.error('Failed to save audit log', error); }); }), (0, operators_1.catchError)((error) => { auditEntry.duration = Date.now() - startTime; auditEntry.responseStatus = error.status || 500; auditEntry.metadata = { ...auditEntry.metadata, error: error.message, }; this.auditStorage.save(auditEntry).catch((saveError) => { this.logger.error('Failed to save audit log for error case', saveError); }); return (0, rxjs_1.throwError)(() => error); })); } getAuditConfig(context) { const handler = context.getHandler(); const auditConfig = Reflect.getMetadata('audit-config', handler); return auditConfig || null; } extractResourceFromContext(context) { const className = context.getClass().name; const handlerName = context.getHandler().name; return `${className}.${handlerName}`; } extractResourceId(request) { return request.params?.id || request.params?.userId || request.body?.id; } sanitizeData(data, excludeFields = []) { if (!data || typeof data !== 'object') { return data; } const sanitized = { ...data }; const defaultExcludeFields = [ 'password', 'token', 'secret', 'key', 'authorization', ]; const fieldsToExclude = [...defaultExcludeFields, ...excludeFields]; fieldsToExclude.forEach((field) => { if (sanitized[field]) { sanitized[field] = '[REDACTED]'; } }); return sanitized; } }; exports.AuditInterceptor = AuditInterceptor; exports.AuditInterceptor = AuditInterceptor = AuditInterceptor_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [Object]) ], AuditInterceptor); const Audit = (action, options = {}) => { return (0, common_2.SetMetadata)('audit-config', { action, ...options }); }; exports.Audit = Audit; let AuditService = class AuditService { constructor(auditStorage) { this.auditStorage = auditStorage; } async getAuditLogs(filters = {}) { return this.auditStorage.find(filters); } async getAuditLogById(id) { return this.auditStorage.findById(id); } async getUserAuditLogs(userId) { return this.auditStorage.find({ userId }); } async getResourceAuditLogs(resource, resourceId) { const filters = { resource }; if (resourceId) { filters.resourceId = resourceId; } return this.auditStorage.find(filters); } }; exports.AuditService = AuditService; exports.AuditService = AuditService = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [Object]) ], AuditService); //# sourceMappingURL=audit.hook.js.map