#!/bin/bash # 完全兼容bash 3.x,支持多语言/安装检测/卸载/简化配置 set -eu # ======================== 全局配置 ======================== # 版本信息 VNT_VERSION="v1.2.16" VNTS_VERSION="v1.2.12" DEFAULT_INSTALL_DIR="/usr/local/vnt" # 语言配置(key: 语言代码, value: 显示名称) SUPPORT_LANGUAGES=("zh_CN" "en_US") LANGUAGE_NAMES=("简体中文" "English") CURRENT_LANG="zh_CN" # ======================== 多语言文本 ======================== # 简体中文 declare -A MSG_ZH=( ["TITLE"]="安装VNT (版本: %s) - by GuiJi" ["WEBSITE"]="VNT官网:https://rustvnt.com/" ["GITHUB"]="GitHub项目:https://github.com/vnt-dev/vnt" ["LANG_SELECT"]="请选择语言 / Please select language:" ["MAIN_MENU"]="请输入数字编号选择操作:" ["MENU_INSTALL_CLIENT"]="1、安装VNT Client 客户端" ["MENU_INSTALL_SERVER"]="2、安装VNT Server 服务端" ["MENU_UNINSTALL"]="3、卸载VNT" ["INPUT_CHOICE"]="请输入选择 [%s]: " ["INVALID_CHOICE"]="无效的选择,请输入%s" ["ALREADY_INSTALLED"]="检测到VNT已安装,请先卸载后再安装!" ["UNINSTALL_MENU"]="请选择要卸载的组件:" ["UNINSTALL_CLIENT"]="1、卸载VNT Client 客户端" ["UNINSTALL_SERVER"]="2、卸载VNT Server 服务端" ["UNINSTALL_BACK"]="3、返回主菜单" ["UNINSTALL_SUCCESS"]="卸载完成!" ["CHECK_BASH_VERSION"]="检测到bash版本为3.x,部分高级特性可能不兼容,建议升级bash" ["INFO"]="【信息】%s" ["WARN"]="【警告】%s" ["ERROR"]="【错误】%s" ["INSTALL_DIR_PROMPT"]="请输入安装目录(默认%s):" ["INSTALL_DIR_CONFIRM"]="安装目录确定为: %s" ["DIR_INVALID"]="目录不存在请重新输入" ["INSTALL_WGET"]="未检测到wget,正在自动安装..." ["WGET_INSTALL_FAILED"]="安装wget失败" ["UNSUPPORTED_SYSTEM"]="不支持的系统,请手动安装wget后重试" ["ARCH_DETECT"]="检测到系统架构: %s" ["UNSUPPORTED_ARCH"]="不支持的架构: %s" ["DOWNLOADING"]="正在下载: %s" ["DOWNLOAD_COMPLETE"]="下载完成: %s" ["FILE_SIZE_ERROR"]="下载的文件大小异常" ["DOWNLOAD_FAILED"]="下载失败: %s" ["NETWORK_CHECK"]="请检查网络配置后再次尝试安装" ["CUSTOM_SOURCE_FAILED"]="自定义源下载失败,尝试从GitHub下载..." ["EXTRACTING"]="解压安装包到 %s..." ["CONFIG_CLIENT"]="开始配置VNT Client..." ["SERVER_ADDRESS_PROMPT"]="请输入中继服务器地址(IP:端口号),默认vnt.wherewego.top:29872:" ["ADDRESS_FORMAT_ERROR"]="地址格式不正确,请重新输入(格式:IP:端口 或 域名:端口)" ["TOKEN_PROMPT"]="请输入加入局域网的名称:" ["TOKEN_EMPTY"]="局域网名称不能为空,请重新输入" ["PASSWORD_PROMPT"]="请输入局域网配对密码:" ["PASSWORD_EMPTY"]="配对密码不能为空,请重新输入" ["IP_PROMPT"]="请输入本机在局域网内的IP地址(推荐直接回车自动分配):" ["DEVICE_NAME_PROMPT"]="请输入本机在局域网内的设备名称:" ["DEVICE_NAME_EMPTY"]="设备名称不能为空,请重新输入" ["SERVER_ENCRYPT_PROMPT"]="是否开启服务端加密 y/n:" ["ALLOW_WG_PROMPT"]="是否允许接入WireGuard客户端 y/n 默认n:" ["CREATE_CONFIG_FILE"]="生成配置文件: %s" ["CONFIG_SERVER"]="开始配置VNT Server..." ["PORT_PROMPT"]="请输入中继服务端口号(默认29872):" ["PORT_FORMAT_ERROR"]="端口号格式不正确,请输入1-65535之间的数字" ["GATEWAY_PROMPT"]="请输入网关地址(默认10.10.0.1):" ["GATEWAY_FORMAT_ERROR"]="网关地址格式不正确,请重新输入" ["SERVER_PARAMS"]="服务端运行参数: %s" ["FIREWALL_CONFIG"]="配置防火墙开放端口 %s..." ["FIREWALL_NOT_FOUND"]="未检测到防火墙管理工具,请手动开放端口 %s" ["CREATE_SERVICE_FILE"]="创建%s服务文件: %s" ["START_SERVICE"]="启动%s服务..." ["SERVICE_STATUS"]="服务状态:" ["INSTALL_CLIENT_SUCCESS"]="VNT Client 安装完成!" ["CONFIG_CHECK_TIP"]="可到%s/config.yml检查并重新配置更多参数,参数说明:https://github.com/vnt-dev/vnt/blob/main/vnt-cli/README.md" ["INSTALL_SERVER_SUCCESS"]="VNT Server 安装完成!" ["RUN_AS_ROOT"]="请使用root用户运行此脚本" ["INTERACTIVE_TERMINAL"]="请在交互式终端中运行此脚本,或使用以下命令:" ) # 英文 declare -A MSG_EN=( ["TITLE"]="Install VNT (Version: %s) - by GuiJi" ["WEBSITE"]="VNT Official Site:https://rustvnt.com/" ["GITHUB"]="GitHub Project:https://github.com/vnt-dev/vnt" ["LANG_SELECT"]="Please select language / 请选择语言:" ["MAIN_MENU"]="Please enter the number to select an operation:" ["MENU_INSTALL_CLIENT"]="1、Install VNT Client" ["MENU_INSTALL_SERVER"]="2、Install VNT Server" ["MENU_UNINSTALL"]="3、Uninstall VNT" ["INPUT_CHOICE"]="Please enter your choice [%s]: " ["INVALID_CHOICE"]="Invalid choice, please enter %s" ["ALREADY_INSTALLED"]="VNT is already installed, please uninstall first!" ["UNINSTALL_MENU"]="Please select the component to uninstall:" ["UNINSTALL_CLIENT"]="1、Uninstall VNT Client" ["UNINSTALL_SERVER"]="2、Uninstall VNT Server" ["UNINSTALL_BACK"]="3、Back to main menu" ["UNINSTALL_SUCCESS"]="Uninstallation completed!" ["CHECK_BASH_VERSION"]="Detected bash version 3.x, some advanced features may not be compatible, please upgrade bash" ["INFO"]="[INFO] %s" ["WARN"]="[WARN] %s" ["ERROR"]="[ERROR] %s" ["INSTALL_DIR_PROMPT"]="Please enter installation directory (default %s): " ["INSTALL_DIR_CONFIRM"]="Installation directory confirmed: %s" ["DIR_INVALID"]="Directory is invalid, please re-enter" ["INSTALL_WGET"]="wget not detected, installing automatically..." ["WGET_INSTALL_FAILED"]="Failed to install wget" ["UNSUPPORTED_SYSTEM"]="Unsupported system, please install wget manually and try again" ["ARCH_DETECT"]="Detected system architecture: %s" ["UNSUPPORTED_ARCH"]="Unsupported architecture: %s" ["DOWNLOADING"]="Downloading: %s" ["DOWNLOAD_COMPLETE"]="Download completed: %s" ["FILE_SIZE_ERROR"]="Downloaded file size is abnormal" ["DOWNLOAD_FAILED"]="Download failed: %s" ["NETWORK_CHECK"]="Please check network configuration and try again" ["CUSTOM_SOURCE_FAILED"]="Custom source download failed, trying to download from GitHub..." ["EXTRACTING"]="Extracting installation package to %s..." ["CONFIG_CLIENT"]="Starting to configure VNT Client..." ["SERVER_ADDRESS_PROMPT"]="Please enter relay server address (IP:port), default vnt.wherewego.top:29872: " ["ADDRESS_FORMAT_ERROR"]="Invalid address format, please re-enter (format: IP:port or domain:port)" ["TOKEN_PROMPT"]="Please enter LAN name: " ["TOKEN_EMPTY"]="LAN name cannot be empty, please re-enter" ["PASSWORD_PROMPT"]="Please enter LAN pairing password: " ["PASSWORD_EMPTY"]="Pairing password cannot be empty, please re-enter" ["IP_PROMPT"]="Please enter local IP in LAN (press Enter for auto-assign): " ["DEVICE_NAME_PROMPT"]="Please enter device name in LAN: " ["DEVICE_NAME_EMPTY"]="Device name cannot be empty, please re-enter" ["SERVER_ENCRYPT_PROMPT"]="Enable server encryption y/n: " ["ALLOW_WG_PROMPT"]="Allow WireGuard client access y/n (default n): " ["CREATE_CONFIG_FILE"]="Creating config file: %s" ["CONFIG_SERVER"]="Starting to configure VNT Server..." ["PORT_PROMPT"]="Please enter relay service port (default 29872): " ["PORT_FORMAT_ERROR"]="Invalid port format, please enter a number between 1-65535" ["GATEWAY_PROMPT"]="Please enter gateway address (default 10.10.0.1): " ["GATEWAY_FORMAT_ERROR"]="Invalid gateway format, please re-enter" ["SERVER_PARAMS"]="Server running parameters: %s" ["FIREWALL_CONFIG"]="Configuring firewall to open port %s..." ["FIREWALL_NOT_FOUND"]="Firewall management tool not detected, please open port %s manually" ["CREATE_SERVICE_FILE"]="Creating %s service file: %s" ["START_SERVICE"]="Starting %s service..." ["SERVICE_STATUS"]="Service status:" ["INSTALL_CLIENT_SUCCESS"]="VNT Client installed successfully!" ["CONFIG_CHECK_TIP"]="You can check and reconfigure parameters in %s/config.yml, parameter description: https://github.com/vnt-dev/vnt/blob/main/vnt-cli/README.md" ["INSTALL_SERVER_SUCCESS"]="VNT Server installed successfully!" ["RUN_AS_ROOT"]="Please run this script as root user" ["INTERACTIVE_TERMINAL"]="Please run this script in an interactive terminal, or use the following command:" ) # ======================== 核心函数 ======================== # 获取翻译文本 get_msg() { local key=$1 shift local msg="" if [ "$CURRENT_LANG" = "zh_CN" ]; then msg=${MSG_ZH[$key]} else msg=${MSG_EN[$key]} fi # 格式化输出 if [ $# -gt 0 ]; then printf "$msg" "$@" else echo -n "$msg" fi } # 日志输出函数 info() { echo -e "\033[0;32m$(get_msg "INFO" "$1")\033[0m" } warn() { echo -e "\033[1;33m$(get_msg "WARN" "$1")\033[0m" } error() { echo -e "\033[0;31m$(get_msg "ERROR" "$1")\033[0m" } # 选择语言 select_language() { clear echo "$(get_msg "LANG_SELECT")" for i in "${!SUPPORT_LANGUAGES[@]}"; do echo "$((i+1))、${LANGUAGE_NAMES[$i]}" done while true; do read -p "$(get_msg "INPUT_CHOICE" "1-${#SUPPORT_LANGUAGES[@]}")" lang_choice < /dev/tty lang_choice=${lang_choice:-""} if [[ "$lang_choice" =~ ^[1-${#SUPPORT_LANGUAGES[@]}]$ ]]; then CURRENT_LANG=${SUPPORT_LANGUAGES[$((lang_choice-1))]} break else error "$(get_msg "INVALID_CHOICE" "1-${#SUPPORT_LANGUAGES[@]}")" fi done } # 检测是否已安装VNT check_vnt_installed() { local component=$1 if [ "$component" = "client" ]; then if [ -f "/etc/systemd/system/vntc.service" ] || [ -d "$DEFAULT_INSTALL_DIR" ] && [ -f "$DEFAULT_INSTALL_DIR/vnt-cli" ]; then return 0 fi elif [ "$component" = "server" ]; then if [ -f "/etc/systemd/system/vnts.service" ] || [ -d "$DEFAULT_INSTALL_DIR" ] && [ -f "$DEFAULT_INSTALL_DIR/vnts" ]; then return 0 fi else # 检测任意组件 if [ -f "/etc/systemd/system/vntc.service" ] || [ -f "/etc/systemd/system/vnts.service" ] || [ -d "$DEFAULT_INSTALL_DIR" ]; then return 0 fi fi return 1 } # 卸载VNT uninstall_vnt() { local component=$1 if [ "$component" = "client" ]; then # 停止并删除服务 if systemctl is-active --quiet vntc; then systemctl stop vntc fi if [ -f "/etc/systemd/system/vntc.service" ]; then rm -f "/etc/systemd/system/vntc.service" systemctl daemon-reload fi elif [ "$component" = "server" ]; then # 停止并删除服务 if systemctl is-active --quiet vnts; then systemctl stop vnts fi if [ -f "/etc/systemd/system/vnts.service" ]; then rm -f "/etc/systemd/system/vnts.service" systemctl daemon-reload fi fi # 删除安装目录 if [ -d "$DEFAULT_INSTALL_DIR" ]; then rm -rf "$DEFAULT_INSTALL_DIR" fi info "$(get_msg "UNINSTALL_SUCCESS")" } # 卸载菜单 show_uninstall_menu() { clear echo "=============================================" echo "$(get_msg "UNINSTALL_MENU")" echo "$(get_msg "UNINSTALL_CLIENT")" echo "$(get_msg "UNINSTALL_SERVER")" echo "$(get_msg "UNINSTALL_BACK")" echo "=============================================" while true; do read -p "$(get_msg "INPUT_CHOICE" "1-3")" uninstall_choice < /dev/tty uninstall_choice=${uninstall_choice:-""} case $uninstall_choice in 1) uninstall_vnt "client" break ;; 2) uninstall_vnt "server" break ;; 3) # 返回主菜单 return ;; *) error "$(get_msg "INVALID_CHOICE" "1-3")" ;; esac done # 等待用户确认后返回主菜单 read -p "$(get_msg "INPUT_CHOICE" "$(get_msg "PRESS_ENTER_TO_CONTINUE")")" -n1 -s < /dev/tty } # 检测bash版本 check_bash_version() { local bash_ver=$(bash --version | head -1 | awk '{print $4}' | cut -d. -f1) if [ "$bash_ver" -lt 4 ]; then warn "$(get_msg "CHECK_BASH_VERSION")" fi } # 检查并安装wget install_wget() { if ! command -v wget &> /dev/null; then info "$(get_msg "INSTALL_WGET")" if [ -f /etc/redhat-release ]; then # CentOS/RHEL yum -y install wget > /dev/null 2>&1 || { error "$(get_msg "WGET_INSTALL_FAILED")"; exit 1; } elif [ -f /etc/debian_version ]; then # Debian/Ubuntu apt update > /dev/null 2>&1 apt -y install wget > /dev/null 2>&1 || { error "$(get_msg "WGET_INSTALL_FAILED")"; exit 1; } else error "$(get_msg "UNSUPPORTED_SYSTEM")" exit 1 fi fi } # 获取系统架构 get_arch() { local arch=$(uname -m) case $arch in x86_64) echo "x86_64-unknown-linux-musl" ;; aarch64) echo "aarch64-unknown-linux-musl" ;; armv7l) if grep -q "v7l" /proc/cpuinfo && grep -q "hf" /etc/os-release 2>/dev/null; then echo "armv7-unknown-linux-musleabihf" else echo "armv7-unknown-linux-musleabi" fi ;; armv6l) if grep -q "hf" /etc/os-release 2>/dev/null; then echo "arm-unknown-linux-musleabihf" else echo "arm-unknown-linux-musleabi" fi ;; mips) echo "mips-unknown-linux-musl" ;; mipsel) echo "mipsel-unknown-linux-musl" ;; i686) echo "i686-unknown-linux-musl" ;; *) error "$(get_msg "UNSUPPORTED_ARCH" "$arch")" exit 1 ;; esac } # 下载文件函数 download_file() { local url=$1 local output=$2 info "$(get_msg "DOWNLOADING" "$url")" if wget --show-progress -q -O "$output" "$url"; then local filesize=$(stat -c%s "$output" 2>/dev/null || stat -f%z "$output" 2>/dev/null) if [ -z "$filesize" ] || [ "$filesize" -lt 1677722 ]; then error "$(get_msg "FILE_SIZE_ERROR")" rm -f "$output" return 1 fi info "$(get_msg "DOWNLOAD_COMPLETE" "$output")" return 0 else error "$(get_msg "DOWNLOAD_FAILED" "$url")" rm -f "$output" return 1 fi } # 主菜单选择 show_main_menu() { while true; do clear echo "=============================================" echo "$(get_msg "TITLE" "$VNT_VERSION")" echo "$(get_msg "WEBSITE")" echo "$(get_msg "GITHUB")" echo "=============================================" echo "$(get_msg "MAIN_MENU")" echo "$(get_msg "MENU_INSTALL_CLIENT")" echo "$(get_msg "MENU_INSTALL_SERVER")" echo "$(get_msg "MENU_UNINSTALL")" echo "=============================================" read -p "$(get_msg "INPUT_CHOICE" "1-3")" install_type < /dev/tty install_type=${install_type:-""} case $install_type in 1) # 安装客户端 - 先检测 if check_vnt_installed "client"; then error "$(get_msg "ALREADY_INSTALLED")" read -p "$(get_msg "INPUT_CHOICE" "$(get_msg "PRESS_ENTER_TO_CONTINUE")")" -n1 -s < /dev/tty continue fi INSTALL_MODE="client" break ;; 2) # 安装服务端 - 先检测 if check_vnt_installed "server"; then error "$(get_msg "ALREADY_INSTALLED")" read -p "$(get_msg "INPUT_CHOICE" "$(get_msg "PRESS_ENTER_TO_CONTINUE")")" -n1 -s < /dev/tty continue fi INSTALL_MODE="server" break ;; 3) # 卸载 show_uninstall_menu continue ;; *) error "$(get_msg "INVALID_CHOICE" "1-3")" read -p "$(get_msg "INPUT_CHOICE" "$(get_msg "PRESS_ENTER_TO_CONTINUE")")" -n1 -s < /dev/tty ;; esac done } # 获取安装目录 get_install_dir() { while true; do read -p "$(get_msg "INSTALL_DIR_PROMPT" "$DEFAULT_INSTALL_DIR")" install_dir < /dev/tty install_dir=${install_dir:-$DEFAULT_INSTALL_DIR} if [ -d "$install_dir" ] || mkdir -p "$install_dir"; then info "$(get_msg "INSTALL_DIR_CONFIRM" "$install_dir")" break else error "$(get_msg "DIR_INVALID")" fi done } # 客户端配置 config_client() { info "$(get_msg "CONFIG_CLIENT")" # 中继服务器地址 while true; do read -p "$(get_msg "SERVER_ADDRESS_PROMPT")" server_address < /dev/tty server_address=${server_address:-""} if [ -z "$server_address" ]; then server_address="vnt.wherewego.top:29872" break elif echo "$server_address" | grep -qE '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]{1,5}$|^[a-zA-Z0-9.-]+:[0-9]{1,5}$'; then break else error "$(get_msg "ADDRESS_FORMAT_ERROR")" fi done # 局域网名称 while true; do read -p "$(get_msg "TOKEN_PROMPT")" token < /dev/tty token=${token:-""} if [ -n "$token" ]; then break else error "$(get_msg "TOKEN_EMPTY")" fi done # 配对密码 while true; do read -p "$(get_msg "PASSWORD_PROMPT")" password < /dev/tty password=${password:-""} if [ -n "$password" ]; then break else error "$(get_msg "PASSWORD_EMPTY")" fi done # 本机IP read -p "$(get_msg "IP_PROMPT")" ip < /dev/tty ip=${ip:-""} # 设备名称 while true; do read -p "$(get_msg "DEVICE_NAME_PROMPT")" name < /dev/tty name=${name:-""} if [ -n "$name" ]; then break else error "$(get_msg "DEVICE_NAME_EMPTY")" fi done # 服务端加密 while true; do read -p "$(get_msg "SERVER_ENCRYPT_PROMPT")" server_encrypt < /dev/tty server_encrypt=${server_encrypt:-""} case $server_encrypt in y|Y) server_encrypt="true"; break ;; n|N) server_encrypt="false"; break ;; *) error "$(get_msg "INVALID_CHOICE" "y/n")" ;; esac done # WireGuard while true; do read -p "$(get_msg "ALLOW_WG_PROMPT")" allow_wire_guard < /dev/tty allow_wire_guard=${allow_wire_guard:-""} if [ -z "$allow_wire_guard" ]; then allow_wire_guard="false" break fi case $allow_wire_guard in y|Y) allow_wire_guard="true"; break ;; n|N) allow_wire_guard="false"; break ;; *) error "$(get_msg "INVALID_CHOICE" "y/n")" ;; esac done # 生成设备ID if command -v uuidgen &> /dev/null; then device_id=$(uuidgen | tr -d '-' | cut -c1-14) else device_id=$(head -c 14 /dev/urandom | md5sum | cut -c1-14) fi # 生成配置文件 config_file="${install_dir}/config.yml" info "$(get_msg "CREATE_CONFIG_FILE" "$config_file")" cat > "$config_file" << EOF token: $token device_id: $device_id name: $name password: $password server_address: $server_address $( [ -n "$ip" ] && echo "ip: $ip" ) server_encrypt: $server_encrypt finger: false cmd: false disable_stats: false allow_wire_guard: $allow_wire_guard EOF } # 服务端配置(简化版,只保留port和gateway) config_server() { info "$(get_msg "CONFIG_SERVER")" # 端口号 while true; do read -p "$(get_msg "PORT_PROMPT")" port < /dev/tty port=${port:-""} if [ -z "$port" ]; then port="29872" break elif echo "$port" | grep -qE '^[0-9]{1,5}$' && [ "$port" -ge 1 ] && [ "$port" -le 65535 ]; then break else error "$(get_msg "PORT_FORMAT_ERROR")" fi done # 网关地址 while true; do read -p "$(get_msg "GATEWAY_PROMPT")" gateway < /dev/tty gateway=${gateway:-""} if [ -z "$gateway" ]; then gateway="10.10.0.1" break elif echo "$gateway" | grep -qE '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'; then break else error "$(get_msg "GATEWAY_FORMAT_ERROR")" fi done # 固定参数(不再让用户配置) finger="false" log_path="/dev/null" # 保存配置参数 server_params="--port $port --gateway $gateway " info "$(get_msg "SERVER_PARAMS" "$server_params")" # 配置防火墙 info "$(get_msg "FIREWALL_CONFIG" "$port")" if command -v firewall-cmd &> /dev/null; then firewall-cmd --add-port="$port"/tcp --permanent > /dev/null 2>&1 firewall-cmd --add-port="$port"/udp --permanent > /dev/null 2>&1 firewall-cmd --reload > /dev/null 2>&1 elif command -v ufw &> /dev/null; then ufw allow "$port"/tcp > /dev/null 2>&1 ufw allow "$port"/udp > /dev/null 2>&1 ufw reload > /dev/null 2>&1 else warn "$(get_msg "FIREWALL_NOT_FOUND" "$port")" fi } # 创建systemd服务 create_systemd_service() { local service_file="" local service_name="" local service_desc="" if [ "$INSTALL_MODE" = "client" ]; then service_name="vntc" service_desc="VNT Client" service_file="/etc/systemd/system/${service_name}.service" info "$(get_msg "CREATE_SERVICE_FILE" "$service_desc" "$service_file")" cat > "$service_file" << EOF [Unit] Description=$service_desc - P2P VPN After=network.target Wants=network.target [Service] Type=simple WorkingDirectory=${install_dir} ExecStart=${install_dir}/vnt-cli -f ${install_dir}/config.yml Restart=always RestartSec=10 User=root Group=root StandardOutput=journal StandardError=journal LimitNOFILE=65536 [Install] WantedBy=multi-user.target EOF else service_name="vnts" service_desc="VNT Server" service_file="/etc/systemd/system/${service_name}.service" info "$(get_msg "CREATE_SERVICE_FILE" "$service_desc" "$service_file")" cat > "$service_file" << EOF [Unit] Description=$service_desc After=network.target [Service] Type=simple WorkingDirectory=${install_dir} ExecStart=${install_dir}/vnts $server_params Restart=always RestartSec=10 User=root [Install] WantedBy=multi-user.target EOF fi # 启动服务 info "$(get_msg "START_SERVICE" "$service_desc")" systemctl daemon-reload systemctl enable --now "$service_name" > /dev/null 2>&1 # 显示服务状态 info "$(get_msg "SERVICE_STATUS")" systemctl status "$service_name" --no-pager } # 主执行流程 main() { # 选择语言 select_language # 检查bash版本 check_bash_version # 检查root权限 if [ "$(id -u)" -ne 0 ]; then error "$(get_msg "RUN_AS_ROOT")" exit 1 fi # 显示主菜单 show_main_menu # 获取安装目录 get_install_dir # 安装wget install_wget # 获取架构 arch=$(get_arch) info "$(get_msg "ARCH_DETECT" "$arch")" # 下载安装包 local download_url="" local file_name="" local temp_file="/tmp/vnt_install.tar.gz" # 定义优先从自定义源下载的架构列表 local priority_archs=("x86_64-unknown-linux-musl" "aarch64-unknown-linux-musl") local use_custom_source=false # 检查当前架构是否在优先列表中 for priority_arch in "${priority_archs[@]}"; do if [ "$arch" = "$priority_arch" ]; then use_custom_source=true break fi done if [ "$INSTALL_MODE" = "client" ]; then file_name="vnt-${arch}-${VNT_VERSION}.tar.gz" # 优先从自定义源下载(x86_64/aarch64) if [ "$use_custom_source" = true ]; then # 构建自定义源URL(按架构匹配) local custom_url="" if [ "$arch" = "x86_64-unknown-linux-musl" ]; then custom_url="https://f.guiji20.top/res/vnt/vnt-x86_64-unknown-linux-musl-v1.2.17.tar.gz" elif [ "$arch" = "aarch64-unknown-linux-musl" ]; then custom_url="https://f.guiji20.top/res/vnt/vnt-aarch64-unknown-linux-musl-v1.2.16.tar.gz" fi # 尝试自定义源下载,失败则回退到GitHub if ! download_file "$custom_url" "$temp_file"; then info "$(get_msg "CUSTOM_SOURCE_FAILED")" download_url="https://github.com/vnt-dev/vnt/releases/download/${VNT_VERSION}/${file_name}" download_file "$download_url" "$temp_file" || { error "$(get_msg "NETWORK_CHECK")"; exit 1; } fi else # 其他架构直接从GitHub下载 download_url="https://github.com/vnt-dev/vnt/releases/download/${VNT_VERSION}/${file_name}" download_file "$download_url" "$temp_file" || { error "$(get_msg "NETWORK_CHECK")"; exit 1; } fi else file_name="vnts-${arch}-${VNTS_VERSION}.tar.gz" # 优先从自定义源下载(x86_64/aarch64) if [ "$use_custom_source" = true ]; then # 构建自定义源URL(按架构匹配) local custom_url="" if [ "$arch" = "x86_64-unknown-linux-musl" ]; then custom_url="https://f.guiji20.top/res/vnt/vnts-x86_64-unknown-linux-musl-v1.2.12.tar.gz" elif [ "$arch" = "aarch64-unknown-linux-musl" ]; then custom_url="https://f.guiji20.top/res/vnt/vnts-aarch64-unknown-linux-musl-v1.2.12.tar.gz" fi # 尝试自定义源下载,失败则回退到GitHub if ! download_file "$custom_url" "$temp_file"; then info "$(get_msg "CUSTOM_SOURCE_FAILED")" download_url="https://github.com/vnt-dev/vnts/releases/download/${VNTS_VERSION}/${file_name}" download_file "$download_url" "$temp_file" || { error "$(get_msg "NETWORK_CHECK")"; exit 1; } fi else # 其他架构直接从GitHub下载 download_url="https://github.com/vnt-dev/vnts/releases/download/${VNTS_VERSION}/${file_name}" download_file "$download_url" "$temp_file" || { error "$(get_msg "NETWORK_CHECK")"; exit 1; } fi fi # 解压 info "$(get_msg "EXTRACTING" "$install_dir")" tar -zxf "$temp_file" -C "$install_dir" --strip-components=0 chmod +x "${install_dir}"/* rm -f "$temp_file" # 配置服务 if [ "$INSTALL_MODE" = "client" ]; then config_client else config_server fi # 创建服务 create_systemd_service # 完成提示 echo -e "\n\033[0;32m=============================================\033[0m" if [ "$INSTALL_MODE" = "client" ]; then info "$(get_msg "INSTALL_CLIENT_SUCCESS")" echo "$(get_msg "CONFIG_CHECK_TIP" "$install_dir")" else info "$(get_msg "INSTALL_SERVER_SUCCESS")" fi echo -e "\033[0;32m=============================================\033[0m" # 等待用户确认后返回主菜单 read -p "$(get_msg "INPUT_CHOICE" "$(get_msg "PRESS_ENTER_TO_CONTINUE")")" -n1 -s < /dev/tty main # 安装完成后返回主菜单 } # 启动主程序 if [ -t 0 ]; then # 补充缺失的翻译项 MSG_ZH["PRESS_ENTER_TO_CONTINUE"]="按回车键继续..." MSG_EN["PRESS_ENTER_TO_CONTINUE"]="Press Enter to continue..." main else error "$(get_msg "INTERACTIVE_TERMINAL")" echo "bash -c \"\$(curl -sSL https://f.guiji20.top/res/vnt/quick_start.sh | tr -d '\r')\"" exit 1 fi