Files
appstore/apps/anubis/latest/scripts/init.sh
T
2026-08-01 03:36:15 +08:00

119 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
read_env_value() {
local key="$1"
local value=""
if [[ -f "$ENV_FILE" ]]; then
value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)"
fi
value="${value%$'\r'}"
case "$value" in
\"*\") value="${value#\"}"; value="${value%\"}" ;;
\'*\') value="${value#\'}"; value="${value%\'}" ;;
esac
printf '%s\n' "$value"
}
set_env_value() {
local key="$1"
local value="$2"
local temp_file
temp_file="$(mktemp "${ENV_FILE}.tmp.XXXXXX")"
awk -v key="$key" -v value="$value" '
BEGIN { updated = 0 }
$0 ~ ("^" key "=") {
if (!updated) {
print key "=" value
updated = 1
}
next
}
{ print }
END {
if (!updated) print key "=" value
}
' "$ENV_FILE" >"$temp_file"
chmod 600 "$temp_file"
mv -f -- "$temp_file" "$ENV_FILE"
}
validate_boolean() {
local key="$1"
local value="$2"
[[ "$value" == "true" || "$value" == "false" ]] || fail "$key must be true or false"
}
[[ -f "$ENV_FILE" ]] || fail "Environment file not found: ${ENV_FILE}"
[[ ! -L "$ENV_FILE" ]] || fail "Environment file must not be a symbolic link"
bind_address="$(read_env_value PANEL_APP_BIND_ADDRESS)"
[[ "$bind_address" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || fail "PANEL_APP_BIND_ADDRESS must be an IPv4 address"
IFS=. read -r -a bind_octets <<< "$bind_address"
for octet in "${bind_octets[@]}"; do
((10#$octet <= 255)) || fail "PANEL_APP_BIND_ADDRESS contains an invalid IPv4 octet"
done
port="$(read_env_value PANEL_APP_PORT_HTTP)"
[[ "$port" =~ ^[0-9]+$ ]] || fail "PANEL_APP_PORT_HTTP must be an integer"
((10#$port >= 1 && 10#$port <= 65535)) || fail "PANEL_APP_PORT_HTTP must be between 1 and 65535"
target="$(read_env_value ANUBIS_TARGET)"
[[ "$target" == http://* || "$target" == https://* ]] || fail "ANUBIS_TARGET must be an http or https URL"
case "$target" in
*[[:space:]]* | *\\* | *'$'* | *'#'* | *'"'* | *"'"*)
fail "ANUBIS_TARGET contains unsupported dotenv characters"
;;
esac
redirect_domains="$(read_env_value ANUBIS_REDIRECT_DOMAINS)"
[[ -n "$redirect_domains" ]] || fail "ANUBIS_REDIRECT_DOMAINS must not be empty"
case "$redirect_domains" in
*[[:space:]]* | *\\* | *'$'* | *'#'* | *'"'* | *"'"*)
fail "ANUBIS_REDIRECT_DOMAINS contains unsupported dotenv characters"
;;
esac
IFS=',' read -r -a redirect_domain_items <<< "$redirect_domains"
for redirect_domain in "${redirect_domain_items[@]}"; do
[[ "$redirect_domain" =~ ^(\*\.)?([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(\.([A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?))*$ ]] || \
fail "ANUBIS_REDIRECT_DOMAINS must contain only hostnames or IPv4 addresses without schemes or ports"
done
difficulty="$(read_env_value ANUBIS_DIFFICULTY)"
[[ "$difficulty" =~ ^[0-9]+$ ]] || fail "ANUBIS_DIFFICULTY must be an integer"
((10#$difficulty >= 1 && 10#$difficulty <= 16)) || fail "ANUBIS_DIFFICULTY must be between 1 and 16"
validate_boolean ANUBIS_SERVE_ROBOTS "$(read_env_value ANUBIS_SERVE_ROBOTS)"
validate_boolean ANUBIS_COOKIE_SECURE "$(read_env_value ANUBIS_COOKIE_SECURE)"
validate_boolean ANUBIS_USE_REMOTE_ADDRESS "$(read_env_value ANUBIS_USE_REMOTE_ADDRESS)"
webmaster_email="$(read_env_value ANUBIS_WEBMASTER_EMAIL)"
if [[ -n "$webmaster_email" ]]; then
case "$webmaster_email" in
*@*.*) ;;
*) fail "ANUBIS_WEBMASTER_EMAIL must be empty or a valid email address" ;;
esac
case "$webmaster_email" in
*[[:space:]]* | *\\* | *'$'* | *'"'* | *"'"*)
fail "ANUBIS_WEBMASTER_EMAIL contains unsupported dotenv characters"
;;
esac
fi
signing_key="$(read_env_value ANUBIS_ED25519_PRIVATE_KEY_HEX)"
if [[ -z "$signing_key" || "$signing_key" == "generate" ]]; then
signing_key="$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n')"
set_env_value ANUBIS_ED25519_PRIVATE_KEY_HEX "$signing_key"
fi
[[ "$signing_key" =~ ^[0-9a-fA-F]{64}$ ]] || fail "ANUBIS_ED25519_PRIVATE_KEY_HEX must contain exactly 64 hexadecimal characters"
chmod 600 "$ENV_FILE"