mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-04-19 21:42:24 +00:00
Merge branch 'v2.4.10'
This commit is contained in:
commit
6a586ce7c2
26 changed files with 352 additions and 215 deletions
|
@ -1,6 +1,7 @@
|
||||||
DOCKER_DEFAULT_PLATFORM=linux/amd64
|
DOCKER_DEFAULT_PLATFORM=linux/amd64
|
||||||
COMPOSE_PROJECT_NAME=3x
|
COMPOSE_PROJECT_NAME=3x
|
||||||
BUILD_WITH_ANTIZAPRET="0"
|
BUILD_WITH_ANTIZAPRET="0"
|
||||||
|
HOSTNAME="3x-ui"
|
||||||
XUI_SERVER_IP=""
|
XUI_SERVER_IP=""
|
||||||
XUI_PANEL_DOMAIN=""
|
XUI_PANEL_DOMAIN=""
|
||||||
XUI_SUB_DOMAIN=""
|
XUI_SUB_DOMAIN=""
|
||||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -83,7 +83,7 @@ jobs:
|
||||||
cd x-ui/bin
|
cd x-ui/bin
|
||||||
|
|
||||||
# Download dependencies
|
# Download dependencies
|
||||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v24.12.15/"
|
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v24.12.18/"
|
||||||
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||||
wget ${Xray_URL}Xray-linux-64.zip
|
wget ${Xray_URL}Xray-linux-64.zip
|
||||||
unzip Xray-linux-64.zip
|
unzip Xray-linux-64.zip
|
||||||
|
|
|
@ -27,7 +27,7 @@ case $1 in
|
||||||
esac
|
esac
|
||||||
mkdir -p build/bin
|
mkdir -p build/bin
|
||||||
cd build/bin
|
cd build/bin
|
||||||
wget "https://github.com/XTLS/Xray-core/releases/download/v24.12.15/Xray-linux-${ARCH}.zip"
|
wget "https://github.com/XTLS/Xray-core/releases/download/v24.12.18/Xray-linux-${ARCH}.zip"
|
||||||
unzip "Xray-linux-${ARCH}.zip"
|
unzip "Xray-linux-${ARCH}.zip"
|
||||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
||||||
mv xray "xray-linux-${FNAME}"
|
mv xray "xray-linux-${FNAME}"
|
||||||
|
|
|
@ -569,7 +569,8 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||||
<img alt="3x-ui" src="./media/06-configs-light.png">
|
<img alt="3x-ui" src="./media/06-configs-light.png">
|
||||||
</picture>
|
</picture>
|
||||||
<picture>
|
<picture>
|
||||||
<img alt="3x-ui" src="./media/7.png">
|
<source media="(prefers-color-scheme: dark)" srcset="./media/07-bot-dark.png">
|
||||||
|
<img alt="3x-ui" src="./media/07-bot-light.png">
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
## Un agradecimiento especial a
|
## Un agradecimiento especial a
|
||||||
|
@ -583,4 +584,4 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||||
|
|
||||||
## Estrellas a lo largo del tiempo
|
## Estrellas a lo largo del tiempo
|
||||||
|
|
||||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
[](https://starchart.cc/MHSanaei/3x-ui)
|
|
@ -566,7 +566,8 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||||
<img alt="3x-ui" src="./media/06-configs-light.png">
|
<img alt="3x-ui" src="./media/06-configs-light.png">
|
||||||
</picture>
|
</picture>
|
||||||
<picture>
|
<picture>
|
||||||
<img alt="3x-ui" src="./media/7.png">
|
<source media="(prefers-color-scheme: dark)" srcset="./media/07-bot-dark.png">
|
||||||
|
<img alt="3x-ui" src="./media/07-bot-light.png">
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
## A Special Thanks to
|
## A Special Thanks to
|
||||||
|
|
|
@ -564,7 +564,8 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||||
<img alt="3x-ui" src="./media/06-configs-light.png">
|
<img alt="3x-ui" src="./media/06-configs-light.png">
|
||||||
</picture>
|
</picture>
|
||||||
<picture>
|
<picture>
|
||||||
<img alt="3x-ui" src="./media/7.png">
|
<source media="(prefers-color-scheme: dark)" srcset="./media/07-bot-dark.png">
|
||||||
|
<img alt="3x-ui" src="./media/07-bot-light.png">
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
## Особая благодарность
|
## Особая благодарность
|
||||||
|
|
|
@ -557,7 +557,8 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||||
<img alt="3x-ui" src="./media/06-configs-light.png">
|
<img alt="3x-ui" src="./media/06-configs-light.png">
|
||||||
</picture>
|
</picture>
|
||||||
<picture>
|
<picture>
|
||||||
<img alt="3x-ui" src="./media/7.png">
|
<source media="(prefers-color-scheme: dark)" srcset="./media/07-bot-dark.png">
|
||||||
|
<img alt="3x-ui" src="./media/07-bot-light.png">
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
## 特别感谢
|
## 特别感谢
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
2.4.9
|
2.4.10
|
|
@ -9,31 +9,36 @@ services:
|
||||||
reservations:
|
reservations:
|
||||||
memory: 256M
|
memory: 256M
|
||||||
container_name: 3x-ui
|
container_name: 3x-ui
|
||||||
hostname: 3x-ui
|
hostname: ${HOSTNAME:-3x-ui}
|
||||||
networks:
|
networks:
|
||||||
traefik:
|
traefik:
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.3x-ui.rule=Host(`${XUI_PANEL_DOMAIN}`)"
|
- "traefik.http.routers.3x-ui.rule=Host(`${XUI_PANEL_DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.3x-ui.middlewares=cf-x-real-ip@file"
|
||||||
|
- "traefik.http.routers.3x-ui.entrypoints=websecure"
|
||||||
- "traefik.http.routers.3x-ui.service=3x-ui"
|
- "traefik.http.routers.3x-ui.service=3x-ui"
|
||||||
- "traefik.http.routers.3x-ui.entrypoints=https"
|
|
||||||
- "traefik.http.services.3x-ui.loadbalancer.server.port=2053"
|
- "traefik.http.services.3x-ui.loadbalancer.server.port=2053"
|
||||||
#
|
#
|
||||||
- "traefik.http.routers.3x-ui-sub.rule=Host(`${XUI_SUB_DOMAIN}`)"
|
- "traefik.http.routers.3x-ui-sub.rule=Host(`${XUI_SUB_DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.3x-ui-sub.middlewares=cf-x-real-ip@file"
|
||||||
|
- "traefik.http.routers.3x-ui-sub.entrypoints=websecure"
|
||||||
- "traefik.http.routers.3x-ui-sub.service=3x-ui-sub"
|
- "traefik.http.routers.3x-ui-sub.service=3x-ui-sub"
|
||||||
- "traefik.http.routers.3x-ui-sub.entrypoints=https"
|
|
||||||
- "traefik.http.services.3x-ui-sub.loadbalancer.server.port=2096"
|
- "traefik.http.services.3x-ui-sub.loadbalancer.server.port=2096"
|
||||||
#
|
#
|
||||||
- "traefik.tcp.routers.vless-reality.rule=HostSNI(`${XUI_VLESS_REALITY_SNI}`)"
|
- "traefik.tcp.routers.vless-reality.rule=HostSNI(`${XUI_VLESS_REALITY_SNI}`) || HostSNI(`www.${XUI_VLESS_REALITY_SNI}`)"
|
||||||
- "traefik.tcp.routers.vless-reality.tls.passthrough=true"
|
- "traefik.tcp.routers.vless-reality.tls.passthrough=true"
|
||||||
|
- "traefik.tcp.routers.vless-reality.entrypoints=websecure"
|
||||||
- "traefik.tcp.routers.vless-reality.service=3x-ui-inbound-443"
|
- "traefik.tcp.routers.vless-reality.service=3x-ui-inbound-443"
|
||||||
- "traefik.tcp.services.3x-ui-inbound-443.loadbalancer.server.port=443"
|
- "traefik.tcp.services.3x-ui-inbound-443.loadbalancer.server.port=443"
|
||||||
|
- "traefik.tcp.services.3x-ui-inbound-443.loadbalancer.proxyprotocol.version=2"
|
||||||
#
|
#
|
||||||
- "traefik.tcp.routers.vless-grpc.rule=HostSNI(`${XUI_VLESS_GRPC_SNI}`)"
|
- "traefik.tcp.routers.vless-grpc.rule=HostSNI(`${XUI_VLESS_GRPC_SNI}`)"
|
||||||
- "traefik.tcp.routers.vless-grpc.tls.passthrough=true"
|
- "traefik.tcp.routers.vless-grpc.tls.passthrough=true"
|
||||||
- "traefik.tcp.routers.vless-grpc.entrypoints=https"
|
- "traefik.tcp.routers.vless-grpc.entrypoints=websecure"
|
||||||
- "traefik.tcp.routers.vless-grpc.service=3x-ui-inbound-8888"
|
- "traefik.tcp.routers.vless-grpc.service=3x-ui-inbound-8888"
|
||||||
- "traefik.tcp.services.3x-ui-inbound-8888.loadbalancer.server.port=8888"
|
- "traefik.tcp.services.3x-ui-inbound-8888.loadbalancer.server.port=8888"
|
||||||
|
- "traefik.tcp.services.3x-ui-inbound-8888.loadbalancer.proxyprotocol.version=2"
|
||||||
volumes:
|
volumes:
|
||||||
- ./db/:/etc/x-ui/
|
- ./db/:/etc/x-ui/
|
||||||
- ./db/fail2ban.sqlite3:/var/lib/fail2ban/fail2ban.sqlite3
|
- ./db/fail2ban.sqlite3:/var/lib/fail2ban/fail2ban.sqlite3
|
||||||
|
|
12
go.mod
12
go.mod
|
@ -14,10 +14,10 @@ require (
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shirou/gopsutil/v4 v4.24.11
|
github.com/shirou/gopsutil/v4 v4.24.11
|
||||||
github.com/valyala/fasthttp v1.58.0
|
github.com/valyala/fasthttp v1.58.0
|
||||||
github.com/xtls/xray-core v1.8.25-0.20241215123619-7d0a80b501d4
|
github.com/xtls/xray-core v1.8.25-0.20241218133935-cab2fdefd321
|
||||||
go.uber.org/atomic v1.11.0
|
go.uber.org/atomic v1.11.0
|
||||||
golang.org/x/text v0.21.0
|
golang.org/x/text v0.21.0
|
||||||
google.golang.org/grpc v1.69.0
|
google.golang.org/grpc v1.69.2
|
||||||
gorm.io/driver/sqlite v1.5.7
|
gorm.io/driver/sqlite v1.5.7
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
)
|
)
|
||||||
|
@ -86,17 +86,17 @@ require (
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
golang.org/x/arch v0.12.0 // indirect
|
golang.org/x/arch v0.12.0 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e // indirect
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
|
||||||
golang.org/x/mod v0.22.0 // indirect
|
golang.org/x/mod v0.22.0 // indirect
|
||||||
golang.org/x/net v0.32.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/sync v0.10.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
golang.org/x/time v0.8.0 // indirect
|
golang.org/x/time v0.8.0 // indirect
|
||||||
golang.org/x/tools v0.28.0 // indirect
|
golang.org/x/tools v0.28.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 // indirect
|
||||||
google.golang.org/protobuf v1.35.2 // indirect
|
google.golang.org/protobuf v1.36.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
|
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
|
||||||
lukechampine.com/blake3 v1.3.0 // indirect
|
lukechampine.com/blake3 v1.3.0 // indirect
|
||||||
|
|
24
go.sum
24
go.sum
|
@ -191,8 +191,8 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd
|
||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
|
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
|
||||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
|
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
|
||||||
github.com/xtls/xray-core v1.8.25-0.20241215123619-7d0a80b501d4 h1:zdd86FEjFZjAaRbWxiZQM2QPOzk/d6cig2DaE7c3MDQ=
|
github.com/xtls/xray-core v1.8.25-0.20241218133935-cab2fdefd321 h1:vk+n1RmfhFCj5xSi4I6C3USpcUQ48H3lt/QrtARVz1M=
|
||||||
github.com/xtls/xray-core v1.8.25-0.20241215123619-7d0a80b501d4/go.mod h1:lduNPDkXku+Avphl8g7W0yJrHhWyxdOnPo0XGYdF0Aw=
|
github.com/xtls/xray-core v1.8.25-0.20241218133935-cab2fdefd321/go.mod h1:DCaUwrBk1RIC7hWg/wGoAynE69g3ptua1sEr8i0BWxA=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
|
@ -217,12 +217,12 @@ golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
|
||||||
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4=
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
|
||||||
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -243,12 +243,12 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 h1:Z7FRVJPSMaHQxD0uXU8WdgFh8PseLM8Q8NzhnpMrBhQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
||||||
google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI=
|
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
|
||||||
google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
|
||||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|
37
install.sh
37
install.sh
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
red='\033[0;31m'
|
red='\033[0;31m'
|
||||||
green='\033[0;32m'
|
green='\033[0;32m'
|
||||||
|
blue='\033[0;34m'
|
||||||
yellow='\033[0;33m'
|
yellow='\033[0;33m'
|
||||||
plain='\033[0m'
|
plain='\033[0m'
|
||||||
|
|
||||||
|
@ -260,24 +261,24 @@ install_x-ui() {
|
||||||
systemctl start x-ui
|
systemctl start x-ui
|
||||||
echo -e "${green}x-ui ${tag_version}${plain} installation finished, it is running now..."
|
echo -e "${green}x-ui ${tag_version}${plain} installation finished, it is running now..."
|
||||||
echo -e ""
|
echo -e ""
|
||||||
echo -e "x-ui control menu usages: "
|
echo -e "┌───────────────────────────────────────────────────────┐
|
||||||
echo -e "----------------------------------------------"
|
│ ${blue}x-ui control menu usages (subcommands):${plain} │
|
||||||
echo -e "SUBCOMMANDS:"
|
│ │
|
||||||
echo -e "x-ui - Admin Management Script"
|
│ ${blue}x-ui${plain} - Admin Management Script │
|
||||||
echo -e "x-ui start - Start"
|
│ ${blue}x-ui start${plain} - Start │
|
||||||
echo -e "x-ui stop - Stop"
|
│ ${blue}x-ui stop${plain} - Stop │
|
||||||
echo -e "x-ui restart - Restart"
|
│ ${blue}x-ui restart${plain} - Restart │
|
||||||
echo -e "x-ui status - Current Status"
|
│ ${blue}x-ui status${plain} - Current Status │
|
||||||
echo -e "x-ui settings - Current Settings"
|
│ ${blue}x-ui settings${plain} - Current Settings │
|
||||||
echo -e "x-ui enable - Enable Autostart on OS Startup"
|
│ ${blue}x-ui enable${plain} - Enable Autostart on OS Startup │
|
||||||
echo -e "x-ui disable - Disable Autostart on OS Startup"
|
│ ${blue}x-ui disable${plain} - Disable Autostart on OS Startup │
|
||||||
echo -e "x-ui log - Check logs"
|
│ ${blue}x-ui log${plain} - Check logs │
|
||||||
echo -e "x-ui banlog - Check Fail2ban ban logs"
|
│ ${blue}x-ui banlog${plain} - Check Fail2ban ban logs │
|
||||||
echo -e "x-ui update - Update"
|
│ ${blue}x-ui update${plain} - Update │
|
||||||
echo -e "x-ui legacy - legacy version"
|
│ ${blue}x-ui legacy${plain} - legacy version │
|
||||||
echo -e "x-ui install - Install"
|
│ ${blue}x-ui install${plain} - Install │
|
||||||
echo -e "x-ui uninstall - Uninstall"
|
│ ${blue}x-ui uninstall${plain} - Uninstall │
|
||||||
echo -e "----------------------------------------------"
|
└───────────────────────────────────────────────────────┘"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo -e "${green}Running...${plain}"
|
echo -e "${green}Running...${plain}"
|
||||||
|
|
BIN
media/07-bot-dark.png
Normal file
BIN
media/07-bot-dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
media/07-bot-light.png
Normal file
BIN
media/07-bot-light.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 255 KiB |
BIN
media/7.png
BIN
media/7.png
Binary file not shown.
Before Width: | Height: | Size: 60 KiB |
|
@ -34,10 +34,6 @@ const TLS_VERSION_OPTION = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const TLS_CIPHER_OPTION = {
|
const TLS_CIPHER_OPTION = {
|
||||||
RSA_AES_128_CBC: "TLS_RSA_WITH_AES_128_CBC_SHA",
|
|
||||||
RSA_AES_256_CBC: "TLS_RSA_WITH_AES_256_CBC_SHA",
|
|
||||||
RSA_AES_128_GCM: "TLS_RSA_WITH_AES_128_GCM_SHA256",
|
|
||||||
RSA_AES_256_GCM: "TLS_RSA_WITH_AES_256_GCM_SHA384",
|
|
||||||
AES_128_GCM: "TLS_AES_128_GCM_SHA256",
|
AES_128_GCM: "TLS_AES_128_GCM_SHA256",
|
||||||
AES_256_GCM: "TLS_AES_256_GCM_SHA384",
|
AES_256_GCM: "TLS_AES_256_GCM_SHA384",
|
||||||
CHACHA20_POLY1305: "TLS_CHACHA20_POLY1305_SHA256",
|
CHACHA20_POLY1305: "TLS_CHACHA20_POLY1305_SHA256",
|
||||||
|
@ -64,6 +60,7 @@ const UTLS_FINGERPRINT = {
|
||||||
UTLS_QQ: "qq",
|
UTLS_QQ: "qq",
|
||||||
UTLS_RANDOM: "random",
|
UTLS_RANDOM: "random",
|
||||||
UTLS_RANDOMIZED: "randomized",
|
UTLS_RANDOMIZED: "randomized",
|
||||||
|
UTLS_UNSAFE: "unsafe",
|
||||||
};
|
};
|
||||||
|
|
||||||
const ALPN_OPTION = {
|
const ALPN_OPTION = {
|
||||||
|
@ -496,19 +493,9 @@ class xHTTPStreamSettings extends XrayCommonClass {
|
||||||
headers = [],
|
headers = [],
|
||||||
scMaxBufferedPosts = 30,
|
scMaxBufferedPosts = 30,
|
||||||
scMaxEachPostBytes = "1000000",
|
scMaxEachPostBytes = "1000000",
|
||||||
scMinPostsIntervalMs = "30",
|
|
||||||
noSSEHeader = false,
|
noSSEHeader = false,
|
||||||
xPaddingBytes = "100-1000",
|
xPaddingBytes = "100-1000",
|
||||||
xmux = {
|
|
||||||
maxConcurrency: "16-32",
|
|
||||||
maxConnections: 0,
|
|
||||||
cMaxReuseTimes: "64-128",
|
|
||||||
cMaxLifetimeMs: 0,
|
|
||||||
hMaxRequestTimes: "800-900",
|
|
||||||
hKeepAlivePeriod: 0,
|
|
||||||
},
|
|
||||||
mode = MODE_OPTION.AUTO,
|
mode = MODE_OPTION.AUTO,
|
||||||
noGRPCHeader = false
|
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.path = path;
|
this.path = path;
|
||||||
|
@ -516,12 +503,9 @@ class xHTTPStreamSettings extends XrayCommonClass {
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
this.scMaxBufferedPosts = scMaxBufferedPosts;
|
this.scMaxBufferedPosts = scMaxBufferedPosts;
|
||||||
this.scMaxEachPostBytes = scMaxEachPostBytes;
|
this.scMaxEachPostBytes = scMaxEachPostBytes;
|
||||||
this.scMinPostsIntervalMs = scMinPostsIntervalMs;
|
|
||||||
this.noSSEHeader = noSSEHeader;
|
this.noSSEHeader = noSSEHeader;
|
||||||
this.xPaddingBytes = xPaddingBytes;
|
this.xPaddingBytes = xPaddingBytes;
|
||||||
this.xmux = xmux;
|
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.noGRPCHeader = noGRPCHeader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addHeader(name, value) {
|
addHeader(name, value) {
|
||||||
|
@ -539,12 +523,9 @@ class xHTTPStreamSettings extends XrayCommonClass {
|
||||||
XrayCommonClass.toHeaders(json.headers),
|
XrayCommonClass.toHeaders(json.headers),
|
||||||
json.scMaxBufferedPosts,
|
json.scMaxBufferedPosts,
|
||||||
json.scMaxEachPostBytes,
|
json.scMaxEachPostBytes,
|
||||||
json.scMinPostsIntervalMs,
|
|
||||||
json.noSSEHeader,
|
json.noSSEHeader,
|
||||||
json.xPaddingBytes,
|
json.xPaddingBytes,
|
||||||
json.xmux,
|
|
||||||
json.mode,
|
json.mode,
|
||||||
json.noGRPCHeader,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,19 +536,9 @@ class xHTTPStreamSettings extends XrayCommonClass {
|
||||||
headers: XrayCommonClass.toV2Headers(this.headers, false),
|
headers: XrayCommonClass.toV2Headers(this.headers, false),
|
||||||
scMaxBufferedPosts: this.scMaxBufferedPosts,
|
scMaxBufferedPosts: this.scMaxBufferedPosts,
|
||||||
scMaxEachPostBytes: this.scMaxEachPostBytes,
|
scMaxEachPostBytes: this.scMaxEachPostBytes,
|
||||||
scMinPostsIntervalMs: this.scMinPostsIntervalMs,
|
|
||||||
noSSEHeader: this.noSSEHeader,
|
noSSEHeader: this.noSSEHeader,
|
||||||
xPaddingBytes: this.xPaddingBytes,
|
xPaddingBytes: this.xPaddingBytes,
|
||||||
xmux: {
|
|
||||||
maxConcurrency: this.xmux.maxConcurrency,
|
|
||||||
maxConnections: this.xmux.maxConnections,
|
|
||||||
cMaxReuseTimes: this.xmux.cMaxReuseTimes,
|
|
||||||
cMaxLifetimeMs: this.xmux.cMaxLifetimeMs,
|
|
||||||
hMaxRequestTimes: this.xmux.hMaxRequestTimes,
|
|
||||||
hKeepAlivePeriod: this.xmux.hKeepAlivePeriod,
|
|
||||||
},
|
|
||||||
mode: this.mode,
|
mode: this.mode,
|
||||||
noGRPCHeader: this.noGRPCHeader,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -718,7 +689,10 @@ TlsStreamSettings.Cert = class extends XrayCommonClass {
|
||||||
};
|
};
|
||||||
|
|
||||||
TlsStreamSettings.Settings = class extends XrayCommonClass {
|
TlsStreamSettings.Settings = class extends XrayCommonClass {
|
||||||
constructor(allowInsecure = false, fingerprint = '') {
|
constructor(
|
||||||
|
allowInsecure = false,
|
||||||
|
fingerprint = UTLS_FINGERPRINT.UTLS_CHROME,
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
this.allowInsecure = allowInsecure;
|
this.allowInsecure = allowInsecure;
|
||||||
this.fingerprint = fingerprint;
|
this.fingerprint = fingerprint;
|
||||||
|
@ -807,7 +781,7 @@ class RealityStreamSettings extends XrayCommonClass {
|
||||||
RealityStreamSettings.Settings = class extends XrayCommonClass {
|
RealityStreamSettings.Settings = class extends XrayCommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
publicKey = '',
|
publicKey = '',
|
||||||
fingerprint = UTLS_FINGERPRINT.UTLS_RANDOM,
|
fingerprint = UTLS_FINGERPRINT.UTLS_CHROME,
|
||||||
serverName = '',
|
serverName = '',
|
||||||
spiderX = '/'
|
spiderX = '/'
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -39,6 +39,7 @@ const UTLS_FINGERPRINT = {
|
||||||
UTLS_QQ: "qq",
|
UTLS_QQ: "qq",
|
||||||
UTLS_RANDOM: "random",
|
UTLS_RANDOM: "random",
|
||||||
UTLS_RANDOMIZED: "randomized",
|
UTLS_RANDOMIZED: "randomized",
|
||||||
|
UTLS_UNSAFE: "unsafe",
|
||||||
};
|
};
|
||||||
|
|
||||||
const ALPN_OPTION = {
|
const ALPN_OPTION = {
|
||||||
|
@ -287,11 +288,24 @@ class xHTTPStreamSettings extends CommonClass {
|
||||||
path = '/',
|
path = '/',
|
||||||
host = '',
|
host = '',
|
||||||
mode = '',
|
mode = '',
|
||||||
|
noGRPCHeader = false,
|
||||||
|
scMinPostsIntervalMs = "30",
|
||||||
|
xmux = {
|
||||||
|
maxConcurrency: "16-32",
|
||||||
|
maxConnections: 0,
|
||||||
|
cMaxReuseTimes: "64-128",
|
||||||
|
cMaxLifetimeMs: 0,
|
||||||
|
hMaxRequestTimes: "800-900",
|
||||||
|
hKeepAlivePeriod: 0,
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
|
this.noGRPCHeader = noGRPCHeader;
|
||||||
|
this.scMinPostsIntervalMs = scMinPostsIntervalMs;
|
||||||
|
this.xmux = xmux;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
|
@ -299,6 +313,9 @@ class xHTTPStreamSettings extends CommonClass {
|
||||||
json.path,
|
json.path,
|
||||||
json.host,
|
json.host,
|
||||||
json.mode,
|
json.mode,
|
||||||
|
json.noGRPCHeader,
|
||||||
|
json.scMinPostsIntervalMs,
|
||||||
|
json.xmux
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,6 +324,16 @@ class xHTTPStreamSettings extends CommonClass {
|
||||||
path: this.path,
|
path: this.path,
|
||||||
host: this.host,
|
host: this.host,
|
||||||
mode: this.mode,
|
mode: this.mode,
|
||||||
|
noGRPCHeader: this.noGRPCHeader,
|
||||||
|
scMinPostsIntervalMs: this.scMinPostsIntervalMs,
|
||||||
|
xmux: {
|
||||||
|
maxConcurrency: this.xmux.maxConcurrency,
|
||||||
|
maxConnections: this.xmux.maxConnections,
|
||||||
|
cMaxReuseTimes: this.xmux.cMaxReuseTimes,
|
||||||
|
cMaxLifetimeMs: this.xmux.cMaxLifetimeMs,
|
||||||
|
hMaxRequestTimes: this.xmux.hMaxRequestTimes,
|
||||||
|
hKeepAlivePeriod: this.xmux.hKeepAlivePeriod,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"x-ui/web/service"
|
"x-ui/web/service"
|
||||||
"x-ui/web/session"
|
"x-ui/web/session"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,8 +50,8 @@ func (a *IndexController) index(c *gin.Context) {
|
||||||
|
|
||||||
func (a *IndexController) login(c *gin.Context) {
|
func (a *IndexController) login(c *gin.Context) {
|
||||||
var form LoginForm
|
var form LoginForm
|
||||||
err := c.ShouldBind(&form)
|
|
||||||
if err != nil {
|
if err := c.ShouldBind(&form); err != nil {
|
||||||
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.invalidFormData"))
|
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.invalidFormData"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -68,29 +69,31 @@ func (a *IndexController) login(c *gin.Context) {
|
||||||
safeUser := template.HTMLEscapeString(form.Username)
|
safeUser := template.HTMLEscapeString(form.Username)
|
||||||
safePass := template.HTMLEscapeString(form.Password)
|
safePass := template.HTMLEscapeString(form.Password)
|
||||||
safeSecret := template.HTMLEscapeString(form.LoginSecret)
|
safeSecret := template.HTMLEscapeString(form.LoginSecret)
|
||||||
|
|
||||||
if user == nil {
|
if user == nil {
|
||||||
logger.Warningf("wrong username: \"%s\", password: \"%s\", secret: \"%s\", IP: \"%s\"", safeUser, safePass, safeSecret, getRemoteIp(c))
|
logger.Warningf("wrong username: \"%s\", password: \"%s\", secret: \"%s\", IP: \"%s\"", safeUser, safePass, safeSecret, getRemoteIp(c))
|
||||||
a.tgbot.UserLoginNotify(safeUser, safePass, getRemoteIp(c), timeStr, 0)
|
a.tgbot.UserLoginNotify(safeUser, safePass, getRemoteIp(c), timeStr, 0)
|
||||||
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword"))
|
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword"))
|
||||||
return
|
return
|
||||||
} else {
|
}
|
||||||
|
|
||||||
logger.Infof("%s logged in successfully, Ip Address: %s\n", safeUser, getRemoteIp(c))
|
logger.Infof("%s logged in successfully, Ip Address: %s\n", safeUser, getRemoteIp(c))
|
||||||
a.tgbot.UserLoginNotify(safeUser, ``, getRemoteIp(c), timeStr, 1)
|
a.tgbot.UserLoginNotify(safeUser, ``, getRemoteIp(c), timeStr, 1)
|
||||||
}
|
|
||||||
|
|
||||||
sessionMaxAge, err := a.settingService.GetSessionMaxAge()
|
sessionMaxAge, err := a.settingService.GetSessionMaxAge()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("Unable to get session's max age from DB")
|
logger.Warning("Unable to get session's max age from DB")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = session.SetMaxAge(c, sessionMaxAge*60)
|
session.SetMaxAge(c, sessionMaxAge*60)
|
||||||
if err != nil {
|
session.SetLoginUser(c, user)
|
||||||
logger.Warning("Unable to set session's max age")
|
if err := sessions.Default(c).Save(); err != nil {
|
||||||
|
logger.Warning("Unable to save session: ", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = session.SetLoginUser(c, user)
|
logger.Infof("%s logged in successfully", safeUser)
|
||||||
logger.Infof("%s logged in successfully", user.Username)
|
jsonMsg(c, I18nWeb(c, "pages.login.toasts.successLogin"), nil)
|
||||||
jsonMsg(c, I18nWeb(c, "pages.login.toasts.successLogin"), err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *IndexController) logout(c *gin.Context) {
|
func (a *IndexController) logout(c *gin.Context) {
|
||||||
|
@ -99,6 +102,9 @@ func (a *IndexController) logout(c *gin.Context) {
|
||||||
logger.Infof("%s logged out successfully", user.Username)
|
logger.Infof("%s logged out successfully", user.Username)
|
||||||
}
|
}
|
||||||
session.ClearSession(c)
|
session.ClearSession(c)
|
||||||
|
if err := sessions.Default(c).Save(); err != nil {
|
||||||
|
logger.Warning("Unable to save session after clearing:", err)
|
||||||
|
}
|
||||||
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
|
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -377,6 +377,30 @@
|
||||||
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="No gRPC Header" v-if="outbound.stream.xhttp.mode === 'stream-up' || outbound.stream.xhttp.mode === 'stream-one'">
|
||||||
|
<a-switch v-model="outbound.stream.xhttp.noGRPCHeader"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Min Upload Interval (Ms)" v-if="outbound.stream.xhttp.mode === 'packet-up'">
|
||||||
|
<a-input v-model.trim="outbound.stream.xhttp.scMinPostsIntervalMs"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Concurrency" v-if="!outbound.stream.xhttp.xmux.maxConnections">
|
||||||
|
<a-input v-model="outbound.stream.xhttp.xmux.maxConcurrency"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Connections" v-if="!outbound.stream.xhttp.xmux.maxConcurrency">
|
||||||
|
<a-input v-model="outbound.stream.xhttp.xmux.maxConnections"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Reuse Times">
|
||||||
|
<a-input v-model="outbound.stream.xhttp.xmux.cMaxReuseTimes"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Lifetime (ms)">
|
||||||
|
<a-input v-model="outbound.stream.xhttp.xmux.cMaxLifetimeMs"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Max Request Times">
|
||||||
|
<a-input v-model="outbound.stream.xhttp.xmux.hMaxRequestTimes"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='Keep Alive Period'>
|
||||||
|
<a-input v-model.number="outbound.stream.xhttp.xmux.hKeepAlivePeriod"></a-input>
|
||||||
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -27,41 +27,17 @@
|
||||||
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Max Buffered Upload">
|
<a-form-item label="Max Buffered Upload" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||||
<a-input v-model.trim="inbound.stream.xhttp.scMaxBufferedPosts"></a-input>
|
<a-input v-model.trim="inbound.stream.xhttp.scMaxBufferedPosts"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Max Upload Size (Byte)">
|
<a-form-item label="Max Upload Size (Byte)" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||||
<a-input v-model.trim="inbound.stream.xhttp.scMaxEachPostBytes"></a-input>
|
<a-input v-model.trim="inbound.stream.xhttp.scMaxEachPostBytes"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Min Upload Interval (Ms)">
|
|
||||||
<a-input v-model.trim="inbound.stream.xhttp.scMinPostsIntervalMs"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Padding Bytes">
|
<a-form-item label="Padding Bytes">
|
||||||
<a-input v-model.trim="inbound.stream.xhttp.xPaddingBytes"></a-input>
|
<a-input v-model.trim="inbound.stream.xhttp.xPaddingBytes"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="No SSE Header">
|
<a-form-item label="No SSE Header">
|
||||||
<a-switch v-model="inbound.stream.xhttp.noSSEHeader"></a-switch>
|
<a-switch v-model="inbound.stream.xhttp.noSSEHeader"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Max Concurrency" v-if="!inbound.stream.xhttp.xmux.maxConnections">
|
|
||||||
<a-input v-model="inbound.stream.xhttp.xmux.maxConcurrency"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Max Connections" v-if="!inbound.stream.xhttp.xmux.maxConcurrency">
|
|
||||||
<a-input v-model="inbound.stream.xhttp.xmux.maxConnections"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Max Reuse Times">
|
|
||||||
<a-input v-model="inbound.stream.xhttp.xmux.cMaxReuseTimes"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Max Lifetime (ms)">
|
|
||||||
<a-input v-model="inbound.stream.xhttp.xmux.cMaxLifetimeMs"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Max Request Times">
|
|
||||||
<a-input v-model="inbound.stream.xhttp.xmux.hMaxRequestTimes"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='Keep Alive Period'>
|
|
||||||
<a-input v-model.number="inbound.stream.xhttp.xmux.hKeepAlivePeriod"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="No gRPC Header">
|
|
||||||
<a-switch v-model="inbound.stream.xhttp.noGRPCHeader"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
|
@ -185,6 +185,25 @@
|
||||||
<a-tag>↑ [[ sizeFormat(infoModal.clientStats.up) ]] / [[ sizeFormat(infoModal.clientStats.down) ]] ↓</a-tag>
|
<a-tag>↑ [[ sizeFormat(infoModal.clientStats.up) ]] / [[ sizeFormat(infoModal.clientStats.down) ]] ↓</a-tag>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr v-if="app.ipLimitEnable">
|
||||||
|
<td>{{ i18n "pages.inbounds.IPLimit" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-tag>[[ infoModal.clientSettings.limitIp ]]</a-tag>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="app.ipLimitEnable">
|
||||||
|
<td>{{ i18n "pages.inbounds.IPLimitlog" }}</td>
|
||||||
|
<td>
|
||||||
|
<a-tag>[[ infoModal.clientIps ]]</a-tag>
|
||||||
|
<a-icon type="sync" :spin="refreshing" @click="refreshIPs" style="margin: 0 5px;"></a-icon>
|
||||||
|
<a-tooltip :title="[[ dbInbound.address ]]">
|
||||||
|
<template slot="title">
|
||||||
|
<span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
|
||||||
|
</template>
|
||||||
|
<a-icon type="delete" @click="clearClientIps"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<table style="display: inline-table; margin-block: 10px; width: 100%; text-align: center;">
|
<table style="display: inline-table; margin-block: 10px; width: 100%; text-align: center;">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -417,6 +436,18 @@
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<script>
|
<script>
|
||||||
|
function refreshIPs(email) {
|
||||||
|
return HttpUtil.post(`/panel/inbound/clientIps/${email}`).then((msg) => {
|
||||||
|
if (msg.success) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(msg.obj).join(', ');
|
||||||
|
} catch (e) {
|
||||||
|
return msg.obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const infoModal = {
|
const infoModal = {
|
||||||
visible: false,
|
visible: false,
|
||||||
inbound: new Inbound(),
|
inbound: new Inbound(),
|
||||||
|
@ -431,6 +462,7 @@
|
||||||
isExpired: false,
|
isExpired: false,
|
||||||
subLink: '',
|
subLink: '',
|
||||||
subJsonLink: '',
|
subJsonLink: '',
|
||||||
|
clientIps: '',
|
||||||
show(dbInbound, index) {
|
show(dbInbound, index) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.inbound = dbInbound.toInbound();
|
this.inbound = dbInbound.toInbound();
|
||||||
|
@ -438,6 +470,12 @@
|
||||||
this.clientSettings = this.inbound.clients ? this.inbound.clients[index] : null;
|
this.clientSettings = this.inbound.clients ? this.inbound.clients[index] : null;
|
||||||
this.isExpired = this.inbound.clients ? this.inbound.isExpiry(index) : this.dbInbound.isExpiry;
|
this.isExpired = this.inbound.clients ? this.inbound.isExpiry(index) : this.dbInbound.isExpiry;
|
||||||
this.clientStats = this.inbound.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
|
this.clientStats = this.inbound.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
|
||||||
|
|
||||||
|
if (app.ipLimitEnable && this.clientSettings.limitIp) {
|
||||||
|
refreshIPs(this.clientStats.email).then((ips) => {
|
||||||
|
this.clientIps = ips;
|
||||||
|
})
|
||||||
|
}
|
||||||
if (this.inbound.protocol == Protocols.WIREGUARD) {
|
if (this.inbound.protocol == Protocols.WIREGUARD) {
|
||||||
this.links = this.inbound.genInboundLinks(dbInbound.remark).split('\r\n')
|
this.links = this.inbound.genInboundLinks(dbInbound.remark).split('\r\n')
|
||||||
} else {
|
} else {
|
||||||
|
@ -466,6 +504,7 @@
|
||||||
el: '#inbound-info-modal',
|
el: '#inbound-info-modal',
|
||||||
data: {
|
data: {
|
||||||
infoModal,
|
infoModal,
|
||||||
|
refreshing: false,
|
||||||
get dbInbound() {
|
get dbInbound() {
|
||||||
return this.infoModal.dbInbound;
|
return this.infoModal.dbInbound;
|
||||||
},
|
},
|
||||||
|
@ -502,6 +541,26 @@
|
||||||
remained = this.infoModal.clientStats.total - this.infoModal.clientStats.up - this.infoModal.clientStats.down;
|
remained = this.infoModal.clientStats.total - this.infoModal.clientStats.up - this.infoModal.clientStats.down;
|
||||||
return remained > 0 ? sizeFormat(remained) : '-';
|
return remained > 0 ? sizeFormat(remained) : '-';
|
||||||
},
|
},
|
||||||
|
refreshIPs() {
|
||||||
|
this.refreshing = true;
|
||||||
|
refreshIPs(this.infoModal.clientStats.email)
|
||||||
|
.then((ips) => {
|
||||||
|
this.infoModal.clientIps = ips;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.refreshing = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
clearClientIps() {
|
||||||
|
HttpUtil.post(`/panel/inbound/clearClientIps/${this.infoModal.clientStats.email}`)
|
||||||
|
.then((msg) => {
|
||||||
|
if (!msg.success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.infoModal.clientIps = 'No IP Record';
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -961,6 +961,8 @@
|
||||||
{ label: 'Google Gemini', value: 'geosite:google-gemini' },
|
{ label: 'Google Gemini', value: 'geosite:google-gemini' },
|
||||||
{ label: 'Perplexity', value: 'geosite:perplexity' },
|
{ label: 'Perplexity', value: 'geosite:perplexity' },
|
||||||
{ label: 'Booking', value: 'geosite:booking' },
|
{ label: 'Booking', value: 'geosite:booking' },
|
||||||
|
{ label: 'JetBrains', value: 'geosite:jetbrains' },
|
||||||
|
{ label: 'Discord', value: 'geosite:discord' },
|
||||||
{ label: 'X (Twitter)', value: 'geosite:x' },
|
{ label: 'X (Twitter)', value: 'geosite:x' },
|
||||||
{ label: 'Category: Porn', value: 'geosite:category-porn' },
|
{ label: 'Category: Porn', value: 'geosite:category-porn' },
|
||||||
{ label: 'Category: AI Chats', value: 'geosite:category-ai-chat-!cn' },
|
{ label: 'Category: AI Chats', value: 'geosite:category-ai-chat-!cn' },
|
||||||
|
|
|
@ -151,13 +151,13 @@ func (j *CheckClientIpJob) processLogFile() bool {
|
||||||
}
|
}
|
||||||
sort.Strings(ips)
|
sort.Strings(ips)
|
||||||
|
|
||||||
inboundClientIps, err := j.getInboundClientIps(email)
|
clientIpsRecord, err := j.getInboundClientIps(email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
j.addInboundClientIps(email, ips)
|
j.addInboundClientIps(email, ips)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldCleanLog = j.updateInboundClientIps(inboundClientIps, email, ips) || shouldCleanLog
|
shouldCleanLog = j.updateInboundClientIps(clientIpsRecord, email, ips) || shouldCleanLog
|
||||||
}
|
}
|
||||||
|
|
||||||
return shouldCleanLog
|
return shouldCleanLog
|
||||||
|
@ -309,12 +309,12 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
|
||||||
|
|
||||||
func (j *CheckClientIpJob) getInboundByEmail(clientEmail string) (*model.Inbound, error) {
|
func (j *CheckClientIpJob) getInboundByEmail(clientEmail string) (*model.Inbound, error) {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var inbounds *model.Inbound
|
inbound := &model.Inbound{}
|
||||||
|
|
||||||
err := db.Model(model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").Find(&inbounds).Error
|
err := db.Model(&model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").First(inbound).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return inbounds, nil
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
loginUser = "LOGIN_USER"
|
loginUserKey = "LOGIN_USER"
|
||||||
defaultPath = "/"
|
defaultPath = "/"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,30 +18,33 @@ func init() {
|
||||||
gob.Register(model.User{})
|
gob.Register(model.User{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetLoginUser(c *gin.Context, user *model.User) error {
|
func SetLoginUser(c *gin.Context, user *model.User) {
|
||||||
|
if user == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
s := sessions.Default(c)
|
s := sessions.Default(c)
|
||||||
s.Set(loginUser, user)
|
s.Set(loginUserKey, *user)
|
||||||
return s.Save()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetMaxAge(c *gin.Context, maxAge int) error {
|
func SetMaxAge(c *gin.Context, maxAge int) {
|
||||||
s := sessions.Default(c)
|
s := sessions.Default(c)
|
||||||
s.Options(sessions.Options{
|
s.Options(sessions.Options{
|
||||||
Path: defaultPath,
|
Path: defaultPath,
|
||||||
MaxAge: maxAge,
|
MaxAge: maxAge,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
})
|
})
|
||||||
return s.Save()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetLoginUser(c *gin.Context) *model.User {
|
func GetLoginUser(c *gin.Context) *model.User {
|
||||||
s := sessions.Default(c)
|
s := sessions.Default(c)
|
||||||
obj := s.Get(loginUser)
|
obj := s.Get(loginUserKey)
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
user, ok := obj.(model.User)
|
user, ok := obj.(model.User)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
||||||
|
s.Delete(loginUserKey)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &user
|
return &user
|
||||||
|
@ -51,7 +54,7 @@ func IsLogin(c *gin.Context) bool {
|
||||||
return GetLoginUser(c) != nil
|
return GetLoginUser(c) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClearSession(c *gin.Context) error {
|
func ClearSession(c *gin.Context) {
|
||||||
s := sessions.Default(c)
|
s := sessions.Default(c)
|
||||||
s.Clear()
|
s.Clear()
|
||||||
s.Options(sessions.Options{
|
s.Options(sessions.Options{
|
||||||
|
@ -59,5 +62,4 @@ func ClearSession(c *gin.Context) error {
|
||||||
MaxAge: -1,
|
MaxAge: -1,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
})
|
})
|
||||||
return s.Save()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
"offline" = "Офлайн"
|
"offline" = "Офлайн"
|
||||||
"online" = "Онлайн"
|
"online" = "Онлайн"
|
||||||
"domainName" = "Домен"
|
"domainName" = "Домен"
|
||||||
"monitor" = "Порт IP"
|
"monitor" = "Слушать IP"
|
||||||
"certificate" = "Цифровой сертификат"
|
"certificate" = "Цифровой сертификат"
|
||||||
"fail" = "Неудачно"
|
"fail" = "Неудачно"
|
||||||
"success" = "Успешно"
|
"success" = "Успешно"
|
||||||
|
|
216
x-ui.sh
216
x-ui.sh
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
red='\033[0;31m'
|
red='\033[0;31m'
|
||||||
green='\033[0;32m'
|
green='\033[0;32m'
|
||||||
|
blue='\033[0;34m'
|
||||||
yellow='\033[0;33m'
|
yellow='\033[0;33m'
|
||||||
plain='\033[0m'
|
plain='\033[0m'
|
||||||
|
|
||||||
|
@ -688,10 +689,12 @@ show_xray_status() {
|
||||||
}
|
}
|
||||||
|
|
||||||
firewall_menu() {
|
firewall_menu() {
|
||||||
echo -e "${green}\t1.${plain} Install Firewall & open ports"
|
echo -e "${green}\t1.${plain} Install Firewall"
|
||||||
echo -e "${green}\t2.${plain} Allowed List"
|
echo -e "${green}\t2.${plain} Port List"
|
||||||
echo -e "${green}\t3.${plain} Delete Ports from List"
|
echo -e "${green}\t3.${plain} Open Ports"
|
||||||
echo -e "${green}\t4.${plain} Disable Firewall"
|
echo -e "${green}\t4.${plain} Delete Ports from List"
|
||||||
|
echo -e "${green}\t5.${plain} Disable Firewall"
|
||||||
|
echo -e "${green}\t6.${plain} Firewall Status"
|
||||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
read -p "Choose an option: " choice
|
read -p "Choose an option: " choice
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
|
@ -699,19 +702,27 @@ firewall_menu() {
|
||||||
show_menu
|
show_menu
|
||||||
;;
|
;;
|
||||||
1)
|
1)
|
||||||
open_ports
|
install_firewall
|
||||||
firewall_menu
|
firewall_menu
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
sudo ufw status
|
ufw status numbered
|
||||||
firewall_menu
|
firewall_menu
|
||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
delete_ports
|
open_ports
|
||||||
firewall_menu
|
firewall_menu
|
||||||
;;
|
;;
|
||||||
4)
|
4)
|
||||||
sudo ufw disable
|
delete_ports
|
||||||
|
firewall_menu
|
||||||
|
;;
|
||||||
|
5)
|
||||||
|
ufw disable
|
||||||
|
firewall_menu
|
||||||
|
;;
|
||||||
|
6)
|
||||||
|
ufw status verbose
|
||||||
firewall_menu
|
firewall_menu
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
@ -721,7 +732,7 @@ firewall_menu() {
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
open_ports() {
|
install_firewall() {
|
||||||
if ! command -v ufw &>/dev/null; then
|
if ! command -v ufw &>/dev/null; then
|
||||||
echo "ufw firewall is not installed. Installing now..."
|
echo "ufw firewall is not installed. Installing now..."
|
||||||
apt-get update
|
apt-get update
|
||||||
|
@ -739,13 +750,16 @@ open_ports() {
|
||||||
ufw allow ssh
|
ufw allow ssh
|
||||||
ufw allow http
|
ufw allow http
|
||||||
ufw allow https
|
ufw allow https
|
||||||
ufw allow 2053/tcp
|
ufw allow 2053/tcp #webPort
|
||||||
|
ufw allow 2096/tcp #subport
|
||||||
|
|
||||||
# Enable the firewall
|
# Enable the firewall
|
||||||
ufw --force enable
|
ufw --force enable
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Prompt the user to enter a list of ports
|
open_ports() {
|
||||||
|
# Prompt the user to enter the ports they want to open
|
||||||
read -p "Enter the ports you want to open (e.g. 80,443,2053 or range 400-500): " ports
|
read -p "Enter the ports you want to open (e.g. 80,443,2053 or range 400-500): " ports
|
||||||
|
|
||||||
# Check if the input is valid
|
# Check if the input is valid
|
||||||
|
@ -761,19 +775,28 @@ open_ports() {
|
||||||
# Split the range into start and end ports
|
# Split the range into start and end ports
|
||||||
start_port=$(echo $port | cut -d'-' -f1)
|
start_port=$(echo $port | cut -d'-' -f1)
|
||||||
end_port=$(echo $port | cut -d'-' -f2)
|
end_port=$(echo $port | cut -d'-' -f2)
|
||||||
|
# Open the port range
|
||||||
ufw allow $start_port:$end_port/tcp
|
ufw allow $start_port:$end_port/tcp
|
||||||
ufw allow $start_port:$end_port/udp
|
ufw allow $start_port:$end_port/udp
|
||||||
else
|
else
|
||||||
|
# Open the single port
|
||||||
ufw allow "$port"
|
ufw allow "$port"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Confirm that the ports are open
|
# Confirm that the ports are opened
|
||||||
echo "The following ports are now open:"
|
echo "Opened the specified ports:"
|
||||||
ufw status | grep "ALLOW" | grep -Eo "[0-9]+(/[a-z]+)?"
|
for port in "${PORT_LIST[@]}"; do
|
||||||
|
if [[ $port == *-* ]]; then
|
||||||
echo "Firewall status:"
|
start_port=$(echo $port | cut -d'-' -f1)
|
||||||
ufw status verbose
|
end_port=$(echo $port | cut -d'-' -f2)
|
||||||
|
# Check if the port range has been successfully opened
|
||||||
|
(ufw status | grep -q "$start_port:$end_port") && echo "$start_port-$end_port"
|
||||||
|
else
|
||||||
|
# Check if the individual port has been successfully opened
|
||||||
|
(ufw status | grep -q "$port") && echo "$port"
|
||||||
|
fi
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_ports() {
|
delete_ports() {
|
||||||
|
@ -1289,8 +1312,8 @@ build_image_tar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
create_iplimit_jails() {
|
create_iplimit_jails() {
|
||||||
# Use default bantime if not passed => 15 minutes
|
# Use default bantime if not passed => 30 minutes
|
||||||
local bantime="${1:-15}"
|
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
|
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
|
||||||
|
@ -1321,7 +1344,7 @@ EOF
|
||||||
|
|
||||||
cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf
|
cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
before = iptables-common.conf
|
before = iptables-allports.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
actionstart = <iptables> -N f2b-<name>
|
actionstart = <iptables> -N f2b-<name>
|
||||||
|
@ -1364,15 +1387,22 @@ iplimit_remove_conflicts() {
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ip_validation() {
|
||||||
|
ipv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
|
||||||
|
ipv4_regex="^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)$"
|
||||||
|
}
|
||||||
|
|
||||||
iplimit_main() {
|
iplimit_main() {
|
||||||
echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
|
echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
|
||||||
echo -e "${green}\t2.${plain} Change Ban Duration"
|
echo -e "${green}\t2.${plain} Change Ban Duration"
|
||||||
echo -e "${green}\t3.${plain} Unban Everyone"
|
echo -e "${green}\t3.${plain} Unban Everyone"
|
||||||
echo -e "${green}\t4.${plain} Ban Logs"
|
echo -e "${green}\t4.${plain} Ban Logs"
|
||||||
echo -e "${green}\t5.${plain} Real-Time Logs"
|
echo -e "${green}\t5.${plain} Ban an IP Address"
|
||||||
echo -e "${green}\t6.${plain} Service Status"
|
echo -e "${green}\t6.${plain} Unban an IP Address"
|
||||||
echo -e "${green}\t7.${plain} Service Restart"
|
echo -e "${green}\t7.${plain} Real-Time Logs"
|
||||||
echo -e "${green}\t8.${plain} Uninstall Fail2ban and IP Limit"
|
echo -e "${green}\t8.${plain} Service Status"
|
||||||
|
echo -e "${green}\t9.${plain} Service Restart"
|
||||||
|
echo -e "${green}\t10.${plain} Uninstall Fail2ban and IP Limit"
|
||||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
read -p "Choose an option: " choice
|
read -p "Choose an option: " choice
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
|
@ -1400,7 +1430,7 @@ iplimit_main() {
|
||||||
3)
|
3)
|
||||||
confirm "Proceed with Unbanning everyone from IP Limit jail?" "y"
|
confirm "Proceed with Unbanning everyone from IP Limit jail?" "y"
|
||||||
if [[ $? == 0 ]]; then
|
if [[ $? == 0 ]]; then
|
||||||
fail2ban-client set 3x-ipl unban --all
|
fail2ban-client reload --restart --unban 3x-ipl
|
||||||
truncate -s 0 "${iplimit_banned_log_path}"
|
truncate -s 0 "${iplimit_banned_log_path}"
|
||||||
echo -e "${green}All users Unbanned successfully.${plain}"
|
echo -e "${green}All users Unbanned successfully.${plain}"
|
||||||
iplimit_main
|
iplimit_main
|
||||||
|
@ -1414,18 +1444,40 @@ iplimit_main() {
|
||||||
iplimit_main
|
iplimit_main
|
||||||
;;
|
;;
|
||||||
5)
|
5)
|
||||||
tail -f /var/log/fail2ban.log
|
read -rp "Enter the IP address you want to ban: " ban_ip
|
||||||
|
ip_validation
|
||||||
|
if [[ $ban_ip =~ $ipv4_regex || $ban_ip =~ $ipv6_regex ]]; then
|
||||||
|
fail2ban-client set 3x-ipl banip "$ban_ip"
|
||||||
|
echo -e "${green}IP Address ${ban_ip} has been banned successfully.${plain}"
|
||||||
|
else
|
||||||
|
echo -e "${red}Invalid IP address format! Please try again.${plain}"
|
||||||
|
fi
|
||||||
iplimit_main
|
iplimit_main
|
||||||
;;
|
;;
|
||||||
6)
|
6)
|
||||||
service fail2ban status
|
read -rp "Enter the IP address you want to unban: " unban_ip
|
||||||
|
ip_validation
|
||||||
|
if [[ $unban_ip =~ $ipv4_regex || $unban_ip =~ $ipv6_regex ]]; then
|
||||||
|
fail2ban-client set 3x-ipl unbanip "$unban_ip"
|
||||||
|
echo -e "${green}IP Address ${unban_ip} has been unbanned successfully.${plain}"
|
||||||
|
else
|
||||||
|
echo -e "${red}Invalid IP address format! Please try again.${plain}"
|
||||||
|
fi
|
||||||
iplimit_main
|
iplimit_main
|
||||||
;;
|
;;
|
||||||
7)
|
7)
|
||||||
systemctl restart fail2ban
|
tail -f /var/log/fail2ban.log
|
||||||
iplimit_main
|
iplimit_main
|
||||||
;;
|
;;
|
||||||
8)
|
8)
|
||||||
|
service fail2ban status
|
||||||
|
iplimit_main
|
||||||
|
;;
|
||||||
|
9)
|
||||||
|
systemctl restart fail2ban
|
||||||
|
iplimit_main
|
||||||
|
;;
|
||||||
|
10)
|
||||||
remove_iplimit
|
remove_iplimit
|
||||||
iplimit_main
|
iplimit_main
|
||||||
;;
|
;;
|
||||||
|
@ -1640,62 +1692,66 @@ SSH_port_forwarding() {
|
||||||
}
|
}
|
||||||
|
|
||||||
show_usage() {
|
show_usage() {
|
||||||
echo "x-ui control menu usages: "
|
echo -e "┌───────────────────────────────────────────────────────┐
|
||||||
echo "------------------------------------------"
|
│ ${blue}x-ui control menu usages (subcommands):${plain} │
|
||||||
echo -e "SUBCOMMANDS:"
|
│ │
|
||||||
echo -e "x-ui - Admin Management Script"
|
│ ${blue}x-ui${plain} - Admin Management Script │
|
||||||
echo -e "x-ui start - Start"
|
│ ${blue}x-ui start${plain} - Start │
|
||||||
echo -e "x-ui stop - Stop"
|
│ ${blue}x-ui stop${plain} - Stop │
|
||||||
echo -e "x-ui restart - Restart"
|
│ ${blue}x-ui restart${plain} - Restart │
|
||||||
echo -e "x-ui status - Current Status"
|
│ ${blue}x-ui status${plain} - Current Status │
|
||||||
echo -e "x-ui settings - Current Settings"
|
│ ${blue}x-ui settings${plain} - Current Settings │
|
||||||
echo -e "x-ui enable - Enable Autostart on OS Startup"
|
│ ${blue}x-ui enable${plain} - Enable Autostart on OS Startup │
|
||||||
echo -e "x-ui disable - Disable Autostart on OS Startup"
|
│ ${blue}x-ui disable${plain} - Disable Autostart on OS Startup │
|
||||||
echo -e "x-ui log - Check logs"
|
│ ${blue}x-ui log${plain} - Check logs │
|
||||||
echo -e "x-ui banlog - Check Fail2ban ban logs"
|
│ ${blue}x-ui banlog${plain} - Check Fail2ban ban logs │
|
||||||
echo -e "x-ui update - Update"
|
│ ${blue}x-ui update${plain} - Update │
|
||||||
echo -e "x-ui custom - custom version"
|
│ ${blue}x-ui legacy${plain} - legacy version │
|
||||||
echo -e "x-ui install - Install"
|
│ ${blue}x-ui install${plain} - Install │
|
||||||
echo -e "x-ui uninstall - Uninstall"
|
│ ${blue}x-ui uninstall${plain} - Uninstall │
|
||||||
echo "------------------------------------------"
|
└───────────────────────────────────────────────────────┘"
|
||||||
}
|
}
|
||||||
|
|
||||||
show_menu() {
|
show_menu() {
|
||||||
echo -e "
|
echo -e "
|
||||||
${green}3X-UI Panel Management Script${plain}
|
╔────────────────────────────────────────────────╗
|
||||||
${green}0.${plain} Exit Script
|
│ ${green}3X-UI Panel Management Script${plain} │
|
||||||
————————————————
|
│ ${green}0.${plain} Exit Script │
|
||||||
${green}1.${plain} Install
|
│────────────────────────────────────────────────│
|
||||||
${green}2.${plain} Update
|
│ ${green}1.${plain} Install │
|
||||||
${green}3.${plain} Update Menu
|
│ ${green}2.${plain} Update │
|
||||||
${green}4.${plain} Legacy Version
|
│ ${green}3.${plain} Update Menu │
|
||||||
${green}5.${plain} Uninstall
|
│ ${green}4.${plain} Legacy Version │
|
||||||
————————————————
|
│ ${green}5.${plain} Uninstall │
|
||||||
${green}6.${plain} Reset Username & Password & Secret Token
|
│────────────────────────────────────────────────│
|
||||||
${green}7.${plain} Reset Web Base Path
|
│ ${green}6.${plain} Reset Username & Password & Secret Token │
|
||||||
${green}8.${plain} Reset Settings
|
│ ${green}7.${plain} Reset Web Base Path │
|
||||||
${green}9.${plain} Change Port
|
│ ${green}8.${plain} Reset Settings │
|
||||||
${green}10.${plain} View Current Settings
|
│ ${green}9.${plain} Change Port │
|
||||||
————————————————
|
│ ${green}10.${plain} View Current Settings │
|
||||||
${green}11.${plain} Start
|
│────────────────────────────────────────────────│
|
||||||
${green}12.${plain} Stop
|
│ ${green}11.${plain} Start │
|
||||||
${green}13.${plain} Restart
|
│ ${green}12.${plain} Stop │
|
||||||
${green}14.${plain} Check Status
|
│ ${green}13.${plain} Restart │
|
||||||
${green}15.${plain} Logs Management
|
│ ${green}14.${plain} Check Status │
|
||||||
————————————————
|
│ ${green}15.${plain} Logs Management │
|
||||||
${green}16.${plain} Enable Autostart
|
│────────────────────────────────────────────────│
|
||||||
${green}17.${plain} Disable Autostart
|
│ ${green}16.${plain} Enable Autostart │
|
||||||
————————————————
|
│ ${green}17.${plain} Disable Autostart │
|
||||||
${green}18.${plain} SSL Certificate Management
|
│────────────────────────────────────────────────│
|
||||||
${green}19.${plain} Cloudflare SSL Certificate
|
│ ${green}18.${plain} SSL Certificate Management │
|
||||||
${green}20.${plain} IP Limit Management
|
│ ${green}19.${plain} Cloudflare SSL Certificate │
|
||||||
${green}21.${plain} Firewall Management
|
│ ${green}20.${plain} IP Limit Management │
|
||||||
${green}22.${plain} SSH Port Forwarding Management
|
│ ${green}21.${plain} Firewall Management │
|
||||||
————————————————
|
│ ${green}22.${plain} SSH Port Forwarding Management │
|
||||||
${green}23.${plain} Enable BBR
|
│────────────────────────────────────────────────│
|
||||||
${green}24.${plain} Update Geo Files
|
│ ${green}23.${plain} Enable BBR │
|
||||||
${green}25.${plain} Speedtest by Ookla
|
│ ${green}24.${plain} Update Geo Files │
|
||||||
${green}99.${plain} Build Docker Image (tar archive)
|
│ ${green}25.${plain} Speedtest by Ookla │
|
||||||
|
│────────────────────────────────────────────────│
|
||||||
|
│ ${green}99.${plain} Build Docker Image (tar archive)
|
||||||
|
│
|
||||||
|
╚────────────────────────────────────────────────╝
|
||||||
"
|
"
|
||||||
show_status
|
show_status
|
||||||
echo && read -p "Please enter your selection [0-25]: " num
|
echo && read -p "Please enter your selection [0-25]: " num
|
||||||
|
|
Loading…
Reference in a new issue