@nest-toolbox/typeorm-audit-log 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/audit-context.d.ts +67 -0
- package/dist/audit-context.js +96 -0
- package/dist/audit-context.js.map +1 -0
- package/dist/audit-context.middleware.d.ts +76 -0
- package/dist/audit-context.middleware.js +91 -0
- package/dist/audit-context.middleware.js.map +1 -0
- package/dist/audit-log.entity.d.ts +56 -0
- package/dist/audit-log.entity.js +89 -0
- package/dist/audit-log.entity.js.map +1 -0
- package/dist/audit-log.module.d.ts +56 -0
- package/dist/audit-log.module.js +130 -0
- package/dist/audit-log.module.js.map +1 -0
- package/dist/audit-log.service.d.ts +43 -0
- package/dist/audit-log.service.js +177 -0
- package/dist/audit-log.service.js.map +1 -0
- package/dist/audit-log.subscriber.d.ts +66 -0
- package/dist/audit-log.subscriber.js +135 -0
- package/dist/audit-log.subscriber.js.map +1 -0
- package/dist/constants.d.ts +32 -0
- package/dist/constants.js +49 -0
- package/dist/constants.js.map +1 -0
- package/dist/decorators.d.ts +109 -0
- package/dist/decorators.js +161 -0
- package/dist/decorators.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/types.d.ts +161 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/diff.d.ts +16 -0
- package/dist/utils/diff.js +142 -0
- package/dist/utils/diff.js.map +1 -0
- package/dist/utils/mask.d.ts +15 -0
- package/dist/utils/mask.js +69 -0
- package/dist/utils/mask.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AuditLog = void 0;
|
|
13
|
+
const typeorm_1 = require("typeorm");
|
|
14
|
+
/**
|
|
15
|
+
* Entity for storing audit log entries.
|
|
16
|
+
* Tracks all changes to entities decorated with @Auditable().
|
|
17
|
+
*/
|
|
18
|
+
let AuditLog = class AuditLog {
|
|
19
|
+
};
|
|
20
|
+
exports.AuditLog = AuditLog;
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
|
23
|
+
__metadata("design:type", String)
|
|
24
|
+
], AuditLog.prototype, "id", void 0);
|
|
25
|
+
__decorate([
|
|
26
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 255 }),
|
|
27
|
+
__metadata("design:type", String)
|
|
28
|
+
], AuditLog.prototype, "entityName", void 0);
|
|
29
|
+
__decorate([
|
|
30
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 255 }),
|
|
31
|
+
__metadata("design:type", String)
|
|
32
|
+
], AuditLog.prototype, "entityId", void 0);
|
|
33
|
+
__decorate([
|
|
34
|
+
(0, typeorm_1.Column)({
|
|
35
|
+
type: 'varchar',
|
|
36
|
+
length: 10,
|
|
37
|
+
}),
|
|
38
|
+
__metadata("design:type", String)
|
|
39
|
+
], AuditLog.prototype, "action", void 0);
|
|
40
|
+
__decorate([
|
|
41
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }),
|
|
42
|
+
__metadata("design:type", Object)
|
|
43
|
+
], AuditLog.prototype, "userId", void 0);
|
|
44
|
+
__decorate([
|
|
45
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 255, nullable: true }),
|
|
46
|
+
__metadata("design:type", Object)
|
|
47
|
+
], AuditLog.prototype, "userName", void 0);
|
|
48
|
+
__decorate([
|
|
49
|
+
(0, typeorm_1.Column)({ type: 'jsonb', nullable: true })
|
|
50
|
+
// biome-ignore lint/suspicious/noExplicitAny: values can be any type
|
|
51
|
+
,
|
|
52
|
+
__metadata("design:type", Object)
|
|
53
|
+
], AuditLog.prototype, "oldValues", void 0);
|
|
54
|
+
__decorate([
|
|
55
|
+
(0, typeorm_1.Column)({ type: 'jsonb', nullable: true })
|
|
56
|
+
// biome-ignore lint/suspicious/noExplicitAny: values can be any type
|
|
57
|
+
,
|
|
58
|
+
__metadata("design:type", Object)
|
|
59
|
+
], AuditLog.prototype, "newValues", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
(0, typeorm_1.Column)({ type: 'jsonb', nullable: true }),
|
|
62
|
+
__metadata("design:type", Object)
|
|
63
|
+
], AuditLog.prototype, "diff", void 0);
|
|
64
|
+
__decorate([
|
|
65
|
+
(0, typeorm_1.Column)({ type: 'jsonb', nullable: true })
|
|
66
|
+
// biome-ignore lint/suspicious/noExplicitAny: metadata can contain any value
|
|
67
|
+
,
|
|
68
|
+
__metadata("design:type", Object)
|
|
69
|
+
], AuditLog.prototype, "metadata", void 0);
|
|
70
|
+
__decorate([
|
|
71
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 45, nullable: true }),
|
|
72
|
+
__metadata("design:type", Object)
|
|
73
|
+
], AuditLog.prototype, "ip", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 500, nullable: true }),
|
|
76
|
+
__metadata("design:type", Object)
|
|
77
|
+
], AuditLog.prototype, "userAgent", void 0);
|
|
78
|
+
__decorate([
|
|
79
|
+
(0, typeorm_1.CreateDateColumn)(),
|
|
80
|
+
__metadata("design:type", Date)
|
|
81
|
+
], AuditLog.prototype, "timestamp", void 0);
|
|
82
|
+
exports.AuditLog = AuditLog = __decorate([
|
|
83
|
+
(0, typeorm_1.Entity)('audit_logs'),
|
|
84
|
+
(0, typeorm_1.Index)(['entityName', 'entityId']),
|
|
85
|
+
(0, typeorm_1.Index)(['userId']),
|
|
86
|
+
(0, typeorm_1.Index)(['timestamp']),
|
|
87
|
+
(0, typeorm_1.Index)(['entityName', 'timestamp'])
|
|
88
|
+
], AuditLog);
|
|
89
|
+
//# sourceMappingURL=audit-log.entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-log.entity.js","sourceRoot":"","sources":["../src/audit-log.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAA0F;AAG1F;;;GAGG;AAMI,IAAM,QAAQ,GAAd,MAAM,QAAQ;CAiFpB,CAAA;AAjFY,4BAAQ;AAEnB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;oCACnB;AAMZ;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;4CACrB;AAMpB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;0CACvB;AASlB;IAJC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,EAAE;KACX,CAAC;;wCACmB;AAMrB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wCAClC;AAMvB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CAChC;AAOzB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,qEAAqE;;;2CAC9B;AAOvC;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,qEAAqE;;;2CAC9B;AAMvC;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;sCAChB;AAO1B;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,6EAA6E;;;0CACvC;AAMtC;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oCACrC;AAMnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CAC/B;AAM1B;IADC,IAAA,0BAAgB,GAAE;8BACP,IAAI;2CAAC;mBAhFN,QAAQ;IALpB,IAAA,gBAAM,EAAC,YAAY,CAAC;IACpB,IAAA,eAAK,EAAC,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IACjC,IAAA,eAAK,EAAC,CAAC,QAAQ,CAAC,CAAC;IACjB,IAAA,eAAK,EAAC,CAAC,WAAW,CAAC,CAAC;IACpB,IAAA,eAAK,EAAC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;GACtB,QAAQ,CAiFpB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type DynamicModule, type OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import type { DataSource } from 'typeorm';
|
|
3
|
+
import { AuditLogService } from './audit-log.service';
|
|
4
|
+
import { AuditLogSubscriber } from './audit-log.subscriber';
|
|
5
|
+
import type { AuditLogModuleAsyncOptions, AuditLogModuleOptions } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* AuditLogModule provides automatic audit logging for TypeORM entities.
|
|
8
|
+
*
|
|
9
|
+
* @example Basic usage
|
|
10
|
+
* ```typescript
|
|
11
|
+
* @Module({
|
|
12
|
+
* imports: [
|
|
13
|
+
* TypeOrmModule.forRoot({...}),
|
|
14
|
+
* AuditLogModule.forRoot({
|
|
15
|
+
* retentionDays: 90,
|
|
16
|
+
* excludeFields: ['password', 'token'],
|
|
17
|
+
* }),
|
|
18
|
+
* ],
|
|
19
|
+
* })
|
|
20
|
+
* export class AppModule implements NestModule {
|
|
21
|
+
* configure(consumer: MiddlewareConsumer) {
|
|
22
|
+
* consumer.apply(AuditContextMiddleware).forRoutes('*');
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example Async configuration
|
|
28
|
+
* ```typescript
|
|
29
|
+
* AuditLogModule.forRootAsync({
|
|
30
|
+
* imports: [ConfigModule],
|
|
31
|
+
* useFactory: (config: ConfigService) => ({
|
|
32
|
+
* retentionDays: config.get('AUDIT_RETENTION_DAYS'),
|
|
33
|
+
* async: config.get('AUDIT_ASYNC') === 'true',
|
|
34
|
+
* }),
|
|
35
|
+
* inject: [ConfigService],
|
|
36
|
+
* })
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare class AuditLogModule implements OnModuleInit {
|
|
40
|
+
private readonly dataSource;
|
|
41
|
+
private readonly subscriber;
|
|
42
|
+
private readonly auditLogService;
|
|
43
|
+
constructor(dataSource: DataSource, subscriber: AuditLogSubscriber, auditLogService: AuditLogService);
|
|
44
|
+
/**
|
|
45
|
+
* Initialize the module: register subscriber with DataSource and inject service.
|
|
46
|
+
*/
|
|
47
|
+
onModuleInit(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Configure the module with static options.
|
|
50
|
+
*/
|
|
51
|
+
static forRoot(options?: AuditLogModuleOptions): DynamicModule;
|
|
52
|
+
/**
|
|
53
|
+
* Configure the module with async options (factory, useClass, useExisting).
|
|
54
|
+
*/
|
|
55
|
+
static forRootAsync(options: AuditLogModuleAsyncOptions): DynamicModule;
|
|
56
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var AuditLogModule_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.AuditLogModule = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const typeorm_1 = require("@nestjs/typeorm");
|
|
16
|
+
const audit_log_entity_1 = require("./audit-log.entity");
|
|
17
|
+
const audit_log_service_1 = require("./audit-log.service");
|
|
18
|
+
const audit_log_subscriber_1 = require("./audit-log.subscriber");
|
|
19
|
+
const constants_1 = require("./constants");
|
|
20
|
+
/**
|
|
21
|
+
* Default module options.
|
|
22
|
+
*/
|
|
23
|
+
const DEFAULT_OPTIONS = {
|
|
24
|
+
storage: 'database',
|
|
25
|
+
retentionDays: 0,
|
|
26
|
+
excludeFields: [],
|
|
27
|
+
excludeEntities: [],
|
|
28
|
+
async: false,
|
|
29
|
+
batchSize: 1,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* AuditLogModule provides automatic audit logging for TypeORM entities.
|
|
33
|
+
*
|
|
34
|
+
* @example Basic usage
|
|
35
|
+
* ```typescript
|
|
36
|
+
* @Module({
|
|
37
|
+
* imports: [
|
|
38
|
+
* TypeOrmModule.forRoot({...}),
|
|
39
|
+
* AuditLogModule.forRoot({
|
|
40
|
+
* retentionDays: 90,
|
|
41
|
+
* excludeFields: ['password', 'token'],
|
|
42
|
+
* }),
|
|
43
|
+
* ],
|
|
44
|
+
* })
|
|
45
|
+
* export class AppModule implements NestModule {
|
|
46
|
+
* configure(consumer: MiddlewareConsumer) {
|
|
47
|
+
* consumer.apply(AuditContextMiddleware).forRoutes('*');
|
|
48
|
+
* }
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @example Async configuration
|
|
53
|
+
* ```typescript
|
|
54
|
+
* AuditLogModule.forRootAsync({
|
|
55
|
+
* imports: [ConfigModule],
|
|
56
|
+
* useFactory: (config: ConfigService) => ({
|
|
57
|
+
* retentionDays: config.get('AUDIT_RETENTION_DAYS'),
|
|
58
|
+
* async: config.get('AUDIT_ASYNC') === 'true',
|
|
59
|
+
* }),
|
|
60
|
+
* inject: [ConfigService],
|
|
61
|
+
* })
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
let AuditLogModule = AuditLogModule_1 = class AuditLogModule {
|
|
65
|
+
constructor(dataSource, subscriber, auditLogService) {
|
|
66
|
+
this.dataSource = dataSource;
|
|
67
|
+
this.subscriber = subscriber;
|
|
68
|
+
this.auditLogService = auditLogService;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Initialize the module: register subscriber with DataSource and inject service.
|
|
72
|
+
*/
|
|
73
|
+
onModuleInit() {
|
|
74
|
+
// Register the subscriber with TypeORM DataSource
|
|
75
|
+
this.subscriber.registerWithDataSource(this.dataSource);
|
|
76
|
+
// Inject the service into the subscriber
|
|
77
|
+
this.subscriber.setAuditLogService(this.auditLogService);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Configure the module with static options.
|
|
81
|
+
*/
|
|
82
|
+
static forRoot(options = {}) {
|
|
83
|
+
const mergedOptions = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
|
|
84
|
+
return {
|
|
85
|
+
module: AuditLogModule_1,
|
|
86
|
+
imports: [typeorm_1.TypeOrmModule.forFeature([audit_log_entity_1.AuditLog])],
|
|
87
|
+
providers: [
|
|
88
|
+
{
|
|
89
|
+
provide: constants_1.AUDIT_LOG_OPTIONS,
|
|
90
|
+
useValue: mergedOptions,
|
|
91
|
+
},
|
|
92
|
+
audit_log_service_1.AuditLogService,
|
|
93
|
+
audit_log_subscriber_1.AuditLogSubscriber,
|
|
94
|
+
],
|
|
95
|
+
exports: [audit_log_service_1.AuditLogService, audit_log_subscriber_1.AuditLogSubscriber],
|
|
96
|
+
global: true,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Configure the module with async options (factory, useClass, useExisting).
|
|
101
|
+
*/
|
|
102
|
+
static forRootAsync(options) {
|
|
103
|
+
var _a, _b;
|
|
104
|
+
return {
|
|
105
|
+
module: AuditLogModule_1,
|
|
106
|
+
imports: [...((_a = options.imports) !== null && _a !== void 0 ? _a : []), typeorm_1.TypeOrmModule.forFeature([audit_log_entity_1.AuditLog])],
|
|
107
|
+
providers: [
|
|
108
|
+
{
|
|
109
|
+
provide: constants_1.AUDIT_LOG_OPTIONS,
|
|
110
|
+
useFactory: async (...args) => {
|
|
111
|
+
const config = options.useFactory ? await options.useFactory(...args) : {};
|
|
112
|
+
return Object.assign(Object.assign({}, DEFAULT_OPTIONS), config);
|
|
113
|
+
},
|
|
114
|
+
inject: (_b = options.inject) !== null && _b !== void 0 ? _b : [],
|
|
115
|
+
},
|
|
116
|
+
audit_log_service_1.AuditLogService,
|
|
117
|
+
audit_log_subscriber_1.AuditLogSubscriber,
|
|
118
|
+
],
|
|
119
|
+
exports: [audit_log_service_1.AuditLogService, audit_log_subscriber_1.AuditLogSubscriber],
|
|
120
|
+
global: true,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
exports.AuditLogModule = AuditLogModule;
|
|
125
|
+
exports.AuditLogModule = AuditLogModule = AuditLogModule_1 = __decorate([
|
|
126
|
+
(0, common_1.Module)({}),
|
|
127
|
+
__metadata("design:paramtypes", [Function, audit_log_subscriber_1.AuditLogSubscriber,
|
|
128
|
+
audit_log_service_1.AuditLogService])
|
|
129
|
+
], AuditLogModule);
|
|
130
|
+
//# sourceMappingURL=audit-log.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-log.module.js","sourceRoot":"","sources":["../src/audit-log.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAA+E;AAC/E,6CAAgD;AAEhD,yDAA8C;AAC9C,2DAAsD;AACtD,iEAA4D;AAC5D,2CAAgD;AAGhD;;GAEG;AACH,MAAM,eAAe,GAA0B;IAC7C,OAAO,EAAE,UAAU;IACnB,aAAa,EAAE,CAAC;IAChB,aAAa,EAAE,EAAE;IACjB,eAAe,EAAE,EAAE;IACnB,KAAK,EAAE,KAAK;IACZ,SAAS,EAAE,CAAC;CACb,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEI,IAAM,cAAc,sBAApB,MAAM,cAAc;IACzB,YACmB,UAAsB,EACtB,UAA8B,EAC9B,eAAgC;QAFhC,eAAU,GAAV,UAAU,CAAY;QACtB,eAAU,GAAV,UAAU,CAAoB;QAC9B,oBAAe,GAAf,eAAe,CAAiB;IAChD,CAAC;IAEJ;;OAEG;IACH,YAAY;QACV,kDAAkD;QAClD,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxD,yCAAyC;QACzC,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC3D,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,UAAiC,EAAE;QAChD,MAAM,aAAa,mCAAQ,eAAe,GAAK,OAAO,CAAE,CAAC;QAEzD,OAAO;YACL,MAAM,EAAE,gBAAc;YACtB,OAAO,EAAE,CAAC,uBAAa,CAAC,UAAU,CAAC,CAAC,2BAAQ,CAAC,CAAC,CAAC;YAC/C,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,6BAAiB;oBAC1B,QAAQ,EAAE,aAAa;iBACxB;gBACD,mCAAe;gBACf,yCAAkB;aACnB;YACD,OAAO,EAAE,CAAC,mCAAe,EAAE,yCAAkB,CAAC;YAC9C,MAAM,EAAE,IAAI;SACb,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,OAAmC;;QACrD,OAAO;YACL,MAAM,EAAE,gBAAc;YACtB,OAAO,EAAE,CAAC,GAAG,CAAC,MAAA,OAAO,CAAC,OAAO,mCAAI,EAAE,CAAC,EAAE,uBAAa,CAAC,UAAU,CAAC,CAAC,2BAAQ,CAAC,CAAC,CAAC;YAC3E,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,6BAAiB;oBAC1B,UAAU,EAAE,KAAK,EAAE,GAAG,IAAe,EAAE,EAAE;wBACvC,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;wBAC3E,uCAAY,eAAe,GAAK,MAAM,EAAG;oBAC3C,CAAC;oBACD,MAAM,EAAE,MAAA,OAAO,CAAC,MAAM,mCAAI,EAAE;iBAC7B;gBACD,mCAAe;gBACf,yCAAkB;aACnB;YACD,OAAO,EAAE,CAAC,mCAAe,EAAE,yCAAkB,CAAC;YAC9C,MAAM,EAAE,IAAI;SACb,CAAC;IACJ,CAAC;CACF,CAAA;AA9DY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,eAAM,EAAC,EAAE,CAAC;+CAIsB,yCAAkB;QACb,mCAAe;GAJxC,cAAc,CA8D1B"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Repository } from 'typeorm';
|
|
2
|
+
import { AuditLog } from './audit-log.entity';
|
|
3
|
+
import type { IAuditLogService } from './audit-log.subscriber';
|
|
4
|
+
import type { AuditLogModuleOptions, FindAllOptions, FindByEntityOptions, FindByUserOptions, LogParams, PaginatedResult } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Service for logging and querying audit entries.
|
|
7
|
+
* Captures entity changes with user attribution, diff calculation,
|
|
8
|
+
* and request context (IP, user agent, metadata).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // Query audit history for an entity
|
|
13
|
+
* const history = await auditLogService.findByEntity('User', '123');
|
|
14
|
+
*
|
|
15
|
+
* // Query changes made by a specific user
|
|
16
|
+
* const userChanges = await auditLogService.findByUser('user-456', {
|
|
17
|
+
* since: new Date('2024-01-01'),
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare class AuditLogService implements IAuditLogService {
|
|
22
|
+
private readonly repo;
|
|
23
|
+
private readonly options;
|
|
24
|
+
constructor(repo: Repository<AuditLog>, options: AuditLogModuleOptions);
|
|
25
|
+
/**
|
|
26
|
+
* Log an audit entry for an entity change.
|
|
27
|
+
* Automatically captures user context from AsyncLocalStorage.
|
|
28
|
+
* Applies @AuditIgnore and @AuditMask decorators from the entity.
|
|
29
|
+
*/
|
|
30
|
+
log(params: LogParams): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Find audit logs for a specific entity.
|
|
33
|
+
*/
|
|
34
|
+
findByEntity(entityName: string, entityId: string, options?: FindByEntityOptions): Promise<AuditLog[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Find audit logs by user ID.
|
|
37
|
+
*/
|
|
38
|
+
findByUser(userId: string, options?: FindByUserOptions): Promise<AuditLog[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Find all audit logs with optional filters and pagination.
|
|
41
|
+
*/
|
|
42
|
+
findAll(options?: FindAllOptions): Promise<PaginatedResult<AuditLog>>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.AuditLogService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const typeorm_1 = require("@nestjs/typeorm");
|
|
18
|
+
const audit_context_1 = require("./audit-context");
|
|
19
|
+
const audit_log_entity_1 = require("./audit-log.entity");
|
|
20
|
+
const constants_1 = require("./constants");
|
|
21
|
+
const diff_1 = require("./utils/diff");
|
|
22
|
+
const mask_1 = require("./utils/mask");
|
|
23
|
+
/**
|
|
24
|
+
* Service for logging and querying audit entries.
|
|
25
|
+
* Captures entity changes with user attribution, diff calculation,
|
|
26
|
+
* and request context (IP, user agent, metadata).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* // Query audit history for an entity
|
|
31
|
+
* const history = await auditLogService.findByEntity('User', '123');
|
|
32
|
+
*
|
|
33
|
+
* // Query changes made by a specific user
|
|
34
|
+
* const userChanges = await auditLogService.findByUser('user-456', {
|
|
35
|
+
* since: new Date('2024-01-01'),
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
let AuditLogService = class AuditLogService {
|
|
40
|
+
constructor(repo, options) {
|
|
41
|
+
this.repo = repo;
|
|
42
|
+
this.options = options;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Log an audit entry for an entity change.
|
|
46
|
+
* Automatically captures user context from AsyncLocalStorage.
|
|
47
|
+
* Applies @AuditIgnore and @AuditMask decorators from the entity.
|
|
48
|
+
*/
|
|
49
|
+
async log(params) {
|
|
50
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
51
|
+
const context = (0, audit_context_1.getAuditContext)();
|
|
52
|
+
// Get entity class for decorator metadata
|
|
53
|
+
const entityClass = (_b = (_a = params.entity) === null || _a === void 0 ? void 0 : _a.constructor) !== null && _b !== void 0 ? _b : null;
|
|
54
|
+
// Get all excluded fields (global + @AuditIgnore)
|
|
55
|
+
const excludeFields = (0, mask_1.getAllExcludedFields)(entityClass, (_c = this.options.excludeFields) !== null && _c !== void 0 ? _c : []);
|
|
56
|
+
// Calculate diff between old and new values using deep-diff
|
|
57
|
+
const diff = (0, diff_1.calculateDiff)(params.oldValues, params.entity, excludeFields);
|
|
58
|
+
// Apply masking and exclusions to values
|
|
59
|
+
const oldValues = (0, mask_1.applyMaskAndExclusions)(params.oldValues, entityClass, (_d = this.options.excludeFields) !== null && _d !== void 0 ? _d : []);
|
|
60
|
+
const newValues = (0, mask_1.applyMaskAndExclusions)(params.entity, entityClass, (_e = this.options.excludeFields) !== null && _e !== void 0 ? _e : []);
|
|
61
|
+
// Create audit log entry
|
|
62
|
+
const entry = this.repo.create({
|
|
63
|
+
entityName: params.entityName,
|
|
64
|
+
entityId: params.entityId,
|
|
65
|
+
action: params.action,
|
|
66
|
+
userId: (_f = context === null || context === void 0 ? void 0 : context.userId) !== null && _f !== void 0 ? _f : null,
|
|
67
|
+
userName: (_g = context === null || context === void 0 ? void 0 : context.userName) !== null && _g !== void 0 ? _g : null,
|
|
68
|
+
oldValues,
|
|
69
|
+
newValues,
|
|
70
|
+
diff,
|
|
71
|
+
ip: (_h = context === null || context === void 0 ? void 0 : context.ip) !== null && _h !== void 0 ? _h : null,
|
|
72
|
+
userAgent: (_j = context === null || context === void 0 ? void 0 : context.userAgent) !== null && _j !== void 0 ? _j : null,
|
|
73
|
+
metadata: (_k = context === null || context === void 0 ? void 0 : context.metadata) !== null && _k !== void 0 ? _k : null,
|
|
74
|
+
});
|
|
75
|
+
// Save the audit log
|
|
76
|
+
if (this.options.async) {
|
|
77
|
+
// Fire and forget for async mode
|
|
78
|
+
this.repo.save(entry).catch((error) => {
|
|
79
|
+
console.error('[AuditLogService] Failed to save audit log:', error);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
await this.repo.save(entry);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Find audit logs for a specific entity.
|
|
88
|
+
*/
|
|
89
|
+
async findByEntity(entityName, entityId, options) {
|
|
90
|
+
const query = this.repo
|
|
91
|
+
.createQueryBuilder('audit')
|
|
92
|
+
.where('audit.entityName = :entityName', { entityName })
|
|
93
|
+
.andWhere('audit.entityId = :entityId', { entityId });
|
|
94
|
+
if (options === null || options === void 0 ? void 0 : options.since) {
|
|
95
|
+
query.andWhere('audit.timestamp >= :since', { since: options.since });
|
|
96
|
+
}
|
|
97
|
+
if (options === null || options === void 0 ? void 0 : options.until) {
|
|
98
|
+
query.andWhere('audit.timestamp <= :until', { until: options.until });
|
|
99
|
+
}
|
|
100
|
+
if (options === null || options === void 0 ? void 0 : options.action) {
|
|
101
|
+
query.andWhere('audit.action = :action', { action: options.action });
|
|
102
|
+
}
|
|
103
|
+
query.orderBy('audit.timestamp', 'DESC');
|
|
104
|
+
if (options === null || options === void 0 ? void 0 : options.limit) {
|
|
105
|
+
query.take(options.limit);
|
|
106
|
+
}
|
|
107
|
+
return query.getMany();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Find audit logs by user ID.
|
|
111
|
+
*/
|
|
112
|
+
async findByUser(userId, options) {
|
|
113
|
+
const query = this.repo.createQueryBuilder('audit').where('audit.userId = :userId', { userId });
|
|
114
|
+
if (options === null || options === void 0 ? void 0 : options.since) {
|
|
115
|
+
query.andWhere('audit.timestamp >= :since', { since: options.since });
|
|
116
|
+
}
|
|
117
|
+
if (options === null || options === void 0 ? void 0 : options.until) {
|
|
118
|
+
query.andWhere('audit.timestamp <= :until', { until: options.until });
|
|
119
|
+
}
|
|
120
|
+
if (options === null || options === void 0 ? void 0 : options.entityName) {
|
|
121
|
+
query.andWhere('audit.entityName = :entityName', { entityName: options.entityName });
|
|
122
|
+
}
|
|
123
|
+
if (options === null || options === void 0 ? void 0 : options.action) {
|
|
124
|
+
query.andWhere('audit.action = :action', { action: options.action });
|
|
125
|
+
}
|
|
126
|
+
query.orderBy('audit.timestamp', 'DESC');
|
|
127
|
+
if (options === null || options === void 0 ? void 0 : options.limit) {
|
|
128
|
+
query.take(options.limit);
|
|
129
|
+
}
|
|
130
|
+
return query.getMany();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Find all audit logs with optional filters and pagination.
|
|
134
|
+
*/
|
|
135
|
+
async findAll(options) {
|
|
136
|
+
var _a, _b;
|
|
137
|
+
const page = (_a = options === null || options === void 0 ? void 0 : options.page) !== null && _a !== void 0 ? _a : 1;
|
|
138
|
+
const limit = (_b = options === null || options === void 0 ? void 0 : options.limit) !== null && _b !== void 0 ? _b : 50;
|
|
139
|
+
const skip = (page - 1) * limit;
|
|
140
|
+
const query = this.repo.createQueryBuilder('audit');
|
|
141
|
+
if (options === null || options === void 0 ? void 0 : options.since) {
|
|
142
|
+
query.andWhere('audit.timestamp >= :since', { since: options.since });
|
|
143
|
+
}
|
|
144
|
+
if (options === null || options === void 0 ? void 0 : options.until) {
|
|
145
|
+
query.andWhere('audit.timestamp <= :until', { until: options.until });
|
|
146
|
+
}
|
|
147
|
+
if (options === null || options === void 0 ? void 0 : options.entityName) {
|
|
148
|
+
query.andWhere('audit.entityName = :entityName', { entityName: options.entityName });
|
|
149
|
+
}
|
|
150
|
+
if (options === null || options === void 0 ? void 0 : options.entityId) {
|
|
151
|
+
query.andWhere('audit.entityId = :entityId', { entityId: options.entityId });
|
|
152
|
+
}
|
|
153
|
+
if (options === null || options === void 0 ? void 0 : options.userId) {
|
|
154
|
+
query.andWhere('audit.userId = :userId', { userId: options.userId });
|
|
155
|
+
}
|
|
156
|
+
if (options === null || options === void 0 ? void 0 : options.action) {
|
|
157
|
+
query.andWhere('audit.action = :action', { action: options.action });
|
|
158
|
+
}
|
|
159
|
+
query.orderBy('audit.timestamp', 'DESC');
|
|
160
|
+
const [items, total] = await query.skip(skip).take(limit).getManyAndCount();
|
|
161
|
+
return {
|
|
162
|
+
items,
|
|
163
|
+
total,
|
|
164
|
+
page,
|
|
165
|
+
limit,
|
|
166
|
+
totalPages: Math.ceil(total / limit),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
exports.AuditLogService = AuditLogService;
|
|
171
|
+
exports.AuditLogService = AuditLogService = __decorate([
|
|
172
|
+
(0, common_1.Injectable)(),
|
|
173
|
+
__param(0, (0, typeorm_1.InjectRepository)(audit_log_entity_1.AuditLog)),
|
|
174
|
+
__param(1, (0, common_1.Inject)(constants_1.AUDIT_LOG_OPTIONS)),
|
|
175
|
+
__metadata("design:paramtypes", [Function, Object])
|
|
176
|
+
], AuditLogService);
|
|
177
|
+
//# sourceMappingURL=audit-log.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-log.service.js","sourceRoot":"","sources":["../src/audit-log.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { DataSource, EntitySubscriberInterface, InsertEvent, RemoveEvent, UpdateEvent } from 'typeorm';
|
|
2
|
+
import { type LogParams } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Interface for the audit log service that the subscriber depends on.
|
|
5
|
+
* Allows for loose coupling and easier testing.
|
|
6
|
+
*/
|
|
7
|
+
export interface IAuditLogService {
|
|
8
|
+
log(params: LogParams): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* TypeORM event subscriber that captures entity changes and logs them.
|
|
12
|
+
* Automatically detects entities decorated with @Auditable() and creates
|
|
13
|
+
* audit log entries for INSERT, UPDATE, and DELETE operations.
|
|
14
|
+
*
|
|
15
|
+
* This subscriber is designed to be instantiated by NestJS DI and then
|
|
16
|
+
* registered with the TypeORM DataSource.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // In your module
|
|
21
|
+
* @Module({
|
|
22
|
+
* providers: [
|
|
23
|
+
* {
|
|
24
|
+
* provide: AuditLogSubscriber,
|
|
25
|
+
* useFactory: (dataSource: DataSource, auditLogService: AuditLogService) => {
|
|
26
|
+
* const subscriber = new AuditLogSubscriber(auditLogService);
|
|
27
|
+
* dataSource.subscribers.push(subscriber);
|
|
28
|
+
* return subscriber;
|
|
29
|
+
* },
|
|
30
|
+
* inject: [DataSource, AuditLogService],
|
|
31
|
+
* },
|
|
32
|
+
* ],
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare class AuditLogSubscriber implements EntitySubscriberInterface {
|
|
37
|
+
private auditLogService;
|
|
38
|
+
/**
|
|
39
|
+
* Set the audit log service. Called during module initialization.
|
|
40
|
+
*/
|
|
41
|
+
setAuditLogService(service: IAuditLogService): void;
|
|
42
|
+
/**
|
|
43
|
+
* Register this subscriber with a TypeORM DataSource.
|
|
44
|
+
*/
|
|
45
|
+
registerWithDataSource(dataSource: DataSource): void;
|
|
46
|
+
/**
|
|
47
|
+
* Handle entity insertion events.
|
|
48
|
+
*/
|
|
49
|
+
afterInsert(event: InsertEvent<unknown>): void;
|
|
50
|
+
/**
|
|
51
|
+
* Handle entity update events.
|
|
52
|
+
*/
|
|
53
|
+
afterUpdate(event: UpdateEvent<unknown>): void;
|
|
54
|
+
/**
|
|
55
|
+
* Handle entity removal events.
|
|
56
|
+
*/
|
|
57
|
+
afterRemove(event: RemoveEvent<unknown>): void;
|
|
58
|
+
/**
|
|
59
|
+
* Common handler for all entity events.
|
|
60
|
+
*/
|
|
61
|
+
private handleEvent;
|
|
62
|
+
/**
|
|
63
|
+
* Extract the entity ID from the event or entity.
|
|
64
|
+
*/
|
|
65
|
+
private getEntityId;
|
|
66
|
+
}
|