@striae-org/striae 3.1.0 → 3.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.
- package/.env.example +2 -0
- package/app/components/actions/generate-pdf.ts +4 -0
- package/app/components/icon/icons.svg +4 -71
- package/app/components/icon/manifest.json +3 -75
- package/app/components/public-signing-key-modal/public-signing-key-modal.module.css +171 -0
- package/app/components/public-signing-key-modal/public-signing-key-modal.tsx +223 -0
- package/app/components/sidebar/case-export/case-export.module.css +0 -158
- package/app/components/sidebar/case-export/case-export.tsx +8 -185
- package/app/config-example/inactivity.ts +1 -1
- package/app/services/audit-export.service.ts +1 -1
- package/app/services/firebase.ts +4 -0
- package/app/utils/auth.ts +5 -1
- package/package.json +6 -3
- package/public/_routes.json +1 -0
- package/public/icon-256.png +0 -0
- package/scripts/deploy-config.sh +226 -2
- package/scripts/deploy-pages.sh +2 -2
- package/scripts/deploy-worker-secrets.sh +6 -2
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/keys-worker/src/keys.example.ts +1 -0
- package/workers/keys-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/package.json +1 -0
- package/workers/pdf-worker/src/format-striae.ts +2 -1
- package/workers/pdf-worker/src/generated-assets.ts +117 -0
- package/workers/pdf-worker/src/pdf-worker.example.ts +12 -1
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
|
@@ -317,164 +317,6 @@
|
|
|
317
317
|
border-color: color-mix(in lab, var(--primary) 55%, transparent);
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
.publicKeyOverlay {
|
|
321
|
-
position: fixed;
|
|
322
|
-
inset: 0;
|
|
323
|
-
background-color: color-mix(in lab, var(--background) 60%, transparent);
|
|
324
|
-
display: flex;
|
|
325
|
-
justify-content: center;
|
|
326
|
-
align-items: center;
|
|
327
|
-
z-index: var(--zIndex5);
|
|
328
|
-
padding: var(--spaceL);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
.publicKeyModal {
|
|
332
|
-
width: 100%;
|
|
333
|
-
max-width: 640px;
|
|
334
|
-
max-height: 90vw;
|
|
335
|
-
background: var(--backgroundLight);
|
|
336
|
-
border-radius: var(--spaceXS);
|
|
337
|
-
display: flex;
|
|
338
|
-
flex-direction: column;
|
|
339
|
-
box-shadow: 0 var(--spaceXS) var(--spaceL)
|
|
340
|
-
color-mix(in lab, var(--black) 18%, transparent);
|
|
341
|
-
overflow: hidden;
|
|
342
|
-
cursor: default;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
.publicKeyHeader {
|
|
346
|
-
display: flex;
|
|
347
|
-
justify-content: space-between;
|
|
348
|
-
align-items: center;
|
|
349
|
-
padding: var(--spaceL);
|
|
350
|
-
border-bottom: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
.publicKeyTitle {
|
|
354
|
-
margin: 0;
|
|
355
|
-
font-size: var(--fontSizeBodyL);
|
|
356
|
-
font-weight: 600;
|
|
357
|
-
color: var(--textTitle);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
.publicKeyContent {
|
|
361
|
-
padding: var(--spaceL);
|
|
362
|
-
flex: 1 1 auto;
|
|
363
|
-
min-height: 0;
|
|
364
|
-
display: flex;
|
|
365
|
-
flex-direction: column;
|
|
366
|
-
gap: var(--spaceM);
|
|
367
|
-
overflow-y: auto;
|
|
368
|
-
overflow-x: hidden;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
.publicKeyDescription {
|
|
372
|
-
margin: 0;
|
|
373
|
-
font-size: var(--fontSizeBodyS);
|
|
374
|
-
color: var(--textBody);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
.publicKeyMeta {
|
|
378
|
-
margin: 0;
|
|
379
|
-
font-size: var(--fontSizeBodyS);
|
|
380
|
-
color: var(--textTitle);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
.publicKeyMeta span {
|
|
384
|
-
font-weight: var(--fontWeightMedium);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
.publicKeyLabel {
|
|
388
|
-
font-size: var(--fontSizeBodyXS);
|
|
389
|
-
font-weight: var(--fontWeightMedium);
|
|
390
|
-
color: var(--textTitle);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
.publicKeyField {
|
|
394
|
-
width: 100%;
|
|
395
|
-
max-width: 100%;
|
|
396
|
-
box-sizing: border-box;
|
|
397
|
-
min-height: 180px;
|
|
398
|
-
padding: var(--spaceM);
|
|
399
|
-
border: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
400
|
-
border-radius: var(--spaceXS);
|
|
401
|
-
background: color-mix(in lab, var(--background) 96%, transparent);
|
|
402
|
-
color: var(--textBody);
|
|
403
|
-
font-size: var(--fontSizeBodyXS);
|
|
404
|
-
line-height: 1.4;
|
|
405
|
-
font-family: Consolas, "Courier New", monospace;
|
|
406
|
-
resize: vertical;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
.publicKeyHowToTitle {
|
|
410
|
-
margin: 0;
|
|
411
|
-
font-size: var(--fontSizeBodyS);
|
|
412
|
-
font-weight: var(--fontWeightMedium);
|
|
413
|
-
color: var(--textTitle);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
.publicKeyHowToList {
|
|
417
|
-
margin: 0;
|
|
418
|
-
padding-left: var(--spaceL);
|
|
419
|
-
display: flex;
|
|
420
|
-
flex-direction: column;
|
|
421
|
-
gap: var(--spaceXS);
|
|
422
|
-
color: var(--textBody);
|
|
423
|
-
font-size: var(--fontSizeBodyS);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
.publicKeyActions {
|
|
427
|
-
display: flex;
|
|
428
|
-
justify-content: flex-end;
|
|
429
|
-
gap: var(--spaceS);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
.publicKeyStatus {
|
|
433
|
-
margin: 0;
|
|
434
|
-
font-size: var(--fontSizeBodyXS);
|
|
435
|
-
color: var(--textBody);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
.publicKeyCopyButton {
|
|
439
|
-
background: transparent;
|
|
440
|
-
color: var(--primary);
|
|
441
|
-
border: 1px solid color-mix(in lab, var(--primary) 35%, transparent);
|
|
442
|
-
border-radius: var(--spaceXS);
|
|
443
|
-
padding: var(--spaceS) var(--spaceL);
|
|
444
|
-
font-size: var(--fontSizeBodyS);
|
|
445
|
-
font-weight: var(--fontWeightMedium);
|
|
446
|
-
cursor: pointer;
|
|
447
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
.publicKeyCopyButton:hover:not(:disabled) {
|
|
451
|
-
background: color-mix(in lab, var(--primary) 10%, transparent);
|
|
452
|
-
border-color: color-mix(in lab, var(--primary) 55%, transparent);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
.publicKeyCopyButton:disabled {
|
|
456
|
-
background: color-mix(in lab, var(--background) 95%, transparent);
|
|
457
|
-
color: var(--textLight);
|
|
458
|
-
border-color: color-mix(in lab, var(--text) 10%, transparent);
|
|
459
|
-
cursor: not-allowed;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
.publicKeyCloseButton {
|
|
463
|
-
background: var(--primary);
|
|
464
|
-
color: white;
|
|
465
|
-
border: none;
|
|
466
|
-
border-radius: var(--spaceXS);
|
|
467
|
-
padding: var(--spaceS) var(--spaceL);
|
|
468
|
-
font-size: var(--fontSizeBodyS);
|
|
469
|
-
font-weight: var(--fontWeightMedium);
|
|
470
|
-
cursor: pointer;
|
|
471
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
.publicKeyCloseButton:hover {
|
|
475
|
-
background: color-mix(in lab, var(--primary) 85%, var(--black));
|
|
476
|
-
}
|
|
477
|
-
|
|
478
320
|
.divider {
|
|
479
321
|
margin: var(--spaceL) 0;
|
|
480
322
|
text-align: center;
|
|
@@ -2,6 +2,7 @@ import { useState, useEffect, useContext } from 'react';
|
|
|
2
2
|
import styles from './case-export.module.css';
|
|
3
3
|
import config from '~/config/config.json';
|
|
4
4
|
import { AuthContext } from '~/contexts/auth.context';
|
|
5
|
+
import { PublicSigningKeyModal } from '~/components/public-signing-key-modal/public-signing-key-modal';
|
|
5
6
|
import { getVerificationPublicKey } from '~/utils/signature-utils';
|
|
6
7
|
import { getCaseConfirmations, exportConfirmationData } from '../../actions/confirm-export';
|
|
7
8
|
|
|
@@ -86,8 +87,6 @@ export const CaseExport = ({
|
|
|
86
87
|
const [includeImages, setIncludeImages] = useState(false);
|
|
87
88
|
const [hasConfirmationData, setHasConfirmationData] = useState(false);
|
|
88
89
|
const [isPublicKeyModalOpen, setIsPublicKeyModalOpen] = useState(false);
|
|
89
|
-
const [isCopyingPublicKey, setIsCopyingPublicKey] = useState(false);
|
|
90
|
-
const [publicKeyCopyMessage, setPublicKeyCopyMessage] = useState('');
|
|
91
90
|
const { keyId: publicSigningKeyId, publicKeyPem } = getPublicSigningKeyDetails();
|
|
92
91
|
|
|
93
92
|
// Update caseNumber when currentCaseNumber prop changes
|
|
@@ -153,22 +152,10 @@ export const CaseExport = ({
|
|
|
153
152
|
}
|
|
154
153
|
}, [isOpen]);
|
|
155
154
|
|
|
156
|
-
useEffect(() => {
|
|
157
|
-
if (!isPublicKeyModalOpen) {
|
|
158
|
-
setIsCopyingPublicKey(false);
|
|
159
|
-
setPublicKeyCopyMessage('');
|
|
160
|
-
}
|
|
161
|
-
}, [isPublicKeyModalOpen]);
|
|
162
|
-
|
|
163
155
|
// Handle Escape key to close modal
|
|
164
156
|
useEffect(() => {
|
|
165
157
|
const handleEscapeKey = (event: KeyboardEvent) => {
|
|
166
|
-
if (event.key === 'Escape' && isOpen) {
|
|
167
|
-
if (isPublicKeyModalOpen) {
|
|
168
|
-
setIsPublicKeyModalOpen(false);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
158
|
+
if (event.key === 'Escape' && isOpen && !isPublicKeyModalOpen) {
|
|
172
159
|
onClose();
|
|
173
160
|
}
|
|
174
161
|
};
|
|
@@ -261,81 +248,6 @@ export const CaseExport = ({
|
|
|
261
248
|
}
|
|
262
249
|
};
|
|
263
250
|
|
|
264
|
-
const handlePublicKeyOverlayMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
265
|
-
if (e.target === e.currentTarget) {
|
|
266
|
-
setIsPublicKeyModalOpen(false);
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
const handlePublicKeyOverlayKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
271
|
-
if (e.target !== e.currentTarget) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
276
|
-
e.preventDefault();
|
|
277
|
-
setIsPublicKeyModalOpen(false);
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
const copyTextWithExecCommand = (text: string): boolean => {
|
|
282
|
-
const tempTextarea = document.createElement('textarea');
|
|
283
|
-
tempTextarea.value = text;
|
|
284
|
-
tempTextarea.setAttribute('readonly', '');
|
|
285
|
-
tempTextarea.style.position = 'fixed';
|
|
286
|
-
tempTextarea.style.opacity = '0';
|
|
287
|
-
tempTextarea.style.pointerEvents = 'none';
|
|
288
|
-
|
|
289
|
-
document.body.appendChild(tempTextarea);
|
|
290
|
-
tempTextarea.select();
|
|
291
|
-
|
|
292
|
-
let copied = false;
|
|
293
|
-
try {
|
|
294
|
-
copied = document.execCommand('copy');
|
|
295
|
-
} finally {
|
|
296
|
-
document.body.removeChild(tempTextarea);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return copied;
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const handleCopyPublicKey = async () => {
|
|
303
|
-
if (!publicKeyPem) {
|
|
304
|
-
setPublicKeyCopyMessage('No public signing key is configured for this environment.');
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
setIsCopyingPublicKey(true);
|
|
309
|
-
setPublicKeyCopyMessage('');
|
|
310
|
-
|
|
311
|
-
try {
|
|
312
|
-
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
|
313
|
-
await navigator.clipboard.writeText(publicKeyPem);
|
|
314
|
-
setPublicKeyCopyMessage('Public key copied to clipboard.');
|
|
315
|
-
} else {
|
|
316
|
-
const copied = copyTextWithExecCommand(publicKeyPem);
|
|
317
|
-
setPublicKeyCopyMessage(
|
|
318
|
-
copied
|
|
319
|
-
? 'Public key copied to clipboard.'
|
|
320
|
-
: 'Copy failed. Select and copy the key manually.'
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
} catch (copyError) {
|
|
324
|
-
const copied = copyTextWithExecCommand(publicKeyPem);
|
|
325
|
-
setPublicKeyCopyMessage(
|
|
326
|
-
copied
|
|
327
|
-
? 'Public key copied to clipboard.'
|
|
328
|
-
: 'Copy failed. Select and copy the key manually.'
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
if (!copied) {
|
|
332
|
-
console.error('Failed to copy public signing key:', copyError);
|
|
333
|
-
}
|
|
334
|
-
} finally {
|
|
335
|
-
setIsCopyingPublicKey(false);
|
|
336
|
-
}
|
|
337
|
-
};
|
|
338
|
-
|
|
339
251
|
return (
|
|
340
252
|
<div
|
|
341
253
|
className={styles.overlay}
|
|
@@ -497,101 +409,12 @@ export const CaseExport = ({
|
|
|
497
409
|
</div>
|
|
498
410
|
</div>
|
|
499
411
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
tabIndex={0}
|
|
507
|
-
aria-label="Close public signing key dialog"
|
|
508
|
-
>
|
|
509
|
-
<div
|
|
510
|
-
className={styles.publicKeyModal}
|
|
511
|
-
role="dialog"
|
|
512
|
-
aria-modal="true"
|
|
513
|
-
aria-labelledby="publicSigningKeyTitle"
|
|
514
|
-
>
|
|
515
|
-
<div className={styles.publicKeyHeader}>
|
|
516
|
-
<h3 id="publicSigningKeyTitle" className={styles.publicKeyTitle}>
|
|
517
|
-
Striae Public Signing Key
|
|
518
|
-
</h3>
|
|
519
|
-
<button
|
|
520
|
-
type="button"
|
|
521
|
-
className={styles.closeButton}
|
|
522
|
-
onClick={() => setIsPublicKeyModalOpen(false)}
|
|
523
|
-
aria-label="Close public signing key dialog"
|
|
524
|
-
>
|
|
525
|
-
×
|
|
526
|
-
</button>
|
|
527
|
-
</div>
|
|
528
|
-
|
|
529
|
-
<div className={styles.publicKeyContent}>
|
|
530
|
-
<p className={styles.publicKeyDescription}>
|
|
531
|
-
This key verifies digital signatures attached to Striae exports. It is safe to share for
|
|
532
|
-
independent verification.
|
|
533
|
-
</p>
|
|
534
|
-
|
|
535
|
-
{publicSigningKeyId && (
|
|
536
|
-
<p className={styles.publicKeyMeta}>
|
|
537
|
-
Key ID: <span>{publicSigningKeyId}</span>
|
|
538
|
-
</p>
|
|
539
|
-
)}
|
|
540
|
-
|
|
541
|
-
<label htmlFor="publicSigningKey" className={styles.publicKeyLabel}>
|
|
542
|
-
Public signing key (PEM)
|
|
543
|
-
</label>
|
|
544
|
-
<textarea
|
|
545
|
-
id="publicSigningKey"
|
|
546
|
-
className={styles.publicKeyField}
|
|
547
|
-
value={publicKeyPem || 'No public signing key is configured for this environment.'}
|
|
548
|
-
readOnly
|
|
549
|
-
rows={10}
|
|
550
|
-
/>
|
|
551
|
-
|
|
552
|
-
<p className={styles.publicKeyHowToTitle}>How to verify Striae exports</p>
|
|
553
|
-
<ol className={styles.publicKeyHowToList}>
|
|
554
|
-
<li>
|
|
555
|
-
Locate signature metadata in the export (for case ZIP exports, see FORENSIC_MANIFEST.json;
|
|
556
|
-
for confirmation exports, see metadata.signature).
|
|
557
|
-
</li>
|
|
558
|
-
<li>
|
|
559
|
-
Use this public key with your signature verification workflow (for example OpenSSL or an
|
|
560
|
-
internal verifier) to validate the signed payload.
|
|
561
|
-
</li>
|
|
562
|
-
<li>
|
|
563
|
-
Trust the export only when signature verification succeeds and the key ID matches the export
|
|
564
|
-
metadata.
|
|
565
|
-
</li>
|
|
566
|
-
</ol>
|
|
567
|
-
|
|
568
|
-
{publicKeyCopyMessage && (
|
|
569
|
-
<p className={styles.publicKeyStatus} role="status" aria-live="polite">
|
|
570
|
-
{publicKeyCopyMessage}
|
|
571
|
-
</p>
|
|
572
|
-
)}
|
|
573
|
-
|
|
574
|
-
<div className={styles.publicKeyActions}>
|
|
575
|
-
<button
|
|
576
|
-
type="button"
|
|
577
|
-
className={styles.publicKeyCopyButton}
|
|
578
|
-
onClick={handleCopyPublicKey}
|
|
579
|
-
disabled={isCopyingPublicKey || !publicKeyPem}
|
|
580
|
-
>
|
|
581
|
-
{isCopyingPublicKey ? 'Copying...' : 'Copy Key'}
|
|
582
|
-
</button>
|
|
583
|
-
<button
|
|
584
|
-
type="button"
|
|
585
|
-
className={styles.publicKeyCloseButton}
|
|
586
|
-
onClick={() => setIsPublicKeyModalOpen(false)}
|
|
587
|
-
>
|
|
588
|
-
Close
|
|
589
|
-
</button>
|
|
590
|
-
</div>
|
|
591
|
-
</div>
|
|
592
|
-
</div>
|
|
593
|
-
</div>
|
|
594
|
-
)}
|
|
412
|
+
<PublicSigningKeyModal
|
|
413
|
+
isOpen={isPublicKeyModalOpen}
|
|
414
|
+
onClose={() => setIsPublicKeyModalOpen(false)}
|
|
415
|
+
publicSigningKeyId={publicSigningKeyId}
|
|
416
|
+
publicKeyPem={publicKeyPem}
|
|
417
|
+
/>
|
|
595
418
|
</div>
|
|
596
419
|
);
|
|
597
420
|
};
|
|
@@ -557,7 +557,7 @@ Audit Signature: ${JSON.stringify(signaturePayload.signature)}
|
|
|
557
557
|
Verification Instructions:
|
|
558
558
|
1. Copy the entire report content above the "INTEGRITY VERIFICATION" section
|
|
559
559
|
2. Calculate SHA256 hash of that content (excluding this verification section)
|
|
560
|
-
3. Validate audit signature metadata and signature with
|
|
560
|
+
3. Validate audit signature metadata and signature with your signature verification workflow (for example OpenSSL or an internal verifier)
|
|
561
561
|
4. Confirm both hash and signature validation pass before relying on this report
|
|
562
562
|
|
|
563
563
|
This report requires both hash and signature validation for tamper detection.
|
package/app/services/firebase.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { initializeApp } from 'firebase/app';
|
|
2
2
|
import {
|
|
3
3
|
getAuth,
|
|
4
|
+
setPersistence,
|
|
5
|
+
browserSessionPersistence,
|
|
4
6
|
//connectAuthEmulator,
|
|
5
7
|
} from 'firebase/auth';
|
|
6
8
|
import firebaseConfig from '~/config/firebase';
|
|
@@ -9,6 +11,8 @@ import { getAppVersion } from '~/utils/version';
|
|
|
9
11
|
export const app = initializeApp(firebaseConfig, "Striae");
|
|
10
12
|
export const auth = getAuth(app);
|
|
11
13
|
|
|
14
|
+
setPersistence(auth, browserSessionPersistence);
|
|
15
|
+
|
|
12
16
|
console.log(`Welcome to ${app.name} v${getAppVersion()}`);
|
|
13
17
|
|
|
14
18
|
//Connect to the Firebase Auth emulator if running locally
|
package/app/utils/auth.ts
CHANGED
|
@@ -3,7 +3,7 @@ import paths from '~/config/config.json';
|
|
|
3
3
|
const KEYS_URL = paths.keys_url;
|
|
4
4
|
const KEYS_AUTH = paths.keys_auth;
|
|
5
5
|
|
|
6
|
-
type KeyType = 'USER_DB_AUTH' | 'R2_KEY_SECRET' | 'IMAGES_API_TOKEN' | 'ACCOUNT_HASH';
|
|
6
|
+
type KeyType = 'USER_DB_AUTH' | 'R2_KEY_SECRET' | 'IMAGES_API_TOKEN' | 'ACCOUNT_HASH' | 'PDF_WORKER_AUTH';
|
|
7
7
|
|
|
8
8
|
async function getApiKey(keyType: KeyType): Promise<string> {
|
|
9
9
|
const keyResponse = await fetch(`${KEYS_URL}/${keyType}`, {
|
|
@@ -31,4 +31,8 @@ export async function getImageApiKey(): Promise<string> {
|
|
|
31
31
|
|
|
32
32
|
export async function getAccountHash(): Promise<string> {
|
|
33
33
|
return getApiKey('ACCOUNT_HASH');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function getPdfApiKey(): Promise<string> {
|
|
37
|
+
return getApiKey('PDF_WORKER_AUTH');
|
|
34
38
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@striae-org/striae",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Cloud-native forensic annotation application for firearms identification (Remix + Cloudflare Workers).",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -77,9 +77,9 @@
|
|
|
77
77
|
"publish:github": "npm publish --registry=https://npm.pkg.github.com --@striae-org:registry=https://npm.pkg.github.com",
|
|
78
78
|
"publish:github:dry-run": "npm publish --dry-run --registry=https://npm.pkg.github.com --@striae-org:registry=https://npm.pkg.github.com",
|
|
79
79
|
"publish:all": "npm run publish:npm && npm run publish:github",
|
|
80
|
-
"publish:all:dry-run": "npm run publish:npm:dry-run && npm run publish:github:dry-run",
|
|
80
|
+
"publish:all:dry-run": "npm run publish:npm:dry-run && npm run publish:github:dry-run",
|
|
81
81
|
"lint": "node ./scripts/run-eslint.cjs",
|
|
82
|
-
"start": "node ./scripts/dev.cjs && wrangler pages dev
|
|
82
|
+
"start": "node ./scripts/dev.cjs && wrangler pages dev",
|
|
83
83
|
"typecheck": "tsc",
|
|
84
84
|
"typegen": "wrangler types",
|
|
85
85
|
"preview": "npm run build && wrangler pages dev",
|
|
@@ -130,6 +130,9 @@
|
|
|
130
130
|
"vite-tsconfig-paths": "^6.1.1",
|
|
131
131
|
"wrangler": "^3.114.17"
|
|
132
132
|
},
|
|
133
|
+
"overrides": {
|
|
134
|
+
"tar": "7.5.11"
|
|
135
|
+
},
|
|
133
136
|
"engines": {
|
|
134
137
|
"node": ">=20.0.0"
|
|
135
138
|
},
|
package/public/_routes.json
CHANGED
|
Binary file
|