Skip to content

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-111 #!/usr/bin/env bash
b8619f148earchadmin2026-10-112 # Pure, sourceable helpers for ArchForge External Status domain/TLS configuration.
b8619f148earchadmin2026-10-113 # Deliberately never source status.env; it contains literal secrets.
b8619f148earchadmin2026-10-114 
b8619f148earchadmin2026-10-115 af_valid_domain() {
b8619f148earchadmin2026-10-116  local domain=${1-} label
b8619f148earchadmin2026-10-117  [[ ${#domain} -le 253 && "$domain" == *.* && "$domain" != *..* && "$domain" != .* && "$domain" != *. ]] || return 1
b8619f148earchadmin2026-10-118  [[ "$domain" =~ ^[a-zA-Z0-9.-]+$ ]] || return 1
b8619f148earchadmin2026-10-119  local IFS=.
b8619f148earchadmin2026-10-1110  local -a labels
b8619f148earchadmin2026-10-1111  read -r -a labels <<< "$domain"
b8619f148earchadmin2026-10-1112  for label in "${labels[@]}"; do
b8619f148earchadmin2026-10-1113  [[ ${#label} -ge 1 && ${#label} -le 63 && "$label" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$ ]] || return 1
b8619f148earchadmin2026-10-1114  done
b8619f148earchadmin2026-10-1115  return 0
b8619f148earchadmin2026-10-1116 }
b8619f148earchadmin2026-10-1117 
b8619f148earchadmin2026-10-1118 af_valid_ipv4() {
b8619f148earchadmin2026-10-1119  local ip=${1-} part
b8619f148earchadmin2026-10-1120  [[ "$ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || return 1
b8619f148earchadmin2026-10-1121  local IFS=.
b8619f148earchadmin2026-10-1122  local -a octets
b8619f148earchadmin2026-10-1123  read -r -a octets <<< "$ip"
b8619f148earchadmin2026-10-1124  [[ ${#octets[@]} == 4 ]] || return 1
b8619f148earchadmin2026-10-1125  for part in "${octets[@]}"; do
b8619f148earchadmin2026-10-1126  [[ ${#part} -le 3 && ( "$part" == 0 || "$part" != 0* ) ]] || return 1
b8619f148earchadmin2026-10-1127  (( 10#$part <= 255 )) || return 1
b8619f148earchadmin2026-10-1128  done
b8619f148earchadmin2026-10-1129  return 0
b8619f148earchadmin2026-10-1130 }
b8619f148earchadmin2026-10-1131 
b8619f148earchadmin2026-10-1132 af_valid_ipv6() {
b8619f148earchadmin2026-10-1133  [[ -n ${1-} ]] || return 1
b8619f148earchadmin2026-10-1134  python3 -c 'import ipaddress,sys;ipaddress.IPv6Address(sys.argv[1])' "$1" >/dev/null 2>&1
b8619f148earchadmin2026-10-1135 }
b8619f148earchadmin2026-10-1136 
b8619f148earchadmin2026-10-1137 af_valid_email() {
b8619f148earchadmin2026-10-1138  local mail=${1-}
b8619f148earchadmin2026-10-1139  [[ -z "$mail" || ( "$mail" =~ ^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$ && ${#mail} -le 254 ) ]]
b8619f148earchadmin2026-10-1140 }
b8619f148earchadmin2026-10-1141 
b8619f148earchadmin2026-10-1142 af_read_public_domain() {
b8619f148earchadmin2026-10-1143  local env_file=$1 url
b8619f148earchadmin2026-10-1144  [[ -f "$env_file" && ! -L "$env_file" ]] || return 1
b8619f148earchadmin2026-10-1145  url=$(grep -m1 '^STATUS_PUBLIC_URL=' "$env_file" | cut -d= -f2-) || return 1
b8619f148earchadmin2026-10-1146  [[ "$url" == https://* ]] || return 1
b8619f148earchadmin2026-10-1147  url=${url#https://}
b8619f148earchadmin2026-10-1148  [[ "$url" != */* ]] || return 1
b8619f148earchadmin2026-10-1149  af_valid_domain "$url" || return 1
b8619f148earchadmin2026-10-1150  printf '%s' "$url"
b8619f148earchadmin2026-10-1151 }
b8619f148earchadmin2026-10-1152 
b8619f148earchadmin2026-10-1153 af_set_public_domain() {
b8619f148earchadmin2026-10-1154  local env_file=$1 domain=$2 tmp
b8619f148earchadmin2026-10-1155  af_valid_domain "$domain" || return 1
b8619f148earchadmin2026-10-1156  [[ -f "$env_file" && ! -L "$env_file" ]] || return 1
b8619f148earchadmin2026-10-1157  tmp=$(mktemp "$(dirname "$env_file")/.status-env.XXXXXXXX") || return 1
b8619f148earchadmin2026-10-1158  chmod --reference="$env_file" "$tmp"
b8619f148earchadmin2026-10-1159  chown --reference="$env_file" "$tmp"
b8619f148earchadmin2026-10-1160  if ! awk -v new="STATUS_PUBLIC_URL=https://${domain}" '
b8619f148earchadmin2026-10-1161  /^STATUS_PUBLIC_URL=/ { if (!seen++) print new; next }
b8619f148earchadmin2026-10-1162  { print }
b8619f148earchadmin2026-10-1163  END { if (!seen) print new }
b8619f148earchadmin2026-10-1164  ' "$env_file" > "$tmp"; then
b8619f148earchadmin2026-10-1165  rm -f "$tmp"
b8619f148earchadmin2026-10-1166  return 1
b8619f148earchadmin2026-10-1167  fi
b8619f148earchadmin2026-10-1168  mv -f -- "$tmp" "$env_file"
b8619f148earchadmin2026-10-1169 }
b8619f148earchadmin2026-10-1170 
b8619f148earchadmin2026-10-1171 af_render_caddy() {
b8619f148earchadmin2026-10-1172  local domain=$1 email=${2-}
b8619f148earchadmin2026-10-1173  af_valid_domain "$domain" && af_valid_email "$email" || return 1
b8619f148earchadmin2026-10-1174  printf '%s {\n' "$domain"
b8619f148earchadmin2026-10-1175  printf ' encode zstd gzip\n'
b8619f148earchadmin2026-10-1176  printf ' reverse_proxy 127.0.0.1:3000\n'
b8619f148earchadmin2026-10-1177  if [[ -n "$email" ]]; then
b8619f148earchadmin2026-10-1178  printf ' tls %s\n' "$email"
b8619f148earchadmin2026-10-1179  fi
b8619f148earchadmin2026-10-1180  printf '}\n'
b8619f148earchadmin2026-10-1181 }
b8619f148earchadmin2026-10-1182 
b8619f148earchadmin2026-10-1183 af_dns_records() {
b8619f148earchadmin2026-10-1184  local type=$1 domain=$2
b8619f148earchadmin2026-10-1185  [[ "$type" == A || "$type" == AAAA ]] && af_valid_domain "$domain" || return 1
b8619f148earchadmin2026-10-1186  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
b8619f148earchadmin2026-10-1187 }