@striae-org/striae 5.3.2 → 5.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/app/components/auth/auth.module.css +531 -0
  2. package/app/components/auth/mfa-enrollment.tsx +132 -79
  3. package/app/components/auth/mfa-totp-enrollment.tsx +231 -0
  4. package/app/components/auth/mfa-verification.tsx +162 -33
  5. package/app/components/{sidebar/cases/cases-modal.tsx → navbar/case-modals/all-cases-modal.tsx} +4 -4
  6. package/app/components/navbar/case-modals/archive-case-modal.tsx +9 -10
  7. package/app/components/navbar/case-modals/case-modal-shared.module.css +88 -0
  8. package/app/components/navbar/case-modals/delete-case-modal.tsx +9 -10
  9. package/app/components/navbar/case-modals/export-case-modal.tsx +9 -10
  10. package/app/components/navbar/case-modals/export-confirmations-modal.tsx +9 -10
  11. package/app/components/navbar/case-modals/open-case-modal.tsx +4 -4
  12. package/app/components/navbar/case-modals/rename-case-modal.tsx +9 -10
  13. package/app/components/navbar/navbar.tsx +1 -1
  14. package/app/components/sidebar/files/delete-files-modal.tsx +3 -3
  15. package/app/components/sidebar/files/files-modal.module.css +29 -0
  16. package/app/components/sidebar/notes/{class-details-fields.tsx → class-details/class-details-fields.tsx} +1 -1
  17. package/app/components/sidebar/notes/{class-details-modal.tsx → class-details/class-details-modal.tsx} +1 -1
  18. package/app/components/sidebar/notes/{class-details-sections.tsx → class-details/class-details-sections.tsx} +1 -1
  19. package/app/components/sidebar/notes/notes-editor-form.tsx +2 -2
  20. package/app/components/sidebar/notes/notes-editor-modal.tsx +6 -6
  21. package/app/components/sidebar/notes/notes.module.css +52 -0
  22. package/app/components/toolbar/toolbar-color-selector.tsx +8 -8
  23. package/app/components/toolbar/toolbar.module.css +181 -2
  24. package/app/components/user/delete-account.tsx +7 -7
  25. package/app/components/user/inactivity-warning.tsx +6 -6
  26. package/app/components/user/manage-profile.tsx +18 -1
  27. package/app/components/user/mfa-enrolled-factors.tsx +117 -0
  28. package/app/components/user/mfa-phone-update.tsx +8 -4
  29. package/app/components/user/mfa-totp-section.tsx +446 -0
  30. package/app/components/user/user.module.css +665 -0
  31. package/app/routes/striae/striae.tsx +1 -1
  32. package/app/services/audit/audit.service.ts +1 -1
  33. package/app/services/audit/builders/audit-event-builders-user-security.ts +4 -2
  34. package/app/services/firebase/errors.ts +2 -0
  35. package/app/utils/auth/mfa.ts +35 -1
  36. package/package.json +23 -28
  37. package/scripts/deploy-all.sh +166 -0
  38. package/scripts/deploy-config/modules/env-utils.sh +322 -0
  39. package/scripts/deploy-config/modules/keys.sh +404 -0
  40. package/scripts/deploy-config/modules/prompt.sh +375 -0
  41. package/scripts/deploy-config/modules/scaffolding.sh +310 -0
  42. package/scripts/deploy-config/modules/validation.sh +354 -0
  43. package/scripts/deploy-config.sh +236 -0
  44. package/scripts/deploy-pages-secrets.sh +231 -0
  45. package/scripts/deploy-pages.sh +34 -0
  46. package/scripts/deploy-primershear-emails.sh +167 -0
  47. package/scripts/deploy-worker-secrets.sh +385 -0
  48. package/scripts/dev.cjs +23 -0
  49. package/scripts/enable-totp-mfa.mjs +57 -0
  50. package/scripts/install-workers.sh +87 -0
  51. package/scripts/run-eslint.cjs +43 -0
  52. package/scripts/unenroll-totp-mfa.mjs +82 -0
  53. package/scripts/update-compatibility-dates.cjs +124 -0
  54. package/scripts/update-markdown-versions.cjs +43 -0
  55. package/workers/audit-worker/.editorconfig +12 -0
  56. package/workers/audit-worker/.prettierrc +6 -0
  57. package/workers/audit-worker/package.json +1 -1
  58. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  59. package/workers/data-worker/.editorconfig +12 -0
  60. package/workers/data-worker/.prettierrc +6 -0
  61. package/workers/data-worker/package.json +1 -1
  62. package/workers/data-worker/wrangler.jsonc.example +1 -1
  63. package/workers/image-worker/.editorconfig +12 -0
  64. package/workers/image-worker/.prettierrc +6 -0
  65. package/workers/image-worker/package.json +1 -1
  66. package/workers/image-worker/wrangler.jsonc.example +1 -1
  67. package/workers/pdf-worker/.editorconfig +12 -0
  68. package/workers/pdf-worker/.prettierrc +6 -0
  69. package/workers/pdf-worker/package.json +1 -1
  70. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  71. package/workers/user-worker/.editorconfig +12 -0
  72. package/workers/user-worker/.prettierrc +6 -0
  73. package/workers/user-worker/package.json +1 -1
  74. package/workers/user-worker/wrangler.jsonc.example +1 -1
  75. package/wrangler.toml.example +1 -1
  76. package/app/components/auth/mfa-enrollment.module.css +0 -276
  77. package/app/components/auth/mfa-verification.module.css +0 -259
  78. package/app/components/navbar/case-modals/archive-case-modal.module.css +0 -34
  79. package/app/components/navbar/case-modals/delete-case-modal.module.css +0 -9
  80. package/app/components/navbar/case-modals/export-case-modal.module.css +0 -27
  81. package/app/components/navbar/case-modals/export-confirmations-modal.module.css +0 -24
  82. package/app/components/navbar/case-modals/open-case-modal.module.css +0 -82
  83. package/app/components/navbar/case-modals/rename-case-modal.module.css +0 -9
  84. package/app/components/sidebar/files/delete-files-modal.module.css +0 -26
  85. package/app/components/sidebar/notes/notes-editor-modal.module.css +0 -49
  86. package/app/components/toolbar/toolbar-color-selector.module.css +0 -171
  87. package/app/components/user/delete-account.module.css +0 -277
  88. package/app/components/user/inactivity-warning.module.css +0 -148
  89. package/app/components/user/manage-profile.module.css +0 -192
  90. package/app/routes/auth/login.module.css +0 -523
  91. package/app/routes/auth/login.tsx +0 -705
  92. package/workers/audit-worker/worker-configuration.d.ts +0 -7448
  93. package/workers/data-worker/worker-configuration.d.ts +0 -7448
  94. package/workers/image-worker/worker-configuration.d.ts +0 -7448
  95. package/workers/pdf-worker/worker-configuration.d.ts +0 -7447
  96. package/workers/user-worker/worker-configuration.d.ts +0 -7450
  97. /package/app/components/{sidebar → navbar}/case-import/case-import.module.css +0 -0
  98. /package/app/components/{sidebar → navbar}/case-import/case-import.tsx +0 -0
  99. /package/app/components/{sidebar → navbar}/case-import/components/CasePreviewSection.tsx +0 -0
  100. /package/app/components/{sidebar → navbar}/case-import/components/ConfirmationDialog.tsx +0 -0
  101. /package/app/components/{sidebar → navbar}/case-import/components/ConfirmationPreviewSection.tsx +0 -0
  102. /package/app/components/{sidebar → navbar}/case-import/components/ExistingCaseSection.tsx +0 -0
  103. /package/app/components/{sidebar → navbar}/case-import/components/FileSelector.tsx +0 -0
  104. /package/app/components/{sidebar → navbar}/case-import/components/ProgressSection.tsx +0 -0
  105. /package/app/components/{sidebar → navbar}/case-import/hooks/useFilePreview.ts +0 -0
  106. /package/app/components/{sidebar → navbar}/case-import/hooks/useImportExecution.ts +0 -0
  107. /package/app/components/{sidebar → navbar}/case-import/hooks/useImportState.ts +0 -0
  108. /package/app/components/{sidebar → navbar}/case-import/index.ts +0 -0
  109. /package/app/components/{sidebar → navbar}/case-import/utils/file-validation.ts +0 -0
  110. /package/app/components/{sidebar/cases/cases-modal.module.css → navbar/case-modals/all-cases-modal.module.css} +0 -0
  111. /package/app/components/sidebar/notes/{class-details-shared.ts → class-details/class-details-shared.ts} +0 -0
  112. /package/app/components/sidebar/notes/{use-class-details-state.ts → class-details/use-class-details-state.ts} +0 -0
