@striae-org/striae 5.1.0 → 5.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 +22 -2
- package/app/components/actions/case-export/download-handlers.ts +18 -1
- package/app/components/actions/case-manage.ts +17 -1
- package/app/components/actions/generate-pdf.ts +9 -3
- package/app/components/actions/image-manage.ts +43 -11
- package/app/routes/striae/striae.tsx +1 -0
- package/app/types/file.ts +18 -2
- package/app/utils/api/image-api-client.ts +49 -1
- package/app/utils/data/permissions.ts +4 -2
- package/functions/api/image/[[path]].ts +2 -1
- package/package.json +4 -4
- package/scripts/deploy-config/modules/env-utils.sh +322 -0
- package/scripts/deploy-config/modules/keys.sh +404 -0
- package/scripts/deploy-config/modules/prompt.sh +372 -0
- package/scripts/deploy-config/modules/scaffolding.sh +336 -0
- package/scripts/deploy-config/modules/validation.sh +365 -0
- package/scripts/deploy-config.sh +59 -1556
- package/scripts/deploy-worker-secrets.sh +101 -6
- package/worker-configuration.d.ts +9 -4
- package/workers/audit-worker/package.json +1 -1
- package/workers/audit-worker/src/audit-worker.example.ts +188 -6
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/package.json +1 -1
- package/workers/data-worker/src/data-worker.example.ts +344 -32
- package/workers/data-worker/wrangler.jsonc.example +2 -4
- package/workers/image-worker/package.json +1 -1
- package/workers/image-worker/src/image-worker.example.ts +456 -20
- package/workers/image-worker/worker-configuration.d.ts +3 -2
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/keys-worker/package.json +1 -1
- package/workers/keys-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/package.json +1 -1
- package/workers/pdf-worker/src/pdf-worker.example.ts +0 -1
- package/workers/pdf-worker/wrangler.jsonc.example +1 -5
- package/workers/user-worker/package.json +17 -17
- package/workers/user-worker/src/encryption-utils.ts +244 -0
- package/workers/user-worker/src/user-worker.example.ts +333 -31
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
- package/scripts/encrypt-r2-backfill.mjs +0 -376
package/.env.example
CHANGED
|
@@ -47,7 +47,6 @@ PAGES_CUSTOM_DOMAIN=your_custom_domain_here
|
|
|
47
47
|
KEYS_WORKER_NAME=your_keys_worker_name_here
|
|
48
48
|
KEYS_WORKER_DOMAIN=your_keys_worker_domain_here
|
|
49
49
|
KEYS_AUTH=your_custom_keys_auth_token_here
|
|
50
|
-
ACCOUNT_HASH=your_cloudflare_images_account_hash_here
|
|
51
50
|
|
|
52
51
|
# ================================
|
|
53
52
|
# USER WORKER ENVIRONMENT VARIABLES
|
|
@@ -55,6 +54,17 @@ ACCOUNT_HASH=your_cloudflare_images_account_hash_here
|
|
|
55
54
|
USER_WORKER_NAME=your_user_worker_name_here
|
|
56
55
|
USER_WORKER_DOMAIN=your_user_worker_domain_here
|
|
57
56
|
KV_STORE_ID=your_kv_store_id_here
|
|
57
|
+
USER_KV_ENCRYPTION_PRIVATE_KEY=your_user_kv_encryption_private_key_here
|
|
58
|
+
USER_KV_ENCRYPTION_KEY_ID=your_user_kv_encryption_key_id_here
|
|
59
|
+
USER_KV_ENCRYPTION_PUBLIC_KEY=your_user_kv_encryption_public_key_here
|
|
60
|
+
# Optional write toggle for USER_DB mutation endpoints.
|
|
61
|
+
# true (default): require USER_KV_ENCRYPTION_PUBLIC_KEY and USER_KV_ENCRYPTION_KEY_ID for encrypt-on-write.
|
|
62
|
+
# false: allow read-only deployments using private key material (legacy key or key registry) without write-path keys.
|
|
63
|
+
USER_KV_WRITE_ENDPOINTS_ENABLED=true
|
|
64
|
+
# Optional key registry for rotation-safe USER_DB reads.
|
|
65
|
+
# JSON shape: {"activeKeyId":"kid_current","keys":{"kid_current":"-----BEGIN PRIVATE KEY-----\\n...","kid_previous":"-----BEGIN PRIVATE KEY-----\\n..."}}
|
|
66
|
+
USER_KV_ENCRYPTION_KEYS_JSON='{"activeKeyId":"your_user_kv_active_encryption_key_id_here","keys":{"your_user_kv_active_encryption_key_id_here":"your_user_kv_encryption_private_key_here"}}'
|
|
67
|
+
USER_KV_ENCRYPTION_ACTIVE_KEY_ID=your_user_kv_active_encryption_key_id_here
|
|
58
68
|
|
|
59
69
|
# ================================
|
|
60
70
|
# DATA WORKER ENVIRONMENT VARIABLES
|
|
@@ -70,11 +80,18 @@ MANIFEST_SIGNING_PUBLIC_KEY=your_manifest_signing_public_key_here
|
|
|
70
80
|
EXPORT_ENCRYPTION_PRIVATE_KEY=your_export_encryption_private_key_here
|
|
71
81
|
EXPORT_ENCRYPTION_KEY_ID=your_export_encryption_key_id_here
|
|
72
82
|
EXPORT_ENCRYPTION_PUBLIC_KEY=your_export_encryption_public_key_here
|
|
83
|
+
# Optional key registry for export decrypt compatibility.
|
|
84
|
+
# JSON shape: {"activeKeyId":"kid_current","keys":{"kid_current":"-----BEGIN PRIVATE KEY-----\\n...","kid_previous":"-----BEGIN PRIVATE KEY-----\\n..."}}
|
|
85
|
+
EXPORT_ENCRYPTION_KEYS_JSON='{"activeKeyId":"your_export_encryption_active_key_id_here","keys":{"your_export_encryption_active_key_id_here":"your_export_encryption_private_key_here"}}'
|
|
86
|
+
EXPORT_ENCRYPTION_ACTIVE_KEY_ID=your_export_encryption_active_key_id_here
|
|
73
87
|
DATA_AT_REST_ENCRYPTION_ENABLED=true
|
|
74
88
|
DATA_AT_REST_ENCRYPTION_PRIVATE_KEY=your_data_at_rest_encryption_private_key_here
|
|
75
89
|
DATA_AT_REST_ENCRYPTION_KEY_ID=your_data_at_rest_encryption_key_id_here
|
|
76
90
|
DATA_AT_REST_ENCRYPTION_PUBLIC_KEY=your_data_at_rest_encryption_public_key_here
|
|
77
|
-
|
|
91
|
+
# Optional key registry for data/files/audit decryption compatibility.
|
|
92
|
+
# JSON shape: {"activeKeyId":"kid_current","keys":{"kid_current":"-----BEGIN PRIVATE KEY-----\\n...","kid_previous":"-----BEGIN PRIVATE KEY-----\\n..."}}
|
|
93
|
+
DATA_AT_REST_ENCRYPTION_KEYS_JSON='{"activeKeyId":"your_data_at_rest_active_encryption_key_id_here","keys":{"your_data_at_rest_active_encryption_key_id_here":"your_data_at_rest_encryption_private_key_here"}}'
|
|
94
|
+
DATA_AT_REST_ENCRYPTION_ACTIVE_KEY_ID=your_data_at_rest_active_encryption_key_id_here
|
|
78
95
|
|
|
79
96
|
# ================================
|
|
80
97
|
# AUDIT WORKER ENVIRONMENT VARIABLES
|
|
@@ -88,6 +105,9 @@ AUDIT_WORKER_DOMAIN=your_audit_worker_domain_here
|
|
|
88
105
|
# ================================
|
|
89
106
|
IMAGES_WORKER_NAME=your_images_worker_name_here
|
|
90
107
|
IMAGES_WORKER_DOMAIN=your_images_worker_domain_here
|
|
108
|
+
IMAGE_SIGNED_URL_SECRET=your_image_signed_url_secret_here
|
|
109
|
+
# Optional: defaults to 3600 and max is 86400.
|
|
110
|
+
IMAGE_SIGNED_URL_TTL_SECONDS=3600
|
|
91
111
|
|
|
92
112
|
# ================================
|
|
93
113
|
# PDF WORKER ENVIRONMENT VARIABLES
|
|
@@ -965,7 +965,24 @@ For questions about this export, contact your Striae system administrator.
|
|
|
965
965
|
*/
|
|
966
966
|
async function fetchImageAsBlob(user: User, fileData: FileData, caseNumber: string): Promise<Blob | null> {
|
|
967
967
|
try {
|
|
968
|
-
const
|
|
968
|
+
const imageAccess = await getImageUrl(user, fileData, caseNumber, 'Export Package');
|
|
969
|
+
const { blob, revoke, url } = imageAccess;
|
|
970
|
+
|
|
971
|
+
if (!blob) {
|
|
972
|
+
const signedResponse = await fetch(url, {
|
|
973
|
+
method: 'GET',
|
|
974
|
+
headers: {
|
|
975
|
+
'Accept': 'application/octet-stream,image/*'
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
if (!signedResponse.ok) {
|
|
980
|
+
throw new Error(`Signed URL fetch failed with status ${signedResponse.status}`);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
return await signedResponse.blob();
|
|
984
|
+
}
|
|
985
|
+
|
|
969
986
|
try {
|
|
970
987
|
return blob;
|
|
971
988
|
} finally {
|
|
@@ -685,7 +685,23 @@ const getVerificationPublicSigningKey = (preferredKeyId?: string): { keyId: stri
|
|
|
685
685
|
|
|
686
686
|
const fetchImageAsBlob = async (user: User, fileData: FileData, caseNumber: string): Promise<Blob | null> => {
|
|
687
687
|
try {
|
|
688
|
-
const
|
|
688
|
+
const imageAccess = await getImageUrl(user, fileData, caseNumber, 'Archive Package');
|
|
689
|
+
const { blob, revoke, url } = imageAccess;
|
|
690
|
+
|
|
691
|
+
if (!blob) {
|
|
692
|
+
const signedResponse = await fetch(url, {
|
|
693
|
+
method: 'GET',
|
|
694
|
+
headers: {
|
|
695
|
+
'Accept': 'application/octet-stream,image/*'
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
if (!signedResponse.ok) {
|
|
700
|
+
throw new Error(`Signed URL fetch failed with status ${signedResponse.status}`);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return await signedResponse.blob();
|
|
704
|
+
}
|
|
689
705
|
|
|
690
706
|
try {
|
|
691
707
|
return blob;
|
|
@@ -6,6 +6,7 @@ import { fetchPdfApi } from '~/utils/api';
|
|
|
6
6
|
interface GeneratePDFParams {
|
|
7
7
|
user: User;
|
|
8
8
|
selectedImage: string | undefined;
|
|
9
|
+
sourceImageId?: string;
|
|
9
10
|
selectedFilename: string | undefined;
|
|
10
11
|
userCompany: string;
|
|
11
12
|
userFirstName: string;
|
|
@@ -44,6 +45,10 @@ const resolvePdfImageUrl = async (selectedImage: string | undefined): Promise<st
|
|
|
44
45
|
return selectedImage;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
if (selectedImage.startsWith('/')) {
|
|
49
|
+
return new URL(selectedImage, window.location.origin).toString();
|
|
50
|
+
}
|
|
51
|
+
|
|
47
52
|
if (selectedImage.startsWith('data:')) {
|
|
48
53
|
return selectedImage;
|
|
49
54
|
}
|
|
@@ -64,6 +69,7 @@ const resolvePdfImageUrl = async (selectedImage: string | undefined): Promise<st
|
|
|
64
69
|
export const generatePDF = async ({
|
|
65
70
|
user,
|
|
66
71
|
selectedImage,
|
|
72
|
+
sourceImageId,
|
|
67
73
|
selectedFilename,
|
|
68
74
|
userCompany,
|
|
69
75
|
userFirstName,
|
|
@@ -187,7 +193,7 @@ export const generatePDF = async ({
|
|
|
187
193
|
processingTime,
|
|
188
194
|
blob.size,
|
|
189
195
|
[],
|
|
190
|
-
|
|
196
|
+
sourceImageId, // Source file ID
|
|
191
197
|
selectedFilename // Source original filename
|
|
192
198
|
);
|
|
193
199
|
} catch (auditError) {
|
|
@@ -215,7 +221,7 @@ export const generatePDF = async ({
|
|
|
215
221
|
processingTime,
|
|
216
222
|
0, // No file size for failed generation
|
|
217
223
|
[errorText || 'PDF generation failed'],
|
|
218
|
-
|
|
224
|
+
sourceImageId, // Source file ID
|
|
219
225
|
selectedFilename // Source original filename
|
|
220
226
|
);
|
|
221
227
|
} catch (auditError) {
|
|
@@ -242,7 +248,7 @@ export const generatePDF = async ({
|
|
|
242
248
|
processingTime,
|
|
243
249
|
0, // No file size for failed generation
|
|
244
250
|
[error instanceof Error ? error.message : 'Unknown error generating PDF'],
|
|
245
|
-
|
|
251
|
+
sourceImageId, // Source file ID
|
|
246
252
|
selectedFilename // Source original filename
|
|
247
253
|
);
|
|
248
254
|
} catch (auditError) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { User } from 'firebase/auth';
|
|
2
|
-
import { fetchImageApi, uploadImageApi } from '~/utils/api';
|
|
2
|
+
import { createSignedImageUrlApi, fetchImageApi, uploadImageApi } from '~/utils/api';
|
|
3
3
|
import { canUploadFile, getCaseData, updateCaseData, deleteFileAnnotations } from '~/utils/data';
|
|
4
|
-
import type { CaseData, FileData, ImageUploadResponse } from '~/types';
|
|
4
|
+
import type { CaseData, FileData, ImageAccessResult, ImageUploadResponse } from '~/types';
|
|
5
5
|
import { auditService } from '~/services/audit';
|
|
6
6
|
|
|
7
7
|
export interface DeleteFileResult {
|
|
@@ -255,20 +255,49 @@ export const deleteFile = async (
|
|
|
255
255
|
}
|
|
256
256
|
};
|
|
257
257
|
|
|
258
|
-
export const getImageUrl = async (
|
|
258
|
+
export const getImageUrl = async (
|
|
259
|
+
user: User,
|
|
260
|
+
fileData: FileData,
|
|
261
|
+
caseNumber: string,
|
|
262
|
+
accessReason?: string
|
|
263
|
+
): Promise<ImageAccessResult> => {
|
|
259
264
|
const startTime = Date.now();
|
|
260
265
|
const defaultAccessReason = accessReason || 'Image viewer access';
|
|
261
|
-
|
|
266
|
+
|
|
262
267
|
try {
|
|
268
|
+
try {
|
|
269
|
+
const signedUrlResponse = await createSignedImageUrlApi(user, fileData.id);
|
|
270
|
+
|
|
271
|
+
await auditService.logFileAccess(
|
|
272
|
+
user,
|
|
273
|
+
fileData.originalFilename || fileData.id,
|
|
274
|
+
fileData.id,
|
|
275
|
+
'signed-url',
|
|
276
|
+
caseNumber,
|
|
277
|
+
'success',
|
|
278
|
+
Date.now() - startTime,
|
|
279
|
+
defaultAccessReason,
|
|
280
|
+
fileData.originalFilename
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
url: signedUrlResponse.result.url,
|
|
285
|
+
revoke: () => {},
|
|
286
|
+
urlType: 'signed',
|
|
287
|
+
expiresAt: signedUrlResponse.result.expiresAt
|
|
288
|
+
};
|
|
289
|
+
} catch {
|
|
290
|
+
// Fallback to direct blob retrieval during migration.
|
|
291
|
+
}
|
|
292
|
+
|
|
263
293
|
const workerResponse = await fetchImageApi(user, `/${encodeURIComponent(fileData.id)}`, {
|
|
264
294
|
method: 'GET',
|
|
265
295
|
headers: {
|
|
266
296
|
'Accept': 'application/octet-stream,image/*'
|
|
267
297
|
}
|
|
268
298
|
});
|
|
269
|
-
|
|
299
|
+
|
|
270
300
|
if (!workerResponse.ok) {
|
|
271
|
-
// Log failed image access
|
|
272
301
|
await auditService.logFileAccess(
|
|
273
302
|
user,
|
|
274
303
|
fileData.originalFilename || fileData.id,
|
|
@@ -285,8 +314,7 @@ export const getImageUrl = async (user: User, fileData: FileData, caseNumber: st
|
|
|
285
314
|
|
|
286
315
|
const blob = await workerResponse.blob();
|
|
287
316
|
const objectUrl = URL.createObjectURL(blob);
|
|
288
|
-
|
|
289
|
-
// Log successful image access
|
|
317
|
+
|
|
290
318
|
await auditService.logFileAccess(
|
|
291
319
|
user,
|
|
292
320
|
fileData.originalFilename || fileData.id,
|
|
@@ -298,10 +326,14 @@ export const getImageUrl = async (user: User, fileData: FileData, caseNumber: st
|
|
|
298
326
|
defaultAccessReason,
|
|
299
327
|
fileData.originalFilename
|
|
300
328
|
);
|
|
301
|
-
|
|
302
|
-
return {
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
blob,
|
|
332
|
+
url: objectUrl,
|
|
333
|
+
revoke: () => URL.revokeObjectURL(objectUrl),
|
|
334
|
+
urlType: 'blob'
|
|
335
|
+
};
|
|
303
336
|
} catch (error) {
|
|
304
|
-
// Log any unexpected errors if not already logged
|
|
305
337
|
if (!(error instanceof Error && error.message.includes('Failed to retrieve image'))) {
|
|
306
338
|
await auditService.logFileAccess(
|
|
307
339
|
user,
|
package/app/types/file.ts
CHANGED
|
@@ -12,8 +12,6 @@ export interface FileUploadResponse {
|
|
|
12
12
|
id: string;
|
|
13
13
|
filename: string;
|
|
14
14
|
uploaded: string;
|
|
15
|
-
requireSignedURLs: boolean;
|
|
16
|
-
variants: string[];
|
|
17
15
|
};
|
|
18
16
|
errors: Array<{
|
|
19
17
|
code: number;
|
|
@@ -27,4 +25,22 @@ export interface ImageUploadResponse {
|
|
|
27
25
|
result: FileUploadResponse['result'];
|
|
28
26
|
errors: FileUploadResponse['errors'];
|
|
29
27
|
messages: FileUploadResponse['messages'];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SignedImageUrlResponse {
|
|
31
|
+
success: boolean;
|
|
32
|
+
result: {
|
|
33
|
+
fileId: string;
|
|
34
|
+
url: string;
|
|
35
|
+
expiresAt: string;
|
|
36
|
+
expiresInSeconds: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ImageAccessResult {
|
|
41
|
+
url: string;
|
|
42
|
+
revoke: () => void;
|
|
43
|
+
blob?: Blob;
|
|
44
|
+
urlType: 'signed' | 'blob';
|
|
45
|
+
expiresAt?: string;
|
|
30
46
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { User } from 'firebase/auth';
|
|
2
|
-
import { type ImageUploadResponse } from '~/types';
|
|
2
|
+
import { type ImageUploadResponse, type SignedImageUrlResponse } from '~/types';
|
|
3
3
|
|
|
4
4
|
const IMAGE_API_BASE = '/api/image';
|
|
5
5
|
|
|
@@ -93,6 +93,54 @@ function parseUploadResponse(payload: string): ImageUploadResponse {
|
|
|
93
93
|
return parsed;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
function parseSignedUrlResponse(payload: string): SignedImageUrlResponse {
|
|
97
|
+
const parsed = JSON.parse(payload) as SignedImageUrlResponse;
|
|
98
|
+
if (!parsed.success || !parsed.result?.url || !parsed.result?.fileId || !parsed.result?.expiresAt) {
|
|
99
|
+
throw new Error('Signed URL response is invalid');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return parsed;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function createSignedImageUrlApi(
|
|
106
|
+
user: User,
|
|
107
|
+
fileId: string,
|
|
108
|
+
expiresInSeconds?: number
|
|
109
|
+
): Promise<SignedImageUrlResponse> {
|
|
110
|
+
const response = await fetchImageApi(user, `/${encodeURIComponent(fileId)}/signed-url`, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
headers: {
|
|
113
|
+
'Content-Type': 'application/json',
|
|
114
|
+
'Accept': 'application/json'
|
|
115
|
+
},
|
|
116
|
+
body: JSON.stringify(
|
|
117
|
+
typeof expiresInSeconds === 'number'
|
|
118
|
+
? { expiresInSeconds }
|
|
119
|
+
: {}
|
|
120
|
+
)
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
throw new Error(`Signed URL request failed with status ${response.status}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const parsed = parseSignedUrlResponse(await response.text());
|
|
128
|
+
const rawUrl = parsed.result.url;
|
|
129
|
+
let normalizedUrl = rawUrl;
|
|
130
|
+
|
|
131
|
+
if (rawUrl.startsWith('/')) {
|
|
132
|
+
normalizedUrl = new URL(rawUrl, window.location.origin).toString();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
...parsed,
|
|
137
|
+
result: {
|
|
138
|
+
...parsed.result,
|
|
139
|
+
url: normalizedUrl
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
96
144
|
export async function uploadImageApi(
|
|
97
145
|
user: User,
|
|
98
146
|
file: File,
|
|
@@ -42,8 +42,10 @@ export const getUserData = async (user: User): Promise<UserData | null> => {
|
|
|
42
42
|
if (response.status === 404) {
|
|
43
43
|
return null; // User not found
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
|
|
46
|
+
const responseBody = await response.text().catch(() => '');
|
|
47
|
+
const detail = responseBody ? `: ${responseBody}` : '';
|
|
48
|
+
throw new Error(`Failed to fetch user data (${response.status} ${response.statusText})${detail}`);
|
|
47
49
|
} catch (error) {
|
|
48
50
|
console.error('Error fetching user data:', error);
|
|
49
51
|
throw error;
|
|
@@ -82,12 +82,13 @@ export const onRequest = async ({ request, env }: ImageProxyContext): Promise<Re
|
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
const requestUrl = new URL(request.url);
|
|
86
|
+
|
|
85
87
|
const identity = await verifyFirebaseIdentityFromRequest(request, env);
|
|
86
88
|
if (!identity) {
|
|
87
89
|
return textResponse('Unauthorized', 401);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
const requestUrl = new URL(request.url);
|
|
91
92
|
const proxyPathResult = extractProxyPath(requestUrl);
|
|
92
93
|
if (!proxyPathResult.ok) {
|
|
93
94
|
return proxyPathResult.reason === 'bad-encoding'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@striae-org/striae",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
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",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"workers/*/src/*.example.ts",
|
|
55
55
|
"workers/*/src/*.example.js",
|
|
56
56
|
"workers/*/src/*.ts",
|
|
57
|
-
"workers/pdf-worker/scripts/*.js",
|
|
57
|
+
"workers/pdf-worker/scripts/*.js",
|
|
58
58
|
"!workers/*/src/*worker.ts",
|
|
59
59
|
"workers/pdf-worker/src/assets/generated-assets.example.ts",
|
|
60
60
|
"workers/pdf-worker/src/formats/format-striae.ts",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"vite.config.ts",
|
|
68
68
|
"worker-configuration.d.ts",
|
|
69
69
|
"wrangler.toml.example",
|
|
70
|
-
"LICENSE"
|
|
70
|
+
"LICENSE"
|
|
71
71
|
],
|
|
72
72
|
"sideEffects": false,
|
|
73
73
|
"type": "module",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"deploy-workers:image": "cd workers/image-worker && npm run deploy",
|
|
107
107
|
"deploy-workers:keys": "cd workers/keys-worker && npm run deploy",
|
|
108
108
|
"deploy-workers:pdf": "cd workers/pdf-worker && npm run deploy",
|
|
109
|
-
"deploy-workers:user": "cd workers/user-worker && npm run deploy"
|
|
109
|
+
"deploy-workers:user": "cd workers/user-worker && npm run deploy"
|
|
110
110
|
},
|
|
111
111
|
"dependencies": {
|
|
112
112
|
"@react-router/cloudflare": "^7.13.2",
|