@striae-org/striae 6.1.6 → 6.1.8
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 +1 -1
- package/README.md +1 -2
- package/app/components/navbar/case-modals/all-cases-modal.tsx +14 -12
- package/app/routes/auth/emailActionHandler.tsx +2 -12
- package/app/routes/auth/login.tsx +2 -4
- package/app/routes/striae/striae.tsx +5 -2
- package/app/utils/api/data-api-client.ts +1 -0
- package/app/utils/data/permissions.ts +4 -0
- package/package.json +14 -7
- package/scripts/deploy-config/modules/scaffolding.sh +0 -68
- package/scripts/deploy-config/modules/validation.sh +1 -12
- package/tsconfig.json +3 -0
- package/workers/audit-worker/package.json +2 -2
- package/workers/audit-worker/src/{audit-worker.example.ts → audit-worker.ts} +1 -12
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/package.json +3 -2
- package/workers/data-worker/src/{data-worker.example.ts → data-worker.ts} +1 -12
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/package.json +2 -2
- package/workers/image-worker/src/handlers/serve-image.ts +1 -3
- package/workers/image-worker/src/{image-worker.example.ts → image-worker.ts} +2 -15
- package/workers/image-worker/src/router.ts +2 -3
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/package.json +2 -2
- package/workers/pdf-worker/src/{pdf-worker.example.ts → pdf-worker.ts} +1 -15
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/package.json +2 -2
- package/workers/user-worker/src/handlers/user-routes.ts +25 -39
- package/workers/user-worker/src/{user-worker.example.ts → user-worker.ts} +14 -27
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
package/.env.example
CHANGED
|
@@ -149,4 +149,4 @@ PRIMERSHEAR_EMAILS=
|
|
|
149
149
|
# Comma-separated list of email addresses that may register an account.
|
|
150
150
|
# Leave empty to disable the feature. Never commit this value to source control.
|
|
151
151
|
# Example: REGISTRATION_EMAILS=analyst@org.com,user2@org.com
|
|
152
|
-
REGISTRATION_EMAILS=
|
|
152
|
+
REGISTRATION_EMAILS=
|
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ Included:
|
|
|
36
36
|
- `app/` source (with `app/config-example/`)
|
|
37
37
|
- `functions/`, `public/`
|
|
38
38
|
- Worker package manifests
|
|
39
|
-
- Worker source files needed by the
|
|
39
|
+
- Worker source files needed by the workers, including nested helper modules
|
|
40
40
|
- PDF worker example support files limited to `workers/pdf-worker/src/assets/generated-assets.example.ts` and `workers/pdf-worker/src/formats/format-striae.ts` (no extra PDF image assets or custom formats)
|
|
41
41
|
- Worker example Wrangler configs (`workers/*/wrangler.jsonc.example`)
|
|
42
42
|
- Project-level example and build config (`.env.example`, `wrangler.toml.example`, `tsconfig.json`, etc.)
|
|
@@ -60,6 +60,5 @@ See `LICENSE`.
|
|
|
60
60
|
|
|
61
61
|
## Support
|
|
62
62
|
|
|
63
|
-
- Striae Community: [https://community.striae.org](https://community.striae.org)
|
|
64
63
|
- Support page: [https://www.striae.org/support](https://www.striae.org/support)
|
|
65
64
|
- Contact: [info@striae.org](mailto:info@striae.org)
|
|
@@ -33,6 +33,7 @@ interface CasesModalProps {
|
|
|
33
33
|
isOpen: boolean;
|
|
34
34
|
onClose: () => void;
|
|
35
35
|
onSelectCase: (caseNum: string) => void;
|
|
36
|
+
onCurrentCaseDeleted?: () => void;
|
|
36
37
|
currentCase: string;
|
|
37
38
|
user: User;
|
|
38
39
|
confirmationSaveVersion?: number;
|
|
@@ -64,6 +65,7 @@ export const CasesModal = ({
|
|
|
64
65
|
isOpen,
|
|
65
66
|
onClose,
|
|
66
67
|
onSelectCase,
|
|
68
|
+
onCurrentCaseDeleted,
|
|
67
69
|
currentCase,
|
|
68
70
|
user,
|
|
69
71
|
confirmationSaveVersion = 0,
|
|
@@ -224,16 +226,14 @@ export const CasesModal = ({
|
|
|
224
226
|
);
|
|
225
227
|
|
|
226
228
|
const canDeleteSelectedCase = Boolean(
|
|
227
|
-
selectedCase &&
|
|
229
|
+
selectedCase && !selectedCase.isReadOnly
|
|
228
230
|
);
|
|
229
231
|
|
|
230
232
|
const deleteSelectedCaseTitle = !selectedCase
|
|
231
233
|
? 'Select a case to delete.'
|
|
232
|
-
: selectedCase.
|
|
233
|
-
? '
|
|
234
|
-
:
|
|
235
|
-
? 'Read-only review cases cannot be deleted. Use Clear RO Case under Case Management first.'
|
|
236
|
-
: undefined;
|
|
234
|
+
: selectedCase.isReadOnly
|
|
235
|
+
? 'Read-only review cases cannot be deleted here. Use Clear RO Case under Case Management first.'
|
|
236
|
+
: undefined;
|
|
237
237
|
|
|
238
238
|
const effectiveFocusedIndex = paginatedCases.length === 0 ? 0 : Math.min(focusedIndex, paginatedCases.length - 1);
|
|
239
239
|
|
|
@@ -475,16 +475,13 @@ export const CasesModal = ({
|
|
|
475
475
|
|
|
476
476
|
const handleDeleteSelectedCase = async () => {
|
|
477
477
|
if (!selectedCase || !canDeleteSelectedCase) {
|
|
478
|
-
const isCurrentCaseSelection = selectedCase?.caseNumber === currentCase;
|
|
479
478
|
const isReadOnlyReviewSelection = selectedCase?.isReadOnly === true;
|
|
480
479
|
|
|
481
480
|
setActionNotice({
|
|
482
481
|
type: 'warning',
|
|
483
|
-
message:
|
|
484
|
-
? '
|
|
485
|
-
:
|
|
486
|
-
? 'Read-only review cases cannot be deleted. Use Clear RO Case under Case Management first.'
|
|
487
|
-
: 'Selected case cannot be deleted.',
|
|
482
|
+
message: isReadOnlyReviewSelection
|
|
483
|
+
? 'Read-only review cases cannot be deleted here. Use Clear RO Case under Case Management first.'
|
|
484
|
+
: 'Selected case cannot be deleted.',
|
|
488
485
|
});
|
|
489
486
|
return;
|
|
490
487
|
}
|
|
@@ -506,11 +503,16 @@ export const CasesModal = ({
|
|
|
506
503
|
setActionNotice(null);
|
|
507
504
|
|
|
508
505
|
try {
|
|
506
|
+
const wasCurrentCase = selectedCase.caseNumber === currentCase;
|
|
509
507
|
const deleteResult = await deleteCase(user, selectedCase.caseNumber);
|
|
510
508
|
setSelectedCaseNumber(null);
|
|
511
509
|
setIsDeleteModalOpen(false);
|
|
512
510
|
setRefreshKey((k) => k + 1);
|
|
513
511
|
|
|
512
|
+
if (wasCurrentCase) {
|
|
513
|
+
onCurrentCaseDeleted?.();
|
|
514
|
+
}
|
|
515
|
+
|
|
514
516
|
if (deleteResult.missingImages.length > 0) {
|
|
515
517
|
setActionNotice({
|
|
516
518
|
type: 'warning',
|
|
@@ -20,7 +20,7 @@ interface EmailActionHandlerProps {
|
|
|
20
20
|
lang: string | null;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
type HandlerState = 'loading' | 'ready-reset' | 'success' | 'error'
|
|
23
|
+
type HandlerState = 'loading' | 'ready-reset' | 'success' | 'error';
|
|
24
24
|
|
|
25
25
|
const getUserAgent = (): string | undefined => {
|
|
26
26
|
if (typeof navigator === 'undefined') {
|
|
@@ -171,16 +171,6 @@ export const EmailActionHandler = ({ mode, oobCode, continueUrl, lang }: EmailAc
|
|
|
171
171
|
return;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
if (mode === 'recoverEmail') {
|
|
175
|
-
if (!isMounted) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
setState('unsupported');
|
|
180
|
-
setError('Email change recovery is not supported for Striae accounts.');
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
174
|
if (!isMounted) {
|
|
185
175
|
return;
|
|
186
176
|
}
|
|
@@ -378,7 +368,7 @@ export const EmailActionHandler = ({ mode, oobCode, continueUrl, lang }: EmailAc
|
|
|
378
368
|
</form>
|
|
379
369
|
)}
|
|
380
370
|
|
|
381
|
-
{(state === 'success' || state === 'error'
|
|
371
|
+
{(state === 'success' || state === 'error') && (
|
|
382
372
|
<div className={styles.actions}>
|
|
383
373
|
{showContinueButton && (
|
|
384
374
|
<button
|
|
@@ -28,9 +28,7 @@ import { generateUniqueId } from '~/utils/common';
|
|
|
28
28
|
import { evaluatePasswordPolicy, buildActionCodeSettings, userHasMFA } from '~/utils/auth';
|
|
29
29
|
import type { UserData } from '~/types';
|
|
30
30
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const SUPPORTED_EMAIL_ACTION_MODES = new Set(['resetPassword', 'verifyEmail', 'recoverEmail']);
|
|
31
|
+
const SUPPORTED_EMAIL_ACTION_MODES = new Set(['resetPassword', 'verifyEmail']);
|
|
34
32
|
|
|
35
33
|
const getUserFirstName = (user: User): string => {
|
|
36
34
|
const displayName = user.displayName?.trim();
|
|
@@ -69,7 +67,7 @@ export const Login = () => {
|
|
|
69
67
|
const [isClient, setIsClient] = useState(false);
|
|
70
68
|
const [firstName, setFirstName] = useState('');
|
|
71
69
|
const [lastName, setLastName] = useState('');
|
|
72
|
-
const [company, setCompany] = useState(
|
|
70
|
+
const [company, setCompany] = useState('');
|
|
73
71
|
const [badgeId, setBadgeId] = useState('');
|
|
74
72
|
const [confirmPasswordValue, setConfirmPasswordValue] = useState('');
|
|
75
73
|
|
|
@@ -22,7 +22,7 @@ import { fetchUserApi } from '~/utils/api';
|
|
|
22
22
|
import { type AnnotationData, type FileData, type ExportOptions } from '~/types';
|
|
23
23
|
import { validateCaseNumber, renameCase, deleteCase, checkExistingCase, createNewCase, archiveCase, deriveCaseArchiveDetails } from '~/components/actions/case-manage';
|
|
24
24
|
import { checkReadOnlyCaseExists, deleteReadOnlyCase } from '~/components/actions/case-review';
|
|
25
|
-
import { canCreateCase,
|
|
25
|
+
import { canCreateCase, ensureCaseConfirmationSummary, getCaseData, getConfirmationSummaryDocument, type UserConfirmationSummaryDocument } from '~/utils/data';
|
|
26
26
|
import {
|
|
27
27
|
resolveEarliestAnnotationTimestamp,
|
|
28
28
|
CREATE_READ_ONLY_CASE_EXISTS_ERROR,
|
|
@@ -382,7 +382,9 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
382
382
|
if (!currentCase || !user) return;
|
|
383
383
|
|
|
384
384
|
try {
|
|
385
|
-
const summary = await
|
|
385
|
+
const summary = await ensureCaseConfirmationSummary(user, currentCase, files, {
|
|
386
|
+
forceRefresh: true,
|
|
387
|
+
});
|
|
386
388
|
const filesById = summary?.filesById ?? {};
|
|
387
389
|
const values = Object.values(filesById);
|
|
388
390
|
const confirmedCount = values.filter((f) => f.includeConfirmation && f.isConfirmed).length;
|
|
@@ -955,6 +957,7 @@ export const Striae = ({ user }: StriaePage) => {
|
|
|
955
957
|
onSelectCase={(selectedCase) => {
|
|
956
958
|
void loadCaseIntoWorkspace(selectedCase);
|
|
957
959
|
}}
|
|
960
|
+
onCurrentCaseDeleted={clearLoadedCaseState}
|
|
958
961
|
currentCase={currentCase || ''}
|
|
959
962
|
user={user}
|
|
960
963
|
confirmationSaveVersion={confirmationSaveVersion}
|
|
@@ -550,6 +550,8 @@ export const addUserCase = async (user: User, caseData: CaseMetadata): Promise<v
|
|
|
550
550
|
const errorText = await response.text();
|
|
551
551
|
throw new Error(`Failed to add case to user: ${response.status} - ${errorText}`);
|
|
552
552
|
}
|
|
553
|
+
|
|
554
|
+
invalidateUserDataCache(user.uid);
|
|
553
555
|
|
|
554
556
|
} catch (error) {
|
|
555
557
|
console.error('Error adding case to user:', error);
|
|
@@ -595,6 +597,8 @@ export const removeUserCase = async (user: User, caseNumber: string): Promise<vo
|
|
|
595
597
|
const errorText = await response.text();
|
|
596
598
|
throw new Error(`Failed to remove case from user: ${response.status} - ${errorText}`);
|
|
597
599
|
}
|
|
600
|
+
|
|
601
|
+
invalidateUserDataCache(user.uid);
|
|
598
602
|
|
|
599
603
|
} catch (error) {
|
|
600
604
|
console.error('Error removing case from user:', error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@striae-org/striae",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.8",
|
|
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",
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
"!workers/*/package-lock.json",
|
|
45
45
|
"!workers/*/worker-configuration.d.ts",
|
|
46
46
|
"!workers/*/wrangler.jsonc",
|
|
47
|
-
"!workers/*/src/**/*worker.ts",
|
|
48
47
|
"!workers/pdf-worker/src/assets/**/*",
|
|
49
48
|
"workers/pdf-worker/src/assets/generated-assets.example.ts",
|
|
50
49
|
"!workers/pdf-worker/src/formats/**/*",
|
|
@@ -79,6 +78,11 @@
|
|
|
79
78
|
"typegen": "wrangler types",
|
|
80
79
|
"preview": "npm run build && wrangler pages dev",
|
|
81
80
|
"cf-typegen": "wrangler types",
|
|
81
|
+
"test": "npm run test:app && npm run test:workers:data",
|
|
82
|
+
"test:app": "vitest run --config tests/app/vitest.config.ts",
|
|
83
|
+
"test:workers:data": "vitest run --config tests/workers/data/vitest.config.mjs",
|
|
84
|
+
"test:watch": "vitest --config tests/app/vitest.config.ts",
|
|
85
|
+
"test:coverage": "vitest run --config tests/app/vitest.config.ts --coverage",
|
|
82
86
|
"enable-totp-mfa": "node ./scripts/enable-totp-mfa.mjs",
|
|
83
87
|
"unenroll-totp-mfa": "node ./scripts/unenroll-totp-mfa.mjs",
|
|
84
88
|
"update-versions": "node ./scripts/update-markdown-versions.cjs",
|
|
@@ -100,7 +104,7 @@
|
|
|
100
104
|
},
|
|
101
105
|
"dependencies": {
|
|
102
106
|
"@react-router/cloudflare": "^7.14.1",
|
|
103
|
-
"firebase": "^12.12.
|
|
107
|
+
"firebase": "^12.12.1",
|
|
104
108
|
"isbot": "^5.1.39",
|
|
105
109
|
"jszip": "^3.10.1",
|
|
106
110
|
"qrcode": "^1.5.4",
|
|
@@ -109,13 +113,15 @@
|
|
|
109
113
|
"react-router": "^7.14.1"
|
|
110
114
|
},
|
|
111
115
|
"devDependencies": {
|
|
116
|
+
"@cloudflare/vitest-pool-workers": "^0.14.8",
|
|
112
117
|
"@react-router/dev": "^7.14.1",
|
|
113
118
|
"@react-router/fs-routes": "^7.14.1",
|
|
114
119
|
"@types/qrcode": "^1.5.6",
|
|
115
120
|
"@types/react": "^19.2.14",
|
|
116
121
|
"@types/react-dom": "^19.2.3",
|
|
117
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
118
|
-
"@typescript-eslint/parser": "^8.
|
|
122
|
+
"@typescript-eslint/eslint-plugin": "^8.59.0",
|
|
123
|
+
"@typescript-eslint/parser": "^8.59.0",
|
|
124
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
119
125
|
"eslint": "^9.39.4",
|
|
120
126
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
121
127
|
"eslint-plugin-import": "^2.32.0",
|
|
@@ -125,8 +131,9 @@
|
|
|
125
131
|
"firebase-admin": "^13.8.0",
|
|
126
132
|
"modern-normalize": "^3.0.1",
|
|
127
133
|
"typescript": "^6.0.3",
|
|
128
|
-
"vite": "^8.0.
|
|
129
|
-
"
|
|
134
|
+
"vite": "^8.0.9",
|
|
135
|
+
"vitest": "^4.1.4",
|
|
136
|
+
"wrangler": "^4.84.0"
|
|
130
137
|
},
|
|
131
138
|
"overrides": {
|
|
132
139
|
"@tootallnate/once": "3.0.1"
|
|
@@ -109,44 +109,6 @@ copy_example_configs() {
|
|
|
109
109
|
# Return to project root
|
|
110
110
|
cd ../..
|
|
111
111
|
|
|
112
|
-
# Copy worker source template files
|
|
113
|
-
echo -e "${YELLOW} Copying worker source template files...${NC}"
|
|
114
|
-
|
|
115
|
-
if [ -f "workers/user-worker/src/user-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/user-worker/src/user-worker.ts" ]; }; then
|
|
116
|
-
cp workers/user-worker/src/user-worker.example.ts workers/user-worker/src/user-worker.ts
|
|
117
|
-
echo -e "${GREEN} ✅ user-worker: user-worker.ts created from example${NC}"
|
|
118
|
-
elif [ -f "workers/user-worker/src/user-worker.ts" ]; then
|
|
119
|
-
echo -e "${YELLOW} ⚠️ user-worker: user-worker.ts already exists, skipping copy${NC}"
|
|
120
|
-
fi
|
|
121
|
-
|
|
122
|
-
if [ -f "workers/data-worker/src/data-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/data-worker/src/data-worker.ts" ]; }; then
|
|
123
|
-
cp workers/data-worker/src/data-worker.example.ts workers/data-worker/src/data-worker.ts
|
|
124
|
-
echo -e "${GREEN} ✅ data-worker: data-worker.ts created from example${NC}"
|
|
125
|
-
elif [ -f "workers/data-worker/src/data-worker.ts" ]; then
|
|
126
|
-
echo -e "${YELLOW} ⚠️ data-worker: data-worker.ts already exists, skipping copy${NC}"
|
|
127
|
-
fi
|
|
128
|
-
|
|
129
|
-
if [ -f "workers/audit-worker/src/audit-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/audit-worker/src/audit-worker.ts" ]; }; then
|
|
130
|
-
cp workers/audit-worker/src/audit-worker.example.ts workers/audit-worker/src/audit-worker.ts
|
|
131
|
-
echo -e "${GREEN} ✅ audit-worker: audit-worker.ts created from example${NC}"
|
|
132
|
-
elif [ -f "workers/audit-worker/src/audit-worker.ts" ]; then
|
|
133
|
-
echo -e "${YELLOW} ⚠️ audit-worker: audit-worker.ts already exists, skipping copy${NC}"
|
|
134
|
-
fi
|
|
135
|
-
|
|
136
|
-
if [ -f "workers/image-worker/src/image-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/image-worker/src/image-worker.ts" ]; }; then
|
|
137
|
-
cp workers/image-worker/src/image-worker.example.ts workers/image-worker/src/image-worker.ts
|
|
138
|
-
echo -e "${GREEN} ✅ image-worker: image-worker.ts created from example${NC}"
|
|
139
|
-
elif [ -f "workers/image-worker/src/image-worker.ts" ]; then
|
|
140
|
-
echo -e "${YELLOW} ⚠️ image-worker: image-worker.ts already exists, skipping copy${NC}"
|
|
141
|
-
fi
|
|
142
|
-
|
|
143
|
-
if [ -f "workers/pdf-worker/src/pdf-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/pdf-worker/src/pdf-worker.ts" ]; }; then
|
|
144
|
-
cp workers/pdf-worker/src/pdf-worker.example.ts workers/pdf-worker/src/pdf-worker.ts
|
|
145
|
-
echo -e "${GREEN} ✅ pdf-worker: pdf-worker.ts created from example${NC}"
|
|
146
|
-
elif [ -f "workers/pdf-worker/src/pdf-worker.ts" ]; then
|
|
147
|
-
echo -e "${YELLOW} ⚠️ pdf-worker: pdf-worker.ts already exists, skipping copy${NC}"
|
|
148
|
-
fi
|
|
149
|
-
|
|
150
112
|
# Copy main wrangler.toml from example
|
|
151
113
|
if [ -f "wrangler.toml.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.toml" ]; }; then
|
|
152
114
|
cp wrangler.toml.example wrangler.toml
|
|
@@ -187,12 +149,6 @@ update_wrangler_configs() {
|
|
|
187
149
|
echo -e "${GREEN} ✅ audit-worker configuration updated${NC}"
|
|
188
150
|
fi
|
|
189
151
|
|
|
190
|
-
if [ -f "workers/audit-worker/src/audit-worker.ts" ]; then
|
|
191
|
-
echo -e "${YELLOW} Updating audit-worker source placeholders...${NC}"
|
|
192
|
-
sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/audit-worker/src/audit-worker.ts
|
|
193
|
-
echo -e "${GREEN} ✅ audit-worker source placeholders updated${NC}"
|
|
194
|
-
fi
|
|
195
|
-
|
|
196
152
|
if [ -f "workers/data-worker/wrangler.jsonc" ]; then
|
|
197
153
|
echo -e "${YELLOW} Updating data-worker/wrangler.jsonc...${NC}"
|
|
198
154
|
sed -i "s/\"DATA_WORKER_NAME\"/\"$DATA_WORKER_NAME\"/g" workers/data-worker/wrangler.jsonc
|
|
@@ -201,12 +157,6 @@ update_wrangler_configs() {
|
|
|
201
157
|
echo -e "${GREEN} ✅ data-worker configuration updated${NC}"
|
|
202
158
|
fi
|
|
203
159
|
|
|
204
|
-
if [ -f "workers/data-worker/src/data-worker.ts" ]; then
|
|
205
|
-
echo -e "${YELLOW} Updating data-worker source placeholders...${NC}"
|
|
206
|
-
sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/data-worker/src/data-worker.ts
|
|
207
|
-
echo -e "${GREEN} ✅ data-worker source placeholders updated${NC}"
|
|
208
|
-
fi
|
|
209
|
-
|
|
210
160
|
if [ -f "workers/image-worker/wrangler.jsonc" ]; then
|
|
211
161
|
echo -e "${YELLOW} Updating image-worker/wrangler.jsonc...${NC}"
|
|
212
162
|
sed -i "s/\"IMAGES_WORKER_NAME\"/\"$IMAGES_WORKER_NAME\"/g" workers/image-worker/wrangler.jsonc
|
|
@@ -215,12 +165,6 @@ update_wrangler_configs() {
|
|
|
215
165
|
echo -e "${GREEN} ✅ image-worker configuration updated${NC}"
|
|
216
166
|
fi
|
|
217
167
|
|
|
218
|
-
if [ -f "workers/image-worker/src/image-worker.ts" ]; then
|
|
219
|
-
echo -e "${YELLOW} Updating image-worker source placeholders...${NC}"
|
|
220
|
-
sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/image-worker/src/image-worker.ts
|
|
221
|
-
echo -e "${GREEN} ✅ image-worker source placeholders updated${NC}"
|
|
222
|
-
fi
|
|
223
|
-
|
|
224
168
|
if [ -f "workers/pdf-worker/wrangler.jsonc" ]; then
|
|
225
169
|
echo -e "${YELLOW} Updating pdf-worker/wrangler.jsonc...${NC}"
|
|
226
170
|
sed -i "s/\"PDF_WORKER_NAME\"/\"$PDF_WORKER_NAME\"/g" workers/pdf-worker/wrangler.jsonc
|
|
@@ -228,12 +172,6 @@ update_wrangler_configs() {
|
|
|
228
172
|
echo -e "${GREEN} ✅ pdf-worker configuration updated${NC}"
|
|
229
173
|
fi
|
|
230
174
|
|
|
231
|
-
if [ -f "workers/pdf-worker/src/pdf-worker.ts" ]; then
|
|
232
|
-
echo -e "${YELLOW} Updating pdf-worker source placeholders...${NC}"
|
|
233
|
-
sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/pdf-worker/src/pdf-worker.ts
|
|
234
|
-
echo -e "${GREEN} ✅ pdf-worker source placeholders updated${NC}"
|
|
235
|
-
fi
|
|
236
|
-
|
|
237
175
|
if [ -f "workers/user-worker/wrangler.jsonc" ]; then
|
|
238
176
|
echo -e "${YELLOW} Updating user-worker/wrangler.jsonc...${NC}"
|
|
239
177
|
sed -i "s/\"USER_WORKER_NAME\"/\"$USER_WORKER_NAME\"/g" workers/user-worker/wrangler.jsonc
|
|
@@ -244,12 +182,6 @@ update_wrangler_configs() {
|
|
|
244
182
|
echo -e "${GREEN} ✅ user-worker configuration updated${NC}"
|
|
245
183
|
fi
|
|
246
184
|
|
|
247
|
-
if [ -f "workers/user-worker/src/user-worker.ts" ]; then
|
|
248
|
-
echo -e "${YELLOW} Updating user-worker source placeholders...${NC}"
|
|
249
|
-
sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/user-worker/src/user-worker.ts
|
|
250
|
-
echo -e "${GREEN} ✅ user-worker source placeholders updated${NC}"
|
|
251
|
-
fi
|
|
252
|
-
|
|
253
185
|
if [ -f "wrangler.toml" ]; then
|
|
254
186
|
echo -e "${YELLOW} Updating wrangler.toml...${NC}"
|
|
255
187
|
sed -i "s/\"PAGES_PROJECT_NAME\"/\"$PAGES_PROJECT_NAME\"/g" wrangler.toml
|
|
@@ -312,14 +312,8 @@ validate_generated_configs() {
|
|
|
312
312
|
assert_contains_literal "app/config/firebase.ts" "$APP_ID" "APP_ID missing in app/config/firebase.ts"
|
|
313
313
|
assert_contains_literal "app/config/firebase.ts" "$MEASUREMENT_ID" "MEASUREMENT_ID missing in app/config/firebase.ts"
|
|
314
314
|
|
|
315
|
-
assert_contains_literal "workers/audit-worker/src/audit-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in audit-worker source"
|
|
316
|
-
assert_contains_literal "workers/data-worker/src/data-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in data-worker source"
|
|
317
|
-
assert_contains_literal "workers/image-worker/src/image-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in image-worker source"
|
|
318
|
-
assert_contains_literal "workers/pdf-worker/src/pdf-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in pdf-worker source"
|
|
319
|
-
assert_contains_literal "workers/user-worker/src/user-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in user-worker source"
|
|
320
|
-
|
|
321
315
|
local placeholder_pattern
|
|
322
|
-
placeholder_pattern="(\"(ACCOUNT_ID|PAGES_PROJECT_NAME|PAGES_CUSTOM_DOMAIN|USER_WORKER_NAME|DATA_WORKER_NAME|AUDIT_WORKER_NAME|IMAGES_WORKER_NAME|PDF_WORKER_NAME|USER_WORKER_DOMAIN|DATA_WORKER_DOMAIN|AUDIT_WORKER_DOMAIN|IMAGES_WORKER_DOMAIN|PDF_WORKER_DOMAIN|DATA_BUCKET_NAME|AUDIT_BUCKET_NAME|FILES_BUCKET_NAME|KV_STORE_ID|MANIFEST_SIGNING_KEY_ID|MANIFEST_SIGNING_PUBLIC_KEY|EXPORT_ENCRYPTION_KEY_ID|EXPORT_ENCRYPTION_PUBLIC_KEY|YOUR_FIREBASE_API_KEY|YOUR_FIREBASE_AUTH_DOMAIN|YOUR_FIREBASE_PROJECT_ID|YOUR_FIREBASE_STORAGE_BUCKET|YOUR_FIREBASE_MESSAGING_SENDER_ID|YOUR_FIREBASE_APP_ID|YOUR_FIREBASE_MEASUREMENT_ID)\"
|
|
316
|
+
placeholder_pattern="(\"(ACCOUNT_ID|PAGES_PROJECT_NAME|PAGES_CUSTOM_DOMAIN|USER_WORKER_NAME|DATA_WORKER_NAME|AUDIT_WORKER_NAME|IMAGES_WORKER_NAME|PDF_WORKER_NAME|USER_WORKER_DOMAIN|DATA_WORKER_DOMAIN|AUDIT_WORKER_DOMAIN|IMAGES_WORKER_DOMAIN|PDF_WORKER_DOMAIN|DATA_BUCKET_NAME|AUDIT_BUCKET_NAME|FILES_BUCKET_NAME|KV_STORE_ID|MANIFEST_SIGNING_KEY_ID|MANIFEST_SIGNING_PUBLIC_KEY|EXPORT_ENCRYPTION_KEY_ID|EXPORT_ENCRYPTION_PUBLIC_KEY|YOUR_FIREBASE_API_KEY|YOUR_FIREBASE_AUTH_DOMAIN|YOUR_FIREBASE_PROJECT_ID|YOUR_FIREBASE_STORAGE_BUCKET|YOUR_FIREBASE_MESSAGING_SENDER_ID|YOUR_FIREBASE_APP_ID|YOUR_FIREBASE_MEASUREMENT_ID)\")"
|
|
323
317
|
|
|
324
318
|
local files_to_scan=(
|
|
325
319
|
"wrangler.toml"
|
|
@@ -328,11 +322,6 @@ validate_generated_configs() {
|
|
|
328
322
|
"workers/image-worker/wrangler.jsonc"
|
|
329
323
|
"workers/pdf-worker/wrangler.jsonc"
|
|
330
324
|
"workers/user-worker/wrangler.jsonc"
|
|
331
|
-
"workers/audit-worker/src/audit-worker.ts"
|
|
332
|
-
"workers/data-worker/src/data-worker.ts"
|
|
333
|
-
"workers/image-worker/src/image-worker.ts"
|
|
334
|
-
"workers/pdf-worker/src/pdf-worker.ts"
|
|
335
|
-
"workers/user-worker/src/user-worker.ts"
|
|
336
325
|
"app/config/config.json"
|
|
337
326
|
"app/config/firebase.ts"
|
|
338
327
|
)
|
package/tsconfig.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "audit-worker",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.8",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"deploy": "wrangler deploy",
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"start": "wrangler dev"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
|
-
"wrangler": "^4.
|
|
11
|
+
"wrangler": "^4.84.0"
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -2,24 +2,13 @@ import { hasValidHeader } from './config';
|
|
|
2
2
|
import { handleAuditRequest } from './handlers/audit-routes';
|
|
3
3
|
import type { CreateResponse, Env } from './types';
|
|
4
4
|
|
|
5
|
-
const corsHeaders: Record<string, string> = {
|
|
6
|
-
'Access-Control-Allow-Origin': 'PAGES_CUSTOM_DOMAIN',
|
|
7
|
-
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
8
|
-
'Access-Control-Allow-Headers': 'Content-Type, X-Custom-Auth-Key',
|
|
9
|
-
'Content-Type': 'application/json'
|
|
10
|
-
};
|
|
11
|
-
|
|
12
5
|
const createWorkerResponse: CreateResponse = (data, status: number = 200): Response => new Response(
|
|
13
6
|
JSON.stringify(data),
|
|
14
|
-
{ status, headers:
|
|
7
|
+
{ status, headers: { 'Content-Type': 'application/json' } }
|
|
15
8
|
);
|
|
16
9
|
|
|
17
10
|
export default {
|
|
18
11
|
async fetch(request: Request, env: Env): Promise<Response> {
|
|
19
|
-
if (request.method === 'OPTIONS') {
|
|
20
|
-
return new Response(null, { headers: corsHeaders });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
12
|
if (!hasValidHeader(request, env)) {
|
|
24
13
|
return createWorkerResponse({ error: 'Forbidden' }, 403);
|
|
25
14
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "data-worker",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.8",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"deploy": "wrangler deploy",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"start": "wrangler dev"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
|
-
"
|
|
11
|
+
"@cloudflare/vitest-pool-workers": "^0.14.8",
|
|
12
|
+
"wrangler": "^4.84.0"
|
|
12
13
|
}
|
|
13
14
|
}
|
|
@@ -14,24 +14,13 @@ import {
|
|
|
14
14
|
import { handleStorageRequest } from './handlers/storage-routes';
|
|
15
15
|
import type { CreateResponse, Env } from './types';
|
|
16
16
|
|
|
17
|
-
const corsHeaders: Record<string, string> = {
|
|
18
|
-
'Access-Control-Allow-Origin': 'PAGES_CUSTOM_DOMAIN',
|
|
19
|
-
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
20
|
-
'Access-Control-Allow-Headers': 'Content-Type, X-Custom-Auth-Key',
|
|
21
|
-
'Content-Type': 'application/json'
|
|
22
|
-
};
|
|
23
|
-
|
|
24
17
|
const createWorkerResponse: CreateResponse = (data, status: number = 200): Response => new Response(
|
|
25
18
|
JSON.stringify(data),
|
|
26
|
-
{ status, headers:
|
|
19
|
+
{ status, headers: { 'Content-Type': 'application/json' } }
|
|
27
20
|
);
|
|
28
21
|
|
|
29
22
|
export default {
|
|
30
23
|
async fetch(request: Request, env: Env): Promise<Response> {
|
|
31
|
-
if (request.method === 'OPTIONS') {
|
|
32
|
-
return new Response(null, { headers: corsHeaders });
|
|
33
|
-
}
|
|
34
|
-
|
|
35
24
|
if (!hasValidHeader(request, env)) {
|
|
36
25
|
return createWorkerResponse({ error: 'Forbidden' }, 403);
|
|
37
26
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "image-worker",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.8",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"deploy": "wrangler deploy",
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"start": "wrangler dev"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
|
-
"wrangler": "^4.
|
|
11
|
+
"wrangler": "^4.84.0"
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -12,8 +12,7 @@ export async function handleImageServing(
|
|
|
12
12
|
request: Request,
|
|
13
13
|
env: Env,
|
|
14
14
|
fileId: string,
|
|
15
|
-
createJsonResponse: CreateImageWorkerResponse
|
|
16
|
-
corsHeaders: Record<string, string>
|
|
15
|
+
createJsonResponse: CreateImageWorkerResponse
|
|
17
16
|
): Promise<Response> {
|
|
18
17
|
const requestUrl = new URL(request.url);
|
|
19
18
|
const hasSignedToken = requestUrl.searchParams.has('st');
|
|
@@ -56,7 +55,6 @@ export async function handleImageServing(
|
|
|
56
55
|
return new Response(plaintext, {
|
|
57
56
|
status: 200,
|
|
58
57
|
headers: {
|
|
59
|
-
...corsHeaders,
|
|
60
58
|
'Cache-Control': 'no-store',
|
|
61
59
|
'Content-Type': contentType,
|
|
62
60
|
'Content-Disposition': contentDisposition
|
|
@@ -1,31 +1,18 @@
|
|
|
1
1
|
import { routeImageWorkerRequest } from './router';
|
|
2
2
|
import type { APIResponse, Env } from './types';
|
|
3
3
|
|
|
4
|
-
const corsHeaders: Record<string, string> = {
|
|
5
|
-
'Access-Control-Allow-Origin': 'PAGES_CUSTOM_DOMAIN',
|
|
6
|
-
'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS',
|
|
7
|
-
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Custom-Auth-Key'
|
|
8
|
-
};
|
|
9
|
-
|
|
10
4
|
const createJsonResponse = (data: APIResponse, status: number = 200): Response => new Response(
|
|
11
5
|
JSON.stringify(data),
|
|
12
6
|
{
|
|
13
7
|
status,
|
|
14
|
-
headers: {
|
|
15
|
-
...corsHeaders,
|
|
16
|
-
'Content-Type': 'application/json'
|
|
17
|
-
}
|
|
8
|
+
headers: { 'Content-Type': 'application/json' }
|
|
18
9
|
}
|
|
19
10
|
);
|
|
20
11
|
|
|
21
12
|
export default {
|
|
22
13
|
async fetch(request: Request, env: Env): Promise<Response> {
|
|
23
|
-
if (request.method === 'OPTIONS') {
|
|
24
|
-
return new Response(null, { headers: corsHeaders });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
14
|
try {
|
|
28
|
-
return await routeImageWorkerRequest(request, env, createJsonResponse
|
|
15
|
+
return await routeImageWorkerRequest(request, env, createJsonResponse);
|
|
29
16
|
} catch (error) {
|
|
30
17
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
31
18
|
return createJsonResponse({ error: errorMessage }, 500);
|
|
@@ -8,8 +8,7 @@ import { parsePathSegments } from './utils/path-utils';
|
|
|
8
8
|
export async function routeImageWorkerRequest(
|
|
9
9
|
request: Request,
|
|
10
10
|
env: Env,
|
|
11
|
-
createJsonResponse: CreateImageWorkerResponse
|
|
12
|
-
corsHeaders: Record<string, string>
|
|
11
|
+
createJsonResponse: CreateImageWorkerResponse
|
|
13
12
|
): Promise<Response> {
|
|
14
13
|
const requestUrl = new URL(request.url);
|
|
15
14
|
const pathSegments = parsePathSegments(requestUrl.pathname);
|
|
@@ -36,7 +35,7 @@ export async function routeImageWorkerRequest(
|
|
|
36
35
|
return createJsonResponse({ error: 'Image ID is required' }, 400);
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
return handleImageServing(request, env, fileId, createJsonResponse
|
|
38
|
+
return handleImageServing(request, env, fileId, createJsonResponse);
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
case 'DELETE': {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-worker",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.8",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"generate:assets": "node scripts/generate-assets.js",
|
|
@@ -9,6 +9,6 @@
|
|
|
9
9
|
"start": "wrangler dev"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"wrangler": "^4.
|
|
12
|
+
"wrangler": "^4.84.0"
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -40,12 +40,6 @@ const reportModuleLoaders: Record<string, () => Promise<ReportModule>> = {
|
|
|
40
40
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
const corsHeaders: Record<string, string> = {
|
|
44
|
-
'Access-Control-Allow-Origin': 'PAGES_CUSTOM_DOMAIN',
|
|
45
|
-
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
46
|
-
'Access-Control-Allow-Headers': 'Content-Type, X-Custom-Auth-Key',
|
|
47
|
-
};
|
|
48
|
-
|
|
49
43
|
const hasValidHeader = (request: Request, env: Env): boolean =>
|
|
50
44
|
request.headers.get('X-Custom-Auth-Key') === env.PDF_WORKER_AUTH;
|
|
51
45
|
|
|
@@ -60,7 +54,7 @@ function isTimeoutError(error: unknown): boolean {
|
|
|
60
54
|
function jsonResponse(body: unknown, status: number): Response {
|
|
61
55
|
return new Response(JSON.stringify(body), {
|
|
62
56
|
status,
|
|
63
|
-
headers: {
|
|
57
|
+
headers: { 'content-type': 'application/json' },
|
|
64
58
|
});
|
|
65
59
|
}
|
|
66
60
|
|
|
@@ -190,10 +184,6 @@ async function renderPdfViaRestEndpoint(env: Env, html: string, pdfOptions: Repo
|
|
|
190
184
|
responseHeaders.set('cache-control', 'no-store');
|
|
191
185
|
}
|
|
192
186
|
|
|
193
|
-
for (const [headerName, headerValue] of Object.entries(corsHeaders)) {
|
|
194
|
-
responseHeaders.set(headerName, headerValue);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
187
|
return new Response(endpointResponse.body, {
|
|
198
188
|
status: endpointResponse.status,
|
|
199
189
|
statusText: endpointResponse.statusText,
|
|
@@ -203,10 +193,6 @@ async function renderPdfViaRestEndpoint(env: Env, html: string, pdfOptions: Repo
|
|
|
203
193
|
|
|
204
194
|
export default {
|
|
205
195
|
async fetch(request: Request, env: Env): Promise<Response> {
|
|
206
|
-
if (request.method === 'OPTIONS') {
|
|
207
|
-
return new Response(null, { headers: corsHeaders });
|
|
208
|
-
}
|
|
209
|
-
|
|
210
196
|
if (!hasValidHeader(request, env)) {
|
|
211
197
|
return jsonResponse({ error: 'Forbidden' }, 403);
|
|
212
198
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "user-worker",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.8",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"deploy": "wrangler deploy",
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"start": "wrangler dev"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
|
-
"wrangler": "^4.
|
|
11
|
+
"wrangler": "^4.84.0"
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -5,56 +5,47 @@ import type {
|
|
|
5
5
|
AccountDeletionProgressEvent,
|
|
6
6
|
DeleteCasesRequest,
|
|
7
7
|
Env,
|
|
8
|
-
ResponseHeaders,
|
|
9
8
|
UserData,
|
|
10
9
|
UserRequestData
|
|
11
10
|
} from '../types';
|
|
12
11
|
|
|
13
|
-
function createJsonResponse(data: unknown,
|
|
12
|
+
function createJsonResponse(data: unknown, status: number = 200): Response {
|
|
14
13
|
return new Response(JSON.stringify(data), {
|
|
15
14
|
status,
|
|
16
|
-
headers: {
|
|
17
|
-
...headers,
|
|
18
|
-
'Content-Type': 'application/json; charset=utf-8'
|
|
19
|
-
}
|
|
15
|
+
headers: { 'Content-Type': 'application/json; charset=utf-8' }
|
|
20
16
|
});
|
|
21
17
|
}
|
|
22
18
|
|
|
23
|
-
function createTextResponse(message: string,
|
|
19
|
+
function createTextResponse(message: string, status: number): Response {
|
|
24
20
|
return new Response(message, {
|
|
25
21
|
status,
|
|
26
|
-
headers: {
|
|
27
|
-
...headers,
|
|
28
|
-
'Content-Type': 'text/plain; charset=utf-8'
|
|
29
|
-
}
|
|
22
|
+
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
|
|
30
23
|
});
|
|
31
24
|
}
|
|
32
25
|
|
|
33
26
|
export async function handleGetUser(
|
|
34
27
|
env: Env,
|
|
35
|
-
userUid: string
|
|
36
|
-
corsHeaders: ResponseHeaders
|
|
28
|
+
userUid: string
|
|
37
29
|
): Promise<Response> {
|
|
38
30
|
try {
|
|
39
31
|
const userData = await readUserRecord(env, userUid);
|
|
40
32
|
if (userData === null) {
|
|
41
|
-
return createTextResponse('User not found',
|
|
33
|
+
return createTextResponse('User not found', 404);
|
|
42
34
|
}
|
|
43
35
|
|
|
44
|
-
return createJsonResponse(userData
|
|
36
|
+
return createJsonResponse(userData);
|
|
45
37
|
} catch (error) {
|
|
46
38
|
const errorMessage = error instanceof Error ? error.message : 'Unknown user data read error';
|
|
47
39
|
console.error('Failed to get user data:', { uid: userUid, reason: errorMessage });
|
|
48
40
|
|
|
49
|
-
return createTextResponse('Failed to get user data',
|
|
41
|
+
return createTextResponse('Failed to get user data', 500);
|
|
50
42
|
}
|
|
51
43
|
}
|
|
52
44
|
|
|
53
45
|
export async function handleAddUser(
|
|
54
46
|
request: Request,
|
|
55
47
|
env: Env,
|
|
56
|
-
userUid: string
|
|
57
|
-
corsHeaders: ResponseHeaders
|
|
48
|
+
userUid: string
|
|
58
49
|
): Promise<Response> {
|
|
59
50
|
try {
|
|
60
51
|
const requestData: UserRequestData = await request.json();
|
|
@@ -96,16 +87,15 @@ export async function handleAddUser(
|
|
|
96
87
|
|
|
97
88
|
await writeUserRecord(env, userUid, userData);
|
|
98
89
|
|
|
99
|
-
return createJsonResponse(userData,
|
|
90
|
+
return createJsonResponse(userData, existingUser !== null ? 200 : 201);
|
|
100
91
|
} catch {
|
|
101
|
-
return createTextResponse('Failed to save user data',
|
|
92
|
+
return createTextResponse('Failed to save user data', 500);
|
|
102
93
|
}
|
|
103
94
|
}
|
|
104
95
|
|
|
105
96
|
export async function handleDeleteUser(
|
|
106
97
|
env: Env,
|
|
107
|
-
userUid: string
|
|
108
|
-
corsHeaders: ResponseHeaders
|
|
98
|
+
userUid: string
|
|
109
99
|
): Promise<Response> {
|
|
110
100
|
try {
|
|
111
101
|
const result = await executeUserDeletion(env, userUid);
|
|
@@ -113,29 +103,27 @@ export async function handleDeleteUser(
|
|
|
113
103
|
return createJsonResponse({
|
|
114
104
|
success: result.success,
|
|
115
105
|
message: result.message
|
|
116
|
-
}
|
|
106
|
+
});
|
|
117
107
|
} catch (error) {
|
|
118
108
|
console.error('Delete user error:', error);
|
|
119
109
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
120
110
|
|
|
121
111
|
if (errorMessage === 'User not found') {
|
|
122
|
-
return createTextResponse('User not found',
|
|
112
|
+
return createTextResponse('User not found', 404);
|
|
123
113
|
}
|
|
124
114
|
|
|
125
115
|
return createJsonResponse({
|
|
126
116
|
success: false,
|
|
127
117
|
message: 'Failed to delete user account'
|
|
128
|
-
},
|
|
118
|
+
}, 500);
|
|
129
119
|
}
|
|
130
120
|
}
|
|
131
121
|
|
|
132
122
|
export function handleDeleteUserWithProgress(
|
|
133
123
|
env: Env,
|
|
134
|
-
userUid: string
|
|
135
|
-
corsHeaders: ResponseHeaders
|
|
124
|
+
userUid: string
|
|
136
125
|
): Response {
|
|
137
|
-
const sseHeaders
|
|
138
|
-
...corsHeaders,
|
|
126
|
+
const sseHeaders = {
|
|
139
127
|
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
140
128
|
'Cache-Control': 'no-cache, no-transform',
|
|
141
129
|
Connection: 'keep-alive'
|
|
@@ -182,14 +170,13 @@ export function handleDeleteUserWithProgress(
|
|
|
182
170
|
export async function handleAddCases(
|
|
183
171
|
request: Request,
|
|
184
172
|
env: Env,
|
|
185
|
-
userUid: string
|
|
186
|
-
corsHeaders: ResponseHeaders
|
|
173
|
+
userUid: string
|
|
187
174
|
): Promise<Response> {
|
|
188
175
|
try {
|
|
189
176
|
const { cases = [] }: AddCasesRequest = await request.json();
|
|
190
177
|
const userData = await readUserRecord(env, userUid);
|
|
191
178
|
if (!userData) {
|
|
192
|
-
return createTextResponse('User not found',
|
|
179
|
+
return createTextResponse('User not found', 404);
|
|
193
180
|
}
|
|
194
181
|
|
|
195
182
|
const existingCases = userData.cases || [];
|
|
@@ -201,31 +188,30 @@ export async function handleAddCases(
|
|
|
201
188
|
userData.updatedAt = new Date().toISOString();
|
|
202
189
|
await writeUserRecord(env, userUid, userData);
|
|
203
190
|
|
|
204
|
-
return createJsonResponse(userData
|
|
191
|
+
return createJsonResponse(userData);
|
|
205
192
|
} catch {
|
|
206
|
-
return createTextResponse('Failed to add cases',
|
|
193
|
+
return createTextResponse('Failed to add cases', 500);
|
|
207
194
|
}
|
|
208
195
|
}
|
|
209
196
|
|
|
210
197
|
export async function handleDeleteCases(
|
|
211
198
|
request: Request,
|
|
212
199
|
env: Env,
|
|
213
|
-
userUid: string
|
|
214
|
-
corsHeaders: ResponseHeaders
|
|
200
|
+
userUid: string
|
|
215
201
|
): Promise<Response> {
|
|
216
202
|
try {
|
|
217
203
|
const { casesToDelete }: DeleteCasesRequest = await request.json();
|
|
218
204
|
const userData = await readUserRecord(env, userUid);
|
|
219
205
|
if (!userData) {
|
|
220
|
-
return createTextResponse('User not found',
|
|
206
|
+
return createTextResponse('User not found', 404);
|
|
221
207
|
}
|
|
222
208
|
|
|
223
209
|
userData.cases = userData.cases.filter((caseItem) => !casesToDelete.includes(caseItem.caseNumber));
|
|
224
210
|
userData.updatedAt = new Date().toISOString();
|
|
225
211
|
await writeUserRecord(env, userUid, userData);
|
|
226
212
|
|
|
227
|
-
return createJsonResponse(userData
|
|
213
|
+
return createJsonResponse(userData);
|
|
228
214
|
} catch {
|
|
229
|
-
return createTextResponse('Failed to delete cases',
|
|
215
|
+
return createTextResponse('Failed to delete cases', 500);
|
|
230
216
|
}
|
|
231
217
|
}
|
|
@@ -10,28 +10,15 @@ import {
|
|
|
10
10
|
} from './handlers/user-routes';
|
|
11
11
|
import type { Env } from './types';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
'Access-Control-Allow-Origin': 'PAGES_CUSTOM_DOMAIN',
|
|
15
|
-
'Access-Control-Allow-Methods': 'GET, PUT, DELETE, OPTIONS',
|
|
16
|
-
'Access-Control-Allow-Headers': 'Content-Type, X-Custom-Auth-Key'
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
function createTextResponse(message: string, status: number, headers: Record<string, string>): Response {
|
|
13
|
+
function createTextResponse(message: string, status: number): Response {
|
|
20
14
|
return new Response(message, {
|
|
21
15
|
status,
|
|
22
|
-
headers: {
|
|
23
|
-
...headers,
|
|
24
|
-
'Content-Type': 'text/plain; charset=utf-8'
|
|
25
|
-
}
|
|
16
|
+
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
|
|
26
17
|
});
|
|
27
18
|
}
|
|
28
19
|
|
|
29
20
|
export default {
|
|
30
21
|
async fetch(request: Request, env: Env): Promise<Response> {
|
|
31
|
-
if (request.method === 'OPTIONS') {
|
|
32
|
-
return new Response(null, { headers: corsHeaders });
|
|
33
|
-
}
|
|
34
|
-
|
|
35
22
|
try {
|
|
36
23
|
await authenticate(request, env);
|
|
37
24
|
|
|
@@ -48,15 +35,15 @@ export default {
|
|
|
48
35
|
const isCasesEndpoint = parts[2] === USER_CASES_SEGMENT;
|
|
49
36
|
|
|
50
37
|
if (!userUid) {
|
|
51
|
-
return createTextResponse('Not Found', 404
|
|
38
|
+
return createTextResponse('Not Found', 404);
|
|
52
39
|
}
|
|
53
40
|
|
|
54
41
|
// Handle regular cases endpoint
|
|
55
42
|
if (isCasesEndpoint) {
|
|
56
43
|
switch (request.method) {
|
|
57
|
-
case 'PUT': return handleAddCases(request, env, userUid
|
|
58
|
-
case 'DELETE': return handleDeleteCases(request, env, userUid
|
|
59
|
-
default: return createTextResponse('Method not allowed', 405
|
|
44
|
+
case 'PUT': return handleAddCases(request, env, userUid);
|
|
45
|
+
case 'DELETE': return handleDeleteCases(request, env, userUid);
|
|
46
|
+
default: return createTextResponse('Method not allowed', 405);
|
|
60
47
|
}
|
|
61
48
|
}
|
|
62
49
|
|
|
@@ -65,24 +52,24 @@ export default {
|
|
|
65
52
|
const streamProgress = url.searchParams.get('stream') === 'true' || acceptsEventStream;
|
|
66
53
|
|
|
67
54
|
switch (request.method) {
|
|
68
|
-
case 'GET': return handleGetUser(env, userUid
|
|
69
|
-
case 'PUT': return handleAddUser(request, env, userUid
|
|
55
|
+
case 'GET': return handleGetUser(env, userUid);
|
|
56
|
+
case 'PUT': return handleAddUser(request, env, userUid);
|
|
70
57
|
case 'DELETE': return streamProgress
|
|
71
|
-
? handleDeleteUserWithProgress(env, userUid
|
|
72
|
-
: handleDeleteUser(env, userUid
|
|
73
|
-
default: return createTextResponse('Method not allowed', 405
|
|
58
|
+
? handleDeleteUserWithProgress(env, userUid)
|
|
59
|
+
: handleDeleteUser(env, userUid);
|
|
60
|
+
default: return createTextResponse('Method not allowed', 405);
|
|
74
61
|
}
|
|
75
62
|
} catch (error) {
|
|
76
63
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
77
64
|
if (errorMessage === 'Unauthorized') {
|
|
78
|
-
return createTextResponse('Forbidden', 403
|
|
65
|
+
return createTextResponse('Forbidden', 403);
|
|
79
66
|
}
|
|
80
67
|
|
|
81
68
|
if (errorMessage === 'User KV encryption is not fully configured') {
|
|
82
|
-
return createTextResponse(errorMessage, 500
|
|
69
|
+
return createTextResponse(errorMessage, 500);
|
|
83
70
|
}
|
|
84
71
|
|
|
85
|
-
return createTextResponse('Internal Server Error', 500
|
|
72
|
+
return createTextResponse('Internal Server Error', 500);
|
|
86
73
|
}
|
|
87
74
|
}
|
|
88
75
|
};
|
package/wrangler.toml.example
CHANGED