@striae-org/striae 4.0.3 → 4.2.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 (118) hide show
  1. package/.env.example +8 -0
  2. package/app/components/actions/case-export/core-export.ts +14 -8
  3. package/app/components/actions/case-export/data-processing.ts +1 -0
  4. package/app/components/actions/case-export/download-handlers.ts +7 -0
  5. package/app/components/actions/case-export/metadata-helpers.ts +2 -1
  6. package/app/components/actions/case-import/confirmation-import.ts +12 -2
  7. package/app/components/actions/case-import/orchestrator.ts +78 -32
  8. package/app/components/actions/case-import/storage-operations.ts +97 -8
  9. package/app/components/actions/case-import/zip-processing.ts +159 -86
  10. package/app/components/actions/case-manage.ts +430 -8
  11. package/app/components/actions/confirm-export.ts +13 -4
  12. package/app/components/actions/generate-pdf.ts +10 -2
  13. package/app/components/actions/image-manage.ts +77 -44
  14. package/app/components/audit/user-audit-viewer.tsx +137 -945
  15. package/app/components/audit/user-audit.module.css +41 -0
  16. package/app/components/audit/viewer/audit-activity-summary.tsx +52 -0
  17. package/app/components/audit/viewer/audit-entries-list.tsx +207 -0
  18. package/app/components/audit/viewer/audit-filters-panel.tsx +307 -0
  19. package/app/components/audit/viewer/audit-user-info-card.tsx +44 -0
  20. package/app/components/audit/viewer/audit-viewer-header.tsx +55 -0
  21. package/app/components/audit/viewer/audit-viewer-utils.ts +123 -0
  22. package/app/components/audit/viewer/types.ts +1 -0
  23. package/app/components/audit/viewer/use-audit-viewer-data.ts +186 -0
  24. package/app/components/audit/viewer/use-audit-viewer-export.ts +176 -0
  25. package/app/components/audit/viewer/use-audit-viewer-filters.ts +141 -0
  26. package/app/components/auth/mfa-enrollment.module.css +13 -5
  27. package/app/components/auth/mfa-verification.module.css +13 -5
  28. package/app/components/canvas/box-annotations/box-annotations.module.css +22 -18
  29. package/app/components/canvas/box-annotations/box-annotations.tsx +15 -0
  30. package/app/components/canvas/canvas.module.css +64 -54
  31. package/app/components/canvas/canvas.tsx +17 -16
  32. package/app/components/canvas/confirmation/confirmation.module.css +1 -0
  33. package/app/components/canvas/confirmation/confirmation.tsx +17 -47
  34. package/app/components/navbar/case-modals/archive-case-modal.module.css +110 -0
  35. package/app/components/navbar/case-modals/archive-case-modal.tsx +129 -0
  36. package/app/components/navbar/case-modals/open-case-modal.module.css +81 -0
  37. package/app/components/navbar/case-modals/open-case-modal.tsx +120 -0
  38. package/app/components/navbar/case-modals/rename-case-modal.module.css +81 -0
  39. package/app/components/navbar/case-modals/rename-case-modal.tsx +107 -0
  40. package/app/components/navbar/navbar.module.css +447 -0
  41. package/app/components/navbar/navbar.tsx +377 -0
  42. package/app/components/public-signing-key-modal/public-signing-key-modal.module.css +2 -0
  43. package/app/components/public-signing-key-modal/public-signing-key-modal.tsx +21 -51
  44. package/app/components/sidebar/case-export/case-export.module.css +1 -0
  45. package/app/components/sidebar/case-export/case-export.tsx +14 -77
  46. package/app/components/sidebar/case-import/case-import.module.css +25 -0
  47. package/app/components/sidebar/case-import/case-import.tsx +64 -40
  48. package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +20 -1
  49. package/app/components/sidebar/case-import/components/ConfirmationDialog.tsx +15 -0
  50. package/app/components/sidebar/cases/case-sidebar.tsx +25 -519
  51. package/app/components/sidebar/cases/cases-modal.module.css +45 -9
  52. package/app/components/sidebar/cases/cases-modal.tsx +16 -16
  53. package/app/components/sidebar/cases/cases.module.css +62 -21
  54. package/app/components/sidebar/files/files-modal.module.css +46 -10
  55. package/app/components/sidebar/files/files-modal.tsx +22 -23
  56. package/app/components/sidebar/notes/notes-editor-modal.module.css +49 -0
  57. package/app/components/sidebar/notes/notes-editor-modal.tsx +66 -0
  58. package/app/components/sidebar/notes/notes-modal.tsx +18 -17
  59. package/app/components/sidebar/notes/notes-sidebar.tsx +199 -113
  60. package/app/components/sidebar/notes/notes.module.css +155 -0
  61. package/app/components/sidebar/sidebar-container.tsx +15 -28
  62. package/app/components/sidebar/sidebar.module.css +7 -71
  63. package/app/components/sidebar/sidebar.tsx +24 -125
  64. package/app/components/sidebar/upload/image-upload-zone.module.css +13 -13
  65. package/app/components/toast/toast.module.css +2 -1
  66. package/app/components/toast/toast.tsx +16 -11
  67. package/app/components/user/delete-account.tsx +10 -31
  68. package/app/components/user/inactivity-warning.module.css +9 -6
  69. package/app/components/user/inactivity-warning.tsx +15 -2
  70. package/app/components/user/manage-profile.module.css +2 -0
  71. package/app/components/user/manage-profile.tsx +108 -40
  72. package/app/hooks/useOverlayDismiss.ts +116 -0
  73. package/app/routes/auth/login.example.tsx +19 -8
  74. package/app/routes/auth/login.tsx +785 -774
  75. package/app/routes/auth/passwordReset.module.css +23 -13
  76. package/app/routes/striae/striae.module.css +10 -3
  77. package/app/routes/striae/striae.tsx +477 -31
  78. package/app/routes.ts +7 -0
  79. package/app/services/audit/audit-export-csv.ts +2 -0
  80. package/app/services/audit/audit.service.ts +202 -32
  81. package/app/services/audit/builders/audit-entry-builder.ts +2 -1
  82. package/app/services/audit/builders/audit-event-builders-case-file.ts +43 -0
  83. package/app/services/audit/builders/audit-event-builders-user-security.ts +4 -2
  84. package/app/services/audit/builders/audit-event-builders-workflow.ts +8 -0
  85. package/app/services/audit/builders/index.ts +1 -0
  86. package/app/types/audit.ts +5 -2
  87. package/app/types/case.ts +29 -0
  88. package/app/types/import.ts +3 -0
  89. package/app/types/user.ts +1 -0
  90. package/app/utils/data/permissions.ts +17 -1
  91. package/app/utils/forensics/audit-export-signature.ts +5 -1
  92. package/app/utils/forensics/confirmation-signature.ts +3 -0
  93. package/app/utils/forensics/export-verification.ts +497 -22
  94. package/functions/api/pdf/[[path]].ts +32 -1
  95. package/load-context.ts +9 -0
  96. package/package.json +6 -2
  97. package/primershear.emails.example +6 -0
  98. package/scripts/deploy-pages-secrets.sh +6 -0
  99. package/scripts/deploy-primershear-emails.sh +167 -0
  100. package/worker-configuration.d.ts +7493 -7491
  101. package/workers/audit-worker/worker-configuration.d.ts +7448 -11323
  102. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  103. package/workers/data-worker/worker-configuration.d.ts +7448 -11323
  104. package/workers/data-worker/wrangler.jsonc.example +1 -1
  105. package/workers/image-worker/worker-configuration.d.ts +7447 -11322
  106. package/workers/image-worker/wrangler.jsonc.example +1 -1
  107. package/workers/keys-worker/worker-configuration.d.ts +7447 -11322
  108. package/workers/keys-worker/wrangler.jsonc.example +1 -1
  109. package/workers/pdf-worker/src/formats/format-striae.ts +8 -7
  110. package/workers/pdf-worker/src/pdf-worker.example.ts +3 -0
  111. package/workers/pdf-worker/src/report-types.ts +3 -0
  112. package/workers/pdf-worker/worker-configuration.d.ts +7448 -11323
  113. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  114. package/workers/user-worker/src/user-worker.example.ts +6 -1
  115. package/workers/user-worker/worker-configuration.d.ts +7448 -11323
  116. package/workers/user-worker/wrangler.jsonc.example +1 -1
  117. package/wrangler.toml.example +1 -1
  118. package/public/.well-known/keybase.txt +0 -56
