mirror of
				https://github.com/MHSanaei/3x-ui.git
				synced 2025-10-30 03:42:51 +00:00 
			
		
		
		
	Merge branch 'main' of https://github.com/MHSanaei/3x-ui into MHSanaei-main
This commit is contained in:
		
						commit
						47b5b11b06
					
				
					 23 changed files with 339 additions and 210 deletions
				
			
		
							
								
								
									
										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 | ||||||
							
								
								
									
										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> | ||||||
|  |  | ||||||
|  | @ -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
	
	 serogaq
						serogaq