@striae-org/striae 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.env.example +22 -2
  2. package/app/components/actions/case-export/download-handlers.ts +18 -1
  3. package/app/components/actions/case-manage.ts +17 -1
  4. package/app/components/actions/generate-pdf.ts +9 -3
  5. package/app/components/actions/image-manage.ts +43 -11
  6. package/app/routes/striae/striae.tsx +1 -0
  7. package/app/types/file.ts +18 -2
  8. package/app/utils/api/image-api-client.ts +49 -1
  9. package/app/utils/data/permissions.ts +4 -2
  10. package/functions/api/image/[[path]].ts +2 -1
  11. package/package.json +4 -4
  12. package/scripts/deploy-config/modules/env-utils.sh +322 -0
  13. package/scripts/deploy-config/modules/keys.sh +404 -0
  14. package/scripts/deploy-config/modules/prompt.sh +372 -0
  15. package/scripts/deploy-config/modules/scaffolding.sh +336 -0
  16. package/scripts/deploy-config/modules/validation.sh +365 -0
  17. package/scripts/deploy-config.sh +59 -1556
  18. package/scripts/deploy-worker-secrets.sh +101 -6
  19. package/worker-configuration.d.ts +9 -4
  20. package/workers/audit-worker/package.json +1 -1
  21. package/workers/audit-worker/src/audit-worker.example.ts +188 -6
  22. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  23. package/workers/data-worker/package.json +1 -1
  24. package/workers/data-worker/src/data-worker.example.ts +344 -32
  25. package/workers/data-worker/wrangler.jsonc.example +2 -4
  26. package/workers/image-worker/package.json +1 -1
  27. package/workers/image-worker/src/image-worker.example.ts +456 -20
  28. package/workers/image-worker/worker-configuration.d.ts +3 -2
  29. package/workers/image-worker/wrangler.jsonc.example +1 -1
  30. package/workers/keys-worker/package.json +1 -1
  31. package/workers/keys-worker/wrangler.jsonc.example +1 -1
  32. package/workers/pdf-worker/package.json +1 -1
  33. package/workers/pdf-worker/src/pdf-worker.example.ts +0 -1
  34. package/workers/pdf-worker/wrangler.jsonc.example +1 -5
  35. package/workers/user-worker/package.json +17 -17
  36. package/workers/user-worker/src/encryption-utils.ts +244 -0
  37. package/workers/user-worker/src/user-worker.example.ts +333 -31
  38. package/workers/user-worker/wrangler.jsonc.example +1 -1
  39. package/wrangler.toml.example +1 -1
  40. package/scripts/encrypt-r2-backfill.mjs +0 -376
@@ -28,6 +28,7 @@ trap 'echo -e "\n${RED}❌ deploy-config.sh failed near line ${LINENO}${NC}"' ER
28
28
  update_env=false
29
29
  show_help=false
30
30
  validate_only=false
31
+ force_rotate_keys=false
31
32
  for arg in "$@"; do
32
33
  case "$arg" in
33
34
  -h|--help)
@@ -39,6 +40,9 @@ for arg in "$@"; do
39
40
  --validate-only)
40
41
  validate_only=true
41
42
  ;;
43
+ --force-rotate-keys)
44
+ force_rotate_keys=true
45
+ ;;
42
46
  *)
43
47
  echo -e "${RED}❌ Unknown option: $arg${NC}"
44
48
  echo "Use --help to see supported options."
@@ -52,18 +56,28 @@ if [ "$update_env" = "true" ] && [ "$validate_only" = "true" ]; then
52
56
  exit 1
53
57
  fi
54
58
 
59
+ if [ "$force_rotate_keys" = "true" ] && [ "$validate_only" = "true" ]; then
60
+ echo -e "${RED}❌ --force-rotate-keys and --validate-only cannot be used together${NC}"
61
+ exit 1
62
+ fi
63
+
55
64
  if [ "$show_help" = "true" ]; then
56
- echo "Usage: bash ./scripts/deploy-config.sh [--update-env] [--validate-only]"
65
+ echo "Usage: bash ./scripts/deploy-config.sh [--update-env] [--validate-only] [--force-rotate-keys]"
57
66
  echo ""
58
67
  echo "Options:"
59
68
  echo " --update-env Reset .env from .env.example and overwrite configs"
60
69
  echo " --validate-only Validate current .env and generated config files without modifying them"
70
+ echo " --force-rotate-keys Force regeneration of all encryption/signing key pairs without prompts"
61
71
  echo " -h, --help Show this help message"
62
72
  exit 0
63
73
  fi
64
74
 
65
75
  if [ "$update_env" = "true" ]; then
66
- echo -e "${YELLOW}⚠️ Update-env mode: overwriting configs and regenerating .env values${NC}"
76
+ echo -e "${YELLOW}⚠️ Update-env mode: overwriting configs and resetting .env values from template${NC}"
77
+ fi
78
+
79
+ if [ "$force_rotate_keys" = "true" ]; then
80
+ echo -e "${YELLOW}⚠️ Force-rotate-keys mode: all encryption/signing key pairs will be regenerated without prompts${NC}"
67
81
  fi
68
82
 
