优化安装脚本

This commit is contained in:
CN-JS-HuiBai
2026-04-15 21:55:45 +08:00
parent 8edea831f6
commit 0df93ed044

View File

@@ -21,8 +21,11 @@ PUBLISHED_SCRIPT_URL="${PUBLISHED_SCRIPT_URL:-https://s3.cloudyun.top/downloads/
V2BX_DETECTED=0
V2BX_CONFIG_PATH=""
UNINSTALL_V2BX_DEFAULT="${UNINSTALL_V2BX_DEFAULT:-n}"
SCRIPT_VERSION="${SCRIPT_VERSION:-v1.2.4}"
declare -a V2BX_IMPORTED_NODE_IDS=()
echo -e "${GREEN}Welcome to singbox Release Installation Script${NC}"
echo -e "${GREEN}Script version: ${SCRIPT_VERSION}${NC}"
echo -e "${YELLOW}Published install script: ${PUBLISHED_SCRIPT_URL}${NC}"
if [[ $EUID -ne 0 ]]; then
@@ -36,6 +39,17 @@ if [[ "$OS" != "Linux" ]]; then
exit 1
fi
if [[ ! -t 0 ]]; then
if [[ -r /dev/tty ]]; then
exec 3</dev/tty
else
echo -e "${RED}Interactive input requires a TTY. Please run this script in a terminal.${NC}"
exit 1
fi
else
exec 3<&0
fi
ARCH="$(uname -m)"
case "$ARCH" in
x86_64) BINARY_ARCH="amd64" ;;
@@ -51,6 +65,50 @@ DOWNLOAD_TARGET="${DOWNLOAD_TARGET:-linux-${BINARY_ARCH}}"
DOWNLOAD_URL="${DOWNLOAD_URL:-${RELEASE_BASE_URL}/sing-box-${DOWNLOAD_TARGET}}"
TMP_BINARY="$(mktemp)"
sanitize_value() {
printf '%s' "$1" | tr -d '\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
}
normalize_node_id_input() {
local value
value="$(sanitize_value "$1")"
value="${value#[}"
value="${value%]}"
value="${value//,/ }"
value="${value//;/ }"
value="${value//\"/}"
value="${value//\'/}"
value="$(printf '%s' "$value" | tr -s '[:space:]' ' ')"
sanitize_value "$value"
}
append_unique_node_id() {
local normalized_value
local node_id_part
local existing_node_id
normalized_value="$(normalize_node_id_input "$1")"
if [[ -z "$normalized_value" ]]; then
return 0
fi
read -r -a NORMALIZED_NODE_ID_PARTS <<< "$normalized_value"
for node_id_part in "${NORMALIZED_NODE_ID_PARTS[@]}"; do
if ! [[ "$node_id_part" =~ ^[0-9]+$ ]]; then
continue
fi
for existing_node_id in "${V2BX_IMPORTED_NODE_IDS[@]}"; do
if [[ "$existing_node_id" == "$node_id_part" ]]; then
node_id_part=""
break
fi
done
if [[ -n "$node_id_part" ]]; then
V2BX_IMPORTED_NODE_IDS+=("$node_id_part")
fi
done
}
find_v2bx_config() {
local candidate
for candidate in \
@@ -83,6 +141,7 @@ load_v2bx_defaults() {
fi
echo -e "${YELLOW}Detected V2bX configuration: ${V2BX_CONFIG_PATH}${NC}"
V2BX_IMPORTED_NODE_IDS=()
if command -v python3 >/dev/null 2>&1; then
local parsed
@@ -97,56 +156,76 @@ with open(path, "r", encoding="utf-8") as f:
nodes = data.get("Nodes") or []
node = nodes[0] if nodes else {}
for key in ("ApiHost", "ApiKey", "NodeID"):
value = node.get(key, "")
if value is None:
value = ""
print(str(value))
api_host = node.get("ApiHost", "") if node else ""
api_key = node.get("ApiKey", "") if node else ""
print(f"API_HOST={api_host or ''}")
print(f"API_KEY={api_key or ''}")
for entry in nodes:
node_id = entry.get("NodeID", "")
if node_id is None:
node_id = ""
print(f"NODE_ID={node_id}")
PY
)"
if [[ -n "$parsed" ]]; then
local parsed_api_host parsed_api_key parsed_node_id
parsed_api_host="$(printf '%s\n' "$parsed" | sed -n '1p')"
parsed_api_key="$(printf '%s\n' "$parsed" | sed -n '2p')"
parsed_node_id="$(printf '%s\n' "$parsed" | sed -n '3p')"
if [[ -z "${PANEL_URL:-}" && -n "$parsed_api_host" ]]; then
PANEL_URL="$parsed_api_host"
local parsed_line
while IFS= read -r parsed_line; do
parsed_line="$(sanitize_value "$parsed_line")"
case "$parsed_line" in
API_HOST=*)
if [[ -z "${PANEL_URL:-}" ]]; then
PANEL_URL="$(sanitize_value "${parsed_line#API_HOST=}")"
fi
if [[ -z "${PANEL_TOKEN:-}" && -n "$parsed_api_key" ]]; then
PANEL_TOKEN="$parsed_api_key"
;;
API_KEY=*)
if [[ -z "${PANEL_TOKEN:-}" ]]; then
PANEL_TOKEN="$(sanitize_value "${parsed_line#API_KEY=}")"
fi
if [[ -z "${NODE_ID:-}" && -n "$parsed_node_id" ]]; then
NODE_ID="$parsed_node_id"
fi
echo -e "${YELLOW}Imported defaults from V2bX config: ApiHost=${parsed_api_host:-<empty>}, NodeID=${parsed_node_id:-<empty>}${NC}"
;;
NODE_ID=*)
append_unique_node_id "${parsed_line#NODE_ID=}"
;;
esac
done <<< "$parsed"
fi
elif command -v jq >/dev/null 2>&1; then
local parsed
parsed="$(jq -r '(.Nodes[0].ApiHost // ""), (.Nodes[0].ApiKey // ""), (.Nodes[0].NodeID // "")' "$V2BX_CONFIG_PATH" 2>/dev/null || true)"
parsed="$(jq -r '(.Nodes[0].ApiHost // "" | "API_HOST=" + .), (.Nodes[0].ApiKey // "" | "API_KEY=" + .), (.Nodes[]?.NodeID // "" | tostring | "NODE_ID=" + .)' "$V2BX_CONFIG_PATH" 2>/dev/null || true)"
if [[ -n "$parsed" ]]; then
local parsed_api_host parsed_api_key parsed_node_id
parsed_api_host="$(printf '%s\n' "$parsed" | sed -n '1p')"
parsed_api_key="$(printf '%s\n' "$parsed" | sed -n '2p')"
parsed_node_id="$(printf '%s\n' "$parsed" | sed -n '3p')"
if [[ -z "${PANEL_URL:-}" && -n "$parsed_api_host" ]]; then
PANEL_URL="$parsed_api_host"
local parsed_line
while IFS= read -r parsed_line; do
parsed_line="$(sanitize_value "$parsed_line")"
case "$parsed_line" in
API_HOST=*)
if [[ -z "${PANEL_URL:-}" ]]; then
PANEL_URL="$(sanitize_value "${parsed_line#API_HOST=}")"
fi
if [[ -z "${PANEL_TOKEN:-}" && -n "$parsed_api_key" ]]; then
PANEL_TOKEN="$parsed_api_key"
;;
API_KEY=*)
if [[ -z "${PANEL_TOKEN:-}" ]]; then
PANEL_TOKEN="$(sanitize_value "${parsed_line#API_KEY=}")"
fi
if [[ -z "${NODE_ID:-}" && -n "$parsed_node_id" ]]; then
NODE_ID="$parsed_node_id"
fi
echo -e "${YELLOW}Imported defaults from V2bX config: ApiHost=${parsed_api_host:-<empty>}, NodeID=${parsed_node_id:-<empty>}${NC}"
;;
NODE_ID=*)
append_unique_node_id "${parsed_line#NODE_ID=}"
;;
esac
done <<< "$parsed"
fi
else
echo -e "${YELLOW}Neither python3 nor jq found, skipping automatic V2bX config import.${NC}"
fi
if [[ -z "${NODE_ID:-}" && "${#V2BX_IMPORTED_NODE_IDS[@]}" -gt 0 ]]; then
NODE_ID="${V2BX_IMPORTED_NODE_IDS[0]}"
fi
if [[ "${#V2BX_IMPORTED_NODE_IDS[@]}" -gt 0 ]]; then
echo -e "${YELLOW}Imported defaults from V2bX config: ApiHost=${PANEL_URL:-<empty>}, NodeIDs=$(IFS=,; echo "${V2BX_IMPORTED_NODE_IDS[*]}")${NC}"
else
echo -e "${YELLOW}Imported defaults from V2bX config: ApiHost=${PANEL_URL:-<empty>}, NodeIDs=<empty>${NC}"
fi
return 0
}
@@ -172,7 +251,7 @@ prompt_uninstall_v2bx() {
return 0
fi
read -p "Detected V2bX. Uninstall it now? [${UNINSTALL_V2BX_DEFAULT}]: " INPUT_UNINSTALL_V2BX
read -u 3 -p "Detected V2bX. Uninstall it now? [${UNINSTALL_V2BX_DEFAULT}]: " INPUT_UNINSTALL_V2BX
local uninstall_v2bx_answer
uninstall_v2bx_answer=${INPUT_UNINSTALL_V2BX:-$UNINSTALL_V2BX_DEFAULT}
@@ -252,32 +331,39 @@ fi
load_v2bx_defaults || true
PANEL_URL="$(sanitize_value "${PANEL_URL:-}")"
PANEL_TOKEN="$(sanitize_value "${PANEL_TOKEN:-}")"
NODE_ID="$(sanitize_value "${NODE_ID:-}")"
ENABLE_PROXY_PROTOCOL_HINT="$(sanitize_value "${ENABLE_PROXY_PROTOCOL_HINT:-n}")"
download_binary
cleanup_legacy_service
read -p "Enter Panel URL [${PANEL_URL}]: " INPUT_URL
PANEL_URL=${INPUT_URL:-$PANEL_URL}
read -u 3 -p "Enter Panel URL [${PANEL_URL}]: " INPUT_URL
PANEL_URL="$(sanitize_value "${INPUT_URL:-$PANEL_URL}")"
read -p "Enter Panel Token (Node Key) [${PANEL_TOKEN}]: " INPUT_TOKEN
PANEL_TOKEN=${INPUT_TOKEN:-$PANEL_TOKEN}
read -u 3 -p "Enter Panel Token (Node Key) [${PANEL_TOKEN}]: " INPUT_TOKEN
PANEL_TOKEN="$(sanitize_value "${INPUT_TOKEN:-$PANEL_TOKEN}")"
read -p "This node is behind an L4 proxy/LB that sends PROXY protocol? [${ENABLE_PROXY_PROTOCOL_HINT:-n}]: " INPUT_PROXY_PROTOCOL
ENABLE_PROXY_PROTOCOL_HINT=${INPUT_PROXY_PROTOCOL:-${ENABLE_PROXY_PROTOCOL_HINT:-n}}
read -u 3 -p "This node is behind an L4 proxy/LB that sends PROXY protocol? [${ENABLE_PROXY_PROTOCOL_HINT:-n}]: " INPUT_PROXY_PROTOCOL
ENABLE_PROXY_PROTOCOL_HINT="$(sanitize_value "${INPUT_PROXY_PROTOCOL:-${ENABLE_PROXY_PROTOCOL_HINT:-n}}")"
declare -a NODE_IDS
i=1
while true; do
DEFAULT_NODE_ID=""
if [[ "$i" -eq 1 && -n "${NODE_ID:-}" ]]; then
if [[ "$i" -le "${#V2BX_IMPORTED_NODE_IDS[@]}" ]]; then
DEFAULT_NODE_ID="${V2BX_IMPORTED_NODE_IDS[$((i-1))]}"
elif [[ "$i" -eq 1 && -n "${NODE_ID:-}" ]]; then
DEFAULT_NODE_ID="$NODE_ID"
fi
if [[ -n "$DEFAULT_NODE_ID" ]]; then
read -p "Enter Node ID for node #$i [${DEFAULT_NODE_ID}] (type NO to finish): " INPUT_ID
read -u 3 -p "Enter Node ID for node #$i [${DEFAULT_NODE_ID}] (type NO to finish): " INPUT_ID
else
read -p "Enter Node ID for node #$i (type NO to finish): " INPUT_ID
read -u 3 -p "Enter Node ID for node #$i (type NO to finish): " INPUT_ID
fi
CURRENT_NODE_ID=${INPUT_ID:-$DEFAULT_NODE_ID}
CURRENT_NODE_ID="$(sanitize_value "${INPUT_ID:-$DEFAULT_NODE_ID}")"
if [[ "$CURRENT_NODE_ID" =~ ^([nN][oO])$ ]]; then
if [[ "${#NODE_IDS[@]}" -eq 0 ]]; then
echo -e "${RED}At least one Node ID is required${NC}"
@@ -285,31 +371,39 @@ while true; do
fi
break
fi
CURRENT_NODE_ID="$(normalize_node_id_input "$CURRENT_NODE_ID")"
if [[ -z "$CURRENT_NODE_ID" ]]; then
echo -e "${RED}Node ID is required for node #$i${NC}"
exit 1
fi
if ! [[ "$CURRENT_NODE_ID" =~ ^[0-9]+$ ]]; then
echo -e "${RED}Node ID must be a positive integer${NC}"
read -r -a CURRENT_NODE_ID_PARTS <<< "$CURRENT_NODE_ID"
if [[ "${#CURRENT_NODE_ID_PARTS[@]}" -eq 0 ]]; then
echo -e "${RED}Node ID is required for node #$i${NC}"
exit 1
fi
NODE_IDS+=("$CURRENT_NODE_ID")
for CURRENT_NODE_ID_PART in "${CURRENT_NODE_ID_PARTS[@]}"; do
if ! [[ "$CURRENT_NODE_ID_PART" =~ ^[0-9]+$ ]]; then
echo -e "${RED}Node ID must be a positive integer, got: ${CURRENT_NODE_ID_PART}${NC}"
exit 1
fi
NODE_IDS+=("$CURRENT_NODE_ID_PART")
done
((i++))
done
NODE_COUNT=${#NODE_IDS[@]}
DNS_MODE_DEFAULT=${DNS_MODE:-udp}
read -p "Enter DNS mode [${DNS_MODE_DEFAULT}] (udp/local): " INPUT_DNS_MODE
read -u 3 -p "Enter DNS mode [${DNS_MODE_DEFAULT}] (udp/local): " INPUT_DNS_MODE
DNS_MODE=$(echo "${INPUT_DNS_MODE:-$DNS_MODE_DEFAULT}" | tr '[:upper:]' '[:lower:]')
case "$DNS_MODE" in
udp)
DNS_SERVER_DEFAULT=${DNS_SERVER:-1.1.1.1}
DNS_SERVER_PORT_DEFAULT=${DNS_SERVER_PORT:-53}
read -p "Enter DNS server [${DNS_SERVER_DEFAULT}]: " INPUT_DNS_SERVER
read -u 3 -p "Enter DNS server [${DNS_SERVER_DEFAULT}]: " INPUT_DNS_SERVER
DNS_SERVER=${INPUT_DNS_SERVER:-$DNS_SERVER_DEFAULT}
read -p "Enter DNS server port [${DNS_SERVER_PORT_DEFAULT}]: " INPUT_DNS_SERVER_PORT
read -u 3 -p "Enter DNS server port [${DNS_SERVER_PORT_DEFAULT}]: " INPUT_DNS_SERVER_PORT
DNS_SERVER_PORT=${INPUT_DNS_SERVER_PORT:-$DNS_SERVER_PORT_DEFAULT}
if [[ -z "$DNS_SERVER" ]]; then
echo -e "${RED}DNS server is required in udp mode${NC}"