@@ -0,0 +1,375 @@
1
+ #!/bin/bash
2
+
3
+ prompt_for_secrets() {
4
+ echo -e "\n${BLUE}🔐 Environment Variables Setup${NC}"
5
+ echo "=============================="
6
+ echo -e "${YELLOW}Please provide values for the following environment variables.${NC}"
7
+ echo -e "${YELLOW}Press Enter to keep existing values (if any).${NC}"
8
+ echo ""
9
+
10
+ # Create or backup existing .env
11
+ if [ -f ".env" ] && [ "$update_env" != "true" ]; then
12
+ cp .env .env.backup
13
+ echo -e "${GREEN}📄 Existing .env backed up to .env.backup${NC}"
14
+ fi
15
+
16
+ # Copy .env.example to .env if it doesn't exist
17
+ if [ ! -f ".env" ]; then
18
+ cp .env.example .env
19
+ echo -e "${GREEN}📄 Created .env from .env.example${NC}"
20
+ fi
21
+
22
+ # Function to prompt for a variable
23
+ is_auto_generated_secret_var() {
24
+ local var_name=$1
25
+ case "$var_name" in
26
+ USER_DB_AUTH|R2_KEY_SECRET|PDF_WORKER_AUTH|IMAGES_API_TOKEN|IMAGE_SIGNED_URL_SECRET)
27
+ return 0
28
+ ;;
29
+ *)
30
+ return 1
31
+ ;;
32
+ esac
33
+ }
34
+
35
+ is_secret_placeholder_value() {
36
+ local var_name=$1
37
+ local value=$2
38
+ case "$var_name" in
39
+ USER_DB_AUTH)
40
+ [ "$value" = "your_custom_user_db_auth_token_here" ]
41
+ ;;
42
+ R2_KEY_SECRET)
43
+ [ "$value" = "your_custom_r2_secret_here" ]
44
+ ;;
45
+ PDF_WORKER_AUTH)
46
+ [ "$value" = "your_custom_pdf_worker_auth_token_here" ]
47
+ ;;
48
+ IMAGES_API_TOKEN)
49
+ [ "$value" = "your_cloudflare_images_api_token_here" ]
50
+ ;;
51
+ IMAGE_SIGNED_URL_SECRET)
52
+ [ "$value" = "your_image_signed_url_secret_here" ]
53
+ ;;
54
+ *)
55
+ return 1
56
+ ;;
57
+ esac
58
+ }
59
+
60
+ generate_secret_value() {
61
+ local var_name=$1
62
+ case "$var_name" in
63
+ IMAGE_SIGNED_URL_SECRET)
64
+ openssl rand -base64 48 2>/dev/null | tr '+/' '-_' | tr -d '='
65
+ ;;
66
+ *)
67
+ openssl rand -hex 32 2>/dev/null
68
+ ;;
69
+ esac
70
+ }
71
+
72
+ prompt_for_var() {
73
+ local var_name=$1
74
+ local description=$2
75
+ local current_value="${!var_name}"
76
+ local new_value=""
77
+ local allow_keep="false"
78
+
79
+ current_value=$(strip_carriage_returns "$current_value")
80
+
81
+ if [ "$var_name" = "PAGES_CUSTOM_DOMAIN" ]; then
82
+ current_value=$(resolve_existing_domain_value "$var_name" "$current_value")
83
+ fi
84
+
85
+ # Auto-generate selected secrets - but allow keeping current.
86
+ if is_auto_generated_secret_var "$var_name"; then
87
+ echo -e "${BLUE}$var_name${NC}"
88
+ echo -e "${YELLOW}$description${NC}"
89
+
90
+ if [ "$update_env" != "true" ] && [ -n "$current_value" ] && ! is_placeholder "$current_value" && ! is_secret_placeholder_value "$var_name" "$current_value"; then
91
+ # Current value exists and is not a placeholder
92
+ echo -e "${GREEN}Current value: [HIDDEN]${NC}"
93
+ read -p "Generate new secret? (press Enter to keep current, or type 'y' to generate): " gen_choice
94
+ gen_choice=$(strip_carriage_returns "$gen_choice")
95
+
96
+ if [ "$gen_choice" = "y" ] || [ "$gen_choice" = "Y" ]; then
97
+ new_value=$(generate_secret_value "$var_name" || echo "")
98
+ if [ -n "$new_value" ]; then
99
+ echo -e "${GREEN}✅ $var_name auto-generated${NC}"
100
+ else
101
+ while true; do
102
+ echo -e "${RED}❌ Failed to auto-generate, please enter manually:${NC}"
103
+ read -p "Enter value: " new_value
104
+ new_value=$(strip_carriage_returns "$new_value")
105
+ if [ -z "$new_value" ]; then
106
+ echo -e "${RED}❌ A value is required.${NC}"
107
+ continue
108
+ fi
109
+ if is_placeholder "$new_value"; then
110
+ echo -e "${RED}❌ Placeholder values are not allowed.${NC}"
111
+ new_value=""
112
+ continue
113
+ fi
114
+ break
115
+ done
116
+ fi
117
+ else
118
+ # User wants to keep current value
119
+ new_value=""
120
+ fi
121
+ else
122
+ # No current value or placeholder value - auto-generate
123
+ echo -e "${YELLOW}Auto-generating secret...${NC}"
124
+ new_value=$(generate_secret_value "$var_name" || echo "")
125
+ if [ -n "$new_value" ]; then
126
+ echo -e "${GREEN}✅ $var_name auto-generated${NC}"
127
+ else
128
+ while true; do
129
+ echo -e "${RED}❌ Failed to auto-generate, please enter manually:${NC}"
130
+ read -p "Enter value: " new_value
131
+ new_value=$(strip_carriage_returns "$new_value")
132
+ if [ -z "$new_value" ]; then
133
+ echo -e "${RED}❌ A value is required.${NC}"
134
+ continue
135
+ fi
136
+ if is_placeholder "$new_value"; then
137
+ echo -e "${RED}❌ Placeholder values are not allowed.${NC}"
138
+ new_value=""
139
+ continue
140
+ fi
141
+ break
142
+ done
143
+ fi
144
+ fi
145
+ else
146
+ # Normal prompt for other variables
147
+ echo -e "${BLUE}$var_name${NC}"
148
+ echo -e "${YELLOW}$description${NC}"
149
+ if [ "$update_env" != "true" ] && [ -n "$current_value" ] && ! is_placeholder "$current_value"; then
150
+ allow_keep="true"
151
+ if [ "$var_name" = "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" ]; then
152
+ echo -e "${GREEN}Current value: [HIDDEN]${NC}"
153
+ else
154
+ echo -e "${GREEN}Current value: $current_value${NC}"
155
+ fi
156
+ fi
157
+
158
+ while true; do
159
+ if [ "$allow_keep" = "true" ]; then
160
+ read -p "New value (or press Enter to keep current): " new_value
161
+ new_value=$(strip_carriage_returns "$new_value")
162
+ if [ -z "$new_value" ]; then
163
+ break
164
+ fi
165
+ else
166
+ read -p "Enter value: " new_value
167
+ new_value=$(strip_carriage_returns "$new_value")
168
+ if [ -z "$new_value" ]; then
169
+ echo -e "${RED}❌ A value is required.${NC}"
170
+ continue
171
+ fi
172
+ fi
173
+
174
+ if is_placeholder "$new_value"; then
175
+ echo -e "${RED}❌ Placeholder values are not allowed.${NC}"
176
+ new_value=""
177
+ continue
178
+ fi
179
+
180
+ if [[ "$var_name" == *_WORKER_NAME ]]; then
181
+ new_value=$(normalize_worker_label_value "$new_value")
182
+
183
+ if [ -z "$new_value" ] || ! is_valid_worker_label "$new_value"; then
184
+ echo -e "${RED}❌ $var_name must use only lowercase letters, numbers, and dashes.${NC}"
185
+ new_value=""
186
+ continue
187
+ fi
188
+ fi
189
+
190
+ break
191
+ done
192
+ fi
193
+
194
+ if [ -n "$new_value" ]; then
195
+ if [ "$var_name" = "PAGES_CUSTOM_DOMAIN" ]; then
196
+ new_value=$(normalize_domain_value "$new_value")
197
+ fi
198
+
199
+ # Update the .env file
200
+ write_env_var "$var_name" "$new_value"
201
+
202
+ export "$var_name=$new_value"
203
+ echo -e "${GREEN}✅ $var_name updated${NC}"
204
+ elif [ -n "$current_value" ]; then
205
+ if [[ "$var_name" == *_WORKER_NAME ]]; then
206
+ current_value=$(normalize_worker_label_value "$current_value")
207
+ fi
208
+
209
+ # Keep values aligned with .env.example ordering and remove stale duplicates.
210
+ write_env_var "$var_name" "$current_value"
211
+ export "$var_name=$current_value"
212
+ echo -e "${GREEN}✅ Keeping current value for $var_name${NC}"
213
+ fi
214
+ echo ""
215
+ }
216
+
217
+ set_worker_domain_from_shared_subdomain() {
218
+ local worker_name_var=$1
219
+ local worker_domain_var=$2
220
+ local worker_name_value="${!worker_name_var}"
221
+ local composed_domain=""
222
+
223
+ worker_name_value=$(normalize_worker_label_value "$worker_name_value")
224
+
225
+ if [ -z "$worker_name_value" ] || ! is_valid_worker_label "$worker_name_value"; then
226
+ echo -e "${RED}❌ $worker_name_var must use only lowercase letters, numbers, and dashes.${NC}"
227
+ exit 1
228
+ fi
229
+
230
+ composed_domain=$(compose_worker_domain "$worker_name_value" "$shared_worker_subdomain" || echo "")
231
+
232
+ if [ -z "$composed_domain" ]; then
233
+ echo -e "${RED}❌ Could not build $worker_domain_var from $worker_name_var and shared worker-subdomain.${NC}"
234
+ exit 1
235
+ fi
236
+
237
+ write_env_var "$worker_domain_var" "$composed_domain"
238
+ export "$worker_domain_var=$composed_domain"
239
+ echo -e "${GREEN}✅ $worker_domain_var set to $composed_domain${NC}"
240
+ }
241
+
242
+ echo -e "${BLUE}📊 CLOUDFLARE CORE CONFIGURATION${NC}"
243
+ echo "=================================="
244
+ prompt_for_var "ACCOUNT_ID" "Your Cloudflare Account ID"
245
+
246
+ echo -e "${BLUE}🔐 SHARED AUTHENTICATION & STORAGE${NC}"
247
+ echo "==================================="
248
+ prompt_for_var "USER_DB_AUTH" "Custom user database authentication token (generate with: openssl rand -hex 16)"
249
+ prompt_for_var "R2_KEY_SECRET" "Custom R2 storage authentication token (generate with: openssl rand -hex 16)"
250
+ prompt_for_var "IMAGES_API_TOKEN" "Image worker API token (shared between workers)"
251
+
252
+ echo -e "${BLUE}🔥 FIREBASE AUTH CONFIGURATION${NC}"
253
+ echo "==============================="
254
+ prompt_for_var "API_KEY" "Firebase API key"
255
+ prompt_for_var "AUTH_DOMAIN" "Firebase auth domain (project-id.firebaseapp.com)"
256
+ prompt_for_var "STORAGE_BUCKET" "Firebase storage bucket"
257
+ prompt_for_var "MESSAGING_SENDER_ID" "Firebase messaging sender ID"
258
+ prompt_for_var "APP_ID" "Firebase app ID"
259
+ prompt_for_var "MEASUREMENT_ID" "Firebase measurement ID (optional)"
260
+ echo -e "${GREEN}Using PROJECT_ID and service account values from app/config/admin-service.json${NC}"
261
+
262
+ echo -e "${BLUE}📄 PAGES CONFIGURATION${NC}"
263
+ echo "======================"
264
+ prompt_for_var "PAGES_PROJECT_NAME" "Your Cloudflare Pages project name"
265
+ prompt_for_var "PAGES_CUSTOM_DOMAIN" "Your custom domain (e.g., striae.org) - DO NOT include https://"
266
+
267
+ echo -e "${BLUE}🔑 WORKER NAMES & DOMAINS${NC}"
268
+ echo "========================="
269
+ echo -e "${YELLOW}Worker names are lowercased automatically and must use only letters, numbers, and dashes.${NC}"
270
+ echo -e "${YELLOW}Enter one shared worker-subdomain as a hostname (for example: team-name.workers.dev).${NC}"
271
+ echo -e "${YELLOW}Each worker domain is generated as {worker-name}.{worker-subdomain}.${NC}"
272
+
273
+ local shared_worker_subdomain=""
274
+ local shared_worker_subdomain_default=""
275
+ local shared_worker_subdomain_input=""
276
+
277
+ shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$USER_WORKER_NAME" "$USER_WORKER_DOMAIN")
278
+ if [ -z "$shared_worker_subdomain_default" ]; then
279
+ shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$DATA_WORKER_NAME" "$DATA_WORKER_DOMAIN")
280
+ fi
281
+ if [ -z "$shared_worker_subdomain_default" ]; then
282
+ shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$AUDIT_WORKER_NAME" "$AUDIT_WORKER_DOMAIN")
283
+ fi
284
+ if [ -z "$shared_worker_subdomain_default" ]; then
285
+ shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$IMAGES_WORKER_NAME" "$IMAGES_WORKER_DOMAIN")
286
+ fi
287
+ if [ -z "$shared_worker_subdomain_default" ]; then
288
+ shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$PDF_WORKER_NAME" "$PDF_WORKER_DOMAIN")
289
+ fi
290
+
291
+ while true; do
292
+ echo -e "${BLUE}WORKER_SUBDOMAIN${NC}"
293
+
294
+ if [ "$update_env" != "true" ] && [ -n "$shared_worker_subdomain_default" ] && ! is_placeholder "$shared_worker_subdomain_default"; then
295
+ echo -e "${GREEN}Current value: $shared_worker_subdomain_default${NC}"
296
+ read -p "New value (or press Enter to keep current): " shared_worker_subdomain_input
297
+ shared_worker_subdomain_input=$(strip_carriage_returns "$shared_worker_subdomain_input")
298
+
299
+ if [ -z "$shared_worker_subdomain_input" ]; then
300
+ shared_worker_subdomain="$shared_worker_subdomain_default"
301
+ else
302
+ shared_worker_subdomain="$shared_worker_subdomain_input"
303
+ fi
304
+ else
305
+ read -p "Enter shared worker-subdomain (e.g., team-name.workers.dev): " shared_worker_subdomain_input
306
+ shared_worker_subdomain_input=$(strip_carriage_returns "$shared_worker_subdomain_input")
307
+ shared_worker_subdomain="$shared_worker_subdomain_input"
308
+ fi
309
+
310
+ if [ -z "$shared_worker_subdomain" ] || is_placeholder "$shared_worker_subdomain"; then
311
+ echo -e "${RED}❌ shared worker-subdomain is required and cannot be a placeholder.${NC}"
312
+ continue
313
+ fi
314
+
315
+ shared_worker_subdomain=$(normalize_worker_subdomain_value "$shared_worker_subdomain")
316
+
317
+ if [ -z "$shared_worker_subdomain" ] || ! is_valid_worker_subdomain "$shared_worker_subdomain"; then
318
+ echo -e "${RED}❌ shared worker-subdomain must be a valid hostname like team-name.workers.dev (letters, numbers, dashes, and dots).${NC}"
319
+ continue
320
+ fi
321
+
322
+ echo -e "${GREEN}✅ Shared worker-subdomain set to: $shared_worker_subdomain${NC}"
323
+ echo ""
324
+ break
325
+ done
326
+
327
+ prompt_for_var "USER_WORKER_NAME" "User worker name"
328
+ prompt_for_var "DATA_WORKER_NAME" "Data worker name"
329
+ prompt_for_var "AUDIT_WORKER_NAME" "Audit worker name"
330
+ prompt_for_var "IMAGES_WORKER_NAME" "Images worker name"
331
+ prompt_for_var "PDF_WORKER_NAME" "PDF worker name"
332
+
333
+ set_worker_domain_from_shared_subdomain "USER_WORKER_NAME" "USER_WORKER_DOMAIN"
334
+ set_worker_domain_from_shared_subdomain "DATA_WORKER_NAME" "DATA_WORKER_DOMAIN"
335
+ set_worker_domain_from_shared_subdomain "AUDIT_WORKER_NAME" "AUDIT_WORKER_DOMAIN"
336
+ set_worker_domain_from_shared_subdomain "IMAGES_WORKER_NAME" "IMAGES_WORKER_DOMAIN"
337
+ set_worker_domain_from_shared_subdomain "PDF_WORKER_NAME" "PDF_WORKER_DOMAIN"
338
+ echo ""
339
+
340
+ echo -e "${BLUE}🗄️ STORAGE CONFIGURATION${NC}"
341
+ echo "========================="
342
+ prompt_for_var "DATA_BUCKET_NAME" "Your R2 bucket name for case data storage"
343
+ prompt_for_var "AUDIT_BUCKET_NAME" "Your R2 bucket name for audit logs (separate from data bucket)"
344
+ prompt_for_var "FILES_BUCKET_NAME" "Your R2 bucket name for encrypted files storage"
345
+ prompt_for_var "KV_STORE_ID" "Your KV namespace ID (UUID format)"
346
+
347
+ echo -e "${BLUE}🔐 SERVICE-SPECIFIC SECRETS${NC}"
348
+ echo "============================"
349
+ prompt_for_var "PDF_WORKER_AUTH" "PDF worker authentication token (generate with: openssl rand -hex 16)"
350
+ prompt_for_var "IMAGE_SIGNED_URL_SECRET" "Image signed URL secret (generate with: openssl rand -base64 48 | tr '+/' '-_' | tr -d '=')"
351
+
352
+ # Auto-derive IMAGE_SIGNED_URL_BASE_URL from PAGES_CUSTOM_DOMAIN if not yet set or still
353
+ # contains a placeholder-domain pattern (i.e. was expanded from .env.example at source time).
354
+ _current_image_base_url="${IMAGE_SIGNED_URL_BASE_URL:-}"
355
+ if [[ "$_current_image_base_url" =~ your_[a-z0-9_]+_here|your-[a-z0-9-]+-here ]] || [ -z "$_current_image_base_url" ]; then
356
+ if [ -n "${PAGES_CUSTOM_DOMAIN:-}" ] && ! is_placeholder "${PAGES_CUSTOM_DOMAIN:-}"; then
357
+ IMAGE_SIGNED_URL_BASE_URL="https://${PAGES_CUSTOM_DOMAIN}/api/image"
358
+ export IMAGE_SIGNED_URL_BASE_URL
359
+ fi
360
+ fi
361
+ prompt_for_var "IMAGE_SIGNED_URL_BASE_URL" "Signed URL delivery base URL — routes signed image delivery through the Pages proxy (leave as-is unless using a non-standard domain)"
362
+
363
+ prompt_for_var "BROWSER_API_TOKEN" "Cloudflare Browser Rendering API token (for PDF Worker)"
364
+
365
+ configure_manifest_signing_credentials
366
+ configure_export_encryption_credentials
367
+ configure_user_kv_encryption_credentials
368
+ configure_data_at_rest_encryption_credentials
369
+
370
+ # Reload the updated .env file
371
+ source .env
372
+
373
+ echo -e "${GREEN}🎉 Environment variables setup completed!${NC}"
374
+ echo -e "${BLUE}📄 All values saved to .env file${NC}"
375
+ }