@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.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +283 -0
  3. package/dist/audit-context.d.ts +67 -0
  4. package/dist/audit-context.js +96 -0
  5. package/dist/audit-context.js.map +1 -0
  6. package/dist/audit-context.middleware.d.ts +76 -0
  7. package/dist/audit-context.middleware.js +91 -0
  8. package/dist/audit-context.middleware.js.map +1 -0
  9. package/dist/audit-log.entity.d.ts +56 -0
  10. package/dist/audit-log.entity.js +89 -0
  11. package/dist/audit-log.entity.js.map +1 -0
  12. package/dist/audit-log.module.d.ts +56 -0
  13. package/dist/audit-log.module.js +130 -0
  14. package/dist/audit-log.module.js.map +1 -0
  15. package/dist/audit-log.service.d.ts +43 -0
  16. package/dist/audit-log.service.js +177 -0
  17. package/dist/audit-log.service.js.map +1 -0
  18. package/dist/audit-log.subscriber.d.ts +66 -0
  19. package/dist/audit-log.subscriber.js +135 -0
  20. package/dist/audit-log.subscriber.js.map +1 -0
  21. package/dist/constants.d.ts +32 -0
  22. package/dist/constants.js +49 -0
  23. package/dist/constants.js.map +1 -0
  24. package/dist/decorators.d.ts +109 -0
  25. package/dist/decorators.js +161 -0
  26. package/dist/decorators.js.map +1 -0
  27. package/dist/index.d.ts +14 -0
  28. package/dist/index.js +31 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/tsconfig.build.tsbuildinfo +1 -0
  31. package/dist/types.d.ts +161 -0
  32. package/dist/types.js +16 -0
  33. package/dist/types.js.map +1 -0
  34. package/dist/utils/diff.d.ts +16 -0
  35. package/dist/utils/diff.js +142 -0
  36. package/dist/utils/diff.js.map +1 -0
  37. package/dist/utils/mask.d.ts +15 -0
  38. package/dist/utils/mask.js +69 -0
  39. package/dist/utils/mask.js.map +1 -0
  40. package/package.json +63 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 bogdanlupu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,283 @@