@@ -7,6 +7,17 @@ import { canUploadFile, getCaseData, updateCaseData, deleteFileAnnotations } fro
7
7
  import type { CaseData, FileData, ImageUploadResponse } from '~/types';
8
8
  import { auditService } from '~/services/audit';
9
9
 
10
+ export interface DeleteFileResult {
11
+ imageMissing: boolean;
12
+ fileName: string;
13
+ }
14
+
15
+ export interface DeleteFileOptions {
16
+ skipValidation?: boolean;
17
+ skipCaseDataUpdate?: boolean;
18
+ suppressAudit?: boolean;
19
+ }
20
+
10
21
  export const fetchFiles = async (
11
22
  user: User,
12
23
  caseNumber: string,
@@ -114,7 +125,13 @@ export const uploadFile = async (
114
125
  }
115
126
  };
116
127
 
117
- export const deleteFile = async (user: User, caseNumber: string, fileId: string, deleteReason: string = 'User-requested deletion via file list'): Promise<void> => {
128
+ export const deleteFile = async (
129
+ user: User,
130
+ caseNumber: string,
131
+ fileId: string,
132
+ deleteReason: string = 'User-requested deletion via file list',
133
+ options: DeleteFileOptions = {}
134
+ ): Promise<DeleteFileResult> => {
118
135
  const startTime = Date.now();
119
136
 
120
137
  // Get file info for audit logging (outside try block so it's available in catch)
@@ -123,7 +140,9 @@ export const deleteFile = async (user: User, caseNumber: string, fileId: string,
123
140
 
124
141
  try {
125
142
  // Get the case data using centralized function
126
- const caseData = await getCaseData(user, caseNumber);
143
+ const caseData = await getCaseData(user, caseNumber, {
144
+ skipValidation: options.skipValidation === true
145
+ });
127
146
  if (!caseData) {
128
147
  throw new Error('Case not found');
129
148
  }
@@ -133,6 +152,7 @@ export const deleteFile = async (user: User, caseNumber: string, fileId: string,
133
152
  const fileSize = 0; // We don't store file size, so use 0
134
153
 
135
154
  let imageDeleteFailed = false;
155
+ let imageMissing = false;
136
156
  let imageDeleteError = '';
137
157
 
138
158
  // Attempt to delete image file
@@ -145,6 +165,7 @@ export const deleteFile = async (user: User, caseNumber: string, fileId: string,
145
165
  if (imageResponse.status === 404) {
146
166
  // Image already doesn't exist - proceed with data cleanup
147
167
  console.warn(`Image ${fileId} not found (404) - proceeding with data cleanup`);
168
+ imageMissing = true;
148
169
  } else {
149
170
  // Other errors should still fail the operation
150
171
  imageDeleteFailed = true;
@@ -160,64 +181,76 @@ export const deleteFile = async (user: User, caseNumber: string, fileId: string,
160
181
  // Clean up data files regardless of image deletion success/404
161
182
  // Try to delete notes file using centralized function
162
183
  try {
163
- await deleteFileAnnotations(user, caseNumber, fileId);
184
+ await deleteFileAnnotations(user, caseNumber, fileId, {
185
+ skipValidation: options.skipValidation === true
186
+ });
164
187
  } catch (error) {
165
188
  // Ignore 404 errors - notes file might not exist
166
189
  console.log('Notes file deletion result:', error);
167
190
  }
168
191
 
169
- // Update case data.json to remove file reference using centralized function
170
- const updatedData: CaseData = {
171
- ...caseData,
172
- files: (caseData.files || []).filter((f: FileData) => f.id !== fileId)
173
- };
192
+ if (options.skipCaseDataUpdate !== true) {
193
+ // Update case data.json to remove file reference using centralized function
194
+ const updatedData: CaseData = {
195
+ ...caseData,
196
+ files: (caseData.files || []).filter((f: FileData) => f.id !== fileId)
197
+ };
174
198
 
175
- await updateCaseData(user, caseNumber, updatedData);
199
+ await updateCaseData(user, caseNumber, updatedData);
200
+ }
176
201
 
177
202
  // Log successful file deletion
178
203
  const endTime = Date.now();
179
- try {
180
- await auditService.logFileDeletion(
181
- user,
182
- fileName,
183
- fileSize,
184
- deleteReason,
185
- caseNumber,
186
- fileId,
187
- fileToDelete?.originalFilename
188
- );
189
- } catch (auditError) {
190
- console.error('Failed to log file deletion:', auditError);
204
+ if (options.suppressAudit !== true) {
205
+ try {
206
+ await auditService.logFileDeletion(
207
+ user,
208
+ fileName,
209
+ fileSize,
210
+ deleteReason,
211
+ caseNumber,
212
+ fileId,
213
+ fileToDelete?.originalFilename
214
+ );
215
+ } catch (auditError) {
216
+ console.error('Failed to log file deletion:', auditError);
217
+ }
191
218
  }
192
219
 
193
220
  console.log(`✅ File deleted: ${fileName} (${endTime - startTime}ms)`);
221
+ return {
222
+ imageMissing,
223
+ fileName
224
+ };
194
225
 
195
226
  } catch (error) {
196
227
  // Log failed file deletion
197
228
  const endTime = Date.now();
198
- try {
199
- await auditService.logEvent({
200
- userId: user.uid,
201
- userEmail: user.email || '',
202
- action: 'file-delete',
203
- result: 'failure',
204
- fileName: fileName, // Now uses the original filename
205
- fileType: 'unknown',
206
- validationErrors: [error instanceof Error ? error.message : 'Unknown error'],
207
- caseNumber,
208
- fileDetails: {
209
- fileId: fileId,
210
- fileSize: 0,
211
- deleteReason: 'Failed deletion attempt',
212
- originalFileName: fileToDelete?.originalFilename
213
- },
214
- performanceMetrics: {
215
- processingTimeMs: endTime - startTime,
216
- fileSizeBytes: 0
217
- }
218
- });
219
- } catch (auditError) {
220
- console.error('Failed to log file deletion failure:', auditError);
229
+ if (options.suppressAudit !== true) {
230
+ try {
231
+ await auditService.logEvent({
232
+ userId: user.uid,
233
+ userEmail: user.email || '',
234
+ action: 'file-delete',
235
+ result: 'failure',
236
+ fileName: fileName, // Now uses the original filename
237
+ fileType: 'unknown',
238
+ validationErrors: [error instanceof Error ? error.message : 'Unknown error'],
239
+ caseNumber,
240
+ fileDetails: {
241
+ fileId: fileId,
242
+ fileSize: 0,
243
+ deleteReason: 'Failed deletion attempt',
244
+ originalFileName: fileToDelete?.originalFilename
245
+ },
246
+ performanceMetrics: {
247
+ processingTimeMs: endTime - startTime,
248
+ fileSizeBytes: 0
249
+ }
250
+ });
251
+ } catch (auditError) {
252
+ console.error('Failed to log file deletion failure:', auditError);
253
+ }
221
254
  }
222
255
 
223
256
  console.error('Error in deleteFile:', error);