Skip to content

Repository files, collaboration, releases and project activity.

archforge-external-status / scripts/domain-lib.shmain
▤ scripts/domain-lib.sh2799 bytes
88 lines · Select a line number for a permalink
1#!/usr/bin/env bash
2# Pure, sourceable helpers for ArchForge External Status domain/TLS configuration.
3# Deliberately never source status.env; it contains literal secrets.
5af_valid_domain() {
6 local domain=${1-} label
7 [[ ${#domain} -le 253 && "$domain" == *.* && "$domain" != *..* && "$domain" != .* && "$domain" != *. ]] || return 1
8 [[ "$domain" =~ ^[a-zA-Z0-9.-]+$ ]] || return 1
9 local IFS=.
10 local -a labels
11 read -r -a labels <<< "$domain"
12 for label in "${labels[@]}"; do
13 [[ ${#label} -ge 1 && ${#label} -le 63 && "$label" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$ ]] || return 1
14 done
15 return 0
18af_valid_ipv4() {
19 local ip=${1-} part
20 [[ "$ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || return 1
21 local IFS=.
22 local -a octets
23 read -r -a octets <<< "$ip"
24 [[ ${#octets[@]} == 4 ]] || return 1
25 for part in "${octets[@]}"; do
26 [[ ${#part} -le 3 && ( "$part" == 0 || "$part" != 0* ) ]] || return 1
27 (( 10#$part <= 255 )) || return 1
28 done
29 return 0
32af_valid_ipv6() {
33 [[ -n ${1-} ]] || return 1
34 python3 -c 'import ipaddress,sys;ipaddress.IPv6Address(sys.argv[1])' "$1" >/dev/null 2>&1
37af_valid_email() {
38 local mail=${1-}
39 [[ -z "$mail" || ( "$mail" =~ ^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$ && ${#mail} -le 254 ) ]]
42af_read_public_domain() {
43 local env_file=$1 url
44 [[ -f "$env_file" && ! -L "$env_file" ]] || return 1
45 url=$(grep -m1 '^STATUS_PUBLIC_URL=' "$env_file" | cut -d= -f2-) || return 1
46 [[ "$url" == https://* ]] || return 1
47 url=${url#https://}
48 [[ "$url" != */* ]] || return 1
49 af_valid_domain "$url" || return 1
50 printf '%s' "$url"
53af_set_public_domain() {
54 local env_file=$1 domain=$2 tmp
55 af_valid_domain "$domain" || return 1
56 [[ -f "$env_file" && ! -L "$env_file" ]] || return 1
57 tmp=$(mktemp "$(dirname "$env_file")/.status-env.XXXXXXXX") || return 1
58 chmod --reference="$env_file" "$tmp"
59 chown --reference="$env_file" "$tmp"
60 if ! awk -v new="STATUS_PUBLIC_URL=https://${domain}" '
61 /^STATUS_PUBLIC_URL=/ { if (!seen++) print new; next }
62 { print }
63 END { if (!seen) print new }
64 ' "$env_file" > "$tmp"; then
65 rm -f "$tmp"
66 return 1
67 fi
68 mv -f -- "$tmp" "$env_file"
71af_render_caddy() {
72 local domain=$1 email=${2-}
73 af_valid_domain "$domain" && af_valid_email "$email" || return 1
74 printf '%s {\n' "$domain"
75 printf ' encode zstd gzip\n'
76 printf ' reverse_proxy 127.0.0.1:3000\n'
77 if [[ -n "$email" ]]; then
78 printf ' tls %s\n' "$email"
79 fi
80 printf '}\n'
83af_dns_records() {
84 local type=$1 domain=$2
85 [[ "$type" == A || "$type" == AAAA ]] && af_valid_domain "$domain" || return 1
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:]+$' )" || true