From 91af54abf10f8ee7352689e3bcd31ecf355b18e3 Mon Sep 17 00:00:00 2001
From: emirjorge <117597846+emirjorge@users.noreply.github.com>
Date: Thu, 18 Jan 2024 01:59:43 -0500
Subject: [PATCH 01/26] Fix Spanish Translation (#1619)
---
web/translation/translate.es_ES.toml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml
index fb22b8a1..42a3a2b2 100644
--- a/web/translation/translate.es_ES.toml
+++ b/web/translation/translate.es_ES.toml
@@ -1,6 +1,6 @@
"username" = "Nombre de Usuario"
"password" = "Contraseña"
-"login" = "Acceso"
+"login" = "Acceder"
"confirm" = "Confirmar"
"cancel" = "Cancelar"
"close" = "Cerrar"
@@ -59,10 +59,10 @@
"settings" = "Configuraciones"
"xray" = "Configuración Xray"
"logout" = "Cerrar Sesión"
-"link" = "Otro"
+"link" = "Gestionar"
[pages.login]
-"title" = "Grata"
+"title" = "Bienvenido"
"loginAgain" = "El límite de tiempo de inicio de sesión ha expirado. Por favor, inicia sesión nuevamente."
[pages.login.toasts]
From 589a8702aad5f882e3df261cc397a966ee09f674 Mon Sep 17 00:00:00 2001
From: Shahin <115543613+shahin-io@users.noreply.github.com>
Date: Thu, 18 Jan 2024 07:00:38 +0000
Subject: [PATCH 02/26] Revert-back some edits (#1617)
* Update index.html
* Update translate.en_US.toml
---
web/html/xui/index.html | 2 +-
web/translation/translate.en_US.toml | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/web/html/xui/index.html b/web/html/xui/index.html
index d3357a4d..3c01a29f 100644
--- a/web/html/xui/index.html
+++ b/web/html/xui/index.html
@@ -93,7 +93,7 @@
{{ i18n "pages.index.xrayStatus" }}:
- [[ status.xray.state.toUpperCase() ]]
+ [[ status.xray.state ]]
An error occurred while running Xray
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index b8fdcde1..2f1c0544 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -54,11 +54,11 @@
"security" = "Security"
[menu]
-"dashboard" = "OVERVIEW"
-"inbounds" = "INBOUNDS"
-"settings" = "PANEL SETTINGS"
-"xray" = "XRAY CONFIGS"
-"logout" = "LOG OUT"
+"dashboard" = "Overview"
+"inbounds" = "Inbounds"
+"settings" = "Panel Settings"
+"xray" = "Xray Configs"
+"logout" = "Log Out"
"link" = "Manage"
[pages.login]
@@ -319,7 +319,7 @@
"ipv4Configs" = "IPv4 Routing"
"ipv4ConfigsDesc" = "These options will route traffic based on a specific destination via IPv4."
"warpConfigs" = "WARP Routing"
-"warpConfigsDesc" = "These options will route traffic based on a specific destination via WARP. (follow the guide on the Panel’s GitHub)"
+"warpConfigsDesc" = "These options will route traffic based on a specific destination via WARP."
"Template" = "Advanced Xray Configuration Template"
"TemplateDesc" = "The final Xray config file will be generated based on this template."
"FreedomStrategy" = "Freedom Protocol Strategy"
From 7fd4015f174e4c529612dfeee534fb1f2842ba7d Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Fri, 19 Jan 2024 18:28:09 +0330
Subject: [PATCH 03/26] Update x-ui.sh
Fail2ban installation failed
---
x-ui.sh | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/x-ui.sh b/x-ui.sh
index d245a247..2807bc4f 100644
--- a/x-ui.sh
+++ b/x-ui.sh
@@ -991,6 +991,12 @@ install_iplimit() {
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
exit 1 ;;
esac
+
+ if ! command -v fail2ban-client &>/dev/null; then
+ echo -e "${red}Fail2ban installation failed.${plain}\n"
+ exit 1
+ fi
+
echo -e "${green}Fail2ban installed successfully!${plain}\n"
else
echo -e "${yellow}Fail2ban is already installed.${plain}\n"
From 74ba6d7825741fe7965c91e5ccd8ae6d0436fe59 Mon Sep 17 00:00:00 2001
From: quydang <81231812+quydang04@users.noreply.github.com>
Date: Sat, 20 Jan 2024 16:32:35 +0700
Subject: [PATCH 04/26] Add support for ARMv5 and x86 (#1642)
---
.github/workflows/docker.yml | 4 ++--
.github/workflows/release.yml | 31 ++++++++++++++++++++++++++--
DockerInit.sh | 10 ++++++++-
install.sh | 8 +++++--
web/translation/translate.vi_VN.toml | 4 ++--
5 files changed, 48 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index b2dc0ca4..6afe8618 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -1,4 +1,4 @@
-name: Release 3X-UI dockerhub
+name: Release 3X-UI for Docker
on:
push:
tags:
@@ -36,6 +36,6 @@ jobs:
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
- platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6
+ platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386, linux/arm/v5
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 91a7e093..62bc6e7c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -15,6 +15,8 @@ jobs:
- arm64
- armv7
- armv6
+ - 386
+ - armv5
runs-on: ubuntu-20.04
steps:
- name: Checkout repository
@@ -30,8 +32,14 @@ jobs:
sudo apt-get update
if [ "${{ matrix.platform }}" == "arm64" ]; then
sudo apt install gcc-aarch64-linux-gnu
- elif [ "${{ matrix.platform }}" == "armv7" ] || [ "${{ matrix.platform }}" == "armv6" ]; then
+ elif [ "${{ matrix.platform }}" == "armv7" ]; then
sudo apt install gcc-arm-linux-gnueabihf
+ elif [ "${{ matrix.platform }}" == "armv6" ]; then
+ sudo apt install gcc-arm-linux-gnueabihf
+ elif [ "${{ matrix.platform }}" == "386" ]; then
+ sudo apt install gcc-i686-linux-gnu
+ elif [ "${{ matrix.platform }}" == "armv5" ]; then
+ sudo apt install gcc-arm-linux-gnueabi
fi
- name: Build x-ui
@@ -42,10 +50,21 @@ jobs:
if [ "${{ matrix.platform }}" == "arm64" ]; then
export GOARCH=arm64
export CC=aarch64-linux-gnu-gcc
- elif [ "${{ matrix.platform }}" == "armv7" ] || [ "${{ matrix.platform }}" == "armv6" ]; then
+ elif [ "${{ matrix.platform }}" == "armv7" ]; then
export GOARCH=arm
export GOARM=7
export CC=arm-linux-gnueabihf-gcc
+ elif [ "${{ matrix.platform }}" == "armv6" ]; then
+ export GOARCH=arm
+ export GOARM=6
+ export CC=arm-linux-gnueabihf-gcc
+ elif [ "${{ matrix.platform }}" == "386" ]; then
+ export GOARCH=386
+ export CC=i686-linux-gnu-gcc
+ elif [ "${{ matrix.platform }}" == "armv5" ]; then
+ export GOARCH=arm
+ export GOARM=5
+ export CC=arm-linux-gnueabi-gcc
fi
go build -o xui-release -v main.go
@@ -75,6 +94,14 @@ jobs:
wget ${Xray_URL}Xray-linux-arm32-v6.zip
unzip Xray-linux-arm32-v6.zip
rm -f Xray-linux-arm32-v6.zip
+ elif [ "${{ matrix.platform }}" == "386" ]; then
+ wget ${Xray_URL}Xray-linux-32.zip
+ unzip Xray-linux-32.zip
+ rm -f Xray-linux-32.zip
+ elif [ "${{ matrix.platform }}" == "armv5" ]; then
+ wget ${Xray_URL}Xray-linux-arm32-v5.zip
+ unzip Xray-linux-arm32-v5.zip
+ rm -f Xray-linux-arm32-v5.zip
fi
rm -f geoip.dat geosite.dat
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
diff --git a/DockerInit.sh b/DockerInit.sh
index c62dc77e..bbfcb04e 100755
--- a/DockerInit.sh
+++ b/DockerInit.sh
@@ -5,6 +5,10 @@ case $1 in
ARCH="64"
FNAME="amd64"
;;
+ i386)
+ ARCH="32"
+ FNAME="i386"
+ ;;
armv8 | arm64 | aarch64)
ARCH="arm64-v8a"
FNAME="arm64"
@@ -17,6 +21,10 @@ case $1 in
ARCH="arm32-v6"
FNAME="armv6"
;;
+ armv5)
+ ARCH="arm32-v5"
+ FNAME="armv5"
+ ;;
*)
ARCH="64"
FNAME="amd64"
@@ -29,7 +37,7 @@ cd build/bin
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.7/Xray-linux-${ARCH}.zip"
unzip "Xray-linux-${ARCH}.zip"
-rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat geoip_VN.dat geosite_VN.dat
+rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
mv xray "xray-linux-${FNAME}"
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
diff --git a/install.sh b/install.sh
index 8d4a0c0c..8fd42ab8 100644
--- a/install.sh
+++ b/install.sh
@@ -26,12 +26,15 @@ echo "The OS release is: $release"
arch3xui() {
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 | arm) echo 'armv6' ;;
+ armv6* | armv6) echo 'armv6' ;;
+ armv5* | armv5) echo 'armv5' ;;
*) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;;
esac
}
+
echo "arch: $(arch3xui)"
os_version=""
@@ -163,7 +166,8 @@ install_x-ui() {
chmod +x x-ui
# Check the system's architecture and rename the file accordingly
- if [[ $(arch3xui) == "armv6" || $(arch3xui) == "armv7" ]]; then
+ if [[ $(arch3xui) == "armv5" || $(arch3xui) == "armv6" || $(arch3xui) == "armv7" ]]; then
+
mv bin/xray-linux-$(arch3xui) bin/xray-linux-arm
chmod +x bin/xray-linux-arm
fi
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index d627254b..7491a70c 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -74,9 +74,9 @@
[pages.index]
"title" = "Trạng thái hệ thống"
-"memory" = "Bộ nhớ"
+"memory" = "Ram"
"hard" = "Dung lượng"
-"xrayStatus" = "Trạng thái"
+"xrayStatus" = "Trạng thái Xray"
"stopXray" = "Dừng lại"
"restartXray" = "Khởi động lại"
"xraySwitch" = "Phiên bản"
From 7f13adbd05b5a1fb5f71e99dcff41bb13814134f Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Sat, 20 Jan 2024 15:49:34 +0330
Subject: [PATCH 05/26] fail2ban - auto allowipv6
---
x-ui.sh | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/x-ui.sh b/x-ui.sh
index 2807bc4f..de7e1f9f 100644
--- a/x-ui.sh
+++ b/x-ui.sh
@@ -862,8 +862,11 @@ run_speedtest() {
}
create_iplimit_jails() {
- # Use default bantime if not passed => 5 minutes
- local bantime="${1:-5}"
+ # Use default bantime if not passed => 30 minutes
+ local bantime="${1:-30}"
+
+ # uncomment allowipv6 = auto in fail2ban.conf
+ sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
cat << EOF > /etc/fail2ban/jail.d/3x-ipl.conf
[3x-ipl]
From 38378fe36fbcaa8ca500fbc1fe1e8ea873dc248f Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Sat, 20 Jan 2024 16:38:54 +0330
Subject: [PATCH 06/26] update fail2ban v1.0.2 for debian
---
install.sh | 30 +++++++++++++++---------------
x-ui.sh | 19 ++++++++++++++-----
2 files changed, 29 insertions(+), 20 deletions(-)
diff --git a/install.sh b/install.sh
index 8fd42ab8..ae489c2a 100644
--- a/install.sh
+++ b/install.sh
@@ -81,19 +81,21 @@ fi
install_base() {
case "${release}" in
- centos|fedora|almalinux|rocky)
- yum -y update && yum install -y -q wget curl tar
- ;;
- arch|manjaro)
- pacman -Syu && pacman -Syu --noconfirm wget curl tar
- ;;
- *)
- apt-get update && apt install -y -q wget curl tar
- ;;
+ centos | almalinux | rocky)
+ yum -y update && yum install -y -q wget curl tar
+ ;;
+ fedora)
+ dnf -y update && dnf install -y -q wget curl tar
+ ;;
+ arch | manjaro)
+ pacman -Syu && pacman -Syu --noconfirm wget curl tar
+ ;;
+ *)
+ apt-get update && apt install -y -q wget curl tar
+ ;;
esac
}
-
# This function will be called when user installed x-ui out of security
config_after_install() {
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
@@ -167,11 +169,10 @@ install_x-ui() {
# Check the system's architecture and rename the file accordingly
if [[ $(arch3xui) == "armv5" || $(arch3xui) == "armv6" || $(arch3xui) == "armv7" ]]; then
-
- mv bin/xray-linux-$(arch3xui) bin/xray-linux-arm
- chmod +x bin/xray-linux-arm
+ mv bin/xray-linux-$(arch3xui) bin/xray-linux-arm
+ chmod +x bin/xray-linux-arm
fi
-
+
chmod +x x-ui bin/xray-linux-$(arch3xui)
cp -f x-ui.service /etc/systemd/system/
wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
@@ -201,7 +202,6 @@ install_x-ui() {
echo -e "----------------------------------------------"
}
-
echo -e "${green}Running...${plain}"
install_base
install_x-ui $1
diff --git a/x-ui.sh b/x-ui.sh
index de7e1f9f..af63b9b4 100644
--- a/x-ui.sh
+++ b/x-ui.sh
@@ -982,12 +982,16 @@ iplimit_main() {
install_iplimit() {
if ! command -v fail2ban-client &>/dev/null; then
echo -e "${green}Fail2ban is not installed. Installing now...!${plain}\n"
+
# Check the OS and install necessary packages
case "${release}" in
ubuntu|debian)
- apt update && apt install fail2ban -y ;;
+ wget -O fail2ban.deb https://github.com/fail2ban/fail2ban/releases/download/1.0.2/fail2ban_1.0.2-1.upstream1_all.deb
+ wget -O fail2ban.deb.asc https://github.com/fail2ban/fail2ban/releases/download/1.0.2/fail2ban_1.0.2-1.upstream1_all.deb.asc
+ dpkg -i fail2ban.deb ;;
centos|almalinux|rocky)
- yum -y update && yum -y install fail2ban ;;
+ yum update -y && yum install epel-release -y
+ yum -y install fail2ban ;;
fedora)
dnf -y update && dnf -y install fail2ban ;;
*)
@@ -1027,6 +1031,7 @@ install_iplimit() {
# Launching fail2ban
if ! systemctl is-active --quiet fail2ban; then
systemctl start fail2ban
+ systemctl enable fail2ban
else
systemctl restart fail2ban
fi
@@ -1054,11 +1059,15 @@ remove_iplimit(){
systemctl stop fail2ban
case "${release}" in
ubuntu|debian)
- apt-get purge fail2ban -y;;
+ apt-get remove -y fail2ban
+ apt-get purge -y fail2ban -y
+ apt-get autoremove -y;;
centos|almalinux|rocky)
- yum remove fail2ban -y;;
+ yum remove fail2ban -y
+ yum autoremove -y;;
fedora)
- dnf remove fail2ban -y;;
+ dnf remove fail2ban -y
+ dnf autoremove -y;;
*)
echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n"
exit 1 ;;
From a7bea936c536c560183e4577996e9a56b4cfd08a Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Sat, 20 Jan 2024 17:28:44 +0330
Subject: [PATCH 07/26] Update x-ui.sh
---
x-ui.sh | 366 ++++++++++++++++++++++++++++++--------------------------
1 file changed, 195 insertions(+), 171 deletions(-)
diff --git a/x-ui.sh b/x-ui.sh
index af63b9b4..cabb546f 100644
--- a/x-ui.sh
+++ b/x-ui.sh
@@ -70,13 +70,11 @@ elif [[ "${release}" == "armbian" ]]; then
echo "Your OS is Armbian"
fi
-
# Declare Variables
log_folder="${XUI_LOG_FOLDER:=/var/log}"
iplimit_log_path="${log_folder}/3xipl.log"
iplimit_banned_log_path="${log_folder}/3xipl-banned.log"
-
confirm() {
if [[ $# > 1 ]]; then
echo && read -p "$1 [Default $2]: " temp
@@ -140,7 +138,7 @@ custom_version() {
if [ -z "$panel_version" ]; then
echo "Panel version cannot be empty. Exiting."
- exit 1
+ exit 1
fi
download_link="https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh"
@@ -329,15 +327,15 @@ show_log() {
}
show_banlog() {
- if test -f "${iplimit_banned_log_path}"; then
- if [[ -s "${iplimit_banned_log_path}" ]]; then
- cat ${iplimit_banned_log_path}
+ if test -f "${iplimit_banned_log_path}"; then
+ if [[ -s "${iplimit_banned_log_path}" ]]; then
+ cat ${iplimit_banned_log_path}
+ else
+ echo -e "${red}Log file is empty.${plain}\n"
+ fi
else
- echo -e "${red}Log file is empty.${plain}\n"
+ echo -e "${red}Log file not found. Please Install Fail2ban and IP Limit first.${plain}\n"
fi
- else
- echo -e "${red}Log file not found. Please Install Fail2ban and IP Limit first.${plain}\n"
- fi
}
enable_bbr() {
@@ -348,19 +346,19 @@ enable_bbr() {
# Check the OS and install necessary packages
case "${release}" in
- ubuntu|debian)
- apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
- ;;
- centos|almalinux|rocky)
- yum -y update && yum -y install ca-certificates
- ;;
- fedora)
- dnf -y update && dnf -y install ca-certificates
- ;;
- *)
- echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
- exit 1
- ;;
+ ubuntu | debian)
+ apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
+ ;;
+ centos | almalinux | rocky)
+ yum -y update && yum -y install ca-certificates
+ ;;
+ fedora)
+ dnf -y update && dnf -y install ca-certificates
+ ;;
+ *)
+ echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
+ exit 1
+ ;;
esac
# Enable BBR
@@ -581,21 +579,24 @@ ssl_cert_issue_main() {
echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice
case "$choice" in
- 0)
- show_menu ;;
- 1)
- ssl_cert_issue ;;
- 2)
- local domain=""
- read -p "Please enter your domain name to revoke the certificate: " domain
- ~/.acme.sh/acme.sh --revoke -d ${domain}
- LOGI "Certificate revoked"
- ;;
- 3)
- local domain=""
- read -p "Please enter your domain name to forcefully renew an SSL certificate: " domain
- ~/.acme.sh/acme.sh --renew -d ${domain} --force ;;
- *) echo "Invalid choice" ;;
+ 0)
+ show_menu
+ ;;
+ 1)
+ ssl_cert_issue
+ ;;
+ 2)
+ local domain=""
+ read -p "Please enter your domain name to revoke the certificate: " domain
+ ~/.acme.sh/acme.sh --revoke -d ${domain}
+ LOGI "Certificate revoked"
+ ;;
+ 3)
+ local domain=""
+ read -p "Please enter your domain name to forcefully renew an SSL certificate: " domain
+ ~/.acme.sh/acme.sh --renew -d ${domain} --force
+ ;;
+ *) echo "Invalid choice" ;;
esac
}
@@ -611,15 +612,19 @@ ssl_cert_issue() {
fi
# install socat second
case "${release}" in
- ubuntu|debian|armbian)
- apt update && apt install socat -y ;;
- centos|almalinux|rocky)
- yum -y update && yum -y install socat ;;
- fedora)
- dnf -y update && dnf -y install socat ;;
- *)
- echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
- exit 1 ;;
+ ubuntu | debian | armbian)
+ apt update && apt install socat -y
+ ;;
+ centos | almalinux | rocky)
+ yum -y update && yum -y install socat
+ ;;
+ fedora)
+ dnf -y update && dnf -y install socat
+ ;;
+ *)
+ echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
+ exit 1
+ ;;
esac
if [ $? -ne 0 ]; then
LOGE "install socat failed, please check logs"
@@ -750,8 +755,8 @@ ssl_cert_issue_CF() {
LOGI "Certificate issued Successfully, Installing..."
fi
~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} --ca-file /root/cert/ca.cer \
- --cert-file /root/cert/${CF_Domain}.cer --key-file /root/cert/${CF_Domain}.key \
- --fullchain-file /root/cert/fullchain.cer
+ --cert-file /root/cert/${CF_Domain}.cer --key-file /root/cert/${CF_Domain}.key \
+ --fullchain-file /root/cert/fullchain.cer
if [ $? -ne 0 ]; then
LOGE "Certificate installation failed, script exiting..."
exit 1
@@ -782,21 +787,22 @@ warp_cloudflare() {
echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice
case "$choice" in
- 0)
- show_menu ;;
- 1)
- bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
- ;;
- 2)
- warp a
- ;;
- 3)
- warp y
- ;;
- 4)
- warp u
- ;;
- *) echo "Invalid choice" ;;
+ 0)
+ show_menu
+ ;;
+ 1)
+ bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
+ ;;
+ 2)
+ warp a
+ ;;
+ 3)
+ warp y
+ ;;
+ 4)
+ warp u
+ ;;
+ *) echo "Invalid choice" ;;
esac
}
@@ -809,45 +815,46 @@ multi_protocol() {
echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice
case "$choice" in
- 0)
- show_menu ;;
- 1)
- bash <(curl -Ls https://raw.githubusercontent.com/M4mmad/3xui-multi-protocol/master/install.sh --ipv4)
- ;;
- 2)
- bash <(curl -Ls https://raw.githubusercontent.com/M4mmad/3xui-multi-protocol/master/unistall.sh --ipv4)
- ;;
- 3)
- systemctl start 3xui-multi-protocol
- ;;
- 4)
- systemctl stop 3xui-multi-protocol
- ;;
- *) echo "Invalid choice" ;;
+ 0)
+ show_menu
+ ;;
+ 1)
+ bash <(curl -Ls https://raw.githubusercontent.com/M4mmad/3xui-multi-protocol/master/install.sh --ipv4)
+ ;;
+ 2)
+ bash <(curl -Ls https://raw.githubusercontent.com/M4mmad/3xui-multi-protocol/master/unistall.sh --ipv4)
+ ;;
+ 3)
+ systemctl start 3xui-multi-protocol
+ ;;
+ 4)
+ systemctl stop 3xui-multi-protocol
+ ;;
+ *) echo "Invalid choice" ;;
esac
}
run_speedtest() {
# Check if Speedtest is already installed
- if ! command -v speedtest &> /dev/null; then
+ if ! command -v speedtest &>/dev/null; then
# If not installed, install it
local pkg_manager=""
local speedtest_install_script=""
-
- if command -v dnf &> /dev/null; then
+
+ if command -v dnf &>/dev/null; then
pkg_manager="dnf"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
- elif command -v yum &> /dev/null; then
+ elif command -v yum &>/dev/null; then
pkg_manager="yum"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
- elif command -v apt-get &> /dev/null; then
+ elif command -v apt-get &>/dev/null; then
pkg_manager="apt-get"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
- elif command -v apt &> /dev/null; then
+ elif command -v apt &>/dev/null; then
pkg_manager="apt"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
fi
-
+
if [[ -z $pkg_manager ]]; then
echo "Error: Package manager not found. You may need to install Speedtest manually."
return 1
@@ -938,65 +945,74 @@ iplimit_main() {
echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice
case "$choice" in
- 0)
- show_menu ;;
- 1)
- confirm "Proceed with installation of Fail2ban & IP Limit?" "y"
- if [[ $? == 0 ]]; then
- install_iplimit
- else
- iplimit_main
- fi ;;
- 2)
- read -rp "Please enter new Ban Duration in Minutes [default 5]: " NUM
- if [[ $NUM =~ ^[0-9]+$ ]]; then
- create_iplimit_jails ${NUM}
- systemctl restart fail2ban
- else
- echo -e "${red}${NUM} is not a number! Please, try again.${plain}"
- fi
- iplimit_main ;;
- 3)
- confirm "Proceed with Unbanning everyone from IP Limit jail?" "y"
- if [[ $? == 0 ]]; then
- fail2ban-client reload --restart --unban 3x-ipl
- echo -e "${green}All users Unbanned successfully.${plain}"
- iplimit_main
- else
- echo -e "${yellow}Cancelled.${plain}"
- fi
- iplimit_main ;;
- 4)
- show_banlog
- ;;
- 5)
- service fail2ban status
- ;;
+ 0)
+ show_menu
+ ;;
+ 1)
+ confirm "Proceed with installation of Fail2ban & IP Limit?" "y"
+ if [[ $? == 0 ]]; then
+ install_iplimit
+ else
+ iplimit_main
+ fi
+ ;;
+ 2)
+ read -rp "Please enter new Ban Duration in Minutes [default 30]: " NUM
+ if [[ $NUM =~ ^[0-9]+$ ]]; then
+ create_iplimit_jails ${NUM}
+ systemctl restart fail2ban
+ else
+ echo -e "${red}${NUM} is not a number! Please, try again.${plain}"
+ fi
+ iplimit_main
+ ;;
+ 3)
+ confirm "Proceed with Unbanning everyone from IP Limit jail?" "y"
+ if [[ $? == 0 ]]; then
+ fail2ban-client reload --restart --unban 3x-ipl
+ echo -e "${green}All users Unbanned successfully.${plain}"
+ iplimit_main
+ else
+ echo -e "${yellow}Cancelled.${plain}"
+ fi
+ iplimit_main
+ ;;
+ 4)
+ show_banlog
+ ;;
+ 5)
+ service fail2ban status
+ ;;
- 6)
- remove_iplimit ;;
- *) echo "Invalid choice" ;;
+ 6)
+ remove_iplimit
+ ;;
+ *) echo "Invalid choice" ;;
esac
}
install_iplimit() {
if ! command -v fail2ban-client &>/dev/null; then
echo -e "${green}Fail2ban is not installed. Installing now...!${plain}\n"
-
+
# Check the OS and install necessary packages
case "${release}" in
- ubuntu|debian)
- wget -O fail2ban.deb https://github.com/fail2ban/fail2ban/releases/download/1.0.2/fail2ban_1.0.2-1.upstream1_all.deb
- wget -O fail2ban.deb.asc https://github.com/fail2ban/fail2ban/releases/download/1.0.2/fail2ban_1.0.2-1.upstream1_all.deb.asc
- dpkg -i fail2ban.deb ;;
- centos|almalinux|rocky)
- yum update -y && yum install epel-release -y
- yum -y install fail2ban ;;
- fedora)
- dnf -y update && dnf -y install fail2ban ;;
- *)
- echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
- exit 1 ;;
+ ubuntu | debian)
+ wget -O fail2ban.deb https://github.com/fail2ban/fail2ban/releases/download/1.0.2/fail2ban_1.0.2-1.upstream1_all.deb
+ wget -O fail2ban.deb.asc https://github.com/fail2ban/fail2ban/releases/download/1.0.2/fail2ban_1.0.2-1.upstream1_all.deb.asc
+ dpkg -i fail2ban.deb
+ ;;
+ centos | almalinux | rocky)
+ yum update -y && yum install epel-release -y
+ yum -y install fail2ban
+ ;;
+ fedora)
+ dnf -y update && dnf -y install fail2ban
+ ;;
+ *)
+ echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
+ exit 1
+ ;;
esac
if ! command -v fail2ban-client &>/dev/null; then
@@ -1041,45 +1057,53 @@ install_iplimit() {
before_show_menu
}
-remove_iplimit(){
+remove_iplimit() {
echo -e "${green}\t1.${plain} Only remove IP Limit configurations"
echo -e "${green}\t2.${plain} Uninstall Fail2ban and IP Limit"
echo -e "${green}\t0.${plain} Abort"
read -p "Choose an option: " num
case "$num" in
- 1)
- rm -f /etc/fail2ban/filter.d/3x-ipl.conf
- rm -f /etc/fail2ban/action.d/3x-ipl.conf
- rm -f /etc/fail2ban/jail.d/3x-ipl.conf
- systemctl restart fail2ban
- echo -e "${green}IP Limit removed successfully!${plain}\n"
- before_show_menu ;;
- 2)
- rm -rf /etc/fail2ban
- systemctl stop fail2ban
- case "${release}" in
- ubuntu|debian)
- apt-get remove -y fail2ban
- apt-get purge -y fail2ban -y
- apt-get autoremove -y;;
- centos|almalinux|rocky)
- yum remove fail2ban -y
- yum autoremove -y;;
- fedora)
- dnf remove fail2ban -y
- dnf autoremove -y;;
- *)
- echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n"
- exit 1 ;;
- esac
- echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n"
- before_show_menu ;;
- 0)
- echo -e "${yellow}Cancelled.${plain}\n"
- iplimit_main ;;
- *)
- echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
- remove_iplimit ;;
+ 1)
+ rm -f /etc/fail2ban/filter.d/3x-ipl.conf
+ rm -f /etc/fail2ban/action.d/3x-ipl.conf
+ rm -f /etc/fail2ban/jail.d/3x-ipl.conf
+ systemctl restart fail2ban
+ echo -e "${green}IP Limit removed successfully!${plain}\n"
+ before_show_menu
+ ;;
+ 2)
+ rm -rf /etc/fail2ban
+ systemctl stop fail2ban
+ case "${release}" in
+ ubuntu | debian)
+ apt-get remove -y fail2ban
+ apt-get purge -y fail2ban -y
+ apt-get autoremove -y
+ ;;
+ centos | almalinux | rocky)
+ yum remove fail2ban -y
+ yum autoremove -y
+ ;;
+ fedora)
+ dnf remove fail2ban -y
+ dnf autoremove -y
+ ;;
+ *)
+ echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n"
+ exit 1
+ ;;
+ esac
+ echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n"
+ before_show_menu
+ ;;
+ 0)
+ echo -e "${yellow}Cancelled.${plain}\n"
+ iplimit_main
+ ;;
+ *)
+ echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
+ remove_iplimit
+ ;;
esac
}
@@ -1214,7 +1238,7 @@ show_menu() {
;;
24)
run_speedtest
- ;;
+ ;;
*)
LOGE "Please enter the correct number [0-24]"
;;
From ebaabf61509a02090016acae886b2a7df216e814 Mon Sep 17 00:00:00 2001
From: quydang <81231812+quydang04@users.noreply.github.com>
Date: Sat, 20 Jan 2024 23:41:58 +0700
Subject: [PATCH 08/26] Update README (#1648)
---
README.md | 29 +++++++++++++++--------------
x-ui.sh | 4 ++--
2 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md
index 60c6c044..6b0beab7 100644
--- a/README.md
+++ b/README.md
@@ -127,7 +127,22 @@ update to latest version
- AlmaLinux 9+
- Rockylinux 9+
+## Compatible Architectures & Devices
+Supports a variety of different architectures and devices. Here are some of the main architectures that we support:
+
+- **amd64**: This is the most common architecture for personal computers and servers. It supports most modern operating systems.
+
+- **x86 / i386**: This architecture is prevalent in desktop and laptop computers. It's widely supported by various operating systems and applications. (Ex: Most Windows, macOS, and Linux systems)
+
+- **armv8 / arm64 / aarch64**: This is the architecture for modern mobile and embedded devices, including smartphones and tablets. (Ex: Raspberry Pi 4, Raspberry Pi 3, Raspberry Pi Zero 2/Zero 2 W, Orange Pi 3 LTS,...)
+
+- **armv7 / arm / arm32**: This is the architecture for older mobile and embedded devices. It is still widely used in many devices. (Ex: Orange Pi Zero LTS, Orange Pi PC Plus, Raspberry Pi 2,...)
+
+- **armv6 / arm / arm32**: This is the architecture for very old embedded devices. While not as common as before, there are still some devices using this architecture. (Ex: Raspberry Pi 1, Raspberry Pi Zero/Zero W,...)
+
+- **armv5 / arm / arm32**: This is an older architecture primarily used in early embedded systems. While it's less common today, some legacy devices may still rely on this architecture. (Ex: Early versions of Raspberry Pi, some older smartphones)
+
## Languages
- English
@@ -398,20 +413,6 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
-
-## Supported Architectures and Devices
-
-Supports a variety of different architectures and devices. Here are some of the main architectures that we support:
-
-- **amd64**: This is the most common architecture for personal computers and servers. It supports most modern operating systems.
-
-- **armv8 / arm64 / aarch64**: This is the architecture for modern mobile and embedded devices, including smartphones and tablets. (Ex: Raspberry Pi 4, Raspberry Pi 3, Raspberry Pi Zero 2/Zero 2 W, Orange Pi 3 LTS,...)
-
-- **armv7 / arm / arm32**: This is the architecture for older mobile and embedded devices. It is still widely used in many devices. (Ex: Orange Pi Zero LTS, Orange Pi PC Plus, Raspberry Pi 2,...)
-
-- **armv6 / arm / arm32**: This is the architecture for very old embedded devices. While not as common as before, there are still some devices using this architecture. (Ex: Raspberry Pi 1, Raspberry Pi Zero/Zero W,...)
-
-
## Preview

diff --git a/x-ui.sh b/x-ui.sh
index cabb546f..9fa43960 100644
--- a/x-ui.sh
+++ b/x-ui.sh
@@ -872,7 +872,7 @@ create_iplimit_jails() {
# Use default bantime if not passed => 30 minutes
local bantime="${1:-30}"
- # uncomment allowipv6 = auto in fail2ban.conf
+ # Uncomment 'allowipv6 = auto' in fail2ban.conf
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
cat << EOF > /etc/fail2ban/jail.d/3x-ipl.conf
@@ -917,7 +917,7 @@ actionunban = -D f2b- -s -j
[Init]
EOF
- echo -e "${green}Created Ip Limit jail files with a bantime of ${bantime} minutes.${plain}"
+ echo -e "${green}Ip Limit jail files created with a bantime of ${bantime} minutes.${plain}"
}
iplimit_remove_conflicts() {
From 820e302aac04312f8353b1bc839e39be70518441 Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Sun, 21 Jan 2024 04:00:58 +0330
Subject: [PATCH 09/26] revert fail2ban v1.0.2
it doesn't work well
---
x-ui.sh | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/x-ui.sh b/x-ui.sh
index 9fa43960..20336c18 100644
--- a/x-ui.sh
+++ b/x-ui.sh
@@ -998,9 +998,7 @@ install_iplimit() {
# Check the OS and install necessary packages
case "${release}" in
ubuntu | debian)
- wget -O fail2ban.deb https://github.com/fail2ban/fail2ban/releases/download/1.0.2/fail2ban_1.0.2-1.upstream1_all.deb
- wget -O fail2ban.deb.asc https://github.com/fail2ban/fail2ban/releases/download/1.0.2/fail2ban_1.0.2-1.upstream1_all.deb.asc
- dpkg -i fail2ban.deb
+ apt update && apt install fail2ban -y
;;
centos | almalinux | rocky)
yum update -y && yum install epel-release -y
From 4b9bbbc34b3ed5563f9cb14420b768d241580cf3 Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Sun, 21 Jan 2024 04:45:17 +0330
Subject: [PATCH 10/26] clear banned log after unban everyone
---
x-ui.sh | 1 +
1 file changed, 1 insertion(+)
diff --git a/x-ui.sh b/x-ui.sh
index 20336c18..c7331e6d 100644
--- a/x-ui.sh
+++ b/x-ui.sh
@@ -970,6 +970,7 @@ iplimit_main() {
confirm "Proceed with Unbanning everyone from IP Limit jail?" "y"
if [[ $? == 0 ]]; then
fail2ban-client reload --restart --unban 3x-ipl
+ truncate -s 0 "${iplimit_banned_log_path}"
echo -e "${green}All users Unbanned successfully.${plain}"
iplimit_main
else
From e7ce8c8ddb8472695b296eac305c5ac9b8c1d3d8 Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Sun, 21 Jan 2024 14:39:15 +0330
Subject: [PATCH 11/26] minor changes
disAllowedIps doesn't show on debug if there is no ip
change copy to clipboard to export inbound
---
go.mod | 2 +-
go.sum | 4 ++--
web/html/xui/inbounds.html | 2 +-
web/job/check_client_ip_job.go | 23 ++++++++++++++---------
web/translation/translate.en_US.toml | 2 +-
web/translation/translate.es_ES.toml | 2 +-
web/translation/translate.fa_IR.toml | 2 +-
web/translation/translate.ru_RU.toml | 2 +-
web/translation/translate.vi_VN.toml | 2 +-
web/translation/translate.zh_Hans.toml | 2 +-
10 files changed, 24 insertions(+), 19 deletions(-)
diff --git a/go.mod b/go.mod
index 6a116ba3..4e84c3e2 100644
--- a/go.mod
+++ b/go.mod
@@ -36,7 +36,7 @@ require (
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/go-playground/validator/v10 v10.16.0 // indirect
+ github.com/go-playground/validator/v10 v10.17.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // indirect
diff --git a/go.sum b/go.sum
index 0b593906..f5d3b46b 100644
--- a/go.sum
+++ b/go.sum
@@ -76,8 +76,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
-github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
-github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
+github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html
index 79326ca2..c986e3fd 100644
--- a/web/html/xui/inbounds.html
+++ b/web/html/xui/inbounds.html
@@ -230,7 +230,7 @@
- {{ i18n "pages.inbounds.copyToClipboard" }}
+ {{ i18n "pages.inbounds.exportInbound" }}
{{ i18n "pages.inbounds.resetTraffic" }}
diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go
index 65257170..b393e68d 100644
--- a/web/job/check_client_ip_job.go
+++ b/web/job/check_client_ip_job.go
@@ -16,17 +16,18 @@ import (
"x-ui/xray"
)
-type CheckClientIpJob struct{}
+type CheckClientIpJob struct {
+ disAllowedIps []string
+}
var job *CheckClientIpJob
-var disAllowedIps []string
var ipFiles = []string{
xray.GetIPLimitLogPath(),
-xray.GetIPLimitPrevLogPath(),
+ xray.GetIPLimitPrevLogPath(),
xray.GetIPLimitBannedLogPath(),
-xray.GetIPLimitBannedPrevLogPath(),
+ xray.GetIPLimitBannedPrevLogPath(),
xray.GetAccessPersistentLogPath(),
-xray.GetAccessPersistentPrevLogPath(),
+ xray.GetAccessPersistentPrevLogPath(),
}
func NewCheckClientIpJob() *CheckClientIpJob {
@@ -130,7 +131,6 @@ func (j *CheckClientIpJob) processLogFile() {
}
}
- disAllowedIps = []string{}
shouldCleanLog := false
for clientEmail, ips := range InboundClientIps {
@@ -237,6 +237,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
json.Unmarshal([]byte(inbound.Settings), &settings)
clients := settings["clients"]
shouldCleanLog := false
+ j.disAllowedIps = []string{}
// create iplimit log file channel
logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
@@ -255,7 +256,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
shouldCleanLog = true
if limitIp < len(ips) && inbound.Enable {
- disAllowedIps = append(disAllowedIps, ips[limitIp:]...)
+ j.disAllowedIps = append(j.disAllowedIps, ips[limitIp:]...)
for i := limitIp; i < len(ips); i++ {
log.Printf("[LIMIT_IP] Email = %s || SRC = %s", clientEmail, ips[i])
}
@@ -263,8 +264,12 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
}
}
}
- logger.Debug("disAllowedIps ", disAllowedIps)
- sort.Strings(disAllowedIps)
+
+ sort.Strings(j.disAllowedIps)
+
+ if len(j.disAllowedIps) > 0 {
+ logger.Debug("disAllowedIps ", j.disAllowedIps)
+ }
db := database.GetDB()
err = db.Save(inboundClientIps).Error
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index 2f1c0544..006378ab 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -178,7 +178,7 @@
"info" = "Info"
"same" = "Same"
"inboundData" = "Inbound's Data"
-"copyToClipboard" = "Copy to Clipboard"
+"exportInbound" = "Export Inbound"
"import" = "Import"
"importInbound" = "Import an Inbound"
diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml
index 42a3a2b2..bbf50b6c 100644
--- a/web/translation/translate.es_ES.toml
+++ b/web/translation/translate.es_ES.toml
@@ -178,7 +178,7 @@
"info" = "Info"
"same" = "misma"
"inboundData" = "Datos de entrada"
-"copyToClipboard" = "Copiar al portapapeles"
+"exportInbound" = "Exportación entrante"
"import" = "Importar"
"importInbound" = "Importar un entrante"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index 3edcb4cb..1de8538c 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -178,7 +178,7 @@
"info" = "اطلاعات"
"same" = "همسان"
"inboundData" = "دادههای ورودی"
-"copyToClipboard" = "کپی در حافظه"
+"exportInbound" = "استخراج ورودی"
"import" = "افزودن"
"importInbound" = "افزودن یک ورودی"
diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml
index d8211d8d..2f33311e 100644
--- a/web/translation/translate.ru_RU.toml
+++ b/web/translation/translate.ru_RU.toml
@@ -178,7 +178,7 @@
"info" = "Информация"
"same" = "Тот же"
"inboundData" = "Входящие данные"
-"copyToClipboard" = "Копировать в буфер обмена"
+"exportInbound" = "Экспорт входящих"
"import" = "Импортировать"
"importInbound" = "Импортировать входящее сообщение"
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index 7491a70c..1a2cfaa0 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -178,7 +178,7 @@
"info" = "Thông tin"
"same" = "Giống nhau"
"inboundData" = "Dữ liệu gửi đến"
-"copyToClipboard" = "Sao chép vào bảng nhớ tạm"
+"exportInbound" = "Xuất nhập khẩu"
"import" = "Nhập"
"importInbound" = "Nhập inbound"
diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml
index 5a46ec73..966ef3f7 100644
--- a/web/translation/translate.zh_Hans.toml
+++ b/web/translation/translate.zh_Hans.toml
@@ -178,7 +178,7 @@
"info" = "信息"
"same" = "相同"
"inboundData" = "入站数据"
-"copyToClipboard" = "复制到剪贴板"
+"exportInbound" = "出口 入境"
"import"="导入"
"importInbound" = "导入入站"
From 5c695ca6520c9cd9c44b18119a862f8f480969af Mon Sep 17 00:00:00 2001
From: Ali Rahimi
Date: Sun, 21 Jan 2024 15:26:19 +0100
Subject: [PATCH 12/26] add group user with the same subscription id to all
inbounds (#1650)
---
web/assets/js/util/utils.js | 35 +++++++++++
web/controller/inbound.go | 39 +++++++------
web/html/common/qrcode_modal.html | 17 ++++--
web/html/xui/client_modal.html | 80 +++++++++++++++++++++-----
web/html/xui/form/client.html | 4 +-
web/html/xui/inbound_modal.html | 4 ++
web/html/xui/inbounds.html | 59 +++++++++++++++----
web/translation/translate.en_US.toml | 1 +
web/translation/translate.es_ES.toml | 1 +
web/translation/translate.fa_IR.toml | 1 +
web/translation/translate.ru_RU.toml | 1 +
web/translation/translate.vi_VN.toml | 1 +
web/translation/translate.zh_Hans.toml | 1 +
13 files changed, 196 insertions(+), 48 deletions(-)
diff --git a/web/assets/js/util/utils.js b/web/assets/js/util/utils.js
index 61b322bd..48ff237d 100644
--- a/web/assets/js/util/utils.js
+++ b/web/assets/js/util/utils.js
@@ -83,6 +83,41 @@ class HttpUtil {
}
return msg;
}
+
+ static async jsonPost(url, data) {
+ let msg;
+ try {
+ const requestOptions = {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ };
+ const resp = await fetch(url, requestOptions);
+ const response = await resp.json();
+
+ msg = this._respToMsg({data : response});
+ } catch (e) {
+ msg = new Msg(false, e.toString());
+ }
+ this._handleMsg(msg);
+ return msg;
+ }
+
+ static async postWithModalJson(url, data, modal) {
+ if (modal) {
+ modal.loading(true);
+ }
+ const msg = await this.jsonPost(url, data);
+ if (modal) {
+ modal.loading(false);
+ if (msg instanceof Msg && msg.success) {
+ modal.close();
+ }
+ }
+ return msg;
+ }
}
class PromiseUtil {
diff --git a/web/controller/inbound.go b/web/controller/inbound.go
index 86da9813..b274be64 100644
--- a/web/controller/inbound.go
+++ b/web/controller/inbound.go
@@ -159,24 +159,31 @@ func (a *InboundController) clearClientIps(c *gin.Context) {
}
func (a *InboundController) addInboundClient(c *gin.Context) {
- data := &model.Inbound{}
- err := c.ShouldBind(data)
- if err != nil {
- jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err)
- return
- }
+ var requestData []model.Inbound
- needRestart := true
+ err := c.ShouldBindJSON(&requestData)
+
+ if err != nil {
+ jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err)
+ return
+ }
+
+ needRestart := true
+
+ for _, data := range requestData {
+
+ needRestart, err = a.inboundService.AddInboundClient(&data)
+ if err != nil {
+ jsonMsg(c, "Something went wrong!", err)
+ return
+ }
+ }
+
+ jsonMsg(c, "Client(s) added", nil)
+ if err == nil && needRestart {
+ a.xrayService.SetToNeedRestart()
+ }
- needRestart, err = a.inboundService.AddInboundClient(data)
- if err != nil {
- jsonMsg(c, "Something went wrong!", err)
- return
- }
- jsonMsg(c, "Client(s) added", nil)
- if err == nil && needRestart {
- a.xrayService.SetToNeedRestart()
- }
}
func (a *InboundController) delInboundClient(c *gin.Context) {
diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html
index 3c4fd929..31b3450c 100644
--- a/web/html/common/qrcode_modal.html
+++ b/web/html/common/qrcode_modal.html
@@ -11,10 +11,12 @@
Subscription
- {{ i18n "pages.inbounds.client" }}
-
- [[ row.remark ]]
-
+ {{ i18n "pages.inbounds.client" }}
+
+
+ [[ row.remark ]]
+
+
@@ -27,12 +29,14 @@
qrcodes: [],
clipboard: null,
visible: false,
+ isJustSub: false,
subId: '',
- show: function (title = '', dbInbound, client) {
+ show: function (title = '', dbInbound, client, isJustSub = false) {
this.title = title;
this.dbInbound = dbInbound;
this.inbound = dbInbound.toInbound();
this.client = client;
+ this.isJustSub = isJustSub;
this.subId = '';
this.qrcodes = [];
this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, client).forEach(l => {
@@ -53,6 +57,9 @@
el: '#qrcode-modal',
data: {
qrModal: qrModal,
+ get isJustSub(){
+ return qrModal.isJustSub
+ }
},
methods: {
copyToClipboard(elmentId, content) {
diff --git a/web/html/xui/client_modal.html b/web/html/xui/client_modal.html
index 4b270607..02c548e3 100644
--- a/web/html/xui/client_modal.html
+++ b/web/html/xui/client_modal.html
@@ -15,7 +15,12 @@
confirmLoading: false,
title: '',
okText: '',
- isEdit: false,
+ group: {
+ isGroup: false,
+ currentClient: null,
+ inbounds: [],
+ clients: [],
+ },
dbInbound: new DBInbound(),
inbound: new Inbound(),
clients: [],
@@ -28,30 +33,76 @@
if (clientModal.isEdit) {
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);
} else {
- ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
+ if (clientModal.group.isGroup) {
+ const currentClient = clientModal.group.currentClient;
+
+ clientModal.group.clients.forEach((client, index) => {
+ const { email, limitIp, totalGB, expiryTime, reset, enable, subId, tgId, flow } = currentClient;
+
+ client.email = `${email}-${index + 1}`;
+ client.limitIp = limitIp;
+ client.totalGB = totalGB;
+ client.expiryTime = expiryTime;
+ client.reset = reset;
+ client.enable = enable;
+
+ if (subId) {
+ client.subId = subId;
+ }
+ if (tgId) {
+ client.tgId = tgId;
+ }
+ if (flow) {
+ client.flow = flow;
+ }
+ });
+ ObjectUtil.execute(clientModal.confirm, clientModal.group.clients, clientModal.group.inbounds);
+ } else {
+ ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
+ }
}
},
show({ title = '', okText = '{{ i18n "sure" }}', index = null, dbInbound = null, confirm = () => { }, isEdit = false }) {
+ this.group = {
+ isGroup: false,
+ currentClient: null,
+ inbounds: [],
+ clients: [],
+ }
this.visible = true;
this.title = title;
this.okText = okText;
this.isEdit = isEdit;
+ if (Array.isArray(dbInbound)) {
+ this.group.isGroup = true;
+ dbInbound.forEach((dbInboundItem) => {
+ this.showProcess(dbInboundItem);
+ this.group.inbounds.push(dbInboundItem.id)
+ this.group.clients.push(this.clients[this.index])
+ })
+ this.group.currentClient = this.clients[this.index]
+ } else {
+ this.showProcess(dbInbound, index);
+ if (isEdit) {
+ if (this.clients[index].expiryTime < 0) {
+ this.delayedStart = true;
+ }
+ this.oldClientId = this.getClientId(dbInbound.protocol, clients[index]);
+ } else {
+ this.addClient(this.inbound.protocol, this.clients);
+ }
+ }
+ this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
+ this.confirm = confirm;
+ },
+ showProcess(dbInbound, index = null) {
this.dbInbound = new DBInbound(dbInbound);
this.inbound = dbInbound.toInbound();
this.clients = this.inbound.clients;
this.index = index === null ? this.clients.length : index;
this.delayedStart = false;
- if (isEdit) {
- if (this.clients[index].expiryTime < 0) {
- this.delayedStart = true;
- }
- this.oldClientId = this.getClientId(dbInbound.protocol, clients[index]);
- } else {
- this.addClient(this.inbound.protocol, this.clients);
- }
- this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
- this.confirm = confirm;
- },
+ this.addClient(this.inbound.protocol, this.clients);
+ },
getClientId(protocol, client) {
switch (protocol) {
case Protocols.TROJAN: return client.password;
@@ -94,6 +145,9 @@
get isEdit() {
return this.clientModal.isEdit;
},
+ get isGroup() {
+ return this.clientModal.group.isGroup;
+ },
get datepicker() {
return app.datepicker;
},
diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html
index 204aca72..f4ac25f3 100644
--- a/web/html/xui/form/client.html
+++ b/web/html/xui/form/client.html
@@ -15,7 +15,7 @@
-
+
@@ -28,7 +28,7 @@
-
+
diff --git a/web/html/xui/inbound_modal.html b/web/html/xui/inbound_modal.html
index ab42e09c..7f9ed740 100644
--- a/web/html/xui/inbound_modal.html
+++ b/web/html/xui/inbound_modal.html
@@ -13,6 +13,7 @@
confirmLoading: false,
okText: '{{ i18n "sure" }}',
isEdit: false,
+ isGroup: false,
confirm: null,
inbound: new Inbound(),
dbInbound: new DBInbound(),
@@ -60,6 +61,9 @@
get isEdit() {
return inModal.isEdit;
},
+ get isGroup() {
+ return inModal.isGroup;
+ },
get client() {
return inModal.inbound.clients[0];
},
diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html
index c986e3fd..53693ab3 100644
--- a/web/html/xui/inbounds.html
+++ b/web/html/xui/inbounds.html
@@ -145,6 +145,10 @@
{{ i18n "pages.inbounds.delDepletedClients" }}
+
+
+ {{ i18n "pages.client.groupAdd"}}
+
@@ -285,7 +289,7 @@
[[ clientEmail ]]
[[ clientCount[dbInbound.id].online.length ]]
-
+
@@ -339,7 +343,7 @@
[[ dbInbound.toInbound().stream.network ]]
tls
reality
-
+
@@ -373,7 +377,7 @@
[[ clientEmail ]]
[[ clientCount[dbInbound.id].online.length ]]
-
+
@@ -740,6 +744,9 @@
case "delDepletedClients":
this.delDepletedClients(-1)
break;
+ case "addGroupClient":
+ this.openGroupAddClient()
+ break;
}
},
clickAction(action, dbInbound) {
@@ -883,6 +890,20 @@
await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal);
},
+ openGroupAddClient() {
+ clientModal.show({
+ title: '{{ i18n "pages.client.groupAdd"}}',
+ okText: '{{ i18n "pages.client.submitAdd"}}',
+ dbInbound: this.dbInbounds,
+ confirm: async (clients, dbInboundIds) => {
+ clientModal.loading();
+ await this.addGroupClient(clients, dbInboundIds);
+ clientModal.close();
+ await this.showQrcode(dbInboundIds[0],clients[0], true)
+ },
+ isEdit: false
+ });
+ },
openAddClient(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
clientModal.show({
@@ -893,6 +914,7 @@
clientModal.loading();
await this.addClient(clients, dbInboundId);
clientModal.close();
+ await this.showQrcode(dbInboundId,clients)
},
isEdit: false
});
@@ -936,11 +958,24 @@
}
},
async addClient(clients, dbInboundId) {
- const data = {
+ const data = [{
id: dbInboundId,
settings: '{"clients": [' + clients.toString() + ']}',
- };
- await this.submit(`/panel/inbound/addClient`, data);
+ }];
+
+ await this.submit(`/panel/inbound/addClient`, data, true)
+ },
+
+ async addGroupClient(clients, dbInboundIds) {
+ const data = []
+ dbInboundIds.forEach((dbInboundId, index) => {
+ data.push({
+ id: dbInboundId,
+ settings: '{"clients": [' + clients[index].toString() + ']}',
+ })
+ })
+
+ await this.submit(`/panel/inbound/addClient`, data, true)
},
async updateClient(client, dbInboundId, clientId) {
const data = {
@@ -1001,8 +1036,8 @@
checkFallback(dbInbound) {
newDbInbound = new DBInbound(dbInbound);
if (dbInbound.listen.startsWith("@")){
- rootInbound = this.inbounds.find((i) =>
- i.isTcp &&
+ rootInbound = this.inbounds.find((i) =>
+ i.isTcp &&
['trojan','vless'].includes(i.protocol) &&
i.settings.fallbacks.find(f => f.dest === dbInbound.listen)
);
@@ -1018,10 +1053,10 @@
}
return newDbInbound;
},
- showQrcode(dbInboundId, client) {
+ showQrcode(dbInboundId, client, isJustSub = false) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
newDbInbound = this.checkFallback(dbInbound);
- qrModal.show('{{ i18n "qrCode"}}', newDbInbound, client);
+ qrModal.show('{{ i18n "qrCode"}}', newDbInbound, client, isJustSub);
},
showInfo(dbInboundId, client) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
@@ -1050,8 +1085,8 @@
await this.updateClient(clients[index], dbInboundId, clientId);
this.loading(false);
},
- async submit(url, data) {
- const msg = await HttpUtil.postWithModal(url, data);
+ async submit(url, data, isJson = false) {
+ const msg = isJson ? await HttpUtil.postWithModalJson(url, data) : await HttpUtil.postWithModal(url, data);
if (msg.success) {
await this.getDBInbounds();
}
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index 006378ab..b2d74c3b 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -184,6 +184,7 @@
[pages.client]
"add" = "Add Client"
+"groupAdd" = "Add subscription user"
"edit" = "Edit Client"
"submitAdd" = "Add Client"
"submitEdit" = "Save Changes"
diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml
index bbf50b6c..83c9460b 100644
--- a/web/translation/translate.es_ES.toml
+++ b/web/translation/translate.es_ES.toml
@@ -184,6 +184,7 @@
[pages.client]
"add" = "Agregar Cliente"
+"groupAdd" = "Agregar usuario de suscripción"
"edit" = "Editar Cliente"
"submitAdd" = "Agregar Cliente"
"submitEdit" = "Guardar Cambios"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index 1de8538c..9613c12e 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -184,6 +184,7 @@
[pages.client]
"add" = "کاربر جدید"
+"groupAdd" = "کاربر جدید سابسکریپشن"
"edit" = "ویرایش کاربر"
"submitAdd" = "اضافه کردن"
"submitEdit" = "ذخیره تغییرات"
diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml
index 2f33311e..7cf2be0a 100644
--- a/web/translation/translate.ru_RU.toml
+++ b/web/translation/translate.ru_RU.toml
@@ -184,6 +184,7 @@
[pages.client]
"add" = "Добавить пользователя"
+"groupAdd" = "Добавить пользователя подписки"
"edit" = "Редактировать пользователя"
"submitAdd" = "Добавить пользователя"
"submitEdit" = "Сохранить изменения"
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index 1a2cfaa0..9cd7bc46 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -184,6 +184,7 @@
[pages.client]
"add" = "Thêm người dùng"
+"groupAdd" = "Thêm người dùng đăng ký"
"edit" = "Chỉnh sửa người dùng"
"submitAdd" = "Thêm"
"submitEdit" = "Lưu thay đổi"
diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml
index 966ef3f7..c229dc29 100644
--- a/web/translation/translate.zh_Hans.toml
+++ b/web/translation/translate.zh_Hans.toml
@@ -184,6 +184,7 @@
[pages.client]
"add" = "添加客户端"
+"groupAdd" = "添加订阅用户"
"edit" = "编辑客户端"
"submitAdd" = "添加客户端"
"submitEdit" = "保存修改"
From b172d450e3a9dba8a56b2f6cd9e059559d232033 Mon Sep 17 00:00:00 2001
From: Ali Rahimi
Date: Mon, 22 Jan 2024 12:36:01 +0100
Subject: [PATCH 13/26] Group editing feature of users with the same
subscription (#1661)
---
web/controller/inbound.go | 81 ++++++++++++++
web/html/xui/client_modal.html | 146 ++++++++++++++++++-------
web/html/xui/form/client.html | 12 ++
web/html/xui/inbounds.html | 33 ++++--
web/translation/translate.en_US.toml | 2 +
web/translation/translate.es_ES.toml | 2 +
web/translation/translate.fa_IR.toml | 2 +
web/translation/translate.ru_RU.toml | 2 +
web/translation/translate.vi_VN.toml | 2 +
web/translation/translate.zh_Hans.toml | 2 +
10 files changed, 237 insertions(+), 47 deletions(-)
diff --git a/web/controller/inbound.go b/web/controller/inbound.go
index b274be64..4d6e0af0 100644
--- a/web/controller/inbound.go
+++ b/web/controller/inbound.go
@@ -1,6 +1,7 @@
package controller
import (
+ "errors"
"encoding/json"
"fmt"
"strconv"
@@ -32,7 +33,9 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
g.POST("/clientIps/:email", a.getClientIps)
g.POST("/clearClientIps/:email", a.clearClientIps)
g.POST("/addClient", a.addInboundClient)
+ g.POST("/addGroupClient", a.addGroupInboundClient)
g.POST("/:id/delClient/:clientId", a.delInboundClient)
+ g.POST("/updateClients", a.updateGroupInboundClient)
g.POST("/updateClient/:clientId", a.updateInboundClient)
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
g.POST("/resetAllTraffics", a.resetAllTraffics)
@@ -186,6 +189,34 @@ func (a *InboundController) addInboundClient(c *gin.Context) {
}
+func (a *InboundController) addGroupInboundClient(c *gin.Context) {
+ var requestData []model.Inbound
+
+ err := c.ShouldBindJSON(&requestData)
+
+ if err != nil {
+ jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err)
+ return
+ }
+
+ needRestart := true
+
+ for _, data := range requestData {
+
+ needRestart, err = a.inboundService.AddInboundClient(&data)
+ if err != nil {
+ jsonMsg(c, "Something went wrong!", err)
+ return
+ }
+ }
+
+ jsonMsg(c, "Client(s) added", nil)
+ if err == nil && needRestart {
+ a.xrayService.SetToNeedRestart()
+ }
+
+}
+
func (a *InboundController) delInboundClient(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
@@ -230,6 +261,56 @@ func (a *InboundController) updateInboundClient(c *gin.Context) {
}
}
+func (a *InboundController) updateGroupInboundClient(c *gin.Context) {
+ var requestData []map[string]interface{}
+
+ if err := c.ShouldBindJSON(&requestData); err != nil {
+ jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err)
+ return
+ }
+
+ needRestart := false
+
+ for _, item := range requestData {
+
+ inboundMap, ok := item["inbound"].(map[string]interface{})
+ if !ok {
+ jsonMsg(c, "Something went wrong!", errors.New("Failed to convert 'inbound' to map"))
+ return
+ }
+
+ clientId, ok := item["clientId"].(string)
+ if !ok {
+ jsonMsg(c, "Something went wrong!", errors.New("Failed to convert 'clientId' to string"))
+ return
+ }
+
+ inboundJSON, err := json.Marshal(inboundMap)
+ if err != nil {
+ jsonMsg(c, "Something went wrong!", err)
+ return
+ }
+
+ var inboundModel model.Inbound
+ if err := json.Unmarshal(inboundJSON, &inboundModel); err != nil {
+ jsonMsg(c, "Something went wrong!", err)
+ return
+ }
+
+ if restart, err := a.inboundService.UpdateInboundClient(&inboundModel, clientId); err != nil {
+ jsonMsg(c, "Something went wrong!", err)
+ return
+ } else {
+ needRestart = needRestart || restart
+ }
+ }
+
+ jsonMsg(c, "Client updated", nil)
+ if needRestart {
+ a.xrayService.SetToNeedRestart()
+ }
+}
+
func (a *InboundController) resetClientTraffic(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
diff --git a/web/html/xui/client_modal.html b/web/html/xui/client_modal.html
index 02c548e3..cb15e1e7 100644
--- a/web/html/xui/client_modal.html
+++ b/web/html/xui/client_modal.html
@@ -16,12 +16,15 @@
title: '',
okText: '',
group: {
+ canGroup: true,
isGroup: false,
currentClient: null,
inbounds: [],
clients: [],
+ editIds: []
},
dbInbound: new DBInbound(),
+ dbInbounds: null,
inbound: new Inbound(),
clients: [],
clientStats: [],
@@ -30,64 +33,95 @@
clientIps: null,
delayedStart: false,
ok() {
- if (clientModal.isEdit) {
- ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);
- } else {
- if (clientModal.group.isGroup) {
- const currentClient = clientModal.group.currentClient;
+ if (clientModal.group.isGroup && clientModal.group.canGroup) {
+ const currentClient = clientModal.group.currentClient;
- clientModal.group.clients.forEach((client, index) => {
- const { email, limitIp, totalGB, expiryTime, reset, enable, subId, tgId, flow } = currentClient;
+ clientModal.group.clients.forEach((client, index) => {
+ const { email, limitIp, totalGB, expiryTime, reset, enable, subId, tgId, flow } = currentClient;
- client.email = `${email}-${index + 1}`;
- client.limitIp = limitIp;
- client.totalGB = totalGB;
- client.expiryTime = expiryTime;
- client.reset = reset;
- client.enable = enable;
+ const match = email.match(/^(.*?)__/);
+ const new_email = match ? match[1] : email;
- if (subId) {
- client.subId = subId;
- }
- if (tgId) {
- client.tgId = tgId;
- }
- if (flow) {
- client.flow = flow;
- }
- });
+ client.email = `${new_email}__${index + 1}`;
+ client.limitIp = limitIp;
+ client.totalGB = totalGB;
+ client.expiryTime = expiryTime;
+ client.reset = reset;
+ client.enable = enable;
+
+ if (subId) {
+ client.subId = subId;
+ }
+ if (tgId) {
+ client.tgId = tgId;
+ }
+ if (flow) {
+ client.flow = flow;
+ }
+ });
+
+ if (clientModal.isEdit) {
+ ObjectUtil.execute(clientModal.confirm, clientModal.group.clients, clientModal.group.inbounds, clientModal.group.editIds);
+ }else{
ObjectUtil.execute(clientModal.confirm, clientModal.group.clients, clientModal.group.inbounds);
- } else {
+ }
+ } else {
+ if (clientModal.isEdit){
+ ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);
+ }else{
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
}
}
},
- show({ title = '', okText = '{{ i18n "sure" }}', index = null, dbInbound = null, confirm = () => { }, isEdit = false }) {
+ show({ title = '', okText = '{{ i18n "sure" }}', index = null, dbInbound = null, dbInbounds = null, confirm = () => { }, isEdit = false }) {
this.group = {
+ canGroup: true,
isGroup: false,
currentClient: null,
inbounds: [],
clients: [],
+ editIds: []
}
+ this.dbInbounds = dbInbounds;
this.visible = true;
this.title = title;
this.okText = okText;
this.isEdit = isEdit;
- if (Array.isArray(dbInbound)) {
- this.group.isGroup = true;
- dbInbound.forEach((dbInboundItem) => {
- this.showProcess(dbInboundItem);
- this.group.inbounds.push(dbInboundItem.id)
- this.group.clients.push(this.clients[this.index])
- })
- this.group.currentClient = this.clients[this.index]
+ if (dbInbounds !== null && Array.isArray(dbInbounds)) {
+ if (isEdit) {
+ this.showProcess(dbInbound, index);
+ let processSingleEdit = true
+ if (this.group.canGroup){
+ this.group.currentClient = this.clients[this.index]
+ const response = this.getGroupInboundsClients(dbInbounds,this.group.currentClient)
+ if (response.clients.length > 1){
+ this.group.isGroup = true;
+ this.group.inbounds = response.inbounds
+ this.group.clients = response.clients
+ this.group.editIds = response.editIds
+ if (this.clients[index].expiryTime < 0) {
+ this.delayedStart = true;
+ }
+ processSingleEdit = false
+ }
+ }
+ if(processSingleEdit){
+ this.singleEditClientProcess(index)
+ }
+ } else {
+ this.group.isGroup = true;
+ dbInbounds.forEach((dbInboundItem) => {
+ this.showProcess(dbInboundItem);
+ this.addClient(this.inbound.protocol, this.clients);
+ this.group.inbounds.push(dbInboundItem.id)
+ this.group.clients.push(this.clients[this.index])
+ })
+ this.group.currentClient = this.clients[this.index]
+ }
} else {
this.showProcess(dbInbound, index);
if (isEdit) {
- if (this.clients[index].expiryTime < 0) {
- this.delayedStart = true;
- }
- this.oldClientId = this.getClientId(dbInbound.protocol, clients[index]);
+ this.singleEditClientProcess(index)
} else {
this.addClient(this.inbound.protocol, this.clients);
}
@@ -101,7 +135,34 @@
this.clients = this.inbound.clients;
this.index = index === null ? this.clients.length : index;
this.delayedStart = false;
- this.addClient(this.inbound.protocol, this.clients);
+ },
+ singleEditClientProcess(index) {
+ if (this.clients[index].expiryTime < 0) {
+ this.delayedStart = true;
+ }
+ this.oldClientId = this.getClientId(this.dbInbound.protocol, this.clients[index]);
+ },
+ getGroupInboundsClients(dbInbounds, currentClient) {
+ const response = {
+ inbounds: [],
+ clients: [],
+ editIds: []
+ }
+ dbInbounds.forEach((dbInboundItem) => {
+ const dbInbound = new DBInbound(dbInboundItem);
+ const inbound = dbInbound.toInbound();
+ const clients = inbound.clients;
+ if (clients.length > 0){
+ clients.forEach((client) => {
+ if (client['subId'] === currentClient['subId']){
+ response.inbounds.push(dbInboundItem.id)
+ response.clients.push(client)
+ response.editIds.push(this.getClientId(dbInbound.protocol, client))
+ }
+ })
+ }
+ })
+ return response;
},
getClientId(protocol, client) {
switch (protocol) {
@@ -148,6 +209,15 @@
get isGroup() {
return this.clientModal.group.isGroup;
},
+ get isGroupEdit() {
+ return this.clientModal.group.canGroup;
+ },
+ set isGroupEdit(value) {
+ this.clientModal.group.canGroup = value;
+ if (!value){
+ this.clientModal.singleEditClientProcess(this.clientModal.index)
+ }
+ },
get datepicker() {
return app.datepicker;
},
diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html
index f4ac25f3..434a806e 100644
--- a/web/html/xui/form/client.html
+++ b/web/html/xui/form/client.html
@@ -3,6 +3,18 @@
+
+
+
+
+ {{ i18n "pages.inbounds.isGroupEditDesc" }}
+
+ {{ i18n "pages.inbounds.isGroupEdit" }}
+
+
+
+
+
diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html
index 53693ab3..e5d4b821 100644
--- a/web/html/xui/inbounds.html
+++ b/web/html/xui/inbounds.html
@@ -894,7 +894,7 @@
clientModal.show({
title: '{{ i18n "pages.client.groupAdd"}}',
okText: '{{ i18n "pages.client.submitAdd"}}',
- dbInbound: this.dbInbounds,
+ dbInbounds: this.dbInbounds,
confirm: async (clients, dbInboundIds) => {
clientModal.loading();
await this.addGroupClient(clients, dbInboundIds);
@@ -939,6 +939,7 @@
clientModal.show({
title: '{{ i18n "pages.client.edit"}}',
okText: '{{ i18n "pages.client.submitEdit"}}',
+ dbInbounds: this.dbInbounds,
dbInbound: dbInbound,
index: index,
confirm: async (client, dbInboundId, clientId) => {
@@ -958,10 +959,10 @@
}
},
async addClient(clients, dbInboundId) {
- const data = [{
+ const data = {
id: dbInboundId,
settings: '{"clients": [' + clients.toString() + ']}',
- }];
+ };
await this.submit(`/panel/inbound/addClient`, data, true)
},
@@ -975,14 +976,28 @@
})
})
- await this.submit(`/panel/inbound/addClient`, data, true)
+ await this.submit(`/panel/inbound/addGroupClient`, data, true)
},
async updateClient(client, dbInboundId, clientId) {
- const data = {
- id: dbInboundId,
- settings: '{"clients": [' + client.toString() + ']}',
- };
- await this.submit(`/panel/inbound/updateClient/${clientId}`, data);
+ if (Array.isArray(client) && Array.isArray(dbInboundId) && Array.isArray(clientId)){
+ const data = []
+ client.forEach((client, index) => {
+ data.push({
+ clientId: clientId[index],
+ inbound: {
+ id: dbInboundId[index],
+ settings: '{"clients": [' + client.toString() + ']}',
+ }
+ })
+ })
+ await this.submit(`/panel/inbound/updateClients`, data, true);
+ }else{
+ const data = {
+ id: dbInboundId,
+ settings: '{"clients": [' + client.toString() + ']}',
+ };
+ await this.submit(`/panel/inbound/updateClient/${clientId}`, data);
+ }
},
resetTraffic(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index b2d74c3b..4fcb50c6 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -181,6 +181,8 @@
"exportInbound" = "Export Inbound"
"import" = "Import"
"importInbound" = "Import an Inbound"
+"isGroupEdit" = "Group editing"
+"isGroupEditDesc" = "All clients with the same subscription are edited"
[pages.client]
"add" = "Add Client"
diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml
index 83c9460b..a61689f9 100644
--- a/web/translation/translate.es_ES.toml
+++ b/web/translation/translate.es_ES.toml
@@ -181,6 +181,8 @@
"exportInbound" = "Exportación entrante"
"import" = "Importar"
"importInbound" = "Importar un entrante"
+"isGroupEdit" = "Edición de grupo"
+"isGroupEditDesc" = "Se editan todos los usuarios con la misma suscripción"
[pages.client]
"add" = "Agregar Cliente"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index 9613c12e..38c28579 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -181,6 +181,8 @@
"exportInbound" = "استخراج ورودی"
"import" = "افزودن"
"importInbound" = "افزودن یک ورودی"
+"isGroupEdit" = "ویرایش گروهی"
+"isGroupEditDesc" = "تمامی کاربران با سابسکریپشن یکسان ویرایش میشوند"
[pages.client]
"add" = "کاربر جدید"
diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml
index 7cf2be0a..4bfda2b9 100644
--- a/web/translation/translate.ru_RU.toml
+++ b/web/translation/translate.ru_RU.toml
@@ -181,6 +181,8 @@
"exportInbound" = "Экспорт входящих"
"import" = "Импортировать"
"importInbound" = "Импортировать входящее сообщение"
+"isGroupEdit" = "Редактирование группы"
+"isGroupEditDesc" = "Редактируются все пользователи с одной подпиской"
[pages.client]
"add" = "Добавить пользователя"
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index 9cd7bc46..cd19328d 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -181,6 +181,8 @@
"exportInbound" = "Xuất nhập khẩu"
"import" = "Nhập"
"importInbound" = "Nhập inbound"
+"isGroupEdit" = "Chỉnh sửa nhóm"
+"isGroupEditDesc" = "Tất cả người dùng có cùng đăng ký đều được chỉnh sửa"
[pages.client]
"add" = "Thêm người dùng"
diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml
index c229dc29..a7399642 100644
--- a/web/translation/translate.zh_Hans.toml
+++ b/web/translation/translate.zh_Hans.toml
@@ -181,6 +181,8 @@
"exportInbound" = "出口 入境"
"import"="导入"
"importInbound" = "导入入站"
+"isGroupEdit" = "分组编辑"
+"isGroupEditDesc" = "编辑具有相同订阅的所有用户"
[pages.client]
"add" = "添加客户端"
From 538fc9b3656afe260af74a71828e15aa069e1434 Mon Sep 17 00:00:00 2001
From: Ali Rahimi
Date: Tue, 23 Jan 2024 11:00:21 +0100
Subject: [PATCH 14/26] add single client bug fixed (#1664)
---
web/controller/inbound.go | 19 ++++++-------------
web/html/xui/inbounds.html | 2 +-
2 files changed, 7 insertions(+), 14 deletions(-)
diff --git a/web/controller/inbound.go b/web/controller/inbound.go
index 4d6e0af0..0aa2e2df 100644
--- a/web/controller/inbound.go
+++ b/web/controller/inbound.go
@@ -162,10 +162,8 @@ func (a *InboundController) clearClientIps(c *gin.Context) {
}
func (a *InboundController) addInboundClient(c *gin.Context) {
- var requestData []model.Inbound
-
- err := c.ShouldBindJSON(&requestData)
-
+ data := &model.Inbound{}
+ err := c.ShouldBind(data)
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err)
return
@@ -173,20 +171,15 @@ func (a *InboundController) addInboundClient(c *gin.Context) {
needRestart := true
- for _, data := range requestData {
-
- needRestart, err = a.inboundService.AddInboundClient(&data)
- if err != nil {
- jsonMsg(c, "Something went wrong!", err)
- return
- }
+ needRestart, err = a.inboundService.AddInboundClient(data)
+ if err != nil {
+ jsonMsg(c, "Something went wrong!", err)
+ return
}
-
jsonMsg(c, "Client(s) added", nil)
if err == nil && needRestart {
a.xrayService.SetToNeedRestart()
}
-
}
func (a *InboundController) addGroupInboundClient(c *gin.Context) {
diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html
index e5d4b821..faff7a0f 100644
--- a/web/html/xui/inbounds.html
+++ b/web/html/xui/inbounds.html
@@ -964,7 +964,7 @@
settings: '{"clients": [' + clients.toString() + ']}',
};
- await this.submit(`/panel/inbound/addClient`, data, true)
+ await this.submit(`/panel/inbound/addClient`, data)
},
async addGroupClient(clients, dbInboundIds) {
From c1422be269bcfe4e5fcdd2095224ac55e50e2d8c Mon Sep 17 00:00:00 2001
From: Ali Rahimi
Date: Tue, 23 Jan 2024 20:46:33 +0100
Subject: [PATCH 15/26] persian datepicker bug fixed (#1668)
* add single client bug fixed
* persian datepicker bug fixed
---
web/html/xui/client_bulk_modal.html | 2 +-
web/html/xui/component/persianDatepicker.html | 6 +++---
web/html/xui/component/themeSwitch.html | 2 ++
web/html/xui/form/client.html | 2 +-
web/html/xui/form/inbound.html | 4 ++--
web/translation/translate.en_US.toml | 1 +
web/translation/translate.es_ES.toml | 1 +
web/translation/translate.fa_IR.toml | 1 +
web/translation/translate.ru_RU.toml | 1 +
web/translation/translate.vi_VN.toml | 1 +
web/translation/translate.zh_Hans.toml | 1 +
11 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/web/html/xui/client_bulk_modal.html b/web/html/xui/client_bulk_modal.html
index 0f3ea24c..9ae84e5a 100644
--- a/web/html/xui/client_bulk_modal.html
+++ b/web/html/xui/client_bulk_modal.html
@@ -106,7 +106,7 @@
-
diff --git a/web/html/xui/component/persianDatepicker.html b/web/html/xui/component/persianDatepicker.html
index 34ae4f8d..10ef8472 100644
--- a/web/html/xui/component/persianDatepicker.html
+++ b/web/html/xui/component/persianDatepicker.html
@@ -3,7 +3,7 @@
+ :placeholder="placeholder">
@@ -21,7 +21,7 @@
const persianDatepicker = {};
Vue.component('persian-datepicker', {
- props: ['dropdown-class-name', 'format', 'value'],
+ props: ['placeholder', 'format', 'value'],
template: `{{template "component/persianDatepickerTemplate"}}`,
data() {
return {
@@ -48,7 +48,7 @@
listenToDatepicker() {
jalaliDatepicker.startWatch({
time: true,
- container: '.ant-modal-wrap',
+ zIndex: '9999',
hideAfterChange: true,
useDropDownYears: false,
changeMonthRotateYear: true,
diff --git a/web/html/xui/component/themeSwitch.html b/web/html/xui/component/themeSwitch.html
index 4d5412e2..3bc1ad7d 100644
--- a/web/html/xui/component/themeSwitch.html
+++ b/web/html/xui/component/themeSwitch.html
@@ -11,6 +11,7 @@
function createThemeSwitcher() {
const isDarkTheme = localStorage.getItem('dark-mode') === 'true';
const theme = isDarkTheme ? 'dark' : 'light';
+ document.querySelector('body').setAttribute('class', theme)
return {
isDarkTheme,
get currentTheme() {
@@ -19,6 +20,7 @@
toggleTheme() {
this.isDarkTheme = !this.isDarkTheme;
localStorage.setItem('dark-mode', this.isDarkTheme);
+ document.querySelector('body').setAttribute('class', this.isDarkTheme ? 'dark' : 'light')
},
};
}
diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html
index 434a806e..526bbb8b 100644
--- a/web/html/xui/form/client.html
+++ b/web/html/xui/form/client.html
@@ -164,7 +164,7 @@
-
Expired
diff --git a/web/html/xui/form/inbound.html b/web/html/xui/form/inbound.html
index 92ba6e75..048fc818 100644
--- a/web/html/xui/form/inbound.html
+++ b/web/html/xui/form/inbound.html
@@ -57,8 +57,8 @@
-
+
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index 4fcb50c6..0b7ac5b2 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -251,6 +251,7 @@
"pageSizeDesc" = "Define page size for inbounds table. (0 = disable)"
"remarkModel" = "Remark Model & Separation Character"
"datepicker" = "Calendar Type"
+"datepickerPlaceholder" = "Select date"
"datepickerDescription" = "Scheduled tasks will run based on this calendar."
"sampleRemark" = "Sample Remark"
"oldUsername" = "Current Username"
diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml
index a61689f9..926325cf 100644
--- a/web/translation/translate.es_ES.toml
+++ b/web/translation/translate.es_ES.toml
@@ -251,6 +251,7 @@
"pageSizeDesc" = "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar"
"remarkModel" = "Modelo de observación y carácter de separación"
"datepicker" = "selector de fechas"
+"datepickerPlaceholder" = "Seleccionar fecha"
"datepickerDescription" = "El tipo de calendario selector especifica la fecha de vencimiento"
"sampleRemark" = "Observación de muestra"
"oldUsername" = "Nombre de Usuario Actual"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index 38c28579..17e6facc 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -251,6 +251,7 @@
"pageSizeDesc" = "(اندازه صفحه برای جدول ورودیها.(0 = غیرفعال"
"remarkModel" = "نامکانفیگ و جداکننده"
"datepicker" = "نوع تقویم"
+"datepickerPlaceholder" = "انتخاب تاریخ"
"datepickerDescription" = "وظایف برنامه ریزی شده بر اساس این تقویم اجرا میشود"
"sampleRemark" = "نمونهنام"
"oldUsername" = "نامکاربری فعلی"
diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml
index 4bfda2b9..c567f38b 100644
--- a/web/translation/translate.ru_RU.toml
+++ b/web/translation/translate.ru_RU.toml
@@ -251,6 +251,7 @@
"pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить"
"remarkModel" = "Модель примечания и символ разделения"
"datepicker" = "выбор даты"
+"datepickerPlaceholder" = "Выберите дату"
"datepickerDescription" = "Тип календаря выбора указывает дату истечения срока действия."
"sampleRemark" = "Пример замечания"
"oldUsername" = "Текущее имя пользователя"
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index cd19328d..5876a5b4 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -251,6 +251,7 @@
"pageSizeDesc" = "Xác định kích thước trang cho bảng gửi đến. Đặt 0 để tắt"
"remarkModel" = "Ghi chú mô hình và ký tự phân tách"
"datepicker" = "Kiểu lịch"
+"datepickerPlaceholder" = "Chọn ngày"
"datepickerDescription" = "Tác vụ chạy theo lịch trình sẽ chạy theo kiểu lịch này."
"sampleRemark" = "Nhận xét mẫu"
"oldUsername" = "Tên người dùng hiện tại"
diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml
index a7399642..29dc8f4c 100644
--- a/web/translation/translate.zh_Hans.toml
+++ b/web/translation/translate.zh_Hans.toml
@@ -251,6 +251,7 @@
"pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用"
"remarkModel" = "备注模型和分隔符"
"datepicker" = "日期选择器"
+"datepickerPlaceholder" = "选择日期"
"datepickerDescription" = "选择器日历类型指定到期日期"
"sampleRemark" = "备注示例"
"oldUsername" = "原用户名"
From 251ceeedbad52fc05c84c431a3d982d558a59d7d Mon Sep 17 00:00:00 2001
From: Ali Rahimi
Date: Tue, 23 Jan 2024 22:53:15 +0100
Subject: [PATCH 16/26] bug fixed (import inbounds) (#1670)
* add single client bug fixed
* bug fixed
---
web/html/xui/inbounds.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html
index faff7a0f..2707fc42 100644
--- a/web/html/xui/inbounds.html
+++ b/web/html/xui/inbounds.html
@@ -1243,7 +1243,7 @@
value: '',
okText: '{{ i18n "pages.inbounds.import" }}',
confirm: async (dbInboundText) => {
- await this.submit('/panel/inbound/import', {data: dbInboundText}, promptModal);
+ await this.submit('/panel/inbound/import', {data: dbInboundText});
promptModal.close();
},
});
From ff547a258d4866440920a293a1e0eca75b2e43f3 Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Wed, 24 Jan 2024 01:24:17 +0330
Subject: [PATCH 17/26] gen button - publicKey & psk
---
web/html/xui/form/outbound.html | 22 ++++++++++++++++++++--
web/html/xui/form/protocol/wireguard.html | 22 ++++++++++++++++++++--
2 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/web/html/xui/form/outbound.html b/web/html/xui/form/outbound.html
index 8bea1fe5..3f11907d 100644
--- a/web/html/xui/form/outbound.html
+++ b/web/html/xui/form/outbound.html
@@ -134,10 +134,28 @@
-
+
+
+
+
+ {{ i18n "reset" }}
+
+ {{ i18n "pages.xray.wireguard.publicKey" }}
+
+
+
-
+
+
+
+
+ {{ i18n "reset" }}
+
+ {{ i18n "pages.xray.wireguard.psk" }}
+
+
+
diff --git a/web/html/xui/form/protocol/wireguard.html b/web/html/xui/form/protocol/wireguard.html
index 553f5d42..ea2c3427 100644
--- a/web/html/xui/form/protocol/wireguard.html
+++ b/web/html/xui/form/protocol/wireguard.html
@@ -32,10 +32,28 @@
inbound.settings.delPeer(index)"
style="color: rgb(255, 77, 79);cursor: pointer;"/>
-
+
+
+
+
+ {{ i18n "reset" }}
+
+ {{ i18n "pages.xray.wireguard.publicKey" }}
+
+
+
-
+
+
+
+
+ {{ i18n "reset" }}
+
+ {{ i18n "pages.xray.wireguard.psk" }}
+
+
+
From 2463b99479c3cb20ea8c1b907f498d665551621c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 24 Jan 2024 15:13:53 +0330
Subject: [PATCH 18/26] Bump google.golang.org/grpc from 1.60.1 to 1.61.0
(#1671)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.60.1 to 1.61.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.60.1...v1.61.0)
---
updated-dependencies:
- dependency-name: google.golang.org/grpc
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
go.mod | 2 +-
go.sum | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/go.mod b/go.mod
index 4e84c3e2..80fd416a 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
github.com/xtls/xray-core v1.8.7
go.uber.org/atomic v1.11.0
golang.org/x/text v0.14.0
- google.golang.org/grpc v1.60.1
+ google.golang.org/grpc v1.61.0
gorm.io/driver/sqlite v1.5.4
gorm.io/gorm v1.25.5
)
diff --git a/go.sum b/go.sum
index f5d3b46b..1663fce7 100644
--- a/go.sum
+++ b/go.sum
@@ -413,8 +413,8 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
-google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
+google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
+google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
From 979fdedbbe1f36962bf72349ba21de4223286227 Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Wed, 24 Jan 2024 20:50:58 +0330
Subject: [PATCH 19/26] [bug] avoid empty inbound
Co-Authored-By: Alireza Ahmadi
---
web/service/inbound.go | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/web/service/inbound.go b/web/service/inbound.go
index 2a4966ab..f3445101 100644
--- a/web/service/inbound.go
+++ b/web/service/inbound.go
@@ -317,7 +317,6 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
oldInbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
}
-
needRestart := false
s.xrayApi.Init(p.GetAPIPort())
if s.xrayApi.DelInbound(tag) == nil {
@@ -509,6 +508,10 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
}
}
+ if len(newClients) == 0 {
+ return false, common.NewError("no client remained in Inbound")
+ }
+
settings["clients"] = newClients
newSettings, err := json.MarshalIndent(settings, "", " ")
if err != nil {
From 5f9058c84f6a46a2233f323dcdac8281975accc0 Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Fri, 26 Jan 2024 16:50:22 +0330
Subject: [PATCH 20/26] fix downloaded log format
Co-Authored-By: Alireza Ahmadi
---
web/html/xui/index.html | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/web/html/xui/index.html b/web/html/xui/index.html
index 3c01a29f..258d4154 100644
--- a/web/html/xui/index.html
+++ b/web/html/xui/index.html
@@ -299,12 +299,12 @@
+ :href="'data:application/text;charset=utf-8,' + encodeURIComponent(logModal.logs.join('\n'))" download="x-ui.log">
{{ i18n "download" }} x-ui.log
-
+
0 ? this.formatLogs(logs) : "No Record...";
},
formatLogs(logs) {
let formattedLogs = '';
From 98384ac2365cabe6a4a200062a90b59f6ce5ad72 Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Fri, 26 Jan 2024 16:50:35 +0330
Subject: [PATCH 21/26] fix - direct tag
---
web/service/config.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/web/service/config.json b/web/service/config.json
index c846d590..82f7dddf 100644
--- a/web/service/config.json
+++ b/web/service/config.json
@@ -24,6 +24,7 @@
],
"outbounds": [
{
+ "tag": "direct",
"protocol": "freedom",
"settings": {}
},
From 08e259327b8a7b606b95e3b145fe1959b426d7c4 Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Fri, 26 Jan 2024 17:06:54 +0330
Subject: [PATCH 22/26] unnecessary use of fmt.Sprintf
---
web/service/xray_setting.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/service/xray_setting.go b/web/service/xray_setting.go
index 1d2e696e..86bb1d7b 100644
--- a/web/service/xray_setting.go
+++ b/web/service/xray_setting.go
@@ -80,7 +80,7 @@ func (s *XraySettingService) RegWarp(secretKey string, publicKey string) (string
hostName, _ := os.Hostname()
data := fmt.Sprintf(`{"key":"%s","tos":"%s","type": "PC","model": "x-ui", "name": "%s"}`, publicKey, tos, hostName)
- url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg")
+ url := "https://api.cloudflareclient.com/v0a2158/reg"
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(data)))
if err != nil {
From ec88053df0f2619bdb6285bcd2085675efbc8ca8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 26 Jan 2024 20:15:07 +0330
Subject: [PATCH 23/26] Bump gorm.io/gorm from 1.25.5 to 1.25.6 (#1683)
Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.25.5 to 1.25.6.
- [Release notes](https://github.com/go-gorm/gorm/releases)
- [Commits](https://github.com/go-gorm/gorm/compare/v1.25.5...v1.25.6)
---
updated-dependencies:
- dependency-name: gorm.io/gorm
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
go.mod | 2 +-
go.sum | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/go.mod b/go.mod
index 80fd416a..d46ed1fe 100644
--- a/go.mod
+++ b/go.mod
@@ -19,7 +19,7 @@ require (
golang.org/x/text v0.14.0
google.golang.org/grpc v1.61.0
gorm.io/driver/sqlite v1.5.4
- gorm.io/gorm v1.25.5
+ gorm.io/gorm v1.25.6
)
require (
diff --git a/go.sum b/go.sum
index 1663fce7..8b6465d7 100644
--- a/go.sum
+++ b/go.sum
@@ -437,8 +437,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
-gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
-gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
+gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A=
+gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b h1:yqkg3pTifuKukuWanp8spDsL4irJkHF5WI0J47hU87o=
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
From daa43540477e18ae5c6e19e887733b793c17ad72 Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Fri, 26 Jan 2024 21:57:46 +0330
Subject: [PATCH 24/26] ipv6 for familyProtect and some changes
---
web/assets/js/model/outbound.js | 4 ++--
web/assets/js/model/xray.js | 2 +-
web/html/xui/warp_modal.html | 1 +
web/html/xui/xray.html | 6 ++++--
4 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js
index 0191c4a4..42fb50a1 100644
--- a/web/assets/js/model/outbound.js
+++ b/web/assets/js/model/outbound.js
@@ -915,7 +915,7 @@ Outbound.HttpSettings = class extends CommonClass {
Outbound.WireguardSettings = class extends CommonClass {
constructor(
mtu=1420, secretKey=Wireguard.generateKeypair().privateKey,
- address='', workers=2, domainStrategy='', reserved='',
+ address=[''], workers=2, domainStrategy='ForceIPv6v4', reserved='',
peers=[new Outbound.WireguardSettings.Peer()], kernelMode=false) {
super();
this.mtu = mtu;
@@ -965,7 +965,7 @@ Outbound.WireguardSettings = class extends CommonClass {
};
Outbound.WireguardSettings.Peer = class extends CommonClass {
- constructor(publicKey='', psk='', allowedIPs=['0.0.0.0/0','::/0'], endpoint='', keepAlive=0) {
+ constructor(publicKey=Wireguard.generateKeypair().publicKey, psk='', allowedIPs=['0.0.0.0/0','::/0'], endpoint='', keepAlive=0) {
super();
this.publicKey = publicKey;
this.psk = psk;
diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js
index b2c8a2e5..e9676252 100644
--- a/web/assets/js/model/xray.js
+++ b/web/assets/js/model/xray.js
@@ -2297,7 +2297,7 @@ Inbound.WireguardSettings = class extends XrayCommonClass {
};
Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
- constructor(publicKey='', psk='', allowedIPs=['0.0.0.0/0','::/0'], keepAlive=0) {
+ constructor(publicKey=Wireguard.generateKeypair().publicKey, psk='', allowedIPs=['0.0.0.0/0','::/0'], keepAlive=0) {
super();
this.publicKey = publicKey;
this.psk = psk;
diff --git a/web/html/xui/warp_modal.html b/web/html/xui/warp_modal.html
index d1663df0..4e6dd4fa 100644
--- a/web/html/xui/warp_modal.html
+++ b/web/html/xui/warp_modal.html
@@ -140,6 +140,7 @@
mtu: 1420,
secretKey: warpModal.warpData.private_key,
address: Object.values(config.interface.addresses),
+ domainStrategy: 'ForceIPv6v4',
peers: [{
publicKey: peer.public_key,
endpoint: peer.endpoint.host,
diff --git a/web/html/xui/xray.html b/web/html/xui/xray.html
index 05f5f178..d6f0c0f8 100644
--- a/web/html/xui/xray.html
+++ b/web/html/xui/xray.html
@@ -569,9 +569,11 @@
familyProtectDNS: {
"servers": [
"1.1.1.3", // https://developers.cloudflare.com/1.1.1.1/setup/
- "1.0.0.3"
+ "1.0.0.3",
+ "2606:4700:4700::1113",
+ "2606:4700:4700::1003"
],
- "queryStrategy": "UseIPv4"
+ "queryStrategy": "UseIP"
},
}
},
From 9fba92d879767394535f70ba83f0a58d6b063a7d Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Fri, 26 Jan 2024 22:07:15 +0330
Subject: [PATCH 25/26] v2.1.2
revert #1650 #1661 #1664 #1670
made panel full of bug
---
.github/workflows/docker.yml | 2 +-
README.md | 4 +-
config/version | 2 +-
web/assets/js/util/utils.js | 35 ------
web/controller/inbound.go | 111 +++---------------
web/html/common/qrcode_modal.html | 17 +--
web/html/xui/client_modal.html | 154 +++----------------------
web/html/xui/form/client.html | 16 +--
web/html/xui/inbound_modal.html | 4 -
web/html/xui/inbounds.html | 82 +++----------
web/translation/translate.en_US.toml | 3 -
web/translation/translate.es_ES.toml | 3 -
web/translation/translate.fa_IR.toml | 3 -
web/translation/translate.ru_RU.toml | 3 -
web/translation/translate.vi_VN.toml | 3 -
web/translation/translate.zh_Hans.toml | 3 -
16 files changed, 57 insertions(+), 388 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 6afe8618..1115de37 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -36,6 +36,6 @@ jobs:
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
- platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386, linux/arm/v5
+ platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
diff --git a/README.md b/README.md
index 6b0beab7..389d6d1d 100644
--- a/README.md
+++ b/README.md
@@ -25,10 +25,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
## Install Custom Version
-To install your desired version, add the version to the end of the installation command. e.g., ver `v2.1.1`:
+To install your desired version, add the version to the end of the installation command. e.g., ver `v2.1.2`:
```
-bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.1.1
+bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.1.2
```
## Manual Install & Upgrade
diff --git a/config/version b/config/version
index 7c327287..8f9174b4 100644
--- a/config/version
+++ b/config/version
@@ -1 +1 @@
-2.1.1
\ No newline at end of file
+2.1.2
\ No newline at end of file
diff --git a/web/assets/js/util/utils.js b/web/assets/js/util/utils.js
index 48ff237d..61b322bd 100644
--- a/web/assets/js/util/utils.js
+++ b/web/assets/js/util/utils.js
@@ -83,41 +83,6 @@ class HttpUtil {
}
return msg;
}
-
- static async jsonPost(url, data) {
- let msg;
- try {
- const requestOptions = {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(data),
- };
- const resp = await fetch(url, requestOptions);
- const response = await resp.json();
-
- msg = this._respToMsg({data : response});
- } catch (e) {
- msg = new Msg(false, e.toString());
- }
- this._handleMsg(msg);
- return msg;
- }
-
- static async postWithModalJson(url, data, modal) {
- if (modal) {
- modal.loading(true);
- }
- const msg = await this.jsonPost(url, data);
- if (modal) {
- modal.loading(false);
- if (msg instanceof Msg && msg.success) {
- modal.close();
- }
- }
- return msg;
- }
}
class PromiseUtil {
diff --git a/web/controller/inbound.go b/web/controller/inbound.go
index 0aa2e2df..86da9813 100644
--- a/web/controller/inbound.go
+++ b/web/controller/inbound.go
@@ -1,7 +1,6 @@
package controller
import (
- "errors"
"encoding/json"
"fmt"
"strconv"
@@ -33,9 +32,7 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
g.POST("/clientIps/:email", a.getClientIps)
g.POST("/clearClientIps/:email", a.clearClientIps)
g.POST("/addClient", a.addInboundClient)
- g.POST("/addGroupClient", a.addGroupInboundClient)
g.POST("/:id/delClient/:clientId", a.delInboundClient)
- g.POST("/updateClients", a.updateGroupInboundClient)
g.POST("/updateClient/:clientId", a.updateInboundClient)
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
g.POST("/resetAllTraffics", a.resetAllTraffics)
@@ -163,51 +160,23 @@ func (a *InboundController) clearClientIps(c *gin.Context) {
func (a *InboundController) addInboundClient(c *gin.Context) {
data := &model.Inbound{}
- err := c.ShouldBind(data)
- if err != nil {
- jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err)
- return
- }
+ err := c.ShouldBind(data)
+ if err != nil {
+ jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err)
+ return
+ }
- needRestart := true
-
- needRestart, err = a.inboundService.AddInboundClient(data)
- if err != nil {
- jsonMsg(c, "Something went wrong!", err)
- return
- }
- jsonMsg(c, "Client(s) added", nil)
- if err == nil && needRestart {
- a.xrayService.SetToNeedRestart()
- }
-}
-
-func (a *InboundController) addGroupInboundClient(c *gin.Context) {
- var requestData []model.Inbound
-
- err := c.ShouldBindJSON(&requestData)
-
- if err != nil {
- jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err)
- return
- }
-
- needRestart := true
-
- for _, data := range requestData {
-
- needRestart, err = a.inboundService.AddInboundClient(&data)
- if err != nil {
- jsonMsg(c, "Something went wrong!", err)
- return
- }
- }
-
- jsonMsg(c, "Client(s) added", nil)
- if err == nil && needRestart {
- a.xrayService.SetToNeedRestart()
- }
+ needRestart := true
+ needRestart, err = a.inboundService.AddInboundClient(data)
+ if err != nil {
+ jsonMsg(c, "Something went wrong!", err)
+ return
+ }
+ jsonMsg(c, "Client(s) added", nil)
+ if err == nil && needRestart {
+ a.xrayService.SetToNeedRestart()
+ }
}
func (a *InboundController) delInboundClient(c *gin.Context) {
@@ -254,56 +223,6 @@ func (a *InboundController) updateInboundClient(c *gin.Context) {
}
}
-func (a *InboundController) updateGroupInboundClient(c *gin.Context) {
- var requestData []map[string]interface{}
-
- if err := c.ShouldBindJSON(&requestData); err != nil {
- jsonMsg(c, I18nWeb(c, "pages.inbounds.update"), err)
- return
- }
-
- needRestart := false
-
- for _, item := range requestData {
-
- inboundMap, ok := item["inbound"].(map[string]interface{})
- if !ok {
- jsonMsg(c, "Something went wrong!", errors.New("Failed to convert 'inbound' to map"))
- return
- }
-
- clientId, ok := item["clientId"].(string)
- if !ok {
- jsonMsg(c, "Something went wrong!", errors.New("Failed to convert 'clientId' to string"))
- return
- }
-
- inboundJSON, err := json.Marshal(inboundMap)
- if err != nil {
- jsonMsg(c, "Something went wrong!", err)
- return
- }
-
- var inboundModel model.Inbound
- if err := json.Unmarshal(inboundJSON, &inboundModel); err != nil {
- jsonMsg(c, "Something went wrong!", err)
- return
- }
-
- if restart, err := a.inboundService.UpdateInboundClient(&inboundModel, clientId); err != nil {
- jsonMsg(c, "Something went wrong!", err)
- return
- } else {
- needRestart = needRestart || restart
- }
- }
-
- jsonMsg(c, "Client updated", nil)
- if needRestart {
- a.xrayService.SetToNeedRestart()
- }
-}
-
func (a *InboundController) resetClientTraffic(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html
index 31b3450c..3c4fd929 100644
--- a/web/html/common/qrcode_modal.html
+++ b/web/html/common/qrcode_modal.html
@@ -11,12 +11,10 @@
Subscription
- {{ i18n "pages.inbounds.client" }}
-
-
- [[ row.remark ]]
-
-
+ {{ i18n "pages.inbounds.client" }}
+
+ [[ row.remark ]]
+
@@ -29,14 +27,12 @@
qrcodes: [],
clipboard: null,
visible: false,
- isJustSub: false,
subId: '',
- show: function (title = '', dbInbound, client, isJustSub = false) {
+ show: function (title = '', dbInbound, client) {
this.title = title;
this.dbInbound = dbInbound;
this.inbound = dbInbound.toInbound();
this.client = client;
- this.isJustSub = isJustSub;
this.subId = '';
this.qrcodes = [];
this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, client).forEach(l => {
@@ -57,9 +53,6 @@
el: '#qrcode-modal',
data: {
qrModal: qrModal,
- get isJustSub(){
- return qrModal.isJustSub
- }
},
methods: {
copyToClipboard(elmentId, content) {
diff --git a/web/html/xui/client_modal.html b/web/html/xui/client_modal.html
index cb15e1e7..4b270607 100644
--- a/web/html/xui/client_modal.html
+++ b/web/html/xui/client_modal.html
@@ -15,16 +15,8 @@
confirmLoading: false,
title: '',
okText: '',
- group: {
- canGroup: true,
- isGroup: false,
- currentClient: null,
- inbounds: [],
- clients: [],
- editIds: []
- },
+ isEdit: false,
dbInbound: new DBInbound(),
- dbInbounds: null,
inbound: new Inbound(),
clients: [],
clientStats: [],
@@ -33,137 +25,33 @@
clientIps: null,
delayedStart: false,
ok() {
- if (clientModal.group.isGroup && clientModal.group.canGroup) {
- const currentClient = clientModal.group.currentClient;
-
- clientModal.group.clients.forEach((client, index) => {
- const { email, limitIp, totalGB, expiryTime, reset, enable, subId, tgId, flow } = currentClient;
-
- const match = email.match(/^(.*?)__/);
- const new_email = match ? match[1] : email;
-
- client.email = `${new_email}__${index + 1}`;
- client.limitIp = limitIp;
- client.totalGB = totalGB;
- client.expiryTime = expiryTime;
- client.reset = reset;
- client.enable = enable;
-
- if (subId) {
- client.subId = subId;
- }
- if (tgId) {
- client.tgId = tgId;
- }
- if (flow) {
- client.flow = flow;
- }
- });
-
- if (clientModal.isEdit) {
- ObjectUtil.execute(clientModal.confirm, clientModal.group.clients, clientModal.group.inbounds, clientModal.group.editIds);
- }else{
- ObjectUtil.execute(clientModal.confirm, clientModal.group.clients, clientModal.group.inbounds);
- }
+ if (clientModal.isEdit) {
+ ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);
} else {
- if (clientModal.isEdit){
- ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);
- }else{
- ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
- }
+ ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
}
},
- show({ title = '', okText = '{{ i18n "sure" }}', index = null, dbInbound = null, dbInbounds = null, confirm = () => { }, isEdit = false }) {
- this.group = {
- canGroup: true,
- isGroup: false,
- currentClient: null,
- inbounds: [],
- clients: [],
- editIds: []
- }
- this.dbInbounds = dbInbounds;
+ show({ title = '', okText = '{{ i18n "sure" }}', index = null, dbInbound = null, confirm = () => { }, isEdit = false }) {
this.visible = true;
this.title = title;
this.okText = okText;
this.isEdit = isEdit;
- if (dbInbounds !== null && Array.isArray(dbInbounds)) {
- if (isEdit) {
- this.showProcess(dbInbound, index);
- let processSingleEdit = true
- if (this.group.canGroup){
- this.group.currentClient = this.clients[this.index]
- const response = this.getGroupInboundsClients(dbInbounds,this.group.currentClient)
- if (response.clients.length > 1){
- this.group.isGroup = true;
- this.group.inbounds = response.inbounds
- this.group.clients = response.clients
- this.group.editIds = response.editIds
- if (this.clients[index].expiryTime < 0) {
- this.delayedStart = true;
- }
- processSingleEdit = false
- }
- }
- if(processSingleEdit){
- this.singleEditClientProcess(index)
- }
- } else {
- this.group.isGroup = true;
- dbInbounds.forEach((dbInboundItem) => {
- this.showProcess(dbInboundItem);
- this.addClient(this.inbound.protocol, this.clients);
- this.group.inbounds.push(dbInboundItem.id)
- this.group.clients.push(this.clients[this.index])
- })
- this.group.currentClient = this.clients[this.index]
- }
- } else {
- this.showProcess(dbInbound, index);
- if (isEdit) {
- this.singleEditClientProcess(index)
- } else {
- this.addClient(this.inbound.protocol, this.clients);
- }
- }
- this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
- this.confirm = confirm;
- },
- showProcess(dbInbound, index = null) {
this.dbInbound = new DBInbound(dbInbound);
this.inbound = dbInbound.toInbound();
this.clients = this.inbound.clients;
this.index = index === null ? this.clients.length : index;
this.delayedStart = false;
- },
- singleEditClientProcess(index) {
- if (this.clients[index].expiryTime < 0) {
- this.delayedStart = true;
- }
- this.oldClientId = this.getClientId(this.dbInbound.protocol, this.clients[index]);
- },
- getGroupInboundsClients(dbInbounds, currentClient) {
- const response = {
- inbounds: [],
- clients: [],
- editIds: []
- }
- dbInbounds.forEach((dbInboundItem) => {
- const dbInbound = new DBInbound(dbInboundItem);
- const inbound = dbInbound.toInbound();
- const clients = inbound.clients;
- if (clients.length > 0){
- clients.forEach((client) => {
- if (client['subId'] === currentClient['subId']){
- response.inbounds.push(dbInboundItem.id)
- response.clients.push(client)
- response.editIds.push(this.getClientId(dbInbound.protocol, client))
- }
- })
+ if (isEdit) {
+ if (this.clients[index].expiryTime < 0) {
+ this.delayedStart = true;
}
- })
- return response;
- },
+ this.oldClientId = this.getClientId(dbInbound.protocol, clients[index]);
+ } else {
+ this.addClient(this.inbound.protocol, this.clients);
+ }
+ this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
+ this.confirm = confirm;
+ },
getClientId(protocol, client) {
switch (protocol) {
case Protocols.TROJAN: return client.password;
@@ -206,18 +94,6 @@
get isEdit() {
return this.clientModal.isEdit;
},
- get isGroup() {
- return this.clientModal.group.isGroup;
- },
- get isGroupEdit() {
- return this.clientModal.group.canGroup;
- },
- set isGroupEdit(value) {
- this.clientModal.group.canGroup = value;
- if (!value){
- this.clientModal.singleEditClientProcess(this.clientModal.index)
- }
- },
get datepicker() {
return app.datepicker;
},
diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html
index 526bbb8b..63cb8aef 100644
--- a/web/html/xui/form/client.html
+++ b/web/html/xui/form/client.html
@@ -3,18 +3,6 @@
-
-
-
-
- {{ i18n "pages.inbounds.isGroupEditDesc" }}
-
- {{ i18n "pages.inbounds.isGroupEdit" }}
-
-
-
-
-
@@ -27,7 +15,7 @@
-
+
@@ -40,7 +28,7 @@
-
+
diff --git a/web/html/xui/inbound_modal.html b/web/html/xui/inbound_modal.html
index 7f9ed740..ab42e09c 100644
--- a/web/html/xui/inbound_modal.html
+++ b/web/html/xui/inbound_modal.html
@@ -13,7 +13,6 @@
confirmLoading: false,
okText: '{{ i18n "sure" }}',
isEdit: false,
- isGroup: false,
confirm: null,
inbound: new Inbound(),
dbInbound: new DBInbound(),
@@ -61,9 +60,6 @@
get isEdit() {
return inModal.isEdit;
},
- get isGroup() {
- return inModal.isGroup;
- },
get client() {
return inModal.inbound.clients[0];
},
diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html
index 2707fc42..c986e3fd 100644
--- a/web/html/xui/inbounds.html
+++ b/web/html/xui/inbounds.html
@@ -145,10 +145,6 @@
{{ i18n "pages.inbounds.delDepletedClients" }}
-
-
- {{ i18n "pages.client.groupAdd"}}
-
@@ -289,7 +285,7 @@
[[ clientEmail ]]
[[ clientCount[dbInbound.id].online.length ]]
-
+
@@ -343,7 +339,7 @@
[[ dbInbound.toInbound().stream.network ]]
tls
reality
-
+
@@ -377,7 +373,7 @@
[[ clientEmail ]]
[[ clientCount[dbInbound.id].online.length ]]
-
+
@@ -744,9 +740,6 @@
case "delDepletedClients":
this.delDepletedClients(-1)
break;
- case "addGroupClient":
- this.openGroupAddClient()
- break;
}
},
clickAction(action, dbInbound) {
@@ -890,20 +883,6 @@
await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal);
},
- openGroupAddClient() {
- clientModal.show({
- title: '{{ i18n "pages.client.groupAdd"}}',
- okText: '{{ i18n "pages.client.submitAdd"}}',
- dbInbounds: this.dbInbounds,
- confirm: async (clients, dbInboundIds) => {
- clientModal.loading();
- await this.addGroupClient(clients, dbInboundIds);
- clientModal.close();
- await this.showQrcode(dbInboundIds[0],clients[0], true)
- },
- isEdit: false
- });
- },
openAddClient(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
clientModal.show({
@@ -914,7 +893,6 @@
clientModal.loading();
await this.addClient(clients, dbInboundId);
clientModal.close();
- await this.showQrcode(dbInboundId,clients)
},
isEdit: false
});
@@ -939,7 +917,6 @@
clientModal.show({
title: '{{ i18n "pages.client.edit"}}',
okText: '{{ i18n "pages.client.submitEdit"}}',
- dbInbounds: this.dbInbounds,
dbInbound: dbInbound,
index: index,
confirm: async (client, dbInboundId, clientId) => {
@@ -963,41 +940,14 @@
id: dbInboundId,
settings: '{"clients": [' + clients.toString() + ']}',
};
-
- await this.submit(`/panel/inbound/addClient`, data)
- },
-
- async addGroupClient(clients, dbInboundIds) {
- const data = []
- dbInboundIds.forEach((dbInboundId, index) => {
- data.push({
- id: dbInboundId,
- settings: '{"clients": [' + clients[index].toString() + ']}',
- })
- })
-
- await this.submit(`/panel/inbound/addGroupClient`, data, true)
+ await this.submit(`/panel/inbound/addClient`, data);
},
async updateClient(client, dbInboundId, clientId) {
- if (Array.isArray(client) && Array.isArray(dbInboundId) && Array.isArray(clientId)){
- const data = []
- client.forEach((client, index) => {
- data.push({
- clientId: clientId[index],
- inbound: {
- id: dbInboundId[index],
- settings: '{"clients": [' + client.toString() + ']}',
- }
- })
- })
- await this.submit(`/panel/inbound/updateClients`, data, true);
- }else{
- const data = {
- id: dbInboundId,
- settings: '{"clients": [' + client.toString() + ']}',
- };
- await this.submit(`/panel/inbound/updateClient/${clientId}`, data);
- }
+ const data = {
+ id: dbInboundId,
+ settings: '{"clients": [' + client.toString() + ']}',
+ };
+ await this.submit(`/panel/inbound/updateClient/${clientId}`, data);
},
resetTraffic(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
@@ -1051,8 +1001,8 @@
checkFallback(dbInbound) {
newDbInbound = new DBInbound(dbInbound);
if (dbInbound.listen.startsWith("@")){
- rootInbound = this.inbounds.find((i) =>
- i.isTcp &&
+ rootInbound = this.inbounds.find((i) =>
+ i.isTcp &&
['trojan','vless'].includes(i.protocol) &&
i.settings.fallbacks.find(f => f.dest === dbInbound.listen)
);
@@ -1068,10 +1018,10 @@
}
return newDbInbound;
},
- showQrcode(dbInboundId, client, isJustSub = false) {
+ showQrcode(dbInboundId, client) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
newDbInbound = this.checkFallback(dbInbound);
- qrModal.show('{{ i18n "qrCode"}}', newDbInbound, client, isJustSub);
+ qrModal.show('{{ i18n "qrCode"}}', newDbInbound, client);
},
showInfo(dbInboundId, client) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
@@ -1100,8 +1050,8 @@
await this.updateClient(clients[index], dbInboundId, clientId);
this.loading(false);
},
- async submit(url, data, isJson = false) {
- const msg = isJson ? await HttpUtil.postWithModalJson(url, data) : await HttpUtil.postWithModal(url, data);
+ async submit(url, data) {
+ const msg = await HttpUtil.postWithModal(url, data);
if (msg.success) {
await this.getDBInbounds();
}
@@ -1243,7 +1193,7 @@
value: '',
okText: '{{ i18n "pages.inbounds.import" }}',
confirm: async (dbInboundText) => {
- await this.submit('/panel/inbound/import', {data: dbInboundText});
+ await this.submit('/panel/inbound/import', {data: dbInboundText}, promptModal);
promptModal.close();
},
});
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index 0b7ac5b2..7e0f26c5 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -181,12 +181,9 @@
"exportInbound" = "Export Inbound"
"import" = "Import"
"importInbound" = "Import an Inbound"
-"isGroupEdit" = "Group editing"
-"isGroupEditDesc" = "All clients with the same subscription are edited"
[pages.client]
"add" = "Add Client"
-"groupAdd" = "Add subscription user"
"edit" = "Edit Client"
"submitAdd" = "Add Client"
"submitEdit" = "Save Changes"
diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml
index 926325cf..6c817a30 100644
--- a/web/translation/translate.es_ES.toml
+++ b/web/translation/translate.es_ES.toml
@@ -181,12 +181,9 @@
"exportInbound" = "Exportación entrante"
"import" = "Importar"
"importInbound" = "Importar un entrante"
-"isGroupEdit" = "Edición de grupo"
-"isGroupEditDesc" = "Se editan todos los usuarios con la misma suscripción"
[pages.client]
"add" = "Agregar Cliente"
-"groupAdd" = "Agregar usuario de suscripción"
"edit" = "Editar Cliente"
"submitAdd" = "Agregar Cliente"
"submitEdit" = "Guardar Cambios"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index 17e6facc..11b66788 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -181,12 +181,9 @@
"exportInbound" = "استخراج ورودی"
"import" = "افزودن"
"importInbound" = "افزودن یک ورودی"
-"isGroupEdit" = "ویرایش گروهی"
-"isGroupEditDesc" = "تمامی کاربران با سابسکریپشن یکسان ویرایش میشوند"
[pages.client]
"add" = "کاربر جدید"
-"groupAdd" = "کاربر جدید سابسکریپشن"
"edit" = "ویرایش کاربر"
"submitAdd" = "اضافه کردن"
"submitEdit" = "ذخیره تغییرات"
diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml
index c567f38b..2e663e4c 100644
--- a/web/translation/translate.ru_RU.toml
+++ b/web/translation/translate.ru_RU.toml
@@ -181,12 +181,9 @@
"exportInbound" = "Экспорт входящих"
"import" = "Импортировать"
"importInbound" = "Импортировать входящее сообщение"
-"isGroupEdit" = "Редактирование группы"
-"isGroupEditDesc" = "Редактируются все пользователи с одной подпиской"
[pages.client]
"add" = "Добавить пользователя"
-"groupAdd" = "Добавить пользователя подписки"
"edit" = "Редактировать пользователя"
"submitAdd" = "Добавить пользователя"
"submitEdit" = "Сохранить изменения"
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index 5876a5b4..4c40adca 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -181,12 +181,9 @@
"exportInbound" = "Xuất nhập khẩu"
"import" = "Nhập"
"importInbound" = "Nhập inbound"
-"isGroupEdit" = "Chỉnh sửa nhóm"
-"isGroupEditDesc" = "Tất cả người dùng có cùng đăng ký đều được chỉnh sửa"
[pages.client]
"add" = "Thêm người dùng"
-"groupAdd" = "Thêm người dùng đăng ký"
"edit" = "Chỉnh sửa người dùng"
"submitAdd" = "Thêm"
"submitEdit" = "Lưu thay đổi"
diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml
index 29dc8f4c..a8cd3aff 100644
--- a/web/translation/translate.zh_Hans.toml
+++ b/web/translation/translate.zh_Hans.toml
@@ -181,12 +181,9 @@
"exportInbound" = "出口 入境"
"import"="导入"
"importInbound" = "导入入站"
-"isGroupEdit" = "分组编辑"
-"isGroupEditDesc" = "编辑具有相同订阅的所有用户"
[pages.client]
"add" = "添加客户端"
-"groupAdd" = "添加订阅用户"
"edit" = "编辑客户端"
"submitAdd" = "添加客户端"
"submitEdit" = "保存修改"
From e04283c1fb3e56867c7922bab5f3d7edb29fe13c Mon Sep 17 00:00:00 2001
From: MHSanaei
Date: Sat, 27 Jan 2024 12:56:10 +0330
Subject: [PATCH 26/26] remove multi protocol script
---
.github/workflows/docker.yml | 4 +--
DockerInit.sh | 10 +------
Dockerfile | 12 ++++----
README.md | 17 +++++++++---
install.sh | 4 +--
x-ui.sh | 54 ++++++++----------------------------
6 files changed, 35 insertions(+), 66 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 1115de37..a7a8c790 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -18,7 +18,7 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0
- - name: Log in to GitHub Container Registry
+ - name: Login to GHCR
uses: docker/login-action@v3.0.0
with:
registry: ghcr.io
@@ -38,4 +38,4 @@ jobs:
push: ${{ github.event_name != 'pull_request' }}
platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386
tags: ${{ steps.meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
+ labels: ${{ steps.meta.outputs.labels }}
\ No newline at end of file
diff --git a/DockerInit.sh b/DockerInit.sh
index bbfcb04e..a2224999 100755
--- a/DockerInit.sh
+++ b/DockerInit.sh
@@ -1,5 +1,4 @@
#!/bin/sh
-
case $1 in
amd64)
ARCH="64"
@@ -21,28 +20,21 @@ case $1 in
ARCH="arm32-v6"
FNAME="armv6"
;;
- armv5)
- ARCH="arm32-v5"
- FNAME="armv5"
- ;;
*)
ARCH="64"
FNAME="amd64"
;;
esac
-
-
mkdir -p build/bin
cd build/bin
-
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.7/Xray-linux-${ARCH}.zip"
unzip "Xray-linux-${ARCH}.zip"
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
mv xray "xray-linux-${FNAME}"
-
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
wget -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
wget -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
wget -O geoip_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat
wget -O geosite_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat
+cd ../../
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 951d463b..7db1a175 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,11 +1,9 @@
# ========================================================
# Stage: Builder
# ========================================================
-FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder
+FROM golang:1.21-alpine AS builder
WORKDIR /app
ARG TARGETARCH
-ENV CGO_ENABLED=1
-ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
RUN apk --no-cache --update add \
build-base \
@@ -15,6 +13,8 @@ RUN apk --no-cache --update add \
COPY . .
+ENV CGO_ENABLED=1
+ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
RUN go build -o build/x-ui main.go
RUN ./DockerInit.sh "$TARGETARCH"
@@ -30,9 +30,9 @@ RUN apk add --no-cache --update \
tzdata \
fail2ban
-COPY --from=builder /app/build/ /app/
-COPY --from=builder /app/DockerEntrypoint.sh /app/
-COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
+COPY --from=builder /app/build/ /app/
+COPY --from=builder /app/DockerEntrypoint.sh /app/
+COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
# Configure fail2ban
RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \
diff --git a/README.md b/README.md
index 389d6d1d..ec1e5a15 100644
--- a/README.md
+++ b/README.md
@@ -106,10 +106,19 @@ systemctl restart x-ui
update to latest version
```sh
- cd 3x-ui
- docker compose down
- docker compose pull 3x-ui
- docker compose up -d
+ cd 3x-ui
+ docker compose down
+ docker compose pull 3x-ui
+ docker compose up -d
+ ```
+
+remove 3x-ui from docker
+
+ ```sh
+ docker stop 3x-ui
+ docker rm 3x-ui
+ cd --
+ rm -r 3x-ui
```
diff --git a/install.sh b/install.sh
index ae489c2a..9f46d4df 100644
--- a/install.sh
+++ b/install.sh
@@ -123,9 +123,9 @@ config_after_install() {
echo -e "${green}username:${usernameTemp}${plain}"
echo -e "${green}password:${passwordTemp}${plain}"
echo -e "###############################################"
- echo -e "${red}if you forgot your login info,you can type x-ui and then type 7 to check after installation${plain}"
+ echo -e "${red}if you forgot your login info,you can type x-ui and then type 8 to check after installation${plain}"
else
- echo -e "${red} this is your upgrade,will keep old settings,if you forgot your login info,you can type x-ui and then type 7 to check${plain}"
+ echo -e "${red} this is your upgrade,will keep old settings,if you forgot your login info,you can type x-ui and then type 8 to check${plain}"
fi
fi
/usr/local/x-ui/x-ui migrate
diff --git a/x-ui.sh b/x-ui.sh
index c7331e6d..ffb20f06 100644
--- a/x-ui.sh
+++ b/x-ui.sh
@@ -806,34 +806,6 @@ warp_cloudflare() {
esac
}
-multi_protocol() {
- echo "This script only supports Vless and Vmess. if you use another protocols, DON'T INSTALL or get backup first! "
- echo -e "${green}\t1.${plain} Install Multi Protocol Script"
- echo -e "${green}\t2.${plain} Uninstall"
- echo -e "${green}\t3.${plain} Start Service"
- echo -e "${green}\t4.${plain} Stop Service"
- echo -e "${green}\t0.${plain} Back to Main Menu"
- read -p "Choose an option: " choice
- case "$choice" in
- 0)
- show_menu
- ;;
- 1)
- bash <(curl -Ls https://raw.githubusercontent.com/M4mmad/3xui-multi-protocol/master/install.sh --ipv4)
- ;;
- 2)
- bash <(curl -Ls https://raw.githubusercontent.com/M4mmad/3xui-multi-protocol/master/unistall.sh --ipv4)
- ;;
- 3)
- systemctl start 3xui-multi-protocol
- ;;
- 4)
- systemctl stop 3xui-multi-protocol
- ;;
- *) echo "Invalid choice" ;;
- esac
-}
-
run_speedtest() {
# Check if Speedtest is already installed
if ! command -v speedtest &>/dev/null; then
@@ -1145,22 +1117,21 @@ show_menu() {
${green}12.${plain} Check Status
${green}13.${plain} Check Logs
————————————————
- ${green}14.${plain} Enable x-ui On System Startup
- ${green}15.${plain} Disable x-ui On System Startup
+ ${green}14.${plain} Enable Autostart
+ ${green}15.${plain} Disable Autostart
————————————————
${green}16.${plain} SSL Certificate Management
${green}17.${plain} Cloudflare SSL Certificate
${green}18.${plain} IP Limit Management
${green}19.${plain} WARP Management
- ${green}20.${plain} Multi Protocol Management
————————————————
- ${green}21.${plain} Enable BBR
- ${green}22.${plain} Update Geo Files
- ${green}23.${plain} Active Firewall and open ports
- ${green}24.${plain} Speedtest by Ookla
+ ${green}20.${plain} Enable BBR
+ ${green}21.${plain} Update Geo Files
+ ${green}22.${plain} Active Firewall and open ports
+ ${green}23.${plain} Speedtest by Ookla
"
show_status
- echo && read -p "Please enter your selection [0-24]: " num
+ echo && read -p "Please enter your selection [0-23]: " num
case "${num}" in
0)
@@ -1224,22 +1195,19 @@ show_menu() {
warp_cloudflare
;;
20)
- multi_protocol
- ;;
- 21)
enable_bbr
;;
- 22)
+ 21)
update_geo
;;
- 23)
+ 22)
open_ports
;;
- 24)
+ 23)
run_speedtest
;;
*)
- LOGE "Please enter the correct number [0-24]"
+ LOGE "Please enter the correct number [0-23]"
;;
esac
}