@tstdl/base 0.93.1 → 0.93.2
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/audit/audit.model.d.ts +101 -6
- package/audit/audit.model.js +138 -4
- package/audit/auditor.d.ts +106 -30
- package/audit/auditor.js +80 -2
- package/audit/drizzle/0000_tiny_the_captain.sql +25 -0
- package/audit/drizzle/meta/0000_snapshot.json +188 -0
- package/audit/drizzle/meta/_journal.json +13 -0
- package/audit/drizzle.config.d.ts +2 -0
- package/audit/drizzle.config.js +11 -0
- package/audit/module.d.ts +22 -0
- package/audit/module.js +34 -0
- package/audit/schemas.d.ts +6 -0
- package/audit/schemas.js +8 -0
- package/authentication/models/index.d.ts +0 -1
- package/authentication/models/index.js +0 -1
- package/authentication/server/drizzle.config.js +1 -1
- package/authentication/server/index.d.ts +1 -0
- package/authentication/server/index.js +1 -0
- package/authentication/{models → server}/schemas.d.ts +2 -2
- package/authentication/{models → server}/schemas.js +2 -2
- package/orm/entity.d.ts +6 -0
- package/orm/entity.js +14 -0
- package/orm/server/drizzle/schema-converter.ts +408 -0
- package/package.json +5 -16
- package/orm/server/drizzle/index.js +0 -1
- package/orm/server/drizzle/schema-converter.d.ts +0 -15
- package/orm/server/drizzle/schema-converter.js +0 -300
- /package/orm/server/drizzle/{index.d.ts → index.ts} +0 -0
package/audit/audit.model.d.ts
CHANGED
|
@@ -1,15 +1,110 @@
|
|
|
1
|
-
import { EntityWithoutMetadata, Json, Timestamp, Uuid } from '../orm/index.js';
|
|
1
|
+
import { Embedded, EntityWithoutMetadata, Json, Timestamp, Uuid } from '../orm/index.js';
|
|
2
|
+
import type { UndefinableJsonObject } from '../types/index.js';
|
|
2
3
|
import { ActorType, AuditOutcome, AuditSeverity } from './types.js';
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Represents the network details of the request that triggered the audit event.
|
|
6
|
+
*/
|
|
7
|
+
export declare class RequestDetails {
|
|
8
|
+
/**
|
|
9
|
+
* The IP address of the client.
|
|
10
|
+
* @example '192.168.1.100'
|
|
11
|
+
*/
|
|
12
|
+
ipAddress: string | null;
|
|
13
|
+
/**
|
|
14
|
+
* The user agent string of the client.
|
|
15
|
+
* @example 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...'
|
|
16
|
+
*/
|
|
17
|
+
userAgent: string | null;
|
|
18
|
+
/**
|
|
19
|
+
* The session ID associated with the request, if any.
|
|
20
|
+
*/
|
|
21
|
+
sessionId: Uuid | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Represents the state of data before and after a change.
|
|
25
|
+
*/
|
|
26
|
+
export declare class ChangeDetails {
|
|
27
|
+
/**
|
|
28
|
+
* The state of the data before the change occurred.
|
|
29
|
+
*/
|
|
30
|
+
before: Json<unknown> | null;
|
|
31
|
+
/**
|
|
32
|
+
* The state of the data after the change occurred.
|
|
33
|
+
*/
|
|
34
|
+
after: Json<unknown> | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Represents a single audit event record in the database.
|
|
38
|
+
* This entity captures who did what, when, and to what, along with other contextual information.
|
|
39
|
+
*
|
|
40
|
+
* @template Details The type of the `details` JSON object, allowing for custom, action-specific data.
|
|
41
|
+
*/
|
|
42
|
+
export declare class AuditEvent<Details extends UndefinableJsonObject = UndefinableJsonObject> extends EntityWithoutMetadata {
|
|
43
|
+
/**
|
|
44
|
+
* The timestamp when the event occurred.
|
|
45
|
+
*/
|
|
4
46
|
timestamp: Timestamp;
|
|
5
|
-
|
|
47
|
+
/**
|
|
48
|
+
* The ID of the tenant in which the event occurred. Can be null for system-level events.
|
|
49
|
+
*/
|
|
50
|
+
tenantId: Uuid | null;
|
|
51
|
+
/**
|
|
52
|
+
* A unique identifier to correlate multiple related audit events.
|
|
53
|
+
*/
|
|
54
|
+
correlationId: Uuid | null;
|
|
55
|
+
/**
|
|
56
|
+
* The module or feature area where the event originated, in dot-separated format.
|
|
57
|
+
* @example 'user.authentication'
|
|
58
|
+
*/
|
|
6
59
|
module: string;
|
|
60
|
+
/**
|
|
61
|
+
* The specific action that was performed.
|
|
62
|
+
* @example 'login'
|
|
63
|
+
*/
|
|
7
64
|
action: string;
|
|
65
|
+
/**
|
|
66
|
+
* The outcome of the action.
|
|
67
|
+
*/
|
|
8
68
|
outcome: AuditOutcome;
|
|
69
|
+
/**
|
|
70
|
+
* The severity level of the event.
|
|
71
|
+
*/
|
|
9
72
|
severity: AuditSeverity;
|
|
10
|
-
|
|
73
|
+
/**
|
|
74
|
+
* The unique identifier of the actor who performed the action.
|
|
75
|
+
*/
|
|
76
|
+
actorId: Uuid;
|
|
77
|
+
/**
|
|
78
|
+
* The type of the actor.
|
|
79
|
+
*/
|
|
11
80
|
actorType: ActorType;
|
|
12
|
-
|
|
81
|
+
/**
|
|
82
|
+
* The unique identifier of the user who is impersonating the actor, if applicable.
|
|
83
|
+
*/
|
|
84
|
+
impersonatorId: Uuid | null;
|
|
85
|
+
/**
|
|
86
|
+
* The type of the impersonator, if applicable.
|
|
87
|
+
*/
|
|
88
|
+
impersonatorType: ActorType | null;
|
|
89
|
+
/**
|
|
90
|
+
* The unique identifier of the primary resource or entity that was the target of the action.
|
|
91
|
+
*/
|
|
92
|
+
targetId: Uuid;
|
|
93
|
+
/**
|
|
94
|
+
* The type of the target entity.
|
|
95
|
+
* @example 'User'
|
|
96
|
+
*/
|
|
13
97
|
targetType: string;
|
|
14
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Network-related details for the request that triggered the event.
|
|
100
|
+
*/
|
|
101
|
+
network: Embedded<RequestDetails> | null;
|
|
102
|
+
/**
|
|
103
|
+
* Details about data changes.
|
|
104
|
+
*/
|
|
105
|
+
changes: Embedded<ChangeDetails> | null;
|
|
106
|
+
/**
|
|
107
|
+
* A flexible JSON object for storing additional, action-specific details.
|
|
108
|
+
*/
|
|
109
|
+
details: Json<Details> | null;
|
|
15
110
|
}
|
package/audit/audit.model.js
CHANGED
|
@@ -7,20 +7,134 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
-
import { EntityWithoutMetadata, Json, Table, Timestamp, Uuid } from '../orm/index.js';
|
|
10
|
+
import { Embedded, EntityWithoutMetadata, Json, Table, Timestamp, Uuid } from '../orm/index.js';
|
|
11
11
|
import { Enumeration, StringProperty } from '../schema/index.js';
|
|
12
12
|
import { ActorType, AuditOutcome, AuditSeverity } from './types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Represents the network details of the request that triggered the audit event.
|
|
15
|
+
*/
|
|
16
|
+
export class RequestDetails {
|
|
17
|
+
/**
|
|
18
|
+
* The IP address of the client.
|
|
19
|
+
* @example '192.168.1.100'
|
|
20
|
+
*/
|
|
21
|
+
ipAddress;
|
|
22
|
+
/**
|
|
23
|
+
* The user agent string of the client.
|
|
24
|
+
* @example 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...'
|
|
25
|
+
*/
|
|
26
|
+
userAgent;
|
|
27
|
+
/**
|
|
28
|
+
* The session ID associated with the request, if any.
|
|
29
|
+
*/
|
|
30
|
+
sessionId;
|
|
31
|
+
}
|
|
32
|
+
__decorate([
|
|
33
|
+
StringProperty({ nullable: true }),
|
|
34
|
+
__metadata("design:type", Object)
|
|
35
|
+
], RequestDetails.prototype, "ipAddress", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
StringProperty({ nullable: true }),
|
|
38
|
+
__metadata("design:type", Object)
|
|
39
|
+
], RequestDetails.prototype, "userAgent", void 0);
|
|
40
|
+
__decorate([
|
|
41
|
+
Uuid({ nullable: true }),
|
|
42
|
+
__metadata("design:type", Object)
|
|
43
|
+
], RequestDetails.prototype, "sessionId", void 0);
|
|
44
|
+
/**
|
|
45
|
+
* Represents the state of data before and after a change.
|
|
46
|
+
*/
|
|
47
|
+
export class ChangeDetails {
|
|
48
|
+
/**
|
|
49
|
+
* The state of the data before the change occurred.
|
|
50
|
+
*/
|
|
51
|
+
before;
|
|
52
|
+
/**
|
|
53
|
+
* The state of the data after the change occurred.
|
|
54
|
+
*/
|
|
55
|
+
after;
|
|
56
|
+
}
|
|
57
|
+
__decorate([
|
|
58
|
+
Json({ nullable: true }),
|
|
59
|
+
__metadata("design:type", Object)
|
|
60
|
+
], ChangeDetails.prototype, "before", void 0);
|
|
61
|
+
__decorate([
|
|
62
|
+
Json({ nullable: true }),
|
|
63
|
+
__metadata("design:type", Object)
|
|
64
|
+
], ChangeDetails.prototype, "after", void 0);
|
|
65
|
+
/**
|
|
66
|
+
* Represents a single audit event record in the database.
|
|
67
|
+
* This entity captures who did what, when, and to what, along with other contextual information.
|
|
68
|
+
*
|
|
69
|
+
* @template Details The type of the `details` JSON object, allowing for custom, action-specific data.
|
|
70
|
+
*/
|
|
13
71
|
let AuditEvent = class AuditEvent extends EntityWithoutMetadata {
|
|
72
|
+
/**
|
|
73
|
+
* The timestamp when the event occurred.
|
|
74
|
+
*/
|
|
14
75
|
timestamp;
|
|
76
|
+
/**
|
|
77
|
+
* The ID of the tenant in which the event occurred. Can be null for system-level events.
|
|
78
|
+
*/
|
|
79
|
+
tenantId;
|
|
80
|
+
/**
|
|
81
|
+
* A unique identifier to correlate multiple related audit events.
|
|
82
|
+
*/
|
|
15
83
|
correlationId;
|
|
84
|
+
/**
|
|
85
|
+
* The module or feature area where the event originated, in dot-separated format.
|
|
86
|
+
* @example 'user.authentication'
|
|
87
|
+
*/
|
|
16
88
|
module;
|
|
89
|
+
/**
|
|
90
|
+
* The specific action that was performed.
|
|
91
|
+
* @example 'login'
|
|
92
|
+
*/
|
|
17
93
|
action;
|
|
94
|
+
/**
|
|
95
|
+
* The outcome of the action.
|
|
96
|
+
*/
|
|
18
97
|
outcome;
|
|
98
|
+
/**
|
|
99
|
+
* The severity level of the event.
|
|
100
|
+
*/
|
|
19
101
|
severity;
|
|
102
|
+
/**
|
|
103
|
+
* The unique identifier of the actor who performed the action.
|
|
104
|
+
*/
|
|
20
105
|
actorId;
|
|
106
|
+
/**
|
|
107
|
+
* The type of the actor.
|
|
108
|
+
*/
|
|
21
109
|
actorType;
|
|
110
|
+
/**
|
|
111
|
+
* The unique identifier of the user who is impersonating the actor, if applicable.
|
|
112
|
+
*/
|
|
113
|
+
impersonatorId;
|
|
114
|
+
/**
|
|
115
|
+
* The type of the impersonator, if applicable.
|
|
116
|
+
*/
|
|
117
|
+
impersonatorType;
|
|
118
|
+
/**
|
|
119
|
+
* The unique identifier of the primary resource or entity that was the target of the action.
|
|
120
|
+
*/
|
|
22
121
|
targetId;
|
|
122
|
+
/**
|
|
123
|
+
* The type of the target entity.
|
|
124
|
+
* @example 'User'
|
|
125
|
+
*/
|
|
23
126
|
targetType;
|
|
127
|
+
/**
|
|
128
|
+
* Network-related details for the request that triggered the event.
|
|
129
|
+
*/
|
|
130
|
+
network;
|
|
131
|
+
/**
|
|
132
|
+
* Details about data changes.
|
|
133
|
+
*/
|
|
134
|
+
changes;
|
|
135
|
+
/**
|
|
136
|
+
* A flexible JSON object for storing additional, action-specific details.
|
|
137
|
+
*/
|
|
24
138
|
details;
|
|
25
139
|
};
|
|
26
140
|
__decorate([
|
|
@@ -29,7 +143,11 @@ __decorate([
|
|
|
29
143
|
], AuditEvent.prototype, "timestamp", void 0);
|
|
30
144
|
__decorate([
|
|
31
145
|
Uuid({ nullable: true }),
|
|
32
|
-
__metadata("design:type",
|
|
146
|
+
__metadata("design:type", Object)
|
|
147
|
+
], AuditEvent.prototype, "tenantId", void 0);
|
|
148
|
+
__decorate([
|
|
149
|
+
Uuid({ nullable: true }),
|
|
150
|
+
__metadata("design:type", Object)
|
|
33
151
|
], AuditEvent.prototype, "correlationId", void 0);
|
|
34
152
|
__decorate([
|
|
35
153
|
StringProperty(),
|
|
@@ -48,7 +166,7 @@ __decorate([
|
|
|
48
166
|
__metadata("design:type", String)
|
|
49
167
|
], AuditEvent.prototype, "severity", void 0);
|
|
50
168
|
__decorate([
|
|
51
|
-
|
|
169
|
+
Uuid(),
|
|
52
170
|
__metadata("design:type", String)
|
|
53
171
|
], AuditEvent.prototype, "actorId", void 0);
|
|
54
172
|
__decorate([
|
|
@@ -56,13 +174,29 @@ __decorate([
|
|
|
56
174
|
__metadata("design:type", String)
|
|
57
175
|
], AuditEvent.prototype, "actorType", void 0);
|
|
58
176
|
__decorate([
|
|
59
|
-
|
|
177
|
+
Uuid({ nullable: true }),
|
|
178
|
+
__metadata("design:type", Object)
|
|
179
|
+
], AuditEvent.prototype, "impersonatorId", void 0);
|
|
180
|
+
__decorate([
|
|
181
|
+
Enumeration(ActorType, { nullable: true }),
|
|
182
|
+
__metadata("design:type", Object)
|
|
183
|
+
], AuditEvent.prototype, "impersonatorType", void 0);
|
|
184
|
+
__decorate([
|
|
185
|
+
Uuid(),
|
|
60
186
|
__metadata("design:type", String)
|
|
61
187
|
], AuditEvent.prototype, "targetId", void 0);
|
|
62
188
|
__decorate([
|
|
63
189
|
StringProperty(),
|
|
64
190
|
__metadata("design:type", String)
|
|
65
191
|
], AuditEvent.prototype, "targetType", void 0);
|
|
192
|
+
__decorate([
|
|
193
|
+
Embedded(RequestDetails),
|
|
194
|
+
__metadata("design:type", Object)
|
|
195
|
+
], AuditEvent.prototype, "network", void 0);
|
|
196
|
+
__decorate([
|
|
197
|
+
Embedded(ChangeDetails),
|
|
198
|
+
__metadata("design:type", Object)
|
|
199
|
+
], AuditEvent.prototype, "changes", void 0);
|
|
66
200
|
__decorate([
|
|
67
201
|
Json({ nullable: true }),
|
|
68
202
|
__metadata("design:type", Object)
|
package/audit/auditor.d.ts
CHANGED
|
@@ -1,42 +1,118 @@
|
|
|
1
1
|
import { type Resolvable, type resolveArgumentType } from '../injector/index.js';
|
|
2
|
-
import type { UndefinableJsonObject } from '../types/index.js';
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
correlationId?: string;
|
|
7
|
-
outcome?: AuditOutcome;
|
|
8
|
-
severity?: AuditSeverity;
|
|
9
|
-
actorId?: string;
|
|
10
|
-
actorType?: ActorType;
|
|
11
|
-
targetId?: string;
|
|
12
|
-
targetType?: string;
|
|
13
|
-
details?: UndefinableJsonObject;
|
|
2
|
+
import type { TypedOmit, UndefinableJsonObject } from '../types/index.js';
|
|
3
|
+
import { AuditEvent } from './audit.model.js';
|
|
4
|
+
export type AuditPayload = Partial<TypedOmit<AuditEvent, 'id' | 'timestamp' | 'module' | 'action' | 'details'> & {
|
|
5
|
+
details: UndefinableJsonObject;
|
|
14
6
|
}>;
|
|
15
7
|
export type AuditorArgument = string | string[] | {
|
|
8
|
+
/**
|
|
9
|
+
* The module or path of modules for the auditor.
|
|
10
|
+
*/
|
|
16
11
|
module?: string | string[];
|
|
12
|
+
/**
|
|
13
|
+
* Default context to apply to all events logged by this auditor instance.
|
|
14
|
+
*/
|
|
17
15
|
context?: Partial<AuditPayload>;
|
|
18
16
|
};
|
|
19
|
-
|
|
17
|
+
type AuditEvents = Record<string, UndefinableJsonObject>;
|
|
18
|
+
/**
|
|
19
|
+
* A service for logging audit events.
|
|
20
|
+
* It provides a structured way to record activities within the system.
|
|
21
|
+
* The Auditor can be forked to create sub-auditors for different modules or enriched with contextual data.
|
|
22
|
+
*
|
|
23
|
+
* @template Events A record mapping event action names to their specific `details` payload types.
|
|
24
|
+
*/
|
|
25
|
+
export declare class Auditor<Events extends AuditEvents = AuditEvents> implements Resolvable<AuditorArgument> {
|
|
20
26
|
#private;
|
|
27
|
+
/**
|
|
28
|
+
* The module path for this auditor instance.
|
|
29
|
+
*/
|
|
21
30
|
readonly module: string[];
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
actorType?: ActorType;
|
|
28
|
-
targetId?: string;
|
|
29
|
-
targetType?: string;
|
|
30
|
-
details?: UndefinableJsonObject;
|
|
31
|
+
/**
|
|
32
|
+
* The context that is automatically merged into every event logged by this auditor.
|
|
33
|
+
*/
|
|
34
|
+
readonly context: Partial<Partial<TypedOmit<AuditEvent<UndefinableJsonObject>, "module" | "timestamp" | "id" | "details" | "action"> & {
|
|
35
|
+
details: UndefinableJsonObject;
|
|
31
36
|
}>>;
|
|
37
|
+
/**
|
|
38
|
+
* A dot-separated string representation of the module path.
|
|
39
|
+
* @example ['user', 'authentication'] becomes 'user.authentication'
|
|
40
|
+
*/
|
|
32
41
|
get moduleString(): string;
|
|
33
42
|
readonly [resolveArgumentType]: AuditorArgument;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Creates a new `Auditor` instance for a submodule.
|
|
45
|
+
* The new auditor inherits the context of its parent and appends the submodule to its module path.
|
|
46
|
+
*
|
|
47
|
+
* @param subModule The name of the submodule or a path of submodules.
|
|
48
|
+
* @returns A new `Auditor` instance for the specified submodule.
|
|
49
|
+
* @template T The event map of the new Auditor instance.
|
|
50
|
+
*/
|
|
51
|
+
fork<T extends AuditEvents = Events>(subModule: string | string[]): Auditor<T>;
|
|
52
|
+
/**
|
|
53
|
+
* Creates a new `Auditor` instance with additional context.
|
|
54
|
+
* The new context is merged with the existing context.
|
|
55
|
+
*
|
|
56
|
+
* @param context The additional context to apply.
|
|
57
|
+
* @returns A new `Auditor` instance with the merged context.
|
|
58
|
+
* @template T The event map of the new Auditor instance.
|
|
59
|
+
*/
|
|
60
|
+
with<T extends AuditEvents = Events>(context: Partial<AuditPayload>): Auditor<T>;
|
|
61
|
+
/**
|
|
62
|
+
* Creates a new `Auditor` instance with a correlation ID in its context.
|
|
63
|
+
* If a correlation ID already exists in the context, it is preserved. Otherwise, a new UUID is generated.
|
|
64
|
+
*
|
|
65
|
+
* @returns A new `Auditor` instance with a correlation ID.
|
|
66
|
+
*/
|
|
67
|
+
withCorrelation(): Auditor<Events>;
|
|
68
|
+
/**
|
|
69
|
+
* Logs a generic audit event.
|
|
70
|
+
*
|
|
71
|
+
* @param action The name of the action being logged. Must be a key of the `Events` type.
|
|
72
|
+
* @param data The payload containing details about the event.
|
|
73
|
+
*/
|
|
74
|
+
log<E extends Extract<keyof Events, string>>(action: E, data?: AuditPayload & {
|
|
75
|
+
details: Events[E];
|
|
76
|
+
}): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Logs an informational event.
|
|
79
|
+
* Automatically sets severity to `Info` and defaults outcome to `Success`.
|
|
80
|
+
*
|
|
81
|
+
* @param action The name of the action being logged.
|
|
82
|
+
* @param data The payload containing details about the event.
|
|
83
|
+
*/
|
|
84
|
+
info<E extends Extract<keyof Events, string>>(action: E, data: AuditPayload & {
|
|
85
|
+
details: Events[E];
|
|
86
|
+
}): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Logs a warning event.
|
|
89
|
+
* Automatically sets severity to `Warn` and defaults outcome to `Failure`.
|
|
90
|
+
*
|
|
91
|
+
* @param action The name of the action being logged.
|
|
92
|
+
* @param data The payload containing details about the event.
|
|
93
|
+
*/
|
|
94
|
+
warn<E extends Extract<keyof Events, string>>(action: E, data: AuditPayload & {
|
|
95
|
+
details: Events[E];
|
|
96
|
+
}): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Logs an error event.
|
|
99
|
+
* Automatically sets severity to `Error` and defaults outcome to `Failure`.
|
|
100
|
+
*
|
|
101
|
+
* @param action The name of the action being logged.
|
|
102
|
+
* @param data The payload containing details about the event.
|
|
103
|
+
*/
|
|
104
|
+
error<E extends Extract<keyof Events, string>>(action: E, data: AuditPayload & {
|
|
105
|
+
details: Events[E];
|
|
106
|
+
}): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Logs a critical event.
|
|
109
|
+
* Automatically sets severity to `Critical` and defaults outcome to `Failure`.
|
|
110
|
+
*
|
|
111
|
+
* @param action The name of the action being logged.
|
|
112
|
+
* @param data The payload containing details about the event.
|
|
113
|
+
*/
|
|
114
|
+
critical<E extends Extract<keyof Events, string>>(action: E, data: AuditPayload & {
|
|
115
|
+
details: Events[E];
|
|
116
|
+
}): Promise<void>;
|
|
42
117
|
}
|
|
118
|
+
export {};
|
package/audit/auditor.js
CHANGED
|
@@ -19,46 +19,103 @@ import { assertDefinedPass, isArray, isNotArray, isObject, isString } from '../u
|
|
|
19
19
|
import { AuditEvent } from './audit.model.js';
|
|
20
20
|
import { AuditOutcome, AuditSeverity } from './types.js';
|
|
21
21
|
const { runInAuditorCreationContext, getCurrentAuditorCreationContext, isInAuditorCreationContext } = createContextProvider('AuditorCreation');
|
|
22
|
+
/**
|
|
23
|
+
* A service for logging audit events.
|
|
24
|
+
* It provides a structured way to record activities within the system.
|
|
25
|
+
* The Auditor can be forked to create sub-auditors for different modules or enriched with contextual data.
|
|
26
|
+
*
|
|
27
|
+
* @template Events A record mapping event action names to their specific `details` payload types.
|
|
28
|
+
*/
|
|
22
29
|
let Auditor = Auditor_1 = class Auditor {
|
|
23
30
|
#repository = injectRepository(AuditEvent);
|
|
24
31
|
#argument = isInAuditorCreationContext() ? injectArgument(this, { optional: true }) : undefined;
|
|
25
32
|
#creationContext = getCurrentAuditorCreationContext();
|
|
33
|
+
/**
|
|
34
|
+
* The module path for this auditor instance.
|
|
35
|
+
*/
|
|
26
36
|
module = this.#creationContext?.module ?? moduleFromArgument(this.#argument);
|
|
37
|
+
/**
|
|
38
|
+
* The context that is automatically merged into every event logged by this auditor.
|
|
39
|
+
*/
|
|
27
40
|
context = this.#creationContext?.context ?? ((isObject(this.#argument) && isNotArray(this.#argument)) ? (this.#argument.context ?? {}) : {});
|
|
41
|
+
/**
|
|
42
|
+
* A dot-separated string representation of the module path.
|
|
43
|
+
* @example ['user', 'authentication'] becomes 'user.authentication'
|
|
44
|
+
*/
|
|
28
45
|
get moduleString() {
|
|
29
46
|
return this.module.join('.');
|
|
30
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Creates a new `Auditor` instance for a submodule.
|
|
50
|
+
* The new auditor inherits the context of its parent and appends the submodule to its module path.
|
|
51
|
+
*
|
|
52
|
+
* @param subModule The name of the submodule or a path of submodules.
|
|
53
|
+
* @returns A new `Auditor` instance for the specified submodule.
|
|
54
|
+
* @template T The event map of the new Auditor instance.
|
|
55
|
+
*/
|
|
31
56
|
fork(subModule) {
|
|
32
57
|
return runInAuditorCreationContext({
|
|
33
58
|
module: [...this.module, ...toArray(subModule)],
|
|
34
59
|
context: this.context,
|
|
35
60
|
}, () => new Auditor_1());
|
|
36
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Creates a new `Auditor` instance with additional context.
|
|
64
|
+
* The new context is merged with the existing context.
|
|
65
|
+
*
|
|
66
|
+
* @param context The additional context to apply.
|
|
67
|
+
* @returns A new `Auditor` instance with the merged context.
|
|
68
|
+
* @template T The event map of the new Auditor instance.
|
|
69
|
+
*/
|
|
37
70
|
with(context) {
|
|
38
71
|
return runInAuditorCreationContext({
|
|
39
72
|
module: this.module,
|
|
40
73
|
context: { ...this.context, ...context, details: { ...this.context.details, ...context.details } },
|
|
41
74
|
}, () => new Auditor_1());
|
|
42
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Creates a new `Auditor` instance with a correlation ID in its context.
|
|
78
|
+
* If a correlation ID already exists in the context, it is preserved. Otherwise, a new UUID is generated.
|
|
79
|
+
*
|
|
80
|
+
* @returns A new `Auditor` instance with a correlation ID.
|
|
81
|
+
*/
|
|
43
82
|
withCorrelation() {
|
|
44
83
|
return this.with({ correlationId: this.context.correlationId ?? crypto.randomUUID() });
|
|
45
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Logs a generic audit event.
|
|
87
|
+
*
|
|
88
|
+
* @param action The name of the action being logged. Must be a key of the `Events` type.
|
|
89
|
+
* @param data The payload containing details about the event.
|
|
90
|
+
*/
|
|
46
91
|
async log(action, data) {
|
|
47
92
|
const mergedData = { ...this.context, ...data, details: filterUndefinedFromRecord({ ...this.context.details, ...data?.details }) };
|
|
48
93
|
await this.#repository.insert({
|
|
49
94
|
timestamp: TRANSACTION_TIMESTAMP,
|
|
50
|
-
|
|
95
|
+
tenantId: mergedData.tenantId ?? null,
|
|
96
|
+
correlationId: mergedData.correlationId ?? null,
|
|
51
97
|
module: this.moduleString,
|
|
52
98
|
action: action,
|
|
53
99
|
outcome: assertDefinedPass(mergedData.outcome, 'Audit outcome is required'),
|
|
54
100
|
severity: assertDefinedPass(mergedData.severity, 'Audit severity is required'),
|
|
55
101
|
actorId: assertDefinedPass(mergedData.actorId, 'Audit actorId is required'),
|
|
56
102
|
actorType: assertDefinedPass(mergedData.actorType, 'Audit actorType is required'),
|
|
103
|
+
impersonatorId: mergedData.impersonatorId ?? null,
|
|
104
|
+
impersonatorType: mergedData.impersonatorType ?? null,
|
|
57
105
|
targetId: assertDefinedPass(mergedData.targetId, 'Audit targetId is required'),
|
|
58
106
|
targetType: assertDefinedPass(mergedData.targetType, 'Audit targetType is required'),
|
|
59
|
-
|
|
107
|
+
network: mergedData.network ?? null,
|
|
108
|
+
changes: mergedData.changes ?? null,
|
|
109
|
+
details: (objectKeys(mergedData.details).length > 0) ? mergedData.details : null,
|
|
60
110
|
});
|
|
61
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Logs an informational event.
|
|
114
|
+
* Automatically sets severity to `Info` and defaults outcome to `Success`.
|
|
115
|
+
*
|
|
116
|
+
* @param action The name of the action being logged.
|
|
117
|
+
* @param data The payload containing details about the event.
|
|
118
|
+
*/
|
|
62
119
|
async info(action, data) {
|
|
63
120
|
await this.log(action, {
|
|
64
121
|
...data,
|
|
@@ -66,6 +123,13 @@ let Auditor = Auditor_1 = class Auditor {
|
|
|
66
123
|
outcome: data.outcome ?? AuditOutcome.Success,
|
|
67
124
|
});
|
|
68
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Logs a warning event.
|
|
128
|
+
* Automatically sets severity to `Warn` and defaults outcome to `Failure`.
|
|
129
|
+
*
|
|
130
|
+
* @param action The name of the action being logged.
|
|
131
|
+
* @param data The payload containing details about the event.
|
|
132
|
+
*/
|
|
69
133
|
async warn(action, data) {
|
|
70
134
|
await this.log(action, {
|
|
71
135
|
...data,
|
|
@@ -73,6 +137,13 @@ let Auditor = Auditor_1 = class Auditor {
|
|
|
73
137
|
outcome: data.outcome ?? AuditOutcome.Failure,
|
|
74
138
|
});
|
|
75
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Logs an error event.
|
|
142
|
+
* Automatically sets severity to `Error` and defaults outcome to `Failure`.
|
|
143
|
+
*
|
|
144
|
+
* @param action The name of the action being logged.
|
|
145
|
+
* @param data The payload containing details about the event.
|
|
146
|
+
*/
|
|
76
147
|
async error(action, data) {
|
|
77
148
|
await this.log(action, {
|
|
78
149
|
...data,
|
|
@@ -80,6 +151,13 @@ let Auditor = Auditor_1 = class Auditor {
|
|
|
80
151
|
outcome: data.outcome ?? AuditOutcome.Failure,
|
|
81
152
|
});
|
|
82
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Logs a critical event.
|
|
156
|
+
* Automatically sets severity to `Critical` and defaults outcome to `Failure`.
|
|
157
|
+
*
|
|
158
|
+
* @param action The name of the action being logged.
|
|
159
|
+
* @param data The payload containing details about the event.
|
|
160
|
+
*/
|
|
83
161
|
async critical(action, data) {
|
|
84
162
|
await this.log(action, {
|
|
85
163
|
...data,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
CREATE TYPE "audit"."actor_type" AS ENUM('user', 'system', 'api-key');--> statement-breakpoint
|
|
2
|
+
CREATE TYPE "audit"."audit_outcome" AS ENUM('pending', 'success', 'cancelled', 'failure', 'denied');--> statement-breakpoint
|
|
3
|
+
CREATE TYPE "audit"."audit_severity" AS ENUM('info', 'warn', 'error', 'critical');--> statement-breakpoint
|
|
4
|
+
CREATE TABLE "audit"."event" (
|
|
5
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
|
6
|
+
"timestamp" timestamp with time zone NOT NULL,
|
|
7
|
+
"tenant_id" uuid,
|
|
8
|
+
"correlation_id" uuid,
|
|
9
|
+
"module" text NOT NULL,
|
|
10
|
+
"action" text NOT NULL,
|
|
11
|
+
"outcome" "audit"."audit_outcome" NOT NULL,
|
|
12
|
+
"severity" "audit"."audit_severity" NOT NULL,
|
|
13
|
+
"actor_id" uuid NOT NULL,
|
|
14
|
+
"actor_type" "audit"."actor_type" NOT NULL,
|
|
15
|
+
"impersonator_id" uuid,
|
|
16
|
+
"impersonator_type" "audit"."actor_type",
|
|
17
|
+
"target_id" uuid NOT NULL,
|
|
18
|
+
"target_type" text NOT NULL,
|
|
19
|
+
"network_ip_address" text,
|
|
20
|
+
"network_user_agent" text,
|
|
21
|
+
"network_session_id" uuid,
|
|
22
|
+
"changes_before" jsonb,
|
|
23
|
+
"changes_after" jsonb,
|
|
24
|
+
"details" jsonb
|
|
25
|
+
);
|