69
83
  require_command() {
@@ -81,13 +95,23 @@ require_command grep
81
95
 
82
96
  is_placeholder() {
83
97
  local value="$1"
84
- local normalized=$(echo "$value" | tr '[:upper:]' '[:lower:]')
98
+ local normalized
99
+
100
+ normalized=$(printf '%s' "$value" | tr -d '\r' | tr '[:upper:]' '[:lower:]')
101
+ normalized=$(printf '%s' "$normalized" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
102
+ normalized=${normalized#\"}
103
+ normalized=${normalized%\"}
85
104
 
86
105
  if [ -z "$normalized" ]; then
87
106
  return 1
88
107
  fi
89
108
 
90
- [[ "$normalized" == your_*_here ]]
109
+ [[ "$normalized" =~ ^your_[a-z0-9_]+_here$ || \
110
+ "$normalized" =~ ^your-[a-z0-9-]+-here$ || \
111
+ "$normalized" == "placeholder" || \
112
+ "$normalized" == "changeme" || \
113
+ "$normalized" == "replace_me" || \
114
+ "$normalized" == "replace-me" ]]
91
115
  }
92
116
 
93
117
  # Check if .env file exists
@@ -136,894 +160,45 @@ fi
136
160
  echo -e "${YELLOW}📖 Loading environment variables from .env...${NC}"
137
161
  source .env
138
162
 
139
- escape_for_sed_pattern() {
140
- printf '%s' "$1" | sed -e 's/[][\\.^$*+?{}|()]/\\&/g'
141
- }
142
-
143
- dedupe_env_var_entries() {
144
- local var_name=$1
145
- local expected_count=1
146
- local escaped_var_name
147
-
148
- escaped_var_name=$(escape_for_sed_pattern "$var_name")
149
-
150
- if [ -f ".env.example" ]; then
151
- expected_count=$(grep -c "^$escaped_var_name=" .env.example || true)
152
-
153
- if [ "$expected_count" -lt 1 ]; then
154
- expected_count=1
155
- fi
156
- fi
157
-
158
- awk -v key="$var_name" -v keep="$expected_count" '
159
- BEGIN { seen = 0 }
160
- {
161
- if (index($0, key "=") == 1) {
162
- seen++
163
-
164
- if (seen > keep) {
165
- next
166
- }
167
- }
168
- print
169
- }
170
- ' .env > .env.tmp && mv .env.tmp .env
171
- }
172
-
173
- normalize_domain_value() {
174
- local domain="$1"
175
-
176
- domain=$(printf '%s' "$domain" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
177
- domain="${domain#http://}"
178
- domain="${domain#https://}"
179
- domain="${domain%/}"
180
-
181
- printf '%s' "$domain"
182
- }
183
-
184
- normalize_worker_label_value() {
185
- local label="$1"
186
-
187
- label=$(normalize_domain_value "$label")
188
- label="${label#.}"
189
- label="${label%.}"
190
- label=$(printf '%s' "$label" | tr '[:upper:]' '[:lower:]')
191
-
192
- printf '%s' "$label"
193
- }
194
-
195
- normalize_worker_subdomain_value() {
196
- local subdomain="$1"
197
-
198
- subdomain=$(normalize_domain_value "$subdomain")
199
- subdomain="${subdomain#.}"
200
- subdomain="${subdomain%.}"
201
- subdomain=$(printf '%s' "$subdomain" | tr '[:upper:]' '[:lower:]')
202
-
203
- printf '%s' "$subdomain"
204
- }
205
-
206
- is_valid_worker_label() {
207
- local label="$1"
208
-
209
- [[ "$label" =~ ^[a-z0-9-]+$ ]]
210
- }
211
-
212
- is_valid_worker_subdomain() {
213
- local subdomain="$1"
214
-
215
- [[ "$subdomain" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$ ]]
216
- }
217
-
218
- strip_carriage_returns() {
219
- printf '%s' "$1" | tr -d '\r'
220
- }
221
-
222
- read_env_var_from_file() {
223
- local env_file=$1
224
- local var_name=$2
225
-
226
- if [ ! -f "$env_file" ]; then
227
- return 0
228
- fi
229
-
230
- awk -v key="$var_name" '
231
- index($0, key "=") == 1 {
232
- value = substr($0, length(key) + 2)
233
- }
234
- END {
235
- if (value != "") {
236
- gsub(/\r/, "", value)
237
- gsub(/^"/, "", value)
238
- gsub(/"$/, "", value)
239
- print value
240
- }
241
- }
242
- ' "$env_file"
243
- }
244
-
245
- resolve_existing_domain_value() {
246
- local var_name=$1
247
- local current_value=$2
248
- local preserved_value=""
249
-
250
- current_value=$(normalize_domain_value "$current_value")
251
-
252
- if [ "$current_value" = "$var_name" ]; then
253
- current_value=""
254
- fi
255
-
256
- if [ -n "$current_value" ] && ! is_placeholder "$current_value"; then
257
- printf '%s' "$current_value"
258
- return 0
259
- fi
260
-
261
- if [ -n "$preserved_domain_env_file" ] && [ -f "$preserved_domain_env_file" ]; then
262
- preserved_value=$(read_env_var_from_file "$preserved_domain_env_file" "$var_name")
263
- preserved_value=$(normalize_domain_value "$preserved_value")
264
-
265
- if [ "$preserved_value" = "$var_name" ]; then
266
- preserved_value=""
267
- fi
268
-
269
- if [ -n "$preserved_value" ] && ! is_placeholder "$preserved_value"; then
270
- printf '%s' "$preserved_value"
271
- return 0
272
- fi
273
- fi
274
-
275
- printf '%s' "$current_value"
276
- }
277
-
278
- generate_worker_subdomain_label() {
279
- node -e "const { randomInt } = require('crypto'); const alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789'; let value = ''; for (let index = 0; index < 10; index += 1) { value += alphabet[randomInt(alphabet.length)]; } process.stdout.write(value);" 2>/dev/null
280
- }
281
-
282
- compose_worker_domain() {
283
- local worker_name=$1
284
- local worker_subdomain=$2
285
-
286
- worker_name=$(normalize_worker_label_value "$worker_name")
287
- worker_subdomain=$(normalize_worker_subdomain_value "$worker_subdomain")
288
-
289
- if [ -z "$worker_name" ] || [ -z "$worker_subdomain" ]; then
290
- return 1
291
- fi
292
-
293
- if ! is_valid_worker_label "$worker_name" || ! is_valid_worker_subdomain "$worker_subdomain"; then
294
- return 1
295
- fi
296
-
297
- printf '%s.%s' "$worker_name" "$worker_subdomain"
298
- }
299
-
300
- infer_worker_subdomain_from_domain() {
301
- local worker_name=$1
302
- local worker_domain=$2
303
- local worker_subdomain=""
304
-
305
- worker_name=$(normalize_worker_label_value "$worker_name")
306
- worker_domain=$(normalize_domain_value "$worker_domain")
307
- worker_domain=$(printf '%s' "$worker_domain" | tr '[:upper:]' '[:lower:]')
308
-
309
- if [ -z "$worker_name" ] || [ -z "$worker_domain" ] || is_placeholder "$worker_name" || is_placeholder "$worker_domain"; then
310
- printf '%s' ""
311
- return 0
312
- fi
313
-
314
- case "$worker_domain" in
315
- "$worker_name".*)
316
- worker_subdomain="${worker_domain#${worker_name}.}"
317
- worker_subdomain=$(normalize_worker_subdomain_value "$worker_subdomain")
318
-
319
- if is_valid_worker_subdomain "$worker_subdomain"; then
320
- printf '%s' "$worker_subdomain"
321
- return 0
322
- fi
323
- ;;
324
- esac
325
-
326
- printf '%s' ""
327
- }
328
-
329
- write_env_var() {
330
- local var_name=$1
331
- local var_value=$2
332
- local env_file_value="$var_value"
333
-
334
- var_value=$(strip_carriage_returns "$var_value")
335
- env_file_value="$var_value"
336
-
337
- if [ "$var_name" = "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" ] || [ "$var_name" = "MANIFEST_SIGNING_PRIVATE_KEY" ] || [ "$var_name" = "MANIFEST_SIGNING_PUBLIC_KEY" ] || [ "$var_name" = "EXPORT_ENCRYPTION_PRIVATE_KEY" ] || [ "$var_name" = "EXPORT_ENCRYPTION_PUBLIC_KEY" ] || [ "$var_name" = "DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" ] || [ "$var_name" = "DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" ]; then
338
- # Store as a quoted string so sourced .env preserves escaped newline markers (\n)
339
- env_file_value=${env_file_value//\"/\\\"}
340
- env_file_value="\"$env_file_value\""
341
- fi
342
-
343
- local escaped_var_name
344
- local replacement_line
345
- escaped_var_name=$(escape_for_sed_pattern "$var_name")
346
- replacement_line=$(escape_for_sed_replacement "$var_name=$env_file_value")
347
-
348
- if grep -q "^$escaped_var_name=" .env; then
349
- # Replace all occurrences so intentional duplicates in .env.example stay in sync.
350
- sed -i "s|^$escaped_var_name=.*|$replacement_line|g" .env
351
- dedupe_env_var_entries "$var_name"
352
- else
353
- echo "$var_name=$env_file_value" >> .env
354
- fi
355
- }
356
-
357
- escape_for_sed_replacement() {
358
- printf '%s' "$1" | sed -e 's/[&|\\]/\\&/g'
359
- }
360
-
361
- is_admin_service_placeholder() {
362
- local value="$1"
363
- local normalized=$(echo "$value" | tr '[:upper:]' '[:lower:]')
364
-
365
- [[ -z "$normalized" || "$normalized" == your-* || "$normalized" == *"your_private_key"* ]]
366
- }
367
-
368
- load_admin_service_credentials() {
369
- local admin_service_path="app/config/admin-service.json"
370
-
371
- if [ ! -f "$admin_service_path" ]; then
372
- echo -e "${RED}❌ Error: Required Firebase admin service file not found: $admin_service_path${NC}"
373
- echo -e "${YELLOW} Create app/config/admin-service.json with service account credentials.${NC}"
374
- exit 1
375
- fi
376
-
377
- local service_project_id
378
- local service_client_email
379
- local service_private_key
380
-
381
- if ! service_project_id=$(node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); process.stdout.write(data.project_id || '');" "$admin_service_path"); then
382
- echo -e "${RED}❌ Error: Could not parse project_id from $admin_service_path${NC}"
383
- exit 1
384
- fi
385
-
386
- if ! service_client_email=$(node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); process.stdout.write(data.client_email || '');" "$admin_service_path"); then
387
- echo -e "${RED}❌ Error: Could not parse client_email from $admin_service_path${NC}"
388
- exit 1
389
- fi
390
-
391
- if ! service_private_key=$(node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); process.stdout.write(data.private_key || '');" "$admin_service_path"); then
392
- echo -e "${RED}❌ Error: Could not parse private_key from $admin_service_path${NC}"
393
- exit 1
394
- fi
395
-
396
- local normalized_private_key="${service_private_key//$'\r'/}"
397
- normalized_private_key="${normalized_private_key//$'\n'/\\n}"
398
-
399
- if is_admin_service_placeholder "$service_project_id"; then
400
- echo -e "${RED}❌ Error: project_id in $admin_service_path is missing or placeholder${NC}"
401
- exit 1
402
- fi
403
-
404
- if is_admin_service_placeholder "$service_client_email" || [[ "$service_client_email" != *".gserviceaccount.com"* ]]; then
405
- echo -e "${RED}❌ Error: client_email in $admin_service_path is invalid${NC}"
406
- exit 1
407
- fi
408
-
409
- if is_admin_service_placeholder "$normalized_private_key" || [[ "$normalized_private_key" != *"-----BEGIN PRIVATE KEY-----"* ]] || [[ "$normalized_private_key" != *"-----END PRIVATE KEY-----"* ]]; then
410
- echo -e "${RED}❌ Error: private_key in $admin_service_path is invalid${NC}"
411
- exit 1
412
- fi
413
-
414
- PROJECT_ID="$service_project_id"
415
- export PROJECT_ID
416
- write_env_var "PROJECT_ID" "$PROJECT_ID"
417
-
418
- FIREBASE_SERVICE_ACCOUNT_EMAIL="$service_client_email"
419
- export FIREBASE_SERVICE_ACCOUNT_EMAIL
420
- write_env_var "FIREBASE_SERVICE_ACCOUNT_EMAIL" "$FIREBASE_SERVICE_ACCOUNT_EMAIL"
421
-
422
- FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY="$normalized_private_key"
423
- export FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY
424
- write_env_var "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" "$FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY"
425
-
426
- echo -e "${GREEN}✅ Imported Firebase service account credentials from $admin_service_path${NC}"
427
- }
428
-
429
- generate_manifest_signing_key_pair() {
430
- local private_key_file
431
- local public_key_file
432
- private_key_file=$(mktemp)
433
- public_key_file=$(mktemp)
434
-
435
- if ! node -e "const { generateKeyPairSync } = require('crypto'); const fs = require('fs'); const pair = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); fs.writeFileSync(process.argv[1], pair.privateKey, 'utf8'); fs.writeFileSync(process.argv[2], pair.publicKey, 'utf8');" "$private_key_file" "$public_key_file"; then
436
- rm -f "$private_key_file" "$public_key_file"
437
- return 1
438
- fi
439
-
440
- local private_key_pem
441
- local public_key_pem
442
- private_key_pem=$(cat "$private_key_file")
443
- public_key_pem=$(cat "$public_key_file")
444
- rm -f "$private_key_file" "$public_key_file"
445
-
446
- private_key_pem="${private_key_pem//$'\r'/}"
447
- public_key_pem="${public_key_pem//$'\r'/}"
448
-
449
- MANIFEST_SIGNING_PRIVATE_KEY="${private_key_pem//$'\n'/\\n}"
450
- MANIFEST_SIGNING_PUBLIC_KEY="${public_key_pem//$'\n'/\\n}"
451
-
452
- export MANIFEST_SIGNING_PRIVATE_KEY
453
- export MANIFEST_SIGNING_PUBLIC_KEY
454
-
455
- write_env_var "MANIFEST_SIGNING_PRIVATE_KEY" "$MANIFEST_SIGNING_PRIVATE_KEY"
456
- write_env_var "MANIFEST_SIGNING_PUBLIC_KEY" "$MANIFEST_SIGNING_PUBLIC_KEY"
457
-
458
- return 0
459
- }
460
-
461
- configure_manifest_signing_credentials() {
462
- echo -e "${BLUE}🛡️ MANIFEST SIGNING CONFIGURATION${NC}"
463
- echo "================================="
464
-
465
- local should_generate="false"
466
- local regenerate_choice=""
467
-
468
- if [ "$update_env" = "true" ]; then
469
- should_generate="true"
470
- elif [ -z "$MANIFEST_SIGNING_PRIVATE_KEY" ] || is_placeholder "$MANIFEST_SIGNING_PRIVATE_KEY" || [ -z "$MANIFEST_SIGNING_PUBLIC_KEY" ] || is_placeholder "$MANIFEST_SIGNING_PUBLIC_KEY"; then
471
- should_generate="true"
472
- else
473
- echo -e "${GREEN}Current manifest signing key pair: [HIDDEN]${NC}"
474
- read -p "Generate new manifest signing key pair? (press Enter to keep current, or type 'y' to regenerate): " regenerate_choice
475
- regenerate_choice=$(strip_carriage_returns "$regenerate_choice")
476
- if [ "$regenerate_choice" = "y" ] || [ "$regenerate_choice" = "Y" ]; then
477
- should_generate="true"
478
- fi
479
- fi
480
-
481
- if [ "$should_generate" = "true" ]; then
482
- echo -e "${YELLOW}Generating manifest signing RSA key pair...${NC}"
483
- if generate_manifest_signing_key_pair; then
484
- echo -e "${GREEN}✅ Manifest signing key pair generated${NC}"
485
- else
486
- echo -e "${RED}❌ Error: Failed to generate manifest signing key pair${NC}"
487
- exit 1
488
- fi
489
- else
490
- echo -e "${GREEN}✅ Keeping current manifest signing key pair${NC}"
491
- fi
492
-
493
- if [ -z "$MANIFEST_SIGNING_KEY_ID" ] || is_placeholder "$MANIFEST_SIGNING_KEY_ID" || [ "$should_generate" = "true" ]; then
494
- local generated_key_id
495
- generated_key_id=$(generate_worker_subdomain_label)
496
- if [ -z "$generated_key_id" ] || [ ${#generated_key_id} -ne 10 ]; then
497
- echo -e "${RED}❌ Error: Failed to generate MANIFEST_SIGNING_KEY_ID${NC}"
498
- exit 1
499
- fi
500
- MANIFEST_SIGNING_KEY_ID="$generated_key_id"
501
- export MANIFEST_SIGNING_KEY_ID
502
- write_env_var "MANIFEST_SIGNING_KEY_ID" "$MANIFEST_SIGNING_KEY_ID"
503
- echo -e "${GREEN}✅ MANIFEST_SIGNING_KEY_ID generated: $MANIFEST_SIGNING_KEY_ID${NC}"
504
- else
505
- echo -e "${GREEN}✅ MANIFEST_SIGNING_KEY_ID: $MANIFEST_SIGNING_KEY_ID${NC}"
506
- fi
507
-
508
- echo ""
509
- }
510
-
511
- generate_export_encryption_key_pair() {
512
- local private_key_file
513
- local public_key_file
514
- private_key_file=$(mktemp)
515
- public_key_file=$(mktemp)
516
-
517
- if ! node -e "const { generateKeyPairSync } = require('crypto'); const fs = require('fs'); const pair = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); fs.writeFileSync(process.argv[1], pair.privateKey, 'utf8'); fs.writeFileSync(process.argv[2], pair.publicKey, 'utf8');" "$private_key_file" "$public_key_file"; then
518
- rm -f "$private_key_file" "$public_key_file"
519
- return 1
520
- fi
521
-
522
- local private_key_pem
523
- local public_key_pem
524
- private_key_pem=$(cat "$private_key_file")
525
- public_key_pem=$(cat "$public_key_file")
526
- rm -f "$private_key_file" "$public_key_file"
527
-
528
- private_key_pem="${private_key_pem//$'\r'/}"
529
- public_key_pem="${public_key_pem//$'\r'/}"
530
-
531
- EXPORT_ENCRYPTION_PRIVATE_KEY="${private_key_pem//$'\n'/\\n}"
532
- EXPORT_ENCRYPTION_PUBLIC_KEY="${public_key_pem//$'\n'/\\n}"
533
-
534
- export EXPORT_ENCRYPTION_PRIVATE_KEY
535
- export EXPORT_ENCRYPTION_PUBLIC_KEY
536
-
537
- write_env_var "EXPORT_ENCRYPTION_PRIVATE_KEY" "$EXPORT_ENCRYPTION_PRIVATE_KEY"
538
- write_env_var "EXPORT_ENCRYPTION_PUBLIC_KEY" "$EXPORT_ENCRYPTION_PUBLIC_KEY"
539
-
540
- return 0
541
- }
542
-
543
- configure_export_encryption_credentials() {
544
- echo -e "${BLUE}🔐 EXPORT ENCRYPTION CONFIGURATION${NC}"
545
- echo "================================="
546
-
547
- local should_generate="false"
548
- local regenerate_choice=""
549
-
550
- if [ "$update_env" = "true" ]; then
551
- should_generate="true"
552
- elif [ -z "$EXPORT_ENCRYPTION_PRIVATE_KEY" ] || is_placeholder "$EXPORT_ENCRYPTION_PRIVATE_KEY" || [ -z "$EXPORT_ENCRYPTION_PUBLIC_KEY" ] || is_placeholder "$EXPORT_ENCRYPTION_PUBLIC_KEY"; then
553
- should_generate="true"
554
- else
555
- echo -e "${GREEN}Current export encryption key pair: [HIDDEN]${NC}"
556
- read -p "Generate new export encryption key pair? (press Enter to keep current, or type 'y' to regenerate): " regenerate_choice
557
- regenerate_choice=$(strip_carriage_returns "$regenerate_choice")
558
- if [ "$regenerate_choice" = "y" ] || [ "$regenerate_choice" = "Y" ]; then
559
- should_generate="true"
560
- fi
561
- fi
562
-
563
- if [ "$should_generate" = "true" ]; then
564
- echo -e "${YELLOW}Generating export encryption RSA key pair...${NC}"
565
- if generate_export_encryption_key_pair; then
566
- echo -e "${GREEN}✅ Export encryption key pair generated${NC}"
567
- else
568
- echo -e "${RED}❌ Error: Failed to generate export encryption key pair${NC}"
569
- exit 1
570
- fi
571
- else
572
- echo -e "${GREEN}✅ Keeping current export encryption key pair${NC}"
573
- fi
574
-
575
- if [ -z "$EXPORT_ENCRYPTION_KEY_ID" ] || is_placeholder "$EXPORT_ENCRYPTION_KEY_ID" || [ "$should_generate" = "true" ]; then
576
- local generated_key_id
577
- generated_key_id=$(generate_worker_subdomain_label)
578
- if [ -z "$generated_key_id" ] || [ ${#generated_key_id} -ne 10 ]; then
579
- echo -e "${RED}❌ Error: Failed to generate EXPORT_ENCRYPTION_KEY_ID${NC}"
580
- exit 1
581
- fi
582
- EXPORT_ENCRYPTION_KEY_ID="$generated_key_id"
583
- export EXPORT_ENCRYPTION_KEY_ID
584
- write_env_var "EXPORT_ENCRYPTION_KEY_ID" "$EXPORT_ENCRYPTION_KEY_ID"
585
- echo -e "${GREEN}✅ EXPORT_ENCRYPTION_KEY_ID generated: $EXPORT_ENCRYPTION_KEY_ID${NC}"
586
- else
587
- echo -e "${GREEN}✅ EXPORT_ENCRYPTION_KEY_ID: $EXPORT_ENCRYPTION_KEY_ID${NC}"
588
- fi
589
-
590
- echo ""
591
- }
163
+ DEPLOY_CONFIG_MODULES_DIR="$SCRIPT_DIR/deploy-config/modules"
164
+ DEPLOY_CONFIG_ENV_UTILS_MODULE="$DEPLOY_CONFIG_MODULES_DIR/env-utils.sh"
592
165
 
593
- generate_data_at_rest_encryption_key_pair() {
594
- local private_key_file
595
- local public_key_file
596
- private_key_file=$(mktemp)
597
- public_key_file=$(mktemp)
598
-
599
- if ! node -e "const { generateKeyPairSync } = require('crypto'); const fs = require('fs'); const pair = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); fs.writeFileSync(process.argv[1], pair.privateKey, 'utf8'); fs.writeFileSync(process.argv[2], pair.publicKey, 'utf8');" "$private_key_file" "$public_key_file"; then
600
- rm -f "$private_key_file" "$public_key_file"
601
- return 1
602
- fi
603
-
604
- local private_key_pem
605
- local public_key_pem
606
- private_key_pem=$(cat "$private_key_file")
607
- public_key_pem=$(cat "$public_key_file")
608
- rm -f "$private_key_file" "$public_key_file"
609
-
610
- private_key_pem="${private_key_pem//$'\r'/}"
611
- public_key_pem="${public_key_pem//$'\r'/}"
612
-
613
- DATA_AT_REST_ENCRYPTION_PRIVATE_KEY="${private_key_pem//$'\n'/\\n}"
614
- DATA_AT_REST_ENCRYPTION_PUBLIC_KEY="${public_key_pem//$'\n'/\\n}"
615
-
616
- export DATA_AT_REST_ENCRYPTION_PRIVATE_KEY
617
- export DATA_AT_REST_ENCRYPTION_PUBLIC_KEY
618
-
619
- write_env_var "DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY"
620
- write_env_var "DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY"
621
-
622
- return 0
623
- }
624
-
625
- configure_data_at_rest_encryption_credentials() {
626
- echo -e "${BLUE}🗃️ DATA-AT-REST ENCRYPTION CONFIGURATION${NC}"
627
- echo "========================================"
628
-
629
- local current_enabled="${DATA_AT_REST_ENCRYPTION_ENABLED:-}"
630
- local normalized_enabled=""
631
- local enable_choice=""
632
-
633
- current_enabled=$(strip_carriage_returns "$current_enabled")
634
- normalized_enabled=$(printf '%s' "$current_enabled" | tr '[:upper:]' '[:lower:]')
635
-
636
- if [ -z "$normalized_enabled" ] || is_placeholder "$normalized_enabled"; then
637
- DATA_AT_REST_ENCRYPTION_ENABLED="false"
638
- elif [ "$normalized_enabled" = "1" ] || [ "$normalized_enabled" = "true" ] || [ "$normalized_enabled" = "yes" ] || [ "$normalized_enabled" = "on" ]; then
639
- DATA_AT_REST_ENCRYPTION_ENABLED="true"
640
- else
641
- DATA_AT_REST_ENCRYPTION_ENABLED="false"
642
- fi
643
-
644
- if [ "$update_env" != "true" ]; then
645
- read -p "Enable data-at-rest encryption for new writes? (y/N, Enter keeps current): " enable_choice
646
- enable_choice=$(strip_carriage_returns "$enable_choice")
647
- case "$enable_choice" in
648
- y|Y|yes|YES)
649
- DATA_AT_REST_ENCRYPTION_ENABLED="true"
650
- ;;
651
- n|N|no|NO)
652
- DATA_AT_REST_ENCRYPTION_ENABLED="false"
653
- ;;
654
- "")
655
- ;;
656
- *)
657
- echo -e "${YELLOW}⚠️ Unrecognized choice '$enable_choice'; keeping current setting${NC}"
658
- ;;
659
- esac
660
- fi
661
-
662
- export DATA_AT_REST_ENCRYPTION_ENABLED
663
- write_env_var "DATA_AT_REST_ENCRYPTION_ENABLED" "$DATA_AT_REST_ENCRYPTION_ENABLED"
664
- echo -e "${GREEN}✅ DATA_AT_REST_ENCRYPTION_ENABLED: $DATA_AT_REST_ENCRYPTION_ENABLED${NC}"
665
-
666
- local should_generate="false"
667
- local regenerate_choice=""
668
-
669
- if [ "$update_env" = "true" ]; then
670
- should_generate="true"
671
- elif [ -z "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" || [ -z "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY"; then
672
- should_generate="true"
673
- else
674
- echo -e "${GREEN}Current data-at-rest encryption key pair: [HIDDEN]${NC}"
675
- read -p "Generate new data-at-rest encryption key pair? (press Enter to keep current, or type 'y' to regenerate): " regenerate_choice
676
- regenerate_choice=$(strip_carriage_returns "$regenerate_choice")
677
- if [ "$regenerate_choice" = "y" ] || [ "$regenerate_choice" = "Y" ]; then
678
- should_generate="true"
679
- fi
680
- fi
681
-
682
- if [ "$should_generate" = "true" ]; then
683
- echo -e "${YELLOW}Generating data-at-rest encryption RSA key pair...${NC}"
684
- if generate_data_at_rest_encryption_key_pair; then
685
- echo -e "${GREEN}✅ Data-at-rest encryption key pair generated${NC}"
686
- else
687
- echo -e "${RED}❌ Error: Failed to generate data-at-rest encryption key pair${NC}"
688
- exit 1
689
- fi
690
- else
691
- echo -e "${GREEN}✅ Keeping current data-at-rest encryption key pair${NC}"
692
- fi
693
-
694
- if [ -z "$DATA_AT_REST_ENCRYPTION_KEY_ID" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_KEY_ID" || [ "$should_generate" = "true" ]; then
695
- local generated_key_id
696
- generated_key_id=$(generate_worker_subdomain_label)
697
- if [ -z "$generated_key_id" ] || [ ${#generated_key_id} -ne 10 ]; then
698
- echo -e "${RED}❌ Error: Failed to generate DATA_AT_REST_ENCRYPTION_KEY_ID${NC}"
699
- exit 1
700
- fi
701
- DATA_AT_REST_ENCRYPTION_KEY_ID="$generated_key_id"
702
- export DATA_AT_REST_ENCRYPTION_KEY_ID
703
- write_env_var "DATA_AT_REST_ENCRYPTION_KEY_ID" "$DATA_AT_REST_ENCRYPTION_KEY_ID"
704
- echo -e "${GREEN}✅ DATA_AT_REST_ENCRYPTION_KEY_ID generated: $DATA_AT_REST_ENCRYPTION_KEY_ID${NC}"
705
- else
706
- echo -e "${GREEN}✅ DATA_AT_REST_ENCRYPTION_KEY_ID: $DATA_AT_REST_ENCRYPTION_KEY_ID${NC}"
707
- fi
708
-
709
- echo ""
710
- }
711
-
712
- validate_data_at_rest_encryption_settings() {
713
- local enabled_normalized
714
- enabled_normalized=$(printf '%s' "${DATA_AT_REST_ENCRYPTION_ENABLED:-false}" | tr '[:upper:]' '[:lower:]')
715
-
716
- if [ "$enabled_normalized" = "1" ] || [ "$enabled_normalized" = "true" ] || [ "$enabled_normalized" = "yes" ] || [ "$enabled_normalized" = "on" ]; then
717
- if [ -z "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY"; then
718
- echo -e "${RED}❌ Error: DATA_AT_REST_ENCRYPTION_PRIVATE_KEY is required when DATA_AT_REST_ENCRYPTION_ENABLED is true${NC}"
719
- exit 1
720
- fi
721
-
722
- if [ -z "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY"; then
723
- echo -e "${RED}❌ Error: DATA_AT_REST_ENCRYPTION_PUBLIC_KEY is required when DATA_AT_REST_ENCRYPTION_ENABLED is true${NC}"
724
- exit 1
725
- fi
726
-
727
- if [ -z "$DATA_AT_REST_ENCRYPTION_KEY_ID" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_KEY_ID"; then
728
- echo -e "${RED}❌ Error: DATA_AT_REST_ENCRYPTION_KEY_ID is required when DATA_AT_REST_ENCRYPTION_ENABLED is true${NC}"
729
- exit 1
730
- fi
731
- fi
732
- }
733
-
734
- # Validate required variables
735
- required_vars=(
736
- # Core Cloudflare Configuration
737
- "ACCOUNT_ID"
738
-
739
- # Shared Authentication & Storage
740
- "USER_DB_AUTH"
741
- "R2_KEY_SECRET"
742
- "IMAGES_API_TOKEN"
743
-
744
- # Firebase Auth Configuration
745
- "API_KEY"
746
- "AUTH_DOMAIN"
747
- "PROJECT_ID"
748
- "STORAGE_BUCKET"
749
- "MESSAGING_SENDER_ID"
750
- "APP_ID"
751
- "MEASUREMENT_ID"
752
- "FIREBASE_SERVICE_ACCOUNT_EMAIL"
753
- "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY"
754
-
755
- # Pages Configuration
756
- "PAGES_PROJECT_NAME"
757
- "PAGES_CUSTOM_DOMAIN"
758
-
759
- # Worker Names (required for config replacement)
760
- "KEYS_WORKER_NAME"
761
- "USER_WORKER_NAME"
762
- "DATA_WORKER_NAME"
763
- "AUDIT_WORKER_NAME"
764
- "IMAGES_WORKER_NAME"
765
- "PDF_WORKER_NAME"
766
-
767
- # Worker Domains (required for proxy/env secrets and worker fallbacks)
768
- "KEYS_WORKER_DOMAIN"
769
- "USER_WORKER_DOMAIN"
770
- "DATA_WORKER_DOMAIN"
771
- "AUDIT_WORKER_DOMAIN"
772
- "IMAGES_WORKER_DOMAIN"
773
- "PDF_WORKER_DOMAIN"
774
-
775
- # Storage Configuration (required for config replacement)
776
- "DATA_BUCKET_NAME"
777
- "AUDIT_BUCKET_NAME"
778
- "FILES_BUCKET_NAME"
779
- "KV_STORE_ID"
780
-
781
- # Worker-Specific Secrets (required for deployment)
782
- "KEYS_AUTH"
783
- "PDF_WORKER_AUTH"
784
- "ACCOUNT_HASH"
785
- "BROWSER_API_TOKEN"
786
- "MANIFEST_SIGNING_PRIVATE_KEY"
787
- "MANIFEST_SIGNING_KEY_ID"
788
- "MANIFEST_SIGNING_PUBLIC_KEY"
789
- "EXPORT_ENCRYPTION_PRIVATE_KEY"
790
- "EXPORT_ENCRYPTION_KEY_ID"
791
- "EXPORT_ENCRYPTION_PUBLIC_KEY"
792
- )
793
-
794
- validate_required_vars() {
795
- echo -e "${YELLOW}🔍 Validating required environment variables...${NC}"
796
- for var in "${required_vars[@]}"; do
797
- if [ -z "${!var}" ] || is_placeholder "${!var}"; then
798
- echo -e "${RED}❌ Error: $var is not set in .env file or is a placeholder${NC}"
799
- exit 1
800
- fi
801
- done
802
- echo -e "${GREEN}✅ All required variables found${NC}"
803
- }
804
-
805
- assert_file_exists() {
806
- local file_path=$1
807
-
808
- if [ ! -f "$file_path" ]; then
809
- echo -e "${RED}❌ Error: required file is missing: $file_path${NC}"
810
- exit 1
811
- fi
812
- }
813
-
814
- assert_contains_literal() {
815
- local file_path=$1
816
- local literal=$2
817
- local description=$3
818
-
819
- if ! grep -Fq -- "$literal" "$file_path"; then
820
- echo -e "${RED}❌ Error: ${description}${NC}"
821
- echo -e "${YELLOW} Expected to find '$literal' in $file_path${NC}"
822
- exit 1
823
- fi
824
- }
825
-
826
- assert_no_match_in_file() {
827
- local file_path=$1
828
- local pattern=$2
829
- local description=$3
830
- local matches
831
-
832
- matches=$(grep -En "$pattern" "$file_path" | head -n 3 || true)
833
- if [ -n "$matches" ]; then
834
- echo -e "${RED}❌ Error: ${description}${NC}"
835
- echo -e "${YELLOW} First matching lines in $file_path:${NC}"
836
- echo "$matches"
837
- exit 1
838
- fi
839
- }
840
-
841
- validate_json_file() {
842
- local file_path=$1
843
-
844
- if ! node -e "const fs=require('fs'); JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));" "$file_path" > /dev/null 2>&1; then
845
- echo -e "${RED}❌ Error: invalid JSON in $file_path${NC}"
846
- exit 1
847
- fi
848
- }
849
-
850
- validate_domain_var() {
851
- local var_name=$1
852
- local value="${!var_name}"
853
- local normalized
854
-
855
- value=$(strip_carriage_returns "$value")
856
- normalized=$(normalize_domain_value "$value")
857
-
858
- if [ -z "$value" ] || is_placeholder "$value"; then
859
- echo -e "${RED}❌ Error: $var_name is missing or placeholder${NC}"
860
- exit 1
861
- fi
862
-
863
- if [ "$value" != "$normalized" ]; then
864
- echo -e "${RED}❌ Error: $var_name must not include protocol, trailing slash, or surrounding whitespace${NC}"
865
- echo -e "${YELLOW} Use '$normalized' instead${NC}"
866
- exit 1
867
- fi
868
-
869
- if [[ "$value" == */* ]]; then
870
- echo -e "${RED}❌ Error: $var_name must be a bare domain (no path segments)${NC}"
871
- exit 1
872
- fi
873
- }
874
-
875
- validate_env_value_formats() {
876
- echo -e "${YELLOW}🔍 Validating environment value formats...${NC}"
877
-
878
- validate_domain_var "PAGES_CUSTOM_DOMAIN"
879
- validate_domain_var "KEYS_WORKER_DOMAIN"
880
- validate_domain_var "USER_WORKER_DOMAIN"
881
- validate_domain_var "DATA_WORKER_DOMAIN"
882
- validate_domain_var "AUDIT_WORKER_DOMAIN"
883
- validate_domain_var "IMAGES_WORKER_DOMAIN"
884
- validate_domain_var "PDF_WORKER_DOMAIN"
885
-
886
- if ! [[ "$KV_STORE_ID" =~ ^([0-9a-fA-F]{32}|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$ ]]; then
887
- echo -e "${RED}❌ Error: KV_STORE_ID must be a 32-character hex namespace ID (or UUID format)${NC}"
888
- exit 1
889
- fi
890
-
891
- if [[ "$ACCOUNT_ID" =~ [[:space:]] ]]; then
892
- echo -e "${RED}❌ Error: ACCOUNT_ID must not contain whitespace${NC}"
893
- exit 1
894
- fi
895
-
896
- echo -e "${GREEN}✅ Environment value formats look valid${NC}"
897
- }
898
-
899
- validate_env_file_entries() {
900
- local var_name
901
- local escaped_var_name
902
- local count
903
-
904
- echo -e "${YELLOW}🔍 Verifying required .env entries...${NC}"
905
- for var_name in "${required_vars[@]}"; do
906
- escaped_var_name=$(escape_for_sed_pattern "$var_name")
907
- count=$(grep -c "^$escaped_var_name=" .env || true)
908
-
909
- if [ "$count" -lt 1 ]; then
910
- echo -e "${RED}❌ Error: missing .env entry for $var_name${NC}"
911
- exit 1
912
- fi
913
- done
914
- echo -e "${GREEN}✅ Required .env entries found${NC}"
915
- }
916
-
917
- validate_generated_configs() {
918
- echo -e "${YELLOW}🔍 Running generated configuration checkpoint validations...${NC}"
919
-
920
- local required_files=(
921
- "wrangler.toml"
922
- "app/config/config.json"
923
- "app/config/firebase.ts"
924
- "app/config/admin-service.json"
925
- "app/routes/auth/login.tsx"
926
- "app/routes/auth/login.module.css"
927
- "workers/audit-worker/wrangler.jsonc"
928
- "workers/data-worker/wrangler.jsonc"
929
- "workers/image-worker/wrangler.jsonc"
930
- "workers/keys-worker/wrangler.jsonc"
931
- "workers/pdf-worker/wrangler.jsonc"
932
- "workers/user-worker/wrangler.jsonc"
933
- "workers/audit-worker/src/audit-worker.ts"
934
- "workers/data-worker/src/data-worker.ts"
935
- "workers/image-worker/src/image-worker.ts"
936
- "workers/keys-worker/src/keys.ts"
937
- "workers/pdf-worker/src/pdf-worker.ts"
938
- "workers/user-worker/src/user-worker.ts"
939
- )
940
-
941
- local file_path
942
- for file_path in "${required_files[@]}"; do
943
- assert_file_exists "$file_path"
944
- done
945
-
946
- validate_json_file "app/config/config.json"
947
- validate_json_file "app/config/admin-service.json"
948
-
949
- assert_contains_literal "wrangler.toml" "\"$PAGES_PROJECT_NAME\"" "PAGES_PROJECT_NAME was not applied to wrangler.toml"
950
-
951
- assert_contains_literal "workers/keys-worker/wrangler.jsonc" "$KEYS_WORKER_NAME" "KEYS_WORKER_NAME was not applied"
952
- assert_contains_literal "workers/user-worker/wrangler.jsonc" "$USER_WORKER_NAME" "USER_WORKER_NAME was not applied"
953
- assert_contains_literal "workers/data-worker/wrangler.jsonc" "$DATA_WORKER_NAME" "DATA_WORKER_NAME was not applied"
954
- assert_contains_literal "workers/audit-worker/wrangler.jsonc" "$AUDIT_WORKER_NAME" "AUDIT_WORKER_NAME was not applied"
955
- assert_contains_literal "workers/image-worker/wrangler.jsonc" "$IMAGES_WORKER_NAME" "IMAGES_WORKER_NAME was not applied"
956
- assert_contains_literal "workers/pdf-worker/wrangler.jsonc" "$PDF_WORKER_NAME" "PDF_WORKER_NAME was not applied"
957
-
958
- assert_contains_literal "workers/keys-worker/wrangler.jsonc" "$ACCOUNT_ID" "ACCOUNT_ID missing in keys worker config"
959
- assert_contains_literal "workers/user-worker/wrangler.jsonc" "$ACCOUNT_ID" "ACCOUNT_ID missing in user worker config"
960
- assert_contains_literal "workers/data-worker/wrangler.jsonc" "$ACCOUNT_ID" "ACCOUNT_ID missing in data worker config"
961
- assert_contains_literal "workers/audit-worker/wrangler.jsonc" "$ACCOUNT_ID" "ACCOUNT_ID missing in audit worker config"
962
- assert_contains_literal "workers/image-worker/wrangler.jsonc" "$ACCOUNT_ID" "ACCOUNT_ID missing in image worker config"
963
- assert_contains_literal "workers/pdf-worker/wrangler.jsonc" "$ACCOUNT_ID" "ACCOUNT_ID missing in pdf worker config"
964
-
965
- assert_contains_literal "workers/data-worker/wrangler.jsonc" "$DATA_BUCKET_NAME" "DATA_BUCKET_NAME missing in data worker config"
966
- assert_contains_literal "workers/audit-worker/wrangler.jsonc" "$AUDIT_BUCKET_NAME" "AUDIT_BUCKET_NAME missing in audit worker config"
967
- assert_contains_literal "workers/image-worker/wrangler.jsonc" "$FILES_BUCKET_NAME" "FILES_BUCKET_NAME missing in image worker config"
968
- assert_contains_literal "workers/user-worker/wrangler.jsonc" "$KV_STORE_ID" "KV_STORE_ID missing in user worker config"
969
-
970
- assert_contains_literal "app/config/config.json" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in app/config/config.json"
971
- assert_contains_literal "app/config/config.json" "$ACCOUNT_HASH" "ACCOUNT_HASH missing in app/config/config.json"
972
- assert_contains_literal "app/config/config.json" "$EXPORT_ENCRYPTION_KEY_ID" "EXPORT_ENCRYPTION_KEY_ID missing in app/config/config.json"
973
- assert_contains_literal "app/config/config.json" "\"export_encryption_public_key\":" "export_encryption_public_key missing in app/config/config.json"
974
- assert_contains_literal "app/routes/auth/login.tsx" "const APP_CANONICAL_ORIGIN = 'https://$PAGES_CUSTOM_DOMAIN';" "PAGES_CUSTOM_DOMAIN missing in app/routes/auth/login.tsx canonical origin"
166
+ if [ ! -f "$DEPLOY_CONFIG_ENV_UTILS_MODULE" ]; then
167
+ echo -e "${RED}❌ Error: Required deploy-config module not found: $DEPLOY_CONFIG_ENV_UTILS_MODULE${NC}"
168
+ exit 1
169
+ fi
975
170
 
976
- assert_contains_literal "app/config/firebase.ts" "$API_KEY" "API_KEY missing in app/config/firebase.ts"
977
- assert_contains_literal "app/config/firebase.ts" "$AUTH_DOMAIN" "AUTH_DOMAIN missing in app/config/firebase.ts"
978
- assert_contains_literal "app/config/firebase.ts" "$PROJECT_ID" "PROJECT_ID missing in app/config/firebase.ts"
979
- assert_contains_literal "app/config/firebase.ts" "$STORAGE_BUCKET" "STORAGE_BUCKET missing in app/config/firebase.ts"
980
- assert_contains_literal "app/config/firebase.ts" "$MESSAGING_SENDER_ID" "MESSAGING_SENDER_ID missing in app/config/firebase.ts"
981
- assert_contains_literal "app/config/firebase.ts" "$APP_ID" "APP_ID missing in app/config/firebase.ts"
982
- assert_contains_literal "app/config/firebase.ts" "$MEASUREMENT_ID" "MEASUREMENT_ID missing in app/config/firebase.ts"
171
+ source "$DEPLOY_CONFIG_ENV_UTILS_MODULE"
983
172
 
984
- assert_contains_literal "workers/audit-worker/src/audit-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in audit-worker source"
985
- assert_contains_literal "workers/data-worker/src/data-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in data-worker source"
986
- assert_contains_literal "workers/image-worker/src/image-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in image-worker source"
987
- assert_contains_literal "workers/keys-worker/src/keys.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in keys-worker source"
988
- assert_contains_literal "workers/pdf-worker/src/pdf-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in pdf-worker source"
989
- assert_contains_literal "workers/user-worker/src/user-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in user-worker source"
173
+ DEPLOY_CONFIG_KEYS_MODULE="$DEPLOY_CONFIG_MODULES_DIR/keys.sh"
174
+ DEPLOY_CONFIG_VALIDATION_MODULE="$DEPLOY_CONFIG_MODULES_DIR/validation.sh"
175
+ DEPLOY_CONFIG_SCAFFOLDING_MODULE="$DEPLOY_CONFIG_MODULES_DIR/scaffolding.sh"
176
+ DEPLOY_CONFIG_PROMPT_MODULE="$DEPLOY_CONFIG_MODULES_DIR/prompt.sh"
990
177
 
991
- local placeholder_pattern
992
- placeholder_pattern="(\"(ACCOUNT_ID|PAGES_PROJECT_NAME|PAGES_CUSTOM_DOMAIN|KEYS_WORKER_NAME|USER_WORKER_NAME|DATA_WORKER_NAME|AUDIT_WORKER_NAME|IMAGES_WORKER_NAME|PDF_WORKER_NAME|KEYS_WORKER_DOMAIN|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|ACCOUNT_HASH|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|IMAGES_WORKER_DOMAIN)')"
178
+ if [ ! -f "$DEPLOY_CONFIG_KEYS_MODULE" ]; then
179
+ echo -e "${RED}❌ Error: Required deploy-config module not found: $DEPLOY_CONFIG_KEYS_MODULE${NC}"
180
+ exit 1
181
+ fi
993
182
 
994
- local files_to_scan=(
995
- "wrangler.toml"
996
- "workers/audit-worker/wrangler.jsonc"
997
- "workers/data-worker/wrangler.jsonc"
998
- "workers/image-worker/wrangler.jsonc"
999
- "workers/keys-worker/wrangler.jsonc"
1000
- "workers/pdf-worker/wrangler.jsonc"
1001
- "workers/user-worker/wrangler.jsonc"
1002
- "workers/audit-worker/src/audit-worker.ts"
1003
- "workers/data-worker/src/data-worker.ts"
1004
- "workers/image-worker/src/image-worker.ts"
1005
- "workers/keys-worker/src/keys.ts"
1006
- "workers/pdf-worker/src/pdf-worker.ts"
1007
- "workers/user-worker/src/user-worker.ts"
1008
- "app/config/config.json"
1009
- "app/config/firebase.ts"
1010
- "app/routes/auth/login.tsx"
1011
- )
183
+ if [ ! -f "$DEPLOY_CONFIG_VALIDATION_MODULE" ]; then
184
+ echo -e "${RED}❌ Error: Required deploy-config module not found: $DEPLOY_CONFIG_VALIDATION_MODULE${NC}"
185
+ exit 1
186
+ fi
1012
187
 
1013
- for file_path in "${files_to_scan[@]}"; do
1014
- assert_no_match_in_file "$file_path" "$placeholder_pattern" "Unresolved placeholder token found after config update"
1015
- done
188
+ if [ ! -f "$DEPLOY_CONFIG_SCAFFOLDING_MODULE" ]; then
189
+ echo -e "${RED}❌ Error: Required deploy-config module not found: $DEPLOY_CONFIG_SCAFFOLDING_MODULE${NC}"
190
+ exit 1
191
+ fi
1016
192
 
1017
- echo -e "${GREEN}✅ Generated configuration checkpoint validation passed${NC}"
1018
- }
193
+ if [ ! -f "$DEPLOY_CONFIG_PROMPT_MODULE" ]; then
194
+ echo -e "${RED}❌ Error: Required deploy-config module not found: $DEPLOY_CONFIG_PROMPT_MODULE${NC}"
195
+ exit 1
196
+ fi
1019
197
 
1020
- run_validation_checkpoint() {
1021
- validate_required_vars
1022
- validate_env_value_formats
1023
- validate_env_file_entries
1024
- validate_data_at_rest_encryption_settings
1025
- validate_generated_configs
1026
- }
198
+ source "$DEPLOY_CONFIG_KEYS_MODULE"
199
+ source "$DEPLOY_CONFIG_VALIDATION_MODULE"
200
+ source "$DEPLOY_CONFIG_SCAFFOLDING_MODULE"
201
+ source "$DEPLOY_CONFIG_PROMPT_MODULE"
1027
202
 
1028
203
  if [ "$validate_only" = "true" ]; then
1029
204
  echo -e "\n${BLUE}🧪 Validate-only mode enabled${NC}"
@@ -1032,196 +207,6 @@ if [ "$validate_only" = "true" ]; then
1032
207
  exit 0
1033
208
  fi
1034
209
 
1035
- # Function to copy example configuration files
1036
- copy_example_configs() {
1037
- echo -e "\n${BLUE}📋 Copying example configuration files...${NC}"
1038
-
1039
- # Copy app configuration files
1040
- echo -e "${YELLOW} Copying app configuration files...${NC}"
1041
-
1042
- # Copy app config-example directory to config (always sync non-admin files)
1043
- if [ -d "app/config-example" ]; then
1044
- local admin_service_backup=""
1045
- local copied_config_files=0
1046
- local skipped_existing_files=0
1047
-
1048
- if [ -f "app/config/admin-service.json" ]; then
1049
- admin_service_backup=$(mktemp)
1050
- cp "app/config/admin-service.json" "$admin_service_backup"
1051
- fi
1052
-
1053
- if [ "$update_env" = "true" ]; then
1054
- rm -rf app/config
1055
- fi
1056
-
1057
- mkdir -p app/config
1058
-
1059
- while IFS= read -r source_file; do
1060
- local relative_path
1061
- local destination_file
1062
- relative_path="${source_file#app/config-example/}"
1063
- destination_file="app/config/$relative_path"
1064
-
1065
- mkdir -p "$(dirname "$destination_file")"
1066
-
1067
- if [ "$update_env" = "true" ] || [ ! -f "$destination_file" ]; then
1068
- cp "$source_file" "$destination_file"
1069
- copied_config_files=$((copied_config_files + 1))
1070
- else
1071
- skipped_existing_files=$((skipped_existing_files + 1))
1072
- fi
1073
- done < <(find app/config-example -type f ! -name "admin-service.json")
1074
-
1075
- # Ensure example credentials are never copied from config-example.
1076
- rm -f app/config/admin-service.json
1077
-
1078
- if [ -n "$admin_service_backup" ] && [ -f "$admin_service_backup" ]; then
1079
- cp "$admin_service_backup" "app/config/admin-service.json"
1080
- rm -f "$admin_service_backup"
1081
- echo -e "${GREEN} ✅ app: preserved existing admin-service.json${NC}"
1082
- else
1083
- echo -e "${YELLOW} ⚠️ app: skipped copying admin-service.json (provide your own credentials file)${NC}"
1084
- fi
1085
-
1086
- if [ "$update_env" = "true" ]; then
1087
- echo -e "${GREEN} ✅ app: config directory reset from config-example (excluding admin-service.json)${NC}"
1088
- else
1089
- echo -e "${GREEN} ✅ app: synced missing files from config-example (excluding admin-service.json)${NC}"
1090
- fi
1091
-
1092
- if [ "$skipped_existing_files" -gt 0 ]; then
1093
- echo -e "${YELLOW} ℹ️ app: kept $skipped_existing_files existing config file(s)${NC}"
1094
- fi
1095
-
1096
- echo -e "${GREEN} ✅ app: copied $copied_config_files config file(s) from config-example${NC}"
1097
- fi
1098
-
1099
- # Copy auth route template files
1100
- echo -e "${YELLOW} Copying auth route template files...${NC}"
1101
-
1102
- if [ -f "app/routes/auth/login.example.tsx" ] && { [ "$update_env" = "true" ] || [ ! -f "app/routes/auth/login.tsx" ]; }; then
1103
- cp app/routes/auth/login.example.tsx app/routes/auth/login.tsx
1104
- echo -e "${GREEN} ✅ auth: login.tsx created from example${NC}"
1105
- elif [ -f "app/routes/auth/login.tsx" ]; then
1106
- echo -e "${YELLOW} ⚠️ auth: login.tsx already exists, skipping copy${NC}"
1107
- fi
1108
-
1109
- if [ -f "app/routes/auth/login.module.example.css" ] && { [ "$update_env" = "true" ] || [ ! -f "app/routes/auth/login.module.css" ]; }; then
1110
- cp app/routes/auth/login.module.example.css app/routes/auth/login.module.css
1111
- echo -e "${GREEN} ✅ auth: login.module.css created from example${NC}"
1112
- elif [ -f "app/routes/auth/login.module.css" ]; then
1113
- echo -e "${YELLOW} ⚠️ auth: login.module.css already exists, skipping copy${NC}"
1114
- fi
1115
-
1116
- # Navigate to each worker directory and copy the example file
1117
- echo -e "${YELLOW} Copying worker configuration files...${NC}"
1118
-
1119
- cd workers/keys-worker
1120
- if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
1121
- cp wrangler.jsonc.example wrangler.jsonc
1122
- echo -e "${GREEN} ✅ keys-worker: wrangler.jsonc created from example${NC}"
1123
- elif [ -f "wrangler.jsonc" ]; then
1124
- echo -e "${YELLOW} ⚠️ keys-worker: wrangler.jsonc already exists, skipping copy${NC}"
1125
- fi
1126
-
1127
- cd ../user-worker
1128
- if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
1129
- cp wrangler.jsonc.example wrangler.jsonc
1130
- echo -e "${GREEN} ✅ user-worker: wrangler.jsonc created from example${NC}"
1131
- elif [ -f "wrangler.jsonc" ]; then
1132
- echo -e "${YELLOW} ⚠️ user-worker: wrangler.jsonc already exists, skipping copy${NC}"
1133
- fi
1134
-
1135
- cd ../data-worker
1136
- if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
1137
- cp wrangler.jsonc.example wrangler.jsonc
1138
- echo -e "${GREEN} ✅ data-worker: wrangler.jsonc created from example${NC}"
1139
- elif [ -f "wrangler.jsonc" ]; then
1140
- echo -e "${YELLOW} ⚠️ data-worker: wrangler.jsonc already exists, skipping copy${NC}"
1141
- fi
1142
-
1143
- cd ../audit-worker
1144
- if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
1145
- cp wrangler.jsonc.example wrangler.jsonc
1146
- echo -e "${GREEN} ✅ audit-worker: wrangler.jsonc created from example${NC}"
1147
- elif [ -f "wrangler.jsonc" ]; then
1148
- echo -e "${YELLOW} ⚠️ audit-worker: wrangler.jsonc already exists, skipping copy${NC}"
1149
- fi
1150
-
1151
- cd ../image-worker
1152
- if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
1153
- cp wrangler.jsonc.example wrangler.jsonc
1154
- echo -e "${GREEN} ✅ image-worker: wrangler.jsonc created from example${NC}"
1155
- elif [ -f "wrangler.jsonc" ]; then
1156
- echo -e "${YELLOW} ⚠️ image-worker: wrangler.jsonc already exists, skipping copy${NC}"
1157
- fi
1158
-
1159
- cd ../pdf-worker
1160
- if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
1161
- cp wrangler.jsonc.example wrangler.jsonc
1162
- echo -e "${GREEN} ✅ pdf-worker: wrangler.jsonc created from example${NC}"
1163
- elif [ -f "wrangler.jsonc" ]; then
1164
- echo -e "${YELLOW} ⚠️ pdf-worker: wrangler.jsonc already exists, skipping copy${NC}"
1165
- fi
1166
-
1167
- # Return to project root
1168
- cd ../..
1169
-
1170
- # Copy worker source template files
1171
- echo -e "${YELLOW} Copying worker source template files...${NC}"
1172
-
1173
- if [ -f "workers/keys-worker/src/keys.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/keys-worker/src/keys.ts" ]; }; then
1174
- cp workers/keys-worker/src/keys.example.ts workers/keys-worker/src/keys.ts
1175
- echo -e "${GREEN} ✅ keys-worker: keys.ts created from example${NC}"
1176
- elif [ -f "workers/keys-worker/src/keys.ts" ]; then
1177
- echo -e "${YELLOW} ⚠️ keys-worker: keys.ts already exists, skipping copy${NC}"
1178
- fi
1179
-
1180
- if [ -f "workers/user-worker/src/user-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/user-worker/src/user-worker.ts" ]; }; then
1181
- cp workers/user-worker/src/user-worker.example.ts workers/user-worker/src/user-worker.ts
1182
- echo -e "${GREEN} ✅ user-worker: user-worker.ts created from example${NC}"
1183
- elif [ -f "workers/user-worker/src/user-worker.ts" ]; then
1184
- echo -e "${YELLOW} ⚠️ user-worker: user-worker.ts already exists, skipping copy${NC}"
1185
- fi
1186
-
1187
- if [ -f "workers/data-worker/src/data-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/data-worker/src/data-worker.ts" ]; }; then
1188
- cp workers/data-worker/src/data-worker.example.ts workers/data-worker/src/data-worker.ts
1189
- echo -e "${GREEN} ✅ data-worker: data-worker.ts created from example${NC}"
1190
- elif [ -f "workers/data-worker/src/data-worker.ts" ]; then
1191
- echo -e "${YELLOW} ⚠️ data-worker: data-worker.ts already exists, skipping copy${NC}"
1192
- fi
1193
-
1194
- if [ -f "workers/audit-worker/src/audit-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/audit-worker/src/audit-worker.ts" ]; }; then
1195
- cp workers/audit-worker/src/audit-worker.example.ts workers/audit-worker/src/audit-worker.ts
1196
- echo -e "${GREEN} ✅ audit-worker: audit-worker.ts created from example${NC}"
1197
- elif [ -f "workers/audit-worker/src/audit-worker.ts" ]; then
1198
- echo -e "${YELLOW} ⚠️ audit-worker: audit-worker.ts already exists, skipping copy${NC}"
1199
- fi
1200
-
1201
- if [ -f "workers/image-worker/src/image-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/image-worker/src/image-worker.ts" ]; }; then
1202
- cp workers/image-worker/src/image-worker.example.ts workers/image-worker/src/image-worker.ts
1203
- echo -e "${GREEN} ✅ image-worker: image-worker.ts created from example${NC}"
1204
- elif [ -f "workers/image-worker/src/image-worker.ts" ]; then
1205
- echo -e "${YELLOW} ⚠️ image-worker: image-worker.ts already exists, skipping copy${NC}"
1206
- fi
1207
-
1208
- if [ -f "workers/pdf-worker/src/pdf-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/pdf-worker/src/pdf-worker.ts" ]; }; then
1209
- cp workers/pdf-worker/src/pdf-worker.example.ts workers/pdf-worker/src/pdf-worker.ts
1210
- echo -e "${GREEN} ✅ pdf-worker: pdf-worker.ts created from example${NC}"
1211
- elif [ -f "workers/pdf-worker/src/pdf-worker.ts" ]; then
1212
- echo -e "${YELLOW} ⚠️ pdf-worker: pdf-worker.ts already exists, skipping copy${NC}"
1213
- fi
1214
-
1215
- # Copy main wrangler.toml from example
1216
- if [ -f "wrangler.toml.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.toml" ]; }; then
1217
- cp wrangler.toml.example wrangler.toml
1218
- echo -e "${GREEN} ✅ root: wrangler.toml created from example${NC}"
1219
- elif [ -f "wrangler.toml" ]; then
1220
- echo -e "${YELLOW} ⚠️ root: wrangler.toml already exists, skipping copy${NC}"
1221
- fi
1222
-
1223
- echo -e "${GREEN}✅ Configuration file copying completed${NC}"
1224
- }
1225
210
 
1226
211
  # Copy example configuration files
1227
212
  copy_example_configs
@@ -1229,494 +214,12 @@ copy_example_configs
1229
214
  # Load required Firebase admin service credentials from app/config/admin-service.json
1230
215
  load_admin_service_credentials
1231
216
 
1232
- # Function to prompt for environment variables and update .env file
1233
- prompt_for_secrets() {
1234
- echo -e "\n${BLUE}🔐 Environment Variables Setup${NC}"
1235
- echo "=============================="
1236
- echo -e "${YELLOW}Please provide values for the following environment variables.${NC}"
1237
- echo -e "${YELLOW}Press Enter to keep existing values (if any).${NC}"
1238
- echo ""
1239
-
1240
- # Create or backup existing .env
1241
- if [ -f ".env" ] && [ "$update_env" != "true" ]; then
1242
- cp .env .env.backup
1243
- echo -e "${GREEN}📄 Existing .env backed up to .env.backup${NC}"
1244
- fi
1245
-
1246
- # Copy .env.example to .env if it doesn't exist
1247
- if [ ! -f ".env" ]; then
1248
- cp .env.example .env
1249
- echo -e "${GREEN}📄 Created .env from .env.example${NC}"
1250
- fi
1251
-
1252
- # Function to prompt for a variable
1253
- prompt_for_var() {
1254
- local var_name=$1
1255
- local description=$2
1256
- local current_value="${!var_name}"
1257
- local new_value=""
1258
- local allow_keep="false"
1259
-
1260
- current_value=$(strip_carriage_returns "$current_value")
1261
-
1262
- if [ "$var_name" = "PAGES_CUSTOM_DOMAIN" ]; then
1263
- current_value=$(resolve_existing_domain_value "$var_name" "$current_value")
1264
- fi
1265
-
1266
- # Auto-generate specific authentication secrets - but allow keeping current
1267
- if [ "$var_name" = "USER_DB_AUTH" ] || [ "$var_name" = "R2_KEY_SECRET" ] || [ "$var_name" = "KEYS_AUTH" ] || [ "$var_name" = "PDF_WORKER_AUTH" ]; then
1268
- echo -e "${BLUE}$var_name${NC}"
1269
- echo -e "${YELLOW}$description${NC}"
1270
-
1271
- if [ "$update_env" != "true" ] && [ -n "$current_value" ] && ! is_placeholder "$current_value" && [ "$current_value" != "your_custom_user_db_auth_token_here" ] && [ "$current_value" != "your_custom_r2_secret_here" ] && [ "$current_value" != "your_custom_keys_auth_token_here" ] && [ "$current_value" != "your_custom_pdf_worker_auth_token_here" ]; then
1272
- # Current value exists and is not a placeholder
1273
- echo -e "${GREEN}Current value: [HIDDEN]${NC}"
1274
- read -p "Generate new secret? (press Enter to keep current, or type 'y' to generate): " gen_choice
1275
- gen_choice=$(strip_carriage_returns "$gen_choice")
1276
-
1277
- if [ "$gen_choice" = "y" ] || [ "$gen_choice" = "Y" ]; then
1278
- new_value=$(openssl rand -hex 32 2>/dev/null || echo "")
1279
- if [ -n "$new_value" ]; then
1280
- echo -e "${GREEN}✅ $var_name auto-generated${NC}"
1281
- else
1282
- while true; do
1283
- echo -e "${RED}❌ Failed to auto-generate, please enter manually:${NC}"
1284
- read -p "Enter value: " new_value
1285
- new_value=$(strip_carriage_returns "$new_value")
1286
- if [ -z "$new_value" ]; then
1287
- echo -e "${RED}❌ A value is required.${NC}"
1288
- continue
1289
- fi
1290
- if is_placeholder "$new_value"; then
1291
- echo -e "${RED}❌ Placeholder values are not allowed.${NC}"
1292
- new_value=""
1293
- continue
1294
- fi
1295
- break
1296
- done
1297
- fi
1298
- else
1299
- # User wants to keep current value
1300
- new_value=""
1301
- fi
1302
- else
1303
- # No current value or placeholder value - auto-generate
1304
- echo -e "${YELLOW}Auto-generating secret...${NC}"
1305
- new_value=$(openssl rand -hex 32 2>/dev/null || echo "")
1306
- if [ -n "$new_value" ]; then
1307
- echo -e "${GREEN}✅ $var_name auto-generated${NC}"
1308
- else
1309
- while true; do
1310
- echo -e "${RED}❌ Failed to auto-generate, please enter manually:${NC}"
1311
- read -p "Enter value: " new_value
1312
- new_value=$(strip_carriage_returns "$new_value")
1313
- if [ -z "$new_value" ]; then
1314
- echo -e "${RED}❌ A value is required.${NC}"
1315
- continue
1316
- fi
1317
- if is_placeholder "$new_value"; then
1318
- echo -e "${RED}❌ Placeholder values are not allowed.${NC}"
1319
- new_value=""
1320
- continue
1321
- fi
1322
- break
1323
- done
1324
- fi
1325
- fi
1326
- else
1327
- # Normal prompt for other variables
1328
- echo -e "${BLUE}$var_name${NC}"
1329
- echo -e "${YELLOW}$description${NC}"
1330
- if [ "$update_env" != "true" ] && [ -n "$current_value" ] && ! is_placeholder "$current_value"; then
1331
- allow_keep="true"
1332
- if [ "$var_name" = "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" ]; then
1333
- echo -e "${GREEN}Current value: [HIDDEN]${NC}"
1334
- else
1335
- echo -e "${GREEN}Current value: $current_value${NC}"
1336
- fi
1337
- fi
1338
-
1339
- while true; do
1340
- if [ "$allow_keep" = "true" ]; then
1341
- read -p "New value (or press Enter to keep current): " new_value
1342
- new_value=$(strip_carriage_returns "$new_value")
1343
- if [ -z "$new_value" ]; then
1344
- break
1345
- fi
1346
- else
1347
- read -p "Enter value: " new_value
1348
- new_value=$(strip_carriage_returns "$new_value")
1349
- if [ -z "$new_value" ]; then
1350
- echo -e "${RED}❌ A value is required.${NC}"
1351
- continue
1352
- fi
1353
- fi
1354
-
1355
- if is_placeholder "$new_value"; then
1356
- echo -e "${RED}❌ Placeholder values are not allowed.${NC}"
1357
- new_value=""
1358
- continue
1359
- fi
1360
-
1361
- if [[ "$var_name" == *_WORKER_NAME ]]; then
1362
- new_value=$(normalize_worker_label_value "$new_value")
1363
-
1364
- if [ -z "$new_value" ] || ! is_valid_worker_label "$new_value"; then
1365
- echo -e "${RED}❌ $var_name must use only lowercase letters, numbers, and dashes.${NC}"
1366
- new_value=""
1367
- continue
1368
- fi
1369
- fi
1370
-
1371
- break
1372
- done
1373
- fi
1374
-
1375
- if [ -n "$new_value" ]; then
1376
- if [ "$var_name" = "PAGES_CUSTOM_DOMAIN" ]; then
1377
- new_value=$(normalize_domain_value "$new_value")
1378
- fi
1379
-
1380
- # Update the .env file
1381
- write_env_var "$var_name" "$new_value"
1382
-
1383
- export "$var_name=$new_value"
1384
- echo -e "${GREEN}✅ $var_name updated${NC}"
1385
- elif [ -n "$current_value" ]; then
1386
- if [[ "$var_name" == *_WORKER_NAME ]]; then
1387
- current_value=$(normalize_worker_label_value "$current_value")
1388
- fi
1389
-
1390
- # Keep values aligned with .env.example ordering and remove stale duplicates.
1391
- write_env_var "$var_name" "$current_value"
1392
- export "$var_name=$current_value"
1393
- echo -e "${GREEN}✅ Keeping current value for $var_name${NC}"
1394
- fi
1395
- echo ""
1396
- }
1397
-
1398
- set_worker_domain_from_shared_subdomain() {
1399
- local worker_name_var=$1
1400
- local worker_domain_var=$2
1401
- local worker_name_value="${!worker_name_var}"
1402
- local composed_domain=""
1403
-
1404
- worker_name_value=$(normalize_worker_label_value "$worker_name_value")
1405
-
1406
- if [ -z "$worker_name_value" ] || ! is_valid_worker_label "$worker_name_value"; then
1407
- echo -e "${RED}❌ $worker_name_var must use only lowercase letters, numbers, and dashes.${NC}"
1408
- exit 1
1409
- fi
1410
-
1411
- composed_domain=$(compose_worker_domain "$worker_name_value" "$shared_worker_subdomain" || echo "")
1412
-
1413
- if [ -z "$composed_domain" ]; then
1414
- echo -e "${RED}❌ Could not build $worker_domain_var from $worker_name_var and shared worker-subdomain.${NC}"
1415
- exit 1
1416
- fi
1417
-
1418
- write_env_var "$worker_domain_var" "$composed_domain"
1419
- export "$worker_domain_var=$composed_domain"
1420
- echo -e "${GREEN}✅ $worker_domain_var set to $composed_domain${NC}"
1421
- }
1422
-
1423
- echo -e "${BLUE}📊 CLOUDFLARE CORE CONFIGURATION${NC}"
1424
- echo "=================================="
1425
- prompt_for_var "ACCOUNT_ID" "Your Cloudflare Account ID"
1426
-
1427
- echo -e "${BLUE}🔐 SHARED AUTHENTICATION & STORAGE${NC}"
1428
- echo "==================================="
1429
- prompt_for_var "USER_DB_AUTH" "Custom user database authentication token (generate with: openssl rand -hex 16)"
1430
- prompt_for_var "R2_KEY_SECRET" "Custom R2 storage authentication token (generate with: openssl rand -hex 16)"
1431
- prompt_for_var "IMAGES_API_TOKEN" "Cloudflare Images API token (shared between workers)"
1432
-
1433
- echo -e "${BLUE}🔥 FIREBASE AUTH CONFIGURATION${NC}"
1434
- echo "==============================="
1435
- prompt_for_var "API_KEY" "Firebase API key"
1436
- prompt_for_var "AUTH_DOMAIN" "Firebase auth domain (project-id.firebaseapp.com)"
1437
- prompt_for_var "STORAGE_BUCKET" "Firebase storage bucket"
1438
- prompt_for_var "MESSAGING_SENDER_ID" "Firebase messaging sender ID"
1439
- prompt_for_var "APP_ID" "Firebase app ID"
1440
- prompt_for_var "MEASUREMENT_ID" "Firebase measurement ID (optional)"
1441
- echo -e "${GREEN}Using PROJECT_ID and service account values from app/config/admin-service.json${NC}"
1442
-
1443
- echo -e "${BLUE}📄 PAGES CONFIGURATION${NC}"
1444
- echo "======================"
1445
- prompt_for_var "PAGES_PROJECT_NAME" "Your Cloudflare Pages project name"
1446
- prompt_for_var "PAGES_CUSTOM_DOMAIN" "Your custom domain (e.g., striae.org) - DO NOT include https://"
1447
-
1448
- echo -e "${BLUE}🔑 WORKER NAMES & DOMAINS${NC}"
1449
- echo "========================="
1450
- echo -e "${YELLOW}Worker names are lowercased automatically and must use only letters, numbers, and dashes.${NC}"
1451
- echo -e "${YELLOW}Enter one shared worker-subdomain as a hostname (for example: team-name.workers.dev).${NC}"
1452
- echo -e "${YELLOW}Each worker domain is generated as {worker-name}.{worker-subdomain}.${NC}"
1453
-
1454
- local shared_worker_subdomain=""
1455
- local shared_worker_subdomain_default=""
1456
- local shared_worker_subdomain_input=""
1457
-
1458
- shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$KEYS_WORKER_NAME" "$KEYS_WORKER_DOMAIN")
1459
- if [ -z "$shared_worker_subdomain_default" ]; then
1460
- shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$USER_WORKER_NAME" "$USER_WORKER_DOMAIN")
1461
- fi
1462
- if [ -z "$shared_worker_subdomain_default" ]; then
1463
- shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$DATA_WORKER_NAME" "$DATA_WORKER_DOMAIN")
1464
- fi
1465
- if [ -z "$shared_worker_subdomain_default" ]; then
1466
- shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$AUDIT_WORKER_NAME" "$AUDIT_WORKER_DOMAIN")
1467
- fi
1468
- if [ -z "$shared_worker_subdomain_default" ]; then
1469
- shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$IMAGES_WORKER_NAME" "$IMAGES_WORKER_DOMAIN")
1470
- fi
1471
- if [ -z "$shared_worker_subdomain_default" ]; then
1472
- shared_worker_subdomain_default=$(infer_worker_subdomain_from_domain "$PDF_WORKER_NAME" "$PDF_WORKER_DOMAIN")
1473
- fi
1474
-
1475
- while true; do
1476
- echo -e "${BLUE}WORKER_SUBDOMAIN${NC}"
1477
-
1478
- if [ "$update_env" != "true" ] && [ -n "$shared_worker_subdomain_default" ] && ! is_placeholder "$shared_worker_subdomain_default"; then
1479
- echo -e "${GREEN}Current value: $shared_worker_subdomain_default${NC}"
1480
- read -p "New value (or press Enter to keep current): " shared_worker_subdomain_input
1481
- shared_worker_subdomain_input=$(strip_carriage_returns "$shared_worker_subdomain_input")
1482
-
1483
- if [ -z "$shared_worker_subdomain_input" ]; then
1484
- shared_worker_subdomain="$shared_worker_subdomain_default"
1485
- else
1486
- shared_worker_subdomain="$shared_worker_subdomain_input"
1487
- fi
1488
- else
1489
- read -p "Enter shared worker-subdomain (e.g., team-name.workers.dev): " shared_worker_subdomain_input
1490
- shared_worker_subdomain_input=$(strip_carriage_returns "$shared_worker_subdomain_input")
1491
- shared_worker_subdomain="$shared_worker_subdomain_input"
1492
- fi
1493
-
1494
- if [ -z "$shared_worker_subdomain" ] || is_placeholder "$shared_worker_subdomain"; then
1495
- echo -e "${RED}❌ shared worker-subdomain is required and cannot be a placeholder.${NC}"
1496
- continue
1497
- fi
1498
-
1499
- shared_worker_subdomain=$(normalize_worker_subdomain_value "$shared_worker_subdomain")
1500
-
1501
- if [ -z "$shared_worker_subdomain" ] || ! is_valid_worker_subdomain "$shared_worker_subdomain"; then
1502
- echo -e "${RED}❌ shared worker-subdomain must be a valid hostname like team-name.workers.dev (letters, numbers, dashes, and dots).${NC}"
1503
- continue
1504
- fi
1505
-
1506
- echo -e "${GREEN}✅ Shared worker-subdomain set to: $shared_worker_subdomain${NC}"
1507
- echo ""
1508
- break
1509
- done
1510
-
1511
- prompt_for_var "KEYS_WORKER_NAME" "Keys worker name"
1512
- prompt_for_var "USER_WORKER_NAME" "User worker name"
1513
- prompt_for_var "DATA_WORKER_NAME" "Data worker name"
1514
- prompt_for_var "AUDIT_WORKER_NAME" "Audit worker name"
1515
- prompt_for_var "IMAGES_WORKER_NAME" "Images worker name"
1516
- prompt_for_var "PDF_WORKER_NAME" "PDF worker name"
1517
-
1518
- set_worker_domain_from_shared_subdomain "KEYS_WORKER_NAME" "KEYS_WORKER_DOMAIN"
1519
- set_worker_domain_from_shared_subdomain "USER_WORKER_NAME" "USER_WORKER_DOMAIN"
1520
- set_worker_domain_from_shared_subdomain "DATA_WORKER_NAME" "DATA_WORKER_DOMAIN"
1521
- set_worker_domain_from_shared_subdomain "AUDIT_WORKER_NAME" "AUDIT_WORKER_DOMAIN"
1522
- set_worker_domain_from_shared_subdomain "IMAGES_WORKER_NAME" "IMAGES_WORKER_DOMAIN"
1523
- set_worker_domain_from_shared_subdomain "PDF_WORKER_NAME" "PDF_WORKER_DOMAIN"
1524
- echo ""
1525
-
1526
- echo -e "${BLUE}🗄️ STORAGE CONFIGURATION${NC}"
1527
- echo "========================="
1528
- prompt_for_var "DATA_BUCKET_NAME" "Your R2 bucket name for case data storage"
1529
- prompt_for_var "AUDIT_BUCKET_NAME" "Your R2 bucket name for audit logs (separate from data bucket)"
1530
- prompt_for_var "FILES_BUCKET_NAME" "Your R2 bucket name for encrypted files storage"
1531
- prompt_for_var "KV_STORE_ID" "Your KV namespace ID (UUID format)"
1532
-
1533
- echo -e "${BLUE}🔐 SERVICE-SPECIFIC SECRETS${NC}"
1534
- echo "============================"
1535
- prompt_for_var "KEYS_AUTH" "Keys worker authentication token (generate with: openssl rand -hex 16)"
1536
- prompt_for_var "PDF_WORKER_AUTH" "PDF worker authentication token (generate with: openssl rand -hex 16)"
1537
- prompt_for_var "ACCOUNT_HASH" "Cloudflare Images Account Hash"
1538
- prompt_for_var "BROWSER_API_TOKEN" "Cloudflare Browser Rendering API token (for PDF Worker)"
1539
-
1540
- configure_manifest_signing_credentials
1541
- configure_export_encryption_credentials
1542
- configure_data_at_rest_encryption_credentials
1543
-
1544
- # Reload the updated .env file
1545
- source .env
1546
-
1547
- echo -e "${GREEN}🎉 Environment variables setup completed!${NC}"
1548
- echo -e "${BLUE}📄 All values saved to .env file${NC}"
1549
- }
1550
-
1551
217
  # Always prompt for secrets to ensure configuration
1552
218
  prompt_for_secrets
1553
219
 
1554
220
  # Validate after secrets have been configured
1555
221
  validate_required_vars
1556
222
 
1557
- # Function to replace variables in wrangler configuration files
1558
- update_wrangler_configs() {
1559
- echo -e "\n${BLUE}🔧 Updating wrangler configuration files...${NC}"
1560
-
1561
- local normalized_pages_custom_domain
1562
- local escaped_pages_custom_domain
1563
-
1564
- normalized_pages_custom_domain=$(normalize_domain_value "$PAGES_CUSTOM_DOMAIN")
1565
- PAGES_CUSTOM_DOMAIN="$normalized_pages_custom_domain"
1566
- export PAGES_CUSTOM_DOMAIN
1567
- write_env_var "PAGES_CUSTOM_DOMAIN" "$PAGES_CUSTOM_DOMAIN"
1568
- escaped_pages_custom_domain=$(escape_for_sed_replacement "$PAGES_CUSTOM_DOMAIN")
1569
-
1570
- # Audit Worker
1571
- if [ -f "workers/audit-worker/wrangler.jsonc" ]; then
1572
- echo -e "${YELLOW} Updating audit-worker/wrangler.jsonc...${NC}"
1573
- sed -i "s/\"AUDIT_WORKER_NAME\"/\"$AUDIT_WORKER_NAME\"/g" workers/audit-worker/wrangler.jsonc
1574
- sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/audit-worker/wrangler.jsonc
1575
- sed -i "s/\"AUDIT_BUCKET_NAME\"/\"$AUDIT_BUCKET_NAME\"/g" workers/audit-worker/wrangler.jsonc
1576
- echo -e "${GREEN} ✅ audit-worker configuration updated${NC}"
1577
- fi
1578
-
1579
- # Update audit-worker source file domain placeholders
1580
- if [ -f "workers/audit-worker/src/audit-worker.ts" ]; then
1581
- echo -e "${YELLOW} Updating audit-worker source placeholders...${NC}"
1582
- sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/audit-worker/src/audit-worker.ts
1583
- echo -e "${GREEN} ✅ audit-worker source placeholders updated${NC}"
1584
- fi
1585
-
1586
- # Data Worker
1587
- if [ -f "workers/data-worker/wrangler.jsonc" ]; then
1588
- echo -e "${YELLOW} Updating data-worker/wrangler.jsonc...${NC}"
1589
- sed -i "s/\"DATA_WORKER_NAME\"/\"$DATA_WORKER_NAME\"/g" workers/data-worker/wrangler.jsonc
1590
- sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/data-worker/wrangler.jsonc
1591
- sed -i "s/\"DATA_BUCKET_NAME\"/\"$DATA_BUCKET_NAME\"/g" workers/data-worker/wrangler.jsonc
1592
- echo -e "${GREEN} ✅ data-worker configuration updated${NC}"
1593
- fi
1594
-
1595
- # Update data-worker source file domain placeholders
1596
- if [ -f "workers/data-worker/src/data-worker.ts" ]; then
1597
- echo -e "${YELLOW} Updating data-worker source placeholders...${NC}"
1598
- sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/data-worker/src/data-worker.ts
1599
- echo -e "${GREEN} ✅ data-worker source placeholders updated${NC}"
1600
- fi
1601
-
1602
- # Image Worker
1603
- if [ -f "workers/image-worker/wrangler.jsonc" ]; then
1604
- echo -e "${YELLOW} Updating image-worker/wrangler.jsonc...${NC}"
1605
- sed -i "s/\"IMAGES_WORKER_NAME\"/\"$IMAGES_WORKER_NAME\"/g" workers/image-worker/wrangler.jsonc
1606
- sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/image-worker/wrangler.jsonc
1607
- sed -i "s/\"FILES_BUCKET_NAME\"/\"$FILES_BUCKET_NAME\"/g" workers/image-worker/wrangler.jsonc
1608
- echo -e "${GREEN} ✅ image-worker configuration updated${NC}"
1609
- fi
1610
-
1611
- # Update image-worker source file domain placeholders
1612
- if [ -f "workers/image-worker/src/image-worker.ts" ]; then
1613
- echo -e "${YELLOW} Updating image-worker source placeholders...${NC}"
1614
- sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/image-worker/src/image-worker.ts
1615
- echo -e "${GREEN} ✅ image-worker source placeholders updated${NC}"
1616
- fi
1617
-
1618
- # Keys Worker
1619
- if [ -f "workers/keys-worker/wrangler.jsonc" ]; then
1620
- echo -e "${YELLOW} Updating keys-worker/wrangler.jsonc...${NC}"
1621
- sed -i "s/\"KEYS_WORKER_NAME\"/\"$KEYS_WORKER_NAME\"/g" workers/keys-worker/wrangler.jsonc
1622
- sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/keys-worker/wrangler.jsonc
1623
- echo -e "${GREEN} ✅ keys-worker configuration updated${NC}"
1624
- fi
1625
-
1626
- # Update keys-worker source file domain placeholders
1627
- if [ -f "workers/keys-worker/src/keys.ts" ]; then
1628
- echo -e "${YELLOW} Updating keys-worker source placeholders...${NC}"
1629
- sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/keys-worker/src/keys.ts
1630
- echo -e "${GREEN} ✅ keys-worker source placeholders updated${NC}"
1631
- fi
1632
-
1633
- # PDF Worker
1634
- if [ -f "workers/pdf-worker/wrangler.jsonc" ]; then
1635
- echo -e "${YELLOW} Updating pdf-worker/wrangler.jsonc...${NC}"
1636
- sed -i "s/\"PDF_WORKER_NAME\"/\"$PDF_WORKER_NAME\"/g" workers/pdf-worker/wrangler.jsonc
1637
- sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/pdf-worker/wrangler.jsonc
1638
- echo -e "${GREEN} ✅ pdf-worker configuration updated${NC}"
1639
- fi
1640
-
1641
- # Update pdf-worker source file domain placeholders
1642
- if [ -f "workers/pdf-worker/src/pdf-worker.ts" ]; then
1643
- echo -e "${YELLOW} Updating pdf-worker source placeholders...${NC}"
1644
- sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/pdf-worker/src/pdf-worker.ts
1645
- echo -e "${GREEN} ✅ pdf-worker source placeholders updated${NC}"
1646
- fi
1647
-
1648
- # User Worker
1649
- if [ -f "workers/user-worker/wrangler.jsonc" ]; then
1650
- echo -e "${YELLOW} Updating user-worker/wrangler.jsonc...${NC}"
1651
- sed -i "s/\"USER_WORKER_NAME\"/\"$USER_WORKER_NAME\"/g" workers/user-worker/wrangler.jsonc
1652
- sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/user-worker/wrangler.jsonc
1653
- sed -i "s/\"KV_STORE_ID\"/\"$KV_STORE_ID\"/g" workers/user-worker/wrangler.jsonc
1654
- echo -e "${GREEN} ✅ user-worker configuration updated${NC}"
1655
- fi
1656
-
1657
- # Update user-worker source file domain placeholders
1658
- if [ -f "workers/user-worker/src/user-worker.ts" ]; then
1659
- echo -e "${YELLOW} Updating user-worker source placeholders...${NC}"
1660
- sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/user-worker/src/user-worker.ts
1661
- sed -i "s|'DATA_WORKER_DOMAIN'|'https://$DATA_WORKER_DOMAIN'|g" workers/user-worker/src/user-worker.ts
1662
- sed -i "s|'IMAGES_WORKER_DOMAIN'|'https://$IMAGES_WORKER_DOMAIN'|g" workers/user-worker/src/user-worker.ts
1663
- echo -e "${GREEN} ✅ user-worker source placeholders updated${NC}"
1664
- fi
1665
-
1666
- # Main wrangler.toml
1667
- if [ -f "wrangler.toml" ]; then
1668
- echo -e "${YELLOW} Updating wrangler.toml...${NC}"
1669
- sed -i "s/\"PAGES_PROJECT_NAME\"/\"$PAGES_PROJECT_NAME\"/g" wrangler.toml
1670
- echo -e "${GREEN} ✅ main wrangler.toml configuration updated${NC}"
1671
- fi
1672
-
1673
- # Update app configuration files
1674
- echo -e "${YELLOW} Updating app configuration files...${NC}"
1675
-
1676
- # Update app/config/config.json
1677
- if [ -f "app/config/config.json" ]; then
1678
- echo -e "${YELLOW} Updating app/config/config.json...${NC}"
1679
- local escaped_manifest_signing_key_id
1680
- local escaped_manifest_signing_public_key
1681
- local escaped_export_encryption_key_id
1682
- local escaped_export_encryption_public_key
1683
- local escaped_account_hash
1684
- escaped_manifest_signing_key_id=$(escape_for_sed_replacement "$MANIFEST_SIGNING_KEY_ID")
1685
- escaped_manifest_signing_public_key=$(escape_for_sed_replacement "$MANIFEST_SIGNING_PUBLIC_KEY")
1686
- escaped_export_encryption_key_id=$(escape_for_sed_replacement "$EXPORT_ENCRYPTION_KEY_ID")
1687
- escaped_export_encryption_public_key=$(escape_for_sed_replacement "$EXPORT_ENCRYPTION_PUBLIC_KEY")
1688
- escaped_account_hash=$(escape_for_sed_replacement "$ACCOUNT_HASH")
1689
-
1690
- sed -i "s|\"url\": \"[^\"]*\"|\"url\": \"https://$escaped_pages_custom_domain\"|g" app/config/config.json
1691
- sed -i "s|\"account_hash\": \"[^\"]*\"|\"account_hash\": \"$escaped_account_hash\"|g" app/config/config.json
1692
- sed -i "s|\"MANIFEST_SIGNING_KEY_ID\"|\"$escaped_manifest_signing_key_id\"|g" app/config/config.json
1693
- sed -i "s|\"MANIFEST_SIGNING_PUBLIC_KEY\"|\"$escaped_manifest_signing_public_key\"|g" app/config/config.json
1694
- sed -i "s|\"EXPORT_ENCRYPTION_KEY_ID\"|\"$escaped_export_encryption_key_id\"|g" app/config/config.json
1695
- sed -i "s|\"EXPORT_ENCRYPTION_PUBLIC_KEY\"|\"$escaped_export_encryption_public_key\"|g" app/config/config.json
1696
- echo -e "${GREEN} ✅ app config.json updated${NC}"
1697
- fi
1698
-
1699
- # Update app/config/firebase.ts
1700
- if [ -f "app/config/firebase.ts" ]; then
1701
- echo -e "${YELLOW} Updating app/config/firebase.ts...${NC}"
1702
- sed -i "s|\"YOUR_FIREBASE_API_KEY\"|\"$API_KEY\"|g" app/config/firebase.ts
1703
- sed -i "s|\"YOUR_FIREBASE_AUTH_DOMAIN\"|\"$AUTH_DOMAIN\"|g" app/config/firebase.ts
1704
- sed -i "s|\"YOUR_FIREBASE_PROJECT_ID\"|\"$PROJECT_ID\"|g" app/config/firebase.ts
1705
- sed -i "s|\"YOUR_FIREBASE_STORAGE_BUCKET\"|\"$STORAGE_BUCKET\"|g" app/config/firebase.ts
1706
- sed -i "s|\"YOUR_FIREBASE_MESSAGING_SENDER_ID\"|\"$MESSAGING_SENDER_ID\"|g" app/config/firebase.ts
1707
- sed -i "s|\"YOUR_FIREBASE_APP_ID\"|\"$APP_ID\"|g" app/config/firebase.ts
1708
- sed -i "s|\"YOUR_FIREBASE_MEASUREMENT_ID\"|\"$MEASUREMENT_ID\"|g" app/config/firebase.ts
1709
- echo -e "${GREEN} ✅ app firebase.ts updated${NC}"
1710
- fi
1711
-
1712
- if [ -f "app/routes/auth/login.tsx" ]; then
1713
- echo -e "${YELLOW} Updating app/routes/auth/login.tsx...${NC}"
1714
- sed -i "s|^const APP_CANONICAL_ORIGIN = .*;|const APP_CANONICAL_ORIGIN = 'https://$escaped_pages_custom_domain';|g" app/routes/auth/login.tsx
1715
- echo -e "${GREEN} ✅ app login.tsx canonical origin updated${NC}"
1716
- fi
1717
-
1718
- echo -e "${GREEN}✅ All configuration files updated${NC}"
1719
- }
1720
223
 
1721
224
  # Update wrangler configurations
1722
225
  update_wrangler_configs