@axova/shared 1.0.0

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 (112) hide show
  1. package/CONFIGURATION_GUIDE.md +1 -0
  2. package/README.md +384 -0
  3. package/SCHEMA_ORGANIZATION.md +209 -0
  4. package/dist/configs/index.d.ts +85 -0
  5. package/dist/configs/index.js +555 -0
  6. package/dist/events/kafka.d.ts +40 -0
  7. package/dist/events/kafka.js +311 -0
  8. package/dist/index.d.ts +13 -0
  9. package/dist/index.js +41 -0
  10. package/dist/interfaces/customer-events.d.ts +85 -0
  11. package/dist/interfaces/customer-events.js +2 -0
  12. package/dist/interfaces/inventory-events.d.ts +453 -0
  13. package/dist/interfaces/inventory-events.js +3 -0
  14. package/dist/interfaces/inventory-types.d.ts +894 -0
  15. package/dist/interfaces/inventory-types.js +3 -0
  16. package/dist/interfaces/order-events.d.ts +320 -0
  17. package/dist/interfaces/order-events.js +3 -0
  18. package/dist/lib/auditLogger.d.ts +162 -0
  19. package/dist/lib/auditLogger.js +626 -0
  20. package/dist/lib/authOrganization.d.ts +24 -0
  21. package/dist/lib/authOrganization.js +110 -0
  22. package/dist/lib/db.d.ts +6 -0
  23. package/dist/lib/db.js +88 -0
  24. package/dist/middleware/serviceAuth.d.ts +60 -0
  25. package/dist/middleware/serviceAuth.js +272 -0
  26. package/dist/middleware/storeOwnership.d.ts +15 -0
  27. package/dist/middleware/storeOwnership.js +156 -0
  28. package/dist/middleware/storeValidationMiddleware.d.ts +44 -0
  29. package/dist/middleware/storeValidationMiddleware.js +180 -0
  30. package/dist/middleware/userAuth.d.ts +27 -0
  31. package/dist/middleware/userAuth.js +218 -0
  32. package/dist/schemas/admin/admin-schema.d.ts +741 -0
  33. package/dist/schemas/admin/admin-schema.js +111 -0
  34. package/dist/schemas/ai-moderation/ai-moderation-schema.d.ts +648 -0
  35. package/dist/schemas/ai-moderation/ai-moderation-schema.js +88 -0
  36. package/dist/schemas/common/common-schemas.d.ts +436 -0
  37. package/dist/schemas/common/common-schemas.js +94 -0
  38. package/dist/schemas/compliance/compliance-schema.d.ts +3388 -0
  39. package/dist/schemas/compliance/compliance-schema.js +472 -0
  40. package/dist/schemas/compliance/kyc-schema.d.ts +2642 -0
  41. package/dist/schemas/compliance/kyc-schema.js +361 -0
  42. package/dist/schemas/customer/customer-schema.d.ts +2727 -0
  43. package/dist/schemas/customer/customer-schema.js +399 -0
  44. package/dist/schemas/index.d.ts +27 -0
  45. package/dist/schemas/index.js +138 -0
  46. package/dist/schemas/inventory/inventory-tables.d.ts +9476 -0
  47. package/dist/schemas/inventory/inventory-tables.js +1470 -0
  48. package/dist/schemas/inventory/lot-tables.d.ts +3281 -0
  49. package/dist/schemas/inventory/lot-tables.js +608 -0
  50. package/dist/schemas/order/order-schema.d.ts +5825 -0
  51. package/dist/schemas/order/order-schema.js +954 -0
  52. package/dist/schemas/product/discount-relations.d.ts +15 -0
  53. package/dist/schemas/product/discount-relations.js +34 -0
  54. package/dist/schemas/product/discount-schema.d.ts +1975 -0
  55. package/dist/schemas/product/discount-schema.js +297 -0
  56. package/dist/schemas/product/product-relations.d.ts +41 -0
  57. package/dist/schemas/product/product-relations.js +133 -0
  58. package/dist/schemas/product/product-schema.d.ts +4544 -0
  59. package/dist/schemas/product/product-schema.js +671 -0
  60. package/dist/schemas/store/store-audit-schema.d.ts +4135 -0
  61. package/dist/schemas/store/store-audit-schema.js +556 -0
  62. package/dist/schemas/store/store-schema.d.ts +3100 -0
  63. package/dist/schemas/store/store-schema.js +381 -0
  64. package/dist/schemas/store/store-settings-schema.d.ts +665 -0
  65. package/dist/schemas/store/store-settings-schema.js +141 -0
  66. package/dist/schemas/types.d.ts +50 -0
  67. package/dist/schemas/types.js +3 -0
  68. package/dist/types/events.d.ts +2396 -0
  69. package/dist/types/events.js +505 -0
  70. package/dist/utils/errorHandler.d.ts +12 -0
  71. package/dist/utils/errorHandler.js +36 -0
  72. package/dist/utils/subdomain.d.ts +6 -0
  73. package/dist/utils/subdomain.js +20 -0
  74. package/nul +8 -0
  75. package/package.json +43 -0
  76. package/src/configs/index.ts +654 -0
  77. package/src/events/kafka.ts +429 -0
  78. package/src/index.ts +26 -0
  79. package/src/interfaces/customer-events.ts +106 -0
  80. package/src/interfaces/inventory-events.ts +545 -0
  81. package/src/interfaces/inventory-types.ts +1004 -0
  82. package/src/interfaces/order-events.ts +381 -0
  83. package/src/lib/auditLogger.ts +1117 -0
  84. package/src/lib/authOrganization.ts +153 -0
  85. package/src/lib/db.ts +64 -0
  86. package/src/middleware/serviceAuth.ts +328 -0
  87. package/src/middleware/storeOwnership.ts +199 -0
  88. package/src/middleware/storeValidationMiddleware.ts +247 -0
  89. package/src/middleware/userAuth.ts +248 -0
  90. package/src/schemas/admin/admin-schema.ts +208 -0
  91. package/src/schemas/ai-moderation/ai-moderation-schema.ts +180 -0
  92. package/src/schemas/common/common-schemas.ts +108 -0
  93. package/src/schemas/compliance/compliance-schema.ts +927 -0
  94. package/src/schemas/compliance/kyc-schema.ts +649 -0
  95. package/src/schemas/customer/customer-schema.ts +576 -0
  96. package/src/schemas/index.ts +189 -0
  97. package/src/schemas/inventory/inventory-tables.ts +1927 -0
  98. package/src/schemas/inventory/lot-tables.ts +799 -0
  99. package/src/schemas/order/order-schema.ts +1400 -0
  100. package/src/schemas/product/discount-relations.ts +44 -0
  101. package/src/schemas/product/discount-schema.ts +464 -0
  102. package/src/schemas/product/product-relations.ts +187 -0
  103. package/src/schemas/product/product-schema.ts +955 -0
  104. package/src/schemas/store/ethiopian_business_api.md.resolved +212 -0
  105. package/src/schemas/store/store-audit-schema.ts +1257 -0
  106. package/src/schemas/store/store-schema.ts +661 -0
  107. package/src/schemas/store/store-settings-schema.ts +231 -0
  108. package/src/schemas/types.ts +67 -0
  109. package/src/types/events.ts +646 -0
  110. package/src/utils/errorHandler.ts +44 -0
  111. package/src/utils/subdomain.ts +19 -0
  112. package/tsconfig.json +21 -0
