优化install.sh
Some checks failed
Build Linux Packages / Calculate version (release) Has been cancelled
Build Linux Packages / Build binary (386, i386, true, linux, i386) (release) Has been cancelled
Build Linux Packages / Build binary (amd64, amd64, true, linux, x86_64, x86_64) (release) Has been cancelled
Build Linux Packages / Build binary (arm, armel, 6, linux, armv6hl) (release) Has been cancelled
Build Linux Packages / Build binary (arm, armhf, 7, true, linux, armv7hl, armv7hl) (release) Has been cancelled
Build Linux Packages / Build binary (arm64, arm64, true, linux, aarch64, aarch64) (release) Has been cancelled
Build Linux Packages / Build binary (loong64, loongarch64, true, linux, loongarch64) (release) Has been cancelled
Build Linux Packages / Build binary (mips64le, mips64el, linux, mips64el) (release) Has been cancelled
Build Linux Packages / Build binary (mipsle, mipsel, softfloat, true, linux, mipsel) (release) Has been cancelled
Build Linux Packages / Build binary (ppc64le, ppc64el, linux, ppc64le) (release) Has been cancelled
Build Linux Packages / Build binary (riscv64, riscv64, true, linux, riscv64) (release) Has been cancelled
Build Linux Packages / Build binary (s390x, s390x, linux, s390x) (release) Has been cancelled
Build Linux Packages / Upload builds (release) Has been cancelled
Publish Docker Images / Build binary (386, linux/386, true) (release) Has been cancelled
Publish Docker Images / Build binary (amd64, linux/amd64, true) (release) Has been cancelled
Publish Docker Images / Build binary (arm, linux/arm/v6, 6) (release) Has been cancelled
Publish Docker Images / Build binary (arm, linux/arm/v7, 7, true) (release) Has been cancelled
Publish Docker Images / Build binary (arm64, linux/arm64, true) (release) Has been cancelled
Publish Docker Images / Build binary (loong64, linux/loong64, true) (release) Has been cancelled
Publish Docker Images / Build binary (mipsle, linux/mipsle, softfloat, true) (release) Has been cancelled
Publish Docker Images / Build binary (ppc64le, linux/ppc64le) (release) Has been cancelled
Publish Docker Images / Build binary (riscv64, linux/riscv64, true) (release) Has been cancelled
Publish Docker Images / Build binary (s390x, linux/s390x) (release) Has been cancelled
Publish Docker Images / Build Docker image (ghcr.io/loong64/alpine:edge, linux/loong64) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/386) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/amd64) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/arm/v6) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/arm/v7) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/arm64) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/ppc64le) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/riscv64) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/s390x) (release) Has been cancelled
Publish Docker Images / merge (release) Has been cancelled
Some checks failed
Build Linux Packages / Calculate version (release) Has been cancelled
Build Linux Packages / Build binary (386, i386, true, linux, i386) (release) Has been cancelled
Build Linux Packages / Build binary (amd64, amd64, true, linux, x86_64, x86_64) (release) Has been cancelled
Build Linux Packages / Build binary (arm, armel, 6, linux, armv6hl) (release) Has been cancelled
Build Linux Packages / Build binary (arm, armhf, 7, true, linux, armv7hl, armv7hl) (release) Has been cancelled
Build Linux Packages / Build binary (arm64, arm64, true, linux, aarch64, aarch64) (release) Has been cancelled
Build Linux Packages / Build binary (loong64, loongarch64, true, linux, loongarch64) (release) Has been cancelled
Build Linux Packages / Build binary (mips64le, mips64el, linux, mips64el) (release) Has been cancelled
Build Linux Packages / Build binary (mipsle, mipsel, softfloat, true, linux, mipsel) (release) Has been cancelled
Build Linux Packages / Build binary (ppc64le, ppc64el, linux, ppc64le) (release) Has been cancelled
Build Linux Packages / Build binary (riscv64, riscv64, true, linux, riscv64) (release) Has been cancelled
Build Linux Packages / Build binary (s390x, s390x, linux, s390x) (release) Has been cancelled
Build Linux Packages / Upload builds (release) Has been cancelled
Publish Docker Images / Build binary (386, linux/386, true) (release) Has been cancelled
Publish Docker Images / Build binary (amd64, linux/amd64, true) (release) Has been cancelled
Publish Docker Images / Build binary (arm, linux/arm/v6, 6) (release) Has been cancelled
Publish Docker Images / Build binary (arm, linux/arm/v7, 7, true) (release) Has been cancelled
Publish Docker Images / Build binary (arm64, linux/arm64, true) (release) Has been cancelled
Publish Docker Images / Build binary (loong64, linux/loong64, true) (release) Has been cancelled
Publish Docker Images / Build binary (mipsle, linux/mipsle, softfloat, true) (release) Has been cancelled
Publish Docker Images / Build binary (ppc64le, linux/ppc64le) (release) Has been cancelled
Publish Docker Images / Build binary (riscv64, linux/riscv64, true) (release) Has been cancelled
Publish Docker Images / Build binary (s390x, linux/s390x) (release) Has been cancelled
Publish Docker Images / Build Docker image (ghcr.io/loong64/alpine:edge, linux/loong64) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/386) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/amd64) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/arm/v6) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/arm/v7) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/arm64) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/ppc64le) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/riscv64) (release) Has been cancelled
Publish Docker Images / Build Docker image (linux/s390x) (release) Has been cancelled
Publish Docker Images / merge (release) Has been cancelled
This commit is contained in:
487
install.sh
487
install.sh
@@ -22,8 +22,14 @@ 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}"
|
SCRIPT_VERSION="${SCRIPT_VERSION:-v1.3.0}"
|
||||||
declare -a V2BX_IMPORTED_NODE_IDS=()
|
declare -a V2BX_IMPORTED_NODE_IDS=()
|
||||||
|
declare -a EXISTING_IMPORTED_NODE_IDS=()
|
||||||
|
declare -a NODE_IDS=()
|
||||||
|
EXISTING_INSTALL=0
|
||||||
|
EXISTING_CONFIG_SOURCE=""
|
||||||
|
PROMPT_FOR_CONFIG=1
|
||||||
|
CONFIG_BACKUP_DIR=""
|
||||||
|
|
||||||
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 "${GREEN}Script version: ${SCRIPT_VERSION}${NC}"
|
||||||
@@ -136,6 +142,279 @@ detect_v2bx() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detect_existing_installation() {
|
||||||
|
if [[ -x "$BINARY_PATH" || -f "$SERVICE_FILE" || -f "$CONFIG_BASE_FILE" || -f "$CONFIG_ROUTE_FILE" || -f "$CONFIG_OUTBOUNDS_FILE" || -f "$CONFIG_DIR/config.json" ]]; then
|
||||||
|
EXISTING_INSTALL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$CONFIG_BASE_FILE" ]]; then
|
||||||
|
EXISTING_CONFIG_SOURCE="$CONFIG_BASE_FILE"
|
||||||
|
elif [[ -f "$CONFIG_DIR/config.json" ]]; then
|
||||||
|
EXISTING_CONFIG_SOURCE="$CONFIG_DIR/config.json"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$EXISTING_INSTALL" -eq 1 ]]; then
|
||||||
|
echo -e "${YELLOW}Detected existing sing-box installation. Switching to update flow...${NC}"
|
||||||
|
if [[ -n "$EXISTING_CONFIG_SOURCE" ]]; then
|
||||||
|
echo -e "${YELLOW}Existing config source: ${EXISTING_CONFIG_SOURCE}${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
load_existing_install_defaults() {
|
||||||
|
if [[ -z "$EXISTING_CONFIG_SOURCE" || ! -f "$EXISTING_CONFIG_SOURCE" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Loading defaults from existing sing-box config...${NC}"
|
||||||
|
EXISTING_IMPORTED_NODE_IDS=()
|
||||||
|
|
||||||
|
local parsed=""
|
||||||
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
parsed="$(python3 - "$EXISTING_CONFIG_SOURCE" <<'PY'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
path = sys.argv[1]
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
xboard = {}
|
||||||
|
for service in data.get("services") or []:
|
||||||
|
if isinstance(service, dict) and service.get("type") == "xboard":
|
||||||
|
xboard = service
|
||||||
|
break
|
||||||
|
|
||||||
|
print(f"PANEL_URL={xboard.get('panel_url', '')}")
|
||||||
|
print(f"PANEL_TOKEN={xboard.get('key', '')}")
|
||||||
|
|
||||||
|
node_id = xboard.get("node_id")
|
||||||
|
if node_id not in (None, ""):
|
||||||
|
print(f"NODE_ID={node_id}")
|
||||||
|
for node in xboard.get("nodes") or []:
|
||||||
|
if isinstance(node, dict):
|
||||||
|
current_node_id = node.get("node_id")
|
||||||
|
if current_node_id not in (None, ""):
|
||||||
|
print(f"NODE_ID={current_node_id}")
|
||||||
|
|
||||||
|
servers = ((data.get("dns") or {}).get("servers") or [])
|
||||||
|
dns_server = servers[0] if servers else {}
|
||||||
|
if isinstance(dns_server, dict):
|
||||||
|
print(f"DNS_MODE={dns_server.get('type', '')}")
|
||||||
|
print(f"DNS_SERVER={dns_server.get('server', '')}")
|
||||||
|
print(f"DNS_SERVER_PORT={dns_server.get('server_port', '')}")
|
||||||
|
PY
|
||||||
|
)"
|
||||||
|
elif command -v jq >/dev/null 2>&1; then
|
||||||
|
parsed="$(jq -r '
|
||||||
|
([.services[]? | select(.type == "xboard")] | .[0] // {}) as $xboard |
|
||||||
|
"PANEL_URL=" + ($xboard.panel_url // ""),
|
||||||
|
"PANEL_TOKEN=" + ($xboard.key // ""),
|
||||||
|
(if ($xboard.node_id // empty) != empty then "NODE_ID=" + ($xboard.node_id | tostring) else empty end),
|
||||||
|
(($xboard.nodes // [])[]? | "NODE_ID=" + (.node_id | tostring)),
|
||||||
|
(.dns.servers[0] // {}) as $dns |
|
||||||
|
"DNS_MODE=" + ($dns.type // ""),
|
||||||
|
"DNS_SERVER=" + ($dns.server // ""),
|
||||||
|
"DNS_SERVER_PORT=" + (($dns.server_port // "") | tostring)
|
||||||
|
' "$EXISTING_CONFIG_SOURCE" 2>/dev/null || true)"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Neither python3 nor jq found, unable to auto-load existing config.${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$parsed" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local parsed_line
|
||||||
|
while IFS= read -r parsed_line; do
|
||||||
|
parsed_line="$(sanitize_value "$parsed_line")"
|
||||||
|
case "$parsed_line" in
|
||||||
|
PANEL_URL=*)
|
||||||
|
PANEL_URL="$(sanitize_value "${parsed_line#PANEL_URL=}")"
|
||||||
|
;;
|
||||||
|
PANEL_TOKEN=*)
|
||||||
|
PANEL_TOKEN="$(sanitize_value "${parsed_line#PANEL_TOKEN=}")"
|
||||||
|
;;
|
||||||
|
NODE_ID=*)
|
||||||
|
append_unique_node_id_from_existing "${parsed_line#NODE_ID=}"
|
||||||
|
;;
|
||||||
|
DNS_MODE=*)
|
||||||
|
DNS_MODE="$(echo "${parsed_line#DNS_MODE=}" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
;;
|
||||||
|
DNS_SERVER=*)
|
||||||
|
DNS_SERVER="$(sanitize_value "${parsed_line#DNS_SERVER=}")"
|
||||||
|
;;
|
||||||
|
DNS_SERVER_PORT=*)
|
||||||
|
DNS_SERVER_PORT="$(sanitize_value "${parsed_line#DNS_SERVER_PORT=}")"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done <<< "$parsed"
|
||||||
|
|
||||||
|
if [[ "${#EXISTING_IMPORTED_NODE_IDS[@]}" -gt 0 ]]; then
|
||||||
|
NODE_IDS=("${EXISTING_IMPORTED_NODE_IDS[@]}")
|
||||||
|
NODE_ID="${EXISTING_IMPORTED_NODE_IDS[0]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${DNS_MODE:-}" ]]; then
|
||||||
|
DNS_MODE="$(sanitize_value "$DNS_MODE")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${PANEL_URL:-}" && -n "${PANEL_TOKEN:-}" && "${#NODE_IDS[@]}" -gt 0 && ( "${DNS_MODE:-}" == "local" || ( "${DNS_MODE:-}" == "udp" && -n "${DNS_SERVER:-}" && -n "${DNS_SERVER_PORT:-}" ) ) ]]; then
|
||||||
|
echo -e "${YELLOW}Loaded existing config: PanelURL=${PANEL_URL}, NodeIDs=$(IFS=,; echo "${NODE_IDS[*]}"), DNS=${DNS_MODE}${NC}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Existing config detected, but some fields are incomplete. The installer will ask for the missing values.${NC}"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
append_unique_node_id_from_existing() {
|
||||||
|
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 "${EXISTING_IMPORTED_NODE_IDS[@]}"; do
|
||||||
|
if [[ "$existing_node_id" == "$node_id_part" ]]; then
|
||||||
|
node_id_part=""
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -n "$node_id_part" ]]; then
|
||||||
|
EXISTING_IMPORTED_NODE_IDS+=("$node_id_part")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_config_backup_dir() {
|
||||||
|
if [[ -n "$CONFIG_BACKUP_DIR" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
CONFIG_BACKUP_DIR="$CONFIG_DIR/backup/$(date +%Y%m%d-%H%M%S)"
|
||||||
|
mkdir -p "$CONFIG_BACKUP_DIR"
|
||||||
|
echo -e "${YELLOW}Backing up existing configuration to ${CONFIG_BACKUP_DIR}${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_path_if_exists() {
|
||||||
|
local path="$1"
|
||||||
|
if [[ ! -e "$path" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
ensure_config_backup_dir
|
||||||
|
cp -a "$path" "$CONFIG_BACKUP_DIR/"
|
||||||
|
}
|
||||||
|
|
||||||
|
archive_legacy_config_json() {
|
||||||
|
local legacy_config_path="$CONFIG_DIR/config.json"
|
||||||
|
if [[ ! -f "$legacy_config_path" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
ensure_config_backup_dir
|
||||||
|
mv "$legacy_config_path" "$CONFIG_BACKUP_DIR/config.json.migrated"
|
||||||
|
echo -e "${YELLOW}Legacy single-file config has been archived to ${CONFIG_BACKUP_DIR}/config.json.migrated${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_legacy_config_sections() {
|
||||||
|
local source_path="$1"
|
||||||
|
local route_written=0
|
||||||
|
local outbound_written=0
|
||||||
|
|
||||||
|
if [[ ! -f "$source_path" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
local parsed
|
||||||
|
parsed="$(python3 - "$source_path" "$CONFIG_ROUTE_FILE" "$CONFIG_OUTBOUNDS_FILE" <<'PY'
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
source_path, route_path, outbounds_path = sys.argv[1:4]
|
||||||
|
with open(source_path, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
route_written = 0
|
||||||
|
if isinstance(data.get("route"), dict):
|
||||||
|
with open(route_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump({"route": data["route"]}, f, ensure_ascii=False, indent=2)
|
||||||
|
f.write("\n")
|
||||||
|
route_written = 1
|
||||||
|
|
||||||
|
outbound_written = 0
|
||||||
|
if isinstance(data.get("outbounds"), list):
|
||||||
|
with open(outbounds_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump({"outbounds": data["outbounds"]}, f, ensure_ascii=False, indent=2)
|
||||||
|
f.write("\n")
|
||||||
|
outbound_written = 1
|
||||||
|
|
||||||
|
print(f"ROUTE_WRITTEN={route_written}")
|
||||||
|
print(f"OUTBOUNDS_WRITTEN={outbound_written}")
|
||||||
|
PY
|
||||||
|
)"
|
||||||
|
while IFS= read -r parsed_line; do
|
||||||
|
case "$parsed_line" in
|
||||||
|
ROUTE_WRITTEN=1) route_written=1 ;;
|
||||||
|
OUTBOUNDS_WRITTEN=1) outbound_written=1 ;;
|
||||||
|
esac
|
||||||
|
done <<< "$parsed"
|
||||||
|
elif command -v jq >/dev/null 2>&1; then
|
||||||
|
if jq -e '.route | type == "object"' "$source_path" >/dev/null 2>&1; then
|
||||||
|
jq '{route: .route}' "$source_path" > "$CONFIG_ROUTE_FILE"
|
||||||
|
route_written=1
|
||||||
|
fi
|
||||||
|
if jq -e '.outbounds | type == "array"' "$source_path" >/dev/null 2>&1; then
|
||||||
|
jq '{outbounds: .outbounds}' "$source_path" > "$CONFIG_OUTBOUNDS_FILE"
|
||||||
|
outbound_written=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$route_written" -eq 1 || "$outbound_written" -eq 1 ]]; then
|
||||||
|
echo -e "${YELLOW}Extracted legacy config sections from ${source_path}${NC}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
write_default_route_config() {
|
||||||
|
cat > "$CONFIG_ROUTE_FILE" <<EOF
|
||||||
|
{
|
||||||
|
"route": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"protocol": "dns",
|
||||||
|
"action": "hijack-dns"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"auto_detect_interface": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
write_default_outbound_config() {
|
||||||
|
cat > "$CONFIG_OUTBOUNDS_FILE" <<EOF
|
||||||
|
{
|
||||||
|
"outbounds": [
|
||||||
|
{
|
||||||
|
"type": "direct",
|
||||||
|
"tag": "direct"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
load_v2bx_defaults() {
|
load_v2bx_defaults() {
|
||||||
if [[ -z "$V2BX_CONFIG_PATH" ]] && ! find_v2bx_config; then
|
if [[ -z "$V2BX_CONFIG_PATH" ]] && ! find_v2bx_config; then
|
||||||
return 1
|
return 1
|
||||||
@@ -319,6 +598,7 @@ cleanup_legacy_service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
detect_v2bx
|
detect_v2bx
|
||||||
|
detect_existing_installation
|
||||||
stop_v2bx_if_present
|
stop_v2bx_if_present
|
||||||
|
|
||||||
mkdir -p "$CONFIG_DIR"
|
mkdir -p "$CONFIG_DIR"
|
||||||
@@ -335,80 +615,107 @@ load_v2bx_defaults || true
|
|||||||
PANEL_URL="$(sanitize_value "${PANEL_URL:-}")"
|
PANEL_URL="$(sanitize_value "${PANEL_URL:-}")"
|
||||||
PANEL_TOKEN="$(sanitize_value "${PANEL_TOKEN:-}")"
|
PANEL_TOKEN="$(sanitize_value "${PANEL_TOKEN:-}")"
|
||||||
NODE_ID="$(sanitize_value "${NODE_ID:-}")"
|
NODE_ID="$(sanitize_value "${NODE_ID:-}")"
|
||||||
|
DNS_MODE="$(sanitize_value "${DNS_MODE:-}")"
|
||||||
|
DNS_SERVER="$(sanitize_value "${DNS_SERVER:-}")"
|
||||||
|
DNS_SERVER_PORT="$(sanitize_value "${DNS_SERVER_PORT:-}")"
|
||||||
ENABLE_PROXY_PROTOCOL_HINT="$(sanitize_value "${ENABLE_PROXY_PROTOCOL_HINT:-n}")"
|
ENABLE_PROXY_PROTOCOL_HINT="$(sanitize_value "${ENABLE_PROXY_PROTOCOL_HINT:-n}")"
|
||||||
|
|
||||||
|
if [[ "$EXISTING_INSTALL" -eq 1 ]]; then
|
||||||
|
if load_existing_install_defaults; then
|
||||||
|
PROMPT_FOR_CONFIG=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
download_binary
|
download_binary
|
||||||
cleanup_legacy_service
|
cleanup_legacy_service
|
||||||
|
|
||||||
read -u 3 -p "Enter Panel URL [${PANEL_URL}]: " INPUT_URL
|
if [[ "$PROMPT_FOR_CONFIG" -eq 1 ]]; then
|
||||||
PANEL_URL="$(sanitize_value "${INPUT_URL:-$PANEL_URL}")"
|
read -u 3 -p "Enter Panel URL [${PANEL_URL}]: " INPUT_URL
|
||||||
|
PANEL_URL="$(sanitize_value "${INPUT_URL:-$PANEL_URL}")"
|
||||||
|
|
||||||
read -u 3 -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="$(sanitize_value "${INPUT_TOKEN:-$PANEL_TOKEN}")"
|
PANEL_TOKEN="$(sanitize_value "${INPUT_TOKEN:-$PANEL_TOKEN}")"
|
||||||
|
|
||||||
read -u 3 -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="$(sanitize_value "${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
|
NODE_IDS=()
|
||||||
|
i=1
|
||||||
i=1
|
while true; do
|
||||||
while true; do
|
DEFAULT_NODE_ID=""
|
||||||
DEFAULT_NODE_ID=""
|
if [[ "$i" -le "${#EXISTING_IMPORTED_NODE_IDS[@]}" ]]; then
|
||||||
if [[ "$i" -le "${#V2BX_IMPORTED_NODE_IDS[@]}" ]]; then
|
DEFAULT_NODE_ID="${EXISTING_IMPORTED_NODE_IDS[$((i-1))]}"
|
||||||
DEFAULT_NODE_ID="${V2BX_IMPORTED_NODE_IDS[$((i-1))]}"
|
elif [[ "$i" -le "${#V2BX_IMPORTED_NODE_IDS[@]}" ]]; then
|
||||||
elif [[ "$i" -eq 1 && -n "${NODE_ID:-}" ]]; then
|
DEFAULT_NODE_ID="${V2BX_IMPORTED_NODE_IDS[$((i-1))]}"
|
||||||
DEFAULT_NODE_ID="$NODE_ID"
|
elif [[ "$i" -eq 1 && -n "${NODE_ID:-}" ]]; then
|
||||||
fi
|
DEFAULT_NODE_ID="$NODE_ID"
|
||||||
if [[ -n "$DEFAULT_NODE_ID" ]]; then
|
fi
|
||||||
read -u 3 -p "Enter Node ID for node #$i [${DEFAULT_NODE_ID}] (type N/NO to finish): " INPUT_ID
|
if [[ -n "$DEFAULT_NODE_ID" ]]; then
|
||||||
else
|
read -u 3 -p "Enter Node ID for node #$i [${DEFAULT_NODE_ID}] (type N/NO to finish): " INPUT_ID
|
||||||
read -u 3 -p "Enter Node ID for node #$i (press Enter or type N/NO to finish): " INPUT_ID
|
else
|
||||||
fi
|
read -u 3 -p "Enter Node ID for node #$i (press Enter or type N/NO to finish): " INPUT_ID
|
||||||
CURRENT_NODE_ID="$(sanitize_value "${INPUT_ID:-$DEFAULT_NODE_ID}")"
|
fi
|
||||||
if [[ -z "$DEFAULT_NODE_ID" && -z "$CURRENT_NODE_ID" && "${#NODE_IDS[@]}" -gt 0 ]]; then
|
CURRENT_NODE_ID="$(sanitize_value "${INPUT_ID:-$DEFAULT_NODE_ID}")"
|
||||||
break
|
if [[ -z "$DEFAULT_NODE_ID" && -z "$CURRENT_NODE_ID" && "${#NODE_IDS[@]}" -gt 0 ]]; then
|
||||||
fi
|
break
|
||||||
if [[ "$CURRENT_NODE_ID" =~ ^([nN]|[nN][oO])$ ]]; then
|
fi
|
||||||
if [[ "${#NODE_IDS[@]}" -eq 0 ]]; then
|
if [[ "$CURRENT_NODE_ID" =~ ^([nN]|[nN][oO])$ ]]; then
|
||||||
echo -e "${RED}At least one Node ID is required${NC}"
|
if [[ "${#NODE_IDS[@]}" -eq 0 ]]; then
|
||||||
|
echo -e "${RED}At least one Node ID is required${NC}"
|
||||||
|
exit 1
|
||||||
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
break
|
read -r -a CURRENT_NODE_ID_PARTS <<< "$CURRENT_NODE_ID"
|
||||||
fi
|
if [[ "${#CURRENT_NODE_ID_PARTS[@]}" -eq 0 ]]; then
|
||||||
CURRENT_NODE_ID="$(normalize_node_id_input "$CURRENT_NODE_ID")"
|
echo -e "${RED}Node ID is required for node #$i${NC}"
|
||||||
if [[ -z "$CURRENT_NODE_ID" ]]; then
|
|
||||||
echo -e "${RED}Node ID is required for node #$i${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
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
|
|
||||||
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
NODE_IDS+=("$CURRENT_NODE_ID_PART")
|
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
|
done
|
||||||
((i++))
|
|
||||||
done
|
DNS_MODE_DEFAULT=${DNS_MODE:-udp}
|
||||||
|
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 -u 3 -p "Enter DNS server [${DNS_SERVER_DEFAULT}]: " INPUT_DNS_SERVER
|
||||||
|
DNS_SERVER="$(sanitize_value "${INPUT_DNS_SERVER:-$DNS_SERVER_DEFAULT}")"
|
||||||
|
read -u 3 -p "Enter DNS server port [${DNS_SERVER_PORT_DEFAULT}]: " INPUT_DNS_SERVER_PORT
|
||||||
|
DNS_SERVER_PORT="$(sanitize_value "${INPUT_DNS_SERVER_PORT:-$DNS_SERVER_PORT_DEFAULT}")"
|
||||||
|
;;
|
||||||
|
local)
|
||||||
|
DNS_SERVER=""
|
||||||
|
DNS_SERVER_PORT=""
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${RED}Unsupported DNS mode: $DNS_MODE. Supported values: udp, local${NC}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Reusing existing install settings for update.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
NODE_COUNT=${#NODE_IDS[@]}
|
NODE_COUNT=${#NODE_IDS[@]}
|
||||||
|
|
||||||
DNS_MODE_DEFAULT=${DNS_MODE:-udp}
|
case "${DNS_MODE:-udp}" in
|
||||||
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)
|
udp)
|
||||||
DNS_SERVER_DEFAULT=${DNS_SERVER:-1.1.1.1}
|
|
||||||
DNS_SERVER_PORT_DEFAULT=${DNS_SERVER_PORT:-53}
|
|
||||||
read -u 3 -p "Enter DNS server [${DNS_SERVER_DEFAULT}]: " INPUT_DNS_SERVER
|
|
||||||
DNS_SERVER=${INPUT_DNS_SERVER:-$DNS_SERVER_DEFAULT}
|
|
||||||
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
|
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}"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -488,6 +795,12 @@ fi
|
|||||||
SERVICE_JSON+=$'\n }'
|
SERVICE_JSON+=$'\n }'
|
||||||
|
|
||||||
echo -e "${YELLOW}Generating configuration...${NC}"
|
echo -e "${YELLOW}Generating configuration...${NC}"
|
||||||
|
backup_path_if_exists "$CONFIG_BASE_FILE"
|
||||||
|
backup_path_if_exists "$CONFIG_ROUTE_FILE"
|
||||||
|
backup_path_if_exists "$CONFIG_OUTBOUNDS_FILE"
|
||||||
|
backup_path_if_exists "$CONFIG_DIR/config.json"
|
||||||
|
backup_path_if_exists "$SERVICE_FILE"
|
||||||
|
|
||||||
cat > "$CONFIG_BASE_FILE" <<EOF
|
cat > "$CONFIG_BASE_FILE" <<EOF
|
||||||
{
|
{
|
||||||
"log": {
|
"log": {
|
||||||
@@ -512,34 +825,35 @@ ${SERVICE_JSON}
|
|||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat > "$CONFIG_ROUTE_FILE" <<EOF
|
if [[ -f "$CONFIG_DIR/config.json" ]]; then
|
||||||
{
|
rm -f "$CONFIG_ROUTE_FILE" "$CONFIG_OUTBOUNDS_FILE"
|
||||||
"route": {
|
if ! extract_legacy_config_sections "$CONFIG_DIR/config.json"; then
|
||||||
"rules": [
|
echo -e "${YELLOW}Legacy config detected but route/outbounds could not be extracted automatically. Writing default route/outbound files.${NC}"
|
||||||
{
|
fi
|
||||||
"protocol": "dns",
|
if [[ ! -f "$CONFIG_ROUTE_FILE" ]]; then
|
||||||
"action": "hijack-dns"
|
write_default_route_config
|
||||||
}
|
fi
|
||||||
],
|
if [[ ! -f "$CONFIG_OUTBOUNDS_FILE" ]]; then
|
||||||
"auto_detect_interface": true
|
write_default_outbound_config
|
||||||
}
|
fi
|
||||||
}
|
archive_legacy_config_json
|
||||||
EOF
|
else
|
||||||
|
if [[ -f "$CONFIG_ROUTE_FILE" ]]; then
|
||||||
|
echo -e "${YELLOW}Preserving existing route config: ${CONFIG_ROUTE_FILE}${NC}"
|
||||||
|
else
|
||||||
|
write_default_route_config
|
||||||
|
fi
|
||||||
|
|
||||||
cat > "$CONFIG_OUTBOUNDS_FILE" <<EOF
|
if [[ -f "$CONFIG_OUTBOUNDS_FILE" ]]; then
|
||||||
{
|
echo -e "${YELLOW}Preserving existing outbound config: ${CONFIG_OUTBOUNDS_FILE}${NC}"
|
||||||
"outbounds": [
|
else
|
||||||
{
|
write_default_outbound_config
|
||||||
"type": "direct",
|
fi
|
||||||
"tag": "direct"
|
fi
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo -e "${GREEN}Base configuration written to $CONFIG_BASE_FILE${NC}"
|
echo -e "${GREEN}Base configuration written to $CONFIG_BASE_FILE${NC}"
|
||||||
echo -e "${GREEN}Route configuration written to $CONFIG_ROUTE_FILE${NC}"
|
echo -e "${GREEN}Route configuration ready at $CONFIG_ROUTE_FILE${NC}"
|
||||||
echo -e "${GREEN}Outbound configuration written to $CONFIG_OUTBOUNDS_FILE${NC}"
|
echo -e "${GREEN}Outbound configuration ready at $CONFIG_OUTBOUNDS_FILE${NC}"
|
||||||
echo -e "${YELLOW}Edit $CONFIG_OUTBOUNDS_FILE when adding custom sing-box outbounds.${NC}"
|
echo -e "${YELLOW}Edit $CONFIG_OUTBOUNDS_FILE when adding custom sing-box outbounds.${NC}"
|
||||||
|
|
||||||
if [[ "$ENABLE_PROXY_PROTOCOL_HINT" =~ ^([yY][eE][sS]|[yY]|1|true|TRUE)$ ]]; then
|
if [[ "$ENABLE_PROXY_PROTOCOL_HINT" =~ ^([yY][eE][sS]|[yY]|1|true|TRUE)$ ]]; then
|
||||||
@@ -577,7 +891,12 @@ systemctl restart "$SERVICE_NAME"
|
|||||||
|
|
||||||
prompt_uninstall_v2bx
|
prompt_uninstall_v2bx
|
||||||
|
|
||||||
echo -e "${GREEN}Service installed and started successfully.${NC}"
|
if [[ "$EXISTING_INSTALL" -eq 1 ]]; then
|
||||||
|
echo -e "${GREEN}Service updated and restarted successfully.${NC}"
|
||||||
|
echo -e "${GREEN}Configuration has been normalized to the split config.d layout.${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}Service installed and started successfully.${NC}"
|
||||||
|
fi
|
||||||
echo -e "${GREEN}Check status with: systemctl status ${SERVICE_NAME}${NC}"
|
echo -e "${GREEN}Check status with: systemctl status ${SERVICE_NAME}${NC}"
|
||||||
echo -e "${GREEN}View logs with: journalctl -u ${SERVICE_NAME} -f${NC}"
|
echo -e "${GREEN}View logs with: journalctl -u ${SERVICE_NAME} -f${NC}"
|
||||||
echo -e "${GREEN}Panel config endpoint must control PROXY protocol via accept_proxy_protocol when needed.${NC}"
|
echo -e "${GREEN}Panel config endpoint must control PROXY protocol via accept_proxy_protocol when needed.${NC}"
|
||||||
|
|||||||
Reference in New Issue
Block a user