mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-08-23 11:26:52 +00:00
![google-labs-jules[bot]](/assets/img/avatar_default.png)
This commit introduces a multi-server architecture to the Sanai panel, allowing you to manage clients across multiple servers from a central panel. Key changes include: - **Database Schema:** Added a `servers` table to store information about slave servers. - **Server Management:** Implemented a new service and controller (`MultiServerService` and `MultiServerController`) for CRUD operations on servers. - **Web UI:** Created a new web page for managing servers, accessible from the sidebar. - **Client Synchronization:** Modified the `InboundService` to synchronize client additions, updates, and deletions across all active slave servers via a REST API. - **API Security:** Added an API key authentication middleware to secure the communication between the master and slave panels. - **Multi-Server Subscriptions:** Updated the subscription service to generate links that include configurations for all active servers. - **Installation Script:** Modified the `install.sh` script to generate a random API key during installation. **Known Issues:** - The integration test for client synchronization (`TestInboundServiceSync`) is currently failing. It seems that the API request to the mock slave server is not being sent correctly or the API key is not being included in the request header. Further investigation is needed to resolve this issue.
245 lines
10 KiB
Bash
245 lines
10 KiB
Bash
#!/bin/bash
|
|
|
|
red='\033[0;31m'
|
|
green='\033[0;32m'
|
|
blue='\033[0;34m'
|
|
yellow='\033[0;33m'
|
|
plain='\033[0m'
|
|
|
|
cur_dir=$(pwd)
|
|
show_ip_service_lists=("https://api.ipify.org" "https://4.ident.me")
|
|
|
|
# check root
|
|
[[ $EUID -ne 0 ]] && echo -e "${red}Fatal error: ${plain} Please run this script with root privilege \n " && exit 1
|
|
|
|
# Check OS and set release variable
|
|
if [[ -f /etc/os-release ]]; then
|
|
source /etc/os-release
|
|
release=$ID
|
|
elif [[ -f /usr/lib/os-release ]]; then
|
|
source /usr/lib/os-release
|
|
release=$ID
|
|
else
|
|
echo "Failed to check the system OS, please contact the author!" >&2
|
|
exit 1
|
|
fi
|
|
echo "The OS release is: $release"
|
|
|
|
arch() {
|
|
case "$(uname -m)" in
|
|
x86_64 | x64 | amd64) echo 'amd64' ;;
|
|
i*86 | x86) echo '386' ;;
|
|
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
|
|
armv7* | armv7 | arm) echo 'armv7' ;;
|
|
armv6* | armv6) echo 'armv6' ;;
|
|
armv5* | armv5) echo 'armv5' ;;
|
|
s390x) echo 's390x' ;;
|
|
*) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;;
|
|
esac
|
|
}
|
|
|
|
echo "Arch: $(arch)"
|
|
|
|
check_glibc_version() {
|
|
glibc_version=$(ldd --version | head -n1 | awk '{print $NF}')
|
|
|
|
required_version="2.32"
|
|
if [[ "$(printf '%s\n' "$required_version" "$glibc_version" | sort -V | head -n1)" != "$required_version" ]]; then
|
|
echo -e "${red}GLIBC version $glibc_version is too old! Required: 2.32 or higher${plain}"
|
|
echo "Please upgrade to a newer version of your operating system to get a higher GLIBC version."
|
|
exit 1
|
|
fi
|
|
echo "GLIBC version: $glibc_version (meets requirement of 2.32+)"
|
|
}
|
|
check_glibc_version
|
|
|
|
install_base() {
|
|
case "${release}" in
|
|
ubuntu | debian | armbian)
|
|
apt-get update && apt-get install -y -q wget curl tar tzdata
|
|
;;
|
|
centos | rhel | almalinux | rocky | ol)
|
|
yum -y update && yum install -y -q wget curl tar tzdata
|
|
;;
|
|
fedora | amzn | virtuozzo)
|
|
dnf -y update && dnf install -y -q wget curl tar tzdata
|
|
;;
|
|
arch | manjaro | parch)
|
|
pacman -Syu && pacman -Syu --noconfirm wget curl tar tzdata
|
|
;;
|
|
opensuse-tumbleweed)
|
|
zypper refresh && zypper -q install -y wget curl tar timezone
|
|
;;
|
|
*)
|
|
apt-get update && apt install -y -q wget curl tar tzdata
|
|
;;
|
|
esac
|
|
}
|
|
|
|
gen_random_string() {
|
|
local length="$1"
|
|
local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' </dev/urandom | fold -w "$length" | head -n 1)
|
|
echo "$random_string"
|
|
}
|
|
|
|
config_after_install() {
|
|
local existing_hasDefaultCredential=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'hasDefaultCredential: .+' | awk '{print $2}')
|
|
local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
|
local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
|
|
|
for ip_service_addr in "${show_ip_service_lists[@]}"; do
|
|
local server_ip=$(curl -s --max-time 3 ${ip_service_addr} 2>/dev/null)
|
|
if [ -n "${server_ip}" ]; then
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ ${#existing_webBasePath} -lt 4 ]]; then
|
|
if [[ "$existing_hasDefaultCredential" == "true" ]]; then
|
|
local config_webBasePath=$(gen_random_string 18)
|
|
local config_username=$(gen_random_string 10)
|
|
local config_password=$(gen_random_string 10)
|
|
|
|
read -rp "Would you like to customize the Panel Port settings? (If not, a random port will be applied) [y/n]: " config_confirm
|
|
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
|
read -rp "Please set up the panel port: " config_port
|
|
echo -e "${yellow}Your Panel Port is: ${config_port}${plain}"
|
|
else
|
|
local config_port=$(shuf -i 1024-62000 -n 1)
|
|
echo -e "${yellow}Generated random port: ${config_port}${plain}"
|
|
fi
|
|
|
|
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}"
|
|
echo -e "This is a fresh installation, generating random login info for security concerns:"
|
|
echo -e "###############################################"
|
|
echo -e "${green}Username: ${config_username}${plain}"
|
|
echo -e "${green}Password: ${config_password}${plain}"
|
|
echo -e "${green}Port: ${config_port}${plain}"
|
|
echo -e "${green}WebBasePath: ${config_webBasePath}${plain}"
|
|
echo -e "${green}Access URL: http://${server_ip}:${config_port}/${config_webBasePath}${plain}"
|
|
echo -e "###############################################"
|
|
else
|
|
local config_webBasePath=$(gen_random_string 18)
|
|
echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}"
|
|
/usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}"
|
|
echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
|
|
echo -e "${green}Access URL: http://${server_ip}:${existing_port}/${config_webBasePath}${plain}"
|
|
fi
|
|
else
|
|
if [[ "$existing_hasDefaultCredential" == "true" ]]; then
|
|
local config_username=$(gen_random_string 10)
|
|
local config_password=$(gen_random_string 10)
|
|
|
|
echo -e "${yellow}Default credentials detected. Security update required...${plain}"
|
|
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}"
|
|
echo -e "Generated new random login credentials:"
|
|
echo -e "###############################################"
|
|
echo -e "${green}Username: ${config_username}${plain}"
|
|
echo -e "${green}Password: ${config_password}${plain}"
|
|
echo -e "###############################################"
|
|
else
|
|
echo -e "${green}Username, Password, and WebBasePath are properly set. Exiting...${plain}"
|
|
fi
|
|
fi
|
|
|
|
/usr/local/x-ui/x-ui migrate
|
|
|
|
local existing_apiKey=$(/usr/local/x-ui/x-ui setting -show true | grep -oP 'ApiKey: \K.*')
|
|
if [[ -z "$existing_apiKey" ]]; then
|
|
local config_apiKey=$(gen_random_string 32)
|
|
/usr/local/x-ui/x-ui setting -apiKey "${config_apiKey}"
|
|
echo -e "${green}Generated random API Key: ${config_apiKey}${plain}"
|
|
fi
|
|
}
|
|
|
|
install_x-ui() {
|
|
cd /usr/local/
|
|
|
|
# Download resources
|
|
if [ $# == 0 ]; then
|
|
tag_version=$(curl -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
|
if [[ ! -n "$tag_version" ]]; then
|
|
echo -e "${red}Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later${plain}"
|
|
exit 1
|
|
fi
|
|
echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
|
|
wget -N -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz
|
|
if [[ $? -ne 0 ]]; then
|
|
echo -e "${red}Downloading x-ui failed, please be sure that your server can access GitHub ${plain}"
|
|
exit 1
|
|
fi
|
|
else
|
|
tag_version=$1
|
|
tag_version_numeric=${tag_version#v}
|
|
min_version="2.3.5"
|
|
|
|
if [[ "$(printf '%s\n' "$min_version" "$tag_version_numeric" | sort -V | head -n1)" != "$min_version" ]]; then
|
|
echo -e "${red}Please use a newer version (at least v2.3.5). Exiting installation.${plain}"
|
|
exit 1
|
|
fi
|
|
|
|
url="https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz"
|
|
echo -e "Beginning to install x-ui $1"
|
|
wget -N -O /usr/local/x-ui-linux-$(arch).tar.gz ${url}
|
|
if [[ $? -ne 0 ]]; then
|
|
echo -e "${red}Download x-ui $1 failed, please check if the version exists ${plain}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
wget -O /usr/bin/x-ui-temp https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
|
|
|
|
# Stop x-ui service and remove old resources
|
|
if [[ -e /usr/local/x-ui/ ]]; then
|
|
systemctl stop x-ui
|
|
rm /usr/local/x-ui/ -rf
|
|
fi
|
|
|
|
# Extract resources and set permissions
|
|
tar zxvf x-ui-linux-$(arch).tar.gz
|
|
rm x-ui-linux-$(arch).tar.gz -f
|
|
|
|
cd x-ui
|
|
chmod +x x-ui
|
|
chmod +x x-ui.sh
|
|
|
|
# Check the system's architecture and rename the file accordingly
|
|
if [[ $(arch) == "armv5" || $(arch) == "armv6" || $(arch) == "armv7" ]]; then
|
|
mv bin/xray-linux-$(arch) bin/xray-linux-arm
|
|
chmod +x bin/xray-linux-arm
|
|
fi
|
|
chmod +x x-ui bin/xray-linux-$(arch)
|
|
|
|
# Update x-ui cli and se set permission
|
|
mv -f /usr/bin/x-ui-temp /usr/bin/x-ui
|
|
chmod +x /usr/bin/x-ui
|
|
config_after_install
|
|
|
|
cp -f x-ui.service /etc/systemd/system/
|
|
systemctl daemon-reload
|
|
systemctl enable x-ui
|
|
systemctl start x-ui
|
|
echo -e "${green}x-ui ${tag_version}${plain} installation finished, it is running now..."
|
|
echo -e ""
|
|
echo -e "┌───────────────────────────────────────────────────────┐
|
|
│ ${blue}x-ui control menu usages (subcommands):${plain} │
|
|
│ │
|
|
│ ${blue}x-ui${plain} - Admin Management Script │
|
|
│ ${blue}x-ui start${plain} - Start │
|
|
│ ${blue}x-ui stop${plain} - Stop │
|
|
│ ${blue}x-ui restart${plain} - Restart │
|
|
│ ${blue}x-ui status${plain} - Current Status │
|
|
│ ${blue}x-ui settings${plain} - Current Settings │
|
|
│ ${blue}x-ui enable${plain} - Enable Autostart on OS Startup │
|
|
│ ${blue}x-ui disable${plain} - Disable Autostart on OS Startup │
|
|
│ ${blue}x-ui log${plain} - Check logs │
|
|
│ ${blue}x-ui banlog${plain} - Check Fail2ban ban logs │
|
|
│ ${blue}x-ui update${plain} - Update │
|
|
│ ${blue}x-ui legacy${plain} - legacy version │
|
|
│ ${blue}x-ui install${plain} - Install │
|
|
│ ${blue}x-ui uninstall${plain} - Uninstall │
|
|
└───────────────────────────────────────────────────────┘"
|
|
}
|
|
|
|
echo -e "${green}Running...${plain}"
|
|
install_base
|
|
install_x-ui $1
|