优化安装脚本
This commit is contained in:
204
install.sh
204
install.sh
@@ -21,8 +21,11 @@ PUBLISHED_SCRIPT_URL="${PUBLISHED_SCRIPT_URL:-https://s3.cloudyun.top/downloads/
|
|||||||
V2BX_DETECTED=0
|
V2BX_DETECTED=0
|
||||||
V2BX_CONFIG_PATH=""
|
V2BX_CONFIG_PATH=""
|
||||||
UNINSTALL_V2BX_DEFAULT="${UNINSTALL_V2BX_DEFAULT:-n}"
|
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}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}"
|
echo -e "${YELLOW}Published install script: ${PUBLISHED_SCRIPT_URL}${NC}"
|
||||||
|
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
@@ -36,6 +39,17 @@ if [[ "$OS" != "Linux" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
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)"
|
ARCH="$(uname -m)"
|
||||||
case "$ARCH" in
|
case "$ARCH" in
|
||||||
x86_64) BINARY_ARCH="amd64" ;;
|
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}}"
|
DOWNLOAD_URL="${DOWNLOAD_URL:-${RELEASE_BASE_URL}/sing-box-${DOWNLOAD_TARGET}}"
|
||||||
TMP_BINARY="$(mktemp)"
|
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() {
|
find_v2bx_config() {
|
||||||
local candidate
|
local candidate
|
||||||
for candidate in \
|
for candidate in \
|
||||||
@@ -83,6 +141,7 @@ load_v2bx_defaults() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${YELLOW}Detected V2bX configuration: ${V2BX_CONFIG_PATH}${NC}"
|
echo -e "${YELLOW}Detected V2bX configuration: ${V2BX_CONFIG_PATH}${NC}"
|
||||||
|
V2BX_IMPORTED_NODE_IDS=()
|
||||||
|
|
||||||
if command -v python3 >/dev/null 2>&1; then
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
local parsed
|
local parsed
|
||||||
@@ -97,56 +156,76 @@ with open(path, "r", encoding="utf-8") as f:
|
|||||||
nodes = data.get("Nodes") or []
|
nodes = data.get("Nodes") or []
|
||||||
node = nodes[0] if nodes else {}
|
node = nodes[0] if nodes else {}
|
||||||
|
|
||||||
for key in ("ApiHost", "ApiKey", "NodeID"):
|
api_host = node.get("ApiHost", "") if node else ""
|
||||||
value = node.get(key, "")
|
api_key = node.get("ApiKey", "") if node else ""
|
||||||
if value is None:
|
print(f"API_HOST={api_host or ''}")
|
||||||
value = ""
|
print(f"API_KEY={api_key or ''}")
|
||||||
print(str(value))
|
for entry in nodes:
|
||||||
|
node_id = entry.get("NodeID", "")
|
||||||
|
if node_id is None:
|
||||||
|
node_id = ""
|
||||||
|
print(f"NODE_ID={node_id}")
|
||||||
PY
|
PY
|
||||||
)"
|
)"
|
||||||
if [[ -n "$parsed" ]]; then
|
if [[ -n "$parsed" ]]; then
|
||||||
local parsed_api_host parsed_api_key parsed_node_id
|
local parsed_line
|
||||||
parsed_api_host="$(printf '%s\n' "$parsed" | sed -n '1p')"
|
while IFS= read -r parsed_line; do
|
||||||
parsed_api_key="$(printf '%s\n' "$parsed" | sed -n '2p')"
|
parsed_line="$(sanitize_value "$parsed_line")"
|
||||||
parsed_node_id="$(printf '%s\n' "$parsed" | sed -n '3p')"
|
case "$parsed_line" in
|
||||||
|
API_HOST=*)
|
||||||
if [[ -z "${PANEL_URL:-}" && -n "$parsed_api_host" ]]; then
|
if [[ -z "${PANEL_URL:-}" ]]; then
|
||||||
PANEL_URL="$parsed_api_host"
|
PANEL_URL="$(sanitize_value "${parsed_line#API_HOST=}")"
|
||||||
fi
|
fi
|
||||||
if [[ -z "${PANEL_TOKEN:-}" && -n "$parsed_api_key" ]]; then
|
;;
|
||||||
PANEL_TOKEN="$parsed_api_key"
|
API_KEY=*)
|
||||||
fi
|
if [[ -z "${PANEL_TOKEN:-}" ]]; then
|
||||||
if [[ -z "${NODE_ID:-}" && -n "$parsed_node_id" ]]; then
|
PANEL_TOKEN="$(sanitize_value "${parsed_line#API_KEY=}")"
|
||||||
NODE_ID="$parsed_node_id"
|
fi
|
||||||
fi
|
;;
|
||||||
|
NODE_ID=*)
|
||||||
echo -e "${YELLOW}Imported defaults from V2bX config: ApiHost=${parsed_api_host:-<empty>}, NodeID=${parsed_node_id:-<empty>}${NC}"
|
append_unique_node_id "${parsed_line#NODE_ID=}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <<< "$parsed"
|
||||||
fi
|
fi
|
||||||
elif command -v jq >/dev/null 2>&1; then
|
elif command -v jq >/dev/null 2>&1; then
|
||||||
local parsed
|
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
|
if [[ -n "$parsed" ]]; then
|
||||||
local parsed_api_host parsed_api_key parsed_node_id
|
local parsed_line
|
||||||
parsed_api_host="$(printf '%s\n' "$parsed" | sed -n '1p')"
|
while IFS= read -r parsed_line; do
|
||||||
parsed_api_key="$(printf '%s\n' "$parsed" | sed -n '2p')"
|
parsed_line="$(sanitize_value "$parsed_line")"
|
||||||
parsed_node_id="$(printf '%s\n' "$parsed" | sed -n '3p')"
|
case "$parsed_line" in
|
||||||
|
API_HOST=*)
|
||||||
if [[ -z "${PANEL_URL:-}" && -n "$parsed_api_host" ]]; then
|
if [[ -z "${PANEL_URL:-}" ]]; then
|
||||||
PANEL_URL="$parsed_api_host"
|
PANEL_URL="$(sanitize_value "${parsed_line#API_HOST=}")"
|
||||||
fi
|
fi
|
||||||
if [[ -z "${PANEL_TOKEN:-}" && -n "$parsed_api_key" ]]; then
|
;;
|
||||||
PANEL_TOKEN="$parsed_api_key"
|
API_KEY=*)
|
||||||
fi
|
if [[ -z "${PANEL_TOKEN:-}" ]]; then
|
||||||
if [[ -z "${NODE_ID:-}" && -n "$parsed_node_id" ]]; then
|
PANEL_TOKEN="$(sanitize_value "${parsed_line#API_KEY=}")"
|
||||||
NODE_ID="$parsed_node_id"
|
fi
|
||||||
fi
|
;;
|
||||||
|
NODE_ID=*)
|
||||||
echo -e "${YELLOW}Imported defaults from V2bX config: ApiHost=${parsed_api_host:-<empty>}, NodeID=${parsed_node_id:-<empty>}${NC}"
|
append_unique_node_id "${parsed_line#NODE_ID=}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <<< "$parsed"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo -e "${YELLOW}Neither python3 nor jq found, skipping automatic V2bX config import.${NC}"
|
echo -e "${YELLOW}Neither python3 nor jq found, skipping automatic V2bX config import.${NC}"
|
||||||
fi
|
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
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +251,7 @@ prompt_uninstall_v2bx() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
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
|
local uninstall_v2bx_answer
|
||||||
uninstall_v2bx_answer=${INPUT_UNINSTALL_V2BX:-$UNINSTALL_V2BX_DEFAULT}
|
uninstall_v2bx_answer=${INPUT_UNINSTALL_V2BX:-$UNINSTALL_V2BX_DEFAULT}
|
||||||
|
|
||||||
@@ -252,32 +331,39 @@ fi
|
|||||||
|
|
||||||
load_v2bx_defaults || true
|
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
|
download_binary
|
||||||
cleanup_legacy_service
|
cleanup_legacy_service
|
||||||
|
|
||||||
read -p "Enter Panel URL [${PANEL_URL}]: " INPUT_URL
|
read -u 3 -p "Enter Panel URL [${PANEL_URL}]: " INPUT_URL
|
||||||
PANEL_URL=${INPUT_URL:-$PANEL_URL}
|
PANEL_URL="$(sanitize_value "${INPUT_URL:-$PANEL_URL}")"
|
||||||
|
|
||||||
read -p "Enter Panel Token (Node Key) [${PANEL_TOKEN}]: " INPUT_TOKEN
|
read -u 3 -p "Enter Panel Token (Node Key) [${PANEL_TOKEN}]: " INPUT_TOKEN
|
||||||
PANEL_TOKEN=${INPUT_TOKEN:-$PANEL_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
|
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=${INPUT_PROXY_PROTOCOL:-${ENABLE_PROXY_PROTOCOL_HINT:-n}}
|
ENABLE_PROXY_PROTOCOL_HINT="$(sanitize_value "${INPUT_PROXY_PROTOCOL:-${ENABLE_PROXY_PROTOCOL_HINT:-n}}")"
|
||||||
|
|
||||||
declare -a NODE_IDS
|
declare -a NODE_IDS
|
||||||
|
|
||||||
i=1
|
i=1
|
||||||
while true; do
|
while true; do
|
||||||
DEFAULT_NODE_ID=""
|
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"
|
DEFAULT_NODE_ID="$NODE_ID"
|
||||||
fi
|
fi
|
||||||
if [[ -n "$DEFAULT_NODE_ID" ]]; then
|
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
|
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
|
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 [[ "$CURRENT_NODE_ID" =~ ^([nN][oO])$ ]]; then
|
||||||
if [[ "${#NODE_IDS[@]}" -eq 0 ]]; then
|
if [[ "${#NODE_IDS[@]}" -eq 0 ]]; then
|
||||||
echo -e "${RED}At least one Node ID is required${NC}"
|
echo -e "${RED}At least one Node ID is required${NC}"
|
||||||
@@ -285,31 +371,39 @@ while true; do
|
|||||||
fi
|
fi
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
CURRENT_NODE_ID="$(normalize_node_id_input "$CURRENT_NODE_ID")"
|
||||||
if [[ -z "$CURRENT_NODE_ID" ]]; then
|
if [[ -z "$CURRENT_NODE_ID" ]]; then
|
||||||
echo -e "${RED}Node ID is required for node #$i${NC}"
|
echo -e "${RED}Node ID is required for node #$i${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! [[ "$CURRENT_NODE_ID" =~ ^[0-9]+$ ]]; then
|
read -r -a CURRENT_NODE_ID_PARTS <<< "$CURRENT_NODE_ID"
|
||||||
echo -e "${RED}Node ID must be a positive integer${NC}"
|
if [[ "${#CURRENT_NODE_ID_PARTS[@]}" -eq 0 ]]; then
|
||||||
|
echo -e "${RED}Node ID is required for node #$i${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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++))
|
((i++))
|
||||||
done
|
done
|
||||||
|
|
||||||
NODE_COUNT=${#NODE_IDS[@]}
|
NODE_COUNT=${#NODE_IDS[@]}
|
||||||
|
|
||||||
DNS_MODE_DEFAULT=${DNS_MODE:-udp}
|
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:]')
|
DNS_MODE=$(echo "${INPUT_DNS_MODE:-$DNS_MODE_DEFAULT}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
case "$DNS_MODE" in
|
case "$DNS_MODE" in
|
||||||
udp)
|
udp)
|
||||||
DNS_SERVER_DEFAULT=${DNS_SERVER:-1.1.1.1}
|
DNS_SERVER_DEFAULT=${DNS_SERVER:-1.1.1.1}
|
||||||
DNS_SERVER_PORT_DEFAULT=${DNS_SERVER_PORT:-53}
|
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}
|
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}
|
DNS_SERVER_PORT=${INPUT_DNS_SERVER_PORT:-$DNS_SERVER_PORT_DEFAULT}
|
||||||
if [[ -z "$DNS_SERVER" ]]; then
|
if [[ -z "$DNS_SERVER" ]]; then
|
||||||
echo -e "${RED}DNS server is required in udp mode${NC}"
|
echo -e "${RED}DNS server is required in udp mode${NC}"
|
||||||
|
|||||||
Reference in New Issue
Block a user