@kitstack/nest-powertools
Version:
A comprehensive collection of NestJS powertools, decorators, and utilities to supercharge your backend development
198 lines • 7.81 kB
JavaScript
;
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