@striae-org/striae 5.2.1 → 5.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -10
- package/README.md +5 -46
- package/app/components/actions/case-export/core-export.ts +5 -174
- package/app/components/actions/case-export/download-handlers.ts +84 -751
- package/app/components/actions/case-export/index.ts +6 -30
- package/app/components/actions/case-export/metadata-helpers.ts +0 -78
- package/app/components/actions/case-export/types-constants.ts +0 -43
- package/app/components/actions/case-import/confirmation-import.ts +75 -36
- package/app/components/actions/case-import/confirmation-package.ts +68 -1
- package/app/components/actions/case-import/index.ts +1 -1
- package/app/components/actions/case-import/orchestrator.ts +78 -53
- package/app/components/actions/case-import/zip-processing.ts +160 -330
- package/app/components/actions/generate-pdf.ts +3 -2
- package/app/components/audit/user-audit-viewer.tsx +0 -19
- package/app/components/audit/viewer/audit-viewer-header.tsx +0 -33
- package/app/components/navbar/case-modals/archive-case-modal.tsx +1 -1
- package/app/components/navbar/case-modals/export-case-modal.module.css +27 -0
- package/app/components/navbar/case-modals/export-case-modal.tsx +132 -0
- package/app/components/navbar/case-modals/export-confirmations-modal.module.css +24 -0
- package/app/components/navbar/case-modals/export-confirmations-modal.tsx +108 -0
- package/app/components/navbar/navbar.tsx +1 -1
- package/app/components/sidebar/case-import/case-import.module.css +35 -0
- package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +51 -3
- package/app/components/sidebar/case-import/components/ConfirmationDialog.tsx +2 -4
- package/app/components/sidebar/case-import/components/ConfirmationPreviewSection.tsx +36 -5
- package/app/components/sidebar/case-import/hooks/useFilePreview.ts +5 -9
- package/app/components/sidebar/case-import/index.ts +1 -4
- package/app/components/sidebar/notes/class-details-shared.ts +2 -2
- package/app/components/toast/toast.module.css +36 -0
- package/app/components/toast/toast.tsx +6 -2
- package/app/components/user/manage-profile.tsx +4 -3
- package/app/config-example/config.json +1 -2
- package/app/root.tsx +0 -7
- package/app/routes/_index.tsx +1 -1
- package/app/routes/auth/login.example.tsx +22 -103
- package/app/routes/auth/login.tsx +22 -103
- package/app/routes/auth/route.ts +1 -1
- package/app/routes/striae/striae.tsx +117 -59
- package/app/services/firebase/index.ts +0 -3
- package/app/types/case.ts +1 -0
- package/app/types/export.ts +2 -2
- package/app/types/import.ts +10 -0
- package/app/utils/auth/index.ts +0 -1
- package/app/utils/data/permissions.ts +3 -2
- package/package.json +9 -16
- package/public/_headers +0 -4
- package/public/_routes.json +0 -1
- package/worker-configuration.d.ts +20 -17
- package/workers/audit-worker/src/audit-worker.example.ts +9 -806
- package/workers/audit-worker/src/config.ts +7 -0
- package/workers/audit-worker/src/crypto/data-at-rest.ts +410 -0
- package/workers/audit-worker/src/handlers/audit-routes.ts +125 -0
- package/workers/audit-worker/src/storage/audit-storage.ts +99 -0
- package/workers/audit-worker/src/types.ts +56 -0
- package/workers/audit-worker/worker-configuration.d.ts +1 -1
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/src/config.ts +11 -0
- package/workers/data-worker/src/data-worker.example.ts +21 -942
- package/workers/data-worker/src/handlers/decrypt-export.ts +118 -0
- package/workers/data-worker/src/handlers/signing.ts +174 -0
- package/workers/data-worker/src/handlers/storage-routes.ts +129 -0
- package/workers/data-worker/src/registry/key-registry.ts +368 -0
- package/workers/data-worker/src/types.ts +46 -0
- package/workers/data-worker/worker-configuration.d.ts +1 -1
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/worker-configuration.d.ts +1 -1
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/worker-configuration.d.ts +2 -3
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/src/auth.ts +30 -0
- package/workers/user-worker/src/cleanup/account-deletion.ts +337 -0
- package/workers/user-worker/src/config.ts +4 -0
- package/workers/user-worker/src/encryption-utils.ts +25 -0
- package/workers/user-worker/src/firebase/admin.ts +152 -0
- package/workers/user-worker/src/handlers/user-routes.ts +242 -0
- package/workers/user-worker/src/registry/user-kv.ts +172 -0
- package/workers/user-worker/src/storage/user-records.ts +34 -0
- package/workers/user-worker/src/types.ts +106 -0
- package/workers/user-worker/src/user-worker.example.ts +18 -964
- package/workers/user-worker/worker-configuration.d.ts +4 -2
- package/workers/user-worker/wrangler.jsonc.example +12 -1
- package/wrangler.toml.example +1 -1
- package/app/components/actions/case-export/data-processing.ts +0 -223
- package/app/components/sidebar/case-export/case-export.module.css +0 -418
- package/app/components/sidebar/case-export/case-export.tsx +0 -310
- package/app/types/exceljs-bare.d.ts +0 -9
- package/app/utils/auth/auth.ts +0 -11
- package/public/.well-known/security.txt +0 -6
- package/public/favicon.ico +0 -0
- package/public/icon-256.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/manifest.json +0 -39
- package/public/shortcut.png +0 -0
- package/public/social-image.png +0 -0
- package/public/vendor/exceljs.LICENSE +0 -22
- package/public/vendor/exceljs.bare.min.js +0 -45
- package/scripts/deploy-all.sh +0 -166
- package/scripts/deploy-config/modules/env-utils.sh +0 -322
- package/scripts/deploy-config/modules/keys.sh +0 -404
- package/scripts/deploy-config/modules/prompt.sh +0 -372
- package/scripts/deploy-config/modules/scaffolding.sh +0 -344
- package/scripts/deploy-config/modules/validation.sh +0 -365
- package/scripts/deploy-config.sh +0 -236
- package/scripts/deploy-pages-secrets.sh +0 -231
- package/scripts/deploy-pages.sh +0 -34
- package/scripts/deploy-primershear-emails.sh +0 -167
- package/scripts/deploy-worker-secrets.sh +0 -374
- package/scripts/dev.cjs +0 -23
- package/scripts/install-workers.sh +0 -88
- package/scripts/run-eslint.cjs +0 -43
- package/scripts/update-compatibility-dates.cjs +0 -124
- package/scripts/update-markdown-versions.cjs +0 -43
- package/workers/keys-worker/package.json +0 -18
- package/workers/keys-worker/src/keys.example.ts +0 -67
- package/workers/keys-worker/src/keys.ts +0 -67
- package/workers/keys-worker/worker-configuration.d.ts +0 -7447
- package/workers/keys-worker/wrangler.jsonc.example +0 -15
|
@@ -5,13 +5,15 @@ import { Navbar } from '~/components/navbar/navbar';
|
|
|
5
5
|
import { RenameCaseModal } from '~/components/navbar/case-modals/rename-case-modal';
|
|
6
6
|
import { ArchiveCaseModal } from '~/components/navbar/case-modals/archive-case-modal';
|
|
7
7
|
import { OpenCaseModal } from '~/components/navbar/case-modals/open-case-modal';
|
|
8
|
+
import { ExportCaseModal } from '~/components/navbar/case-modals/export-case-modal';
|
|
9
|
+
import { ExportConfirmationsModal } from '~/components/navbar/case-modals/export-confirmations-modal';
|
|
8
10
|
import { Toolbar } from '~/components/toolbar/toolbar';
|
|
9
11
|
import { Canvas } from '~/components/canvas/canvas';
|
|
10
|
-
import { Toast } from '~/components/toast/toast';
|
|
12
|
+
import { Toast, type ToastType } from '~/components/toast/toast';
|
|
11
13
|
import { getImageUrl, fetchFiles, deleteFile } from '~/components/actions/image-manage';
|
|
12
14
|
import { getNotes, saveNotes } from '~/components/actions/notes-manage';
|
|
13
15
|
import { generatePDF } from '~/components/actions/generate-pdf';
|
|
14
|
-
import {
|
|
16
|
+
import { exportConfirmationData } from '~/components/actions/confirm-export';
|
|
15
17
|
import { CasesModal } from '~/components/sidebar/cases/cases-modal';
|
|
16
18
|
import { FilesModal } from '~/components/sidebar/files/files-modal';
|
|
17
19
|
import { NotesEditorModal } from '~/components/sidebar/notes/notes-editor-modal';
|
|
@@ -20,7 +22,7 @@ import { fetchUserApi } from '~/utils/api';
|
|
|
20
22
|
import { type AnnotationData, type FileData } from '~/types';
|
|
21
23
|
import { validateCaseNumber, renameCase, deleteCase, checkExistingCase, createNewCase, archiveCase, getCaseArchiveDetails } from '~/components/actions/case-manage';
|
|
22
24
|
import { checkReadOnlyCaseExists, deleteReadOnlyCase } from '~/components/actions/case-review';
|
|
23
|
-
import { canCreateCase } from '~/utils/data';
|
|
25
|
+
import { canCreateCase, getCaseConfirmationSummary } from '~/utils/data';
|
|
24
26
|
import {
|
|
25
27
|
resolveEarliestAnnotationTimestamp,
|
|
26
28
|
CREATE_READ_ONLY_CASE_EXISTS_ERROR,
|
|
@@ -77,8 +79,8 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
77
79
|
const [isGeneratingPDF, setIsGeneratingPDF] = useState(false);
|
|
78
80
|
const [showToast, setShowToast] = useState(false);
|
|
79
81
|
const [toastMessage, setToastMessage] = useState('');
|
|
80
|
-
const [toastType, setToastType] = useState<
|
|
81
|
-
const [
|
|
82
|
+
const [toastType, setToastType] = useState<ToastType>('success');
|
|
83
|
+
const [toastDuration, setToastDuration] = useState(4000);
|
|
82
84
|
const [isAuditTrailOpen, setIsAuditTrailOpen] = useState(false);
|
|
83
85
|
const [isRenameCaseModalOpen, setIsRenameCaseModalOpen] = useState(false);
|
|
84
86
|
const [isOpenCaseModalOpen, setIsOpenCaseModalOpen] = useState(false);
|
|
@@ -91,6 +93,14 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
91
93
|
const [isOpeningCase, setIsOpeningCase] = useState(false);
|
|
92
94
|
const [openCaseHelperText, setOpenCaseHelperText] = useState('');
|
|
93
95
|
const [isArchiveCaseModalOpen, setIsArchiveCaseModalOpen] = useState(false);
|
|
96
|
+
const [isExportCaseModalOpen, setIsExportCaseModalOpen] = useState(false);
|
|
97
|
+
const [isExportingCase, setIsExportingCase] = useState(false);
|
|
98
|
+
const [isExportConfirmationsModalOpen, setIsExportConfirmationsModalOpen] = useState(false);
|
|
99
|
+
const [isExportingConfirmations, setIsExportingConfirmations] = useState(false);
|
|
100
|
+
const [exportConfirmationStats, setExportConfirmationStats] = useState<{
|
|
101
|
+
confirmedCount: number;
|
|
102
|
+
unconfirmedCount: number;
|
|
103
|
+
} | null>(null);
|
|
94
104
|
const [archiveDetails, setArchiveDetails] = useState<{
|
|
95
105
|
archived: boolean;
|
|
96
106
|
archivedAt?: string;
|
|
@@ -253,13 +263,19 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
253
263
|
setIsGeneratingPDF,
|
|
254
264
|
setToastType,
|
|
255
265
|
setToastMessage,
|
|
256
|
-
setShowToast
|
|
266
|
+
setShowToast,
|
|
267
|
+
setToastDuration
|
|
257
268
|
});
|
|
258
269
|
};
|
|
259
270
|
|
|
260
|
-
const showNotification = (
|
|
271
|
+
const showNotification = (
|
|
272
|
+
message: string,
|
|
273
|
+
type: ToastType = 'success',
|
|
274
|
+
duration = 4000
|
|
275
|
+
) => {
|
|
261
276
|
setToastType(type);
|
|
262
277
|
setToastMessage(message);
|
|
278
|
+
setToastDuration(duration);
|
|
263
279
|
setShowToast(true);
|
|
264
280
|
};
|
|
265
281
|
|
|
@@ -270,60 +286,82 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
270
286
|
|
|
271
287
|
const handleExport = async (
|
|
272
288
|
exportCaseNumber: string,
|
|
273
|
-
|
|
274
|
-
includeImages?: boolean,
|
|
289
|
+
designatedReviewerEmail?: string,
|
|
275
290
|
onProgress?: (progress: number, label: string) => void
|
|
276
291
|
) => {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (includeImages) {
|
|
280
|
-
await caseExportActions.downloadCaseAsZip(user, exportCaseNumber, format, (progress) => {
|
|
281
|
-
const label = getExportProgressLabel(progress);
|
|
282
|
-
onProgress?.(Math.round(progress), label);
|
|
283
|
-
});
|
|
284
|
-
showNotification(`Case ${exportCaseNumber} exported successfully.`, 'success');
|
|
292
|
+
if (!exportCaseNumber) {
|
|
293
|
+
showNotification('Select a case before exporting.', 'error');
|
|
285
294
|
return;
|
|
286
295
|
}
|
|
287
296
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
(
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
297
|
+
showNotification(`Exporting case ${exportCaseNumber}...`, 'loading', 0);
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
const caseExportActions = await loadCaseExportActions();
|
|
301
|
+
|
|
302
|
+
await caseExportActions.downloadCaseAsZip(
|
|
303
|
+
user,
|
|
304
|
+
exportCaseNumber,
|
|
305
|
+
(progress) => {
|
|
306
|
+
const roundedProgress = Math.round(progress);
|
|
307
|
+
const label = getExportProgressLabel(progress);
|
|
308
|
+
setToastType('loading');
|
|
309
|
+
setToastMessage(`Exporting case ${exportCaseNumber}... ${label} (${roundedProgress}%)`);
|
|
310
|
+
setToastDuration(0);
|
|
311
|
+
setShowToast(true);
|
|
312
|
+
onProgress?.(roundedProgress, label);
|
|
313
|
+
},
|
|
314
|
+
{ designatedReviewerEmail }
|
|
315
|
+
);
|
|
298
316
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
} else {
|
|
303
|
-
await caseExportActions.downloadCaseAsCSV(user, exportData);
|
|
317
|
+
showNotification(`Case ${exportCaseNumber} exported successfully.`, 'success');
|
|
318
|
+
} catch (error) {
|
|
319
|
+
showNotification(error instanceof Error ? error.message : 'Export failed. Please try again.', 'error');
|
|
304
320
|
}
|
|
305
|
-
onProgress?.(100, 'Complete');
|
|
306
|
-
showNotification(`Case ${exportCaseNumber} exported successfully.`, 'success');
|
|
307
321
|
};
|
|
308
322
|
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
323
|
+
const handleExportCaseModalSubmit = async (designatedReviewerEmail: string | undefined) => {
|
|
324
|
+
setIsExportingCase(true);
|
|
325
|
+
setIsExportCaseModalOpen(false);
|
|
326
|
+
try {
|
|
327
|
+
await handleExport(currentCase || '', designatedReviewerEmail);
|
|
328
|
+
} finally {
|
|
329
|
+
setIsExportingCase(false);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const handleOpenExportConfirmationsModal = async () => {
|
|
334
|
+
if (!currentCase || !user) return;
|
|
319
335
|
|
|
320
|
-
|
|
321
|
-
await
|
|
322
|
-
|
|
323
|
-
|
|
336
|
+
try {
|
|
337
|
+
const summary = await getCaseConfirmationSummary(user, currentCase);
|
|
338
|
+
const filesById = summary?.filesById ?? {};
|
|
339
|
+
const values = Object.values(filesById);
|
|
340
|
+
const confirmedCount = values.filter((f) => f.includeConfirmation && f.isConfirmed).length;
|
|
341
|
+
const unconfirmedCount = values.filter((f) => f.includeConfirmation && !f.isConfirmed).length;
|
|
342
|
+
setExportConfirmationStats({ confirmedCount, unconfirmedCount });
|
|
343
|
+
} catch {
|
|
344
|
+
setExportConfirmationStats({ confirmedCount: 0, unconfirmedCount: 0 });
|
|
324
345
|
}
|
|
325
346
|
|
|
326
|
-
|
|
347
|
+
setIsExportConfirmationsModalOpen(true);
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const handleExportConfirmations = async () => {
|
|
351
|
+
if (!currentCase || !user) return;
|
|
352
|
+
|
|
353
|
+
setIsExportingConfirmations(true);
|
|
354
|
+
showNotification(`Exporting confirmations for case ${currentCase}...`, 'loading', 0);
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
await exportConfirmationData(user, currentCase);
|
|
358
|
+
setIsExportConfirmationsModalOpen(false);
|
|
359
|
+
showNotification(`Confirmations for case ${currentCase} exported successfully.`, 'success');
|
|
360
|
+
} catch (e) {
|
|
361
|
+
showNotification(e instanceof Error ? e.message : 'Confirmation export failed. Please try again.', 'error');
|
|
362
|
+
} finally {
|
|
363
|
+
setIsExportingConfirmations(false);
|
|
364
|
+
}
|
|
327
365
|
};
|
|
328
366
|
|
|
329
367
|
const handleRenameCaseSubmit = async (newCaseName: string) => {
|
|
@@ -370,6 +408,8 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
370
408
|
}
|
|
371
409
|
|
|
372
410
|
setIsDeletingCase(true);
|
|
411
|
+
showNotification(`Deleting case ${currentCase}...`, 'loading', 0);
|
|
412
|
+
|
|
373
413
|
try {
|
|
374
414
|
const deleteResult = await deleteCase(user, currentCase);
|
|
375
415
|
clearLoadedCaseState();
|
|
@@ -474,6 +514,8 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
474
514
|
}
|
|
475
515
|
|
|
476
516
|
setIsArchivingCase(true);
|
|
517
|
+
showNotification(`Archiving case ${currentCase}... Preparing archive package.`, 'loading', 0);
|
|
518
|
+
|
|
477
519
|
try {
|
|
478
520
|
await archiveCase(user, currentCase, archiveReason);
|
|
479
521
|
setIsReadOnlyCase(true);
|
|
@@ -767,7 +809,13 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
767
809
|
void handleOpenCaseModal();
|
|
768
810
|
}}
|
|
769
811
|
onOpenListAllCases={() => setIsListCasesModalOpen(true)}
|
|
770
|
-
onOpenCaseExport={() =>
|
|
812
|
+
onOpenCaseExport={() => {
|
|
813
|
+
if (isReadOnlyCase) {
|
|
814
|
+
void handleOpenExportConfirmationsModal();
|
|
815
|
+
} else {
|
|
816
|
+
setIsExportCaseModalOpen(true);
|
|
817
|
+
}
|
|
818
|
+
}}
|
|
771
819
|
onOpenAuditTrail={() => setIsAuditTrailOpen(true)}
|
|
772
820
|
onOpenRenameCase={() => setIsRenameCaseModalOpen(true)}
|
|
773
821
|
onDeleteCase={() => {
|
|
@@ -790,7 +838,7 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
790
838
|
onOpenCase={() => {
|
|
791
839
|
void handleOpenCaseModal();
|
|
792
840
|
}}
|
|
793
|
-
onOpenCaseExport={() =>
|
|
841
|
+
onOpenCaseExport={() => void handleOpenExportConfirmationsModal()}
|
|
794
842
|
imageId={imageId}
|
|
795
843
|
currentCase={currentCase}
|
|
796
844
|
imageLoaded={imageLoaded}
|
|
@@ -883,14 +931,6 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
883
931
|
isUploading={isUploading}
|
|
884
932
|
showNotification={showNotification}
|
|
885
933
|
/>
|
|
886
|
-
<CaseExport
|
|
887
|
-
isOpen={isCaseExportModalOpen}
|
|
888
|
-
onClose={() => setIsCaseExportModalOpen(false)}
|
|
889
|
-
onExport={handleExport}
|
|
890
|
-
onExportAll={handleExportAll}
|
|
891
|
-
currentCaseNumber={currentCase}
|
|
892
|
-
isReadOnly={isReadOnlyCase}
|
|
893
|
-
/>
|
|
894
934
|
<UserAuditViewer
|
|
895
935
|
caseNumber={currentCase || ''}
|
|
896
936
|
isOpen={isAuditTrailOpen}
|
|
@@ -911,11 +951,29 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
911
951
|
onClose={() => setIsArchiveCaseModalOpen(false)}
|
|
912
952
|
onSubmit={handleArchiveCaseSubmit}
|
|
913
953
|
/>
|
|
954
|
+
<ExportCaseModal
|
|
955
|
+
isOpen={isExportCaseModalOpen}
|
|
956
|
+
caseNumber={currentCase || ''}
|
|
957
|
+
currentUserEmail={user.email ?? undefined}
|
|
958
|
+
isSubmitting={isExportingCase}
|
|
959
|
+
onClose={() => setIsExportCaseModalOpen(false)}
|
|
960
|
+
onSubmit={handleExportCaseModalSubmit}
|
|
961
|
+
/>
|
|
962
|
+
<ExportConfirmationsModal
|
|
963
|
+
isOpen={isExportConfirmationsModalOpen}
|
|
964
|
+
caseNumber={currentCase || ''}
|
|
965
|
+
confirmedCount={exportConfirmationStats?.confirmedCount ?? 0}
|
|
966
|
+
unconfirmedCount={exportConfirmationStats?.unconfirmedCount ?? 0}
|
|
967
|
+
isSubmitting={isExportingConfirmations}
|
|
968
|
+
onClose={() => setIsExportConfirmationsModalOpen(false)}
|
|
969
|
+
onConfirm={() => void handleExportConfirmations()}
|
|
970
|
+
/>
|
|
914
971
|
<Toast
|
|
915
972
|
message={toastMessage}
|
|
916
973
|
type={toastType}
|
|
917
974
|
isVisible={showToast}
|
|
918
975
|
onClose={closeToast}
|
|
976
|
+
duration={toastDuration}
|
|
919
977
|
/>
|
|
920
978
|
</div>
|
|
921
979
|
);
|
|
@@ -6,14 +6,11 @@ import {
|
|
|
6
6
|
//connectAuthEmulator,
|
|
7
7
|
} from 'firebase/auth';
|
|
8
8
|
import firebaseConfig from '~/config/firebase';
|
|
9
|
-
import { getAppVersion } from '~/utils/common';
|
|
10
9
|
|
|
11
10
|
export const app = initializeApp(firebaseConfig, "Striae");
|
|
12
11
|
export const auth = getAuth(app);
|
|
13
12
|
|
|
14
13
|
setPersistence(auth, browserSessionPersistence);
|
|
15
14
|
|
|
16
|
-
console.log(`Welcome to ${app.name} v${getAppVersion()}`);
|
|
17
|
-
|
|
18
15
|
//Connect to the Firebase Auth emulator if running locally
|
|
19
16
|
//connectAuthEmulator(auth, 'http://127.0.0.1:9099');
|
package/app/types/case.ts
CHANGED
package/app/types/export.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Export-related types and interfaces
|
|
2
2
|
|
|
3
3
|
export interface ExportOptions {
|
|
4
|
-
format?: 'json' | 'csv';
|
|
5
4
|
includeMetadata?: boolean;
|
|
6
5
|
includeUserInfo?: boolean;
|
|
7
|
-
protectForensicData?: boolean;
|
|
6
|
+
protectForensicData?: boolean;
|
|
7
|
+
designatedReviewerEmail?: string;
|
|
8
8
|
}
|
package/app/types/import.ts
CHANGED
|
@@ -78,6 +78,16 @@ export interface ConfirmationImportData {
|
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
export interface ConfirmationImportPreview {
|
|
82
|
+
caseNumber: string;
|
|
83
|
+
exportedBy: string;
|
|
84
|
+
exportedByName: string;
|
|
85
|
+
exportedByCompany: string;
|
|
86
|
+
exportedByBadgeId?: string;
|
|
87
|
+
exportDate: string;
|
|
88
|
+
totalConfirmations: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
81
91
|
export interface CaseImportPreview {
|
|
82
92
|
caseNumber: string;
|
|
83
93
|
archived?: boolean;
|
package/app/utils/auth/index.ts
CHANGED
|
@@ -103,7 +103,8 @@ export const createUser = async (
|
|
|
103
103
|
firstName: string,
|
|
104
104
|
lastName: string,
|
|
105
105
|
company: string,
|
|
106
|
-
permitted: boolean = false
|
|
106
|
+
permitted: boolean = false,
|
|
107
|
+
badgeId: string = ''
|
|
107
108
|
): Promise<UserData> => {
|
|
108
109
|
try {
|
|
109
110
|
const userData: UserData = {
|
|
@@ -112,7 +113,7 @@ export const createUser = async (
|
|
|
112
113
|
firstName,
|
|
113
114
|
lastName,
|
|
114
115
|
company,
|
|
115
|
-
badgeId
|
|
116
|
+
badgeId,
|
|
116
117
|
permitted,
|
|
117
118
|
cases: [],
|
|
118
119
|
readOnlyCases: [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@striae-org/striae",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Striae is a specialized, cloud-native platform designed to streamline forensic firearms identification by providing an intuitive environment for digital comparison image annotation, authenticated confirmations, and automated report generation.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -49,14 +49,15 @@
|
|
|
49
49
|
"load-context.ts",
|
|
50
50
|
"functions/",
|
|
51
51
|
"public/",
|
|
52
|
-
"scripts/",
|
|
53
52
|
"workers/*/package.json",
|
|
54
|
-
"workers/*/src
|
|
55
|
-
"workers/*/src
|
|
56
|
-
"workers/*/src
|
|
53
|
+
"workers/*/src/**/*.example.ts",
|
|
54
|
+
"workers/*/src/**/*.example.js",
|
|
55
|
+
"workers/*/src/**/*.ts",
|
|
57
56
|
"workers/pdf-worker/scripts/*.js",
|
|
58
|
-
"!workers/*/src
|
|
57
|
+
"!workers/*/src/**/*worker.ts",
|
|
58
|
+
"!workers/pdf-worker/src/assets/**/*",
|
|
59
59
|
"workers/pdf-worker/src/assets/generated-assets.example.ts",
|
|
60
|
+
"!workers/pdf-worker/src/formats/**/*",
|
|
60
61
|
"workers/pdf-worker/src/formats/format-striae.ts",
|
|
61
62
|
"workers/pdf-worker/src/report-types.ts",
|
|
62
63
|
"workers/*/wrangler.jsonc.example",
|
|
@@ -96,7 +97,7 @@
|
|
|
96
97
|
"deploy-config": "bash ./scripts/deploy-config.sh",
|
|
97
98
|
"update-env": "bash ./scripts/deploy-config.sh --update-env",
|
|
98
99
|
"install-workers": "bash ./scripts/install-workers.sh",
|
|
99
|
-
"deploy-workers": "npm run deploy-workers:audit && npm run deploy-workers:data && npm run deploy-workers:image && npm run deploy-workers:
|
|
100
|
+
"deploy-workers": "npm run deploy-workers:audit && npm run deploy-workers:data && npm run deploy-workers:image && npm run deploy-workers:pdf && npm run deploy-workers:user",
|
|
100
101
|
"deploy-workers:secrets": "bash ./scripts/deploy-worker-secrets.sh",
|
|
101
102
|
"deploy-pages:secrets": "bash ./scripts/deploy-pages-secrets.sh --production-only",
|
|
102
103
|
"deploy-pages": "bash ./scripts/deploy-pages.sh",
|
|
@@ -104,13 +105,11 @@
|
|
|
104
105
|
"deploy-workers:audit": "cd workers/audit-worker && npm run deploy",
|
|
105
106
|
"deploy-workers:data": "cd workers/data-worker && npm run deploy",
|
|
106
107
|
"deploy-workers:image": "cd workers/image-worker && npm run deploy",
|
|
107
|
-
"deploy-workers:keys": "cd workers/keys-worker && npm run deploy",
|
|
108
108
|
"deploy-workers:pdf": "cd workers/pdf-worker && npm run deploy",
|
|
109
109
|
"deploy-workers:user": "cd workers/user-worker && npm run deploy"
|
|
110
110
|
},
|
|
111
111
|
"dependencies": {
|
|
112
112
|
"@react-router/cloudflare": "^7.13.2",
|
|
113
|
-
"exceljs": "^4.4.0",
|
|
114
113
|
"firebase": "^12.10.0",
|
|
115
114
|
"isbot": "^5.1.36",
|
|
116
115
|
"jszip": "^3.10.1",
|
|
@@ -139,13 +138,7 @@
|
|
|
139
138
|
},
|
|
140
139
|
"overrides": {
|
|
141
140
|
"tar": "7.5.11",
|
|
142
|
-
"undici": "7.24.1"
|
|
143
|
-
"exceljs": {
|
|
144
|
-
"archiver": "7.0.1",
|
|
145
|
-
"fast-csv": "5.0.5",
|
|
146
|
-
"unzipper": "0.12.3",
|
|
147
|
-
"glob": "13.0.6"
|
|
148
|
-
}
|
|
141
|
+
"undici": "7.24.1"
|
|
149
142
|
},
|
|
150
143
|
"engines": {
|
|
151
144
|
"node": ">=20.0.0"
|
package/public/_headers
CHANGED
package/public/_routes.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
|
-
// Generated by Wrangler by running `wrangler types` (hash:
|
|
3
|
-
// Runtime types generated with workerd@1.20250823.0 2026-03-
|
|
2
|
+
// Generated by Wrangler by running `wrangler types` (hash: d8f8f87d89a635e81e94aa31fb52008f)
|
|
3
|
+
// Runtime types generated with workerd@1.20250823.0 2026-03-26 nodejs_compat
|
|
4
4
|
declare namespace Cloudflare {
|
|
5
5
|
interface Env {
|
|
6
6
|
ACCOUNT_ID: string;
|
|
@@ -16,31 +16,35 @@ declare namespace Cloudflare {
|
|
|
16
16
|
MEASUREMENT_ID: string;
|
|
17
17
|
FIREBASE_SERVICE_ACCOUNT_EMAIL: string;
|
|
18
18
|
FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY: string;
|
|
19
|
-
PAGES_PROJECT_NAME: string;
|
|
20
|
-
PAGES_CUSTOM_DOMAIN: string;
|
|
21
|
-
KEYS_WORKER_NAME: string;
|
|
22
|
-
KEYS_WORKER_DOMAIN: string;
|
|
23
|
-
KEYS_AUTH: string;
|
|
24
|
-
ACCOUNT_HASH: string;
|
|
25
|
-
USER_WORKER_NAME: string;
|
|
26
|
-
USER_WORKER_DOMAIN: string;
|
|
27
|
-
KV_STORE_ID: string;
|
|
28
19
|
USER_KV_ENCRYPTION_PRIVATE_KEY: string;
|
|
29
20
|
USER_KV_ENCRYPTION_KEY_ID: string;
|
|
30
21
|
USER_KV_ENCRYPTION_PUBLIC_KEY: string;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
DATA_WORKER_DOMAIN: string;
|
|
22
|
+
USER_KV_WRITE_ENDPOINTS_ENABLED: string;
|
|
23
|
+
USER_KV_ENCRYPTION_KEYS_JSON: string;
|
|
24
|
+
USER_KV_ENCRYPTION_ACTIVE_KEY_ID: string;
|
|
35
25
|
MANIFEST_SIGNING_PRIVATE_KEY: string;
|
|
36
26
|
MANIFEST_SIGNING_KEY_ID: string;
|
|
37
27
|
MANIFEST_SIGNING_PUBLIC_KEY: string;
|
|
38
28
|
EXPORT_ENCRYPTION_PRIVATE_KEY: string;
|
|
39
29
|
EXPORT_ENCRYPTION_KEY_ID: string;
|
|
40
30
|
EXPORT_ENCRYPTION_PUBLIC_KEY: string;
|
|
31
|
+
EXPORT_ENCRYPTION_KEYS_JSON: string;
|
|
32
|
+
EXPORT_ENCRYPTION_ACTIVE_KEY_ID: string;
|
|
33
|
+
DATA_AT_REST_ENCRYPTION_ENABLED: string;
|
|
41
34
|
DATA_AT_REST_ENCRYPTION_PRIVATE_KEY: string;
|
|
42
35
|
DATA_AT_REST_ENCRYPTION_KEY_ID: string;
|
|
43
36
|
DATA_AT_REST_ENCRYPTION_PUBLIC_KEY: string;
|
|
37
|
+
DATA_AT_REST_ENCRYPTION_KEYS_JSON: string;
|
|
38
|
+
DATA_AT_REST_ENCRYPTION_ACTIVE_KEY_ID: string;
|
|
39
|
+
PAGES_PROJECT_NAME: string;
|
|
40
|
+
PAGES_CUSTOM_DOMAIN: string;
|
|
41
|
+
USER_WORKER_NAME: string;
|
|
42
|
+
USER_WORKER_DOMAIN: string;
|
|
43
|
+
KV_STORE_ID: string;
|
|
44
|
+
DATA_WORKER_NAME: string;
|
|
45
|
+
DATA_BUCKET_NAME: string;
|
|
46
|
+
FILES_BUCKET_NAME: string;
|
|
47
|
+
DATA_WORKER_DOMAIN: string;
|
|
44
48
|
AUDIT_WORKER_NAME: string;
|
|
45
49
|
AUDIT_BUCKET_NAME: string;
|
|
46
50
|
AUDIT_WORKER_DOMAIN: string;
|
|
@@ -53,7 +57,6 @@ declare namespace Cloudflare {
|
|
|
53
57
|
PDF_WORKER_AUTH: string;
|
|
54
58
|
BROWSER_API_TOKEN: string;
|
|
55
59
|
PRIMERSHEAR_EMAILS: string;
|
|
56
|
-
DATA_AT_REST_ENCRYPTION_ENABLED: string;
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
interface Env extends Cloudflare.Env {}
|
|
@@ -61,7 +64,7 @@ type StringifyValues<EnvType extends Record<string, unknown>> = {
|
|
|
61
64
|
[Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string;
|
|
62
65
|
};
|
|
63
66
|
declare namespace NodeJS {
|
|
64
|
-
interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "ACCOUNT_ID" | "USER_DB_AUTH" | "R2_KEY_SECRET" | "IMAGES_API_TOKEN" | "API_KEY" | "AUTH_DOMAIN" | "PROJECT_ID" | "STORAGE_BUCKET" | "MESSAGING_SENDER_ID" | "APP_ID" | "MEASUREMENT_ID" | "FIREBASE_SERVICE_ACCOUNT_EMAIL" | "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" | "
|
|
67
|
+
interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "ACCOUNT_ID" | "USER_DB_AUTH" | "R2_KEY_SECRET" | "IMAGES_API_TOKEN" | "API_KEY" | "AUTH_DOMAIN" | "PROJECT_ID" | "STORAGE_BUCKET" | "MESSAGING_SENDER_ID" | "APP_ID" | "MEASUREMENT_ID" | "FIREBASE_SERVICE_ACCOUNT_EMAIL" | "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" | "USER_KV_ENCRYPTION_PRIVATE_KEY" | "USER_KV_ENCRYPTION_KEY_ID" | "USER_KV_ENCRYPTION_PUBLIC_KEY" | "USER_KV_WRITE_ENDPOINTS_ENABLED" | "USER_KV_ENCRYPTION_KEYS_JSON" | "USER_KV_ENCRYPTION_ACTIVE_KEY_ID" | "MANIFEST_SIGNING_PRIVATE_KEY" | "MANIFEST_SIGNING_KEY_ID" | "MANIFEST_SIGNING_PUBLIC_KEY" | "EXPORT_ENCRYPTION_PRIVATE_KEY" | "EXPORT_ENCRYPTION_KEY_ID" | "EXPORT_ENCRYPTION_PUBLIC_KEY" | "EXPORT_ENCRYPTION_KEYS_JSON" | "EXPORT_ENCRYPTION_ACTIVE_KEY_ID" | "DATA_AT_REST_ENCRYPTION_ENABLED" | "DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" | "DATA_AT_REST_ENCRYPTION_KEY_ID" | "DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" | "DATA_AT_REST_ENCRYPTION_KEYS_JSON" | "DATA_AT_REST_ENCRYPTION_ACTIVE_KEY_ID" | "PAGES_PROJECT_NAME" | "PAGES_CUSTOM_DOMAIN" | "USER_WORKER_NAME" | "USER_WORKER_DOMAIN" | "KV_STORE_ID" | "DATA_WORKER_NAME" | "DATA_BUCKET_NAME" | "FILES_BUCKET_NAME" | "DATA_WORKER_DOMAIN" | "AUDIT_WORKER_NAME" | "AUDIT_BUCKET_NAME" | "AUDIT_WORKER_DOMAIN" | "IMAGES_WORKER_NAME" | "IMAGES_WORKER_DOMAIN" | "IMAGE_SIGNED_URL_SECRET" | "IMAGE_SIGNED_URL_TTL_SECONDS" | "PDF_WORKER_NAME" | "PDF_WORKER_DOMAIN" | "PDF_WORKER_AUTH" | "BROWSER_API_TOKEN" | "PRIMERSHEAR_EMAILS">> {}
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
// Begin runtime types
|