diff --git a/install.sh b/install.sh index 7521c045..cb79c4a4 100644 --- a/install.sh +++ b/install.sh @@ -18,6 +18,9 @@ SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" LEGACY_SERVICE_NAMES=("ganclient" "sing-box") RELEASE_BASE_URL="${RELEASE_BASE_URL:-https://s3.cloudyun.top/downloads/singbox}" PUBLISHED_SCRIPT_URL="${PUBLISHED_SCRIPT_URL:-https://s3.cloudyun.top/downloads/singbox/install.sh}" +V2BX_DETECTED=0 +V2BX_CONFIG_PATH="" +UNINSTALL_V2BX_DEFAULT="${UNINSTALL_V2BX_DEFAULT:-n}" echo -e "${GREEN}Welcome to singbox Release Installation Script${NC}" echo -e "${YELLOW}Published install script: ${PUBLISHED_SCRIPT_URL}${NC}" @@ -48,9 +51,138 @@ DOWNLOAD_TARGET="${DOWNLOAD_TARGET:-linux-${BINARY_ARCH}}" DOWNLOAD_URL="${DOWNLOAD_URL:-${RELEASE_BASE_URL}/sing-box-${DOWNLOAD_TARGET}}" TMP_BINARY="$(mktemp)" -mkdir -p "$CONFIG_DIR" -mkdir -p "$CONFIG_MERGE_DIR" -mkdir -p "$WORK_DIR" +find_v2bx_config() { + local candidate + for candidate in \ + "/etc/V2bX/config.json" \ + "/usr/local/V2bX/config.json" \ + "/etc/v2bx/config.json" \ + "/usr/local/etc/V2bX/config.json" + do + if [[ -f "$candidate" ]]; then + V2BX_CONFIG_PATH="$candidate" + return 0 + fi + done + return 1 +} + +detect_v2bx() { + if command -v v2bx >/dev/null 2>&1; then + V2BX_DETECTED=1 + fi + if find_v2bx_config; then + V2BX_DETECTED=1 + fi + return 0 +} + +load_v2bx_defaults() { + if [[ -z "$V2BX_CONFIG_PATH" ]] && ! find_v2bx_config; then + return 1 + fi + + echo -e "${YELLOW}Detected V2bX configuration: ${V2BX_CONFIG_PATH}${NC}" + + if command -v python3 >/dev/null 2>&1; then + local parsed + parsed="$(python3 - "$V2BX_CONFIG_PATH" <<'PY' +import json +import sys + +path = sys.argv[1] +with open(path, "r", encoding="utf-8") as f: + data = json.load(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)) +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" + fi + if [[ -z "${PANEL_TOKEN:-}" && -n "$parsed_api_key" ]]; then + PANEL_TOKEN="$parsed_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:-}, NodeID=${parsed_node_id:-}${NC}" + 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)" + 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" + fi + if [[ -z "${PANEL_TOKEN:-}" && -n "$parsed_api_key" ]]; then + PANEL_TOKEN="$parsed_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:-}, NodeID=${parsed_node_id:-}${NC}" + fi + else + echo -e "${YELLOW}Neither python3 nor jq found, skipping automatic V2bX config import.${NC}" + fi + + return 0 +} + +stop_v2bx_if_present() { + if [[ "$V2BX_DETECTED" -ne 1 ]]; then + return 0 + fi + if ! command -v v2bx >/dev/null 2>&1; then + echo -e "${YELLOW}V2bX config detected but 'v2bx' command not found, skipping stop/disable.${NC}" + return 0 + fi + + echo -e "${YELLOW}Detected V2bX, stopping and disabling it before continuing...${NC}" + v2bx stop || true + v2bx disable || true +} + +prompt_uninstall_v2bx() { + if [[ "$V2BX_DETECTED" -ne 1 ]]; then + return 0 + fi + if ! command -v v2bx >/dev/null 2>&1; then + return 0 + fi + + read -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} + + if [[ "$uninstall_v2bx_answer" =~ ^([yY][eE][sS]|[yY]|1|true|TRUE)$ ]]; then + echo -e "${YELLOW}Running: v2bx uninstall${NC}" + v2bx uninstall + else + echo -e "${YELLOW}Keeping existing V2bX installation.${NC}" + fi +} download_binary() { echo -e "${YELLOW}Downloading sing-box release binary...${NC}" @@ -106,14 +238,23 @@ cleanup_legacy_service() { done } -download_binary -cleanup_legacy_service +detect_v2bx +stop_v2bx_if_present + +mkdir -p "$CONFIG_DIR" +mkdir -p "$CONFIG_MERGE_DIR" +mkdir -p "$WORK_DIR" if [[ -f ".env" ]]; then echo -e "${YELLOW}Loading configuration from .env...${NC}" source .env fi +load_v2bx_defaults || true + +download_binary +cleanup_legacy_service + read -p "Enter Panel URL [${PANEL_URL}]: " INPUT_URL PANEL_URL=${INPUT_URL:-$PANEL_URL} @@ -330,6 +471,8 @@ systemctl daemon-reload systemctl enable "$SERVICE_NAME" systemctl restart "$SERVICE_NAME" +prompt_uninstall_v2bx + echo -e "${GREEN}Service installed and started successfully.${NC}" echo -e "${GREEN}Check status with: systemctl status ${SERVICE_NAME}${NC}" echo -e "${GREEN}View logs with: journalctl -u ${SERVICE_NAME} -f${NC}"