@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.
Files changed (31) hide show
  1. package/.env.example +1 -1
  2. package/README.md +1 -2
  3. package/app/components/navbar/case-modals/all-cases-modal.tsx +14 -12
  4. package/app/routes/auth/emailActionHandler.tsx +2 -12
  5. package/app/routes/auth/login.tsx +2 -4
  6. package/app/routes/striae/striae.tsx +5 -2
  7. package/app/utils/api/data-api-client.ts +1 -0
  8. package/app/utils/data/permissions.ts +4 -0
  9. package/package.json +14 -7
  10. package/scripts/deploy-config/modules/scaffolding.sh +0 -68
  11. package/scripts/deploy-config/modules/validation.sh +1 -12
  12. package/tsconfig.json +3 -0
  13. package/workers/audit-worker/package.json +2 -2
  14. package/workers/audit-worker/src/{audit-worker.example.ts → audit-worker.ts} +1 -12
  15. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  16. package/workers/data-worker/package.json +3 -2
  17. package/workers/data-worker/src/{data-worker.example.ts → data-worker.ts} +1 -12
  18. package/workers/data-worker/wrangler.jsonc.example +1 -1
  19. package/workers/image-worker/package.json +2 -2
  20. package/workers/image-worker/src/handlers/serve-image.ts +1 -3
  21. package/workers/image-worker/src/{image-worker.example.ts → image-worker.ts} +2 -15
  22. package/workers/image-worker/src/router.ts +2 -3
  23. package/workers/image-worker/wrangler.jsonc.example +1 -1
  24. package/workers/pdf-worker/package.json +2 -2
  25. package/workers/pdf-worker/src/{pdf-worker.example.ts → pdf-worker.ts} +1 -15
  26. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  27. package/workers/user-worker/package.json +2 -2
  28. package/workers/user-worker/src/handlers/user-routes.ts +25 -39
  29. package/workers/user-worker/src/{user-worker.example.ts → user-worker.ts} +14 -27
  30. package/workers/user-worker/wrangler.jsonc.example +1 -1
  31. 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 example workers, including nested helper modules, while excluding production worker entry files (`workers/*/src/**/*.ts` excluding `workers/*/src/**/*worker.ts`)
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 && selectedCase.caseNumber !== currentCase && !selectedCase.isReadOnly
229
+ selectedCase && !selectedCase.isReadOnly
228
230
  );
229
231
 
230
232
  const deleteSelectedCaseTitle = !selectedCase
231
233
  ? 'Select a case to delete.'
232
- : selectedCase.caseNumber === currentCase
233
- ? 'Open a different case before deleting this one.'
234
- : selectedCase.isReadOnly
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: isCurrentCaseSelection
484
- ? 'Open a different case before deleting this one.'
485
- : isReadOnlyReviewSelection
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' | 'unsupported';
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' || state === 'unsupported') && (
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 DEMO_COMPANY_NAME = 'STRIAE DEMO';
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(DEMO_COMPANY_NAME);
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, getCaseConfirmationSummary, getCaseData, getConfirmationSummaryDocument, type UserConfirmationSummaryDocument } from '~/utils/data';
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 getCaseConfirmationSummary(user, currentCase);
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}
@@ -38,6 +38,7 @@ export async function fetchDataApi(
38
38
 
39
39
  return fetch(`${DATA_API_BASE}${normalizedPath}`, {
40
40
  ...init,
41
+ cache: 'no-store',
41
42
  headers
42
43
  });
43
44
  }
@@ -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.6",
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.0",
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.58.2",
118
- "@typescript-eslint/parser": "^8.58.2",
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.8",
129
- "wrangler": "^4.83.0"
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)\"|'(PAGES_CUSTOM_DOMAIN|DATA_WORKER_DOMAIN|AUDIT_WORKER_DOMAIN|IMAGES_WORKER_DOMAIN)')"
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
@@ -8,6 +8,9 @@
8
8
  "**/.client/**/*.tsx",
9
9
  ".react-router/types/**/*"
10
10
  ],
11
+ "exclude": [
12
+ "tests/e2e"
13
+ ],
11
14
  "compilerOptions": {
12
15
  "lib": [
13
16
  "DOM",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "audit-worker",
3
- "version": "6.1.6",
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.83.0"
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: corsHeaders }
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
  }
@@ -7,7 +7,7 @@
7
7
  "name": "AUDIT_WORKER_NAME",
8
8
  "account_id": "ACCOUNT_ID",
9
9
  "main": "src/audit-worker.ts",
10
- "compatibility_date": "2026-04-19",
10
+ "compatibility_date": "2026-04-20",
11
11
  "compatibility_flags": [
12
12
  "nodejs_compat"
13
13
  ],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "data-worker",
