Files
script/xray-manager.sh
eddy ce229fae0b 1
2025-11-13 00:54:10 +08:00

1964 lines
57 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 "$@"