1
+ # @nest-toolbox/typeorm-audit-log
2
+
3
+ Automatic audit logging for TypeORM entities with user attribution, diff tracking, and queryable audit trails.
4
+
5
+ [![npm version](https://badge.fury.io/js/%40nest-toolbox%2Ftypeorm-audit-log.svg)](https://www.npmjs.com/package/@nest-toolbox/typeorm-audit-log)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - 🔄 **Automatic tracking** - Entity changes logged via TypeORM subscribers
11
+ - 👤 **User attribution** - Captures who made each change via AsyncLocalStorage
12
+ - 📊 **Diff calculation** - Shows exactly what changed between old and new values
13
+ - 🔍 **Queryable history** - Find audit logs by entity, user, or time range
14
+ - 🛡️ **Field exclusions** - Skip sensitive fields like passwords
15
+ - ⚡ **Async mode** - Non-blocking audit writes for performance
16
+ - 🎯 **Selective auditing** - Only track entities you care about
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @nest-toolbox/typeorm-audit-log
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ### 1. Import the module
27
+
28
+ ```typescript
29
+ // app.module.ts
30
+ import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
31
+ import { TypeOrmModule } from '@nestjs/typeorm';
32
+ import { AuditLogModule, AuditContextMiddleware } from '@nest-toolbox/typeorm-audit-log';
33
+
34
+ @Module({
35
+ imports: [
36
+ TypeOrmModule.forRoot({
37
+ // ... your database config
38
+ }),
39
+ AuditLogModule.forRoot({
40
+ retentionDays: 90,
41
+ excludeFields: ['password', 'refreshToken'],
42
+ }),
43
+ ],
44
+ })
45
+ export class AppModule implements NestModule {
46
+ configure(consumer: MiddlewareConsumer) {
47
+ // Required: captures user context for audit logs
48
+ consumer.apply(AuditContextMiddleware).forRoutes('*');
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### 2. Mark entities as auditable
54
+
55
+ ```typescript
56
+ // user.entity.ts
57
+ import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
58
+ import { Auditable } from '@nest-toolbox/typeorm-audit-log';
59
+
60
+ @Entity()
61
+ @Auditable()
62
+ export class User {
63
+ @PrimaryGeneratedColumn()
64
+ id: number;
65
+
66
+ @Column()
67
+ name: string;
68
+
69
+ @Column()
70
+ email: string;
71
+
72
+ @Column()
73
+ role: string;
74
+ }
75
+ ```
76
+
77
+ That's it! All INSERT, UPDATE, and DELETE operations on `User` will be automatically logged.
78
+
79
+ ## Configuration
80
+
81
+ ### Module Options
82
+
83
+ ```typescript
84
+ AuditLogModule.forRoot({
85
+ // Days to keep audit logs (0 = forever)
86
+ retentionDays: 90,
87
+
88
+ // Fields to exclude from all audit logs
89
+ excludeFields: ['password', 'token', 'secret'],
90
+
91
+ // Entity names to exclude from auditing
92
+ excludeEntities: ['Session', 'Cache'],
93
+
94
+ // Don't block on audit writes (fire-and-forget)
95
+ async: false,
96
+ });
97
+ ```
98
+
99
+ ### Async Configuration
100
+
101
+ ```typescript
102
+ AuditLogModule.forRootAsync({
103
+ imports: [ConfigModule],
104
+ useFactory: (config: ConfigService) => ({
105
+ retentionDays: config.get('AUDIT_RETENTION_DAYS'),
106
+ async: config.get('AUDIT_ASYNC') === 'true',
107
+ excludeFields: config.get('AUDIT_EXCLUDE_FIELDS')?.split(',') || [],
108
+ }),
109
+ inject: [ConfigService],
110
+ });
111
+ ```
112
+
113
+ ### Entity Options
114
+
115
+ ```typescript
116
+ @Entity()
117
+ @Auditable({
118
+ // Custom name in audit logs (default: class name)
119
+ entityName: 'UserAccount',
120
+
121
+ // Fields to exclude for this entity only
122
+ excludeFields: ['lastLoginAt', 'sessionData'],
123
+ })
124
+ export class User {
125
+ // ...
126
+ }
127
+ ```
128
+
129
+ ## Querying Audit Logs
130
+
131
+ Inject `AuditLogService` to query the audit trail:
132
+
133
+ ```typescript
134
+ import { Injectable } from '@nestjs/common';
135
+ import { AuditLogService } from '@nest-toolbox/typeorm-audit-log';
136
+
137
+ @Injectable()
138
+ export class AuditService {
139
+ constructor(private auditLogService: AuditLogService) {}
140
+
141
+ // Get history for a specific entity
142
+ async getEntityHistory(entityName: string, entityId: string) {
143
+ return this.auditLogService.findByEntity(entityName, entityId);
144
+ }
145
+
146
+ // Get all changes by a user
147
+ async getUserActivity(userId: string) {
148
+ return this.auditLogService.findByUser(userId, {
149
+ since: new Date(Date.now() - 24 * 60 * 60 * 1000), // Last 24h
150
+ });
151
+ }
152
+
153
+ // Search all audit logs with filters
154
+ async searchAuditLogs() {
155
+ return this.auditLogService.findAll({
156
+ entityName: 'User',
157
+ action: 'UPDATE',
158
+ since: new Date('2024-01-01'),
159
+ page: 1,
160
+ limit: 50,
161
+ });
162
+ }
163
+ }
164
+ ```
165
+
166
+ ## Manual Context Setting
167
+
168
+ For non-HTTP contexts (queues, cron jobs, CLI), set the audit context manually:
169
+
170
+ ```typescript
171
+ import { AuditContext } from '@nest-toolbox/typeorm-audit-log';
172
+
173
+ @Injectable()
174
+ export class OrderProcessor {
175
+ @Process('process-order')
176
+ async processOrder(job: Job) {
177
+ await AuditContext.run(
178
+ {
179
+ userId: 'system',
180
+ userName: 'Order Processor',
181
+ metadata: { jobId: job.id },
182
+ },
183
+ async () => {
184
+ // All entity changes here will be attributed to "system"
185
+ await this.orderService.updateStatus(job.data.orderId, 'processed');
186
+ },
187
+ );
188
+ }
189
+ }
190
+ ```
191
+
192
+ ## Custom Context Extraction
193
+
194
+ Customize how user info is extracted from requests:
195
+
196
+ ```typescript
197
+ import { AuditContextMiddleware, createAuditContextMiddleware } from '@nest-toolbox/typeorm-audit-log';
198
+
199
+ // Custom extractor for JWT claims
200
+ const customMiddleware = createAuditContextMiddleware((req) => ({
201
+ userId: req.user?.sub,
202
+ userName: req.user?.preferred_username,
203
+ ip: req.headers['x-forwarded-for'] as string || req.ip,
204
+ userAgent: req.headers['user-agent'],
205
+ metadata: {
206
+ tenantId: req.headers['x-tenant-id'],
207
+ },
208
+ }));
209
+
210
+ // In your module
211
+ consumer.apply(customMiddleware.use.bind(customMiddleware)).forRoutes('*');
212
+ ```
213
+
214
+ ## Audit Log Entry Structure
215
+
216
+ Each audit log entry contains:
217
+
218
+ | Field | Type | Description |
219
+ |-------|------|-------------|
220
+ | `id` | UUID | Unique identifier |
221
+ | `entityName` | string | Name of the entity |
222
+ | `entityId` | string | Primary key of the entity |
223
+ | `action` | enum | CREATE, UPDATE, or DELETE |
224
+ | `userId` | string | ID of the user who made the change |
225
+ | `userName` | string | Name/email of the user |
226
+ | `oldValues` | JSON | Previous values (UPDATE/DELETE) |
227
+ | `newValues` | JSON | New values (CREATE/UPDATE) |
228
+ | `diff` | JSON | Array of changed fields |
229
+ | `ip` | string | IP address of the request |
230
+ | `userAgent` | string | Browser/client user agent |
231
+ | `metadata` | JSON | Custom metadata |
232
+ | `timestamp` | Date | When the change occurred |
233
+
234
+ ## Database Migration
235
+
236
+ The module will create an `audit_logs` table. If you need to create it manually:
237
+
238
+ ```sql
239
+ CREATE TABLE audit_logs (
240
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
241
+ entity_name VARCHAR(255) NOT NULL,
242
+ entity_id VARCHAR(255) NOT NULL,
243
+ action VARCHAR(10) NOT NULL,
244
+ user_id VARCHAR(255),
245
+ user_name VARCHAR(255),
246
+ old_values JSONB,
247
+ new_values JSONB,
248
+ diff JSONB,
249
+ metadata JSONB,
250
+ ip VARCHAR(45),
251
+ user_agent VARCHAR(500),
252
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
253
+ );
254
+
255
+ CREATE INDEX idx_audit_entity ON audit_logs(entity_name, entity_id);
256
+ CREATE INDEX idx_audit_user ON audit_logs(user_id);
257
+ CREATE INDEX idx_audit_timestamp ON audit_logs(timestamp);
258
+ ```
259
+
260
+ ## API Reference
261
+
262
+ ### Decorators
263
+
264
+ - `@Auditable(options?)` - Mark an entity for audit logging
265
+ - `@AuditIgnore()` - Exclude a field from audit logs (Phase 2)
266
+ - `@AuditMask()` - Mask a field value in audit logs (Phase 2)
267
+
268
+ ### Services
269
+
270
+ - `AuditLogService.log(params)` - Manually log an audit entry
271
+ - `AuditLogService.findByEntity(name, id, options?)` - Query by entity
272
+ - `AuditLogService.findByUser(userId, options?)` - Query by user
273
+ - `AuditLogService.findAll(options?)` - Paginated search
274
+
275
+ ### Context
276
+
277
+ - `AuditContext.run(data, fn)` - Run code with audit context
278
+ - `AuditContext.get()` - Get current audit context
279
+ - `AuditContext.set(data)` - Update current audit context
280
+
281
+ ## License
282
+
283
+ MIT © [Bogdan Lupu](https://github.com/lupu60)
@@ -0,0 +1,67 @@
1
+ import type { AuditContextData } from './types';
2
+ /**
3
+ * Run a function with the given audit context.
4
+ * All code executed within fn (including async operations) will have access
5
+ * to the provided context data.
6
+ *
7
+ * @param data - The audit context data to set
8
+ * @param fn - The function to run with the context
9
+ * @returns The return value of fn
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // In middleware or guard
14
+ * auditContextRun({
15
+ * userId: request.user.id,
16
+ * userName: request.user.email,
17
+ * ip: request.ip,
18
+ * }, () => next());
19
+ * ```
20
+ */
21
+ export declare function auditContextRun<T>(data: AuditContextData, fn: () => T): T;
22
+ /**
23
+ * Get the current audit context data.
24
+ * Returns undefined if called outside of a run() context.
25
+ *
26
+ * @returns The current context data or undefined
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const context = getAuditContext();
31
+ * console.log(context?.userId);
32
+ * ```
33
+ */
34
+ export declare function getAuditContext(): AuditContextData | undefined;
35
+ /**
36
+ * Update the current audit context with additional data.
37
+ * Merges the provided data with existing context.
38
+ * No-op if called outside of a run() context.
39
+ *
40
+ * @param data - Partial data to merge into the current context
41
+ */
42
+ export declare function setAuditContext(data: Partial<AuditContextData>): void;
43
+ /**
44
+ * AuditContext class for backward compatibility and namespacing.
45
+ * Provides static methods that wrap the exported functions.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * AuditContext.run({ userId: 'user-1' }, () => {
50
+ * const ctx = AuditContext.get();
51
+ * });
52
+ * ```
53
+ */
54
+ export declare class AuditContext {
55
+ /**
56
+ * Run a function with the given audit context.
57
+ */
58
+ static run<T>(data: AuditContextData, fn: () => T): T;
59
+ /**
60
+ * Get the current audit context data.
61
+ */
62
+ static get(): AuditContextData | undefined;
63
+ /**
64
+ * Update the current audit context with additional data.
65
+ */
66
+ static set(data: Partial<AuditContextData>): void;
67
+ }
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuditContext = void 0;
4
+ exports.auditContextRun = auditContextRun;
5
+ exports.getAuditContext = getAuditContext;
6
+ exports.setAuditContext = setAuditContext;
7
+ const node_async_hooks_1 = require("node:async_hooks");
8
+ /**
9
+ * Internal storage for audit context data.
10
+ * Uses AsyncLocalStorage to propagate context across async boundaries.
11
+ */
12
+ const storage = new node_async_hooks_1.AsyncLocalStorage();
13
+ /**
14
+ * Run a function with the given audit context.
15
+ * All code executed within fn (including async operations) will have access
16
+ * to the provided context data.
17
+ *
18
+ * @param data - The audit context data to set
19
+ * @param fn - The function to run with the context
20
+ * @returns The return value of fn
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // In middleware or guard
25
+ * auditContextRun({
26
+ * userId: request.user.id,
27
+ * userName: request.user.email,
28
+ * ip: request.ip,
29
+ * }, () => next());
30
+ * ```
31
+ */
32
+ function auditContextRun(data, fn) {
33
+ return storage.run(data, fn);
34
+ }
35
+ /**
36
+ * Get the current audit context data.
37
+ * Returns undefined if called outside of a run() context.
38
+ *
39
+ * @returns The current context data or undefined
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const context = getAuditContext();
44
+ * console.log(context?.userId);
45
+ * ```
46
+ */
47
+ function getAuditContext() {
48
+ return storage.getStore();
49
+ }
50
+ /**
51
+ * Update the current audit context with additional data.
52
+ * Merges the provided data with existing context.
53
+ * No-op if called outside of a run() context.
54
+ *
55
+ * @param data - Partial data to merge into the current context
56
+ */
57
+ function setAuditContext(data) {
58
+ const current = getAuditContext();
59
+ if (current) {
60
+ Object.assign(current, data);
61
+ }
62
+ }
63
+ /**
64
+ * AuditContext class for backward compatibility and namespacing.
65
+ * Provides static methods that wrap the exported functions.
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * AuditContext.run({ userId: 'user-1' }, () => {
70
+ * const ctx = AuditContext.get();
71
+ * });
72
+ * ```
73
+ */
74
+ // biome-ignore lint/complexity/noStaticOnlyClass: class provides namespacing for cleaner API
75
+ class AuditContext {
76
+ /**
77
+ * Run a function with the given audit context.
78
+ */
79
+ static run(data, fn) {
80
+ return auditContextRun(data, fn);
81
+ }
82
+ /**
83
+ * Get the current audit context data.
84
+ */
85
+ static get() {
86
+ return getAuditContext();
87
+ }
88
+ /**
89
+ * Update the current audit context with additional data.
90
+ */
91
+ static set(data) {
92
+ setAuditContext(data);
93
+ }
94
+ }
95
+ exports.AuditContext = AuditContext;
96
+ //# sourceMappingURL=audit-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-context.js","sourceRoot":"","sources":["../src/audit-context.ts"],"names":[],"mappings":";;;AA4BA,0CAEC;AAcD,0CAEC;AASD,0CAKC;AA5DD,uDAAqD;AAGrD;;;GAGG;AACH,MAAM,OAAO,GAAG,IAAI,oCAAiB,EAAoB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,eAAe,CAAI,IAAsB,EAAE,EAAW;IACpE,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,eAAe;IAC7B,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,eAAe,CAAC,IAA+B;IAC7D,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;IAClC,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,6FAA6F;AAC7F,MAAa,YAAY;IACvB;;OAEG;IACH,MAAM,CAAC,GAAG,CAAI,IAAsB,EAAE,EAAW;QAC/C,OAAO,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,GAAG;QACR,OAAO,eAAe,EAAE,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,IAA+B;QACxC,eAAe,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;CACF;AArBD,oCAqBC"}
@@ -0,0 +1,76 @@
1
+ import { type NestMiddleware } from '@nestjs/common';
2
+ import type { NextFunction, Request, Response } from 'express';
3
+ import type { AuditContextData } from './types';
4
+ /**
5
+ * Express request with optional user property.
6
+ * Extend this interface if your user object has different properties.
7
+ */
8
+ interface RequestWithUser extends Request {
9
+ user?: {
10
+ id?: string;
11
+ name?: string;
12
+ email?: string;
13
+ [key: string]: any;
14
+ };
15
+ }
16
+ /**
17
+ * Type for custom context extractor function.
18
+ * Allows customization of how audit context is extracted from requests.
19
+ */
20
+ export type AuditContextExtractor = (req: RequestWithUser) => AuditContextData;
21
+ /**
22
+ * Default context extractor.
23
+ * Extracts userId, userName, IP, and userAgent from the request.
24
+ */
25
+ export declare const defaultContextExtractor: AuditContextExtractor;
26
+ /**
27
+ * NestJS middleware that initializes audit context from the HTTP request.
28
+ * Automatically captures user information, IP address, and user agent
29
+ * for all audit log entries created during the request lifecycle.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * // app.module.ts
34
+ * export class AppModule implements NestModule {
35
+ * configure(consumer: MiddlewareConsumer) {
36
+ * consumer
37
+ * .apply(AuditContextMiddleware)
38
+ * .forRoutes('*');
39
+ * }
40
+ * }
41
+ * ```
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * // With custom extractor
46
+ * export class AppModule implements NestModule {
47
+ * configure(consumer: MiddlewareConsumer) {
48
+ * const middleware = new AuditContextMiddleware((req) => ({
49
+ * userId: req.user?.sub,
50
+ * userName: req.user?.preferred_username,
51
+ * ip: req.headers['x-forwarded-for'] as string || req.ip,
52
+ * userAgent: req.headers['user-agent'],
53
+ * metadata: { tenantId: req.headers['x-tenant-id'] },
54
+ * }));
55
+ * consumer.apply(middleware.use.bind(middleware)).forRoutes('*');
56
+ * }
57
+ * }
58
+ * ```
59
+ */
60
+ export declare class AuditContextMiddleware implements NestMiddleware {
61
+ private extractor;
62
+ constructor(extractor?: AuditContextExtractor);
63
+ /**
64
+ * Middleware handler that wraps the request in an audit context.
65
+ */
66
+ use(req: RequestWithUser, _res: Response, next: NextFunction): void;
67
+ }
68
+ /**
69
+ * Factory function to create a middleware with a custom extractor.
70
+ * Useful when you need to inject dependencies into the extractor.
71
+ *
72
+ * @param extractor - Custom function to extract audit context from request
73
+ * @returns A new middleware instance with the custom extractor
74
+ */
75
+ export declare function createAuditContextMiddleware(extractor: AuditContextExtractor): AuditContextMiddleware;
76
+ export {};
@@ -0,0 +1,91 @@
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.AuditContextMiddleware = exports.defaultContextExtractor = void 0;
13
+ exports.createAuditContextMiddleware = createAuditContextMiddleware;
14
+ const common_1 = require("@nestjs/common");
15
+ const audit_context_1 = require("./audit-context");
16
+ /**
17
+ * Default context extractor.
18
+ * Extracts userId, userName, IP, and userAgent from the request.
19
+ */
20
+ const defaultContextExtractor = (req) => {
21
+ var _a, _b, _c, _d, _e, _f;
22
+ return ({
23
+ userId: (_a = req.user) === null || _a === void 0 ? void 0 : _a.id,
24
+ userName: (_c = (_b = req.user) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : (_d = req.user) === null || _d === void 0 ? void 0 : _d.email,
25
+ ip: (_e = req.ip) !== null && _e !== void 0 ? _e : (_f = req.socket) === null || _f === void 0 ? void 0 : _f.remoteAddress,
26
+ userAgent: req.headers['user-agent'],
27
+ });
28
+ };
29
+ exports.defaultContextExtractor = defaultContextExtractor;
30
+ /**
31
+ * NestJS middleware that initializes audit context from the HTTP request.
32
+ * Automatically captures user information, IP address, and user agent
33
+ * for all audit log entries created during the request lifecycle.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // app.module.ts
38
+ * export class AppModule implements NestModule {
39
+ * configure(consumer: MiddlewareConsumer) {
40
+ * consumer
41
+ * .apply(AuditContextMiddleware)
42
+ * .forRoutes('*');
43
+ * }
44
+ * }
45
+ * ```
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * // With custom extractor
50
+ * export class AppModule implements NestModule {
51
+ * configure(consumer: MiddlewareConsumer) {
52
+ * const middleware = new AuditContextMiddleware((req) => ({
53
+ * userId: req.user?.sub,
54
+ * userName: req.user?.preferred_username,
55
+ * ip: req.headers['x-forwarded-for'] as string || req.ip,
56
+ * userAgent: req.headers['user-agent'],
57
+ * metadata: { tenantId: req.headers['x-tenant-id'] },
58
+ * }));
59
+ * consumer.apply(middleware.use.bind(middleware)).forRoutes('*');
60
+ * }
61
+ * }
62
+ * ```
63
+ */
64
+ let AuditContextMiddleware = class AuditContextMiddleware {
65
+ constructor(extractor) {
66
+ this.extractor = extractor !== null && extractor !== void 0 ? extractor : exports.defaultContextExtractor;
67
+ }
68
+ /**
69
+ * Middleware handler that wraps the request in an audit context.
70
+ */
71
+ use(req, _res, next) {
72
+ const contextData = this.extractor(req);
73
+ (0, audit_context_1.auditContextRun)(contextData, () => next());
74
+ }
75
+ };
76
+ exports.AuditContextMiddleware = AuditContextMiddleware;
77
+ exports.AuditContextMiddleware = AuditContextMiddleware = __decorate([
78
+ (0, common_1.Injectable)(),
79
+ __metadata("design:paramtypes", [Function])
80
+ ], AuditContextMiddleware);
81
+ /**
82
+ * Factory function to create a middleware with a custom extractor.
83
+ * Useful when you need to inject dependencies into the extractor.
84
+ *
85
+ * @param extractor - Custom function to extract audit context from request
86
+ * @returns A new middleware instance with the custom extractor
87
+ */
88
+ function createAuditContextMiddleware(extractor) {
89
+ return new AuditContextMiddleware(extractor);
90
+ }
91
+ //# sourceMappingURL=audit-context.middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-context.middleware.js","sourceRoot":"","sources":["../src/audit-context.middleware.ts"],"names":[],"mappings":";;;;;;;;;;;;AA8FA,oEAEC;AAhGD,2CAAiE;AAEjE,mDAAkD;AAuBlD;;;GAGG;AACI,MAAM,uBAAuB,GAA0B,CAAC,GAAoB,EAAoB,EAAE;;IAAC,OAAA,CAAC;QACzG,MAAM,EAAE,MAAA,GAAG,CAAC,IAAI,0CAAE,EAAE;QACpB,QAAQ,EAAE,MAAA,MAAA,GAAG,CAAC,IAAI,0CAAE,IAAI,mCAAI,MAAA,GAAG,CAAC,IAAI,0CAAE,KAAK;QAC3C,EAAE,EAAE,MAAA,GAAG,CAAC,EAAE,mCAAI,MAAA,GAAG,CAAC,MAAM,0CAAE,aAAa;QACvC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC;KACrC,CAAC,CAAA;CAAA,CAAC;AALU,QAAA,uBAAuB,2BAKjC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEI,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IAGjC,YAAY,SAAiC;QAC3C,IAAI,CAAC,SAAS,GAAG,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,+BAAuB,CAAC;IACxD,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,GAAoB,EAAE,IAAc,EAAE,IAAkB;QAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACxC,IAAA,+BAAe,EAAC,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;CACF,CAAA;AAdY,wDAAsB;iCAAtB,sBAAsB;IADlC,IAAA,mBAAU,GAAE;;GACA,sBAAsB,CAclC;AAED;;;;;;GAMG;AACH,SAAgB,4BAA4B,CAAC,SAAgC;IAC3E,OAAO,IAAI,sBAAsB,CAAC,SAAS,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,56 @@
1
+ import type { AuditAction, AuditDiff } from './types';
2
+ /**
3
+ * Entity for storing audit log entries.
4
+ * Tracks all changes to entities decorated with @Auditable().
5
+ */
6
+ export declare class AuditLog {
7
+ id: string;
8
+ /**
9
+ * Name of the entity that was modified
10
+ */
11
+ entityName: string;
12
+ /**
13
+ * ID of the entity that was modified
14
+ */
15
+ entityId: string;
16
+ /**
17
+ * Type of action performed (CREATE, UPDATE, DELETE)
18
+ */
19
+ action: AuditAction;
20
+ /**
21
+ * ID of the user who made the change
22
+ */
23
+ userId: string | null;
24
+ /**
25
+ * Name/email of the user who made the change
26
+ */
27
+ userName: string | null;
28
+ /**
29
+ * Previous values of changed fields (for UPDATE/DELETE)
30
+ */
31
+ oldValues: Record<string, any> | null;
32
+ /**
33
+ * New values of changed fields (for CREATE/UPDATE)
34
+ */
35
+ newValues: Record<string, any> | null;
36
+ /**
37
+ * Computed diff of changes between old and new values
38
+ */
39
+ diff: AuditDiff[] | null;
40
+ /**
41
+ * Additional metadata captured at audit time
42
+ */
43
+ metadata: Record<string, any> | null;
44
+ /**
45
+ * IP address of the request that triggered the change
46
+ */
47
+ ip: string | null;
48
+ /**
49
+ * User agent of the request that triggered the change
50
+ */
51
+ userAgent: string | null;
52
+ /**
53
+ * Timestamp when the audit log was created
54
+ */
55
+ timestamp: Date;
56
+ }