@@ -0,0 +1,1117 @@
1
+ import { performance } from "node:perf_hooks";
2
+ import { createId } from "@paralleldrive/cuid2";
3
+
4
+ // Re-export types from audit service for convenience
5
+ export interface AuditLogEntry {
6
+ eventType:
7
+ | "CREATE"
8
+ | "UPDATE"
9
+ | "DELETE"
10
+ | "LOGIN"
11
+ | "LOGOUT"
12
+ | "ACCESS"
13
+ | "PERMISSION_CHANGE"
14
+ | "SYSTEM_ACTION"
15
+ | "COMPLIANCE_ACTION"
16
+ | "SECURITY_EVENT";
17
+ eventCategory:
18
+ | "USER_ACTION"
19
+ | "SYSTEM_ACTION"
20
+ | "ADMIN_ACTION"
21
+ | "COMPLIANCE_ACTION"
22
+ | "SECURITY_ACTION"
23
+ | "DATA_ACTION"
24
+ | "NOTIFICATION_ACTION";
25
+ action: string;
26
+ resource: string;
27
+ resourceId: string;
28
+ resourceType?:
29
+ | "STORE"
30
+ | "USER"
31
+ | "PRODUCT"
32
+ | "ORDER"
33
+ | "COMPLIANCE"
34
+ | "POLICY"
35
+ | "VIOLATION"
36
+ | "APPEAL"
37
+ | "NOTIFICATION"
38
+ | "SYSTEM"
39
+ | "OTHER";
40
+ performedBy: string;
41
+ performedByType:
42
+ | "USER"
43
+ | "SYSTEM"
44
+ | "ADMIN"
45
+ | "SERVICE"
46
+ | "AI"
47
+ | "AUTOMATION"
48
+ | "ANONYMOUS";
49
+ performedByName?: string;
50
+ sessionId?: string;
51
+ requestId?: string;
52
+ correlationId?: string;
53
+ ipAddress?: string;
54
+ userAgent?: string;
55
+ clientType?:
56
+ | "WEB"
57
+ | "MOBILE_APP"
58
+ | "API"
59
+ | "ADMIN_PANEL"
60
+ | "SYSTEM"
61
+ | "WEBHOOK"
62
+ | "CRON_JOB";
63
+ clientId?: string;
64
+ clientVersion?: string;
65
+ location?: {
66
+ country?: string;
67
+ region?: string;
68
+ city?: string;
69
+ coordinates?: { lat: number; lng: number };
70
+ timezone?: string;
71
+ };
72
+ changes?: {
73
+ before?: Record<string, unknown>;
74
+ after?: Record<string, unknown>;
75
+ fields?: string[];
76
+ };
77
+ metadata?: {
78
+ businessImpact?: string;
79
+ severity?: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
80
+ tags?: string[];
81
+ department?: string;
82
+ project?: string;
83
+ feature?: string;
84
+ version?: string;
85
+ environment?: "DEVELOPMENT" | "STAGING" | "PRODUCTION";
86
+ customFields?: Record<string, unknown>;
87
+ };
88
+ success?: boolean;
89
+ errorCode?: string;
90
+ errorMessage?: string;
91
+ stackTrace?: string;
92
+ duration?: number;
93
+ size?: number;
94
+ sensitiveData?: boolean;
95
+ piiInvolved?: boolean;
96
+ complianceRelevant?: boolean;
97
+ retentionPeriod?: number;
98
+ riskLevel?: "NONE" | "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
99
+ securityImpact?: "NONE" | "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
100
+ workflowId?: string;
101
+ workflowStep?: string;
102
+ parentEventId?: string;
103
+ storeId?: string;
104
+ storeName?: string;
105
+ storeSubdomain?: string;
106
+ }
107
+
108
+ // Context interface for automatic enrichment
109
+ export interface AuditContext {
110
+ serviceName: string;
111
+ serviceVersion?: string;
112
+ requestId?: string;
113
+ correlationId?: string;
114
+ sessionId?: string;
115
+ userId?: string;
116
+ userType?: "USER" | "ADMIN" | "SYSTEM" | "SERVICE";
117
+ ipAddress?: string;
118
+ userAgent?: string;
119
+ clientId?: string;
120
+ clientType?:
121
+ | "WEB"
122
+ | "MOBILE_APP"
123
+ | "API"
124
+ | "ADMIN_PANEL"
125
+ | "SYSTEM"
126
+ | "WEBHOOK"
127
+ | "CRON_JOB";
128
+ storeId?: string;
129
+ storeName?: string;
130
+ environment?: "DEVELOPMENT" | "STAGING" | "PRODUCTION";
131
+ features?: string[];
132
+ tags?: string[];
133
+ customMetadata?: Record<string, unknown>;
134
+ }
135
+
136
+ // Performance tracker for duration logging
137
+ export interface PerformanceTracker {
138
+ start: number;
139
+ end(): number;
140
+ duration(): number;
141
+ }
142
+
143
+ // Audit logger configuration
144
+ export interface AuditLoggerConfig {
145
+ serviceName: string;
146
+ serviceVersion?: string;
147
+ environment?: "DEVELOPMENT" | "STAGING" | "PRODUCTION";
148
+ auditServiceUrl?: string;
149
+ auditServiceApiKey?: string;
150
+ enableLocalLogging?: boolean;
151
+ enableRemoteLogging?: boolean;
152
+ bufferSize?: number;
153
+ flushInterval?: number;
154
+ defaultRetentionDays?: number;
155
+ autoEnrichment?: boolean;
156
+ sensitiveFieldMasking?: boolean;
157
+ geoLocationEnabled?: boolean;
158
+ }
159
+
160
+ // Quick action builders for common operations
161
+ export interface QuickActionBuilders {
162
+ userLogin: (
163
+ userId: string,
164
+ additionalData?: Partial<AuditLogEntry>,
165
+ ) => AuditLogEntry;
166
+ userLogout: (
167
+ userId: string,
168
+ additionalData?: Partial<AuditLogEntry>,
169
+ ) => AuditLogEntry;
170
+ dataAccess: (
171
+ resource: string,
172
+ resourceId: string,
173
+ userId: string,
174
+ additionalData?: Partial<AuditLogEntry>,
175
+ ) => AuditLogEntry;
176
+ dataCreate: (
177
+ resource: string,
178
+ resourceId: string,
179
+ userId: string,
180
+ data?: unknown,
181
+ additionalData?: Partial<AuditLogEntry>,
182
+ ) => AuditLogEntry;
183
+ dataUpdate: (
184
+ resource: string,
185
+ resourceId: string,
186
+ userId: string,
187
+ changes?: unknown,
188
+ additionalData?: Partial<AuditLogEntry>,
189
+ ) => AuditLogEntry;
190
+ dataDelete: (
191
+ resource: string,
192
+ resourceId: string,
193
+ userId: string,
194
+ additionalData?: Partial<AuditLogEntry>,
195
+ ) => AuditLogEntry;
196
+ permissionChange: (
197
+ targetUserId: string,
198
+ changedBy: string,
199
+ changes: unknown,
200
+ additionalData?: Partial<AuditLogEntry>,
201
+ ) => AuditLogEntry;
202
+ securityEvent: (
203
+ eventType: string,
204
+ description: string,
205
+ severity: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL",
206
+ additionalData?: Partial<AuditLogEntry>,
207
+ ) => AuditLogEntry;
208
+ complianceEvent: (
209
+ framework: string,
210
+ requirement: string,
211
+ status: "MET" | "NOT_MET",
212
+ additionalData?: Partial<AuditLogEntry>,
213
+ ) => AuditLogEntry;
214
+ systemAction: (
215
+ action: string,
216
+ resource: string,
217
+ resourceId: string,
218
+ additionalData?: Partial<AuditLogEntry>,
219
+ ) => AuditLogEntry;
220
+ storeAction: (
221
+ storeId: string,
222
+ action: string,
223
+ resource: string,
224
+ resourceId: string,
225
+ userId?: string,
226
+ additionalData?: Partial<AuditLogEntry>,
227
+ ) => AuditLogEntry;
228
+ }
229
+
230
+ // Shared Audit Logger Class
231
+ export class AuditLogger {
232
+ private config: AuditLoggerConfig;
233
+ private context: AuditContext;
234
+ private buffer: AuditLogEntry[] = [];
235
+ private flushTimer: NodeJS.Timeout | null = null;
236
+ private performanceTrackers = new Map<string, PerformanceTracker>();
237
+
238
+ constructor(
239
+ config: AuditLoggerConfig,
240
+ initialContext?: Partial<AuditContext>,
241
+ ) {
242
+ this.config = {
243
+ bufferSize: 100,
244
+ flushInterval: 5000,
245
+ defaultRetentionDays: 2555,
246
+ autoEnrichment: true,
247
+ sensitiveFieldMasking: true,
248
+ geoLocationEnabled: false,
249
+ enableLocalLogging: true,
250
+ enableRemoteLogging: true,
251
+ ...config,
252
+ };
253
+
254
+ this.context = {
255
+ serviceName: config.serviceName,
256
+ serviceVersion: config.serviceVersion,
257
+ environment:
258
+ config.environment ||
259
+ (process.env.NODE_ENV as "DEVELOPMENT" | "STAGING" | "PRODUCTION") ||
260
+ "DEVELOPMENT",
261
+ ...initialContext,
262
+ };
263
+
264
+ this.setupAutoFlush();
265
+ }
266
+
267
+ // Update context (useful for request-scoped information)
268
+ updateContext(contextUpdate: Partial<AuditContext>): void {
269
+ this.context = { ...this.context, ...contextUpdate };
270
+ }
271
+
272
+ // Get current context
273
+ getContext(): AuditContext {
274
+ return { ...this.context };
275
+ }
276
+
277
+ // Log a custom audit event
278
+ async log(entry: Partial<AuditLogEntry>): Promise<string> {
279
+ try {
280
+ const enrichedEntry = await this.enrichEntry(entry);
281
+ const auditId = createId();
282
+
283
+ // Add to buffer
284
+ this.addToBuffer(enrichedEntry);
285
+
286
+ // Local logging
287
+ if (this.config.enableLocalLogging) {
288
+ this.logLocally(enrichedEntry, auditId);
289
+ }
290
+
291
+ // Check if immediate flush is needed for critical events
292
+ if (this.isCriticalEvent(enrichedEntry)) {
293
+ await this.flush();
294
+ }
295
+
296
+ return auditId;
297
+ } catch (error) {
298
+ console.error("Failed to log audit event:", error);
299
+ throw error;
300
+ }
301
+ }
302
+
303
+ // Performance tracking helpers
304
+ startPerformanceTracking(operationId?: string): string {
305
+ const id = operationId || createId();
306
+ const tracker: PerformanceTracker = {
307
+ start: performance.now(),
308
+ end: () => performance.now(),
309
+ duration: function () {
310
+ return this.end() - this.start;
311
+ },
312
+ };
313
+
314
+ this.performanceTrackers.set(id, tracker);
315
+ return id;
316
+ }
317
+
318
+ endPerformanceTracking(operationId: string): number {
319
+ const tracker = this.performanceTrackers.get(operationId);
320
+ if (tracker) {
321
+ const duration = tracker.duration();
322
+ this.performanceTrackers.delete(operationId);
323
+ return Math.round(duration);
324
+ }
325
+ return 0;
326
+ }
327
+
328
+ // Quick action builders
329
+ get quick(): QuickActionBuilders {
330
+ return {
331
+ userLogin: (userId: string, additionalData?: Partial<AuditLogEntry>) => ({
332
+ eventType: "LOGIN",
333
+ eventCategory: "USER_ACTION",
334
+ action: "user_login",
335
+ resource: "authentication",
336
+ resourceId: userId,
337
+ resourceType: "USER",
338
+ performedBy: userId,
339
+ performedByType: "USER",
340
+ riskLevel: "LOW",
341
+ complianceRelevant: true,
342
+ ...additionalData,
343
+ }),
344
+
345
+ userLogout: (
346
+ userId: string,
347
+ additionalData?: Partial<AuditLogEntry>,
348
+ ) => ({
349
+ eventType: "LOGOUT",
350
+ eventCategory: "USER_ACTION",
351
+ action: "user_logout",
352
+ resource: "authentication",
353
+ resourceId: userId,
354
+ resourceType: "USER",
355
+ performedBy: userId,
356
+ performedByType: "USER",
357
+ riskLevel: "NONE",
358
+ ...additionalData,
359
+ }),
360
+
361
+ dataAccess: (
362
+ resource: string,
363
+ resourceId: string,
364
+ userId: string,
365
+ additionalData?: Partial<AuditLogEntry>,
366
+ ) => ({
367
+ eventType: "ACCESS",
368
+ eventCategory: "DATA_ACTION",
369
+ action: "data_access",
370
+ resource,
371
+ resourceId,
372
+ performedBy: userId,
373
+ performedByType: "USER",
374
+ riskLevel: "LOW",
375
+ complianceRelevant: true,
376
+ ...additionalData,
377
+ }),
378
+
379
+ dataCreate: (
380
+ resource: string,
381
+ resourceId: string,
382
+ userId: string,
383
+ data?: unknown,
384
+ additionalData?: Partial<AuditLogEntry>,
385
+ ): AuditLogEntry => ({
386
+ eventType: "CREATE",
387
+ eventCategory: "DATA_ACTION",
388
+ action: "data_create",
389
+ resource,
390
+ resourceId,
391
+ performedBy: userId,
392
+ performedByType: "USER",
393
+ changes: data ? { after: data as Record<string, unknown> } : undefined,
394
+ riskLevel: "LOW",
395
+ ...additionalData,
396
+ }),
397
+
398
+ dataUpdate: (
399
+ resource: string,
400
+ resourceId: string,
401
+ userId: string,
402
+ changes?: unknown,
403
+ additionalData?: Partial<AuditLogEntry>,
404
+ ): AuditLogEntry => ({
405
+ eventType: "UPDATE",
406
+ eventCategory: "DATA_ACTION",
407
+ action: "data_update",
408
+ resource,
409
+ resourceId,
410
+ performedBy: userId,
411
+ performedByType: "USER",
412
+ changes: changes
413
+ ? {
414
+ before: (changes as any)?.before,
415
+ after: (changes as any)?.after,
416
+ fields: (changes as any)?.fields,
417
+ }
418
+ : undefined,
419
+ riskLevel: "MEDIUM",
420
+ ...additionalData,
421
+ }),
422
+
423
+ dataDelete: (
424
+ resource: string,
425
+ resourceId: string,
426
+ userId: string,
427
+ additionalData?: Partial<AuditLogEntry>,
428
+ ) => ({
429
+ eventType: "DELETE",
430
+ eventCategory: "DATA_ACTION",
431
+ action: "data_delete",
432
+ resource,
433
+ resourceId,
434
+ performedBy: userId,
435
+ performedByType: "USER",
436
+ riskLevel: "HIGH",
437
+ complianceRelevant: true,
438
+ ...additionalData,
439
+ }),
440
+
441
+ permissionChange: (
442
+ targetUserId: string,
443
+ changedBy: string,
444
+ changes: unknown,
445
+ additionalData?: Partial<AuditLogEntry>,
446
+ ): AuditLogEntry => ({
447
+ eventType: "PERMISSION_CHANGE",
448
+ eventCategory: "ADMIN_ACTION",
449
+ action: "permission_change",
450
+ resource: "user_permissions",
451
+ resourceId: targetUserId,
452
+ performedBy: changedBy,
453
+ performedByType: "ADMIN",
454
+ changes: changes
455
+ ? {
456
+ before: (changes as any)?.before,
457
+ after: (changes as any)?.after,
458
+ fields: (changes as any)?.fields,
459
+ }
460
+ : undefined,
461
+ riskLevel: "HIGH",
462
+ securityImpact: "MEDIUM",
463
+ ...additionalData,
464
+ }),
465
+
466
+ securityEvent: (
467
+ eventType: string,
468
+ description: string,
469
+ severity: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL",
470
+ additionalData?: Partial<AuditLogEntry>,
471
+ ) => ({
472
+ eventType: "SECURITY_EVENT",
473
+ eventCategory: "SECURITY_ACTION",
474
+ action: eventType,
475
+ resource: "security",
476
+ resourceId: createId(),
477
+ performedBy: "SYSTEM",
478
+ performedByType: "SYSTEM",
479
+ riskLevel: severity,
480
+ securityImpact: severity,
481
+ metadata: {
482
+ severity,
483
+ description,
484
+ businessImpact:
485
+ severity === "CRITICAL"
486
+ ? "Service disruption possible"
487
+ : "Monitoring required",
488
+ },
489
+ ...additionalData,
490
+ }),
491
+
492
+ complianceEvent: (
493
+ framework: string,
494
+ requirement: string,
495
+ status: "MET" | "NOT_MET",
496
+ additionalData?: Partial<AuditLogEntry>,
497
+ ) => ({
498
+ eventType: "COMPLIANCE_ACTION",
499
+ eventCategory: "COMPLIANCE_ACTION",
500
+ action: "compliance_check",
501
+ resource: "compliance_requirement",
502
+ resourceId: `${framework}_${requirement}`,
503
+ performedBy: "SYSTEM",
504
+ performedByType: "SYSTEM",
505
+ complianceRelevant: true,
506
+ riskLevel: status === "NOT_MET" ? "HIGH" : "LOW",
507
+ metadata: {
508
+ framework,
509
+ requirement,
510
+ status,
511
+ businessImpact:
512
+ status === "NOT_MET"
513
+ ? "Compliance violation detected"
514
+ : "Compliance maintained",
515
+ },
516
+ ...additionalData,
517
+ }),
518
+
519
+ systemAction: (
520
+ action: string,
521
+ resource: string,
522
+ resourceId: string,
523
+ additionalData?: Partial<AuditLogEntry>,
524
+ ) => ({
525
+ eventType: "SYSTEM_ACTION",
526
+ eventCategory: "SYSTEM_ACTION",
527
+ action,
528
+ resource,
529
+ resourceId,
530
+ performedBy: this.context.serviceName,
531
+ performedByType: "SYSTEM",
532
+ riskLevel: "NONE",
533
+ ...additionalData,
534
+ }),
535
+
536
+ storeAction: (
537
+ storeId: string,
538
+ action: string,
539
+ resource: string,
540
+ resourceId: string,
541
+ userId?: string,
542
+ additionalData?: Partial<AuditLogEntry>,
543
+ ) => ({
544
+ eventType: "UPDATE",
545
+ eventCategory: userId ? "USER_ACTION" : "SYSTEM_ACTION",
546
+ action,
547
+ resource,
548
+ resourceId,
549
+ resourceType: "STORE",
550
+ performedBy: userId || this.context.serviceName,
551
+ performedByType: userId ? "USER" : "SYSTEM",
552
+ storeId,
553
+ riskLevel: "LOW",
554
+ ...additionalData,
555
+ }),
556
+ };
557
+ }
558
+
559
+ // Convenience methods for common audit events
560
+ async logUserLogin(
561
+ userId: string,
562
+ additionalData?: Partial<AuditLogEntry>,
563
+ ): Promise<string> {
564
+ return this.log(this.quick.userLogin(userId, additionalData));
565
+ }
566
+
567
+ async logUserLogout(
568
+ userId: string,
569
+ additionalData?: Partial<AuditLogEntry>,
570
+ ): Promise<string> {
571
+ return this.log(this.quick.userLogout(userId, additionalData));
572
+ }
573
+
574
+ async logDataAccess(
575
+ resource: string,
576
+ resourceId: string,
577
+ userId: string,
578
+ additionalData?: Partial<AuditLogEntry>,
579
+ ): Promise<string> {
580
+ return this.log(
581
+ this.quick.dataAccess(resource, resourceId, userId, additionalData),
582
+ );
583
+ }
584
+
585
+ async logDataCreate(
586
+ resource: string,
587
+ resourceId: string,
588
+ userId: string,
589
+ data?: unknown,
590
+ additionalData?: Partial<AuditLogEntry>,
591
+ ): Promise<string> {
592
+ return this.log(
593
+ this.quick.dataCreate(resource, resourceId, userId, data, additionalData),
594
+ );
595
+ }
596
+
597
+ async logDataUpdate(
598
+ resource: string,
599
+ resourceId: string,
600
+ userId: string,
601
+ changes?: unknown,
602
+ additionalData?: Partial<AuditLogEntry>,
603
+ ): Promise<string> {
604
+ return this.log(
605
+ this.quick.dataUpdate(
606
+ resource,
607
+ resourceId,
608
+ userId,
609
+ changes,
610
+ additionalData,
611
+ ),
612
+ );
613
+ }
614
+
615
+ async logDataDelete(
616
+ resource: string,
617
+ resourceId: string,
618
+ userId: string,
619
+ additionalData?: Partial<AuditLogEntry>,
620
+ ): Promise<string> {
621
+ return this.log(
622
+ this.quick.dataDelete(resource, resourceId, userId, additionalData),
623
+ );
624
+ }
625
+
626
+ async logSecurityEvent(
627
+ eventType: string,
628
+ description: string,
629
+ severity: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL",
630
+ additionalData?: Partial<AuditLogEntry>,
631
+ ): Promise<string> {
632
+ return this.log(
633
+ this.quick.securityEvent(
634
+ eventType,
635
+ description,
636
+ severity,
637
+ additionalData,
638
+ ),
639
+ );
640
+ }
641
+
642
+ async logStoreAction(
643
+ storeId: string,
644
+ action: string,
645
+ resource: string,
646
+ resourceId: string,
647
+ userId?: string,
648
+ additionalData?: Partial<AuditLogEntry>,
649
+ ): Promise<string> {
650
+ return this.log(
651
+ this.quick.storeAction(
652
+ storeId,
653
+ action,
654
+ resource,
655
+ resourceId,
656
+ userId,
657
+ additionalData,
658
+ ),
659
+ );
660
+ }
661
+
662
+ // Wrapper for async operations with automatic audit logging
663
+ async auditedOperation<T>(
664
+ operationName: string,
665
+ operation: () => Promise<T>,
666
+ auditData: Partial<AuditLogEntry>,
667
+ ): Promise<{ result: T; auditId: string; duration: number }> {
668
+ const trackingId = this.startPerformanceTracking();
669
+ const _startTime = Date.now();
670
+
671
+ try {
672
+ const result = await operation();
673
+ const duration = this.endPerformanceTracking(trackingId);
674
+
675
+ const _auditId = await this.log({
676
+ ...auditData,
677
+ action: operationName,
678
+ success: true,
679
+ duration,
680
+ });
681
+
682
+ return { result, auditId: _auditId, duration };
683
+ } catch (error) {
684
+ const duration = this.endPerformanceTracking(trackingId);
685
+
686
+ const _auditId = await this.log({
687
+ ...auditData,
688
+ action: operationName,
689
+ success: false,
690
+ duration,
691
+ errorMessage: error instanceof Error ? error.message : "Unknown error",
692
+ stackTrace: error instanceof Error ? error.stack : undefined,
693
+ riskLevel: "MEDIUM",
694
+ });
695
+
696
+ throw error;
697
+ }
698
+ }
699
+
700
+ // Bulk logging for batch operations
701
+ async logBatch(entries: Partial<AuditLogEntry>[]): Promise<string[]> {
702
+ try {
703
+ const enrichedEntries = await Promise.all(
704
+ entries.map((entry) => this.enrichEntry(entry)),
705
+ );
706
+
707
+ const auditIds = enrichedEntries.map(() => createId());
708
+
709
+ // Add all to buffer
710
+ enrichedEntries.forEach((entry) => this.addToBuffer(entry));
711
+
712
+ // Local logging
713
+ if (this.config.enableLocalLogging) {
714
+ enrichedEntries.forEach((entry, index) => {
715
+ this.logLocally(entry, auditIds[index]);
716
+ });
717
+ }
718
+
719
+ // Check if any critical events require immediate flush
720
+ const hasCriticalEvents = enrichedEntries.some((entry) =>
721
+ this.isCriticalEvent(entry),
722
+ );
723
+ if (hasCriticalEvents) {
724
+ await this.flush();
725
+ }
726
+
727
+ return auditIds;
728
+ } catch (error) {
729
+ console.error("Failed to log batch audit events:", error);
730
+ throw error;
731
+ }
732
+ }
733
+
734
+ // Flush buffer manually
735
+ async flush(): Promise<void> {
736
+ if (this.buffer.length === 0) return;
737
+
738
+ try {
739
+ const entriesToFlush = this.buffer.splice(0);
740
+
741
+ if (this.config.enableRemoteLogging) {
742
+ await this.sendToAuditService(entriesToFlush);
743
+ }
744
+
745
+ console.log(`✅ Flushed ${entriesToFlush.length} audit log entries`);
746
+ } catch (error) {
747
+ console.error("Failed to flush audit log buffer:", error);
748
+ // Re-add entries to buffer for retry
749
+ this.buffer.unshift(...this.buffer);
750
+ throw error;
751
+ }
752
+ }
753
+
754
+ // Private helper methods
755
+
756
+ private async enrichEntry(
757
+ entry: Partial<AuditLogEntry>,
758
+ ): Promise<AuditLogEntry> {
759
+ const baseEntry: AuditLogEntry = {
760
+ eventType: entry.eventType || "SYSTEM_ACTION",
761
+ eventCategory: entry.eventCategory || "SYSTEM_ACTION",
762
+ action: entry.action || "unknown_action",
763
+ resource: entry.resource || "unknown_resource",
764
+ resourceId: entry.resourceId || createId(),
765
+ performedBy: entry.performedBy || this.context.serviceName,
766
+ performedByType: entry.performedByType || "SYSTEM",
767
+ success: entry.success ?? true,
768
+ riskLevel: entry.riskLevel || "NONE",
769
+ securityImpact: entry.securityImpact || "NONE",
770
+ retentionPeriod:
771
+ entry.retentionPeriod || this.config.defaultRetentionDays,
772
+ ...entry,
773
+ };
774
+
775
+ // Auto-enrichment from context
776
+ if (this.config.autoEnrichment) {
777
+ if (this.context.requestId && !baseEntry.requestId) {
778
+ baseEntry.requestId = this.context.requestId;
779
+ }
780
+ if (this.context.correlationId && !baseEntry.correlationId) {
781
+ baseEntry.correlationId = this.context.correlationId;
782
+ }
783
+ if (this.context.sessionId && !baseEntry.sessionId) {
784
+ baseEntry.sessionId = this.context.sessionId;
785
+ }
786
+ if (this.context.ipAddress && !baseEntry.ipAddress) {
787
+ baseEntry.ipAddress = this.context.ipAddress;
788
+ }
789
+ if (this.context.userAgent && !baseEntry.userAgent) {
790
+ baseEntry.userAgent = this.context.userAgent;
791
+ }
792
+ if (this.context.clientId && !baseEntry.clientId) {
793
+ baseEntry.clientId = this.context.clientId;
794
+ }
795
+ if (this.context.clientType && !baseEntry.clientType) {
796
+ baseEntry.clientType = this.context.clientType;
797
+ }
798
+ if (this.context.storeId && !baseEntry.storeId) {
799
+ baseEntry.storeId = this.context.storeId;
800
+ }
801
+ if (this.context.storeName && !baseEntry.storeName) {
802
+ baseEntry.storeName = this.context.storeName;
803
+ }
804
+
805
+ // Enrich metadata
806
+ if (!baseEntry.metadata) {
807
+ baseEntry.metadata = {};
808
+ }
809
+
810
+ baseEntry.metadata.environment = this.context.environment;
811
+ baseEntry.metadata.version = this.context.serviceVersion;
812
+
813
+ if (this.context.features?.length) {
814
+ baseEntry.metadata.tags = [
815
+ ...(baseEntry.metadata.tags || []),
816
+ ...this.context.features,
817
+ ];
818
+ }
819
+
820
+ if (this.context.tags?.length) {
821
+ baseEntry.metadata.tags = [
822
+ ...(baseEntry.metadata.tags || []),
823
+ ...this.context.tags,
824
+ ];
825
+ }
826
+
827
+ if (this.context.customMetadata) {
828
+ baseEntry.metadata.customFields = {
829
+ ...baseEntry.metadata.customFields,
830
+ ...this.context.customMetadata,
831
+ };
832
+ }
833
+ }
834
+
835
+ // Mask sensitive data if enabled
836
+ if (this.config.sensitiveFieldMasking) {
837
+ baseEntry.changes = this.maskSensitiveData(
838
+ baseEntry.changes,
839
+ ) as typeof baseEntry.changes;
840
+ baseEntry.metadata = this.maskSensitiveData(
841
+ baseEntry.metadata,
842
+ ) as typeof baseEntry.metadata;
843
+ }
844
+
845
+ return baseEntry;
846
+ }
847
+
848
+ private addToBuffer(entry: AuditLogEntry): void {
849
+ this.buffer.push(entry);
850
+
851
+ if (this.buffer.length >= (this.config.bufferSize || 100)) {
852
+ setImmediate(() => this.flush());
853
+ }
854
+ }
855
+
856
+ private logLocally(entry: AuditLogEntry, auditId: string): void {
857
+ const logLevel = this.getLogLevel(entry);
858
+ const message = `AUDIT [${auditId}] ${entry.action} on ${entry.resource}:${entry.resourceId} by ${entry.performedBy}`;
859
+
860
+ switch (logLevel) {
861
+ case "error":
862
+ console.error(message, { auditEntry: entry });
863
+ break;
864
+ case "warn":
865
+ console.warn(message, { auditEntry: entry });
866
+ break;
867
+ default:
868
+ console.log(message, { auditEntry: entry });
869
+ break;
870
+ }
871
+ }
872
+
873
+ private getLogLevel(entry: AuditLogEntry): "error" | "warn" | "info" {
874
+ if (
875
+ !entry.success ||
876
+ entry.riskLevel === "CRITICAL" ||
877
+ entry.securityImpact === "CRITICAL"
878
+ ) {
879
+ return "error";
880
+ }
881
+ if (entry.riskLevel === "HIGH" || entry.securityImpact === "HIGH") {
882
+ return "warn";
883
+ }
884
+ return "info";
885
+ }
886
+
887
+ private isCriticalEvent(entry: AuditLogEntry): boolean {
888
+ return (
889
+ entry.riskLevel === "CRITICAL" ||
890
+ entry.securityImpact === "CRITICAL" ||
891
+ entry.eventCategory === "SECURITY_ACTION" ||
892
+ (entry.success === false && entry.eventType === "LOGIN")
893
+ );
894
+ }
895
+
896
+ private async sendToAuditService(entries: AuditLogEntry[]): Promise<void> {
897
+ if (!this.config.auditServiceUrl) return;
898
+
899
+ try {
900
+ const response = await fetch(
901
+ `${this.config.auditServiceUrl}/audit/bulk`,
902
+ {
903
+ method: "POST",
904
+ headers: {
905
+ "Content-Type": "application/json",
906
+ ...(this.config.auditServiceApiKey && {
907
+ Authorization: `Bearer ${this.config.auditServiceApiKey}`,
908
+ }),
909
+ },
910
+ body: JSON.stringify({ entries }),
911
+ },
912
+ );
913
+
914
+ if (!response.ok) {
915
+ throw new Error(
916
+ `Audit service responded with status: ${response.status}`,
917
+ );
918
+ }
919
+ } catch (error) {
920
+ console.error("Failed to send audit logs to audit service:", error);
921
+ throw error;
922
+ }
923
+ }
924
+
925
+ private maskSensitiveData(data: unknown): unknown {
926
+ if (!data || typeof data !== "object") {
927
+ return data;
928
+ }
929
+
930
+ const sensitiveKeys = [
931
+ "password",
932
+ "token",
933
+ "secret",
934
+ "key",
935
+ "ssn",
936
+ "credit",
937
+ "card",
938
+ "bank",
939
+ "account",
940
+ ];
941
+
942
+ const masked = { ...(data as Record<string, unknown>) };
943
+
944
+ Object.keys(masked).forEach((key) => {
945
+ const lowerKey = key.toLowerCase();
946
+ if (sensitiveKeys.some((sensitive) => lowerKey.includes(sensitive))) {
947
+ masked[key] = "[MASKED]";
948
+ } else if (typeof masked[key] === "object" && masked[key] !== null) {
949
+ masked[key] = this.maskSensitiveData(masked[key]);
950
+ }
951
+ });
952
+
953
+ return masked;
954
+ }
955
+
956
+ private setupAutoFlush(): void {
957
+ if (this.config.flushInterval && this.config.flushInterval > 0) {
958
+ this.flushTimer = setInterval(() => {
959
+ if (this.buffer.length > 0) {
960
+ this.flush().catch((error) => {
961
+ console.error("Auto-flush failed:", error);
962
+ });
963
+ }
964
+ }, this.config.flushInterval);
965
+ }
966
+ }
967
+
968
+ // Cleanup
969
+ async shutdown(): Promise<void> {
970
+ try {
971
+ if (this.flushTimer) {
972
+ clearInterval(this.flushTimer);
973
+ this.flushTimer = null;
974
+ }
975
+
976
+ // Flush any remaining entries
977
+ if (this.buffer.length > 0) {
978
+ await this.flush();
979
+ }
980
+
981
+ console.log("✅ Audit Logger shutdown complete");
982
+ } catch (error) {
983
+ console.error("❌ Error during audit logger shutdown:", error);
984
+ }
985
+ }
986
+ }
987
+
988
+ // Factory function for creating audit logger instances
989
+ export function createAuditLogger(
990
+ config: AuditLoggerConfig,
991
+ initialContext?: Partial<AuditContext>,
992
+ ): AuditLogger {
993
+ return new AuditLogger(config, initialContext);
994
+ }
995
+
996
+ // Middleware factory for Express/Fastify request context
997
+ export function createAuditMiddleware(logger: AuditLogger) {
998
+ return (req: any, _res: unknown, next: () => void) => {
999
+ const requestId = req.headers?.["x-request-id"] || createId();
1000
+ const correlationId = req.headers?.["x-correlation-id"];
1001
+ const sessionId = req.headers?.["x-session-id"] || req.session?.id;
1002
+
1003
+ // Update audit context for this request
1004
+ logger.updateContext({
1005
+ requestId,
1006
+ correlationId,
1007
+ sessionId,
1008
+ ipAddress: req.ip || req.connection?.remoteAddress,
1009
+ userAgent: req.headers?.["user-agent"],
1010
+ userId: req.user?.id,
1011
+ userType: req.user?.type,
1012
+ clientId: req.headers?.["x-client-id"],
1013
+ clientType: req.headers?.["x-client-type"] as
1014
+ | "WEB"
1015
+ | "MOBILE_APP"
1016
+ | "API"
1017
+ | "ADMIN_PANEL"
1018
+ | "SYSTEM"
1019
+ | "WEBHOOK"
1020
+ | "CRON_JOB"
1021
+ | undefined,
1022
+ });
1023
+
1024
+ // Attach audit logger to request for easy access
1025
+ (req as any).audit = logger;
1026
+
1027
+ next();
1028
+ };
1029
+ }
1030
+
1031
+ // Export convenience functions
1032
+ export async function logAuditEvent(
1033
+ serviceName: string,
1034
+ entry: Partial<AuditLogEntry>,
1035
+ context?: Partial<AuditContext>,
1036
+ ): Promise<string> {
1037
+ const logger = createAuditLogger({ serviceName }, context);
1038
+ return logger.log(entry);
1039
+ }
1040
+
1041
+ export async function logStoreAuditEvent(
1042
+ serviceName: string,
1043
+ storeId: string,
1044
+ action: string,
1045
+ resource: string,
1046
+ resourceId: string,
1047
+ userId?: string,
1048
+ additionalData?: Partial<AuditLogEntry>,
1049
+ ): Promise<string> {
1050
+ const logger = createAuditLogger({ serviceName }, { storeId });
1051
+ return logger.logStoreAction(
1052
+ storeId,
1053
+ action,
1054
+ resource,
1055
+ resourceId,
1056
+ userId,
1057
+ additionalData,
1058
+ );
1059
+ }
1060
+
1061
+ // Default audit logger instance (can be configured once and reused)
1062
+ let defaultLogger: AuditLogger | null = null;
1063
+
1064
+ export function setDefaultAuditLogger(logger: AuditLogger): void {
1065
+ defaultLogger = logger;
1066
+ }
1067
+
1068
+ export function getDefaultAuditLogger(): AuditLogger {
1069
+ if (!defaultLogger) {
1070
+ throw new Error(
1071
+ "Default audit logger not configured. Call setDefaultAuditLogger() first.",
1072
+ );
1073
+ }
1074
+ return defaultLogger;
1075
+ }
1076
+
1077
+ // Quick logging functions using default logger
1078
+ export async function quickLog(entry: Partial<AuditLogEntry>): Promise<string> {
1079
+ return getDefaultAuditLogger().log(entry);
1080
+ }
1081
+
1082
+ export async function quickLogUserAction(
1083
+ action: string,
1084
+ resource: string,
1085
+ resourceId: string,
1086
+ userId: string,
1087
+ additionalData?: Partial<AuditLogEntry>,
1088
+ ): Promise<string> {
1089
+ return getDefaultAuditLogger().log({
1090
+ eventType: "UPDATE",
1091
+ eventCategory: "USER_ACTION",
1092
+ action,
1093
+ resource,
1094
+ resourceId,
1095
+ performedBy: userId,
1096
+ performedByType: "USER",
1097
+ ...additionalData,
1098
+ });
1099
+ }
1100
+
1101
+ export async function quickLogSystemAction(
1102
+ action: string,
1103
+ resource: string,
1104
+ resourceId: string,
1105
+ additionalData?: Partial<AuditLogEntry>,
1106
+ ): Promise<string> {
1107
+ return getDefaultAuditLogger().log({
1108
+ eventType: "SYSTEM_ACTION",
1109
+ eventCategory: "SYSTEM_ACTION",
1110
+ action,
1111
+ resource,
1112
+ resourceId,
1113
+ performedBy: "SYSTEM",
1114
+ performedByType: "SYSTEM",
1115
+ ...additionalData,
1116
+ });
1117
+ }