@striae-org/striae 3.2.1 → 3.2.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.
Files changed (81) hide show
  1. package/app/components/actions/case-export/core-export.ts +2 -2
  2. package/app/components/actions/case-export/data-processing.ts +19 -4
  3. package/app/components/actions/case-export/download-handlers.ts +6 -5
  4. package/app/components/actions/case-export/metadata-helpers.ts +1 -1
  5. package/app/components/actions/case-import/annotation-import.ts +2 -2
  6. package/app/components/actions/case-import/confirmation-import.ts +3 -3
  7. package/app/components/actions/case-import/image-operations.ts +1 -1
  8. package/app/components/actions/case-import/orchestrator.ts +4 -4
  9. package/app/components/actions/case-import/storage-operations.ts +7 -7
  10. package/app/components/actions/case-import/validation.ts +3 -3
  11. package/app/components/actions/case-import/zip-processing.ts +3 -3
  12. package/app/components/actions/case-manage.ts +3 -3
  13. package/app/components/actions/confirm-export.ts +3 -3
  14. package/app/components/actions/generate-pdf.ts +3 -3
  15. package/app/components/actions/image-manage.ts +3 -3
  16. package/app/components/actions/notes-manage.ts +3 -3
  17. package/app/components/actions/signout.tsx +1 -1
  18. package/app/components/audit/user-audit-viewer.tsx +2 -3
  19. package/app/components/auth/auth-provider.tsx +2 -2
  20. package/app/components/auth/mfa-enrollment.tsx +3 -3
  21. package/app/components/auth/mfa-verification.tsx +4 -4
  22. package/app/components/canvas/box-annotations/box-annotations.tsx +2 -2
  23. package/app/components/canvas/canvas.tsx +1 -1
  24. package/app/components/canvas/confirmation/confirmation.tsx +1 -1
  25. package/app/components/sidebar/case-import/case-import.tsx +2 -2
  26. package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +1 -1
  27. package/app/components/sidebar/case-import/components/ConfirmationDialog.tsx +1 -1
  28. package/app/components/sidebar/case-import/hooks/useFilePreview.ts +3 -3
  29. package/app/components/sidebar/case-import/hooks/useImportExecution.ts +2 -2
  30. package/app/components/sidebar/cases/case-sidebar.tsx +5 -4
  31. package/app/components/sidebar/cases/cases-modal.tsx +1 -1
  32. package/app/components/sidebar/files/files-modal.tsx +3 -2
  33. package/app/components/sidebar/notes/notes-sidebar.tsx +3 -3
  34. package/app/components/sidebar/sidebar-container.tsx +4 -3
  35. package/app/components/sidebar/sidebar.tsx +2 -2
  36. package/app/components/sidebar/upload/image-upload-zone.tsx +2 -2
  37. package/app/components/theme-provider/theme-provider.tsx +1 -1
  38. package/app/components/user/delete-account.tsx +1 -1
  39. package/app/components/user/manage-profile.tsx +2 -2
  40. package/app/components/user/mfa-phone-update.tsx +2 -2
  41. package/app/contexts/auth.context.ts +1 -1
  42. package/app/routes/auth/emailActionHandler.tsx +2 -2
  43. package/app/routes/auth/emailVerification.tsx +2 -2
  44. package/app/routes/auth/login.tsx +5 -5
  45. package/app/routes/auth/passwordReset.tsx +2 -2
  46. package/app/routes/striae/striae.tsx +2 -2
  47. package/app/services/audit/audit-console-logger.ts +46 -0
  48. package/app/services/audit/audit-export-csv.ts +126 -0
  49. package/app/services/audit/audit-export-report.ts +174 -0
  50. package/app/services/audit/audit-export-signing.ts +85 -0
  51. package/app/services/audit/audit-export.service.ts +334 -0
  52. package/app/services/audit/audit-file-type.ts +13 -0
  53. package/app/services/audit/audit-query-helpers.ts +88 -0
  54. package/app/services/audit/audit-worker-client.ts +95 -0
  55. package/app/services/audit/audit.service.ts +990 -0
  56. package/app/services/audit/builders/audit-entry-builder.ts +32 -0
  57. package/app/services/audit/builders/audit-event-builders-annotation.ts +150 -0
  58. package/app/services/audit/builders/audit-event-builders-case-file.ts +249 -0
  59. package/app/services/audit/builders/audit-event-builders-user-security.ts +449 -0
  60. package/app/services/audit/builders/audit-event-builders-workflow.ts +272 -0
  61. package/app/services/audit/builders/index.ts +40 -0
  62. package/app/services/audit/index.ts +2 -0
  63. package/app/types/case.ts +2 -2
  64. package/app/types/exceljs-bare.d.ts +3 -1
  65. package/app/types/user.ts +1 -1
  66. package/app/utils/audit-export-signature.ts +2 -2
  67. package/app/utils/confirmation-signature.ts +3 -3
  68. package/app/utils/data-operations.ts +5 -5
  69. package/app/utils/mfa-phone.ts +1 -1
  70. package/app/utils/mfa.ts +1 -1
  71. package/app/utils/permissions.ts +2 -2
  72. package/package.json +7 -8
  73. package/worker-configuration.d.ts +4435 -562
  74. package/workers/data-worker/src/data-worker.example.ts +3 -3
  75. package/workers/pdf-worker/src/{generated-assets.ts → assets/generated-assets.ts} +117 -117
  76. package/workers/pdf-worker/src/{format-striae.ts → formats/format-striae.ts} +535 -535
  77. package/workers/pdf-worker/src/pdf-worker.example.ts +1 -1
  78. package/app/services/audit-export.service.ts +0 -755
  79. package/app/services/audit.service.ts +0 -1474
  80. /package/app/services/{firebase-errors.ts → firebase/errors.ts} +0 -0
  81. /package/app/services/{firebase.ts → firebase/index.ts} +0 -0
