diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b2dc0ca4..a7a8c790 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: @@ -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 @@ -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 tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file 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..a2224999 100755 --- a/DockerInit.sh +++ b/DockerInit.sh @@ -1,10 +1,13 @@ #!/bin/sh - case $1 in amd64) ARCH="64" FNAME="amd64" ;; + i386) + ARCH="32" + FNAME="i386" + ;; armv8 | arm64 | aarch64) ARCH="arm64-v8a" FNAME="arm64" @@ -22,19 +25,16 @@ case $1 in 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 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 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 60c6c044..ec1e5a15 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 @@ -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 ``` @@ -127,7 +136,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 +422,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 ![1](./media/1.png) 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/go.mod b/go.mod index 6a116ba3..d46ed1fe 100644 --- a/go.mod +++ b/go.mod @@ -17,9 +17,9 @@ 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 + gorm.io/gorm v1.25.6 ) require ( @@ -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..8b6465d7 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= @@ -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= @@ -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= diff --git a/install.sh b/install.sh index 8d4a0c0c..9f46d4df 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="" @@ -78,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}" @@ -118,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 @@ -163,11 +168,11 @@ install_x-ui() { chmod +x x-ui # Check the system's architecture and rename the file accordingly - if [[ $(arch3xui) == "armv6" || $(arch3xui) == "armv7" ]]; then - mv bin/xray-linux-$(arch3xui) bin/xray-linux-arm - chmod +x bin/xray-linux-arm + if [[ $(arch3xui) == "armv5" || $(arch3xui) == "armv6" || $(arch3xui) == "armv7" ]]; then + 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 @@ -197,7 +202,6 @@ install_x-ui() { echo -e "----------------------------------------------" } - echo -e "${green}Running...${plain}" install_base install_x-ui $1 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/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 204aca72..63cb8aef 100644 --- a/web/html/xui/form/client.html +++ b/web/html/xui/form/client.html @@ -152,7 +152,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/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 @@ - + + - + + 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 @@ - + + - + + 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/html/xui/index.html b/web/html/xui/index.html index d3357a4d..258d4154 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 @@ -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 = ''; 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" }, } }, 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/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": {} }, 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 { 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 { diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index b8fdcde1..7e0f26c5 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] @@ -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" @@ -248,6 +248,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" @@ -319,7 +320,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" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index 42a3a2b2..6c817a30 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" @@ -248,6 +248,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 3edcb4cb..11b66788 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" = "افزودن یک ورودی" @@ -248,6 +248,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 d8211d8d..2e663e4c 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" = "Импортировать входящее сообщение" @@ -248,6 +248,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 d627254b..4c40adca 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" @@ -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" @@ -248,6 +248,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 5a46ec73..a8cd3aff 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" = "导入入站" @@ -248,6 +248,7 @@ "pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用" "remarkModel" = "备注模型和分隔符" "datepicker" = "日期选择器" +"datepickerPlaceholder" = "选择日期" "datepickerDescription" = "选择器日历类型指定到期日期" "sampleRemark" = "备注示例" "oldUsername" = "原用户名" diff --git a/x-ui.sh b/x-ui.sh index d245a247..ffb20f06 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,72 +787,46 @@ 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" ;; - 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" ;; + 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 } 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 @@ -862,8 +841,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] @@ -907,7 +889,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() { @@ -935,62 +917,80 @@ 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 + truncate -s 0 "${iplimit_banned_log_path}" + 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) - apt update && apt install fail2ban -y ;; - centos|almalinux|rocky) - yum -y update && 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) + apt update && apt install fail2ban -y + ;; + 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 + 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" @@ -1018,6 +1018,7 @@ install_iplimit() { # Launching fail2ban if ! systemctl is-active --quiet fail2ban; then systemctl start fail2ban + systemctl enable fail2ban else systemctl restart fail2ban fi @@ -1027,41 +1028,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 purge fail2ban -y;; - centos|almalinux|rocky) - yum remove fail2ban -y;; - fedora) - dnf remove fail2ban -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 } @@ -1104,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) @@ -1183,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 }