@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,449 @@
1
+ import type { User } from 'firebase/auth';
2
+ import { type AuditResult, type CreateAuditEntryParams } from '~/types';
3
+
4
+ interface BuildUserLoginAuditParamsInput {
5
+ user: User;
6
+ sessionId: string;
7
+ loginMethod: 'firebase' | 'sso' | 'api-key' | 'manual';
8
+ userAgent?: string;
9
+ }
10
+
11
+ export const buildUserLoginAuditParams = (
12
+ input: BuildUserLoginAuditParamsInput
13
+ ): CreateAuditEntryParams => {
14
+ return {
15
+ userId: input.user.uid,
16
+ userEmail: input.user.email || '',
17
+ action: 'user-login',
18
+ result: 'success',
19
+ fileName: `session-${input.sessionId}.log`,
20
+ fileType: 'log-file',
21
+ validationErrors: [],
22
+ workflowPhase: 'user-management',
23
+ sessionDetails: {
24
+ sessionId: input.sessionId,
25
+ userAgent: input.userAgent,
26
+ loginMethod: input.loginMethod
27
+ }
28
+ };
29
+ };
30
+
31
+ interface BuildUserLogoutAuditParamsInput {
32
+ user: User;
33
+ sessionId: string;
34
+ sessionDuration: number;
35
+ logoutReason: 'user-initiated' | 'timeout' | 'security' | 'error';
36
+ }
37
+
38
+ export const buildUserLogoutAuditParams = (
39
+ input: BuildUserLogoutAuditParamsInput
40
+ ): CreateAuditEntryParams => {
41
+ return {
42
+ userId: input.user.uid,
43
+ userEmail: input.user.email || '',
44
+ action: 'user-logout',
45
+ result: 'success',
46
+ fileName: `session-${input.sessionId}.log`,
47
+ fileType: 'log-file',
48
+ validationErrors: [],
49
+ workflowPhase: 'user-management',
50
+ sessionDetails: {
51
+ sessionId: input.sessionId,
52
+ sessionDuration: input.sessionDuration,
53
+ logoutReason: input.logoutReason
54
+ }
55
+ };
56
+ };
57
+
58
+ interface BuildUserProfileUpdateAuditParamsInput {
59
+ user: User;
60
+ profileField: 'displayName' | 'email' | 'organization' | 'role' | 'preferences' | 'avatar';
61
+ oldValue: string;
62
+ newValue: string;
63
+ result: AuditResult;
64
+ sessionId?: string;
65
+ errors?: string[];
66
+ }
67
+
68
+ export const buildUserProfileUpdateAuditParams = (
69
+ input: BuildUserProfileUpdateAuditParamsInput
70
+ ): CreateAuditEntryParams => {
71
+ return {
72
+ userId: input.user.uid,
73
+ userEmail: input.user.email || '',
74
+ action: 'user-profile-update',
75
+ result: input.result,
76
+ fileName: `profile-update-${input.profileField}.log`,
77
+ fileType: 'log-file',
78
+ validationErrors: input.errors || [],
79
+ workflowPhase: 'user-management',
80
+ sessionDetails: input.sessionId
81
+ ? {
82
+ sessionId: input.sessionId
83
+ }
84
+ : undefined,
85
+ userProfileDetails: {
86
+ profileField: input.profileField,
87
+ oldValue: input.oldValue,
88
+ newValue: input.newValue
89
+ }
90
+ };
91
+ };
92
+
93
+ interface BuildPasswordResetAuditParamsInput {
94
+ userEmail: string;
95
+ resetMethod: 'email' | 'sms' | 'security-questions' | 'admin-reset';
96
+ result: AuditResult;
97
+ resetToken?: string;
98
+ verificationMethod?: 'email-link' | 'sms-code' | 'totp' | 'backup-codes';
99
+ verificationAttempts?: number;
100
+ passwordComplexityMet?: boolean;
101
+ previousPasswordReused?: boolean;
102
+ sessionId?: string;
103
+ errors?: string[];
104
+ }
105
+
106
+ export const buildPasswordResetAuditParams = (
107
+ input: BuildPasswordResetAuditParamsInput
108
+ ): CreateAuditEntryParams => {
109
+ return {
110
+ userId: '',
111
+ userEmail: input.userEmail,
112
+ action: 'user-password-reset',
113
+ result: input.result,
114
+ fileName: `password-reset-${input.resetMethod}.log`,
115
+ fileType: 'log-file',
116
+ validationErrors: input.errors || [],
117
+ workflowPhase: 'user-management',
118
+ sessionDetails: input.sessionId
119
+ ? {
120
+ sessionId: input.sessionId
121
+ }
122
+ : undefined,
123
+ userProfileDetails: {
124
+ resetMethod: input.resetMethod,
125
+ resetToken: input.resetToken ? `***${input.resetToken.slice(-4)}` : undefined,
126
+ verificationMethod: input.verificationMethod,
127
+ verificationAttempts: input.verificationAttempts,
128
+ passwordComplexityMet: input.passwordComplexityMet,
129
+ previousPasswordReused: input.previousPasswordReused
130
+ }
131
+ };
132
+ };
133
+
134
+ interface BuildAccountDeletionAuditParamsInput {
135
+ userId: string;
136
+ userEmail: string;
137
+ result: AuditResult;
138
+ deletionReason?: 'user-requested' | 'admin-initiated' | 'policy-violation' | 'inactive-account';
139
+ confirmationMethod?: 'uid-email' | 'password' | 'admin-override';
140
+ casesCount?: number;
141
+ filesCount?: number;
142
+ dataRetentionPeriod?: number;
143
+ emailNotificationSent?: boolean;
144
+ sessionId?: string;
145
+ errors?: string[];
146
+ }
147
+
148
+ export const buildAccountDeletionAuditParams = (
149
+ input: BuildAccountDeletionAuditParamsInput
150
+ ): CreateAuditEntryParams => {
151
+ return {
152
+ userId: input.userId,
153
+ userEmail: input.userEmail || '',
154
+ action: 'user-account-delete',
155
+ result: input.result,
156
+ fileName: `account-deletion-${input.userId}.log`,
157
+ fileType: 'log-file',
158
+ validationErrors: input.errors || [],
159
+ workflowPhase: 'user-management',
160
+ sessionDetails: input.sessionId
161
+ ? {
162
+ sessionId: input.sessionId
163
+ }
164
+ : undefined,
165
+ userProfileDetails: {
166
+ deletionReason: input.deletionReason || 'user-requested',
167
+ confirmationMethod: input.confirmationMethod || 'uid-email',
168
+ casesCount: input.casesCount,
169
+ filesCount: input.filesCount,
170
+ dataRetentionPeriod: input.dataRetentionPeriod,
171
+ emailNotificationSent: input.emailNotificationSent
172
+ }
173
+ };
174
+ };
175
+
176
+ interface BuildUserRegistrationAuditParamsInput {
177
+ user: User;
178
+ firstName: string;
179
+ lastName: string;
180
+ company: string;
181
+ registrationMethod: 'email-password' | 'sso' | 'admin-created' | 'api';
182
+ userAgent?: string;
183
+ sessionId?: string;
184
+ }
185
+
186
+ export const buildUserRegistrationAuditParams = (
187
+ input: BuildUserRegistrationAuditParamsInput
188
+ ): CreateAuditEntryParams => {
189
+ return {
190
+ userId: input.user.uid,
191
+ userEmail: input.user.email || '',
192
+ action: 'user-registration',
193
+ result: 'success',
194
+ fileName: `registration-${input.user.uid}.log`,
195
+ fileType: 'log-file',
196
+ validationErrors: [],
197
+ workflowPhase: 'user-management',
198
+ sessionDetails: input.sessionId
199
+ ? {
200
+ sessionId: input.sessionId,
201
+ userAgent: input.userAgent
202
+ }
203
+ : { userAgent: input.userAgent },
204
+ userProfileDetails: {
205
+ registrationMethod: input.registrationMethod,
206
+ firstName: input.firstName,
207
+ lastName: input.lastName,
208
+ company: input.company,
209
+ emailVerificationRequired: true,
210
+ mfaEnrollmentRequired: true
211
+ }
212
+ };
213
+ };
214
+
215
+ interface BuildMfaEnrollmentAuditParamsInput {
216
+ user: User;
217
+ phoneNumber: string;
218
+ mfaMethod: 'sms' | 'totp' | 'hardware-key';
219
+ result: AuditResult;
220
+ enrollmentAttempts?: number;
221
+ sessionId?: string;
222
+ userAgent?: string;
223
+ errors?: string[];
224
+ }
225
+
226
+ const getMaskedPhoneNumber = (phoneNumber: string): string => {
227
+ return phoneNumber.length > 4 ? `***-***-${phoneNumber.slice(-4)}` : '***-***-****';
228
+ };
229
+
230
+ export const buildMfaEnrollmentAuditParams = (
231
+ input: BuildMfaEnrollmentAuditParamsInput
232
+ ): CreateAuditEntryParams => {
233
+ return {
234
+ userId: input.user.uid,
235
+ userEmail: input.user.email || '',
236
+ action: 'mfa-enrollment',
237
+ result: input.result,
238
+ fileName: `mfa-enrollment-${input.user.uid}.log`,
239
+ fileType: 'log-file',
240
+ validationErrors: input.errors || [],
241
+ workflowPhase: 'user-management',
242
+ sessionDetails: input.sessionId
243
+ ? {
244
+ sessionId: input.sessionId,
245
+ userAgent: input.userAgent
246
+ }
247
+ : { userAgent: input.userAgent },
248
+ securityDetails: {
249
+ mfaMethod: input.mfaMethod,
250
+ phoneNumber: getMaskedPhoneNumber(input.phoneNumber),
251
+ enrollmentAttempts: input.enrollmentAttempts,
252
+ enrollmentDate: new Date().toISOString(),
253
+ mandatoryEnrollment: true,
254
+ backupCodesGenerated: false
255
+ }
256
+ };
257
+ };
258
+
259
+ interface BuildMfaAuthenticationAuditParamsInput {
260
+ user: User;
261
+ mfaMethod: 'sms' | 'totp' | 'hardware-key';
262
+ result: AuditResult;
263
+ verificationAttempts?: number;
264
+ sessionId?: string;
265
+ userAgent?: string;
266
+ errors?: string[];
267
+ }
268
+
269
+ export const buildMfaAuthenticationAuditParams = (
270
+ input: BuildMfaAuthenticationAuditParamsInput
271
+ ): CreateAuditEntryParams => {
272
+ return {
273
+ userId: input.user.uid,
274
+ userEmail: input.user.email || '',
275
+ action: 'mfa-authentication',
276
+ result: input.result,
277
+ fileName: `mfa-auth-${input.sessionId || Date.now()}.log`,
278
+ fileType: 'log-file',
279
+ validationErrors: input.errors || [],
280
+ workflowPhase: 'user-management',
281
+ sessionDetails: input.sessionId
282
+ ? {
283
+ sessionId: input.sessionId,
284
+ userAgent: input.userAgent
285
+ }
286
+ : { userAgent: input.userAgent },
287
+ securityDetails: {
288
+ mfaMethod: input.mfaMethod,
289
+ verificationAttempts: input.verificationAttempts,
290
+ authenticationDate: new Date().toISOString(),
291
+ loginFlowStep: 'second-factor'
292
+ }
293
+ };
294
+ };
295
+
296
+ interface BuildEmailVerificationAuditParamsInput {
297
+ user: User;
298
+ result: AuditResult;
299
+ verificationMethod: 'email-link' | 'admin-verification';
300
+ verificationAttempts?: number;
301
+ sessionId?: string;
302
+ userAgent?: string;
303
+ errors?: string[];
304
+ }
305
+
306
+ export const buildEmailVerificationAuditParams = (
307
+ input: BuildEmailVerificationAuditParamsInput
308
+ ): CreateAuditEntryParams => {
309
+ return {
310
+ userId: input.user.uid,
311
+ userEmail: input.user.email || '',
312
+ action: 'email-verification',
313
+ result: input.result,
314
+ fileName: `email-verification-${input.user.uid}.log`,
315
+ fileType: 'log-file',
316
+ validationErrors: input.errors || [],
317
+ workflowPhase: 'user-management',
318
+ sessionDetails: input.sessionId
319
+ ? {
320
+ sessionId: input.sessionId,
321
+ userAgent: input.userAgent
322
+ }
323
+ : { userAgent: input.userAgent },
324
+ userProfileDetails: {
325
+ verificationMethod: input.verificationMethod,
326
+ verificationAttempts: input.verificationAttempts,
327
+ verificationDate: new Date().toISOString(),
328
+ emailVerified: input.result === 'success'
329
+ }
330
+ };
331
+ };
332
+
333
+ interface BuildEmailVerificationByEmailAuditParamsInput {
334
+ userEmail: string;
335
+ result: AuditResult;
336
+ verificationMethod: 'email-link' | 'admin-verification';
337
+ verificationAttempts?: number;
338
+ sessionId?: string;
339
+ userAgent?: string;
340
+ errors?: string[];
341
+ userId?: string;
342
+ }
343
+
344
+ export const buildEmailVerificationByEmailAuditParams = (
345
+ input: BuildEmailVerificationByEmailAuditParamsInput
346
+ ): CreateAuditEntryParams => {
347
+ const userId = input.userId || '';
348
+
349
+ return {
350
+ userId,
351
+ userEmail: input.userEmail,
352
+ action: 'email-verification',
353
+ result: input.result,
354
+ fileName: `email-verification-${userId || Date.now()}.log`,
355
+ fileType: 'log-file',
356
+ validationErrors: input.errors || [],
357
+ workflowPhase: 'user-management',
358
+ sessionDetails: input.sessionId
359
+ ? {
360
+ sessionId: input.sessionId,
361
+ userAgent: input.userAgent
362
+ }
363
+ : { userAgent: input.userAgent },
364
+ userProfileDetails: {
365
+ verificationMethod: input.verificationMethod,
366
+ verificationAttempts: input.verificationAttempts,
367
+ verificationDate: new Date().toISOString(),
368
+ emailVerified: input.result === 'success'
369
+ }
370
+ };
371
+ };
372
+
373
+ interface BuildMarkEmailVerificationSuccessfulAuditParamsInput {
374
+ user: User;
375
+ reason?: string;
376
+ sessionId?: string;
377
+ userAgent?: string;
378
+ }
379
+
380
+ export const buildMarkEmailVerificationSuccessfulAuditParams = (
381
+ input: BuildMarkEmailVerificationSuccessfulAuditParamsInput
382
+ ): CreateAuditEntryParams => {
383
+ return {
384
+ userId: input.user.uid,
385
+ userEmail: input.user.email || '',
386
+ action: 'email-verification',
387
+ result: 'success',
388
+ fileName: `email-verification-${input.user.uid}.log`,
389
+ fileType: 'log-file',
390
+ validationErrors: [],
391
+ workflowPhase: 'user-management',
392
+ sessionDetails: input.sessionId
393
+ ? {
394
+ sessionId: input.sessionId,
395
+ userAgent: input.userAgent
396
+ }
397
+ : { userAgent: input.userAgent },
398
+ userProfileDetails: {
399
+ verificationMethod: 'email-link',
400
+ verificationAttempts: 1,
401
+ verificationDate: new Date().toISOString(),
402
+ emailVerified: true,
403
+ retroactiveVerification: true,
404
+ retroactiveReason: input.reason || 'MFA enrollment completed'
405
+ }
406
+ };
407
+ };
408
+
409
+ interface BuildSecurityViolationAuditParamsInput {
410
+ user: User | null;
411
+ incidentType:
412
+ | 'unauthorized-access'
413
+ | 'data-breach'
414
+ | 'malware'
415
+ | 'injection'
416
+ | 'brute-force'
417
+ | 'privilege-escalation';
418
+ severity: 'low' | 'medium' | 'high' | 'critical';
419
+ description: string;
420
+ targetResource?: string;
421
+ blockedBySystem?: boolean;
422
+ }
423
+
424
+ export const buildSecurityViolationAuditParams = (
425
+ input: BuildSecurityViolationAuditParamsInput
426
+ ): CreateAuditEntryParams => {
427
+ const blockedBySystem = input.blockedBySystem !== undefined ? input.blockedBySystem : true;
428
+
429
+ return {
430
+ userId: input.user?.uid || 'unknown',
431
+ userEmail: input.user?.email || 'unknown@system.com',
432
+ action: 'security-violation',
433
+ result: blockedBySystem ? 'blocked' : 'failure',
434
+ fileName: `security-incident-${Date.now()}.log`,
435
+ fileType: 'log-file',
436
+ validationErrors: [input.description],
437
+ securityDetails: {
438
+ incidentType: input.incidentType,
439
+ severity: input.severity,
440
+ targetResource: input.targetResource,
441
+ blockedBySystem,
442
+ investigationId: `INV-${Date.now()}`,
443
+ reportedToAuthorities: input.severity === 'critical',
444
+ mitigationSteps: [
445
+ blockedBySystem ? 'Automatically blocked by system' : 'Manual intervention required'
446
+ ]
447
+ }
448
+ };
449
+ };
@@ -0,0 +1,272 @@
1
+ import type { User } from 'firebase/auth';
2
+ import {
3
+ type AuditFileType,
4
+ type AuditResult,
5
+ type CreateAuditEntryParams,
6
+ type PerformanceMetrics,
7
+ type SecurityCheckResults
8
+ } from '~/types';
9
+
10
+ interface SignatureDetailsInput {
11
+ present?: boolean;
12
+ valid?: boolean;
13
+ keyId?: string;
14
+ }
15
+
16
+ interface BuildCaseExportAuditParamsInput {
17
+ user: User;
18
+ caseNumber: string;
19
+ fileName: string;
20
+ result: AuditResult;
21
+ errors?: string[];
22
+ performanceMetrics?: PerformanceMetrics;
23
+ exportFormat?: 'json' | 'csv' | 'xlsx' | 'zip';
24
+ signatureDetails?: SignatureDetailsInput;
25
+ }
26
+
27
+ const resolveCaseExportFileType = (
28
+ fileName: string,
29
+ exportFormat?: 'json' | 'csv' | 'xlsx' | 'zip'
30
+ ): AuditFileType => {
31
+ if (exportFormat) {
32
+ switch (exportFormat) {
33
+ case 'json':
34
+ return 'json-data';
35
+ case 'csv':
36
+ case 'xlsx':
37
+ return 'csv-export';
38
+ case 'zip':
39
+ default:
40
+ return 'case-package';
41
+ }
42
+ }
43
+
44
+ if (fileName.includes('.json')) return 'json-data';
45
+ if (fileName.includes('.csv') || fileName.includes('.xlsx')) return 'csv-export';
46
+ return 'case-package';
47
+ };
48
+
49
+ export const buildCaseExportAuditParams = (
50
+ input: BuildCaseExportAuditParamsInput
51
+ ): CreateAuditEntryParams => {
52
+ const securityChecks: SecurityCheckResults = {
53
+ selfConfirmationPrevented: false,
54
+ fileIntegrityValid: input.result === 'success',
55
+ manifestSignaturePresent: input.signatureDetails?.present,
56
+ manifestSignatureValid: input.signatureDetails?.valid,
57
+ manifestSignatureKeyId: input.signatureDetails?.keyId
58
+ };
59
+
60
+ return {
61
+ userId: input.user.uid,
62
+ userEmail: input.user.email || '',
63
+ action: 'export',
64
+ result: input.result,
65
+ fileName: input.fileName,
66
+ fileType: resolveCaseExportFileType(input.fileName, input.exportFormat),
67
+ validationErrors: input.errors || [],
68
+ caseNumber: input.caseNumber,
69
+ workflowPhase: 'case-export',
70
+ securityChecks,
71
+ performanceMetrics: input.performanceMetrics,
72
+ originalExaminerUid: input.user.uid
73
+ };
74
+ };
75
+
76
+ interface BuildCaseImportAuditParamsInput {
77
+ user: User;
78
+ caseNumber: string;
79
+ fileName: string;
80
+ result: AuditResult;
81
+ hashValid: boolean;
82
+ errors?: string[];
83
+ originalExaminerUid?: string;
84
+ performanceMetrics?: PerformanceMetrics;
85
+ exporterUidValidated?: boolean;
86
+ signatureDetails?: SignatureDetailsInput;
87
+ }
88
+
89
+ export const buildCaseImportAuditParams = (
90
+ input: BuildCaseImportAuditParamsInput
91
+ ): CreateAuditEntryParams => {
92
+ const securityChecks: SecurityCheckResults = {
93
+ selfConfirmationPrevented: input.originalExaminerUid
94
+ ? input.originalExaminerUid !== input.user.uid
95
+ : false,
96
+ fileIntegrityValid: input.hashValid,
97
+ exporterUidValidated:
98
+ input.exporterUidValidated !== undefined
99
+ ? input.exporterUidValidated
100
+ : !!input.originalExaminerUid,
101
+ manifestSignaturePresent: input.signatureDetails?.present,
102
+ manifestSignatureValid: input.signatureDetails?.valid,
103
+ manifestSignatureKeyId: input.signatureDetails?.keyId
104
+ };
105
+
106
+ return {
107
+ userId: input.user.uid,
108
+ userEmail: input.user.email || '',
109
+ action: 'import',
110
+ result: input.result,
111
+ fileName: input.fileName,
112
+ fileType: 'case-package',
113
+ hashValid: input.hashValid,
114
+ validationErrors: input.errors || [],
115
+ caseNumber: input.caseNumber,
116
+ workflowPhase: 'case-import',
117
+ securityChecks,
118
+ performanceMetrics: input.performanceMetrics,
119
+ originalExaminerUid: input.originalExaminerUid,
120
+ reviewingExaminerUid: input.user.uid
121
+ };
122
+ };
123
+
124
+ interface BuildConfirmationCreationAuditParamsInput {
125
+ user: User;
126
+ caseNumber: string;
127
+ confirmationId: string;
128
+ result: AuditResult;
129
+ errors?: string[];
130
+ originalExaminerUid?: string;
131
+ performanceMetrics?: PerformanceMetrics;
132
+ imageFileId?: string;
133
+ originalImageFileName?: string;
134
+ }
135
+
136
+ export const buildConfirmationCreationAuditParams = (
137
+ input: BuildConfirmationCreationAuditParamsInput
138
+ ): CreateAuditEntryParams => {
139
+ const securityChecks: SecurityCheckResults = {
140
+ selfConfirmationPrevented: false,
141
+ fileIntegrityValid: true
142
+ };
143
+
144
+ return {
145
+ userId: input.user.uid,
146
+ userEmail: input.user.email || '',
147
+ action: 'confirm',
148
+ result: input.result,
149
+ fileName: `confirmation-${input.confirmationId}`,
150
+ fileType: 'confirmation-data',
151
+ validationErrors: input.errors || [],
152
+ caseNumber: input.caseNumber,
153
+ confirmationId: input.confirmationId,
154
+ workflowPhase: 'confirmation',
155
+ securityChecks,
156
+ performanceMetrics: input.performanceMetrics,
157
+ originalExaminerUid: input.originalExaminerUid,
158
+ reviewingExaminerUid: input.user.uid,
159
+ fileDetails: input.imageFileId && input.originalImageFileName
160
+ ? {
161
+ fileId: input.imageFileId,
162
+ originalFileName: input.originalImageFileName,
163
+ fileSize: 0
164
+ }
165
+ : undefined
166
+ };
167
+ };
168
+
169
+ interface BuildConfirmationExportAuditParamsInput {
170
+ user: User;
171
+ caseNumber: string;
172
+ fileName: string;
173
+ result: AuditResult;
174
+ errors?: string[];
175
+ originalExaminerUid?: string;
176
+ performanceMetrics?: PerformanceMetrics;
177
+ signatureDetails?: SignatureDetailsInput;
178
+ }
179
+
180
+ export const buildConfirmationExportAuditParams = (
181
+ input: BuildConfirmationExportAuditParamsInput
182
+ ): CreateAuditEntryParams => {
183
+ const securityChecks: SecurityCheckResults = {
184
+ selfConfirmationPrevented: false,
185
+ fileIntegrityValid: input.result === 'success',
186
+ manifestSignaturePresent: input.signatureDetails?.present,
187
+ manifestSignatureValid: input.signatureDetails?.valid,
188
+ manifestSignatureKeyId: input.signatureDetails?.keyId
189
+ };
190
+
191
+ return {
192
+ userId: input.user.uid,
193
+ userEmail: input.user.email || '',
194
+ action: 'export',
195
+ result: input.result,
196
+ fileName: input.fileName,
197
+ fileType: 'confirmation-data',
198
+ validationErrors: input.errors || [],
199
+ caseNumber: input.caseNumber,
200
+ workflowPhase: 'confirmation',
201
+ securityChecks,
202
+ performanceMetrics: input.performanceMetrics,
203
+ originalExaminerUid: input.originalExaminerUid,
204
+ reviewingExaminerUid: input.user.uid
205
+ };
206
+ };
207
+
208
+ interface BuildConfirmationImportAuditParamsInput {
209
+ user: User;
210
+ caseNumber: string;
211
+ fileName: string;
212
+ result: AuditResult;
213
+ hashValid: boolean;
214
+ confirmationsImported: number;
215
+ errors?: string[];
216
+ reviewingExaminerUid?: string;
217
+ performanceMetrics?: PerformanceMetrics;
218
+ exporterUidValidated?: boolean;
219
+ totalConfirmationsInFile?: number;
220
+ signatureDetails?: SignatureDetailsInput;
221
+ }
222
+
223
+ export const buildConfirmationImportAuditParams = (
224
+ input: BuildConfirmationImportAuditParamsInput
225
+ ): CreateAuditEntryParams => {
226
+ const securityChecks: SecurityCheckResults = {
227
+ selfConfirmationPrevented: input.reviewingExaminerUid
228
+ ? input.reviewingExaminerUid === input.user.uid
229
+ : false,
230
+ fileIntegrityValid: input.hashValid,
231
+ exporterUidValidated:
232
+ input.exporterUidValidated !== undefined
233
+ ? input.exporterUidValidated
234
+ : !!input.reviewingExaminerUid,
235
+ manifestSignaturePresent: input.signatureDetails?.present,
236
+ manifestSignatureValid: input.signatureDetails?.valid,
237
+ manifestSignatureKeyId: input.signatureDetails?.keyId
238
+ };
239
+
240
+ return {
241
+ userId: input.user.uid,
242
+ userEmail: input.user.email || '',
243
+ action: 'import',
244
+ result: input.result,
245
+ fileName: input.fileName,
246
+ fileType: 'confirmation-data',
247
+ hashValid: input.hashValid,
248
+ validationErrors: input.errors || [],
249
+ caseNumber: input.caseNumber,
250
+ workflowPhase: 'confirmation',
251
+ securityChecks,
252
+ performanceMetrics: input.performanceMetrics
253
+ ? {
254
+ ...input.performanceMetrics,
255
+ validationStepsCompleted: input.confirmationsImported,
256
+ validationStepsFailed: (input.errors || []).length
257
+ }
258
+ : {
259
+ processingTimeMs: 0,
260
+ fileSizeBytes: 0,
261
+ validationStepsCompleted: input.confirmationsImported,
262
+ validationStepsFailed: (input.errors || []).length
263
+ },
264
+ originalExaminerUid: input.user.uid,
265
+ reviewingExaminerUid: input.reviewingExaminerUid,
266
+ caseDetails: input.totalConfirmationsInFile !== undefined
267
+ ? {
268
+ totalAnnotations: input.totalConfirmationsInFile
269
+ }
270
+ : undefined
271
+ };
272
+ };