Repository files, collaboration, releases and project activity.
ARCHFORGE HISTORY
Blame · scripts/domain-lib.sh
Branch main · the Commit author is not necessarily a verified ArchForge account.
b8619f148earchadmin2026-10-11
1 #!/usr/bin/env bashb8619f148earchadmin2026-10-11
2 # Pure, sourceable helpers for ArchForge External Status domain/TLS configuration.b8619f148earchadmin2026-10-11
3 # Deliberately never source status.env; it contains literal secrets.b8619f148earchadmin2026-10-11
4 b8619f148earchadmin2026-10-11
5 af_valid_domain() {b8619f148earchadmin2026-10-11
6 local domain=${1-} labelb8619f148earchadmin2026-10-11
7 [[ ${#domain} -le 253 && "$domain" == *.* && "$domain" != *..* && "$domain" != .* && "$domain" != *. ]] || return 1b8619f148earchadmin2026-10-11
8 [[ "$domain" =~ ^[a-zA-Z0-9.-]+$ ]] || return 1b8619f148earchadmin2026-10-11
9 local IFS=.b8619f148earchadmin2026-10-11
10 local -a labelsb8619f148earchadmin2026-10-11
11 read -r -a labels <<< "$domain"b8619f148earchadmin2026-10-11
12 for label in "${labels[@]}"; dob8619f148earchadmin2026-10-11
13 [[ ${#label} -ge 1 && ${#label} -le 63 && "$label" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$ ]] || return 1b8619f148earchadmin2026-10-11
14 doneb8619f148earchadmin2026-10-11
15 return 0b8619f148earchadmin2026-10-11
16 }b8619f148earchadmin2026-10-11
17 b8619f148earchadmin2026-10-11
18 af_valid_ipv4() {b8619f148earchadmin2026-10-11
19 local ip=${1-} partb8619f148earchadmin2026-10-11
20 [[ "$ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || return 1b8619f148earchadmin2026-10-11
21 local IFS=.b8619f148earchadmin2026-10-11
22 local -a octetsb8619f148earchadmin2026-10-11
23 read -r -a octets <<< "$ip"b8619f148earchadmin2026-10-11
24 [[ ${#octets[@]} == 4 ]] || return 1b8619f148earchadmin2026-10-11
25 for part in "${octets[@]}"; dob8619f148earchadmin2026-10-11
26 [[ ${#part} -le 3 && ( "$part" == 0 || "$part" != 0* ) ]] || return 1b8619f148earchadmin2026-10-11
27 (( 10#$part <= 255 )) || return 1b8619f148earchadmin2026-10-11
28 doneb8619f148earchadmin2026-10-11
29 return 0b8619f148earchadmin2026-10-11
30 }b8619f148earchadmin2026-10-11
31 b8619f148earchadmin2026-10-11
32 af_valid_ipv6() {b8619f148earchadmin2026-10-11
33 [[ -n ${1-} ]] || return 1b8619f148earchadmin2026-10-11
34 python3 -c 'import ipaddress,sys;ipaddress.IPv6Address(sys.argv[1])' "$1" >/dev/null 2>&1b8619f148earchadmin2026-10-11
35 }b8619f148earchadmin2026-10-11
36 b8619f148earchadmin2026-10-11
37 af_valid_email() {b8619f148earchadmin2026-10-11
38 local mail=${1-}b8619f148earchadmin2026-10-11
39 [[ -z "$mail" || ( "$mail" =~ ^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$ && ${#mail} -le 254 ) ]]b8619f148earchadmin2026-10-11
40 }b8619f148earchadmin2026-10-11
41 b8619f148earchadmin2026-10-11
42 af_read_public_domain() {b8619f148earchadmin2026-10-11
43 local env_file=$1 urlb8619f148earchadmin2026-10-11
44 [[ -f "$env_file" && ! -L "$env_file" ]] || return 1b8619f148earchadmin2026-10-11
45 url=$(grep -m1 '^STATUS_PUBLIC_URL=' "$env_file" | cut -d= -f2-) || return 1b8619f148earchadmin2026-10-11
46 [[ "$url" == https://* ]] || return 1b8619f148earchadmin2026-10-11
47 url=${url#https://}b8619f148earchadmin2026-10-11
48 [[ "$url" != */* ]] || return 1b8619f148earchadmin2026-10-11
49 af_valid_domain "$url" || return 1b8619f148earchadmin2026-10-11
50 printf '%s' "$url"b8619f148earchadmin2026-10-11
51 }b8619f148earchadmin2026-10-11
52 b8619f148earchadmin2026-10-11
53 af_set_public_domain() {b8619f148earchadmin2026-10-11
54 local env_file=$1 domain=$2 tmpb8619f148earchadmin2026-10-11
55 af_valid_domain "$domain" || return 1b8619f148earchadmin2026-10-11
56 [[ -f "$env_file" && ! -L "$env_file" ]] || return 1b8619f148earchadmin2026-10-11
57 tmp=$(mktemp "$(dirname "$env_file")/.status-env.XXXXXXXX") || return 1b8619f148earchadmin2026-10-11
58 chmod --reference="$env_file" "$tmp"b8619f148earchadmin2026-10-11
59 chown --reference="$env_file" "$tmp"b8619f148earchadmin2026-10-11
60 if ! awk -v new="STATUS_PUBLIC_URL=https://${domain}" 'b8619f148earchadmin2026-10-11
61 /^STATUS_PUBLIC_URL=/ { if (!seen++) print new; next }b8619f148earchadmin2026-10-11
62 { print }b8619f148earchadmin2026-10-11
63 END { if (!seen) print new }b8619f148earchadmin2026-10-11
64 ' "$env_file" > "$tmp"; thenb8619f148earchadmin2026-10-11
65 rm -f "$tmp"b8619f148earchadmin2026-10-11
66 return 1b8619f148earchadmin2026-10-11
67 fib8619f148earchadmin2026-10-11
68 mv -f -- "$tmp" "$env_file"b8619f148earchadmin2026-10-11
69 }b8619f148earchadmin2026-10-11
70 b8619f148earchadmin2026-10-11
71 af_render_caddy() {b8619f148earchadmin2026-10-11
72 local domain=$1 email=${2-}b8619f148earchadmin2026-10-11
73 af_valid_domain "$domain" && af_valid_email "$email" || return 1b8619f148earchadmin2026-10-11
74 printf '%s {\n' "$domain"b8619f148earchadmin2026-10-11
75 printf ' encode zstd gzip\n'b8619f148earchadmin2026-10-11
76 printf ' reverse_proxy 127.0.0.1:3000\n'b8619f148earchadmin2026-10-11
77 if [[ -n "$email" ]]; thenb8619f148earchadmin2026-10-11
78 printf ' tls %s\n' "$email"b8619f148earchadmin2026-10-11
79 fib8619f148earchadmin2026-10-11
80 printf '}\n'b8619f148earchadmin2026-10-11
81 }b8619f148earchadmin2026-10-11
82 b8619f148earchadmin2026-10-11
83 af_dns_records() {b8619f148earchadmin2026-10-11
84 local type=$1 domain=$2b8619f148earchadmin2026-10-11
85 [[ "$type" == A || "$type" == AAAA ]] && af_valid_domain "$domain" || return 1b8619f148earchadmin2026-10-11
86 dig +time=3 +tries=1 +short "$type" "$domain" | grep -E "$( [[ "$type" == A ]] && printf '^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$' || printf '^[0-9A-Fa-f:]+$' )" || trueb8619f148earchadmin2026-10-11
87 }