@striae-org/striae 3.1.1 → 3.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.
- package/.env.example +2 -0
- package/app/components/actions/generate-pdf.ts +4 -0
- package/app/config-example/inactivity.ts +1 -1
- package/app/services/firebase.ts +4 -0
- package/app/utils/auth.ts +5 -1
- package/package.json +2 -2
- package/public/_routes.json +1 -0
- package/public/icon-256.png +0 -0
- package/scripts/deploy-config.sh +226 -2
- package/scripts/deploy-worker-secrets.sh +6 -2
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/keys-worker/src/keys.example.ts +1 -0
- package/workers/keys-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/package.json +1 -0
- package/workers/pdf-worker/src/format-striae.ts +2 -1
- package/workers/pdf-worker/src/generated-assets.ts +117 -0
- package/workers/pdf-worker/src/pdf-worker.example.ts +12 -1
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
package/.env.example
CHANGED
|
@@ -43,6 +43,7 @@ PAGES_CUSTOM_DOMAIN=your_custom_domain_here
|
|
|
43
43
|
# ================================
|
|
44
44
|
# KEYS WORKER ENVIRONMENT VARIABLES
|
|
45
45
|
# ================================
|
|
46
|
+
# Worker domains can be entered manually or auto-generated as a 10-character subdomain of PAGES_CUSTOM_DOMAIN during scripts/deploy-config.sh.
|
|
46
47
|
KEYS_WORKER_NAME=your_keys_worker_name_here
|
|
47
48
|
KEYS_WORKER_DOMAIN=your_keys_worker_domain_here
|
|
48
49
|
KEYS_AUTH=your_custom_keys_auth_token_here
|
|
@@ -86,6 +87,7 @@ HMAC_KEY=your_cloudflare_images_hmac_key_here
|
|
|
86
87
|
# ================================
|
|
87
88
|
PDF_WORKER_NAME=your_pdf_worker_name_here
|
|
88
89
|
PDF_WORKER_DOMAIN=your_pdf_worker_domain_here
|
|
90
|
+
PDF_WORKER_AUTH=your_custom_pdf_worker_auth_token_here
|
|
89
91
|
|
|
90
92
|
# ================================
|
|
91
93
|
# QUICK MANUAL SETUP CHECKLIST
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import paths from '~/config/config.json';
|
|
2
2
|
import { AnnotationData } from '~/types/annotations';
|
|
3
3
|
import { auditService } from '~/services/audit.service';
|
|
4
|
+
import { getPdfApiKey } from '~/utils/auth';
|
|
4
5
|
import { User } from 'firebase/auth';
|
|
5
6
|
|
|
6
7
|
interface GeneratePDFParams {
|
|
@@ -74,10 +75,13 @@ export const generatePDF = async ({
|
|
|
74
75
|
data: pdfData,
|
|
75
76
|
};
|
|
76
77
|
|
|
78
|
+
const pdfAuthKey = await getPdfApiKey();
|
|
79
|
+
|
|
77
80
|
const response = await fetch(paths.pdf_worker_url, {
|
|
78
81
|
method: 'POST',
|
|
79
82
|
headers: {
|
|
80
83
|
'Content-Type': 'application/json',
|
|
84
|
+
'X-Custom-Auth-Key': pdfAuthKey,
|
|
81
85
|
},
|
|
82
86
|
body: JSON.stringify(pdfRequest)
|
|
83
87
|
});
|
package/app/services/firebase.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { initializeApp } from 'firebase/app';
|
|
2
2
|
import {
|
|
3
3
|
getAuth,
|
|
4
|
+
setPersistence,
|
|
5
|
+
browserSessionPersistence,
|
|
4
6
|
//connectAuthEmulator,
|
|
5
7
|
} from 'firebase/auth';
|
|
6
8
|
import firebaseConfig from '~/config/firebase';
|
|
@@ -9,6 +11,8 @@ import { getAppVersion } from '~/utils/version';
|
|
|
9
11
|
export const app = initializeApp(firebaseConfig, "Striae");
|
|
10
12
|
export const auth = getAuth(app);
|
|
11
13
|
|
|
14
|
+
setPersistence(auth, browserSessionPersistence);
|
|
15
|
+
|
|
12
16
|
console.log(`Welcome to ${app.name} v${getAppVersion()}`);
|
|
13
17
|
|
|
14
18
|
//Connect to the Firebase Auth emulator if running locally
|
package/app/utils/auth.ts
CHANGED
|
@@ -3,7 +3,7 @@ import paths from '~/config/config.json';
|
|
|
3
3
|
const KEYS_URL = paths.keys_url;
|
|
4
4
|
const KEYS_AUTH = paths.keys_auth;
|
|
5
5
|
|
|
6
|
-
type KeyType = 'USER_DB_AUTH' | 'R2_KEY_SECRET' | 'IMAGES_API_TOKEN' | 'ACCOUNT_HASH';
|
|
6
|
+
type KeyType = 'USER_DB_AUTH' | 'R2_KEY_SECRET' | 'IMAGES_API_TOKEN' | 'ACCOUNT_HASH' | 'PDF_WORKER_AUTH';
|
|
7
7
|
|
|
8
8
|
async function getApiKey(keyType: KeyType): Promise<string> {
|
|
9
9
|
const keyResponse = await fetch(`${KEYS_URL}/${keyType}`, {
|
|
@@ -31,4 +31,8 @@ export async function getImageApiKey(): Promise<string> {
|
|
|
31
31
|
|
|
32
32
|
export async function getAccountHash(): Promise<string> {
|
|
33
33
|
return getApiKey('ACCOUNT_HASH');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function getPdfApiKey(): Promise<string> {
|
|
37
|
+
return getApiKey('PDF_WORKER_AUTH');
|
|
34
38
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@striae-org/striae",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Cloud-native forensic annotation application for firearms identification (Remix + Cloudflare Workers).",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"publish:github": "npm publish --registry=https://npm.pkg.github.com --@striae-org:registry=https://npm.pkg.github.com",
|
|
78
78
|
"publish:github:dry-run": "npm publish --dry-run --registry=https://npm.pkg.github.com --@striae-org:registry=https://npm.pkg.github.com",
|
|
79
79
|
"publish:all": "npm run publish:npm && npm run publish:github",
|
|
80
|
-
"publish:all:dry-run": "npm run publish:npm:dry-run && npm run publish:github:dry-run",
|
|
80
|
+
"publish:all:dry-run": "npm run publish:npm:dry-run && npm run publish:github:dry-run",
|
|
81
81
|
"lint": "node ./scripts/run-eslint.cjs",
|
|
82
82
|
"start": "node ./scripts/dev.cjs && wrangler pages dev",
|
|
83
83
|
"typecheck": "tsc",
|
package/public/_routes.json
CHANGED
|
Binary file
|
package/scripts/deploy-config.sh
CHANGED
|
@@ -57,9 +57,16 @@ is_placeholder() {
|
|
|
57
57
|
|
|
58
58
|
# Check if .env file exists
|
|
59
59
|
env_created_from_example=false
|
|
60
|
+
preserved_domain_env_file=""
|
|
61
|
+
|
|
62
|
+
if [ -f ".env" ]; then
|
|
63
|
+
preserved_domain_env_file=".env"
|
|
64
|
+
fi
|
|
65
|
+
|
|
60
66
|
if [ "$update_env" = "true" ]; then
|
|
61
67
|
if [ -f ".env" ]; then
|
|
62
68
|
cp .env .env.backup
|
|
69
|
+
preserved_domain_env_file=".env.backup"
|
|
63
70
|
echo -e "${GREEN}📄 Existing .env backed up to .env.backup${NC}"
|
|
64
71
|
fi
|
|
65
72
|
|
|
@@ -133,11 +140,151 @@ normalize_domain_value() {
|
|
|
133
140
|
printf '%s' "$domain"
|
|
134
141
|
}
|
|
135
142
|
|
|
143
|
+
strip_carriage_returns() {
|
|
144
|
+
printf '%s' "$1" | tr -d '\r'
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
read_env_var_from_file() {
|
|
148
|
+
local env_file=$1
|
|
149
|
+
local var_name=$2
|
|
150
|
+
|
|
151
|
+
if [ ! -f "$env_file" ]; then
|
|
152
|
+
return 0
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
awk -v key="$var_name" '
|
|
156
|
+
index($0, key "=") == 1 {
|
|
157
|
+
value = substr($0, length(key) + 2)
|
|
158
|
+
}
|
|
159
|
+
END {
|
|
160
|
+
if (value != "") {
|
|
161
|
+
gsub(/\r/, "", value)
|
|
162
|
+
gsub(/^"/, "", value)
|
|
163
|
+
gsub(/"$/, "", value)
|
|
164
|
+
print value
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
' "$env_file"
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
worker_domain_wrangler_path() {
|
|
171
|
+
case "$1" in
|
|
172
|
+
KEYS_WORKER_DOMAIN)
|
|
173
|
+
printf '%s' "workers/keys-worker/wrangler.jsonc"
|
|
174
|
+
;;
|
|
175
|
+
USER_WORKER_DOMAIN)
|
|
176
|
+
printf '%s' "workers/user-worker/wrangler.jsonc"
|
|
177
|
+
;;
|
|
178
|
+
DATA_WORKER_DOMAIN)
|
|
179
|
+
printf '%s' "workers/data-worker/wrangler.jsonc"
|
|
180
|
+
;;
|
|
181
|
+
AUDIT_WORKER_DOMAIN)
|
|
182
|
+
printf '%s' "workers/audit-worker/wrangler.jsonc"
|
|
183
|
+
;;
|
|
184
|
+
IMAGES_WORKER_DOMAIN)
|
|
185
|
+
printf '%s' "workers/image-worker/wrangler.jsonc"
|
|
186
|
+
;;
|
|
187
|
+
PDF_WORKER_DOMAIN)
|
|
188
|
+
printf '%s' "workers/pdf-worker/wrangler.jsonc"
|
|
189
|
+
;;
|
|
190
|
+
esac
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
read_worker_domain_from_wrangler() {
|
|
194
|
+
local wrangler_file=$1
|
|
195
|
+
|
|
196
|
+
if [ ! -f "$wrangler_file" ]; then
|
|
197
|
+
return 0
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
sed -n 's/.*"pattern"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$wrangler_file" | head -n 1
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
resolve_existing_domain_value() {
|
|
204
|
+
local var_name=$1
|
|
205
|
+
local current_value=$2
|
|
206
|
+
local preserved_value=""
|
|
207
|
+
local wrangler_file=""
|
|
208
|
+
|
|
209
|
+
current_value=$(normalize_domain_value "$current_value")
|
|
210
|
+
|
|
211
|
+
if [ "$current_value" = "$var_name" ]; then
|
|
212
|
+
current_value=""
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
if [ -n "$current_value" ] && ! is_placeholder "$current_value"; then
|
|
216
|
+
printf '%s' "$current_value"
|
|
217
|
+
return 0
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
if [ -n "$preserved_domain_env_file" ] && [ -f "$preserved_domain_env_file" ]; then
|
|
221
|
+
preserved_value=$(read_env_var_from_file "$preserved_domain_env_file" "$var_name")
|
|
222
|
+
preserved_value=$(normalize_domain_value "$preserved_value")
|
|
223
|
+
|
|
224
|
+
if [ "$preserved_value" = "$var_name" ]; then
|
|
225
|
+
preserved_value=""
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
if [ -n "$preserved_value" ] && ! is_placeholder "$preserved_value"; then
|
|
229
|
+
printf '%s' "$preserved_value"
|
|
230
|
+
return 0
|
|
231
|
+
fi
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
if [[ "$var_name" == *_WORKER_DOMAIN ]]; then
|
|
235
|
+
wrangler_file=$(worker_domain_wrangler_path "$var_name")
|
|
236
|
+
|
|
237
|
+
if [ -n "$wrangler_file" ] && [ -f "$wrangler_file" ]; then
|
|
238
|
+
preserved_value=$(read_worker_domain_from_wrangler "$wrangler_file")
|
|
239
|
+
preserved_value=$(normalize_domain_value "$preserved_value")
|
|
240
|
+
|
|
241
|
+
if [ "$preserved_value" = "$var_name" ]; then
|
|
242
|
+
preserved_value=""
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
if [ -n "$preserved_value" ] && ! is_placeholder "$preserved_value"; then
|
|
246
|
+
printf '%s' "$preserved_value"
|
|
247
|
+
return 0
|
|
248
|
+
fi
|
|
249
|
+
fi
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
printf '%s' "$current_value"
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
generate_worker_subdomain_label() {
|
|
256
|
+
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
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
generate_worker_subdomain() {
|
|
260
|
+
local pages_domain=$1
|
|
261
|
+
local subdomain_label=""
|
|
262
|
+
|
|
263
|
+
pages_domain=$(normalize_domain_value "$pages_domain")
|
|
264
|
+
|
|
265
|
+
if [ -z "$pages_domain" ] || is_placeholder "$pages_domain"; then
|
|
266
|
+
return 1
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
if ! subdomain_label=$(generate_worker_subdomain_label); then
|
|
270
|
+
return 1
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
if [ ${#subdomain_label} -ne 10 ]; then
|
|
274
|
+
return 1
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
printf '%s.%s' "$subdomain_label" "$pages_domain"
|
|
278
|
+
}
|
|
279
|
+
|
|
136
280
|
write_env_var() {
|
|
137
281
|
local var_name=$1
|
|
138
282
|
local var_value=$2
|
|
139
283
|
local env_file_value="$var_value"
|
|
140
284
|
|
|
285
|
+
var_value=$(strip_carriage_returns "$var_value")
|
|
286
|
+
env_file_value="$var_value"
|
|
287
|
+
|
|
141
288
|
if [ "$var_name" = "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" ] || [ "$var_name" = "MANIFEST_SIGNING_PRIVATE_KEY" ] || [ "$var_name" = "MANIFEST_SIGNING_PUBLIC_KEY" ]; then
|
|
142
289
|
# Store as a quoted string so sourced .env preserves escaped newline markers (\n)
|
|
143
290
|
env_file_value=${env_file_value//\"/\\\"}
|
|
@@ -276,6 +423,7 @@ configure_manifest_signing_credentials() {
|
|
|
276
423
|
else
|
|
277
424
|
echo -e "${GREEN}Current manifest signing key pair: [HIDDEN]${NC}"
|
|
278
425
|
read -p "Generate new manifest signing key pair? (press Enter to keep current, or type 'y' to regenerate): " regenerate_choice
|
|
426
|
+
regenerate_choice=$(strip_carriage_returns "$regenerate_choice")
|
|
279
427
|
if [ "$regenerate_choice" = "y" ] || [ "$regenerate_choice" = "Y" ]; then
|
|
280
428
|
should_generate="true"
|
|
281
429
|
fi
|
|
@@ -353,6 +501,7 @@ required_vars=(
|
|
|
353
501
|
|
|
354
502
|
# Worker-Specific Secrets (required for deployment)
|
|
355
503
|
"KEYS_AUTH"
|
|
504
|
+
"PDF_WORKER_AUTH"
|
|
356
505
|
"ACCOUNT_HASH"
|
|
357
506
|
"API_TOKEN"
|
|
358
507
|
"HMAC_KEY"
|
|
@@ -579,16 +728,23 @@ prompt_for_secrets() {
|
|
|
579
728
|
local current_value="${!var_name}"
|
|
580
729
|
local new_value=""
|
|
581
730
|
local allow_keep="false"
|
|
731
|
+
|
|
732
|
+
current_value=$(strip_carriage_returns "$current_value")
|
|
733
|
+
|
|
734
|
+
if [ "$var_name" = "PAGES_CUSTOM_DOMAIN" ] || [[ "$var_name" == *_WORKER_DOMAIN ]]; then
|
|
735
|
+
current_value=$(resolve_existing_domain_value "$var_name" "$current_value")
|
|
736
|
+
fi
|
|
582
737
|
|
|
583
738
|
# 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
|
|
739
|
+
if [ "$var_name" = "USER_DB_AUTH" ] || [ "$var_name" = "R2_KEY_SECRET" ] || [ "$var_name" = "KEYS_AUTH" ] || [ "$var_name" = "PDF_WORKER_AUTH" ]; then
|
|
585
740
|
echo -e "${BLUE}$var_name${NC}"
|
|
586
741
|
echo -e "${YELLOW}$description${NC}"
|
|
587
742
|
|
|
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
|
|
743
|
+
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
|
|
589
744
|
# Current value exists and is not a placeholder
|
|
590
745
|
echo -e "${GREEN}Current value: [HIDDEN]${NC}"
|
|
591
746
|
read -p "Generate new secret? (press Enter to keep current, or type 'y' to generate): " gen_choice
|
|
747
|
+
gen_choice=$(strip_carriage_returns "$gen_choice")
|
|
592
748
|
|
|
593
749
|
if [ "$gen_choice" = "y" ] || [ "$gen_choice" = "Y" ]; then
|
|
594
750
|
new_value=$(openssl rand -hex 32 2>/dev/null || echo "")
|
|
@@ -598,6 +754,7 @@ prompt_for_secrets() {
|
|
|
598
754
|
while true; do
|
|
599
755
|
echo -e "${RED}❌ Failed to auto-generate, please enter manually:${NC}"
|
|
600
756
|
read -p "Enter value: " new_value
|
|
757
|
+
new_value=$(strip_carriage_returns "$new_value")
|
|
601
758
|
if [ -z "$new_value" ]; then
|
|
602
759
|
echo -e "${RED}❌ A value is required.${NC}"
|
|
603
760
|
continue
|
|
@@ -624,6 +781,7 @@ prompt_for_secrets() {
|
|
|
624
781
|
while true; do
|
|
625
782
|
echo -e "${RED}❌ Failed to auto-generate, please enter manually:${NC}"
|
|
626
783
|
read -p "Enter value: " new_value
|
|
784
|
+
new_value=$(strip_carriage_returns "$new_value")
|
|
627
785
|
if [ -z "$new_value" ]; then
|
|
628
786
|
echo -e "${RED}❌ A value is required.${NC}"
|
|
629
787
|
continue
|
|
@@ -637,6 +795,68 @@ prompt_for_secrets() {
|
|
|
637
795
|
done
|
|
638
796
|
fi
|
|
639
797
|
fi
|
|
798
|
+
elif [[ "$var_name" == *_WORKER_DOMAIN ]]; then
|
|
799
|
+
local pages_domain
|
|
800
|
+
local domain_choice=""
|
|
801
|
+
|
|
802
|
+
pages_domain=$(resolve_existing_domain_value "PAGES_CUSTOM_DOMAIN" "$PAGES_CUSTOM_DOMAIN")
|
|
803
|
+
|
|
804
|
+
echo -e "${BLUE}$var_name${NC}"
|
|
805
|
+
echo -e "${YELLOW}$description${NC}"
|
|
806
|
+
|
|
807
|
+
if [ -n "$current_value" ] && ! is_placeholder "$current_value"; then
|
|
808
|
+
echo -e "${GREEN}Current value: $current_value${NC}"
|
|
809
|
+
else
|
|
810
|
+
while true; do
|
|
811
|
+
if [ -n "$pages_domain" ] && ! is_placeholder "$pages_domain"; then
|
|
812
|
+
echo -e "${YELLOW}Choose how to configure this worker domain:${NC}"
|
|
813
|
+
echo " A) Auto-generate a 10-character subdomain of $pages_domain"
|
|
814
|
+
echo " M) Manually enter the worker domain"
|
|
815
|
+
read -p "Selection (A/M): " domain_choice
|
|
816
|
+
domain_choice=$(strip_carriage_returns "$domain_choice")
|
|
817
|
+
domain_choice=$(printf '%s' "$domain_choice" | tr '[:upper:]' '[:lower:]')
|
|
818
|
+
|
|
819
|
+
if [ -z "$domain_choice" ] || [ "$domain_choice" = "a" ]; then
|
|
820
|
+
new_value=$(generate_worker_subdomain "$pages_domain" || echo "")
|
|
821
|
+
|
|
822
|
+
if [ -n "$new_value" ]; then
|
|
823
|
+
echo -e "${GREEN}✅ Generated worker domain: $new_value${NC}"
|
|
824
|
+
break
|
|
825
|
+
fi
|
|
826
|
+
|
|
827
|
+
echo -e "${RED}❌ Failed to auto-generate a worker subdomain. Please try manual entry.${NC}"
|
|
828
|
+
continue
|
|
829
|
+
fi
|
|
830
|
+
|
|
831
|
+
if [ "$domain_choice" = "m" ]; then
|
|
832
|
+
read -p "Enter value: " new_value
|
|
833
|
+
new_value=$(strip_carriage_returns "$new_value")
|
|
834
|
+
else
|
|
835
|
+
echo -e "${RED}❌ Please choose 'A' for automatic or 'M' for manual.${NC}"
|
|
836
|
+
continue
|
|
837
|
+
fi
|
|
838
|
+
else
|
|
839
|
+
echo -e "${YELLOW}PAGES_CUSTOM_DOMAIN is required for auto-generated worker subdomains.${NC}"
|
|
840
|
+
read -p "Enter value: " new_value
|
|
841
|
+
new_value=$(strip_carriage_returns "$new_value")
|
|
842
|
+
fi
|
|
843
|
+
|
|
844
|
+
if [ -z "$new_value" ]; then
|
|
845
|
+
echo -e "${RED}❌ A value is required.${NC}"
|
|
846
|
+
continue
|
|
847
|
+
fi
|
|
848
|
+
|
|
849
|
+
new_value=$(normalize_domain_value "$new_value")
|
|
850
|
+
|
|
851
|
+
if is_placeholder "$new_value"; then
|
|
852
|
+
echo -e "${RED}❌ Placeholder values are not allowed.${NC}"
|
|
853
|
+
new_value=""
|
|
854
|
+
continue
|
|
855
|
+
fi
|
|
856
|
+
|
|
857
|
+
break
|
|
858
|
+
done
|
|
859
|
+
fi
|
|
640
860
|
else
|
|
641
861
|
# Normal prompt for other variables
|
|
642
862
|
echo -e "${BLUE}$var_name${NC}"
|
|
@@ -653,11 +873,13 @@ prompt_for_secrets() {
|
|
|
653
873
|
while true; do
|
|
654
874
|
if [ "$allow_keep" = "true" ]; then
|
|
655
875
|
read -p "New value (or press Enter to keep current): " new_value
|
|
876
|
+
new_value=$(strip_carriage_returns "$new_value")
|
|
656
877
|
if [ -z "$new_value" ]; then
|
|
657
878
|
break
|
|
658
879
|
fi
|
|
659
880
|
else
|
|
660
881
|
read -p "Enter value: " new_value
|
|
882
|
+
new_value=$(strip_carriage_returns "$new_value")
|
|
661
883
|
if [ -z "$new_value" ]; then
|
|
662
884
|
echo -e "${RED}❌ A value is required.${NC}"
|
|
663
885
|
continue
|
|
@@ -687,6 +909,7 @@ prompt_for_secrets() {
|
|
|
687
909
|
elif [ -n "$current_value" ]; then
|
|
688
910
|
# Keep values aligned with .env.example ordering and remove stale duplicates.
|
|
689
911
|
write_env_var "$var_name" "$current_value"
|
|
912
|
+
export "$var_name=$current_value"
|
|
690
913
|
echo -e "${GREEN}✅ Keeping current value for $var_name${NC}"
|
|
691
914
|
fi
|
|
692
915
|
echo ""
|
|
@@ -741,6 +964,7 @@ prompt_for_secrets() {
|
|
|
741
964
|
echo -e "${BLUE}🔐 SERVICE-SPECIFIC SECRETS${NC}"
|
|
742
965
|
echo "============================"
|
|
743
966
|
prompt_for_var "KEYS_AUTH" "Keys worker authentication token (generate with: openssl rand -hex 16)"
|
|
967
|
+
prompt_for_var "PDF_WORKER_AUTH" "PDF worker authentication token (generate with: openssl rand -hex 16)"
|
|
744
968
|
prompt_for_var "ACCOUNT_HASH" "Cloudflare Images Account Hash"
|
|
745
969
|
prompt_for_var "API_TOKEN" "Cloudflare Images API token (for Images Worker)"
|
|
746
970
|
prompt_for_var "HMAC_KEY" "Cloudflare Images HMAC signing key"
|
|
@@ -175,7 +175,7 @@ fi
|
|
|
175
175
|
|
|
176
176
|
# Keys Worker
|
|
177
177
|
if ! set_worker_secrets "Keys Worker" "workers/keys-worker" \
|
|
178
|
-
"KEYS_AUTH" "USER_DB_AUTH" "R2_KEY_SECRET" "ACCOUNT_HASH" "IMAGES_API_TOKEN"; then
|
|
178
|
+
"KEYS_AUTH" "USER_DB_AUTH" "R2_KEY_SECRET" "ACCOUNT_HASH" "IMAGES_API_TOKEN" "PDF_WORKER_AUTH"; then
|
|
179
179
|
echo -e "${YELLOW}⚠️ Skipping Keys Worker (not configured)${NC}"
|
|
180
180
|
fi
|
|
181
181
|
|
|
@@ -198,7 +198,11 @@ if ! set_worker_secrets "Images Worker" "workers/image-worker" \
|
|
|
198
198
|
fi
|
|
199
199
|
|
|
200
200
|
# PDF Worker (no secrets needed)
|
|
201
|
-
|
|
201
|
+
# PDF Worker
|
|
202
|
+
if ! set_worker_secrets "PDF Worker" "workers/pdf-worker" \
|
|
203
|
+
"PDF_WORKER_AUTH"; then
|
|
204
|
+
echo -e "${YELLOW}⚠️ Skipping PDF Worker (not configured)${NC}"
|
|
205
|
+
fi
|
|
202
206
|
|
|
203
207
|
echo -e "\n${GREEN}🎉 Worker secrets deployment completed!${NC}"
|
|
204
208
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PDFGenerationData, ReportRenderer } from './report-types';
|
|
2
|
+
import { ICON_256 } from './generated-assets';
|
|
2
3
|
|
|
3
4
|
export const renderReport: ReportRenderer = (data: PDFGenerationData): string => {
|
|
4
5
|
const { imageUrl, caseNumber, annotationData, activeAnnotations, currentDate, notesUpdatedFormatted, userCompany } = data;
|
|
@@ -519,7 +520,7 @@ export const renderReport: ReportRenderer = (data: PDFGenerationData): string =>
|
|
|
519
520
|
<div class="footer">
|
|
520
521
|
<div class="footer-left">
|
|
521
522
|
<span>Notes formatted by Striae</span>
|
|
522
|
-
|
|
523
|
+
<img class="footer-brand-icon" src="${ICON_256}" alt="Striae icon" />
|
|
523
524
|
</div>
|
|
524
525
|
<div class="footer-center">
|
|
525
526
|
${userCompany ? userCompany : ''}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Auto-generated by scripts/generate-assets.js
|
|
2
|
+
// Do not edit manually — run `npm run generate:assets` to regenerate.
|
|
3
|
+
|
|
4
|
+
// Source: src/assets/icon-256.png
|
|
5
|
+
export const ICON_256 =
|
|
6
|
+
"data:image/png;base64," +
|
|
7
|
+
"iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVY" +
|
|
8
|
+
"dFNvZnR3YXJlAFBhaW50Lk5FVCA1LjEuMvu8A7YAAAC2ZVhJZklJKgAIAAAABQAaAQUAAQAAAEoAAAAbAQUAAQAAAFIAAAAoAQMAAQAAAAIAAAAxAQIAEAAA" +
|
|
9
|
+
"AFoAAABphwQAAQAAAGoAAAAAAAAAYAAAAAEAAABgAAAAAQAAAFBhaW50Lk5FVCA1LjEuMgADAACQBwAEAAAAMDIzMAGgAwABAAAAAQAAAAWgBAABAAAAlAAA" +
|
|
10
|
+
"AAAAAAACAAEAAgAEAAAAUjk4AAIABwAEAAAAMDEwMAAAAADp1fY4ytpsegAAJW1JREFUeF7tnQecVdW1/9ftbXpv9KaYKFHMM3+RZzRoNAYFJY/yBGOMIu/p" +
|
|
11
|
+
"U3z2gJ/YRYLGmIiRYsooSIvGqMSnWGLJx5K8hygCQxkYhun99nv3f68zexQGpt/T9lnfz+d8zt7nUu49Z6/fLmevtYAgCIIgCIIgCIIgCIIgCIIgCEmxiTMx" +
|
|
12
|
+
"RB5//HHW0NAgaoSajBgxAq655hpquymAbuIQuP/++9n27dshFouJK4SW+P1+OP3002Hx4sXUjgcJ3bgBsGXLFvbhhx/C7t27xRXCSEyaNAmWLl1KbXoA0M3q" +
|
|
13
|
+
"Bw8//DD75JNPIJFIiCuEkfF6vTBlyhRYtGgRte8+oBvUA+Xl5Wzbtm3Q2NgorhBmZNiwYXD++efD9OnTqa0TBEEQBEEo0LDIhKx9/T1W29wKR/hR19IO9fxo" +
|
|
14
|
+
"C4UhHItBPJGARCIJ0XgCkowB48fR4AO32+38sIGDn/FwORzgdPCz0wFpfP5ckJUOZXnZUJiVAf916fnURiSGHq5BeWTDq+yLQ0dgz+FaCIajijEnkknlHOcG" +
|
|
15
|
+
"joaNdTVBkbDbbIpAuJ1OcHCRcHLBCHg9kJeZBhNKi+Dn/05zazNDD88ALNv4GjtY3wSHG5qhtrkNmjqCEIpExafGB0cLJTlZMKIwFx6+aia1KRNBD0sHVmx5" +
|
|
16
|
+
"nW3ffwg+raiE9lBEXJUHGx81jC8pgIkjSmD5T2ZRGzMw9HA04qoVa9kHOyuU4bsVGZafA1MmjoX7519Gbc5A0MNQkSV/eJG9u2M3VPHhPc7dCVDWEnLSA3BS" +
|
|
17
|
+
"WRGcPXEMXH3BFGqDOmKpm79u3TpVrbA1EoOKxnb4ZzWfz7eFIRiNi0+InsjxuWFcbjqcUZoD+QEPuB128Yk+OLlAXXHFFZaxC0v80JdffpmVl5dDJKLCfJvP" +
|
|
18
|
+
"d4N2DzR7MqHFlQZRu0t8QAwEbIiBeBCyIy2QEe8AZ1I/8czKyoI1a9ZYwjak/5F33303++KLL0QtdcTtTmhxp0OtJ4eMXgWyom1QGG4AbyIsrmjP1KlT4aab" +
|
|
19
|
+
"bpLaRqT+cfPmzWOhUEjUUkPY7oYGTxY08R4/AXy4ykcAhDo4WAIyo+1QEGkAT0Kf16IFBQWwcuVKaR+ylD9s1apV7JVXXhG11JDgt6rOm8MNP4t6fI3xJGN8" +
|
|
20
|
+
"atCsTA/cTPupgcvlgvXr10tpK9R99YPH17/EXvj7Z1DT0i6uEHpQmJkGl0+eCLfMpVeJqYJuZB+ctfhBhvvtCeMwsjAX3njwFmq7KYBuYg9c/+s/sjf/90vF" +
|
|
21
|
+
"uYYwHui49KMpk+HeKy+lNjwE6OadgHNuXcYONzaLGmFkJo0eBpvuvp7a8SChG3cUi35dzrZ+ukPUCLOAHotXnncWLJ37Q2rPA4RumOD/3fIwq2luFTXCjIwo" +
|
|
22
|
+
"yIU3H6K1gYFg+Zv14PpX2B+3fQiRGG3blYH8zHT4cMWdJAL9xNI36rY1G9mm9z4VNUIWcIFw59P3kQj0A8PfpCVLlqTcgSfK/8VtDUlowAIhLRMCNjg9065b" +
|
|
23
|
+
"I3e73dh+DW1jhv5yM2fOTLmFdji8UBUohpDDI64QMpMZa4eyjmpwMn1e52ZnZ8Pq1asNa2f6+l72wpw5c1Ju/G2uAFSmlZLxWwj00DzoL4KYzSGuaEtTUxNc" +
|
|
24
|
+
"d911hh1qGlIArrrqKpZq190Opx8OBEppH78FaXWnQ7W/EBI6iUBdXR16FRpSBAwnAAsWLGCtral9HdfOjX8/7/kTNsMOeAiVaXJnQJWvAPSywsrKSrj55psN" +
|
|
25
|
+
"JwKGmptg4I729tTuu99V3wZbPj+kROshrA029slluTD9pBJl85AezJ4921A2Z9jFiVQx5dZHWHVji6gRVgcjFt84/Tx+UMITROqb8J3FD7HaljZRI4hOsNHv" +
|
|
26
|
+
"Wf0gCQBH2knx9HufJOMnTghOxE+/4T7DrsxriZQCcPXjz7IdBw6LGkEcT0swBOff9QvLi4B0ArD0jy+yt7fvEjWC6Jn9NQ1KwhZRtSTSCUD5tr+LEkH0DSZu" +
|
|
27
|
+
"Wb31XcuKgFQCcMr199C8jhgwv3zpTVGyHtIIwL8/uoqFo/Sunxg4HeEIzF32jCU7DykE4Dcvb2Mf7NwragQxcP7+5T54dNNWy4mAZu9CMS/fjh2pD7fVFgd4" +
|
|
28
|
+
"rS4BMRr8E0PEwa3hB/l2SHPqu0Xgvvu0i2Wg2X+khmsvsj9QqqToIohUkB7rgNHtB0VNH7RMRKLJFEAN114k6PBy408TNYIYOugy3urSt03FYjHNXIhVF4Ab" +
|
|
29
|
+
"brgh5a69COblq/YX8JK+wzVCPqq9eUryVz1BF+IVK1aoLgKqCsDatWtZVVWVqKUWHPajmy9BpJqw02uItvW3v/1NlNRDVQF4+eWXRSm1MJsNGt2ZokYQqafG" +
|
|
30
|
+
"mwNJA8SPmD9/vqqjANOOn0++bimLximUN6EODrsd7vjRRXD1tLOlnmPqL3GDYMGKNWT8hKokkknY8O7HoiYvphSAj3cfECWCUI9dVTWiJC+mE4DFz7xAW34J" +
|
|
31
|
+
"zbj8gac0eR2nF6YTgNf/8bkoEYT6/HOvvpuC1MZ0AhCMREWJILThgfV/kXYUYCoB+PFjz0o9HCOMybuf7RYl+TDVK47xP72bJZKkAUeTm5EGJTmZkJ3mh3Sf" +
|
|
32
|
+
"F7ICfkjzecDt7HknG65wh/hIqjkYgsbWDmho64CKI3XKNeLEVEgaRNRUP2rMT+6yrPVjHPuA1wNZ3NALMtNhQmkh3Df/spQ/P3SJ3X24Fg7WNUJTewc/QhBP" +
|
|
33
|
+
"6JNXz0hM/5fT4LFr/006ERjyD1q9ejX7y1/+ImrqkORf80BaCbS6rOf1h0ktvfEwBOIhSI93gI+X7Vrkt+GCE7K7ocPpU+47no2wM04vnMk4jGmrBG/SOKOk" +
|
|
34
|
+
"zZs3D9l+h/wPzJo1iyVU7iFCDq+S2ssqef1sjEFWrB1yI03g54Zv0y2h1dcw/i2a3enQ5M5UPOasB4OSUB3khxtFXX/y8vLgt7/97ZBseEiS/sQTT6hu/Aj2" +
|
|
35
|
+
"PlYwfnyS6dzwJ7Tug+HBw7zXDxrC+BH8HtnRVhjZfkjpCbOibWBnSfGpFbBBm9NYwldfXy9Kg2dIAvDWW2+Jkrp0uOT3+vMkolAcrIFR3MA8OMzkowAjgtOP" +
|
|
36
|
+
"NC5MwzuqYCQ/fImw+ER+gk6vMhIyEldfffWQGsqgBeDRRx/VrIUGJc/nnxdphrFtByCfD/mN1bx6pnO00gHjWg9AAR8Wm+V7DwVMLx7ko1Ej0dzcDH/6058G" +
|
|
37
|
+
"bYuDFoAPPvhAlNQFF56YTnnd1YdBWagGSoNHlMU+M4JTg+JQrSJguEApO60GXP/gAiBKA2fQwo1BPkVRVapag7Dq470QTcg133Ta7XDJSSVwZmmOuGJ+8Blt" +
|
|
38
|
+
"/Owg7KiVNxtzSYYPrj59NPhcxuqUBpt23BQjtwnXLmGyvYuWdWMJ8pPHf8fe2v6lqMkFxgnY9cz90jw7w7/Yvfe5PzPcuSYTp40eJkpysvqmBTbcrCQjsrVF" +
|
|
39
|
+
"wwvAjsrDwAy6Ij4YHHYbbL77eml7/y4+WHGntL/xqb+8JU2DNLwAVNYaZ+NFKjh1lNy9/9FMHF4sSnKx67A8gUIMLwCN7UFRMj9Ohx023rVQ+t6/iz/fc4Mt" +
|
|
40
|
+
"M2Cs12ap4IBEnZLhBUCmxb+xxZjHwFp8/4xviJI8HKojASAGwSkjSkTJOjy4YIbN55ZrGze6T8sCCYCGjCrKEyVrcfGZ3xQleVjyh8HvvjMS/ZqP3nPPPWz7" +
|
|
41
|
+
"9u2iph1hhwe+zBglauZnWLAaciLybpLpCfQe3Jsm1+In+kOgU5RRufTSS2HBggV92ne/RgCfffaZKGmL0RwvhopNnreZAwK9Gr0JuaINoV+Akdm2bZso9U6f" +
|
|
42
|
+
"AlBeXs70eg+PKcBkIm6X1aehd+y8/WRHm0VNDhK8czJygJTW1lZR6p0+f0F/lUQNZBsBxGz6ZpzVE4wfINPTxBFA1ODP86GHHuqz5+5TABob9XvlIdsIIKZz" +
|
|
43
|
+
"ymk9cSdjfCQgkT8Hb5oxh1tUjMnHH/ed2qxXAVi2bJmus1YMjSUTUd5g9M47ryfFoXol2AlGEjLz4cA4jYmI+FXGBafuW7Zs6dWIeu1if/7zn7O4jkk4G6IM" +
|
|
44
|
+
"ttbL43yBajs50wZjA8adO6pNhD/OcIJB51M15wgPoyJ5HTbwmOAxlpSUwPXX9+x7YvgnIFso8FNHlsGWJYvM2fIJ6bBuV6QT/7f/EDz1yttyzW0I00ICoAPL" +
|
|
45
|
+
"N22VOt8cYR4MLwCYEUdG1vz1PfjPp54jESB0xfAC4HLKu3nm1Y8/g+8sfog9uP4VEgJCFwzfvX7rhntZa1D+aLOYzHPat06GJxbOoQVCQjMM39guXvpL9mWV" +
|
|
46
|
+
"PBFY+gKnPONLC+F7k06Gm2dMIzEgVMXwDWz+IyvZe7uM63WlJll+LxRmpkERP75RVgCL56Q+GzBhbY5rULfffjvbvXu3qOlPjTcXjvjQj57aPm5ACcQ6wJeI" +
|
|
47
|
+
"gJ8fHn64knFld5pRcggSxuXCCy+E66677hhDOm4RcM+ePaJkDLChozcZ0Zkmvc2VBrVcFPcHSpRYCV9kjIaKtGFQ7cuHZle6st1YNicqIjXs2LFDlL7mOAEw" +
|
|
48
|
+
"Wghu7OWod+uZhN2hJE9FUahMK4FdGSPh88wx/DwKDgSKlRFUizvd0j4IRCeHDh0Spa85RgDWrl1rOEtThrgkAP0Ce/4Ev1to7CGHB5rdmcr06aCvUBkl7E0r" +
|
|
49
|
+
"gypfATR6Mr9KuY5+7YR1OUYAvvzSeOmcsPd3chEgBgsXBS4IGF4Npw/13hw46C+GivQRsD+tFA4HiqDekwUhLghKgAtJN14RnaxZs+aY3vQYAaioqBAlY+GP" +
|
|
50
|
+
"h0SJSBXYCkIOLzTyUUKVvwh2cUH4LGs87EwfxQWiCJr49Yjd2P7uxMDp3skfIwAJg8bgT+MCQP2S+qAoRBxuPkXIgoOBYtidMUJZaDwQKIE6PnLAEUSMTxsI" +
|
|
51
|
+
"87J3715R6sQ0dnX+7cvY/nq54sqZjQyfB4qz0mF4biaMKciBW+fNIF02OaZ5gIufeYG9+OE/RY0wCrhr8cLTT4GbLvseiYEJMdVDky04iGxk+L1K8tNzvzke" +
|
|
52
|
+
"fjztbBIEE2CqhzT+pz9jsuVnlxGHww6Zfh9kp/nhrAmj4d4rLyUxMCimejBXLl/N3v/CmG8qiJ4JeD3KVOGMsSPgzh9dRGJgIEz3MGgaYH6G5+fAd0+dAEvn" +
|
|
53
|
+
"/pDEQGdM9wDOvOkB1ihRdlark8GnCt85aTT85j/mkRjogOlu+m0r/8g2ffS5qBGy4HTYIZuLwcSyfDh73HD4yaXfJ0HQAOUm33rrrcyouwC7g+m10OGFnFvk" +
|
|
54
|
+
"BX0/PIkopMU7IDPaDv540Hw9lcG54IILYOHChZ3ZDWtqzBNxx8XikM4bBiEv6PaMzkx1nhzYkz5c8Vtodmco14nU0GXzigC0t7crFbOQF25SvAQJa4Cei7gd" +
|
|
55
|
+
"eWfmGKjyFyriQAyN2tpa5XyML4BZ8CfCUBhuEDXCKmBy1XpPNuzlo4JKLgjom0CjgsFRXV2tnE0pAEh2pBm8fJ5IWI+4zQFNfEqA8Q0qA8UQ5CMEejc8OEwr" +
|
|
56
|
+
"ALhQVBiqEzXCqrRwIditxDYoU2IeEAPD/vzzz5tWPLNibZAWD4oaYWVa+XQAhQCnBkGHV1wl+sL20ksvsY8++khUzUdDjME7DUkIkYsAIcC03d9It8NInzlS" +
|
|
57
|
+
"eOvFfffdJ0f8p9Vb/8Ye2fgakKMQcTSnjSqDzT+jVOy9Ic3NWfrHF1n5tr+LGkF8zeypZ8IDCyh4yYmQ6qbMenAl+7TCmlmEiN4ZWZALbzx0C4lAN6S7IWct" +
|
|
58
|
+
"fojVtbSJGkF8DWaavnjyN2HFT39EQiCQ8kac8V/3s+Z2ejtAnJgpp4yD3y3+MYkAR9qbMJmLQBOJANEDZXnZ8PYjt1peBGzr1q2TchNVXUcYVn+8F9qi5DNA" +
|
|
59
|
+
"nJiA2wk/mFACpxVliSvWYvbs2TbbzJkzpd1FiTHu96YNU1JgEcSJwB2lZe3VkB1rFVesQ2lpqXm3AvcH9Ckf21YJnmRMXCGIY0FnooNpnf4EVgMTAUm/T8rF" +
|
|
60
|
+
"jX9c2wHIiJnL5ZnQDkyqio5Fba6AuGINksmk/AKAOJJxGN5+GHKirfKuehJDImFzdPoRWGgkwBizhgAgDj7YK+s4DHmRJmXeRxDdQTfjfYFSJe6AFbDb7dYR" +
|
|
61
|
+
"AAR7/5JgDZTyw8nIb4A4How1ia7FVog56XA4rCUAXeREmmF8617I5lMCgugOuhNX+QpETV4sKwAIxhQc1lENZaEa5W0BQRxNsytdSYkuMy6Xi9bEulj42Gr2" +
|
|
62
|
+
"xo69kGS0PkB04ve4YPtvfi61jZAAdOMH9zzBdh46ImqE1SnKzoD3lt8hrZ2QAPTABT97jFVUU8xBAuCWmRfAoh+cK6WtWHYNoC/+ev/Nthunnw9jS+RfDCJ6" +
|
|
63
|
+
"57m35A00QyOAfvD0q2+zLe//A/YeqaewYxblmgunwJ0/ulg6eyEBGCD/vWoDe+3THRCK0JsDK+F1uWDHSvkWBJUfJKtLsBrgS4K2aAy2H2mGL+raYF8T+RhY" +
|
|
64
|
+
"heFZfrh28hiwyRFLt9MduLy8nG3atElcIgYC7h8POr3Q6gxAszsdEnYXbTKWGgbjWw+ALxEWdXOzefNmm33evHk0DRgkDpaA9FgHlIZqYVxbpZKpyJukqYG8" +
|
|
65
|
+
"2KDRk6mcZYHeAqQIdzKmJCyd0LIXJrZUwLDgEUUcyPFILjAnYdzuEDXzQwKgAhiDAP0NRnVUwfiWfYoYYBozSmlufnDah2nIzE7XOgYJgIrYWBI8fEqAYjC8" +
|
|
66
|
+
"vQpGdhyCIj5N8MdD4ORiQHMvc4J+Aug6bGays7OVsyIA6BRAqAsauz8eVqYJGKFofOt+KBXTBCdLdL5eIExBh8tv+sAhhYWFylkRgKKiIqVCaIeLxSGXjwxG" +
|
|
67
|
+
"tx+Eic27YWLLHhjDhaEkVAsZ0TZlhEAYE4wjaHYBKC4uVs6KAHRVCH3A0YGLjwLS+NQgP9wIwzuqvxoljGo/BAWhesjkouDjIwhHko8WCN3pkEQAvpqGLlmy" +
|
|
68
|
+
"hMagBgYfTlucQQe3/+YYg8YYQCsfJESSDHgV4snOP0Nog98BcEmBA5wmXcjB1OB4NunXJ7rz21ffYZ/sOQCfV1ZDdVOLEvCRUI+A1wP/9+t7TG8/JAAS88sX" +
|
|
69
|
+
"/4eLQiWgMISjlBshlaDhLLrku7B4xjRT2xAJgMW497k/s0MNzXCEjxIO1TdBS0dIfEIMlDPHj4R1t19LAkCYlyf//Cb7sqoG9h2p46LQCsFIFKKxOK0n9IPM" +
|
|
70
|
+
"gA8+fWIJCQAhF49teZ19zKcNXxysphFCL+Buuj2rHpBHAOhNANFFkrcEbAxtCYCGSBLqY6AcLfjKgeiEMbgo3wHZbnNpQNcbAOSYby5zpmBiaCR5bxe1uZSs" +
|
|
71
|
+
"ORGnF8ION0TtbgjxwwpJNHoC92mYKe8k5gLYsGHDV3Z/jC+A2+0WJYI4Fjvv7dDVOT0ehLxwI5R1HOGN/6BiAIWhesW/wWbBV48Rk6WeHzdunCh1cowAdP+Q" +
|
|
72
|
+
"IHoDDd6fCENRuF7ZuTihdS8vNyhxEqxC1OFRtgabhQkTJohSJ8cIwPjx40WJIAaOB2MihOrgpNb9UMBHCVbwZ4jy6U/SdowZGZoFCxYco1bHfPMrr7zSPFJG" +
|
|
73
|
+
"GBYnF4LiUC2M4VOEzvyL8k4NInY3JEwkAN057pvjIoGuYKACMx/EV3gTERjecRjKgrXKirmM4KIoM4kAjBw5UpS+5rgWe9ddd7GdO3eKmnaE+Vyq3RUwf1/B" +
|
|
74
|
+
"Gzr692fEOiw1F+6LJk8mVPrl8zq18RY7jk95fFzsjM706dPhqquuOsbmjxMADBH+wgsviJp2VPvyodabK2rmBuMAFvNeLy/SJK4QSJWvEOq9nZFo5AEFoBL8" +
|
|
75
|
+
"CeNvmMIowKL4FceNXTBWuChqipnnUd3BVeFGT5apFoe0oDRUA+kmemfeP2zKHgmj09PU3jDf/NHntrCVb3wkanJQsfpB47cMjXnu1TfYko1viJoc3Hv5eTDv" +
|
|
76
|
+
"4u+Z8lkbpou6de4M6Yzl8T/9D+2s7Mbci863jSiQY6rXhVmNHzHUGDXN5xElOXj/iwpRIo7mW2OGixKhN4YSgDFF+aIkB59WVIoScTTjKOW6YTCUAAzLzxEl" +
|
|
77
|
+
"OcCwXHc+u5mmAd1YePG/0tqIQej1QWjtHvx5O4N/tsqVf9/NJfb7eXZIM2v0SBWojjDY1iDPc56aY4cyr3Gf79Huv93p9VvPnTuXhcPaZUJtc/phb7p880PM" +
|
|
78
|
+
"DITpwYhOWtwZsD9QImrmB3M7YIIXIzJ27FhYtmxZj3be6xTgrLPOEiVtQGcSGbeMNisJJa3rM9+dqMnTanXHyG7QvRk/0qsA3HjjjZqOa3ALrYfJ50GGG4Jq" +
|
|
79
|
+
"PdkSu8T0H9wkJZsY4nZgI9KVALQ3+lwEzMzEfOjagMk0MbCEjDR4cxRfB6vD7A4IObyiZn7Q+I06Apg8ebIo9UyfAnDeeeeJkvqgXqXxuZSmww6NwJ7viDdP" +
|
|
80
|
+
"8R+3MhGbE9pcflEzPzZu+0YdAdx55519mlKfAoAxAvozlEgVGHIKRwIyggkla3xy7XUYKLjQ2yn1cmDn01Z0/jIaWVlZotQ7fQoAMmnSJFFSH1cyBp5EVNTk" +
|
|
81
|
+
"o9GdCXUeufY79Je43QVNnv41TLPg5u0V4yUajWnTpolS7/RLAJYs0Tb5QcAErpVD4bC/AJq4EFiNal8eRBxyBZ7FQKlGi/uAI/Y5c+b0y2b7JQBIf4cUqQCn" +
|
|
82
|
+
"AQ5JpwFdHAoUQb1kvWFvtLrSoMmVIWrygFGP7AZrq2eeeaYo9c2AenatdgZi7ok36pPQaIEkFKem2+Eb6fLMiU9EKMHglbokRCTU9PNy7VDkMdbz623nX3cM" +
|
|
83
|
+
"2/JuWbWB/emDf4ia3Jw2qgw2/2yRtCow9bZlrKqhWdTkwuwxH/o9BdCaX1wzS1qD6M7/7jsE37nlIbb29fekG/KcedMD0hp/ht/8+xkMKwDIhLIiUZKf2uY2" +
|
|
84
|
+
"eHjDa/AfvymXQgSWb9rKTll4D2tsM+Ye+VQwXALvVUMLwCXfPlWUrEE8kYDXPtkBJ123hM1Z9owpheC3r77DvrP4IfbUK29DOBYTV+VEhg5qUMPsOXPmsEhE" +
|
|
85
|
+
"/TDIGCh0R9Z4S++hxweE3oQ50RYlDZfRnKVwh2PQ6YVWd4ayx0Gm4K69wp9Dacg4kZ9LSkrgySefHLA9D0oAnn/+ebZhwwZRU5eKtGG0h56Dr5rcLA6uZBx8" +
|
|
86
|
+
"8ZDy/hk3obgSMeWsBbjnLeFwKgkxcT9/mytN8exD5x6rRUB2JBMwtr1SeQ1oBE4U8rs/DOovIfPnz2ft7eqHeMb3x/vTyiw9CugJNHzMv+fmYuDlZXciquyi" +
|
|
87
|
+
"xK2puJ0aTbLLWQXv3wkfNr+Igwr+p3j5613tSqhrbtRo9GFu8LiBJ8bPUX4OOTydf97CBOJBGNtmjJBvo0ePhuXLlw/qgQz6Kb700kvs2WefFTX1wIb2ReYY" +
|
|
88
|
+
"JQUT0T/woeKIAXeooYs19lZ2HKx3mz5gLYm9N/8baPAJ3pvj/cYyprvC68SJwYzImBbdCAy290cGPW6bPn26LTdX/fDO2CfhHJjoP2jYOBeP8h47qAzVA9Di" +
|
|
89
|
+
"SlcCkxx9YGQedM7pcPqUIT3+eRRaFAIy/p5BcQ0YxG29Py6/vTHkpzxz5sxjuxUVwIa5N22YdPvICXOCW9VHtFcZwgdgKL0/MuSVm1GjRomSeuBcN0tJM00Q" +
|
|
90
|
+
"+oMxK4xg/BdeeKEoDZ6UjPO0GAXE+LC0ImOksgJNEHoyXskGrF2w3BPh8/mgvLx8yPabknc35557riiph4srbi5l2yV0Rnn9aoB4FakwfiQlAoDBQz0e9dN6" +
|
|
91
|
+
"4WIgrmgThF7khRoAl0j1pLS0VJSGTkoEAJk1a5YoqQfGCMiPNPKS6jMOgjgOfKWabYC1qF/96lcp6f2RlP1DWvLdO5azyjoUAoLQjp9+/xy4Y9ZFprSZnjDt" +
|
|
92
|
+
"jxl7zd0Mc+8RhBbkZaTB3x+7SyrjR1I2BdCauf/6bVEiCPW5aPI3REkuTK1oZ//3I+xIU4uoEYQ6FGVnwnvLb5eu90dU+1Fa7A3AyLqVgWJRIwh1yI00Q2nw" +
|
|
93
|
+
"iG69pcvlgvXr16vy36s2BbjgggtEST2yoy2QFg+KGkGkHkcyDkXhBl2HymoZP6KaACxcuNCGQQrUpowrsycpbyIRQk+YktbdqVG8hRNxxhlniJI6qC5ss2bN" +
|
|
94
|
+
"YomEupt30NMNpwJWC0pBqEtGrB1GtR8SNe3BXBxr1qxR1UZVt5i5c+eKknpkxtqgkA/TCCJVYKCV0mCNqGkPZvdR2/gR1QVgxowZtlNOOUXU1CMv0miIXVqE" +
|
|
95
|
+
"HJSG6jQLtXYipk+fLkrqosmYGTOV5OXliZo6YIJGVGx01iCIoZAXboRMHTuTCRMmwIIFCzRZd9TkP+mCTwdYOKyuGyVGwMHgIQm7Q1whiP7jSUSUWH+4718P" +
|
|
96
|
+
"CgoKYOXKlZrZpaYCgFx++eWqb+Ftd/pgX/pwCmtFDAiMuDy27YBuQ/9U+fgPBM2XzefPny9K6pEWDykhm7oHwSSInsAev4xPIfUyfrvdrrnxI5oLwKWXXmqb" +
|
|
97
|
+
"OnWqqKkHvsIZFqzmP5BEgOgD3lEUhep5m2kTF7Rn48aNugxXdRsjb9q0icU0SB31cVUjvLSzChJJEgLixEwdmQ8XjtNvS/ns2bN1s0Pd/mOtGXfN3SxJUwKi" +
|
|
98
|
+
"G+jl9+T1cy1jB93RfAqgF7tXPWALeNUPW0aYA9xoM2vKGZY2fsQyAoDceckUyJIgpzsxNND4L588ER7+8eWWNn7EMDdAq1yDmMjygL+YEo5aGHTw0TPbFAb1" +
|
|
99
|
+
"TGVcv6FgKAWcN28eC4XUT7mETkM13jyo9WRjdyCuErLTtb8/S8fV/sGm8VYLw7V+LbwHEVwOrPfmQK03F+I22jUoO/5EGIqCtUpaL73QwrtvoBiy+9Nit2AX" +
|
|
100
|
+
"HS4/HPYVKFuICTnJjLVDSahW14QemDfj+eefN5y9GVIAEC1FAFNiH/YXQD1OCQiJYNzw6yA/rG8IeT22+PYXwwoAotV0QMFmgyZXOtT48in/oAR442EoDdcp" +
|
|
101
|
+
"iTz1JBAIwB/+8AfD2pmhBQDRVAQ4YacXary50MzFgDAfuPU7nRt9cbAGPDr68yOZmZmwdu1aQ9uY4QUAmT17NotGtZu/4ZSg1psNR/howCS3iODgHL8oXK+k" +
|
|
102
|
+
"ktf7qeXk5MCqVasM33hM07q12idwNDG7U3lLUO/OUqYIhDFxJ2Lc8OsgG1/vGWC798iRI2HFihWmaDCmatW33HIL27dvn6hpR9jhgWpvHrS70iBJQmAY3Mko" +
|
|
103
|
+
"5ERaID/SBHamb8beLs466yy47bbbTNNITNealy9fzt5//31R0w40/HZnAOo82dDhCij7CAh9wA09udEWyIo0g1fneX4XuL14zpw5cMUVV5jKpkzZnb344ovs" +
|
|
104
|
+
"97//PR/t6WOGTZ5MZaEwYneLK4QWYMQezNKD23hdLC6u6o+RX/P1hSm/tJFY/MwLbOsnOyCsQWwDq3LaqDLY/LNF1FZVgG5qivjF5r+yt7Z/CZ9XVosrxFDI" +
|
|
105
|
+
"9Ptgyilj4YmFc6iNqgjd3BTz2LoX2btfHoA9NQ0QisaBgpD0HzufRxdnpcM5E0bAAz8lw9cCqW6yVt6E/SHscEObKw1aXenQ4fAAo7RlvYI79nB+j/v2bQZe" +
|
|
106
|
+
"YjXL+/3+Ip3K3nbbbWzPnj2ipj/YlBM2BwRdPmhzBrggpEGUthor0XcD8aDinReIhZRXekbnnHPOgZtvvlkqm5FymLVu3Tq2ceNGSCaN8W64CxSDODf+MD/a" +
|
|
107
|
+
"3WkQsnsUMYjbHYpIyAyu4KORp8dD4Oe9PQoAruRjRiejY+ZV/r6Q8kd1ccMNN7CqqipRMx7Y9MMOL0T4dCHEzyGnR3FESoADmB13tRt5MHw82JjwG9tYUjFs" +
|
|
108
|
+
"NHLMtOPjh1ccemXcGSyTJk2CpUuXSmsnUgsA8sILL7BNmzaBFiHIUwWOBnD3YYfTBx0OnyIQyigBQzgabCciGrQzwQ2dxRQD98fD/BzmPX7CZPJ1LBkZGfDs" +
|
|
109
|
+
"s89Kbx/S/8Au7rjjDrZr1y5RMw/KKIAbvZLmTJwTNrsiEhjJKGF3KmcUiDi/nuTlEBePJL/elRptsGaIBoxG7ebDdwc/XNzY8ezmQ3fcf985hOfTLN7bKz2/" +
|
|
110
|
+
"+Htm59xzz4Ubb7zRErZhGQHowkhvClJNjBs/xjvENw5o/Cgc+IiVMp9S4Bm3NDM+kkjgGd9McONFI+aTDjEfx087h/EOHMqj8fPr2NPj35SZ/Px8ePrppy1l" +
|
|
111
|
+
"E5YTAOTxxx9n77zzjqgRVsflcsGMGTN0zdCjF5YUgC4WLVrEjhw5ImqEFZk4cSLcf//9lrUDSwsAsn79erZlyxbQMuAIoT+ybegZLJa/AV387ne/Y6+99hpE" +
|
|
112
|
+
"IhFxhZCR7OxsZbh/ySWXUNvn0E3oxtq1a9nWrVtpRCAZGJP/sssug+nTp1ObPwq6GT2wefNmTeMQEuphxcU9giCIPiFlHCC4aKhXJCKid5xOp+lCcukN3axB" +
|
|
113
|
+
"gAuGb775JrS16ZdkkviawsJCmDZtGsycOZPa8wChGzZE7rrrLrZz505RI7TCbrfD5MmTcYs3teEhQDcvRTz33HPK7sLa2lpxhVCDMWPGwNSpU+GHP/whtd0U" +
|
|
114
|
+
"QDdRBZ5++mkldDlNEVIDDvExGMfcuXOpvaYYuqEq89RTT7EPP/yQxGCAFBQUwNlnnw1XXnkltVEVoZurIbh4+NFHH8Hhw4fFFaILTKwxbtw4+Pa3v02LeRpC" +
|
|
115
|
+
"N1pH0CsRFxCtum4wYsQIOPnkk+Haa6+ldqgTdOMNBG5D3r17N1RUVJgqglF/wDz548ePV3p52plnHOhBGJzy8nIlIer+/fuhsbFRXDUuOJQvLi6GUaNGKQcN" +
|
|
116
|
+
"540NPRwTg/EOGxoa4OhD7RTq6Eabl5ennHNzc5Xjsssuo3ZkUujBSQ46NXV0dCjejSc6sMd2u93K4fF4vip7vV5IT08nt1mCIAiCIAiCIAiCIAjTA/D/Abmz" +
|
|
117
|
+
"ZXM4My2jAAAAAElFTkSuQmCC";
|
|
@@ -3,6 +3,7 @@ import type { PDFGenerationData, PDFGenerationRequest, ReportModule } from './re
|
|
|
3
3
|
|
|
4
4
|
interface Env {
|
|
5
5
|
BROWSER: Fetcher;
|
|
6
|
+
PDF_WORKER_AUTH: string;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
const DEFAULT_REPORT_FORMAT = 'striae';
|
|
@@ -15,9 +16,12 @@ const reportModuleLoaders: Record<string, () => Promise<ReportModule>> = {
|
|
|
15
16
|
const corsHeaders: Record<string, string> = {
|
|
16
17
|
'Access-Control-Allow-Origin': 'PAGES_CUSTOM_DOMAIN',
|
|
17
18
|
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
18
|
-
'Access-Control-Allow-Headers': 'Content-Type',
|
|
19
|
+
'Access-Control-Allow-Headers': 'Content-Type, X-Custom-Auth-Key',
|
|
19
20
|
};
|
|
20
21
|
|
|
22
|
+
const hasValidHeader = (request: Request, env: Env): boolean =>
|
|
23
|
+
request.headers.get('X-Custom-Auth-Key') === env.PDF_WORKER_AUTH;
|
|
24
|
+
|
|
21
25
|
function normalizeReportFormat(format: unknown): string {
|
|
22
26
|
if (typeof format !== 'string') {
|
|
23
27
|
return DEFAULT_REPORT_FORMAT;
|
|
@@ -71,6 +75,13 @@ export default {
|
|
|
71
75
|
return new Response(null, { headers: corsHeaders });
|
|
72
76
|
}
|
|
73
77
|
|
|
78
|
+
if (!hasValidHeader(request, env)) {
|
|
79
|
+
return new Response(JSON.stringify({ error: 'Forbidden' }), {
|
|
80
|
+
status: 403,
|
|
81
|
+
headers: { ...corsHeaders, 'content-type': 'application/json' },
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
74
85
|
if (request.method === 'POST') {
|
|
75
86
|
let browser: Awaited<ReturnType<typeof launch>> | undefined;
|
|
76
87
|
|
package/wrangler.toml.example
CHANGED