@striae-org/striae 3.0.4

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 (223) hide show
  1. package/.env.example +100 -0
  2. package/LICENSE +190 -0
  3. package/NOTICE +18 -0
  4. package/README.md +133 -0
  5. package/app/components/actions/case-export/core-export.ts +328 -0
  6. package/app/components/actions/case-export/data-processing.ts +167 -0
  7. package/app/components/actions/case-export/download-handlers.ts +900 -0
  8. package/app/components/actions/case-export/index.ts +41 -0
  9. package/app/components/actions/case-export/metadata-helpers.ts +107 -0
  10. package/app/components/actions/case-export/types-constants.ts +56 -0
  11. package/app/components/actions/case-export/validation-utils.ts +25 -0
  12. package/app/components/actions/case-export.ts +4 -0
  13. package/app/components/actions/case-import/annotation-import.ts +35 -0
  14. package/app/components/actions/case-import/confirmation-import.ts +363 -0
  15. package/app/components/actions/case-import/image-operations.ts +61 -0
  16. package/app/components/actions/case-import/index.ts +39 -0
  17. package/app/components/actions/case-import/orchestrator.ts +420 -0
  18. package/app/components/actions/case-import/storage-operations.ts +270 -0
  19. package/app/components/actions/case-import/validation.ts +189 -0
  20. package/app/components/actions/case-import/zip-processing.ts +413 -0
  21. package/app/components/actions/case-manage.ts +524 -0
  22. package/app/components/actions/case-review.ts +4 -0
  23. package/app/components/actions/confirm-export.ts +351 -0
  24. package/app/components/actions/generate-pdf.ts +210 -0
  25. package/app/components/actions/image-manage.ts +385 -0
  26. package/app/components/actions/notes-manage.ts +33 -0
  27. package/app/components/actions/signout.module.css +15 -0
  28. package/app/components/actions/signout.tsx +50 -0
  29. package/app/components/audit/user-audit-viewer.tsx +975 -0
  30. package/app/components/audit/user-audit.module.css +568 -0
  31. package/app/components/auth/auth-provider.tsx +78 -0
  32. package/app/components/auth/mfa-enrollment.module.css +268 -0
  33. package/app/components/auth/mfa-enrollment.tsx +398 -0
  34. package/app/components/auth/mfa-verification.module.css +251 -0
  35. package/app/components/auth/mfa-verification.tsx +295 -0
  36. package/app/components/button/button.module.css +63 -0
  37. package/app/components/button/button.tsx +46 -0
  38. package/app/components/canvas/box-annotations/box-annotations.module.css +170 -0
  39. package/app/components/canvas/box-annotations/box-annotations.tsx +634 -0
  40. package/app/components/canvas/canvas.module.css +314 -0
  41. package/app/components/canvas/canvas.tsx +449 -0
  42. package/app/components/canvas/confirmation/confirmation.module.css +187 -0
  43. package/app/components/canvas/confirmation/confirmation.tsx +214 -0
  44. package/app/components/colors/colors.module.css +59 -0
  45. package/app/components/colors/colors.tsx +68 -0
  46. package/app/components/form/base-form.tsx +21 -0
  47. package/app/components/form/form-button.tsx +28 -0
  48. package/app/components/form/form-field.tsx +53 -0
  49. package/app/components/form/form-message.tsx +17 -0
  50. package/app/components/form/form-toggle.tsx +23 -0
  51. package/app/components/form/form.module.css +427 -0
  52. package/app/components/form/index.ts +6 -0
  53. package/app/components/icon/icon.module.css +3 -0
  54. package/app/components/icon/icon.tsx +27 -0
  55. package/app/components/icon/icons.svg +102 -0
  56. package/app/components/icon/manifest.json +110 -0
  57. package/app/components/sidebar/case-export/case-export.module.css +386 -0
  58. package/app/components/sidebar/case-export/case-export.tsx +317 -0
  59. package/app/components/sidebar/case-import/case-import.module.css +626 -0
  60. package/app/components/sidebar/case-import/case-import.tsx +404 -0
  61. package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +72 -0
  62. package/app/components/sidebar/case-import/components/ConfirmationDialog.tsx +72 -0
  63. package/app/components/sidebar/case-import/components/ConfirmationPreviewSection.tsx +71 -0
  64. package/app/components/sidebar/case-import/components/ExistingCaseSection.tsx +40 -0
  65. package/app/components/sidebar/case-import/components/FileSelector.tsx +161 -0
  66. package/app/components/sidebar/case-import/components/ProgressSection.tsx +46 -0
  67. package/app/components/sidebar/case-import/hooks/useFilePreview.ts +101 -0
  68. package/app/components/sidebar/case-import/hooks/useImportExecution.ts +152 -0
  69. package/app/components/sidebar/case-import/hooks/useImportState.ts +88 -0
  70. package/app/components/sidebar/case-import/index.ts +18 -0
  71. package/app/components/sidebar/case-import/utils/file-validation.ts +43 -0
  72. package/app/components/sidebar/cases/case-sidebar.tsx +827 -0
  73. package/app/components/sidebar/cases/cases-modal.module.css +166 -0
  74. package/app/components/sidebar/cases/cases-modal.tsx +201 -0
  75. package/app/components/sidebar/cases/cases.module.css +713 -0
  76. package/app/components/sidebar/files/files-modal.module.css +209 -0
  77. package/app/components/sidebar/files/files-modal.tsx +239 -0
  78. package/app/components/sidebar/hash/hash-utility.module.css +366 -0
  79. package/app/components/sidebar/hash/hash-utility.tsx +982 -0
  80. package/app/components/sidebar/notes/notes-modal.tsx +51 -0
  81. package/app/components/sidebar/notes/notes-sidebar.tsx +491 -0
  82. package/app/components/sidebar/notes/notes.module.css +360 -0
  83. package/app/components/sidebar/sidebar-container.tsx +149 -0
  84. package/app/components/sidebar/sidebar.module.css +321 -0
  85. package/app/components/sidebar/sidebar.tsx +215 -0
  86. package/app/components/sidebar/upload/image-upload-zone.module.css +123 -0
  87. package/app/components/sidebar/upload/image-upload-zone.tsx +330 -0
  88. package/app/components/theme-provider/theme-provider.tsx +131 -0
  89. package/app/components/theme-provider/theme.ts +155 -0
  90. package/app/components/toast/toast.module.css +137 -0
  91. package/app/components/toast/toast.tsx +56 -0
  92. package/app/components/toolbar/toolbar-color-selector.module.css +171 -0
  93. package/app/components/toolbar/toolbar-color-selector.tsx +129 -0
  94. package/app/components/toolbar/toolbar.module.css +42 -0
  95. package/app/components/toolbar/toolbar.tsx +167 -0
  96. package/app/components/user/delete-account.module.css +274 -0
  97. package/app/components/user/delete-account.tsx +471 -0
  98. package/app/components/user/inactivity-warning.module.css +145 -0
  99. package/app/components/user/inactivity-warning.tsx +84 -0
  100. package/app/components/user/manage-profile.module.css +190 -0
  101. package/app/components/user/manage-profile.tsx +253 -0
  102. package/app/components/user/mfa-phone-update.tsx +739 -0
  103. package/app/config-example/admin-service.json +13 -0
  104. package/app/config-example/config.json +17 -0
  105. package/app/config-example/firebase.ts +21 -0
  106. package/app/config-example/inactivity.ts +13 -0
  107. package/app/config-example/meta-config.json +6 -0
  108. package/app/contexts/auth.context.ts +12 -0
  109. package/app/entry.client.tsx +12 -0
  110. package/app/entry.server.tsx +44 -0
  111. package/app/hooks/useInactivityTimeout.ts +110 -0
  112. package/app/root.tsx +170 -0
  113. package/app/routes/_index.tsx +16 -0
  114. package/app/routes/auth/emailActionHandler.module.css +232 -0
  115. package/app/routes/auth/emailActionHandler.tsx +405 -0
  116. package/app/routes/auth/emailVerification.tsx +120 -0
  117. package/app/routes/auth/login.module.css +523 -0
  118. package/app/routes/auth/login.tsx +654 -0
  119. package/app/routes/auth/passwordReset.module.css +274 -0
  120. package/app/routes/auth/passwordReset.tsx +154 -0
  121. package/app/routes/auth/route.ts +16 -0
  122. package/app/routes/mobile-prevented/mobilePrevented.module.css +47 -0
  123. package/app/routes/mobile-prevented/mobilePrevented.tsx +26 -0
  124. package/app/routes/mobile-prevented/route.ts +14 -0
  125. package/app/routes/striae/striae.module.css +30 -0
  126. package/app/routes/striae/striae.tsx +417 -0
  127. package/app/services/audit-export.service.ts +755 -0
  128. package/app/services/audit.service.ts +1454 -0
  129. package/app/services/firebase-errors.ts +106 -0
  130. package/app/services/firebase.ts +15 -0
  131. package/app/styles/legal-pages.module.css +113 -0
  132. package/app/styles/root.module.css +146 -0
  133. package/app/tailwind.css +225 -0
  134. package/app/types/annotations.ts +45 -0
  135. package/app/types/audit.ts +301 -0
  136. package/app/types/case.ts +90 -0
  137. package/app/types/export.ts +8 -0
  138. package/app/types/file.ts +30 -0
  139. package/app/types/import.ts +107 -0
  140. package/app/types/index.ts +24 -0
  141. package/app/types/user.ts +38 -0
  142. package/app/utils/SHA256.ts +461 -0
  143. package/app/utils/annotation-timestamp.ts +25 -0
  144. package/app/utils/audit-export-signature.ts +117 -0
  145. package/app/utils/auth-action-settings.ts +48 -0
  146. package/app/utils/auth.ts +34 -0
  147. package/app/utils/batch-operations.ts +135 -0
  148. package/app/utils/confirmation-signature.ts +193 -0
  149. package/app/utils/data-operations.ts +871 -0
  150. package/app/utils/device-detection.ts +5 -0
  151. package/app/utils/html-sanitizer.ts +80 -0
  152. package/app/utils/id-generator.ts +36 -0
  153. package/app/utils/meta.ts +48 -0
  154. package/app/utils/mfa-phone.ts +97 -0
  155. package/app/utils/mfa.ts +79 -0
  156. package/app/utils/password-policy.ts +28 -0
  157. package/app/utils/permissions.ts +562 -0
  158. package/app/utils/signature-utils.ts +160 -0
  159. package/app/utils/style.ts +83 -0
  160. package/app/utils/version.ts +5 -0
  161. package/firebase.json +11 -0
  162. package/functions/[[path]].ts +10 -0
  163. package/package.json +138 -0
  164. package/postcss.config.js +6 -0
  165. package/public/.well-known/publickey.info@striae.org.asc +17 -0
  166. package/public/.well-known/security.txt +7 -0
  167. package/public/_headers +28 -0
  168. package/public/_routes.json +13 -0
  169. package/public/assets/striae.jpg +0 -0
  170. package/public/clear.jpg +0 -0
  171. package/public/favicon.ico +0 -0
  172. package/public/favicon.svg +9 -0
  173. package/public/icon-256.png +0 -0
  174. package/public/icon-512.png +0 -0
  175. package/public/logo-dark.png +0 -0
  176. package/public/manifest.json +25 -0
  177. package/public/oin-badge.png +0 -0
  178. package/public/shortcut.png +0 -0
  179. package/public/social-image.png +0 -0
  180. package/public/striae-ascii.txt +10 -0
  181. package/scripts/deploy-all.sh +100 -0
  182. package/scripts/deploy-config.sh +940 -0
  183. package/scripts/deploy-pages.sh +34 -0
  184. package/scripts/deploy-worker-secrets.sh +215 -0
  185. package/scripts/dev.cjs +23 -0
  186. package/scripts/install-workers.sh +88 -0
  187. package/scripts/run-eslint.cjs +35 -0
  188. package/scripts/update-compatibility-dates.cjs +124 -0
  189. package/scripts/update-markdown-versions.cjs +43 -0
  190. package/tailwind.config.ts +22 -0
  191. package/tsconfig.json +33 -0
  192. package/vite.config.ts +35 -0
  193. package/worker-configuration.d.ts +7490 -0
  194. package/workers/audit-worker/package.json +17 -0
  195. package/workers/audit-worker/src/audit-worker.example.ts +195 -0
  196. package/workers/audit-worker/worker-configuration.d.ts +7448 -0
  197. package/workers/audit-worker/wrangler.jsonc.example +29 -0
  198. package/workers/data-worker/package.json +17 -0
  199. package/workers/data-worker/src/data-worker.example.ts +267 -0
  200. package/workers/data-worker/src/signature-utils.ts +79 -0
  201. package/workers/data-worker/src/signing-payload-utils.ts +290 -0
  202. package/workers/data-worker/worker-configuration.d.ts +7448 -0
  203. package/workers/data-worker/wrangler.jsonc.example +30 -0
  204. package/workers/image-worker/package.json +17 -0
  205. package/workers/image-worker/src/image-worker.example.ts +180 -0
  206. package/workers/image-worker/worker-configuration.d.ts +7447 -0
  207. package/workers/image-worker/wrangler.jsonc.example +22 -0
  208. package/workers/keys-worker/package.json +17 -0
  209. package/workers/keys-worker/src/keys.example.ts +66 -0
  210. package/workers/keys-worker/src/keys.ts +66 -0
  211. package/workers/keys-worker/worker-configuration.d.ts +7447 -0
  212. package/workers/keys-worker/wrangler.jsonc.example +22 -0
  213. package/workers/pdf-worker/package.json +17 -0
  214. package/workers/pdf-worker/src/format-striae.ts +534 -0
  215. package/workers/pdf-worker/src/pdf-worker.example.ts +119 -0
  216. package/workers/pdf-worker/src/report-types.ts +69 -0
  217. package/workers/pdf-worker/worker-configuration.d.ts +7448 -0
  218. package/workers/pdf-worker/wrangler.jsonc.example +26 -0
  219. package/workers/user-worker/package.json +17 -0
  220. package/workers/user-worker/src/user-worker.example.ts +636 -0
  221. package/workers/user-worker/worker-configuration.d.ts +7448 -0
  222. package/workers/user-worker/wrangler.jsonc.example +29 -0
  223. package/wrangler.toml.example +8 -0