3
- "version": "6.1.6",
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
- "wrangler": "^4.83.0"
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: corsHeaders }
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
  }
@@ -5,7 +5,7 @@
5
5
  "name": "DATA_WORKER_NAME",
6
6
  "account_id": "ACCOUNT_ID",
7
7
  "main": "src/data-worker.ts",
8
- "compatibility_date": "2026-04-19",
8
+ "compatibility_date": "2026-04-20",
9
9
  "compatibility_flags": [
10
10
  "nodejs_compat"
11
11
  ],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "image-worker",
3
- "version": "6.1.6",
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.83.0"
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, corsHeaders);
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, corsHeaders);
38
+ return handleImageServing(request, env, fileId, createJsonResponse);
40
39
  }
41
40
 
42
41
  case 'DELETE': {
@@ -2,7 +2,7 @@
2
2
  "name": "IMAGES_WORKER_NAME",
3
3
  "account_id": "ACCOUNT_ID",
4
4
  "main": "src/image-worker.ts",
5
- "compatibility_date": "2026-04-19",
5
+ "compatibility_date": "2026-04-20",
6
6
  "compatibility_flags": [
7
7
  "nodejs_compat"
8
8
  ],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdf-worker",
3
- "version": "6.1.6",
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.83.0"
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: { ...corsHeaders, 'content-type': 'application/json' },
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
  }
@@ -2,7 +2,7 @@
2
2
  "name": "PDF_WORKER_NAME",
3
3
  "account_id": "ACCOUNT_ID",
4
4
  "main": "src/pdf-worker.ts",
5
- "compatibility_date": "2026-04-19",
5
+ "compatibility_date": "2026-04-20",
6
6
  "compatibility_flags": [
7
7
  "nodejs_compat"
8
8
  ],
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "user-worker",
3
- "version": "6.1.6",
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.83.0"
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, headers: ResponseHeaders, status: number = 200): Response {
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, headers: ResponseHeaders, status: number): Response {
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', corsHeaders, 404);
33
+ return createTextResponse('User not found', 404);
42
34
  }
43
35
 
44
- return createJsonResponse(userData, corsHeaders);
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', corsHeaders, 500);
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, corsHeaders, existingUser !== null ? 200 : 201);
90
+ return createJsonResponse(userData, existingUser !== null ? 200 : 201);
100
91
  } catch {
101
- return createTextResponse('Failed to save user data', corsHeaders, 500);
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
- }, corsHeaders);
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', corsHeaders, 404);
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
- }, corsHeaders, 500);
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: ResponseHeaders = {
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', corsHeaders, 404);
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, corsHeaders);
191
+ return createJsonResponse(userData);
205
192
  } catch {
206
- return createTextResponse('Failed to add cases', corsHeaders, 500);
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', corsHeaders, 404);
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, corsHeaders);
213
+ return createJsonResponse(userData);
228
214
  } catch {
229
- return createTextResponse('Failed to delete cases', corsHeaders, 500);
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
- const corsHeaders: Record<string, string> = {
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, corsHeaders);
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, corsHeaders);
58
- case 'DELETE': return handleDeleteCases(request, env, userUid, corsHeaders);
59
- default: return createTextResponse('Method not allowed', 405, corsHeaders);
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, corsHeaders);
69
- case 'PUT': return handleAddUser(request, env, userUid, corsHeaders);
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, corsHeaders)
72
- : handleDeleteUser(env, userUid, corsHeaders);
73
- default: return createTextResponse('Method not allowed', 405, corsHeaders);
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, corsHeaders);
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, corsHeaders);
69
+ return createTextResponse(errorMessage, 500);
83
70
  }
84
71
 
85
- return createTextResponse('Internal Server Error', 500, corsHeaders);
72
+ return createTextResponse('Internal Server Error', 500);
86
73
  }
87
74
  }
88
75
  };
@@ -2,7 +2,7 @@
2
2
  "name": "USER_WORKER_NAME",
3
3
  "account_id": "ACCOUNT_ID",
4
4
  "main": "src/user-worker.ts",
5
- "compatibility_date": "2026-04-19",
5
+ "compatibility_date": "2026-04-20",
6
6
  "compatibility_flags": [
7
7
  "nodejs_compat"
8
8
  ],
@@ -1,6 +1,6 @@
1
1
  #:schema node_modules/wrangler/config-schema.json
2
2
  name = "PAGES_PROJECT_NAME"
3
- compatibility_date = "2026-04-19"
3
+ compatibility_date = "2026-04-20"
4
4
  compatibility_flags = ["nodejs_compat"]
5
5
  pages_build_output_dir = "./build/client"
6
6