@@ -0,0 +1,990 @@
1
+ import type { User } from 'firebase/auth';
2
+ import type {
3
+ ValidationAuditEntry,
4
+ CreateAuditEntryParams,
5
+ AuditTrail,
6
+ AuditQueryParams,
7
+ WorkflowPhase,
8
+ AuditAction,
9
+ AuditResult,
10
+ PerformanceMetrics
11
+ } from '~/types';
12
+ import { generateWorkflowId } from '../../utils/id-generator';
13
+ import {
14
+ fetchAuditEntriesForUser,
15
+ persistAuditEntryForUser
16
+ } from './audit-worker-client';
17
+ import {
18
+ applyAuditEntryFilters,
19
+ applyAuditPagination,
20
+ generateAuditSummary,
21
+ sortAuditEntriesNewestFirst
22
+ } from './audit-query-helpers';
23
+ import { logAuditEntryToConsole } from './audit-console-logger';
24
+ import {
25
+ buildAccountDeletionAuditParams,
26
+ buildAnnotationCreateAuditParams,
27
+ buildAnnotationDeleteAuditParams,
28
+ buildAnnotationEditAuditParams,
29
+ buildCaseCreationAuditParams,
30
+ buildCaseDeletionAuditParams,
31
+ buildCaseExportAuditParams,
32
+ buildCaseImportAuditParams,
33
+ buildCaseRenameAuditParams,
34
+ buildConfirmationCreationAuditParams,
35
+ buildConfirmationExportAuditParams,
36
+ buildConfirmationImportAuditParams,
37
+ buildEmailVerificationAuditParams,
38
+ buildEmailVerificationByEmailAuditParams,
39
+ buildFileAccessAuditParams,
40
+ buildFileDeletionAuditParams,
41
+ buildFileUploadAuditParams,
42
+ buildMarkEmailVerificationSuccessfulAuditParams,
43
+ buildMfaAuthenticationAuditParams,
44
+ buildMfaEnrollmentAuditParams,
45
+ buildPDFGenerationAuditParams,
46
+ buildPasswordResetAuditParams,
47
+ buildSecurityViolationAuditParams,
48
+ buildUserLoginAuditParams,
49
+ buildUserLogoutAuditParams,
50
+ buildUserProfileUpdateAuditParams,
51
+ buildUserRegistrationAuditParams,
52
+ buildValidationAuditEntry
53
+ } from './builders/index';
54
+
55
+ /**
56
+ * Audit Service for ValidationAuditEntry system
57
+ * Provides comprehensive audit logging throughout the confirmation workflow
58
+ */
59
+ export class AuditService {
60
+ private static instance: AuditService;
61
+ private auditBuffer: ValidationAuditEntry[] = [];
62
+ private workflowId: string | null = null;
63
+
64
+ private constructor() {}
65
+
66
+ public static getInstance(): AuditService {
67
+ if (!AuditService.instance) {
68
+ AuditService.instance = new AuditService();
69
+ }
70
+ return AuditService.instance;
71
+ }
72
+
73
+ /**
74
+ * Initialize a new workflow session with unique ID
75
+ */
76
+ public startWorkflow(caseNumber: string): string {
77
+ const workflowId = generateWorkflowId(caseNumber);
78
+ this.workflowId = workflowId;
79
+ console.log(`🔍 Audit: Started workflow ${this.workflowId}`);
80
+ return workflowId;
81
+ }
82
+
83
+ /**
84
+ * End current workflow session
85
+ */
86
+ public endWorkflow(): void {
87
+ if (this.workflowId) {
88
+ console.log(`🔍 Audit: Ended workflow ${this.workflowId}`);
89
+ this.workflowId = null;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Create and log an audit entry
95
+ */
96
+ public async logEvent(params: CreateAuditEntryParams): Promise<void> {
97
+ const startTime = Date.now();
98
+
99
+ try {
100
+ const auditEntry = buildValidationAuditEntry(params);
101
+
102
+ // Add to buffer for batch processing
103
+ this.auditBuffer.push(auditEntry);
104
+
105
+ // Log to console for immediate feedback
106
+ logAuditEntryToConsole(auditEntry);
107
+
108
+ // Persist to storage asynchronously
109
+ await this.persistAuditEntry(auditEntry);
110
+
111
+ const endTime = Date.now();
112
+ console.log(`🔍 Audit: Event logged in ${endTime - startTime}ms`);
113
+
114
+ } catch (error) {
115
+ console.error('🚨 Audit: Failed to log event:', error);
116
+ // Don't throw - audit failures shouldn't break the main workflow
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Log case export event
122
+ */
123
+ public async logCaseExport(
124
+ user: User,
125
+ caseNumber: string,
126
+ fileName: string,
127
+ result: AuditResult,
128
+ errors: string[] = [],
129
+ performanceMetrics?: PerformanceMetrics,
130
+ exportFormat?: 'json' | 'csv' | 'xlsx' | 'zip',
131
+ protectionEnabled?: boolean,
132
+ signatureDetails?: {
133
+ present?: boolean;
134
+ valid?: boolean;
135
+ keyId?: string;
136
+ }
137
+ ): Promise<void> {
138
+ await this.logEvent(
139
+ buildCaseExportAuditParams({
140
+ user,
141
+ caseNumber,
142
+ fileName,
143
+ result,
144
+ errors,
145
+ performanceMetrics,
146
+ exportFormat,
147
+ signatureDetails
148
+ })
149
+ );
150
+ }
151
+
152
+ /**
153
+ * Log case import event
154
+ */
155
+ public async logCaseImport(
156
+ user: User,
157
+ caseNumber: string,
158
+ fileName: string,
159
+ result: AuditResult,
160
+ hashValid: boolean,
161
+ errors: string[] = [],
162
+ originalExaminerUid?: string,
163
+ performanceMetrics?: PerformanceMetrics,
164
+ exporterUidValidated?: boolean, // Separate flag for validation status
165
+ signatureDetails?: {
166
+ present?: boolean;
167
+ valid?: boolean;
168
+ keyId?: string;
169
+ }
170
+ ): Promise<void> {
171
+ await this.logEvent(
172
+ buildCaseImportAuditParams({
173
+ user,
174
+ caseNumber,
175
+ fileName,
176
+ result,
177
+ hashValid,
178
+ errors,
179
+ originalExaminerUid,
180
+ performanceMetrics,
181
+ exporterUidValidated,
182
+ signatureDetails
183
+ })
184
+ );
185
+ }
186
+
187
+ /**
188
+ * Log confirmation creation event
189
+ */
190
+ public async logConfirmationCreation(
191
+ user: User,
192
+ caseNumber: string,
193
+ confirmationId: string,
194
+ result: AuditResult,
195
+ errors: string[] = [],
196
+ originalExaminerUid?: string,
197
+ performanceMetrics?: PerformanceMetrics,
198
+ imageFileId?: string,
199
+ originalImageFileName?: string
200
+ ): Promise<void> {
201
+ await this.logEvent(
202
+ buildConfirmationCreationAuditParams({
203
+ user,
204
+ caseNumber,
205
+ confirmationId,
206
+ result,
207
+ errors,
208
+ originalExaminerUid,
209
+ performanceMetrics,
210
+ imageFileId,
211
+ originalImageFileName
212
+ })
213
+ );
214
+ }
215
+
216
+ /**
217
+ * Log confirmation export event
218
+ */
219
+ public async logConfirmationExport(
220
+ user: User,
221
+ caseNumber: string,
222
+ fileName: string,
223
+ confirmationCount: number,
224
+ result: AuditResult,
225
+ errors: string[] = [],
226
+ originalExaminerUid?: string,
227
+ performanceMetrics?: PerformanceMetrics,
228
+ signatureDetails?: {
229
+ present: boolean;
230
+ valid: boolean;
231
+ keyId?: string;
232
+ }
233
+ ): Promise<void> {
234
+ await this.logEvent(
235
+ buildConfirmationExportAuditParams({
236
+ user,
237
+ caseNumber,
238
+ fileName,
239
+ result,
240
+ errors,
241
+ originalExaminerUid,
242
+ performanceMetrics,
243
+ signatureDetails
244
+ })
245
+ );
246
+ }
247
+
248
+ /**
249
+ * Log confirmation import event
250
+ */
251
+ public async logConfirmationImport(
252
+ user: User,
253
+ caseNumber: string,
254
+ fileName: string,
255
+ result: AuditResult,
256
+ hashValid: boolean,
257
+ confirmationsImported: number,
258
+ errors: string[] = [],
259
+ reviewingExaminerUid?: string,
260
+ performanceMetrics?: PerformanceMetrics,
261
+ exporterUidValidated?: boolean, // Separate flag for validation status
262
+ totalConfirmationsInFile?: number, // Total confirmations in the import file
263
+ signatureDetails?: {
264
+ present: boolean;
265
+ valid: boolean;
266
+ keyId?: string;
267
+ }
268
+ ): Promise<void> {
269
+ await this.logEvent(
270
+ buildConfirmationImportAuditParams({
271
+ user,
272
+ caseNumber,
273
+ fileName,
274
+ result,
275
+ hashValid,
276
+ confirmationsImported,
277
+ errors,
278
+ reviewingExaminerUid,
279
+ performanceMetrics,
280
+ exporterUidValidated,
281
+ totalConfirmationsInFile,
282
+ signatureDetails
283
+ })
284
+ );
285
+ }
286
+
287
+ // =============================================================================
288
+ // COMPREHENSIVE AUDIT LOGGING METHODS
289
+ // =============================================================================
290
+
291
+ /**
292
+ * Log case creation event
293
+ */
294
+ public async logCaseCreation(
295
+ user: User,
296
+ caseNumber: string,
297
+ caseName: string
298
+ ): Promise<void> {
299
+ await this.logEvent(
300
+ buildCaseCreationAuditParams({
301
+ user,
302
+ caseNumber,
303
+ caseName
304
+ })
305
+ );
306
+ }
307
+
308
+ /**
309
+ * Log case rename event
310
+ */
311
+ public async logCaseRename(
312
+ user: User,
313
+ caseNumber: string,
314
+ oldName: string,
315
+ newName: string
316
+ ): Promise<void> {
317
+ await this.logEvent(
318
+ buildCaseRenameAuditParams({
319
+ user,
320
+ caseNumber,
321
+ oldName,
322
+ newName
323
+ })
324
+ );
325
+ }
326
+
327
+ /**
328
+ * Log case deletion event
329
+ */
330
+ public async logCaseDeletion(
331
+ user: User,
332
+ caseNumber: string,
333
+ caseName: string,
334
+ deleteReason: string,
335
+ backupCreated: boolean = false
336
+ ): Promise<void> {
337
+ await this.logEvent(
338
+ buildCaseDeletionAuditParams({
339
+ user,
340
+ caseNumber,
341
+ caseName,
342
+ deleteReason,
343
+ backupCreated
344
+ })
345
+ );
346
+ }
347
+
348
+ /**
349
+ * Log file upload event
350
+ */
351
+ public async logFileUpload(
352
+ user: User,
353
+ fileName: string,
354
+ fileSize: number,
355
+ mimeType: string,
356
+ uploadMethod: 'drag-drop' | 'file-picker' | 'api' | 'import',
357
+ caseNumber: string,
358
+ result: AuditResult = 'success',
359
+ processingTime?: number,
360
+ fileId?: string
361
+ ): Promise<void> {
362
+ await this.logEvent(
363
+ buildFileUploadAuditParams({
364
+ user,
365
+ fileName,
366
+ fileSize,
367
+ mimeType,
368
+ uploadMethod,
369
+ caseNumber,
370
+ result,
371
+ processingTime,
372
+ fileId
373
+ })
374
+ );
375
+ }
376
+
377
+ /**
378
+ * Log file deletion event
379
+ */
380
+ public async logFileDeletion(
381
+ user: User,
382
+ fileName: string,
383
+ fileSize: number,
384
+ deleteReason: string,
385
+ caseNumber: string,
386
+ fileId?: string,
387
+ originalFileName?: string
388
+ ): Promise<void> {
389
+ await this.logEvent(
390
+ buildFileDeletionAuditParams({
391
+ user,
392
+ fileName,
393
+ fileSize,
394
+ deleteReason,
395
+ caseNumber,
396
+ fileId,
397
+ originalFileName
398
+ })
399
+ );
400
+ }
401
+
402
+ /**
403
+ * Log file access event (e.g., viewing an image)
404
+ */
405
+ public async logFileAccess(
406
+ user: User,
407
+ fileName: string,
408
+ fileId: string,
409
+ accessMethod: 'direct-url' | 'signed-url' | 'download',
410
+ caseNumber: string,
411
+ result: AuditResult = 'success',
412
+ processingTime?: number,
413
+ accessReason?: string,
414
+ originalFileName?: string
415
+ ): Promise<void> {
416
+ await this.logEvent(
417
+ buildFileAccessAuditParams({
418
+ user,
419
+ fileName,
420
+ fileId,
421
+ accessMethod,
422
+ caseNumber,
423
+ result,
424
+ processingTime,
425
+ accessReason,
426
+ originalFileName
427
+ })
428
+ );
429
+ }
430
+
431
+ /**
432
+ * Log annotation creation event
433
+ */
434
+ public async logAnnotationCreate(
435
+ user: User,
436
+ annotationId: string,
437
+ annotationType: 'measurement' | 'identification' | 'comparison' | 'note' | 'region',
438
+ annotationData: unknown,
439
+ caseNumber: string,
440
+ tool?: string,
441
+ imageFileId?: string,
442
+ originalImageFileName?: string
443
+ ): Promise<void> {
444
+ await this.logEvent(
445
+ buildAnnotationCreateAuditParams({
446
+ user,
447
+ annotationId,
448
+ annotationType,
449
+ annotationData,
450
+ caseNumber,
451
+ tool,
452
+ imageFileId,
453
+ originalImageFileName
454
+ })
455
+ );
456
+ }
457
+
458
+ /**
459
+ * Log annotation edit event
460
+ */
461
+ public async logAnnotationEdit(
462
+ user: User,
463
+ annotationId: string,
464
+ previousValue: unknown,
465
+ newValue: unknown,
466
+ caseNumber: string,
467
+ tool?: string,
468
+ imageFileId?: string,
469
+ originalImageFileName?: string
470
+ ): Promise<void> {
471
+ await this.logEvent(
472
+ buildAnnotationEditAuditParams({
473
+ user,
474
+ annotationId,
475
+ previousValue,
476
+ newValue,
477
+ caseNumber,
478
+ tool,
479
+ imageFileId,
480
+ originalImageFileName
481
+ })
482
+ );
483
+ }
484
+
485
+ /**
486
+ * Log annotation deletion event
487
+ */
488
+ public async logAnnotationDelete(
489
+ user: User,
490
+ annotationId: string,
491
+ annotationData: unknown,
492
+ caseNumber: string,
493
+ deleteReason?: string,
494
+ imageFileId?: string,
495
+ originalImageFileName?: string
496
+ ): Promise<void> {
497
+ await this.logEvent(
498
+ buildAnnotationDeleteAuditParams({
499
+ user,
500
+ annotationId,
501
+ annotationData,
502
+ caseNumber,
503
+ deleteReason,
504
+ imageFileId,
505
+ originalImageFileName
506
+ })
507
+ );
508
+ }
509
+
510
+ /**
511
+ * Log user login event
512
+ */
513
+ public async logUserLogin(
514
+ user: User,
515
+ sessionId: string,
516
+ loginMethod: 'firebase' | 'sso' | 'api-key' | 'manual',
517
+ userAgent?: string
518
+ ): Promise<void> {
519
+ await this.logEvent(
520
+ buildUserLoginAuditParams({
521
+ user,
522
+ sessionId,
523
+ loginMethod,
524
+ userAgent
525
+ })
526
+ );
527
+ }
528
+
529
+ /**
530
+ * Log user logout event
531
+ */
532
+ public async logUserLogout(
533
+ user: User,
534
+ sessionId: string,
535
+ sessionDuration: number,
536
+ logoutReason: 'user-initiated' | 'timeout' | 'security' | 'error'
537
+ ): Promise<void> {
538
+ await this.logEvent(
539
+ buildUserLogoutAuditParams({
540
+ user,
541
+ sessionId,
542
+ sessionDuration,
543
+ logoutReason
544
+ })
545
+ );
546
+ }
547
+
548
+ /**
549
+ * Log user profile update event
550
+ */
551
+ public async logUserProfileUpdate(
552
+ user: User,
553
+ profileField: 'displayName' | 'email' | 'organization' | 'role' | 'preferences' | 'avatar',
554
+ oldValue: string,
555
+ newValue: string,
556
+ result: AuditResult,
557
+ sessionId?: string,
558
+ errors: string[] = []
559
+ ): Promise<void> {
560
+ await this.logEvent(
561
+ buildUserProfileUpdateAuditParams({
562
+ user,
563
+ profileField,
564
+ oldValue,
565
+ newValue,
566
+ result,
567
+ sessionId,
568
+ errors
569
+ })
570
+ );
571
+ }
572
+
573
+ /**
574
+ * Log password reset event
575
+ */
576
+ public async logPasswordReset(
577
+ userEmail: string,
578
+ resetMethod: 'email' | 'sms' | 'security-questions' | 'admin-reset',
579
+ result: AuditResult,
580
+ resetToken?: string,
581
+ verificationMethod?: 'email-link' | 'sms-code' | 'totp' | 'backup-codes',
582
+ verificationAttempts?: number,
583
+ passwordComplexityMet?: boolean,
584
+ previousPasswordReused?: boolean,
585
+ sessionId?: string,
586
+ errors: string[] = []
587
+ ): Promise<void> {
588
+ await this.logEvent(
589
+ buildPasswordResetAuditParams({
590
+ userEmail,
591
+ resetMethod,
592
+ result,
593
+ resetToken,
594
+ verificationMethod,
595
+ verificationAttempts,
596
+ passwordComplexityMet,
597
+ previousPasswordReused,
598
+ sessionId,
599
+ errors
600
+ })
601
+ );
602
+ }
603
+
604
+ /**
605
+ * Log user account deletion event
606
+ */
607
+ public async logAccountDeletion(
608
+ user: User,
609
+ result: AuditResult,
610
+ deletionReason: 'user-requested' | 'admin-initiated' | 'policy-violation' | 'inactive-account' = 'user-requested',
611
+ confirmationMethod: 'uid-email' | 'password' | 'admin-override' = 'uid-email',
612
+ casesCount?: number,
613
+ filesCount?: number,
614
+ dataRetentionPeriod?: number,
615
+ emailNotificationSent?: boolean,
616
+ sessionId?: string,
617
+ errors: string[] = []
618
+ ): Promise<void> {
619
+ // Wrapper that extracts user data and calls the simplified version
620
+ return this.logAccountDeletionSimple(
621
+ user.uid,
622
+ user.email || '',
623
+ result,
624
+ deletionReason,
625
+ confirmationMethod,
626
+ casesCount,
627
+ filesCount,
628
+ dataRetentionPeriod,
629
+ emailNotificationSent,
630
+ sessionId,
631
+ errors
632
+ );
633
+ }
634
+
635
+ /**
636
+ * Log user account deletion event with simplified user data
637
+ */
638
+ public async logAccountDeletionSimple(
639
+ userId: string,
640
+ userEmail: string,
641
+ result: AuditResult,
642
+ deletionReason: 'user-requested' | 'admin-initiated' | 'policy-violation' | 'inactive-account' = 'user-requested',
643
+ confirmationMethod: 'uid-email' | 'password' | 'admin-override' = 'uid-email',
644
+ casesCount?: number,
645
+ filesCount?: number,
646
+ dataRetentionPeriod?: number,
647
+ emailNotificationSent?: boolean,
648
+ sessionId?: string,
649
+ errors: string[] = []
650
+ ): Promise<void> {
651
+ await this.logEvent(
652
+ buildAccountDeletionAuditParams({
653
+ userId,
654
+ userEmail,
655
+ result,
656
+ deletionReason,
657
+ confirmationMethod,
658
+ casesCount,
659
+ filesCount,
660
+ dataRetentionPeriod,
661
+ emailNotificationSent,
662
+ sessionId,
663
+ errors
664
+ })
665
+ );
666
+ }
667
+
668
+ /**
669
+ * Log user registration/creation event
670
+ */
671
+ public async logUserRegistration(
672
+ user: User,
673
+ firstName: string,
674
+ lastName: string,
675
+ company: string,
676
+ registrationMethod: 'email-password' | 'sso' | 'admin-created' | 'api',
677
+ userAgent?: string,
678
+ sessionId?: string
679
+ ): Promise<void> {
680
+ await this.logEvent(
681
+ buildUserRegistrationAuditParams({
682
+ user,
683
+ firstName,
684
+ lastName,
685
+ company,
686
+ registrationMethod,
687
+ userAgent,
688
+ sessionId
689
+ })
690
+ );
691
+ }
692
+
693
+ /**
694
+ * Log successful MFA enrollment event
695
+ */
696
+ public async logMfaEnrollment(
697
+ user: User,
698
+ phoneNumber: string,
699
+ mfaMethod: 'sms' | 'totp' | 'hardware-key',
700
+ result: AuditResult,
701
+ enrollmentAttempts?: number,
702
+ sessionId?: string,
703
+ userAgent?: string,
704
+ errors: string[] = []
705
+ ): Promise<void> {
706
+ await this.logEvent(
707
+ buildMfaEnrollmentAuditParams({
708
+ user,
709
+ phoneNumber,
710
+ mfaMethod,
711
+ result,
712
+ enrollmentAttempts,
713
+ sessionId,
714
+ userAgent,
715
+ errors
716
+ })
717
+ );
718
+ }
719
+
720
+ /**
721
+ * Log MFA authentication/verification event
722
+ */
723
+ public async logMfaAuthentication(
724
+ user: User,
725
+ mfaMethod: 'sms' | 'totp' | 'hardware-key',
726
+ result: AuditResult,
727
+ verificationAttempts?: number,
728
+ sessionId?: string,
729
+ userAgent?: string,
730
+ errors: string[] = []
731
+ ): Promise<void> {
732
+ await this.logEvent(
733
+ buildMfaAuthenticationAuditParams({
734
+ user,
735
+ mfaMethod,
736
+ result,
737
+ verificationAttempts,
738
+ sessionId,
739
+ userAgent,
740
+ errors
741
+ })
742
+ );
743
+ }
744
+
745
+ /**
746
+ * Log email verification event
747
+ */
748
+ public async logEmailVerification(
749
+ user: User,
750
+ result: AuditResult,
751
+ verificationMethod: 'email-link' | 'admin-verification',
752
+ verificationAttempts?: number,
753
+ sessionId?: string,
754
+ userAgent?: string,
755
+ errors: string[] = []
756
+ ): Promise<void> {
757
+ await this.logEvent(
758
+ buildEmailVerificationAuditParams({
759
+ user,
760
+ result,
761
+ verificationMethod,
762
+ verificationAttempts,
763
+ sessionId,
764
+ userAgent,
765
+ errors
766
+ })
767
+ );
768
+ }
769
+
770
+ /**
771
+ * Log email verification event when no authenticated User object is available.
772
+ */
773
+ public async logEmailVerificationByEmail(
774
+ userEmail: string,
775
+ result: AuditResult,
776
+ verificationMethod: 'email-link' | 'admin-verification',
777
+ verificationAttempts?: number,
778
+ sessionId?: string,
779
+ userAgent?: string,
780
+ errors: string[] = [],
781
+ userId: string = ''
782
+ ): Promise<void> {
783
+ await this.logEvent(
784
+ buildEmailVerificationByEmailAuditParams({
785
+ userEmail,
786
+ result,
787
+ verificationMethod,
788
+ verificationAttempts,
789
+ sessionId,
790
+ userAgent,
791
+ errors,
792
+ userId
793
+ })
794
+ );
795
+ }
796
+
797
+ /**
798
+ * Mark pending email verification as successful (retroactive)
799
+ * Called when user completes MFA enrollment, which implies email verification was successful
800
+ */
801
+ public async markEmailVerificationSuccessful(
802
+ user: User,
803
+ reason: string = 'MFA enrollment completed',
804
+ sessionId?: string,
805
+ userAgent?: string
806
+ ): Promise<void> {
807
+ await this.logEvent(
808
+ buildMarkEmailVerificationSuccessfulAuditParams({
809
+ user,
810
+ reason,
811
+ sessionId,
812
+ userAgent
813
+ })
814
+ );
815
+ }
816
+
817
+ /**
818
+ * Log PDF generation event
819
+ */
820
+ public async logPDFGeneration(
821
+ user: User,
822
+ fileName: string,
823
+ caseNumber: string,
824
+ result: AuditResult,
825
+ processingTime: number,
826
+ fileSize?: number,
827
+ errors: string[] = [],
828
+ sourceFileId?: string,
829
+ sourceFileName?: string
830
+ ): Promise<void> {
831
+ await this.logEvent(
832
+ buildPDFGenerationAuditParams({
833
+ user,
834
+ fileName,
835
+ caseNumber,
836
+ result,
837
+ processingTime,
838
+ fileSize,
839
+ errors,
840
+ sourceFileId,
841
+ sourceFileName
842
+ })
843
+ );
844
+ }
845
+
846
+ /**
847
+ * Log security violation event
848
+ */
849
+ public async logSecurityViolation(
850
+ user: User | null,
851
+ incidentType: 'unauthorized-access' | 'data-breach' | 'malware' | 'injection' | 'brute-force' | 'privilege-escalation',
852
+ severity: 'low' | 'medium' | 'high' | 'critical',
853
+ description: string,
854
+ targetResource?: string,
855
+ blockedBySystem: boolean = true
856
+ ): Promise<void> {
857
+ await this.logEvent(
858
+ buildSecurityViolationAuditParams({
859
+ user,
860
+ incidentType,
861
+ severity,
862
+ description,
863
+ targetResource,
864
+ blockedBySystem
865
+ })
866
+ );
867
+ }
868
+
869
+ // =============================================================================
870
+ // HELPER METHODS
871
+ // =============================================================================
872
+
873
+ /**
874
+ * Get audit entries for display (public method for components)
875
+ */
876
+ public async getAuditEntriesForUser(userId: string, params?: {
877
+ startDate?: string;
878
+ endDate?: string;
879
+ caseNumber?: string;
880
+ action?: AuditAction;
881
+ result?: AuditResult;
882
+ workflowPhase?: WorkflowPhase;
883
+ offset?: number;
884
+ limit?: number;
885
+ }): Promise<ValidationAuditEntry[]> {
886
+ const queryParams: AuditQueryParams = {
887
+ userId,
888
+ ...params
889
+ };
890
+ return await this.getAuditEntries(queryParams);
891
+ }
892
+
893
+ /**
894
+ * Get audit trail for a case
895
+ */
896
+ public async getAuditTrail(caseNumber: string): Promise<AuditTrail | null> {
897
+ try {
898
+ // Implement retrieval from storage
899
+ const entries = await this.getAuditEntries({ caseNumber });
900
+ if (!entries || entries.length === 0) {
901
+ return null;
902
+ }
903
+
904
+ const summary = generateAuditSummary(entries);
905
+ const workflowId = this.workflowId || `${caseNumber}-archived`;
906
+
907
+ return {
908
+ caseNumber,
909
+ workflowId,
910
+ entries,
911
+ summary
912
+ };
913
+ } catch (error) {
914
+ console.error('🚨 Audit: Failed to get audit trail:', error);
915
+ return null;
916
+ }
917
+ }
918
+
919
+ /**
920
+ * Get audit entries based on query parameters
921
+ */
922
+ private async getAuditEntries(params: AuditQueryParams): Promise<ValidationAuditEntry[]> {
923
+ try {
924
+ // If userId is provided, fetch from server
925
+ if (params.userId) {
926
+ const serverEntries = await fetchAuditEntriesForUser({
927
+ userId: params.userId,
928
+ startDate: params.startDate,
929
+ endDate: params.endDate
930
+ });
931
+
932
+ if (serverEntries) {
933
+ const filteredEntries = applyAuditEntryFilters(serverEntries, {
934
+ ...params,
935
+ userId: undefined
936
+ });
937
+ return applyAuditPagination(filteredEntries, params);
938
+ }
939
+
940
+ console.error('🚨 Audit: Failed to fetch entries from server');
941
+ }
942
+
943
+ // Fallback to buffer for backward compatibility
944
+ const filteredEntries = applyAuditEntryFilters([...this.auditBuffer], params);
945
+ const sortedEntries = sortAuditEntriesNewestFirst(filteredEntries);
946
+ return applyAuditPagination(sortedEntries, params);
947
+ } catch (error) {
948
+ console.error('🚨 Audit: Failed to get audit entries:', error);
949
+ return [];
950
+ }
951
+ }
952
+
953
+ /**
954
+ * Persist audit entry to storage
955
+ */
956
+ private async persistAuditEntry(entry: ValidationAuditEntry): Promise<void> {
957
+ try {
958
+ const persistResult = await persistAuditEntryForUser(entry);
959
+
960
+ if (!persistResult.ok) {
961
+ console.error(
962
+ '🚨 Audit: Failed to persist entry:',
963
+ persistResult.status,
964
+ persistResult.errorData
965
+ );
966
+ } else {
967
+ console.log(`🔍 Audit: Entry persisted (${persistResult.entryCount} total entries)`);
968
+ }
969
+ } catch (error) {
970
+ console.error('🚨 Audit: Storage error:', error);
971
+ }
972
+ }
973
+
974
+ /**
975
+ * Clear audit buffer (for testing)
976
+ */
977
+ public clearBuffer(): void {
978
+ this.auditBuffer = [];
979
+ }
980
+
981
+ /**
982
+ * Get current buffer size (for monitoring)
983
+ */
984
+ public getBufferSize(): number {
985
+ return this.auditBuffer.length;
986
+ }
987
+ }
988
+
989
+ // Export singleton instance
990
+ export const auditService = AuditService.getInstance();