@@ -0,0 +1,940 @@
1
+ #!/bin/bash
2
+
3
+ # ===================================
4
+ # STRIAE CONFIGURATION SETUP SCRIPT
5
+ # ===================================
6
+ # This script sets up all configuration files and replaces placeholders
7
+ # Run this BEFORE installing worker dependencies to avoid wrangler validation errors
8
+
9
+ set -e
10
+
11
+ # Colors for output
12
+ RED='\033[0;31m'
13
+ GREEN='\033[0;32m'
14
+ YELLOW='\033[1;33m'
15
+ BLUE='\033[0;34m'
16
+ NC='\033[0m' # No Color
17
+
18
+ echo -e "${BLUE}⚙️ Striae Configuration Setup Script${NC}"
19
+ echo "====================================="
20
+
21
+ update_env=false
22
+ show_help=false
23
+ for arg in "$@"; do
24
+ case "$arg" in
25
+ -h|--help)
26
+ show_help=true
27
+ ;;
28
+ --update-env)
29
+ update_env=true
30
+ ;;
31
+ esac
32
+ done
33
+
34
+ if [ "$show_help" = "true" ]; then
35
+ echo "Usage: bash ./scripts/deploy-config.sh [--update-env]"
36
+ echo ""
37
+ echo "Options:"
38
+ echo " --update-env Reset .env from .env.example and overwrite configs"
39
+ echo " -h, --help Show this help message"
40
+ exit 0
41
+ fi
42
+
43
+ if [ "$update_env" = "true" ]; then
44
+ echo -e "${YELLOW}⚠️ Update-env mode: overwriting configs and regenerating .env values${NC}"
45
+ fi
46
+
47
+ is_placeholder() {
48
+ local value="$1"
49
+ local normalized=$(echo "$value" | tr '[:upper:]' '[:lower:]')
50
+
51
+ if [ -z "$normalized" ]; then
52
+ return 1
53
+ fi
54
+
55
+ [[ "$normalized" == your_*_here ]]
56
+ }
57
+
58
+ # Check if .env file exists
59
+ env_created_from_example=false
60
+ if [ "$update_env" = "true" ]; then
61
+ if [ -f ".env" ]; then
62
+ cp .env .env.backup
63
+ echo -e "${GREEN}📄 Existing .env backed up to .env.backup${NC}"
64
+ fi
65
+
66
+ if [ -f ".env.example" ]; then
67
+ cp ".env.example" ".env"
68
+ echo -e "${GREEN}✅ .env file reset from .env.example${NC}"
69
+ env_created_from_example=true
70
+ else
71
+ echo -e "${RED}❌ Error: .env.example file not found!${NC}"
72
+ exit 1
73
+ fi
74
+ elif [ ! -f ".env" ]; then
75
+ echo -e "${YELLOW}📄 .env file not found, copying from .env.example...${NC}"
76
+ if [ -f ".env.example" ]; then
77
+ cp ".env.example" ".env"
78
+ echo -e "${GREEN}✅ .env file created from .env.example${NC}"
79
+ env_created_from_example=true
80
+ else
81
+ echo -e "${RED}❌ Error: Neither .env nor .env.example file found!${NC}"
82
+ echo "Please create a .env.example file or provide a .env file."
83
+ exit 1
84
+ fi
85
+ fi
86
+
87
+ # Source the .env file
88
+ echo -e "${YELLOW}📖 Loading environment variables from .env...${NC}"
89
+ source .env
90
+
91
+ escape_for_sed_pattern() {
92
+ printf '%s' "$1" | sed -e 's/[][\\.^$*+?{}|()]/\\&/g'
93
+ }
94
+
95
+ dedupe_env_var_entries() {
96
+ local var_name=$1
97
+ local expected_count=1
98
+ local escaped_var_name
99
+
100
+ escaped_var_name=$(escape_for_sed_pattern "$var_name")
101
+
102
+ if [ -f ".env.example" ]; then
103
+ expected_count=$(grep -c "^$escaped_var_name=" .env.example || true)
104
+
105
+ if [ "$expected_count" -lt 1 ]; then
106
+ expected_count=1
107
+ fi
108
+ fi
109
+
110
+ awk -v key="$var_name" -v keep="$expected_count" '
111
+ BEGIN { seen = 0 }
112
+ {
113
+ if (index($0, key "=") == 1) {
114
+ seen++
115
+
116
+ if (seen > keep) {
117
+ next
118
+ }
119
+ }
120
+ print
121
+ }
122
+ ' .env > .env.tmp && mv .env.tmp .env
123
+ }
124
+
125
+ normalize_domain_value() {
126
+ local domain="$1"
127
+
128
+ domain=$(printf '%s' "$domain" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
129
+ domain="${domain#http://}"
130
+ domain="${domain#https://}"
131
+ domain="${domain%/}"
132
+
133
+ printf '%s' "$domain"
134
+ }
135
+
136
+ write_env_var() {
137
+ local var_name=$1
138
+ local var_value=$2
139
+ local env_file_value="$var_value"
140
+
141
+ if [ "$var_name" = "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" ] || [ "$var_name" = "MANIFEST_SIGNING_PRIVATE_KEY" ] || [ "$var_name" = "MANIFEST_SIGNING_PUBLIC_KEY" ]; then
142
+ # Store as a quoted string so sourced .env preserves escaped newline markers (\n)
143
+ env_file_value=${env_file_value//\"/\\\"}
144
+ env_file_value="\"$env_file_value\""
145
+ fi
146
+
147
+ local escaped_var_name
148
+ local replacement_line
149
+ escaped_var_name=$(escape_for_sed_pattern "$var_name")
150
+ replacement_line=$(escape_for_sed_replacement "$var_name=$env_file_value")
151
+
152
+ if grep -q "^$escaped_var_name=" .env; then
153
+ # Replace all occurrences so intentional duplicates in .env.example stay in sync.
154
+ sed -i "s|^$escaped_var_name=.*|$replacement_line|g" .env
155
+ dedupe_env_var_entries "$var_name"
156
+ else
157
+ echo "$var_name=$env_file_value" >> .env
158
+ fi
159
+ }
160
+
161
+ escape_for_sed_replacement() {
162
+ printf '%s' "$1" | sed -e 's/[&|\\]/\\&/g'
163
+ }
164
+
165
+ is_admin_service_placeholder() {
166
+ local value="$1"
167
+ local normalized=$(echo "$value" | tr '[:upper:]' '[:lower:]')
168
+
169
+ [[ -z "$normalized" || "$normalized" == your-* || "$normalized" == *"your_private_key"* ]]
170
+ }
171
+
172
+ load_admin_service_credentials() {
173
+ local admin_service_path="app/config/admin-service.json"
174
+
175
+ if [ ! -f "$admin_service_path" ]; then
176
+ echo -e "${RED}❌ Error: Required Firebase admin service file not found: $admin_service_path${NC}"
177
+ echo -e "${YELLOW} Create app/config/admin-service.json with service account credentials.${NC}"
178
+ exit 1
179
+ fi
180
+
181
+ local service_project_id
182
+ local service_client_email
183
+ local service_private_key
184
+
185
+ 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
186
+ echo -e "${RED}❌ Error: Could not parse project_id from $admin_service_path${NC}"
187
+ exit 1
188
+ fi
189
+
190
+ 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
191
+ echo -e "${RED}❌ Error: Could not parse client_email from $admin_service_path${NC}"
192
+ exit 1
193
+ fi
194
+
195
+ 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
196
+ echo -e "${RED}❌ Error: Could not parse private_key from $admin_service_path${NC}"
197
+ exit 1
198
+ fi
199
+
200
+ local normalized_private_key="${service_private_key//$'\r'/}"
201
+ normalized_private_key="${normalized_private_key//$'\n'/\\n}"
202
+
203
+ if is_admin_service_placeholder "$service_project_id"; then
204
+ echo -e "${RED}❌ Error: project_id in $admin_service_path is missing or placeholder${NC}"
205
+ exit 1
206
+ fi
207
+
208
+ if is_admin_service_placeholder "$service_client_email" || [[ "$service_client_email" != *".gserviceaccount.com"* ]]; then
209
+ echo -e "${RED}❌ Error: client_email in $admin_service_path is invalid${NC}"
210
+ exit 1
211
+ fi
212
+
213
+ if is_admin_service_placeholder "$normalized_private_key" || [[ "$normalized_private_key" != *"-----BEGIN PRIVATE KEY-----"* ]] || [[ "$normalized_private_key" != *"-----END PRIVATE KEY-----"* ]]; then
214
+ echo -e "${RED}❌ Error: private_key in $admin_service_path is invalid${NC}"
215
+ exit 1
216
+ fi
217
+
218
+ PROJECT_ID="$service_project_id"
219
+ export PROJECT_ID
220
+ write_env_var "PROJECT_ID" "$PROJECT_ID"
221
+
222
+ FIREBASE_SERVICE_ACCOUNT_EMAIL="$service_client_email"
223
+ export FIREBASE_SERVICE_ACCOUNT_EMAIL
224
+ write_env_var "FIREBASE_SERVICE_ACCOUNT_EMAIL" "$FIREBASE_SERVICE_ACCOUNT_EMAIL"
225
+
226
+ FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY="$normalized_private_key"
227
+ export FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY
228
+ write_env_var "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" "$FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY"
229
+
230
+ echo -e "${GREEN}✅ Imported Firebase service account credentials from $admin_service_path${NC}"
231
+ }
232
+
233
+ generate_manifest_signing_key_pair() {
234
+ local private_key_file
235
+ local public_key_file
236
+ private_key_file=$(mktemp)
237
+ public_key_file=$(mktemp)
238
+
239
+ 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
240
+ rm -f "$private_key_file" "$public_key_file"
241
+ return 1
242
+ fi
243
+
244
+ local private_key_pem
245
+ local public_key_pem
246
+ private_key_pem=$(cat "$private_key_file")
247
+ public_key_pem=$(cat "$public_key_file")
248
+ rm -f "$private_key_file" "$public_key_file"
249
+
250
+ private_key_pem="${private_key_pem//$'\r'/}"
251
+ public_key_pem="${public_key_pem//$'\r'/}"
252
+
253
+ MANIFEST_SIGNING_PRIVATE_KEY="${private_key_pem//$'\n'/\\n}"
254
+ MANIFEST_SIGNING_PUBLIC_KEY="${public_key_pem//$'\n'/\\n}"
255
+
256
+ export MANIFEST_SIGNING_PRIVATE_KEY
257
+ export MANIFEST_SIGNING_PUBLIC_KEY
258
+
259
+ write_env_var "MANIFEST_SIGNING_PRIVATE_KEY" "$MANIFEST_SIGNING_PRIVATE_KEY"
260
+ write_env_var "MANIFEST_SIGNING_PUBLIC_KEY" "$MANIFEST_SIGNING_PUBLIC_KEY"
261
+
262
+ return 0
263
+ }
264
+
265
+ configure_manifest_signing_credentials() {
266
+ echo -e "${BLUE}🛡️ MANIFEST SIGNING CONFIGURATION${NC}"
267
+ echo "================================="
268
+
269
+ local should_generate="false"
270
+ local regenerate_choice=""
271
+
272
+ if [ "$update_env" = "true" ]; then
273
+ should_generate="true"
274
+ elif [ -z "$MANIFEST_SIGNING_PRIVATE_KEY" ] || is_placeholder "$MANIFEST_SIGNING_PRIVATE_KEY" || [ -z "$MANIFEST_SIGNING_PUBLIC_KEY" ] || is_placeholder "$MANIFEST_SIGNING_PUBLIC_KEY"; then
275
+ should_generate="true"
276
+ else
277
+ echo -e "${GREEN}Current manifest signing key pair: [HIDDEN]${NC}"
278
+ read -p "Generate new manifest signing key pair? (press Enter to keep current, or type 'y' to regenerate): " regenerate_choice
279
+ if [ "$regenerate_choice" = "y" ] || [ "$regenerate_choice" = "Y" ]; then
280
+ should_generate="true"
281
+ fi
282
+ fi
283
+
284
+ if [ "$should_generate" = "true" ]; then
285
+ echo -e "${YELLOW}Generating manifest signing RSA key pair...${NC}"
286
+ if generate_manifest_signing_key_pair; then
287
+ echo -e "${GREEN}✅ Manifest signing key pair generated${NC}"
288
+ else
289
+ echo -e "${RED}❌ Error: Failed to generate manifest signing key pair${NC}"
290
+ exit 1
291
+ fi
292
+ else
293
+ echo -e "${GREEN}✅ Keeping current manifest signing key pair${NC}"
294
+ fi
295
+
296
+ if [ -z "$MANIFEST_SIGNING_KEY_ID" ] || is_placeholder "$MANIFEST_SIGNING_KEY_ID"; then
297
+ MANIFEST_SIGNING_KEY_ID="forensic-signing-key-v1"
298
+ export MANIFEST_SIGNING_KEY_ID
299
+ write_env_var "MANIFEST_SIGNING_KEY_ID" "$MANIFEST_SIGNING_KEY_ID"
300
+ echo -e "${GREEN}✅ MANIFEST_SIGNING_KEY_ID set to default: $MANIFEST_SIGNING_KEY_ID${NC}"
301
+ else
302
+ echo -e "${GREEN}✅ MANIFEST_SIGNING_KEY_ID: $MANIFEST_SIGNING_KEY_ID${NC}"
303
+ fi
304
+
305
+ echo ""
306
+ }
307
+
308
+ # Validate required variables
309
+ required_vars=(
310
+ # Core Cloudflare Configuration
311
+ "ACCOUNT_ID"
312
+
313
+ # Shared Authentication & Storage
314
+ "USER_DB_AUTH"
315
+ "R2_KEY_SECRET"
316
+ "IMAGES_API_TOKEN"
317
+
318
+ # Firebase Auth Configuration
319
+ "API_KEY"
320
+ "AUTH_DOMAIN"
321
+ "PROJECT_ID"
322
+ "STORAGE_BUCKET"
323
+ "MESSAGING_SENDER_ID"
324
+ "APP_ID"
325
+ "MEASUREMENT_ID"
326
+ "FIREBASE_SERVICE_ACCOUNT_EMAIL"
327
+ "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY"
328
+
329
+ # Pages Configuration
330
+ "PAGES_PROJECT_NAME"
331
+ "PAGES_CUSTOM_DOMAIN"
332
+
333
+ # Worker Names (required for config replacement)
334
+ "KEYS_WORKER_NAME"
335
+ "USER_WORKER_NAME"
336
+ "DATA_WORKER_NAME"
337
+ "AUDIT_WORKER_NAME"
338
+ "IMAGES_WORKER_NAME"
339
+ "PDF_WORKER_NAME"
340
+
341
+ # Worker Domains (required for config replacement)
342
+ "KEYS_WORKER_DOMAIN"
343
+ "USER_WORKER_DOMAIN"
344
+ "DATA_WORKER_DOMAIN"
345
+ "AUDIT_WORKER_DOMAIN"
346
+ "IMAGES_WORKER_DOMAIN"
347
+ "PDF_WORKER_DOMAIN"
348
+
349
+ # Storage Configuration (required for config replacement)
350
+ "DATA_BUCKET_NAME"
351
+ "AUDIT_BUCKET_NAME"
352
+ "KV_STORE_ID"
353
+
354
+ # Worker-Specific Secrets (required for deployment)
355
+ "KEYS_AUTH"
356
+ "ACCOUNT_HASH"
357
+ "API_TOKEN"
358
+ "HMAC_KEY"
359
+ "MANIFEST_SIGNING_PRIVATE_KEY"
360
+ "MANIFEST_SIGNING_KEY_ID"
361
+ "MANIFEST_SIGNING_PUBLIC_KEY"
362
+ )
363
+
364
+ validate_required_vars() {
365
+ echo -e "${YELLOW}🔍 Validating required environment variables...${NC}"
366
+ for var in "${required_vars[@]}"; do
367
+ if [ -z "${!var}" ] || is_placeholder "${!var}"; then
368
+ echo -e "${RED}❌ Error: $var is not set in .env file or is a placeholder${NC}"
369
+ exit 1
370
+ fi
371
+ done
372
+ echo -e "${GREEN}✅ All required variables found${NC}"
373
+ }
374
+
375
+ # Function to copy example configuration files
376
+ copy_example_configs() {
377
+ echo -e "\n${BLUE}📋 Copying example configuration files...${NC}"
378
+
379
+ # Copy app configuration files
380
+ echo -e "${YELLOW} Copying app configuration files...${NC}"
381
+
382
+ # Copy app config-example directory to config (always sync non-admin files)
383
+ if [ -d "app/config-example" ]; then
384
+ local admin_service_backup=""
385
+ local copied_config_files=0
386
+ local skipped_existing_files=0
387
+
388
+ if [ -f "app/config/admin-service.json" ]; then
389
+ admin_service_backup=$(mktemp)
390
+ cp "app/config/admin-service.json" "$admin_service_backup"
391
+ fi
392
+
393
+ if [ "$update_env" = "true" ]; then
394
+ rm -rf app/config
395
+ fi
396
+
397
+ mkdir -p app/config
398
+
399
+ while IFS= read -r source_file; do
400
+ local relative_path
401
+ local destination_file
402
+ relative_path="${source_file#app/config-example/}"
403
+ destination_file="app/config/$relative_path"
404
+
405
+ mkdir -p "$(dirname "$destination_file")"
406
+
407
+ if [ "$update_env" = "true" ] || [ ! -f "$destination_file" ]; then
408
+ cp "$source_file" "$destination_file"
409
+ copied_config_files=$((copied_config_files + 1))
410
+ else
411
+ skipped_existing_files=$((skipped_existing_files + 1))
412
+ fi
413
+ done < <(find app/config-example -type f ! -name "admin-service.json")
414
+
415
+ # Ensure example credentials are never copied from config-example.
416
+ rm -f app/config/admin-service.json
417
+
418
+ if [ -n "$admin_service_backup" ] && [ -f "$admin_service_backup" ]; then
419
+ cp "$admin_service_backup" "app/config/admin-service.json"
420
+ rm -f "$admin_service_backup"
421
+ echo -e "${GREEN} ✅ app: preserved existing admin-service.json${NC}"
422
+ else
423
+ echo -e "${YELLOW} ⚠️ app: skipped copying admin-service.json (provide your own credentials file)${NC}"
424
+ fi
425
+
426
+ if [ "$update_env" = "true" ]; then
427
+ echo -e "${GREEN} ✅ app: config directory reset from config-example (excluding admin-service.json)${NC}"
428
+ else
429
+ echo -e "${GREEN} ✅ app: synced missing files from config-example (excluding admin-service.json)${NC}"
430
+ fi
431
+
432
+ if [ "$skipped_existing_files" -gt 0 ]; then
433
+ echo -e "${YELLOW} ℹ️ app: kept $skipped_existing_files existing config file(s)${NC}"
434
+ fi
435
+
436
+ echo -e "${GREEN} ✅ app: copied $copied_config_files config file(s) from config-example${NC}"
437
+ fi
438
+
439
+ # Navigate to each worker directory and copy the example file
440
+ echo -e "${YELLOW} Copying worker configuration files...${NC}"
441
+
442
+ cd workers/keys-worker
443
+ if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
444
+ cp wrangler.jsonc.example wrangler.jsonc
445
+ echo -e "${GREEN} ✅ keys-worker: wrangler.jsonc created from example${NC}"
446
+ elif [ -f "wrangler.jsonc" ]; then
447
+ echo -e "${YELLOW} ⚠️ keys-worker: wrangler.jsonc already exists, skipping copy${NC}"
448
+ fi
449
+
450
+ cd ../user-worker
451
+ if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
452
+ cp wrangler.jsonc.example wrangler.jsonc
453
+ echo -e "${GREEN} ✅ user-worker: wrangler.jsonc created from example${NC}"
454
+ elif [ -f "wrangler.jsonc" ]; then
455
+ echo -e "${YELLOW} ⚠️ user-worker: wrangler.jsonc already exists, skipping copy${NC}"
456
+ fi
457
+
458
+ cd ../data-worker
459
+ if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
460
+ cp wrangler.jsonc.example wrangler.jsonc
461
+ echo -e "${GREEN} ✅ data-worker: wrangler.jsonc created from example${NC}"
462
+ elif [ -f "wrangler.jsonc" ]; then
463
+ echo -e "${YELLOW} ⚠️ data-worker: wrangler.jsonc already exists, skipping copy${NC}"
464
+ fi
465
+
466
+ cd ../audit-worker
467
+ if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
468
+ cp wrangler.jsonc.example wrangler.jsonc
469
+ echo -e "${GREEN} ✅ audit-worker: wrangler.jsonc created from example${NC}"
470
+ elif [ -f "wrangler.jsonc" ]; then
471
+ echo -e "${YELLOW} ⚠️ audit-worker: wrangler.jsonc already exists, skipping copy${NC}"
472
+ fi
473
+
474
+ cd ../image-worker
475
+ if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
476
+ cp wrangler.jsonc.example wrangler.jsonc
477
+ echo -e "${GREEN} ✅ image-worker: wrangler.jsonc created from example${NC}"
478
+ elif [ -f "wrangler.jsonc" ]; then
479
+ echo -e "${YELLOW} ⚠️ image-worker: wrangler.jsonc already exists, skipping copy${NC}"
480
+ fi
481
+
482
+ cd ../pdf-worker
483
+ if [ -f "wrangler.jsonc.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.jsonc" ]; }; then
484
+ cp wrangler.jsonc.example wrangler.jsonc
485
+ echo -e "${GREEN} ✅ pdf-worker: wrangler.jsonc created from example${NC}"
486
+ elif [ -f "wrangler.jsonc" ]; then
487
+ echo -e "${YELLOW} ⚠️ pdf-worker: wrangler.jsonc already exists, skipping copy${NC}"
488
+ fi
489
+
490
+ # Return to project root
491
+ cd ../..
492
+
493
+ # Copy worker source template files
494
+ echo -e "${YELLOW} Copying worker source template files...${NC}"
495
+
496
+ if [ -f "workers/keys-worker/src/keys.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/keys-worker/src/keys.ts" ]; }; then
497
+ cp workers/keys-worker/src/keys.example.ts workers/keys-worker/src/keys.ts
498
+ echo -e "${GREEN} ✅ keys-worker: keys.ts created from example${NC}"
499
+ elif [ -f "workers/keys-worker/src/keys.ts" ]; then
500
+ echo -e "${YELLOW} ⚠️ keys-worker: keys.ts already exists, skipping copy${NC}"
501
+ fi
502
+
503
+ if [ -f "workers/user-worker/src/user-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/user-worker/src/user-worker.ts" ]; }; then
504
+ cp workers/user-worker/src/user-worker.example.ts workers/user-worker/src/user-worker.ts
505
+ echo -e "${GREEN} ✅ user-worker: user-worker.ts created from example${NC}"
506
+ elif [ -f "workers/user-worker/src/user-worker.ts" ]; then
507
+ echo -e "${YELLOW} ⚠️ user-worker: user-worker.ts already exists, skipping copy${NC}"
508
+ fi
509
+
510
+ if [ -f "workers/data-worker/src/data-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/data-worker/src/data-worker.ts" ]; }; then
511
+ cp workers/data-worker/src/data-worker.example.ts workers/data-worker/src/data-worker.ts
512
+ echo -e "${GREEN} ✅ data-worker: data-worker.ts created from example${NC}"
513
+ elif [ -f "workers/data-worker/src/data-worker.ts" ]; then
514
+ echo -e "${YELLOW} ⚠️ data-worker: data-worker.ts already exists, skipping copy${NC}"
515
+ fi
516
+
517
+ if [ -f "workers/audit-worker/src/audit-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/audit-worker/src/audit-worker.ts" ]; }; then
518
+ cp workers/audit-worker/src/audit-worker.example.ts workers/audit-worker/src/audit-worker.ts
519
+ echo -e "${GREEN} ✅ audit-worker: audit-worker.ts created from example${NC}"
520
+ elif [ -f "workers/audit-worker/src/audit-worker.ts" ]; then
521
+ echo -e "${YELLOW} ⚠️ audit-worker: audit-worker.ts already exists, skipping copy${NC}"
522
+ fi
523
+
524
+ if [ -f "workers/image-worker/src/image-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/image-worker/src/image-worker.ts" ]; }; then
525
+ cp workers/image-worker/src/image-worker.example.ts workers/image-worker/src/image-worker.ts
526
+ echo -e "${GREEN} ✅ image-worker: image-worker.ts created from example${NC}"
527
+ elif [ -f "workers/image-worker/src/image-worker.ts" ]; then
528
+ echo -e "${YELLOW} ⚠️ image-worker: image-worker.ts already exists, skipping copy${NC}"
529
+ fi
530
+
531
+ if [ -f "workers/pdf-worker/src/pdf-worker.example.ts" ] && { [ "$update_env" = "true" ] || [ ! -f "workers/pdf-worker/src/pdf-worker.ts" ]; }; then
532
+ cp workers/pdf-worker/src/pdf-worker.example.ts workers/pdf-worker/src/pdf-worker.ts
533
+ echo -e "${GREEN} ✅ pdf-worker: pdf-worker.ts created from example${NC}"
534
+ elif [ -f "workers/pdf-worker/src/pdf-worker.ts" ]; then
535
+ echo -e "${YELLOW} ⚠️ pdf-worker: pdf-worker.ts already exists, skipping copy${NC}"
536
+ fi
537
+
538
+ # Copy main wrangler.toml from example
539
+ if [ -f "wrangler.toml.example" ] && { [ "$update_env" = "true" ] || [ ! -f "wrangler.toml" ]; }; then
540
+ cp wrangler.toml.example wrangler.toml
541
+ echo -e "${GREEN} ✅ root: wrangler.toml created from example${NC}"
542
+ elif [ -f "wrangler.toml" ]; then
543
+ echo -e "${YELLOW} ⚠️ root: wrangler.toml already exists, skipping copy${NC}"
544
+ fi
545
+
546
+ echo -e "${GREEN}✅ Configuration file copying completed${NC}"
547
+ }
548
+
549
+ # Copy example configuration files
550
+ copy_example_configs
551
+
552
+ # Load required Firebase admin service credentials from app/config/admin-service.json
553
+ load_admin_service_credentials
554
+
555
+ # Function to prompt for environment variables and update .env file
556
+ prompt_for_secrets() {
557
+ echo -e "\n${BLUE}🔐 Environment Variables Setup${NC}"
558
+ echo "=============================="
559
+ echo -e "${YELLOW}Please provide values for the following environment variables.${NC}"
560
+ echo -e "${YELLOW}Press Enter to keep existing values (if any).${NC}"
561
+ echo ""
562
+
563
+ # Create or backup existing .env
564
+ if [ -f ".env" ] && [ "$update_env" != "true" ]; then
565
+ cp .env .env.backup
566
+ echo -e "${GREEN}📄 Existing .env backed up to .env.backup${NC}"
567
+ fi
568
+
569
+ # Copy .env.example to .env if it doesn't exist
570
+ if [ ! -f ".env" ]; then
571
+ cp .env.example .env
572
+ echo -e "${GREEN}📄 Created .env from .env.example${NC}"
573
+ fi
574
+
575
+ # Function to prompt for a variable
576
+ prompt_for_var() {
577
+ local var_name=$1
578
+ local description=$2
579
+ local current_value="${!var_name}"
580
+ local new_value=""
581
+ local allow_keep="false"
582
+
583
+ # Auto-generate specific authentication secrets - but allow keeping current
584
+ if [ "$var_name" = "USER_DB_AUTH" ] || [ "$var_name" = "R2_KEY_SECRET" ] || [ "$var_name" = "KEYS_AUTH" ]; then
585
+ echo -e "${BLUE}$var_name${NC}"
586
+ echo -e "${YELLOW}$description${NC}"
587
+
588
+ 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" ]; then
589
+ # Current value exists and is not a placeholder
590
+ echo -e "${GREEN}Current value: [HIDDEN]${NC}"
591
+ read -p "Generate new secret? (press Enter to keep current, or type 'y' to generate): " gen_choice
592
+
593
+ if [ "$gen_choice" = "y" ] || [ "$gen_choice" = "Y" ]; then
594
+ new_value=$(openssl rand -hex 32 2>/dev/null || echo "")
595
+ if [ -n "$new_value" ]; then
596
+ echo -e "${GREEN}✅ $var_name auto-generated${NC}"
597
+ else
598
+ while true; do
599
+ echo -e "${RED}❌ Failed to auto-generate, please enter manually:${NC}"
600
+ read -p "Enter value: " new_value
601
+ if [ -z "$new_value" ]; then
602
+ echo -e "${RED}❌ A value is required.${NC}"
603
+ continue
604
+ fi
605
+ if is_placeholder "$new_value"; then
606
+ echo -e "${RED}❌ Placeholder values are not allowed.${NC}"
607
+ new_value=""
608
+ continue
609
+ fi
610
+ break
611
+ done
612
+ fi
613
+ else
614
+ # User wants to keep current value
615
+ new_value=""
616
+ fi
617
+ else
618
+ # No current value or placeholder value - auto-generate
619
+ echo -e "${YELLOW}Auto-generating secret...${NC}"
620
+ new_value=$(openssl rand -hex 32 2>/dev/null || echo "")
621
+ if [ -n "$new_value" ]; then
622
+ echo -e "${GREEN}✅ $var_name auto-generated${NC}"
623
+ else
624
+ while true; do
625
+ echo -e "${RED}❌ Failed to auto-generate, please enter manually:${NC}"
626
+ read -p "Enter value: " new_value
627
+ if [ -z "$new_value" ]; then
628
+ echo -e "${RED}❌ A value is required.${NC}"
629
+ continue
630
+ fi
631
+ if is_placeholder "$new_value"; then
632
+ echo -e "${RED}❌ Placeholder values are not allowed.${NC}"
633
+ new_value=""
634
+ continue
635
+ fi
636
+ break
637
+ done
638
+ fi
639
+ fi
640
+ else
641
+ # Normal prompt for other variables
642
+ echo -e "${BLUE}$var_name${NC}"
643
+ echo -e "${YELLOW}$description${NC}"
644
+ if [ "$update_env" != "true" ] && [ -n "$current_value" ] && ! is_placeholder "$current_value"; then
645
+ allow_keep="true"
646
+ if [ "$var_name" = "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" ]; then
647
+ echo -e "${GREEN}Current value: [HIDDEN]${NC}"
648
+ else
649
+ echo -e "${GREEN}Current value: $current_value${NC}"
650
+ fi
651
+ fi
652
+
653
+ while true; do
654
+ if [ "$allow_keep" = "true" ]; then
655
+ read -p "New value (or press Enter to keep current): " new_value
656
+ if [ -z "$new_value" ]; then
657
+ break
658
+ fi
659
+ else
660
+ read -p "Enter value: " new_value
661
+ if [ -z "$new_value" ]; then
662
+ echo -e "${RED}❌ A value is required.${NC}"
663
+ continue
664
+ fi
665
+ fi
666
+
667
+ if is_placeholder "$new_value"; then
668
+ echo -e "${RED}❌ Placeholder values are not allowed.${NC}"
669
+ new_value=""
670
+ continue
671
+ fi
672
+
673
+ break
674
+ done
675
+ fi
676
+
677
+ if [ -n "$new_value" ]; then
678
+ if [ "$var_name" = "PAGES_CUSTOM_DOMAIN" ] || [[ "$var_name" == *_WORKER_DOMAIN ]]; then
679
+ new_value=$(normalize_domain_value "$new_value")
680
+ fi
681
+
682
+ # Update the .env file
683
+ write_env_var "$var_name" "$new_value"
684
+
685
+ export "$var_name=$new_value"
686
+ echo -e "${GREEN}✅ $var_name updated${NC}"
687
+ elif [ -n "$current_value" ]; then
688
+ # Keep values aligned with .env.example ordering and remove stale duplicates.
689
+ write_env_var "$var_name" "$current_value"
690
+ echo -e "${GREEN}✅ Keeping current value for $var_name${NC}"
691
+ fi
692
+ echo ""
693
+ }
694
+
695
+ echo -e "${BLUE}📊 CLOUDFLARE CORE CONFIGURATION${NC}"
696
+ echo "=================================="
697
+ prompt_for_var "ACCOUNT_ID" "Your Cloudflare Account ID"
698
+
699
+ echo -e "${BLUE}🔐 SHARED AUTHENTICATION & STORAGE${NC}"
700
+ echo "==================================="
701
+ prompt_for_var "USER_DB_AUTH" "Custom user database authentication token (generate with: openssl rand -hex 16)"
702
+ prompt_for_var "R2_KEY_SECRET" "Custom R2 storage authentication token (generate with: openssl rand -hex 16)"
703
+ prompt_for_var "IMAGES_API_TOKEN" "Cloudflare Images API token (shared between workers)"
704
+
705
+ echo -e "${BLUE}🔥 FIREBASE AUTH CONFIGURATION${NC}"
706
+ echo "==============================="
707
+ prompt_for_var "API_KEY" "Firebase API key"
708
+ prompt_for_var "AUTH_DOMAIN" "Firebase auth domain (project-id.firebaseapp.com)"
709
+ prompt_for_var "STORAGE_BUCKET" "Firebase storage bucket"
710
+ prompt_for_var "MESSAGING_SENDER_ID" "Firebase messaging sender ID"
711
+ prompt_for_var "APP_ID" "Firebase app ID"
712
+ prompt_for_var "MEASUREMENT_ID" "Firebase measurement ID (optional)"
713
+ echo -e "${GREEN}Using PROJECT_ID and service account values from app/config/admin-service.json${NC}"
714
+
715
+ echo -e "${BLUE}📄 PAGES CONFIGURATION${NC}"
716
+ echo "======================"
717
+ prompt_for_var "PAGES_PROJECT_NAME" "Your Cloudflare Pages project name"
718
+ prompt_for_var "PAGES_CUSTOM_DOMAIN" "Your custom domain (e.g., striae.org) - DO NOT include https://"
719
+
720
+ echo -e "${BLUE}🔑 WORKER NAMES & DOMAINS${NC}"
721
+ echo "========================="
722
+ prompt_for_var "KEYS_WORKER_NAME" "Keys worker name"
723
+ prompt_for_var "KEYS_WORKER_DOMAIN" "Keys worker domain (e.g., keys.striae.org) - DO NOT include https://"
724
+ prompt_for_var "USER_WORKER_NAME" "User worker name"
725
+ prompt_for_var "USER_WORKER_DOMAIN" "User worker domain (e.g., users.striae.org) - DO NOT include https://"
726
+ prompt_for_var "DATA_WORKER_NAME" "Data worker name"
727
+ prompt_for_var "DATA_WORKER_DOMAIN" "Data worker domain (e.g., data.striae.org) - DO NOT include https://"
728
+ prompt_for_var "AUDIT_WORKER_NAME" "Audit worker name"
729
+ prompt_for_var "AUDIT_WORKER_DOMAIN" "Audit worker domain (e.g., audit.striae.org) - DO NOT include https://"
730
+ prompt_for_var "IMAGES_WORKER_NAME" "Images worker name"
731
+ prompt_for_var "IMAGES_WORKER_DOMAIN" "Images worker domain (e.g., images.striae.org) - DO NOT include https://"
732
+ prompt_for_var "PDF_WORKER_NAME" "PDF worker name"
733
+ prompt_for_var "PDF_WORKER_DOMAIN" "PDF worker domain (e.g., pdf.striae.org) - DO NOT include https://"
734
+
735
+ echo -e "${BLUE}🗄️ STORAGE CONFIGURATION${NC}"
736
+ echo "========================="
737
+ prompt_for_var "DATA_BUCKET_NAME" "Your R2 bucket name for case data storage"
738
+ prompt_for_var "AUDIT_BUCKET_NAME" "Your R2 bucket name for audit logs (separate from data bucket)"
739
+ prompt_for_var "KV_STORE_ID" "Your KV namespace ID (UUID format)"
740
+
741
+ echo -e "${BLUE}🔐 SERVICE-SPECIFIC SECRETS${NC}"
742
+ echo "============================"
743
+ prompt_for_var "KEYS_AUTH" "Keys worker authentication token (generate with: openssl rand -hex 16)"
744
+ prompt_for_var "ACCOUNT_HASH" "Cloudflare Images Account Hash"
745
+ prompt_for_var "API_TOKEN" "Cloudflare Images API token (for Images Worker)"
746
+ prompt_for_var "HMAC_KEY" "Cloudflare Images HMAC signing key"
747
+
748
+ configure_manifest_signing_credentials
749
+
750
+ # Reload the updated .env file
751
+ source .env
752
+
753
+ echo -e "${GREEN}🎉 Environment variables setup completed!${NC}"
754
+ echo -e "${BLUE}📄 All values saved to .env file${NC}"
755
+ }
756
+
757
+ # Always prompt for secrets to ensure configuration
758
+ prompt_for_secrets
759
+
760
+ # Validate after secrets have been configured
761
+ validate_required_vars
762
+
763
+ # Function to replace variables in wrangler configuration files
764
+ update_wrangler_configs() {
765
+ echo -e "\n${BLUE}🔧 Updating wrangler configuration files...${NC}"
766
+
767
+ local normalized_pages_custom_domain
768
+ local escaped_pages_custom_domain
769
+
770
+ normalized_pages_custom_domain=$(normalize_domain_value "$PAGES_CUSTOM_DOMAIN")
771
+ PAGES_CUSTOM_DOMAIN="$normalized_pages_custom_domain"
772
+ export PAGES_CUSTOM_DOMAIN
773
+ write_env_var "PAGES_CUSTOM_DOMAIN" "$PAGES_CUSTOM_DOMAIN"
774
+ escaped_pages_custom_domain=$(escape_for_sed_replacement "$PAGES_CUSTOM_DOMAIN")
775
+
776
+ # Audit Worker
777
+ if [ -f "workers/audit-worker/wrangler.jsonc" ]; then
778
+ echo -e "${YELLOW} Updating audit-worker/wrangler.jsonc...${NC}"
779
+ sed -i "s/\"AUDIT_WORKER_NAME\"/\"$AUDIT_WORKER_NAME\"/g" workers/audit-worker/wrangler.jsonc
780
+ sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/audit-worker/wrangler.jsonc
781
+ sed -i "s/\"AUDIT_WORKER_DOMAIN\"/\"$AUDIT_WORKER_DOMAIN\"/g" workers/audit-worker/wrangler.jsonc
782
+ sed -i "s/\"AUDIT_BUCKET_NAME\"/\"$AUDIT_BUCKET_NAME\"/g" workers/audit-worker/wrangler.jsonc
783
+ echo -e "${GREEN} ✅ audit-worker configuration updated${NC}"
784
+ fi
785
+
786
+ # Update audit-worker source file domain placeholders
787
+ if [ -f "workers/audit-worker/src/audit-worker.ts" ]; then
788
+ echo -e "${YELLOW} Updating audit-worker source placeholders...${NC}"
789
+ sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/audit-worker/src/audit-worker.ts
790
+ echo -e "${GREEN} ✅ audit-worker source placeholders updated${NC}"
791
+ fi
792
+
793
+ # Data Worker
794
+ if [ -f "workers/data-worker/wrangler.jsonc" ]; then
795
+ echo -e "${YELLOW} Updating data-worker/wrangler.jsonc...${NC}"
796
+ sed -i "s/\"DATA_WORKER_NAME\"/\"$DATA_WORKER_NAME\"/g" workers/data-worker/wrangler.jsonc
797
+ sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/data-worker/wrangler.jsonc
798
+ sed -i "s/\"DATA_WORKER_DOMAIN\"/\"$DATA_WORKER_DOMAIN\"/g" workers/data-worker/wrangler.jsonc
799
+ sed -i "s/\"DATA_BUCKET_NAME\"/\"$DATA_BUCKET_NAME\"/g" workers/data-worker/wrangler.jsonc
800
+ echo -e "${GREEN} ✅ data-worker configuration updated${NC}"
801
+ fi
802
+
803
+ # Update data-worker source file domain placeholders
804
+ if [ -f "workers/data-worker/src/data-worker.ts" ]; then
805
+ echo -e "${YELLOW} Updating data-worker source placeholders...${NC}"
806
+ sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/data-worker/src/data-worker.ts
807
+ echo -e "${GREEN} ✅ data-worker source placeholders updated${NC}"
808
+ fi
809
+
810
+ # Image Worker
811
+ if [ -f "workers/image-worker/wrangler.jsonc" ]; then
812
+ echo -e "${YELLOW} Updating image-worker/wrangler.jsonc...${NC}"
813
+ sed -i "s/\"IMAGES_WORKER_NAME\"/\"$IMAGES_WORKER_NAME\"/g" workers/image-worker/wrangler.jsonc
814
+ sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/image-worker/wrangler.jsonc
815
+ sed -i "s/\"IMAGES_WORKER_DOMAIN\"/\"$IMAGES_WORKER_DOMAIN\"/g" workers/image-worker/wrangler.jsonc
816
+ echo -e "${GREEN} ✅ image-worker configuration updated${NC}"
817
+ fi
818
+
819
+ # Update image-worker source file domain placeholders
820
+ if [ -f "workers/image-worker/src/image-worker.ts" ]; then
821
+ echo -e "${YELLOW} Updating image-worker source placeholders...${NC}"
822
+ sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/image-worker/src/image-worker.ts
823
+ echo -e "${GREEN} ✅ image-worker source placeholders updated${NC}"
824
+ fi
825
+
826
+ # Keys Worker
827
+ if [ -f "workers/keys-worker/wrangler.jsonc" ]; then
828
+ echo -e "${YELLOW} Updating keys-worker/wrangler.jsonc...${NC}"
829
+ sed -i "s/\"KEYS_WORKER_NAME\"/\"$KEYS_WORKER_NAME\"/g" workers/keys-worker/wrangler.jsonc
830
+ sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/keys-worker/wrangler.jsonc
831
+ sed -i "s/\"KEYS_WORKER_DOMAIN\"/\"$KEYS_WORKER_DOMAIN\"/g" workers/keys-worker/wrangler.jsonc
832
+ echo -e "${GREEN} ✅ keys-worker configuration updated${NC}"
833
+ fi
834
+
835
+ # Update keys-worker source file domain placeholders
836
+ if [ -f "workers/keys-worker/src/keys.ts" ]; then
837
+ echo -e "${YELLOW} Updating keys-worker source placeholders...${NC}"
838
+ sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/keys-worker/src/keys.ts
839
+ echo -e "${GREEN} ✅ keys-worker source placeholders updated${NC}"
840
+ fi
841
+
842
+ # PDF Worker
843
+ if [ -f "workers/pdf-worker/wrangler.jsonc" ]; then
844
+ echo -e "${YELLOW} Updating pdf-worker/wrangler.jsonc...${NC}"
845
+ sed -i "s/\"PDF_WORKER_NAME\"/\"$PDF_WORKER_NAME\"/g" workers/pdf-worker/wrangler.jsonc
846
+ sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/pdf-worker/wrangler.jsonc
847
+ sed -i "s/\"PDF_WORKER_DOMAIN\"/\"$PDF_WORKER_DOMAIN\"/g" workers/pdf-worker/wrangler.jsonc
848
+ echo -e "${GREEN} ✅ pdf-worker configuration updated${NC}"
849
+ fi
850
+
851
+ # Update pdf-worker source file domain placeholders
852
+ if [ -f "workers/pdf-worker/src/pdf-worker.ts" ]; then
853
+ echo -e "${YELLOW} Updating pdf-worker source placeholders...${NC}"
854
+ sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/pdf-worker/src/pdf-worker.ts
855
+ echo -e "${GREEN} ✅ pdf-worker source placeholders updated${NC}"
856
+ fi
857
+
858
+ # User Worker
859
+ if [ -f "workers/user-worker/wrangler.jsonc" ]; then
860
+ echo -e "${YELLOW} Updating user-worker/wrangler.jsonc...${NC}"
861
+ sed -i "s/\"USER_WORKER_NAME\"/\"$USER_WORKER_NAME\"/g" workers/user-worker/wrangler.jsonc
862
+ sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/user-worker/wrangler.jsonc
863
+ sed -i "s/\"USER_WORKER_DOMAIN\"/\"$USER_WORKER_DOMAIN\"/g" workers/user-worker/wrangler.jsonc
864
+ sed -i "s/\"KV_STORE_ID\"/\"$KV_STORE_ID\"/g" workers/user-worker/wrangler.jsonc
865
+ echo -e "${GREEN} ✅ user-worker configuration updated${NC}"
866
+ fi
867
+
868
+ # Update user-worker source file domain placeholders
869
+ if [ -f "workers/user-worker/src/user-worker.ts" ]; then
870
+ echo -e "${YELLOW} Updating user-worker source placeholders...${NC}"
871
+ sed -i "s|'Access-Control-Allow-Origin': '[^']*'|'Access-Control-Allow-Origin': 'https://$escaped_pages_custom_domain'|g" workers/user-worker/src/user-worker.ts
872
+ sed -i "s|'DATA_WORKER_DOMAIN'|'https://$DATA_WORKER_DOMAIN'|g" workers/user-worker/src/user-worker.ts
873
+ sed -i "s|'IMAGES_WORKER_DOMAIN'|'https://$IMAGES_WORKER_DOMAIN'|g" workers/user-worker/src/user-worker.ts
874
+ echo -e "${GREEN} ✅ user-worker source placeholders updated${NC}"
875
+ fi
876
+
877
+ # Main wrangler.toml
878
+ if [ -f "wrangler.toml" ]; then
879
+ echo -e "${YELLOW} Updating wrangler.toml...${NC}"
880
+ sed -i "s/\"PAGES_PROJECT_NAME\"/\"$PAGES_PROJECT_NAME\"/g" wrangler.toml
881
+ echo -e "${GREEN} ✅ main wrangler.toml configuration updated${NC}"
882
+ fi
883
+
884
+ # Update app configuration files
885
+ echo -e "${YELLOW} Updating app configuration files...${NC}"
886
+
887
+ # Update app/config/config.json
888
+ if [ -f "app/config/config.json" ]; then
889
+ echo -e "${YELLOW} Updating app/config/config.json...${NC}"
890
+ local escaped_manifest_signing_key_id
891
+ local escaped_manifest_signing_public_key
892
+ escaped_manifest_signing_key_id=$(escape_for_sed_replacement "$MANIFEST_SIGNING_KEY_ID")
893
+ escaped_manifest_signing_public_key=$(escape_for_sed_replacement "$MANIFEST_SIGNING_PUBLIC_KEY")
894
+
895
+ sed -i "s|\"url\": \"[^\"]*\"|\"url\": \"https://$escaped_pages_custom_domain\"|g" app/config/config.json
896
+ sed -i "s|\"DATA_WORKER_CUSTOM_DOMAIN\"|\"https://$DATA_WORKER_DOMAIN\"|g" app/config/config.json
897
+ sed -i "s|\"AUDIT_WORKER_CUSTOM_DOMAIN\"|\"https://$AUDIT_WORKER_DOMAIN\"|g" app/config/config.json
898
+ sed -i "s|\"KEYS_WORKER_CUSTOM_DOMAIN\"|\"https://$KEYS_WORKER_DOMAIN\"|g" app/config/config.json
899
+ sed -i "s|\"IMAGE_WORKER_CUSTOM_DOMAIN\"|\"https://$IMAGES_WORKER_DOMAIN\"|g" app/config/config.json
900
+ sed -i "s|\"USER_WORKER_CUSTOM_DOMAIN\"|\"https://$USER_WORKER_DOMAIN\"|g" app/config/config.json
901
+ sed -i "s|\"PDF_WORKER_CUSTOM_DOMAIN\"|\"https://$PDF_WORKER_DOMAIN\"|g" app/config/config.json
902
+ sed -i "s|\"YOUR_KEYS_AUTH_TOKEN\"|\"$KEYS_AUTH\"|g" app/config/config.json
903
+ sed -i "s|\"MANIFEST_SIGNING_KEY_ID\"|\"$escaped_manifest_signing_key_id\"|g" app/config/config.json
904
+ sed -i "s|\"MANIFEST_SIGNING_PUBLIC_KEY\"|\"$escaped_manifest_signing_public_key\"|g" app/config/config.json
905
+ echo -e "${GREEN} ✅ app config.json updated${NC}"
906
+ fi
907
+
908
+ # Update app/config/meta-config.json
909
+ if [ -f "app/config/meta-config.json" ]; then
910
+ echo -e "${YELLOW} Updating app/config/meta-config.json...${NC}"
911
+ sed -i "s|\"url\": \"[^\"]*\"|\"url\": \"https://$escaped_pages_custom_domain\"|g" app/config/meta-config.json
912
+ echo -e "${GREEN} ✅ app meta-config.json updated${NC}"
913
+ fi
914
+
915
+ # Update app/config/firebase.ts
916
+ if [ -f "app/config/firebase.ts" ]; then
917
+ echo -e "${YELLOW} Updating app/config/firebase.ts...${NC}"
918
+ sed -i "s|\"YOUR_FIREBASE_API_KEY\"|\"$API_KEY\"|g" app/config/firebase.ts
919
+ sed -i "s|\"YOUR_FIREBASE_AUTH_DOMAIN\"|\"$AUTH_DOMAIN\"|g" app/config/firebase.ts
920
+ sed -i "s|\"YOUR_FIREBASE_PROJECT_ID\"|\"$PROJECT_ID\"|g" app/config/firebase.ts
921
+ sed -i "s|\"YOUR_FIREBASE_STORAGE_BUCKET\"|\"$STORAGE_BUCKET\"|g" app/config/firebase.ts
922
+ sed -i "s|\"YOUR_FIREBASE_MESSAGING_SENDER_ID\"|\"$MESSAGING_SENDER_ID\"|g" app/config/firebase.ts
923
+ sed -i "s|\"YOUR_FIREBASE_APP_ID\"|\"$APP_ID\"|g" app/config/firebase.ts
924
+ sed -i "s|\"YOUR_FIREBASE_MEASUREMENT_ID\"|\"$MEASUREMENT_ID\"|g" app/config/firebase.ts
925
+ echo -e "${GREEN} ✅ app firebase.ts updated${NC}"
926
+ fi
927
+
928
+ echo -e "${GREEN}✅ All configuration files updated${NC}"
929
+ }
930
+
931
+ # Update wrangler configurations
932
+ update_wrangler_configs
933
+
934
+ echo -e "\n${GREEN}🎉 Configuration setup completed!${NC}"
935
+ echo -e "${BLUE}📝 Next Steps:${NC}"
936
+ echo " 1. Install worker dependencies"
937
+ echo " 2. Deploy workers"
938
+ echo " 3. Deploy worker secrets"
939
+ echo " 4. Deploy pages"
940
+ echo -e "\n${GREEN}✨ Ready for deployment!${NC}"