@studiosonrai/nestjs-audit-log 1.0.0 → 1.1.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/README.md +8 -4
- package/dist/src/audit-table.initializer.d.ts +12 -0
- package/dist/src/audit-table.initializer.js +89 -0
- package/dist/src/audit-table.initializer.js.map +1 -0
- package/dist/src/audit.logger.d.ts +3 -0
- package/dist/src/audit.logger.js +22 -0
- package/dist/src/audit.logger.js.map +1 -0
- package/dist/src/audit.module.js +3 -6
- package/dist/src/audit.module.js.map +1 -1
- package/dist/src/audit.service.d.ts +4 -3
- package/dist/src/audit.service.js +96 -50
- package/dist/src/audit.service.js.map +1 -1
- package/dist/src/audit.subscriber.d.ts +7 -2
- package/dist/src/audit.subscriber.js +54 -9
- package/dist/src/audit.subscriber.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/interfaces/audit.interfaces.d.ts +5 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/src/audit.builder.d.ts +0 -3
- package/dist/src/audit.builder.js +0 -15
- package/dist/src/audit.builder.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# @studiosonrai/nestjs-audit-log
|
|
2
2
|
|
|
3
|
-
Audit logging for NestJS + TypeORM. Mark an entity `@Auditable()` and CREATE/UPDATE
|
|
4
|
-
are recorded automatically by a TypeORM subscriber — no per-service code
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Audit logging for NestJS + TypeORM. Mark an entity `@Auditable()` and CREATE / UPDATE /
|
|
4
|
+
DELETE are recorded automatically by a TypeORM subscriber — no per-service code — for
|
|
5
|
+
`repository.save()`, `repository.remove()`, and `repository.softRemove()`. For legacy
|
|
6
|
+
`repository.delete(id)` paths there's a single-line `AuditService.logEntity()` escape
|
|
7
|
+
hatch. The acting user is captured from the request via CLS, so you never thread
|
|
8
|
+
`userId` through your services. The `auditLog` table is created at startup and read/written
|
|
9
|
+
via raw SQL through your existing DataSource, so **no migration is required and you do
|
|
10
|
+
not need to add `AuditLog` to your `entities` array** (Microsoft SQL Server).
|
|
7
11
|
|
|
8
12
|
## Documentation
|
|
9
13
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { DataSource } from 'typeorm';
|
|
3
|
+
import { AuditModuleOptions } from './interfaces/audit.interfaces';
|
|
4
|
+
export declare class AuditTableInitializer implements OnModuleInit {
|
|
5
|
+
private readonly dataSource;
|
|
6
|
+
private readonly options;
|
|
7
|
+
private static readonly TABLE;
|
|
8
|
+
private readonly logger;
|
|
9
|
+
constructor(dataSource: DataSource, options: AuditModuleOptions);
|
|
10
|
+
onModuleInit(): Promise<void>;
|
|
11
|
+
private buildCreateTableSql;
|
|
12
|
+
}
|
|
@@ -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
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var AuditTableInitializer_1;
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.AuditTableInitializer = void 0;
|
|
17
|
+
const common_1 = require("@nestjs/common");
|
|
18
|
+
const typeorm_1 = require("@nestjs/typeorm");
|
|
19
|
+
const typeorm_2 = require("typeorm");
|
|
20
|
+
const audit_logger_1 = require("./audit.logger");
|
|
21
|
+
const audit_constants_1 = require("./audit.constants");
|
|
22
|
+
let AuditTableInitializer = class AuditTableInitializer {
|
|
23
|
+
static { AuditTableInitializer_1 = this; }
|
|
24
|
+
dataSource;
|
|
25
|
+
options;
|
|
26
|
+
static TABLE = 'auditLog';
|
|
27
|
+
logger;
|
|
28
|
+
constructor(dataSource, options) {
|
|
29
|
+
this.dataSource = dataSource;
|
|
30
|
+
this.options = options;
|
|
31
|
+
this.logger = (0, audit_logger_1.resolveLogger)(options, AuditTableInitializer_1.name);
|
|
32
|
+
}
|
|
33
|
+
async onModuleInit() {
|
|
34
|
+
if (this.options.autoCreateTable === false) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const driverType = this.dataSource.options.type;
|
|
38
|
+
if (driverType !== 'mssql') {
|
|
39
|
+
this.logger.warn(`Skipping auditLog table creation: this package targets Microsoft SQL Server (got driver "${driverType}"). Create the table manually via a migration.`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!this.dataSource.isInitialized) {
|
|
43
|
+
await this.dataSource.initialize();
|
|
44
|
+
}
|
|
45
|
+
const schema = this.options.schema ?? 'dbo';
|
|
46
|
+
const fullName = `[${schema}].[${AuditTableInitializer_1.TABLE}]`;
|
|
47
|
+
try {
|
|
48
|
+
await this.dataSource.query(this.buildCreateTableSql(schema, fullName));
|
|
49
|
+
this.logger.log(`Ensured auditLog table exists at ${fullName}`);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
this.logger.error(`Failed to ensure auditLog table at ${fullName}. ` +
|
|
53
|
+
`Set AuditModuleOptions.autoCreateTable: false to manage the schema manually.`, error instanceof Error ? error.stack : String(error));
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
buildCreateTableSql(schema, fullName) {
|
|
58
|
+
return `
|
|
59
|
+
IF NOT EXISTS (
|
|
60
|
+
SELECT 1 FROM sys.objects
|
|
61
|
+
WHERE object_id = OBJECT_ID(N'${fullName}') AND type = 'U'
|
|
62
|
+
)
|
|
63
|
+
BEGIN
|
|
64
|
+
CREATE TABLE ${fullName} (
|
|
65
|
+
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
|
|
66
|
+
entityType NVARCHAR(255) NOT NULL,
|
|
67
|
+
entityId INT NOT NULL,
|
|
68
|
+
action NVARCHAR(50) NOT NULL DEFAULT 'CREATE',
|
|
69
|
+
changes NVARCHAR(MAX) NULL,
|
|
70
|
+
entityName NVARCHAR(500) NULL,
|
|
71
|
+
changedBy INT NULL,
|
|
72
|
+
changedAt DATETIME2 NOT NULL DEFAULT GETDATE(),
|
|
73
|
+
INDEX IX_${AuditTableInitializer_1.TABLE}_entityType_entityId (entityType, entityId),
|
|
74
|
+
INDEX IX_${AuditTableInitializer_1.TABLE}_changedBy (changedBy),
|
|
75
|
+
INDEX IX_${AuditTableInitializer_1.TABLE}_action (action),
|
|
76
|
+
INDEX IX_${AuditTableInitializer_1.TABLE}_changedAt (changedAt DESC)
|
|
77
|
+
);
|
|
78
|
+
END
|
|
79
|
+
`;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
exports.AuditTableInitializer = AuditTableInitializer;
|
|
83
|
+
exports.AuditTableInitializer = AuditTableInitializer = AuditTableInitializer_1 = __decorate([
|
|
84
|
+
(0, common_1.Injectable)(),
|
|
85
|
+
__param(0, (0, typeorm_1.InjectDataSource)()),
|
|
86
|
+
__param(1, (0, common_1.Inject)(audit_constants_1.AUDIT_MODULE_OPTIONS)),
|
|
87
|
+
__metadata("design:paramtypes", [typeorm_2.DataSource, Object])
|
|
88
|
+
], AuditTableInitializer);
|
|
89
|
+
//# sourceMappingURL=audit-table.initializer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-table.initializer.js","sourceRoot":"","sources":["../../src/audit-table.initializer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAKwB;AACxB,6CAAmD;AACnD,qCAAqC;AACrC,iDAA+C;AAC/C,uDAAyD;AAgBlD,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;;IAKO;IAEpB;IANX,MAAM,CAAU,KAAK,GAAG,UAAU,CAAC;IAC1B,MAAM,CAAgB;IAEvC,YACuC,UAAsB,EAE1C,OAA2B;QAFP,eAAU,GAAV,UAAU,CAAY;QAE1C,YAAO,GAAP,OAAO,CAAoB;QAE5C,IAAI,CAAC,MAAM,GAAG,IAAA,4BAAa,EAAC,OAAO,EAAE,uBAAqB,CAAC,IAAI,CAAC,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,IAAI,IAAI,CAAC,OAAO,CAAC,eAAe,KAAK,KAAK,EAAE,CAAC;YAC3C,OAAO;QACT,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;QAChD,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,4FAA4F,UAAU,gDAAgD,CACvJ,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC;YACnC,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;QACrC,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;QAC5C,MAAM,QAAQ,GAAG,IAAI,MAAM,MAAM,uBAAqB,CAAC,KAAK,GAAG,CAAC;QAEhE,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;YACxE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,oCAAoC,QAAQ,EAAE,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,sCAAsC,QAAQ,IAAI;gBAChD,8EAA8E,EAChF,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACrD,CAAC;YACF,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,mBAAmB,CAAC,MAAc,EAAE,QAAgB;QAC1D,OAAO;;;wCAG6B,QAAQ;;;uBAGzB,QAAQ;;;;;;;;;qBASV,uBAAqB,CAAC,KAAK;qBAC3B,uBAAqB,CAAC,KAAK;qBAC3B,uBAAqB,CAAC,KAAK;qBAC3B,uBAAqB,CAAC,KAAK;;;KAG3C,CAAC;IACJ,CAAC;;AApEU,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;IAMR,WAAA,IAAA,0BAAgB,GAAE,CAAA;IAClB,WAAA,IAAA,eAAM,EAAC,sCAAoB,CAAC,CAAA;qCADoB,oBAAU;GALlD,qBAAqB,CAqEjC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveLogger = resolveLogger;
|
|
4
|
+
class NoopLogger {
|
|
5
|
+
log() {
|
|
6
|
+
}
|
|
7
|
+
error() {
|
|
8
|
+
}
|
|
9
|
+
warn() {
|
|
10
|
+
}
|
|
11
|
+
debug() {
|
|
12
|
+
}
|
|
13
|
+
verbose() {
|
|
14
|
+
}
|
|
15
|
+
fatal() {
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const NOOP_LOGGER = new NoopLogger();
|
|
19
|
+
function resolveLogger(options, context) {
|
|
20
|
+
return options.logger ? options.logger(context) : NOOP_LOGGER;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=audit.logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.logger.js","sourceRoot":"","sources":["../../src/audit.logger.ts"],"names":[],"mappings":";;AAoCA,sCAKC;AAhCD,MAAM,UAAU;IACd,GAAG;IAEH,CAAC;IACD,KAAK;IAEL,CAAC;IACD,IAAI;IAEJ,CAAC;IACD,KAAK;IAEL,CAAC;IACD,OAAO;IAEP,CAAC;IACD,KAAK;IAEL,CAAC;CACF;AAED,MAAM,WAAW,GAAkB,IAAI,UAAU,EAAE,CAAC;AAMpD,SAAgB,aAAa,CAC3B,OAA2B,EAC3B,OAAe;IAEf,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;AAChE,CAAC"}
|
package/dist/src/audit.module.js
CHANGED
|
@@ -10,14 +10,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.AuditModule = void 0;
|
|
11
11
|
const common_1 = require("@nestjs/common");
|
|
12
12
|
const core_1 = require("@nestjs/core");
|
|
13
|
-
const typeorm_1 = require("@nestjs/typeorm");
|
|
14
13
|
const nestjs_cls_1 = require("nestjs-cls");
|
|
15
|
-
const audit_log_entity_1 = require("./entities/audit-log.entity");
|
|
16
14
|
const audit_service_1 = require("./audit.service");
|
|
17
15
|
const audit_subscriber_1 = require("./audit.subscriber");
|
|
18
16
|
const audit_context_interceptor_1 = require("./audit-context.interceptor");
|
|
17
|
+
const audit_table_initializer_1 = require("./audit-table.initializer");
|
|
19
18
|
const audit_constants_1 = require("./audit.constants");
|
|
20
19
|
const sharedProviders = [
|
|
20
|
+
audit_table_initializer_1.AuditTableInitializer,
|
|
21
21
|
audit_service_1.AuditService,
|
|
22
22
|
audit_subscriber_1.AuditSubscriber,
|
|
23
23
|
{ provide: core_1.APP_INTERCEPTOR, useClass: audit_context_interceptor_1.AuditContextInterceptor },
|
|
@@ -46,10 +46,7 @@ let AuditModule = AuditModule_1 = class AuditModule {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
static baseImports() {
|
|
49
|
-
return [
|
|
50
|
-
nestjs_cls_1.ClsModule.forRoot({ global: true, guard: { mount: true } }),
|
|
51
|
-
typeorm_1.TypeOrmModule.forFeature([audit_log_entity_1.AuditLog]),
|
|
52
|
-
];
|
|
49
|
+
return [nestjs_cls_1.ClsModule.forRoot({ global: true, middleware: { mount: true } })];
|
|
53
50
|
}
|
|
54
51
|
static createAsyncProviders(options) {
|
|
55
52
|
if (options.useExisting || options.useFactory) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.module.js","sourceRoot":"","sources":["../../src/audit.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAyE;AACzE,uCAA+C;AAC/C,
|
|
1
|
+
{"version":3,"file":"audit.module.js","sourceRoot":"","sources":["../../src/audit.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAyE;AACzE,uCAA+C;AAC/C,2CAAuC;AACvC,mDAA+C;AAC/C,yDAAqD;AACrD,2EAAsE;AACtE,uEAAkE;AAClE,uDAAyD;AAOzD,MAAM,eAAe,GAAe;IAClC,+CAAqB;IACrB,4BAAY;IACZ,kCAAe;IACf,EAAE,OAAO,EAAE,sBAAe,EAAE,QAAQ,EAAE,mDAAuB,EAAE;CAChE,CAAC;AAIK,IAAM,WAAW,mBAAjB,MAAM,WAAW;IACtB,MAAM,CAAC,OAAO,CAAC,UAA8B,EAAE;QAC7C,OAAO;YACL,MAAM,EAAE,aAAW;YACnB,OAAO,EAAE,CAAC,GAAG,aAAW,CAAC,WAAW,EAAE,CAAC;YACvC,SAAS,EAAE;gBACT,EAAE,OAAO,EAAE,sCAAoB,EAAE,QAAQ,EAAE,OAAO,EAAE;gBACpD,GAAG,eAAe;aACnB;YACD,OAAO,EAAE,CAAC,4BAAY,EAAE,sCAAoB,CAAC;SAC9C,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,OAAgC;QAClD,OAAO;YACL,MAAM,EAAE,aAAW;YACnB,OAAO,EAAE,CAAC,GAAG,aAAW,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YACnE,SAAS,EAAE;gBACT,GAAG,aAAW,CAAC,oBAAoB,CAAC,OAAO,CAAC;gBAC5C,GAAG,eAAe;aACnB;YACD,OAAO,EAAE,CAAC,4BAAY,EAAE,sCAAoB,CAAC;SAC9C,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,WAAW;QACxB,OAAO,CAAC,sBAAS,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5E,CAAC;IAEO,MAAM,CAAC,oBAAoB,CACjC,OAAgC;QAEhC,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YAC9C,OAAO,CAAC,aAAW,CAAC,0BAA0B,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,OAAO;gBACL,aAAW,CAAC,0BAA0B,CAAC,OAAO,CAAC;gBAC/C,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE;aAC1D,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,0BAA0B,CACvC,OAAgC;QAEhC,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,OAAO;gBACL,OAAO,EAAE,sCAAoB;gBAC7B,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;aAC7B,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC;QACvD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,+EAA+E,CAChF,CAAC;QACJ,CAAC;QAED,OAAO;YACL,OAAO,EAAE,sCAAoB;YAC7B,UAAU,EAAE,CAAC,OAAkC,EAAE,EAAE,CACjD,OAAO,CAAC,kBAAkB,EAAE;YAC9B,MAAM,EAAE,CAAC,MAAM,CAAC;SACjB,CAAC;IACJ,CAAC;CACF,CAAA;AAzEY,kCAAW;sBAAX,WAAW;IAFvB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,WAAW,CAyEvB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DataSource } from 'typeorm';
|
|
2
2
|
import { ClsService } from 'nestjs-cls';
|
|
3
3
|
import { AuditLog, AuditActionType } from './entities/audit-log.entity';
|
|
4
4
|
import { AuditFilterDto } from './dto/audit-filter.dto';
|
|
@@ -6,11 +6,12 @@ import { AuditLogResponseDto } from './dto/audit-log-response.dto';
|
|
|
6
6
|
import { PaginatedResultDto } from './dto/paginated-result.dto';
|
|
7
7
|
import { AuditLogInput, AuditModuleOptions } from './interfaces/audit.interfaces';
|
|
8
8
|
export declare class AuditService {
|
|
9
|
-
private readonly
|
|
9
|
+
private readonly dataSource;
|
|
10
10
|
private readonly options;
|
|
11
11
|
private readonly cls?;
|
|
12
12
|
private readonly logger;
|
|
13
|
-
|
|
13
|
+
private readonly tableName;
|
|
14
|
+
constructor(dataSource: DataSource, options: AuditModuleOptions, cls?: ClsService | undefined);
|
|
14
15
|
private currentUserId;
|
|
15
16
|
private enrichAuditLog;
|
|
16
17
|
log(input: AuditLogInput): Promise<AuditLog | undefined>;
|
|
@@ -18,51 +18,66 @@ const common_1 = require("@nestjs/common");
|
|
|
18
18
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
19
19
|
const typeorm_2 = require("typeorm");
|
|
20
20
|
const nestjs_cls_1 = require("nestjs-cls");
|
|
21
|
-
const audit_log_entity_1 = require("./entities/audit-log.entity");
|
|
22
21
|
const audit_helper_1 = require("./audit.helper");
|
|
23
|
-
const audit_builder_1 = require("./audit.builder");
|
|
24
22
|
const auditable_decorator_1 = require("./decorators/auditable.decorator");
|
|
23
|
+
const audit_logger_1 = require("./audit.logger");
|
|
25
24
|
const audit_constants_1 = require("./audit.constants");
|
|
25
|
+
const SELECT_COLUMNS = 'id, entityType, entityId, action, changes, entityName, changedBy, changedAt';
|
|
26
26
|
let AuditService = AuditService_1 = class AuditService {
|
|
27
|
-
|
|
27
|
+
dataSource;
|
|
28
28
|
options;
|
|
29
29
|
cls;
|
|
30
|
-
logger
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
logger;
|
|
31
|
+
tableName;
|
|
32
|
+
constructor(dataSource, options, cls) {
|
|
33
|
+
this.dataSource = dataSource;
|
|
33
34
|
this.options = options;
|
|
34
35
|
this.cls = cls;
|
|
36
|
+
const schema = options.schema ?? 'dbo';
|
|
37
|
+
this.tableName = `[${schema}].[auditLog]`;
|
|
38
|
+
this.logger = (0, audit_logger_1.resolveLogger)(options, AuditService_1.name);
|
|
35
39
|
}
|
|
36
40
|
currentUserId() {
|
|
37
41
|
return this.cls?.get(audit_constants_1.AUDIT_CLS_USER_ID);
|
|
38
42
|
}
|
|
39
|
-
async enrichAuditLog(
|
|
43
|
+
async enrichAuditLog(row) {
|
|
40
44
|
const response = {
|
|
41
|
-
...
|
|
45
|
+
...row,
|
|
42
46
|
changedByName: undefined,
|
|
43
47
|
changedByEmail: undefined,
|
|
44
48
|
};
|
|
45
|
-
if (
|
|
49
|
+
if (row.changedBy && this.options.resolveUser) {
|
|
46
50
|
try {
|
|
47
|
-
const user = await this.options.resolveUser(
|
|
51
|
+
const user = await this.options.resolveUser(row.changedBy);
|
|
48
52
|
if (user) {
|
|
49
53
|
response.changedByName = user.name;
|
|
50
54
|
response.changedByEmail = user.email;
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
catch (error) {
|
|
54
|
-
this.logger.error(`Failed to enrich user data for audit log ${
|
|
58
|
+
this.logger.error(`Failed to enrich user data for audit log ${row.id}`, String(error));
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
return response;
|
|
58
62
|
}
|
|
59
63
|
async log(input) {
|
|
60
64
|
try {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const changedBy = input.changedBy ?? this.currentUserId();
|
|
66
|
+
const changes = input.changes ? JSON.stringify(input.changes) : null;
|
|
67
|
+
const sql = `
|
|
68
|
+
INSERT INTO ${this.tableName} (entityType, entityId, action, changes, entityName, changedBy)
|
|
69
|
+
OUTPUT INSERTED.${SELECT_COLUMNS.split(', ').join(', INSERTED.')}
|
|
70
|
+
VALUES (@0, @1, @2, @3, @4, @5)
|
|
71
|
+
`;
|
|
72
|
+
const rows = await this.dataSource.query(sql, [
|
|
73
|
+
input.entityType,
|
|
74
|
+
input.entityId,
|
|
75
|
+
input.action,
|
|
76
|
+
changes,
|
|
77
|
+
input.entityName ?? null,
|
|
78
|
+
changedBy ?? null,
|
|
79
|
+
]);
|
|
80
|
+
const saved = rows[0];
|
|
66
81
|
this.logger.log(`Audit: ${saved.entityType} ${saved.action} id=${saved.entityId} (log #${saved.id})`);
|
|
67
82
|
return saved;
|
|
68
83
|
}
|
|
@@ -92,11 +107,17 @@ let AuditService = AuditService_1 = class AuditService {
|
|
|
92
107
|
}
|
|
93
108
|
async findByEntity(entityType, entityId) {
|
|
94
109
|
try {
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
110
|
+
const sql = `
|
|
111
|
+
SELECT ${SELECT_COLUMNS}
|
|
112
|
+
FROM ${this.tableName}
|
|
113
|
+
WHERE entityType = @0 AND entityId = @1
|
|
114
|
+
ORDER BY changedAt DESC, id DESC
|
|
115
|
+
`;
|
|
116
|
+
const rows = await this.dataSource.query(sql, [
|
|
117
|
+
entityType,
|
|
118
|
+
entityId,
|
|
119
|
+
]);
|
|
120
|
+
return Promise.all(rows.map((row) => this.enrichAuditLog(row)));
|
|
100
121
|
}
|
|
101
122
|
catch (error) {
|
|
102
123
|
this.logger.error(`Error fetching audit logs for ${entityType} ${entityId}`, String(error));
|
|
@@ -105,11 +126,14 @@ let AuditService = AuditService_1 = class AuditService {
|
|
|
105
126
|
}
|
|
106
127
|
async findByUser(userId) {
|
|
107
128
|
try {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
129
|
+
const sql = `
|
|
130
|
+
SELECT ${SELECT_COLUMNS}
|
|
131
|
+
FROM ${this.tableName}
|
|
132
|
+
WHERE changedBy = @0
|
|
133
|
+
ORDER BY changedAt DESC, id DESC
|
|
134
|
+
`;
|
|
135
|
+
const rows = await this.dataSource.query(sql, [userId]);
|
|
136
|
+
return Promise.all(rows.map((row) => this.enrichAuditLog(row)));
|
|
113
137
|
}
|
|
114
138
|
catch (error) {
|
|
115
139
|
this.logger.error(`Error fetching audit logs for user ${userId}`, String(error));
|
|
@@ -118,11 +142,14 @@ let AuditService = AuditService_1 = class AuditService {
|
|
|
118
142
|
}
|
|
119
143
|
async findByAction(action) {
|
|
120
144
|
try {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
145
|
+
const sql = `
|
|
146
|
+
SELECT ${SELECT_COLUMNS}
|
|
147
|
+
FROM ${this.tableName}
|
|
148
|
+
WHERE action = @0
|
|
149
|
+
ORDER BY changedAt DESC, id DESC
|
|
150
|
+
`;
|
|
151
|
+
const rows = await this.dataSource.query(sql, [action]);
|
|
152
|
+
return Promise.all(rows.map((row) => this.enrichAuditLog(row)));
|
|
126
153
|
}
|
|
127
154
|
catch (error) {
|
|
128
155
|
this.logger.error(`Error fetching audit logs for action ${action}`, String(error));
|
|
@@ -132,34 +159,53 @@ let AuditService = AuditService_1 = class AuditService {
|
|
|
132
159
|
async findAll(filterDto) {
|
|
133
160
|
try {
|
|
134
161
|
const { page = 0, limit = 10, entityType, entityId, changedBy, action, search, } = filterDto;
|
|
135
|
-
const
|
|
162
|
+
const conditions = [];
|
|
163
|
+
const params = [];
|
|
136
164
|
if (search) {
|
|
137
|
-
const
|
|
138
|
-
|
|
165
|
+
const idx = params.length;
|
|
166
|
+
conditions.push(`(LOWER(entityType) LIKE @${idx} OR LOWER(ISNULL(entityName, '')) LIKE @${idx})`);
|
|
167
|
+
params.push(`%${search.toLowerCase()}%`);
|
|
139
168
|
}
|
|
140
169
|
if (entityType) {
|
|
141
|
-
|
|
170
|
+
conditions.push(`entityType = @${params.length}`);
|
|
171
|
+
params.push(entityType);
|
|
142
172
|
}
|
|
143
173
|
if (entityId) {
|
|
144
|
-
|
|
174
|
+
conditions.push(`entityId = @${params.length}`);
|
|
175
|
+
params.push(entityId);
|
|
145
176
|
}
|
|
146
177
|
if (changedBy) {
|
|
147
|
-
|
|
178
|
+
conditions.push(`changedBy = @${params.length}`);
|
|
179
|
+
params.push(changedBy);
|
|
148
180
|
}
|
|
149
181
|
if (action) {
|
|
150
|
-
|
|
182
|
+
conditions.push(`action = @${params.length}`);
|
|
183
|
+
params.push(action);
|
|
151
184
|
}
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
const
|
|
185
|
+
const where = conditions.length
|
|
186
|
+
? `WHERE ${conditions.join(' AND ')}`
|
|
187
|
+
: '';
|
|
188
|
+
const countRows = await this.dataSource.query(`SELECT COUNT(*) AS total FROM ${this.tableName} ${where}`, params);
|
|
189
|
+
const total = Number(countRows[0]?.total ?? 0);
|
|
190
|
+
const offset = page * limit;
|
|
191
|
+
const offsetIdx = params.length;
|
|
192
|
+
const limitIdx = params.length + 1;
|
|
193
|
+
const dataSql = `
|
|
194
|
+
SELECT ${SELECT_COLUMNS}
|
|
195
|
+
FROM ${this.tableName}
|
|
196
|
+
${where}
|
|
197
|
+
ORDER BY changedAt DESC, id DESC
|
|
198
|
+
OFFSET @${offsetIdx} ROWS FETCH NEXT @${limitIdx} ROWS ONLY
|
|
199
|
+
`;
|
|
200
|
+
const rows = await this.dataSource.query(dataSql, [
|
|
201
|
+
...params,
|
|
202
|
+
offset,
|
|
203
|
+
limit,
|
|
204
|
+
]);
|
|
205
|
+
const data = await Promise.all(rows.map((row) => this.enrichAuditLog(row)));
|
|
206
|
+
const totalPages = total === 0 ? 0 : Math.ceil(total / limit);
|
|
161
207
|
return {
|
|
162
|
-
data
|
|
208
|
+
data,
|
|
163
209
|
total,
|
|
164
210
|
page,
|
|
165
211
|
limit,
|
|
@@ -177,9 +223,9 @@ let AuditService = AuditService_1 = class AuditService {
|
|
|
177
223
|
exports.AuditService = AuditService;
|
|
178
224
|
exports.AuditService = AuditService = AuditService_1 = __decorate([
|
|
179
225
|
(0, common_1.Injectable)(),
|
|
180
|
-
__param(0, (0, typeorm_1.
|
|
226
|
+
__param(0, (0, typeorm_1.InjectDataSource)()),
|
|
181
227
|
__param(1, (0, common_1.Inject)(audit_constants_1.AUDIT_MODULE_OPTIONS)),
|
|
182
228
|
__param(2, (0, common_1.Optional)()),
|
|
183
|
-
__metadata("design:paramtypes", [typeorm_2.
|
|
229
|
+
__metadata("design:paramtypes", [typeorm_2.DataSource, Object, nestjs_cls_1.ClsService])
|
|
184
230
|
], AuditService);
|
|
185
231
|
//# sourceMappingURL=audit.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.service.js","sourceRoot":"","sources":["../../src/audit.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAMwB;AACxB,6CAAmD;AACnD,qCAAqC;AACrC,2CAAwC;
|
|
1
|
+
{"version":3,"file":"audit.service.js","sourceRoot":"","sources":["../../src/audit.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAMwB;AACxB,6CAAmD;AACnD,qCAAqC;AACrC,2CAAwC;AAKxC,iDAAwD;AACxD,0EAAuE;AACvE,iDAA+C;AAC/C,uDAA4E;AAG5E,MAAM,cAAc,GAClB,6EAA6E,CAAC;AAGzE,IAAM,YAAY,oBAAlB,MAAM,YAAY;IAKgB;IAEpB;IACY;IAPd,MAAM,CAAgB;IACtB,SAAS,CAAS;IAEnC,YACuC,UAAsB,EAE1C,OAA2B,EACf,GAAgB;QAHR,eAAU,GAAV,UAAU,CAAY;QAE1C,YAAO,GAAP,OAAO,CAAoB;QACf,QAAG,GAAH,GAAG,CAAa;QAE7C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;QACvC,IAAI,CAAC,SAAS,GAAG,IAAI,MAAM,cAAc,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,IAAA,4BAAa,EAAC,OAAO,EAAE,cAAY,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC;IAGO,aAAa;QACnB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,CAAqB,mCAAiB,CAAC,CAAC;IAC9D,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,GAAa;QACxC,MAAM,QAAQ,GAAwB;YACpC,GAAG,GAAG;YACN,aAAa,EAAE,SAAS;YACxB,cAAc,EAAE,SAAS;SAC1B,CAAC;QAEF,IAAI,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9C,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC3D,IAAI,IAAI,EAAE,CAAC;oBACT,QAAQ,CAAC,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC;oBACnC,QAAQ,CAAC,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC;gBACvC,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,4CAA4C,GAAG,CAAC,EAAE,EAAE,EACpD,MAAM,CAAC,KAAK,CAAC,CACd,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAGD,KAAK,CAAC,GAAG,CAAC,KAAoB;QAC5B,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YAC1D,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACrE,MAAM,GAAG,GAAG;sBACI,IAAI,CAAC,SAAS;0BACV,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC;;OAEjE,CAAC;YACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAa,GAAG,EAAE;gBACxD,KAAK,CAAC,UAAU;gBAChB,KAAK,CAAC,QAAQ;gBACd,KAAK,CAAC,MAAM;gBACZ,OAAO;gBACP,KAAK,CAAC,UAAU,IAAI,IAAI;gBACxB,SAAS,IAAI,IAAI;aAClB,CAAC,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,GAAG,CACb,UAAU,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,QAAQ,UAAU,KAAK,CAAC,EAAE,GAAG,CACrF,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9D,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IASD,KAAK,CAAC,SAAS,CACb,MAAc,EACd,MAAuB,EACvB,SAAkB;QAElB,MAAM,SAAS,GAAG,IAAA,yCAAmB,EAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,iDAAiD,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,CAC7E,CAAC;YACF,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,MAAM,GAAG,MAAiC,CAAC;QACjD,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS;YACpC,CAAC,CAAE,MAAM,CAAC,SAAS,CAAC,SAAS,CAAwB;YACrD,CAAC,CAAC,SAAS,CAAC;QAEd,OAAO,IAAI,CAAC,GAAG,CAAC;YACd,UAAU,EAAE,SAAS,CAAC,UAAoB;YAC1C,QAAQ,EAAE,MAAM,CAAC,EAAY;YAC7B,UAAU;YACV,MAAM;YACN,OAAO,EAAE,IAAA,qCAAsB,EAAC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC;YAC1D,SAAS;SACV,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,UAAkB,EAClB,QAAgB;QAEhB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG;iBACD,cAAc;eAChB,IAAI,CAAC,SAAS;;;OAGtB,CAAC;YACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAa,GAAG,EAAE;gBACxD,UAAU;gBACV,QAAQ;aACT,CAAC,CAAC;YACH,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,iCAAiC,UAAU,IAAI,QAAQ,EAAE,EACzD,MAAM,CAAC,KAAK,CAAC,CACd,CAAC;YACF,MAAM,IAAI,qCAA4B,CAAC,4BAA4B,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAc;QAC7B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG;iBACD,cAAc;eAChB,IAAI,CAAC,SAAS;;;OAGtB,CAAC;YACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAa,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;YACpE,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,sCAAsC,MAAM,EAAE,EAC9C,MAAM,CAAC,KAAK,CAAC,CACd,CAAC;YACF,MAAM,IAAI,qCAA4B,CAAC,4BAA4B,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,MAAuB;QAEvB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG;iBACD,cAAc;eAChB,IAAI,CAAC,SAAS;;;OAGtB,CAAC;YACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAa,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;YACpE,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,wCAAwC,MAAM,EAAE,EAChD,MAAM,CAAC,KAAK,CAAC,CACd,CAAC;YACF,MAAM,IAAI,qCAA4B,CAAC,4BAA4B,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CACX,SAAyB;QAEzB,IAAI,CAAC;YACH,MAAM,EACJ,IAAI,GAAG,CAAC,EACR,KAAK,GAAG,EAAE,EACV,UAAU,EACV,QAAQ,EACR,SAAS,EACT,MAAM,EACN,MAAM,GACP,GAAG,SAAS,CAAC;YAEd,MAAM,UAAU,GAAa,EAAE,CAAC;YAChC,MAAM,MAAM,GAAc,EAAE,CAAC;YAE7B,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;gBAC1B,UAAU,CAAC,IAAI,CACb,4BAA4B,GAAG,2CAA2C,GAAG,GAAG,CACjF,CAAC;gBACF,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YAC3C,CAAC;YACD,IAAI,UAAU,EAAE,CAAC;gBACf,UAAU,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBAClD,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC1B,CAAC;YACD,IAAI,QAAQ,EAAE,CAAC;gBACb,UAAU,CAAC,IAAI,CAAC,eAAe,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBAChD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxB,CAAC;YACD,IAAI,SAAS,EAAE,CAAC;gBACd,UAAU,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBACjD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACzB,CAAC;YACD,IAAI,MAAM,EAAE,CAAC;gBACX,UAAU,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC9C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtB,CAAC;YAED,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM;gBAC7B,CAAC,CAAC,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;gBACrC,CAAC,CAAC,EAAE,CAAC;YAEP,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAC3C,iCAAiC,IAAI,CAAC,SAAS,IAAI,KAAK,EAAE,EAC1D,MAAM,CACP,CAAC;YACF,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC;YAE/C,MAAM,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;YAC5B,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC;YAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;YACnC,MAAM,OAAO,GAAG;iBACL,cAAc;eAChB,IAAI,CAAC,SAAS;UACnB,KAAK;;kBAEG,SAAS,qBAAqB,QAAQ;OACjD,CAAC;YACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAa,OAAO,EAAE;gBAC5D,GAAG,MAAM;gBACT,MAAM;gBACN,KAAK;aACN,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAC5B,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAC5C,CAAC;YACF,MAAM,UAAU,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;YAE9D,OAAO;gBACL,IAAI;gBACJ,KAAK;gBACL,IAAI;gBACJ,KAAK;gBACL,UAAU;gBACV,WAAW,EAAE,IAAI,GAAG,UAAU,GAAG,CAAC;gBAClC,eAAe,EAAE,IAAI,GAAG,CAAC;aAC1B,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9D,MAAM,IAAI,qCAA4B,CAAC,4BAA4B,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;CACF,CAAA;AApQY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;IAMR,WAAA,IAAA,0BAAgB,GAAE,CAAA;IAClB,WAAA,IAAA,eAAM,EAAC,sCAAoB,CAAC,CAAA;IAE5B,WAAA,IAAA,iBAAQ,GAAE,CAAA;qCAHsC,oBAAU,UAGxB,uBAAU;GARpC,YAAY,CAoQxB"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { DataSource, EntitySubscriberInterface, InsertEvent, UpdateEvent } from 'typeorm';
|
|
1
|
+
import { DataSource, EntitySubscriberInterface, InsertEvent, RemoveEvent, SoftRemoveEvent, UpdateEvent } from 'typeorm';
|
|
2
2
|
import { ClsService } from 'nestjs-cls';
|
|
3
|
+
import { AuditModuleOptions } from './interfaces/audit.interfaces';
|
|
3
4
|
export declare class AuditSubscriber implements EntitySubscriberInterface {
|
|
4
5
|
private readonly cls;
|
|
5
6
|
private readonly logger;
|
|
6
|
-
|
|
7
|
+
private readonly tableName;
|
|
8
|
+
constructor(dataSource: DataSource, options: AuditModuleOptions, cls: ClsService);
|
|
7
9
|
private currentUserId;
|
|
8
10
|
afterInsert(event: InsertEvent<object>): Promise<void>;
|
|
9
11
|
afterUpdate(event: UpdateEvent<object>): Promise<void>;
|
|
12
|
+
afterRemove(event: RemoveEvent<object>): Promise<void>;
|
|
13
|
+
afterSoftRemove(event: SoftRemoveEvent<object>): Promise<void>;
|
|
14
|
+
private handleRemoval;
|
|
10
15
|
private persist;
|
|
11
16
|
}
|
|
@@ -20,14 +20,18 @@ const typeorm_2 = require("typeorm");
|
|
|
20
20
|
const nestjs_cls_1 = require("nestjs-cls");
|
|
21
21
|
const audit_log_entity_1 = require("./entities/audit-log.entity");
|
|
22
22
|
const audit_helper_1 = require("./audit.helper");
|
|
23
|
-
const audit_builder_1 = require("./audit.builder");
|
|
24
23
|
const auditable_decorator_1 = require("./decorators/auditable.decorator");
|
|
24
|
+
const audit_logger_1 = require("./audit.logger");
|
|
25
25
|
const audit_constants_1 = require("./audit.constants");
|
|
26
26
|
let AuditSubscriber = AuditSubscriber_1 = class AuditSubscriber {
|
|
27
27
|
cls;
|
|
28
|
-
logger
|
|
29
|
-
|
|
28
|
+
logger;
|
|
29
|
+
tableName;
|
|
30
|
+
constructor(dataSource, options, cls) {
|
|
30
31
|
this.cls = cls;
|
|
32
|
+
const schema = options.schema ?? 'dbo';
|
|
33
|
+
this.tableName = `[${schema}].[auditLog]`;
|
|
34
|
+
this.logger = (0, audit_logger_1.resolveLogger)(options, AuditSubscriber_1.name);
|
|
31
35
|
dataSource.subscribers.push(this);
|
|
32
36
|
}
|
|
33
37
|
currentUserId() {
|
|
@@ -43,7 +47,7 @@ let AuditSubscriber = AuditSubscriber_1 = class AuditSubscriber {
|
|
|
43
47
|
return;
|
|
44
48
|
}
|
|
45
49
|
const record = entity;
|
|
46
|
-
await this.persist(event.manager
|
|
50
|
+
await this.persist(event.manager, {
|
|
47
51
|
entityType: auditable.entityType,
|
|
48
52
|
entityId: record.id,
|
|
49
53
|
entityName: auditable.nameField
|
|
@@ -52,6 +56,7 @@ let AuditSubscriber = AuditSubscriber_1 = class AuditSubscriber {
|
|
|
52
56
|
action: audit_log_entity_1.AuditActionType.CREATE,
|
|
53
57
|
changes: (0, audit_helper_1.extractAuditableFields)(entity, auditable.exclude),
|
|
54
58
|
});
|
|
59
|
+
this.logger.log(`Recorded CREATE audit for ${auditable.entityType} ${record.id}`);
|
|
55
60
|
}
|
|
56
61
|
async afterUpdate(event) {
|
|
57
62
|
const entity = event.entity;
|
|
@@ -71,7 +76,7 @@ let AuditSubscriber = AuditSubscriber_1 = class AuditSubscriber {
|
|
|
71
76
|
return;
|
|
72
77
|
}
|
|
73
78
|
const record = entity;
|
|
74
|
-
await this.persist(event.manager
|
|
79
|
+
await this.persist(event.manager, {
|
|
75
80
|
entityType: auditable.entityType,
|
|
76
81
|
entityId: record.id,
|
|
77
82
|
entityName: auditable.nameField
|
|
@@ -80,10 +85,50 @@ let AuditSubscriber = AuditSubscriber_1 = class AuditSubscriber {
|
|
|
80
85
|
action: audit_log_entity_1.AuditActionType.UPDATE,
|
|
81
86
|
changes,
|
|
82
87
|
});
|
|
88
|
+
this.logger.log(`Recorded UPDATE audit for ${auditable.entityType} ${record.id}`);
|
|
83
89
|
}
|
|
84
|
-
async
|
|
90
|
+
async afterRemove(event) {
|
|
91
|
+
await this.handleRemoval(event);
|
|
92
|
+
}
|
|
93
|
+
async afterSoftRemove(event) {
|
|
94
|
+
await this.handleRemoval(event);
|
|
95
|
+
}
|
|
96
|
+
async handleRemoval(event) {
|
|
97
|
+
const source = event.databaseEntity ?? event.entity;
|
|
98
|
+
if (!source || source instanceof audit_log_entity_1.AuditLog) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const auditable = (0, auditable_decorator_1.getAuditableOptions)(source);
|
|
102
|
+
if (!auditable) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const record = source;
|
|
106
|
+
const entityId = event.entityId ?? record.id;
|
|
107
|
+
await this.persist(event.manager, {
|
|
108
|
+
entityType: auditable.entityType,
|
|
109
|
+
entityId,
|
|
110
|
+
entityName: auditable.nameField
|
|
111
|
+
? record[auditable.nameField]
|
|
112
|
+
: undefined,
|
|
113
|
+
action: audit_log_entity_1.AuditActionType.DELETE,
|
|
114
|
+
changes: (0, audit_helper_1.extractAuditableFields)(source, auditable.exclude),
|
|
115
|
+
});
|
|
116
|
+
this.logger.log(`Recorded DELETE audit for ${auditable.entityType} ${entityId}`);
|
|
117
|
+
}
|
|
118
|
+
async persist(manager, input) {
|
|
85
119
|
try {
|
|
86
|
-
|
|
120
|
+
const sql = `
|
|
121
|
+
INSERT INTO ${this.tableName} (entityType, entityId, action, changes, entityName, changedBy)
|
|
122
|
+
VALUES (@0, @1, @2, @3, @4, @5)
|
|
123
|
+
`;
|
|
124
|
+
await manager.query(sql, [
|
|
125
|
+
input.entityType,
|
|
126
|
+
input.entityId,
|
|
127
|
+
input.action,
|
|
128
|
+
JSON.stringify(input.changes),
|
|
129
|
+
input.entityName ?? null,
|
|
130
|
+
this.currentUserId() ?? null,
|
|
131
|
+
]);
|
|
87
132
|
}
|
|
88
133
|
catch (error) {
|
|
89
134
|
this.logger.error(`Failed to record ${input.action} audit for ${input.entityType} ${input.entityId}`, String(error));
|
|
@@ -94,7 +139,7 @@ exports.AuditSubscriber = AuditSubscriber;
|
|
|
94
139
|
exports.AuditSubscriber = AuditSubscriber = AuditSubscriber_1 = __decorate([
|
|
95
140
|
(0, common_1.Injectable)(),
|
|
96
141
|
__param(0, (0, typeorm_1.InjectDataSource)()),
|
|
97
|
-
|
|
98
|
-
|
|
142
|
+
__param(1, (0, common_1.Inject)(audit_constants_1.AUDIT_MODULE_OPTIONS)),
|
|
143
|
+
__metadata("design:paramtypes", [typeorm_2.DataSource, Object, nestjs_cls_1.ClsService])
|
|
99
144
|
], AuditSubscriber);
|
|
100
145
|
//# sourceMappingURL=audit.subscriber.js.map
|