1964 lines
57 KiB
Bash
1964 lines
57 KiB
Bash
#!/bin/bash
|
||
# Xray 管理脚本 - 集成安装、卸载和管理功能
|
||
# 专门支持 VLESS+REALITY 协议
|
||
# 使用方法: chmod +x xray-manager.sh && ./xray-manager.sh
|
||
|
||
# 全局变量
|
||
XRAY_PATH="/usr/local/bin"
|
||
CONFIG_PATH="/usr/local/etc/xray"
|
||
REALITY_PORT="" # VLESS+REALITY 端口
|
||
LOG_PATH="/var/log/xray"
|
||
LOG_FILE="/var/log/xray-manager.log"
|
||
SCRIPT_VERSION="1.0.0"
|
||
XRAY_VERSION="v25.3.6" # 当前固定的Xray版本
|
||
UUID=""
|
||
PRIVATE_KEY=""
|
||
PUBLIC_KEY=""
|
||
CONFIG_BACKUP=""
|
||
SERVER_IP=""
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[0;33m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 进度条函数
|
||
show_progress() {
|
||
local pid=$1
|
||
local delay=0.1
|
||
local spinstr='|/-\'
|
||
local temp
|
||
local count=0
|
||
local start_time=$(date +%s)
|
||
echo -n " "
|
||
|
||
while ps -p $pid > /dev/null; do
|
||
temp=${spinstr#?}
|
||
printf "\r[%c] %s 已进行 %ds" "$spinstr" "$2" "$(($(date +%s) - start_time))"
|
||
spinstr=$temp${spinstr%"$temp"}
|
||
sleep $delay
|
||
count=$((count + 1))
|
||
done
|
||
printf "\r\033[K"
|
||
}
|
||
|
||
# 进度条显示函数 - 适用于apt操作
|
||
apt_progress() {
|
||
local cmd="$1"
|
||
local msg="$2"
|
||
local logfile="$LOG_FILE.tmp"
|
||
|
||
echo -e "${CYAN}开始 $msg...${NC}"
|
||
touch "$logfile"
|
||
($cmd 2>&1 | tee -a "$LOG_FILE" > "$logfile") &
|
||
local pid=$!
|
||
|
||
# 显示进度条
|
||
local start_time=$(date +%s)
|
||
local dots=""
|
||
local status=""
|
||
local progress=0
|
||
local last_line=""
|
||
local delay=0.2
|
||
|
||
while ps -p $pid > /dev/null; do
|
||
# 读取最后一行日志
|
||
if [[ -f "$logfile" ]]; then
|
||
last_line=$(tail -n 1 "$logfile")
|
||
|
||
# 尝试从输出中提取进度信息
|
||
if [[ $last_line == *%* ]]; then
|
||
status="${last_line%%(*}"
|
||
progress="${last_line#*(}"
|
||
progress="${progress%%)*}"
|
||
printf "\r\033[K${CYAN}[$msg]${NC} %s %s " "$status" "$progress"
|
||
else
|
||
dots="${dots}."
|
||
if [[ ${#dots} -gt 5 ]]; then dots="."; fi
|
||
elapsed=$(($(date +%s) - start_time))
|
||
printf "\r\033[K${CYAN}[$msg]${NC} 进行中%s (%ds)" "$dots" "$elapsed"
|
||
fi
|
||
fi
|
||
sleep $delay
|
||
done
|
||
|
||
if wait $pid; then
|
||
printf "\r\033[K${GREEN}[$msg]${NC} 完成! 用时: %ds\n" "$(($(date +%s) - start_time))"
|
||
rm -f "$logfile"
|
||
return 0
|
||
else
|
||
printf "\r\033[K${RED}[$msg]${NC} 失败! 查看日志: $LOG_FILE\n"
|
||
rm -f "$logfile"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# 日志函数
|
||
log_info() {
|
||
echo -e "${GREEN}[INFO]${NC} $1" | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
log_warn() {
|
||
echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[ERROR]${NC} $1" >&2 | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
log_debug() {
|
||
echo -e "${BLUE}[DEBUG]${NC} $1" | tee -a "$LOG_FILE"
|
||
}
|
||
|
||
# 检查是否有 root 权限
|
||
check_root() {
|
||
if [[ $EUID -ne 0 ]]; then
|
||
log_error "此脚本需要 root 权限运行"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 检查系统环境
|
||
check_system() {
|
||
# 显示系统信息
|
||
echo "系统信息:"
|
||
echo "------------------------"
|
||
if [ -f /etc/os-release ]; then
|
||
cat /etc/os-release | grep "PRETTY_NAME" | cut -d= -f2- | tr -d '"'
|
||
fi
|
||
echo "内核版本: $(uname -r)"
|
||
echo "架构: $(uname -m)"
|
||
echo "------------------------"
|
||
|
||
# 检查是否为 Debian/Ubuntu 系统
|
||
if [[ ! -f /etc/debian_version && ! -f /etc/lsb-release ]]; then
|
||
log_warn "未检测到 Debian/Ubuntu 系统,脚本可能无法正常工作"
|
||
read -rp "是否继续? [y/N] " response
|
||
if [[ ! "$response" =~ ^[yY]$ ]]; then
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
# 检查网络连接
|
||
echo -n "检查网络连接... "
|
||
if ping -c 1 -W 2 github.com &>/dev/null; then
|
||
echo -e "${GREEN}连接正常${NC}"
|
||
else
|
||
echo -e "${YELLOW}无法连接到 GitHub${NC}"
|
||
log_warn "无法连接到 GitHub,请检查网络连接"
|
||
read -rp "是否继续? [y/N] " response
|
||
if [[ ! "$response" =~ ^[yY]$ ]]; then
|
||
exit 1
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 获取最新版本号
|
||
get_latest_version() {
|
||
# 利用GitHub重定向特性获取最新版本
|
||
local redirect_url=$(curl -s -L -o /dev/null -w '%{url_effective}' https://github.com/XTLS/Xray-core/releases/latest 2>/dev/null)
|
||
local version=$(echo "$redirect_url" | grep -o 'tag/v[0-9.]*' | cut -d/ -f2 2>/dev/null)
|
||
|
||
# 如果获取失败,尝试备用方法
|
||
if [[ -z "$version" ]]; then
|
||
# 方法2: 通过API获取
|
||
version=$(curl -s https://api.github.com/repos/XTLS/Xray-core/releases/latest | grep -o '"tag_name": "v[0-9.]*"' | cut -d'"' -f4 2>/dev/null)
|
||
fi
|
||
|
||
# 如果还是失败,返回当前全局版本
|
||
if [[ -z "$version" ]]; then
|
||
version="$XRAY_VERSION"
|
||
else
|
||
# 更新全局变量
|
||
XRAY_VERSION="$version"
|
||
fi
|
||
|
||
echo "$version"
|
||
}
|
||
|
||
# 备份函数
|
||
backup_config() {
|
||
if [[ -d "$CONFIG_PATH" ]]; then
|
||
CONFIG_BACKUP="${CONFIG_PATH}_backup_$(date +%Y%m%d%H%M%S)"
|
||
log_info "备份现有配置到 $CONFIG_BACKUP"
|
||
cp -r "$CONFIG_PATH" "$CONFIG_BACKUP" || log_warn "配置备份失败"
|
||
|
||
# 备份 Xray 信息文件
|
||
if [[ -f "/root/xray_info.txt" ]]; then
|
||
cp "/root/xray_info.txt" "${CONFIG_BACKUP}/xray_info.txt.bak" || log_warn "Xray 信息文件备份失败"
|
||
fi
|
||
else
|
||
log_warn "找不到配置目录,跳过备份"
|
||
fi
|
||
}
|
||
|
||
# 显示帮助信息
|
||
show_help() {
|
||
echo "Xray 管理脚本 v${SCRIPT_VERSION}"
|
||
echo "用法: $0 [选项]"
|
||
echo ""
|
||
echo "选项:"
|
||
echo " -h, --help 显示此帮助信息"
|
||
echo " -i, --install 直接运行安装"
|
||
echo " -u, --uninstall 直接运行卸载"
|
||
echo " -s, --status 查看 Xray 状态"
|
||
echo " -up, --update 更新 Xray"
|
||
echo ""
|
||
echo "无参数运行脚本将显示交互式菜单"
|
||
}
|
||
|
||
# 菜单函数
|
||
show_menu() {
|
||
clear
|
||
echo "=================================================="
|
||
echo -e "${CYAN}Xray REALITY管理脚本 v${SCRIPT_VERSION}${NC}"
|
||
echo -e "${CYAN}(VLESS+REALITY 协议 - 稳定版本)${NC}"
|
||
echo "=================================================="
|
||
echo -e "1) ${GREEN}安装 Xray (自动配置所有稳定功能)${NC}"
|
||
echo -e "2) ${RED}卸载 Xray${NC}"
|
||
echo -e "3) ${YELLOW}更新 Xray${NC}"
|
||
echo -e "4) ${BLUE}查看 Xray 状态${NC}"
|
||
echo -e "5) ${CYAN}查看配置信息${NC}"
|
||
echo -e "6) ${GREEN}重启服务${NC}"
|
||
echo -e "0) ${RED}退出${NC}"
|
||
echo "=================================================="
|
||
echo ""
|
||
read -rp "请输入选项 [0-6]: " choice
|
||
|
||
case $choice in
|
||
1) install_xray ;;
|
||
2) uninstall_xray ;;
|
||
3) update_xray ;;
|
||
4) check_status ;;
|
||
5) show_config ;;
|
||
6) restart_service ;;
|
||
0) exit 0 ;;
|
||
*) log_error "无效选项" && sleep 2 && show_menu ;;
|
||
esac
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
# 安装依赖
|
||
install_dependencies() {
|
||
log_info "安装必要依赖"
|
||
|
||
# 更新软件包列表
|
||
apt_progress "apt-get update" "更新软件包列表" || {
|
||
log_error "更新软件包列表失败"
|
||
return 1
|
||
}
|
||
|
||
# 安装必要工具(包含 jq 以支持后量子密码学配置)
|
||
local deps=(curl wget jq unzip)
|
||
for dep in "${deps[@]}"; do
|
||
if ! command -v "$dep" &>/dev/null; then
|
||
apt_progress "apt-get install -y $dep" "安装 $dep" || {
|
||
log_error "安装 $dep 失败"
|
||
return 1
|
||
}
|
||
else
|
||
log_info "$dep 已安装,跳过"
|
||
fi
|
||
done
|
||
|
||
# 检查安装结果
|
||
local all_installed=true
|
||
for dep in "${deps[@]}"; do
|
||
if ! command -v "$dep" &>/dev/null; then
|
||
log_error "$dep 安装失败"
|
||
all_installed=false
|
||
fi
|
||
done
|
||
|
||
if [ "$all_installed" = true ]; then
|
||
log_info "所有依赖安装完成"
|
||
return 0
|
||
else
|
||
log_error "部分依赖安装失败"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# 下载 Xray
|
||
download_xray() {
|
||
log_info "开始下载 Xray"
|
||
|
||
# 检查Xray服务是否正在运行,如果是则停止
|
||
if systemctl is-active xray &>/dev/null; then
|
||
log_info "检测到Xray服务正在运行,先停止服务"
|
||
echo -n "停止 Xray 服务... "
|
||
if systemctl stop xray &>/dev/null; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_warn "无法停止 Xray 服务,可能会影响安装"
|
||
fi
|
||
|
||
# 等待进程完全停止
|
||
echo -n "等待进程释放资源... "
|
||
sleep 2
|
||
if pgrep -x "xray" > /dev/null; then
|
||
# 如果进程仍在运行,尝试强制终止
|
||
pkill -9 -x "xray" &>/dev/null
|
||
sleep 1
|
||
fi
|
||
echo -e "${GREEN}完成${NC}"
|
||
fi
|
||
|
||
# 创建临时目录
|
||
local tmp_dir="/tmp/xray_install"
|
||
mkdir -p "$tmp_dir"
|
||
|
||
# 获取最新版本
|
||
echo -n "获取 Xray 最新版本... "
|
||
local latest_version=$(get_latest_version)
|
||
echo -e "${GREEN}$latest_version${NC}"
|
||
|
||
# 确定系统架构
|
||
local arch
|
||
case $(uname -m) in
|
||
x86_64|amd64) arch="64" ;;
|
||
armv7l|armv8l) arch="arm32-v7a" ;;
|
||
aarch64) arch="arm64-v8a" ;;
|
||
*) arch="64" ;; # 默认使用64位版本
|
||
esac
|
||
|
||
# 构建下载URL
|
||
local download_url="https://github.com/XTLS/Xray-core/releases/download/$latest_version/Xray-linux-$arch.zip"
|
||
log_info "下载链接: $download_url"
|
||
|
||
# 下载Xray
|
||
echo -n "下载 Xray... "
|
||
if wget -q --show-progress -O "$tmp_dir/xray.zip" "$download_url"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "下载 Xray 失败"
|
||
return 1
|
||
fi
|
||
|
||
# 解压文件
|
||
echo -n "解压 Xray... "
|
||
if unzip -q -o "$tmp_dir/xray.zip" -d "$tmp_dir"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "解压 Xray 失败"
|
||
return 1
|
||
fi
|
||
|
||
# 创建目录
|
||
mkdir -p "$XRAY_PATH" "$CONFIG_PATH" "$LOG_PATH"
|
||
|
||
# 复制文件前确保目标文件不被占用
|
||
if [[ -f "$XRAY_PATH/xray" ]]; then
|
||
# 如果文件存在,先尝试重命名它
|
||
mv "$XRAY_PATH/xray" "$XRAY_PATH/xray.old" 2>/dev/null
|
||
fi
|
||
|
||
# 复制文件
|
||
echo -n "安装 Xray 核心文件... "
|
||
if cp "$tmp_dir/xray" "$XRAY_PATH/xray" && chmod +x "$XRAY_PATH/xray"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
# 删除旧文件
|
||
rm -f "$XRAY_PATH/xray.old" 2>/dev/null
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "安装 Xray 核心文件失败"
|
||
# 恢复旧文件
|
||
if [[ -f "$XRAY_PATH/xray.old" ]]; then
|
||
mv "$XRAY_PATH/xray.old" "$XRAY_PATH/xray" 2>/dev/null
|
||
fi
|
||
return 1
|
||
fi
|
||
|
||
# 复制 geoip.dat 和 geosite.dat
|
||
echo -n "安装 GeoIP 和 GeoSite 数据... "
|
||
if cp "$tmp_dir/geoip.dat" "$XRAY_PATH/geoip.dat" && \
|
||
cp "$tmp_dir/geosite.dat" "$XRAY_PATH/geosite.dat"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_warn "安装 GeoIP 和 GeoSite 数据失败,将在配置时下载"
|
||
fi
|
||
|
||
# 清理临时文件
|
||
rm -rf "$tmp_dir"
|
||
|
||
log_info "Xray $latest_version 安装完成"
|
||
return 0
|
||
}
|
||
|
||
# 生成随机 PORT 和 UUID
|
||
generate_random_values() {
|
||
log_info "生成随机配置值"
|
||
|
||
# 询问用户是否指定端口
|
||
if [[ -z "$REALITY_PORT" ]]; then
|
||
read -rp "是否指定端口? [y/N] " specify_port
|
||
if [[ "$specify_port" =~ ^[yY]$ ]]; then
|
||
# 用户选择指定端口
|
||
while true; do
|
||
read -rp "请输入端口号 (1-65535): " REALITY_PORT
|
||
# 验证端口是否为有效数字
|
||
if ! [[ "$REALITY_PORT" =~ ^[0-9]+$ ]] || [ "$REALITY_PORT" -lt 1 ] || [ "$REALITY_PORT" -gt 65535 ]; then
|
||
log_error "无效的端口号,请输入1-65535之间的数字"
|
||
continue
|
||
fi
|
||
|
||
# 检查端口是否被占用
|
||
if ss -tuln | grep -q ":$REALITY_PORT "; then
|
||
log_warn "端口 $REALITY_PORT 已被占用,请选择其他端口"
|
||
continue
|
||
fi
|
||
|
||
log_info "将使用指定端口: $REALITY_PORT"
|
||
break
|
||
done
|
||
else
|
||
# 用户选择随机端口,继续原来的逻辑
|
||
# 尝试找一个未被占用的端口
|
||
local attempts=0
|
||
while [[ "$attempts" -lt 10 ]]; do
|
||
REALITY_PORT=$(shuf -i 10000-60000 -n 1)
|
||
# 检查端口是否被占用
|
||
if ! ss -tuln | grep -q ":$REALITY_PORT "; then
|
||
log_info "生成随机端口: $REALITY_PORT"
|
||
break
|
||
fi
|
||
attempts=$((attempts + 1))
|
||
done
|
||
if [[ "$attempts" -eq 10 ]]; then
|
||
log_warn "无法找到未占用的端口,使用随机端口: $REALITY_PORT"
|
||
fi
|
||
fi
|
||
else
|
||
# 验证端口是否为有效数字
|
||
if ! [[ "$REALITY_PORT" =~ ^[0-9]+$ ]]; then
|
||
log_warn "无效的端口: $REALITY_PORT,生成新的随机端口"
|
||
REALITY_PORT=$(shuf -i 10000-60000 -n 1)
|
||
fi
|
||
fi
|
||
|
||
# 生成 UUID
|
||
if [[ -z "$UUID" ]]; then
|
||
# 检查xray命令是否可用
|
||
if [[ -f "$XRAY_PATH/xray" && -x "$XRAY_PATH/xray" ]]; then
|
||
UUID=$($XRAY_PATH/xray uuid)
|
||
log_info "生成 UUID: $UUID"
|
||
else
|
||
# 如果xray不可用,使用uuidgen或者随机生成
|
||
if command -v uuidgen &>/dev/null; then
|
||
UUID=$(uuidgen)
|
||
else
|
||
# 简单的UUID生成方法(不完全符合标准但足够使用)
|
||
UUID=$(cat /proc/sys/kernel/random/uuid 2>/dev/null ||
|
||
(date +%s%N | sha256sum | head -c 32 |
|
||
sed 's/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1\2\3\4-\5\6-\7\8-/'))
|
||
fi
|
||
log_info "生成 UUID: $UUID"
|
||
fi
|
||
else
|
||
# 验证UUID格式
|
||
if ! [[ "$UUID" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]]; then
|
||
log_warn "无效的 UUID 格式: $UUID,生成新的 UUID"
|
||
if [[ -f "$XRAY_PATH/xray" && -x "$XRAY_PATH/xray" ]]; then
|
||
UUID=$($XRAY_PATH/xray uuid)
|
||
else
|
||
UUID=$(cat /proc/sys/kernel/random/uuid 2>/dev/null ||
|
||
(date +%s%N | sha256sum | head -c 32 |
|
||
sed 's/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1\2\3\4-\5\6-\7\8-/'))
|
||
fi
|
||
log_info "生成新的 UUID: $UUID"
|
||
fi
|
||
fi
|
||
|
||
# 生成 REALITY 密钥对
|
||
log_info "生成 REALITY 密钥对"
|
||
if [[ -z "$PRIVATE_KEY" || -z "$PUBLIC_KEY" ]]; then
|
||
local key_pair
|
||
if [[ -f "$XRAY_PATH/xray" && -x "$XRAY_PATH/xray" ]]; then
|
||
key_pair=$($XRAY_PATH/xray x25519)
|
||
PRIVATE_KEY=$(echo "$key_pair" | grep -E "(Private|PrivateKey)" | awk '{print $NF}')
|
||
PUBLIC_KEY=$(echo "$key_pair" | grep -E "(Public|Password)" | awk '{print $NF}')
|
||
|
||
# 验证密钥是否正确生成
|
||
if [[ -z "$PRIVATE_KEY" || -z "$PUBLIC_KEY" ]]; then
|
||
log_error "密钥生成失败,请检查 Xray 版本"
|
||
return 1
|
||
fi
|
||
else
|
||
log_warn "无法使用 xray 生成密钥对,将跳过密钥生成"
|
||
log_info "安装完成后,将自动生成密钥对"
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
# 获取服务器IP
|
||
get_server_ip
|
||
|
||
log_debug "私钥: $PRIVATE_KEY"
|
||
log_debug "公钥: $PUBLIC_KEY"
|
||
|
||
# 验证密钥是否成功生成
|
||
if [[ -z "$PRIVATE_KEY" || -z "$PUBLIC_KEY" ]]; then
|
||
log_error "REALITY 密钥生成失败"
|
||
return 1
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
# 获取服务器IP
|
||
get_server_ip() {
|
||
log_info "获取服务器IP地址"
|
||
|
||
if [[ -n "$SERVER_IP" ]]; then
|
||
log_info "使用已设置的IP: $SERVER_IP"
|
||
return 0
|
||
fi
|
||
|
||
# 尝试首选方法获取公网IP
|
||
SERVER_IP=$(curl -s -m 5 https://api.ipify.org 2>/dev/null)
|
||
|
||
# 验证获取到的IP是否为有效IPv4地址
|
||
if [[ -n "$SERVER_IP" && "$SERVER_IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||
log_info "成功获取公网IP: $SERVER_IP"
|
||
return 0
|
||
fi
|
||
|
||
# 如果第一个方法失败,尝试备用方法
|
||
local backup_services=("https://ifconfig.me" "https://ip.sb" "https://ipinfo.io/ip")
|
||
|
||
for service in "${backup_services[@]}"; do
|
||
SERVER_IP=$(curl -s -m 3 "$service" 2>/dev/null)
|
||
if [[ -n "$SERVER_IP" && "$SERVER_IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||
log_info "成功从 $service 获取公网IP: $SERVER_IP"
|
||
return 0
|
||
fi
|
||
done
|
||
|
||
# 如果所有公网IP获取方式都失败,则使用本地IP
|
||
if command -v hostname &>/dev/null; then
|
||
SERVER_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||
fi
|
||
|
||
# 如果hostname命令失败,尝试使用ip命令
|
||
if [[ -z "$SERVER_IP" && -x "$(command -v ip)" ]]; then
|
||
SERVER_IP=$(ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n 1)
|
||
fi
|
||
|
||
# 如果还是失败,尝试使用ifconfig命令
|
||
if [[ -z "$SERVER_IP" && -x "$(command -v ifconfig)" ]]; then
|
||
SERVER_IP=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -n 1)
|
||
fi
|
||
|
||
if [[ -n "$SERVER_IP" ]]; then
|
||
log_warn "无法获取公网IP,使用本地IP: $SERVER_IP"
|
||
return 0
|
||
else
|
||
log_error "无法获取任何有效IP地址,将使用127.0.0.1作为占位符"
|
||
SERVER_IP="127.0.0.1"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# 配置 Xray - 修改为 VLESS+REALITY 并支持 TUN 模式
|
||
configure_xray() {
|
||
log_info "配置 Xray"
|
||
|
||
# 生成随机值
|
||
generate_random_values || {
|
||
log_error "生成随机值失败"
|
||
return 1
|
||
}
|
||
|
||
# 验证必要的变量是否已设置
|
||
if [[ -z "$PRIVATE_KEY" || -z "$PUBLIC_KEY" || -z "$UUID" || -z "$REALITY_PORT" ]]; then
|
||
log_error "必要的配置参数缺失:"
|
||
[[ -z "$PRIVATE_KEY" ]] && log_error "- PRIVATE_KEY 未设置"
|
||
[[ -z "$PUBLIC_KEY" ]] && log_error "- PUBLIC_KEY 未设置"
|
||
[[ -z "$UUID" ]] && log_error "- UUID 未设置"
|
||
[[ -z "$REALITY_PORT" ]] && log_error "- REALITY_PORT 未设置"
|
||
return 1
|
||
fi
|
||
|
||
# 创建配置目录
|
||
mkdir -p "$CONFIG_PATH"
|
||
|
||
# 创建示例目录
|
||
mkdir -p "$CONFIG_PATH/examples"
|
||
|
||
log_info "创建 VLESS+REALITY 配置文件(最新版本,稳定功能)"
|
||
|
||
# 注意:后量子密码学功能需要更新版本的 Xray,暂时禁用以确保兼容性
|
||
log_info "配置 REALITY 协议基础功能(稳定版本)"
|
||
|
||
# VLESS + REALITY 配置 (最新版本,包含所有功能)
|
||
cat > "$CONFIG_PATH/config.json" << EOF
|
||
{
|
||
"log": {
|
||
"loglevel": "warning",
|
||
"access": "$LOG_PATH/access.log",
|
||
"error": "$LOG_PATH/error.log"
|
||
},
|
||
"inbounds": [
|
||
{
|
||
"port": $REALITY_PORT,
|
||
"protocol": "vless",
|
||
"settings": {
|
||
"clients": [
|
||
{
|
||
"id": "$UUID",
|
||
"flow": "xtls-rprx-vision"
|
||
}
|
||
],
|
||
"decryption": "none"
|
||
},
|
||
"streamSettings": {
|
||
"network": "tcp",
|
||
"security": "reality",
|
||
"realitySettings": {
|
||
"show": false,
|
||
"target": "www.shopify.com:443",
|
||
"xver": 0,
|
||
"serverNames": [
|
||
"shopify.com",
|
||
"www.shopify.com"
|
||
],
|
||
"privateKey": "$PRIVATE_KEY",
|
||
"minClientVer": "",
|
||
"maxClientVer": "",
|
||
"maxTimeDiff": 0,
|
||
"shortIds": [
|
||
"",
|
||
"6ba85179e30d"
|
||
],
|
||
"limitFallbackUpload": {
|
||
"afterBytes": 1048576,
|
||
"bytesPerSec": 1048576,
|
||
"burstBytesPerSec": 2097152
|
||
},
|
||
"limitFallbackDownload": {
|
||
"afterBytes": 1048576,
|
||
"bytesPerSec": 1048576,
|
||
"burstBytesPerSec": 2097152
|
||
}
|
||
}
|
||
},
|
||
"sniffing": {
|
||
"enabled": true,
|
||
"destOverride": [
|
||
"http",
|
||
"tls",
|
||
"quic"
|
||
],
|
||
"routeOnly": false
|
||
}
|
||
}
|
||
],
|
||
"outbounds": [
|
||
{
|
||
"protocol": "freedom",
|
||
"tag": "direct"
|
||
},
|
||
{
|
||
"protocol": "blackhole",
|
||
"tag": "block"
|
||
}
|
||
],
|
||
"dns": {
|
||
"hosts": {
|
||
"dns.google": "8.8.8.8",
|
||
"proxy.example.com": "127.0.0.1"
|
||
},
|
||
"servers": [
|
||
{
|
||
"address": "https://1.1.1.1/dns-query",
|
||
"domains": [
|
||
"geosite:geolocation-!cn"
|
||
],
|
||
"expectedIPs": [
|
||
"geoip:!cn"
|
||
],
|
||
"skipFallback": true,
|
||
"queryStrategy": "UseIPv4"
|
||
},
|
||
{
|
||
"address": "https://223.5.5.5/dns-query",
|
||
"domains": [
|
||
"geosite:cn"
|
||
],
|
||
"expectedIPs": [
|
||
"geoip:cn"
|
||
],
|
||
"skipFallback": false,
|
||
"queryStrategy": "UseIP"
|
||
},
|
||
"8.8.8.8",
|
||
"https://dns.google/dns-query"
|
||
],
|
||
"queryStrategy": "UseIP",
|
||
"disableCache": false,
|
||
"disableFallback": false
|
||
},
|
||
"routing": {
|
||
"domainStrategy": "AsIs",
|
||
"rules": [
|
||
{
|
||
"type": "field",
|
||
"outboundTag": "block",
|
||
"domain": [
|
||
"geosite:category-ads-all"
|
||
]
|
||
},
|
||
{
|
||
"type": "field",
|
||
"outboundTag": "direct",
|
||
"ip": [
|
||
"geoip:private"
|
||
]
|
||
},
|
||
{
|
||
"type": "field",
|
||
"outboundTag": "direct",
|
||
"domain": [
|
||
"geosite:private"
|
||
]
|
||
},
|
||
{
|
||
"type": "field",
|
||
"port": "443",
|
||
"network": "udp",
|
||
"outboundTag": "block"
|
||
},
|
||
{
|
||
"type": "field",
|
||
"outboundTag": "direct",
|
||
"ip": [
|
||
"geoip:cn"
|
||
]
|
||
},
|
||
{
|
||
"type": "field",
|
||
"outboundTag": "direct",
|
||
"domain": [
|
||
"geosite:cn"
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
EOF
|
||
|
||
if [[ -f "$CONFIG_PATH/config.json" ]]; then
|
||
log_info "配置文件创建成功"
|
||
else
|
||
log_error "配置文件创建失败"
|
||
return 1
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# 配置防火墙 - 简化只处理 REALITY 端口
|
||
configure_firewall() {
|
||
log_info "配置防火墙"
|
||
|
||
# 检测和关闭旧端口
|
||
local old_ports=()
|
||
|
||
# 从备份中查找旧端口
|
||
if [[ -n "$CONFIG_BACKUP" && -f "$CONFIG_BACKUP/config.json" ]]; then
|
||
log_info "检测旧配置中的端口"
|
||
if command -v jq &>/dev/null; then
|
||
# 使用jq查找所有inbounds的端口
|
||
local detected_ports=$(jq '.inbounds[].port' "$CONFIG_BACKUP/config.json" 2>/dev/null)
|
||
for port in $detected_ports; do
|
||
if [[ "$port" != "null" && -n "$port" ]]; then
|
||
old_ports+=("$port")
|
||
log_info "检测到旧端口: $port"
|
||
fi
|
||
done
|
||
else
|
||
# 使用grep查找端口
|
||
local detected_ports=$(grep -o '"port": [0-9]*' "$CONFIG_BACKUP/config.json" | awk '{print $2}')
|
||
for port in $detected_ports; do
|
||
if [[ -n "$port" ]]; then
|
||
old_ports+=("$port")
|
||
log_info "检测到旧端口: $port"
|
||
fi
|
||
done
|
||
fi
|
||
fi
|
||
|
||
# 如果找不到旧端口,也查找默认位置
|
||
if [[ ${#old_ports[@]} -eq 0 && -f "$CONFIG_PATH/config.json" && "$CONFIG_PATH/config.json" != "$(readlink -f "$CONFIG_BACKUP/config.json")" ]]; then
|
||
if command -v jq &>/dev/null; then
|
||
# 使用jq查找所有inbounds的端口
|
||
local detected_ports=$(jq '.inbounds[].port' "$CONFIG_PATH/config.json" 2>/dev/null)
|
||
for port in $detected_ports; do
|
||
if [[ "$port" != "null" && -n "$port" ]]; then
|
||
old_ports+=("$port")
|
||
log_info "检测到当前配置的端口: $port"
|
||
fi
|
||
done
|
||
else
|
||
# 使用grep查找端口
|
||
local detected_ports=$(grep -o '"port": [0-9]*' "$CONFIG_PATH/config.json" | awk '{print $2}')
|
||
for port in $detected_ports; do
|
||
if [[ -n "$port" ]]; then
|
||
old_ports+=("$port")
|
||
log_info "检测到当前配置的端口: $port"
|
||
fi
|
||
done
|
||
fi
|
||
fi
|
||
|
||
# 从旧端口列表中过滤掉当前端口
|
||
local filtered_ports=()
|
||
for port in "${old_ports[@]}"; do
|
||
# 检查端口是否为有效数字
|
||
if ! [[ "$port" =~ ^[0-9]+$ ]]; then
|
||
log_warn "无效的端口号: $port,已跳过"
|
||
continue
|
||
fi
|
||
|
||
# 检查是否与当前端口相同
|
||
if [[ "$port" -eq "$REALITY_PORT" ]]; then
|
||
log_info "端口 $port 与当前端口相同,已跳过"
|
||
continue
|
||
fi
|
||
|
||
filtered_ports+=("$port")
|
||
done
|
||
old_ports=("${filtered_ports[@]}")
|
||
|
||
# 检查是否安装了 ufw
|
||
if command -v ufw &>/dev/null; then
|
||
# 检查ufw是否启用
|
||
local ufw_status=$(ufw status | grep -o "Status: active" 2>/dev/null)
|
||
|
||
if [[ -z "$ufw_status" ]]; then
|
||
log_warn "UFW 防火墙未启用,可能需要手动配置防火墙规则"
|
||
log_info "您可以运行 'sudo ufw enable' 启用 UFW 防火墙"
|
||
return 0
|
||
fi
|
||
|
||
# 关闭旧端口
|
||
if [[ ${#old_ports[@]} -gt 0 ]]; then
|
||
log_info "开始关闭旧端口"
|
||
local closed_ports=()
|
||
|
||
for port in "${old_ports[@]}"; do
|
||
# 检查端口是否已开放在UFW中
|
||
if ! ufw status | grep -q "$port/tcp"; then
|
||
log_info "端口 $port 未在 UFW 中开放,跳过"
|
||
continue
|
||
fi
|
||
|
||
echo -n "关闭 UFW 防火墙端口 $port... "
|
||
if ufw delete allow "$port/tcp" &>/dev/null; then
|
||
echo -e "${GREEN}完成${NC}"
|
||
closed_ports+=("$port")
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_warn "无法关闭端口 $port"
|
||
fi
|
||
done
|
||
|
||
# 打印关闭的端口
|
||
if [[ ${#closed_ports[@]} -gt 0 ]]; then
|
||
local closed_list=$(printf ", %s" "${closed_ports[@]}")
|
||
closed_list=${closed_list:2} # 移除开头的逗号和空格
|
||
log_info "已关闭 UFW 防火墙中的旧端口: $closed_list"
|
||
else
|
||
log_info "没有需要关闭的旧端口"
|
||
fi
|
||
else
|
||
log_info "未检测到旧端口,跳过关闭端口步骤"
|
||
fi
|
||
|
||
# 确定需要开放的端口
|
||
local ports=("$REALITY_PORT")
|
||
|
||
echo -n "配置 UFW 防火墙... "
|
||
local opened_ports=()
|
||
for port in "${ports[@]}"; do
|
||
# 检查端口是否为有效数字
|
||
if ! [[ "$port" =~ ^[0-9]+$ ]]; then
|
||
log_warn "端口 '$port' 不是有效数字,已跳过"
|
||
continue
|
||
fi
|
||
|
||
# 检查端口是否已经开放
|
||
if ufw status | grep -q "$port/tcp"; then
|
||
log_info "端口 $port 已经开放,跳过"
|
||
opened_ports+=("$port")
|
||
continue
|
||
fi
|
||
|
||
# 开放端口
|
||
if ufw allow "$port/tcp" &>/dev/null; then
|
||
opened_ports+=("$port")
|
||
else
|
||
log_warn "无法开放端口 $port"
|
||
fi
|
||
done
|
||
echo -e "${GREEN}完成${NC}"
|
||
|
||
# 打印开放的端口
|
||
if [[ ${#opened_ports[@]} -gt 0 ]]; then
|
||
local port_list=$(printf ", %s" "${opened_ports[@]}")
|
||
port_list=${port_list:2} # 移除开头的逗号和空格
|
||
log_info "已在 UFW 防火墙开放端口: $port_list"
|
||
else
|
||
log_warn "没有成功开放任何端口"
|
||
fi
|
||
else
|
||
log_warn "未检测到 UFW 防火墙,跳过防火墙配置"
|
||
log_info "如需管理防火墙规则,请安装 UFW: sudo apt install ufw"
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# 生成客户端配置 - 更新为只提供 VLESS+REALITY
|
||
generate_client_info() {
|
||
log_info "生成客户端信息"
|
||
|
||
# 确保IP地址已获取
|
||
if [[ -z "$SERVER_IP" ]]; then
|
||
get_server_ip
|
||
fi
|
||
|
||
# 生成 VLESS + REALITY 分享链接
|
||
local share_link="vless://${UUID}@${SERVER_IP}:${REALITY_PORT}?security=reality&encryption=none&pbk=${PUBLIC_KEY}&fp=chrome&type=tcp&flow=xtls-rprx-vision&sni=www.shopify.com&sid=6ba85179e30d&spx=%2Fsearch%3Fq%3Dcdn#Xray-Reality"
|
||
|
||
# 保存客户端信息
|
||
cat > /root/xray_info.txt << EOF
|
||
========================= Xray Reality 配置信息 =========================
|
||
服务器地址: ${SERVER_IP}
|
||
端口: ${REALITY_PORT}
|
||
UUID: ${UUID}
|
||
协议: vless
|
||
传输协议: tcp
|
||
加密方式: none
|
||
流控: xtls-rprx-vision
|
||
安全: reality
|
||
公钥: ${PUBLIC_KEY}
|
||
私钥: ${PRIVATE_KEY}
|
||
SNI: www.shopify.com
|
||
指纹: chrome
|
||
短 ID: 6ba85179e30d
|
||
爬虫路径: /search?q=cdn
|
||
目标站点: www.shopify.com:443
|
||
速率限制: 已启用回退连接限制
|
||
TUN模式支持: 已启用
|
||
DNS优化: 已启用(DoH)
|
||
兼容性: 当前版本完全兼容
|
||
==================================================================
|
||
|
||
分享链接:
|
||
${share_link}
|
||
|
||
二维码链接:
|
||
https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${share_link}
|
||
==================================================================
|
||
|
||
TUN模式支持:
|
||
REALITY 协议已配置支持 TUN 模式,客户端配置示例文件已生成:
|
||
1. REALITY-TUN配置: ${CONFIG_PATH}/examples/reality_tun_example.json
|
||
|
||
将配置文件导入到支持TUN模式的客户端后:
|
||
1. 切换到"TUN模式"选项卡
|
||
2. 点击"启用TUN模式"
|
||
3. 选择"系统代理"或"全局代理"
|
||
4. 重启客户端并允许管理员权限
|
||
|
||
==================================================================
|
||
|
||
配置文件路径: ${CONFIG_PATH}/config.json
|
||
服务控制:
|
||
启动: systemctl start xray
|
||
停止: systemctl stop xray
|
||
重启: systemctl restart xray
|
||
状态: systemctl status xray
|
||
==================================================================
|
||
EOF
|
||
|
||
# 生成 REALITY TUN 模式配置示例
|
||
generate_reality_tun_config
|
||
|
||
log_info "客户端信息已保存到 /root/xray_info.txt"
|
||
|
||
# 打印信息
|
||
cat /root/xray_info.txt
|
||
|
||
log_info "安装完成!Xray REALITY 已成功部署"
|
||
return 0
|
||
}
|
||
|
||
# 新增:生成 REALITY TUN 模式配置
|
||
generate_reality_tun_config() {
|
||
log_info "生成 REALITY TUN 模式配置示例"
|
||
|
||
# 确保IP地址已设置
|
||
if [[ -z "$SERVER_IP" ]]; then
|
||
get_server_ip
|
||
fi
|
||
|
||
# 创建 TUN 配置示例目录
|
||
local example_dir="$CONFIG_PATH/examples"
|
||
mkdir -p "$example_dir"
|
||
|
||
# 生成客户端配置示例,用于TUN模式
|
||
cat > "$example_dir/reality_tun_example.json" << EOF
|
||
{
|
||
"log": {
|
||
"loglevel": "warning"
|
||
},
|
||
"inbounds": [
|
||
{
|
||
"tag": "socks",
|
||
"port": 10808,
|
||
"listen": "127.0.0.1",
|
||
"protocol": "socks",
|
||
"sniffing": {
|
||
"enabled": true,
|
||
"destOverride": [
|
||
"http",
|
||
"tls"
|
||
],
|
||
"routeOnly": false
|
||
},
|
||
"settings": {
|
||
"auth": "noauth",
|
||
"udp": true,
|
||
"allowTransparent": false
|
||
}
|
||
},
|
||
{
|
||
"tag": "tun",
|
||
"protocol": "tun",
|
||
"settings": {
|
||
"network": "all"
|
||
},
|
||
"sniffing": {
|
||
"enabled": true,
|
||
"destOverride": [
|
||
"http",
|
||
"tls"
|
||
],
|
||
"routeOnly": false
|
||
}
|
||
}
|
||
],
|
||
"tun": {
|
||
"enable": true,
|
||
"stack": "gvisor",
|
||
"mtu": 9000,
|
||
"strict_route": true
|
||
},
|
||
"outbounds": [
|
||
{
|
||
"tag": "proxy",
|
||
"protocol": "vless",
|
||
"settings": {
|
||
"vnext": [
|
||
{
|
||
"address": "${SERVER_IP}",
|
||
"port": ${REALITY_PORT},
|
||
"users": [
|
||
{
|
||
"id": "${UUID}",
|
||
"flow": "xtls-rprx-vision",
|
||
"encryption": "none"
|
||
}
|
||
]
|
||
}
|
||
]
|
||
},
|
||
"streamSettings": {
|
||
"network": "tcp",
|
||
"security": "reality",
|
||
"realitySettings": {
|
||
"show": false,
|
||
"fingerprint": "chrome",
|
||
"serverName": "www.shopify.com",
|
||
"publicKey": "${PUBLIC_KEY}",
|
||
"shortId": "6ba85179e30d",
|
||
"spiderX": "/search?q=cdn"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"tag": "direct",
|
||
"protocol": "freedom"
|
||
},
|
||
{
|
||
"tag": "block",
|
||
"protocol": "blackhole"
|
||
}
|
||
],
|
||
"dns": {
|
||
"hosts": {
|
||
"dns.google": "8.8.8.8",
|
||
"proxy.example.com": "127.0.0.1"
|
||
},
|
||
"servers": [
|
||
{
|
||
"address": "1.1.1.1",
|
||
"domains": [
|
||
"geosite:geolocation-!cn"
|
||
],
|
||
"expectIPs": [
|
||
"geoip:!cn"
|
||
]
|
||
},
|
||
{
|
||
"address": "223.5.5.5",
|
||
"domains": [
|
||
"geosite:cn"
|
||
],
|
||
"expectIPs": [
|
||
"geoip:cn"
|
||
]
|
||
},
|
||
"8.8.8.8",
|
||
"https://dns.google/dns-query"
|
||
],
|
||
"queryStrategy": "UseIP",
|
||
"disableCache": false,
|
||
"disableFallback": false
|
||
},
|
||
"routing": {
|
||
"domainStrategy": "IPIfNonMatch",
|
||
"rules": [
|
||
{
|
||
"type": "field",
|
||
"outboundTag": "proxy",
|
||
"domain": [
|
||
"domain:google.com",
|
||
"domain:googleapis.cn",
|
||
"domain:gstatic.com"
|
||
]
|
||
},
|
||
{
|
||
"type": "field",
|
||
"outboundTag": "direct",
|
||
"domain": [
|
||
"geosite:cn"
|
||
]
|
||
},
|
||
{
|
||
"type": "field",
|
||
"outboundTag": "direct",
|
||
"ip": [
|
||
"geoip:private",
|
||
"geoip:cn"
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
EOF
|
||
|
||
log_info "REALITY TUN 模式配置示例已保存到 $example_dir/reality_tun_example.json"
|
||
log_info "使用方法: 将配置导入到支持TUN模式的客户端,然后开启TUN模式"
|
||
|
||
return 0
|
||
}
|
||
|
||
# 创建系统服务
|
||
create_service() {
|
||
log_info "创建 Xray 系统服务"
|
||
|
||
# 创建服务文件
|
||
cat > /etc/systemd/system/xray.service << EOF
|
||
[Unit]
|
||
Description=Xray Service
|
||
Documentation=https://github.com/XTLS/Xray-core
|
||
After=network.target nss-lookup.target
|
||
|
||
[Service]
|
||
User=root
|
||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||
NoNewPrivileges=true
|
||
ExecStart=$XRAY_PATH/xray run -config $CONFIG_PATH/config.json
|
||
Restart=on-failure
|
||
RestartPreventExitStatus=23
|
||
LimitNPROC=10000
|
||
LimitNOFILE=1000000
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
# 重新加载 systemd 配置并启用服务
|
||
systemctl daemon-reload
|
||
|
||
echo -n "启用 Xray 服务... "
|
||
if systemctl enable xray &>/dev/null; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "无法启用 Xray 服务"
|
||
return 1
|
||
fi
|
||
|
||
echo -n "启动 Xray 服务... "
|
||
if systemctl start xray; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "无法启动 Xray 服务"
|
||
return 1
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# 安装完整流程 - 更新为直接安装 VLESS+REALITY
|
||
install_xray() {
|
||
clear
|
||
echo "=================================================="
|
||
echo -e "${GREEN}开始安装 Xray REALITY 协议(稳定版本)${NC}"
|
||
echo "=================================================="
|
||
echo -e "${CYAN}自动配置功能列表:${NC}"
|
||
echo -e "✓ VLESS+REALITY 协议"
|
||
echo -e "✓ 速率限制保护"
|
||
echo -e "✓ TUN模式支持"
|
||
echo -e "✓ DNS优化 (DoH)"
|
||
echo -e "✓ 智能路由规则"
|
||
echo -e "✓ 自动防火墙配置"
|
||
echo -e "✓ 完全兼容当前版本"
|
||
echo "=================================================="
|
||
|
||
# 检查是否为 root
|
||
check_root
|
||
|
||
# 检查系统环境
|
||
check_system
|
||
|
||
# 备份现有配置
|
||
backup_config
|
||
|
||
# 安装依赖
|
||
install_dependencies || {
|
||
log_error "安装依赖失败,退出安装"
|
||
return 1
|
||
}
|
||
|
||
# 下载和安装 Xray
|
||
download_xray || {
|
||
log_error "下载 Xray 失败,退出安装"
|
||
return 1
|
||
}
|
||
|
||
# 配置 Xray
|
||
configure_xray || {
|
||
log_error "配置 Xray 失败,退出安装"
|
||
return 1
|
||
}
|
||
|
||
# 创建系统服务
|
||
create_service || {
|
||
log_error "创建系统服务失败,但会继续安装过程"
|
||
}
|
||
|
||
# 配置防火墙
|
||
configure_firewall
|
||
|
||
# 生成客户端信息
|
||
generate_client_info
|
||
|
||
echo ""
|
||
echo "=================================================="
|
||
echo -e "${GREEN}🎉 安装完成!${NC}"
|
||
echo "=================================================="
|
||
echo -e "${CYAN}已自动启用的功能:${NC}"
|
||
echo -e "✅ VLESS+REALITY 协议"
|
||
echo -e "✅ 速率限制保护"
|
||
echo -e "✅ TUN模式支持"
|
||
echo -e "✅ DNS优化 (DoH)"
|
||
echo -e "✅ 智能路由规则"
|
||
echo -e "✅ 自动防火墙配置"
|
||
echo -e "✅ 完全兼容当前版本"
|
||
echo "=================================================="
|
||
log_info "Xray REALITY 协议(稳定版本)安装成功!"
|
||
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
}
|
||
|
||
# 停止 Xray 服务
|
||
stop_xray_service() {
|
||
log_info "停止 Xray 服务"
|
||
|
||
if systemctl is-active xray &>/dev/null; then
|
||
echo -n "正在停止 Xray 服务... "
|
||
if systemctl stop xray &>/dev/null; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_warn "无法停止 Xray 服务,将尝试继续卸载"
|
||
fi
|
||
else
|
||
log_info "Xray 服务未运行"
|
||
fi
|
||
|
||
echo -n "禁用 Xray 服务自启动... "
|
||
if systemctl disable xray &>/dev/null; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${YELLOW}失败${NC}"
|
||
log_warn "无法禁用 Xray 服务自启动,服务可能不存在"
|
||
fi
|
||
}
|
||
|
||
# 读取并关闭防火墙端口
|
||
close_firewall_port() {
|
||
log_info "尝试关闭之前开放的防火墙端口"
|
||
|
||
# 检查是否安装了 ufw
|
||
if ! command -v ufw &>/dev/null; then
|
||
log_warn "未检测到 UFW 防火墙,跳过防火墙配置关闭"
|
||
return 0
|
||
fi
|
||
|
||
# 检查ufw是否启用
|
||
local ufw_status=$(ufw status | grep -o "Status: active" 2>/dev/null)
|
||
if [[ -z "$ufw_status" ]]; then
|
||
log_warn "UFW 防火墙未启用,跳过防火墙规则关闭"
|
||
return 0
|
||
fi
|
||
|
||
# 尝试从配置或备份文件中读取端口
|
||
local ports=()
|
||
|
||
# 从当前配置中查找端口
|
||
if [[ -f "$CONFIG_PATH/config.json" ]]; then
|
||
if command -v jq &>/dev/null; then
|
||
# 使用jq查找所有inbounds的端口
|
||
local all_ports=$(jq '.inbounds[].port' "$CONFIG_PATH/config.json" 2>/dev/null)
|
||
for port in $all_ports; do
|
||
if [[ "$port" != "null" && -n "$port" ]]; then
|
||
ports+=("$port")
|
||
fi
|
||
done
|
||
else
|
||
# 使用grep查找端口
|
||
local all_ports=$(grep -o '"port": [0-9]*' "$CONFIG_PATH/config.json" | awk '{print $2}')
|
||
for port in $all_ports; do
|
||
if [[ -n "$port" ]]; then
|
||
ports+=("$port")
|
||
fi
|
||
done
|
||
fi
|
||
fi
|
||
|
||
# 如果找不到端口,从备份中查找
|
||
if [[ ${#ports[@]} -eq 0 && -n "$CONFIG_BACKUP" && -f "$CONFIG_BACKUP/config.json" ]]; then
|
||
if command -v jq &>/dev/null; then
|
||
local all_ports=$(jq '.inbounds[].port' "$CONFIG_BACKUP/config.json" 2>/dev/null)
|
||
for port in $all_ports; do
|
||
if [[ "$port" != "null" && -n "$port" ]]; then
|
||
ports+=("$port")
|
||
fi
|
||
done
|
||
else
|
||
local all_ports=$(grep -o '"port": [0-9]*' "$CONFIG_BACKUP/config.json" | awk '{print $2}')
|
||
for port in $all_ports; do
|
||
if [[ -n "$port" ]]; then
|
||
ports+=("$port")
|
||
fi
|
||
done
|
||
fi
|
||
fi
|
||
|
||
# 如果还是找不到端口,尝试使用全局变量
|
||
if [[ ${#ports[@]} -eq 0 ]]; then
|
||
if [[ -n "$REALITY_PORT" && "$REALITY_PORT" =~ ^[0-9]+$ ]]; then
|
||
ports+=("$REALITY_PORT")
|
||
fi
|
||
fi
|
||
|
||
# 去除重复的端口
|
||
if [[ ${#ports[@]} -gt 0 ]]; then
|
||
local unique_ports=()
|
||
local port_list=""
|
||
for port in "${ports[@]}"; do
|
||
# 检查端口是否为有效数字
|
||
if ! [[ "$port" =~ ^[0-9]+$ ]]; then
|
||
log_warn "端口 '$port' 不是有效数字,已跳过"
|
||
continue
|
||
fi
|
||
|
||
# 检查端口是否已在列表中
|
||
if [[ "$port_list" != *",$port,"* ]]; then
|
||
unique_ports+=("$port")
|
||
port_list="$port_list,$port,"
|
||
fi
|
||
done
|
||
ports=("${unique_ports[@]}")
|
||
fi
|
||
|
||
# 如果找到了端口,关闭防火墙规则
|
||
if [[ ${#ports[@]} -gt 0 ]]; then
|
||
local port_list=$(printf ", %s" "${ports[@]}")
|
||
port_list=${port_list:2} # 移除开头的逗号和空格
|
||
log_info "找到端口: $port_list,尝试关闭 UFW 防火墙规则"
|
||
|
||
local closed_ports=()
|
||
for port in "${ports[@]}"; do
|
||
# 检查端口是否已开放在UFW中
|
||
if ! ufw status | grep -q "$port/tcp"; then
|
||
log_info "端口 $port 未在 UFW 中开放,跳过"
|
||
continue
|
||
fi
|
||
|
||
echo -n "关闭 UFW 防火墙端口 $port... "
|
||
if ufw delete allow "$port/tcp" &>/dev/null; then
|
||
echo -e "${GREEN}完成${NC}"
|
||
closed_ports+=("$port")
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_warn "无法关闭端口 $port"
|
||
fi
|
||
done
|
||
|
||
# 打印关闭的端口
|
||
if [[ ${#closed_ports[@]} -gt 0 ]]; then
|
||
local closed_list=$(printf ", %s" "${closed_ports[@]}")
|
||
closed_list=${closed_list:2} # 移除开头的逗号和空格
|
||
log_info "已关闭 UFW 防火墙中的端口: $closed_list"
|
||
else
|
||
log_warn "没有关闭任何端口"
|
||
fi
|
||
else
|
||
log_warn "无法确定之前使用的端口,跳过防火墙规则关闭"
|
||
fi
|
||
}
|
||
|
||
# 删除 Xray 文件
|
||
remove_xray_files() {
|
||
log_info "删除 Xray 文件"
|
||
|
||
# 删除二进制文件
|
||
echo -n "删除 Xray 核心文件... "
|
||
if rm -f "$XRAY_PATH/xray" "$XRAY_PATH/geoip.dat" "$XRAY_PATH/geosite.dat"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${YELLOW}部分文件未能删除${NC}"
|
||
log_warn "部分 Xray 核心文件可能未能完全删除"
|
||
fi
|
||
|
||
# 删除配置目录(不删除备份)
|
||
echo -n "删除 Xray 配置目录... "
|
||
if [[ -d "$CONFIG_PATH" ]]; then
|
||
if rm -rf "$CONFIG_PATH"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_warn "无法删除配置目录 $CONFIG_PATH"
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}配置目录不存在${NC}"
|
||
fi
|
||
|
||
# 删除日志目录
|
||
echo -n "删除 Xray 日志目录... "
|
||
if [[ -d "$LOG_PATH" ]]; then
|
||
if rm -rf "$LOG_PATH"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_warn "无法删除日志目录 $LOG_PATH"
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}日志目录不存在${NC}"
|
||
fi
|
||
|
||
# 删除服务文件
|
||
echo -n "删除 Xray 服务文件... "
|
||
if rm -f /etc/systemd/system/xray.service; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
systemctl daemon-reload
|
||
else
|
||
echo -e "${YELLOW}服务文件不存在或无法删除${NC}"
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# 完整卸载流程
|
||
uninstall_xray() {
|
||
clear
|
||
echo "=================================================="
|
||
echo -e "${RED}开始卸载 Xray${NC}"
|
||
echo "=================================================="
|
||
|
||
# 确认卸载
|
||
echo -e "${YELLOW}警告: 这将卸载 Xray 并删除相关文件${NC}"
|
||
read -rp "是否继续? [y/N] " confirm
|
||
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
|
||
log_info "卸载已取消"
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 0
|
||
fi
|
||
|
||
# 检查 root 权限
|
||
check_root
|
||
|
||
# 备份配置
|
||
backup_config
|
||
|
||
# 停止服务
|
||
stop_xray_service
|
||
|
||
# 关闭防火墙端口
|
||
close_firewall_port
|
||
|
||
# 删除文件
|
||
remove_xray_files
|
||
|
||
echo ""
|
||
log_info "Xray 卸载完成"
|
||
|
||
# 直接删除备份文件,不询问用户
|
||
if [[ -n "$CONFIG_BACKUP" && -d "$CONFIG_BACKUP" ]]; then
|
||
rm -rf "$CONFIG_BACKUP"
|
||
log_info "备份文件已删除"
|
||
fi
|
||
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
}
|
||
|
||
# 检查 Xray 状态
|
||
check_status() {
|
||
clear
|
||
echo "=================================================="
|
||
echo -e "${BLUE}Xray 状态检查${NC}"
|
||
echo "=================================================="
|
||
|
||
# 检查是否安装
|
||
if [[ ! -f "$XRAY_PATH/xray" ]]; then
|
||
echo -e "${RED}Xray 未安装${NC}"
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 0
|
||
fi
|
||
|
||
# 检查版本
|
||
echo -n "Xray 版本: "
|
||
$XRAY_PATH/xray version | head -n 1
|
||
|
||
# 检查服务状态
|
||
echo -n "服务状态: "
|
||
if systemctl is-active xray &>/dev/null; then
|
||
echo -e "${GREEN}运行中${NC}"
|
||
else
|
||
echo -e "${RED}未运行${NC}"
|
||
fi
|
||
|
||
echo -n "自启动状态: "
|
||
if systemctl is-enabled xray &>/dev/null; then
|
||
echo -e "${GREEN}已启用${NC}"
|
||
else
|
||
echo -e "${RED}未启用${NC}"
|
||
fi
|
||
|
||
# 获取内存和 CPU 使用情况
|
||
echo "资源使用情况:"
|
||
ps -aux | grep xray | grep -v grep | awk '{print "内存使用: " $4 "%, CPU使用: " $3 "%"}'
|
||
|
||
# 检查端口
|
||
if [[ -f "$CONFIG_PATH/config.json" ]]; then
|
||
local current_port=""
|
||
if command -v jq &>/dev/null; then
|
||
current_port=$(jq '.inbounds[0].port' "$CONFIG_PATH/config.json" 2>/dev/null)
|
||
else
|
||
current_port=$(grep -o '"port": [0-9]*' "$CONFIG_PATH/config.json" | head -1 | awk '{print $2}')
|
||
fi
|
||
|
||
if [[ -n "$current_port" && "$current_port" != "null" ]]; then
|
||
echo -n "端口 $current_port 状态: "
|
||
if command -v ss &>/dev/null; then
|
||
if ss -tuln | grep -q ":$current_port "; then
|
||
echo -e "${GREEN}已开放${NC}"
|
||
else
|
||
echo -e "${RED}未开放${NC}"
|
||
fi
|
||
elif command -v netstat &>/dev/null; then
|
||
if netstat -tuln | grep -q ":$current_port "; then
|
||
echo -e "${GREEN}已开放${NC}"
|
||
else
|
||
echo -e "${RED}未开放${NC}"
|
||
fi
|
||
else
|
||
echo -e "${YELLOW}无法检查${NC}"
|
||
fi
|
||
|
||
# 显示链接数
|
||
echo "当前连接:"
|
||
if command -v ss &>/dev/null; then
|
||
ss -tn | grep ":$current_port" | wc -l | awk '{print "活跃连接数: " $1}'
|
||
elif command -v netstat &>/dev/null; then
|
||
netstat -tn | grep ":$current_port" | wc -l | awk '{print "活跃连接数: " $1}'
|
||
else
|
||
echo "无法获取连接信息"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
echo -e "\n最近的日志:"
|
||
if [[ -f "$LOG_PATH/error.log" ]]; then
|
||
tail -n 10 "$LOG_PATH/error.log"
|
||
else
|
||
echo "找不到错误日志文件"
|
||
fi
|
||
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
}
|
||
|
||
# 显示配置信息
|
||
show_config() {
|
||
clear
|
||
echo "=================================================="
|
||
echo -e "${CYAN}Xray 配置信息${NC}"
|
||
echo "=================================================="
|
||
|
||
# 检查是否已安装
|
||
if [[ ! -f "$XRAY_PATH/xray" ]]; then
|
||
echo -e "${RED}Xray 未安装${NC}"
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 0
|
||
fi
|
||
|
||
# 显示配置文件内容
|
||
if [[ -f "$CONFIG_PATH/config.json" ]]; then
|
||
if command -v jq &>/dev/null; then
|
||
echo "配置信息 (美化格式):"
|
||
jq . "$CONFIG_PATH/config.json"
|
||
else
|
||
echo "配置文件内容:"
|
||
cat "$CONFIG_PATH/config.json"
|
||
fi
|
||
else
|
||
echo -e "${RED}找不到配置文件${NC}"
|
||
fi
|
||
|
||
# 显示客户端信息
|
||
if [[ -f "/root/xray_info.txt" ]]; then
|
||
echo -e "\n客户端信息:"
|
||
cat /root/xray_info.txt
|
||
else
|
||
echo -e "\n${RED}找不到客户端信息文件${NC}"
|
||
fi
|
||
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
}
|
||
|
||
# 重启 Xray 服务
|
||
restart_service() {
|
||
clear
|
||
echo "=================================================="
|
||
echo -e "${GREEN}重启 Xray 服务${NC}"
|
||
echo "=================================================="
|
||
|
||
# 检查是否已安装
|
||
if [[ ! -f "$XRAY_PATH/xray" ]]; then
|
||
echo -e "${RED}Xray 未安装${NC}"
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 0
|
||
fi
|
||
|
||
echo -n "重启 Xray 服务... "
|
||
if systemctl restart xray; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
log_info "Xray 服务已重启"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "无法重启 Xray 服务"
|
||
fi
|
||
|
||
# 检查服务状态
|
||
echo -n "Xray 服务状态: "
|
||
if systemctl is-active xray &>/dev/null; then
|
||
echo -e "${GREEN}运行中${NC}"
|
||
else
|
||
echo -e "${RED}未运行${NC}"
|
||
fi
|
||
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
}
|
||
|
||
# 更新 Xray
|
||
update_xray() {
|
||
clear
|
||
echo "=================================================="
|
||
echo -e "${YELLOW}更新 Xray${NC}"
|
||
echo "=================================================="
|
||
|
||
# 检查是否已安装
|
||
if [[ ! -f "$XRAY_PATH/xray" ]]; then
|
||
echo -e "${RED}Xray 未安装,请先安装${NC}"
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 0
|
||
fi
|
||
|
||
# 获取当前版本
|
||
local current_version
|
||
current_version=$($XRAY_PATH/xray version | head -n 1 | cut -d ' ' -f 2)
|
||
echo "当前版本: $current_version"
|
||
|
||
# 获取最新版本
|
||
echo -n "获取最新版本... "
|
||
local latest_version=$(get_latest_version)
|
||
echo -e "${GREEN}$latest_version${NC}"
|
||
|
||
# 比较版本
|
||
if [[ "$current_version" == "$latest_version" ]]; then
|
||
echo -e "${GREEN}已经是最新版本${NC}"
|
||
read -rp "是否强制更新? [y/N] " confirm
|
||
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
|
||
log_info "更新已取消"
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
# 备份配置
|
||
backup_config
|
||
|
||
# 停止服务
|
||
echo -n "停止 Xray 服务... "
|
||
if systemctl stop xray &>/dev/null; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_warn "无法停止 Xray 服务,将尝试继续更新"
|
||
fi
|
||
|
||
# 下载新版本
|
||
log_info "开始下载新版本"
|
||
|
||
# 创建临时目录
|
||
local tmp_dir="/tmp/xray_update"
|
||
mkdir -p "$tmp_dir"
|
||
|
||
# 确定系统架构
|
||
local arch
|
||
case $(uname -m) in
|
||
x86_64|amd64) arch="64" ;;
|
||
armv7l|armv8l) arch="arm32-v7a" ;;
|
||
aarch64) arch="arm64-v8a" ;;
|
||
*) arch="64" ;; # 默认使用64位版本
|
||
esac
|
||
|
||
# 构建下载URL
|
||
local download_url="https://github.com/XTLS/Xray-core/releases/download/$latest_version/Xray-linux-$arch.zip"
|
||
log_info "下载链接: $download_url"
|
||
|
||
# 下载Xray
|
||
echo -n "下载 Xray... "
|
||
if wget -q --show-progress -O "$tmp_dir/xray.zip" "$download_url"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "下载 Xray 失败"
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 1
|
||
fi
|
||
|
||
# 解压文件
|
||
echo -n "解压 Xray... "
|
||
if unzip -q -o "$tmp_dir/xray.zip" -d "$tmp_dir"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "解压 Xray 失败"
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 1
|
||
fi
|
||
|
||
# 备份旧的二进制文件
|
||
mv "$XRAY_PATH/xray" "$XRAY_PATH/xray.old" 2>/dev/null
|
||
|
||
# 复制新文件
|
||
echo -n "更新 Xray 核心文件... "
|
||
if cp "$tmp_dir/xray" "$XRAY_PATH/xray" && chmod +x "$XRAY_PATH/xray"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "更新 Xray 核心文件失败"
|
||
|
||
# 恢复旧文件
|
||
if [[ -f "$XRAY_PATH/xray.old" ]]; then
|
||
mv "$XRAY_PATH/xray.old" "$XRAY_PATH/xray"
|
||
log_warn "已恢复为旧版本"
|
||
fi
|
||
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 1
|
||
fi
|
||
|
||
# 更新 geoip.dat 和 geosite.dat
|
||
echo -n "更新 GeoIP 和 GeoSite 数据... "
|
||
if cp "$tmp_dir/geoip.dat" "$XRAY_PATH/geoip.dat" && \
|
||
cp "$tmp_dir/geosite.dat" "$XRAY_PATH/geosite.dat"; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${YELLOW}失败${NC}"
|
||
log_warn "更新 GeoIP 和 GeoSite 数据失败,但不影响核心功能"
|
||
fi
|
||
|
||
# 删除旧备份和临时文件
|
||
rm -f "$XRAY_PATH/xray.old"
|
||
rm -rf "$tmp_dir"
|
||
|
||
# 启动服务
|
||
echo -n "启动 Xray 服务... "
|
||
if systemctl start xray; then
|
||
echo -e "${GREEN}成功${NC}"
|
||
else
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "启动 Xray 服务失败,请检查配置"
|
||
systemctl status xray
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 1
|
||
fi
|
||
|
||
# 检查更新后的版本
|
||
local new_version
|
||
new_version=$($XRAY_PATH/xray version | head -n 1 | cut -d ' ' -f 2)
|
||
|
||
log_info "Xray 更新完成,版本: $new_version"
|
||
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
}
|
||
|
||
# 更新脚本版本信息
|
||
update_script_version() {
|
||
clear
|
||
echo "=================================================="
|
||
echo -e "${GREEN}更新脚本版本信息${NC}"
|
||
echo "=================================================="
|
||
|
||
# 显示当前脚本版本
|
||
echo "当前脚本版本: $SCRIPT_VERSION"
|
||
|
||
# 获取最新脚本版本
|
||
echo -n "获取最新脚本版本... "
|
||
local latest_version
|
||
latest_version=$(curl -s https://raw.githubusercontent.com/XTLS/Xray-core/main/xray-manager.sh | grep -o 'SCRIPT_VERSION="[^"]*"' | cut -d'"' -f2)
|
||
|
||
if [[ -z "$latest_version" ]]; then
|
||
echo -e "${RED}失败${NC}"
|
||
log_error "无法获取最新脚本版本信息"
|
||
read -rp "是否继续更新? [y/N] " confirm
|
||
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
|
||
log_info "更新已取消"
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 0
|
||
fi
|
||
latest_version="1.0.0" # 设置一个固定版本作为备用
|
||
log_info "将使用固定版本: $latest_version"
|
||
else
|
||
echo -e "${GREEN}$latest_version${NC}"
|
||
|
||
# 比较版本
|
||
if [[ "$SCRIPT_VERSION" == "$latest_version" ]]; then
|
||
echo -e "${GREEN}已经是最新版本${NC}"
|
||
read -rp "是否强制更新? [y/N] " confirm
|
||
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
|
||
log_info "更新已取消"
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
return 0
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# 更新脚本版本
|
||
echo -n "更新脚本版本... "
|
||
sed -i "s/SCRIPT_VERSION=\"[^\"]*\"/SCRIPT_VERSION=\"$latest_version\"/" "$0"
|
||
echo -e "${GREEN}成功${NC}"
|
||
|
||
log_info "脚本版本已更新为 $latest_version"
|
||
|
||
read -rp "按回车键返回主菜单..." temp
|
||
show_menu
|
||
}
|
||
|
||
# 主函数
|
||
main() {
|
||
# 处理命令行参数
|
||
if [[ $# -gt 0 ]]; then
|
||
case "$1" in
|
||
-h|--help)
|
||
show_help
|
||
exit 0
|
||
;;
|
||
-i|--install)
|
||
check_root
|
||
install_xray
|
||
exit 0
|
||
;;
|
||
-u|--uninstall)
|
||
check_root
|
||
uninstall_xray
|
||
exit 0
|
||
;;
|
||
-s|--status)
|
||
check_root
|
||
check_status
|
||
exit 0
|
||
;;
|
||
-up|--update)
|
||
check_root
|
||
update_xray
|
||
exit 0
|
||
;;
|
||
*)
|
||
log_error "未知参数: $1"
|
||
show_help
|
||
exit 1
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
# 无参数则显示菜单
|
||
check_root
|
||
show_menu
|
||
}
|
||
|
||
# 执行主函数
|
||
main "$@" |