mirror of
				https://github.com/MHSanaei/3x-ui.git
				synced 2025-10-31 04:12:51 +00:00 
			
		
		
		
	Merge branch 'main' into periodic-traffic-reset
This commit is contained in:
		
						commit
						a1c0231abc
					
				
					 30 changed files with 317 additions and 170 deletions
				
			
		
							
								
								
									
										76
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										76
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							|  | @ -146,3 +146,79 @@ jobs: | ||||||
|           asset_name: x-ui-linux-${{ matrix.platform }}.tar.gz |           asset_name: x-ui-linux-${{ matrix.platform }}.tar.gz | ||||||
|           overwrite: true |           overwrite: true | ||||||
|           prerelease: true |           prerelease: true | ||||||
|  | 
 | ||||||
|  |   # ================================= | ||||||
|  |   #  Windows Build | ||||||
|  |   # ================================= | ||||||
|  |   build-windows: | ||||||
|  |     name: Build for Windows | ||||||
|  |     permissions: | ||||||
|  |       contents: write | ||||||
|  |     strategy: | ||||||
|  |       matrix: | ||||||
|  |         platform: | ||||||
|  |           - amd64 | ||||||
|  |     runs-on: windows-latest | ||||||
|  |     steps: | ||||||
|  |       - name: Checkout repository | ||||||
|  |         uses: actions/checkout@v5 | ||||||
|  | 
 | ||||||
|  |       - name: Setup Go | ||||||
|  |         uses: actions/setup-go@v6 | ||||||
|  |         with: | ||||||
|  |           go-version-file: go.mod | ||||||
|  |           check-latest: true | ||||||
|  | 
 | ||||||
|  |       - name: Build 3X-UI for Windows | ||||||
|  |         shell: pwsh | ||||||
|  |         run: | | ||||||
|  |           $env:CGO_ENABLED="1" | ||||||
|  |           $env:GOOS="windows" | ||||||
|  |           $env:GOARCH="amd64" | ||||||
|  |           go build -ldflags "-w -s" -o xui-release.exe -v main.go | ||||||
|  |            | ||||||
|  |           mkdir x-ui | ||||||
|  |           Copy-Item xui-release.exe x-ui\ | ||||||
|  |           mkdir x-ui\bin | ||||||
|  |           cd x-ui\bin | ||||||
|  |            | ||||||
|  |           # Download Xray for Windows | ||||||
|  |           $Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v25.6.8/" | ||||||
|  |           Invoke-WebRequest -Uri "${Xray_URL}Xray-windows-64.zip" -OutFile "Xray-windows-64.zip" | ||||||
|  |           Expand-Archive -Path "Xray-windows-64.zip" -DestinationPath . | ||||||
|  |           Remove-Item "Xray-windows-64.zip" | ||||||
|  |           Remove-Item geoip.dat, geosite.dat -ErrorAction SilentlyContinue | ||||||
|  |           Invoke-WebRequest -Uri "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" -OutFile "geoip.dat" | ||||||
|  |           Invoke-WebRequest -Uri "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" -OutFile "geosite.dat" | ||||||
|  |           Invoke-WebRequest -Uri "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat" -OutFile "geoip_IR.dat" | ||||||
|  |           Invoke-WebRequest -Uri "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat" -OutFile "geosite_IR.dat" | ||||||
|  |           Invoke-WebRequest -Uri "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat" -OutFile "geoip_RU.dat" | ||||||
|  |           Invoke-WebRequest -Uri "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat" -OutFile "geosite_RU.dat" | ||||||
|  |           Rename-Item xray.exe xray-windows-amd64.exe | ||||||
|  |           cd .. | ||||||
|  |           Copy-Item -Path ..\windows_files\* -Destination . -Recurse | ||||||
|  |           cd .. | ||||||
|  | 
 | ||||||
|  |       - name: Package to Zip | ||||||
|  |         shell: pwsh | ||||||
|  |         run: | | ||||||
|  |           Compress-Archive -Path .\x-ui -DestinationPath "x-ui-windows-amd64.zip" | ||||||
|  | 
 | ||||||
|  |       - name: Upload files to Artifacts | ||||||
|  |         uses: actions/upload-artifact@v4 | ||||||
|  |         with: | ||||||
|  |           name: x-ui-windows-amd64 | ||||||
|  |           path: ./x-ui-windows-amd64.zip | ||||||
|  | 
 | ||||||
|  |       - name: Upload files to GH release | ||||||
|  |         uses: svenstaro/upload-release-action@v2 | ||||||
|  |         if: | | ||||||
|  |           (github.event_name == 'release' && github.event.action == 'published') || | ||||||
|  |           (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) | ||||||
|  |         with: | ||||||
|  |           repo_token: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |           tag: ${{ github.ref }} | ||||||
|  |           file: x-ui-windows-amd64.zip | ||||||
|  |           asset_name: x-ui-windows-amd64.zip | ||||||
|  |           overwrite: true | ||||||
|  |           prerelease: true | ||||||
|  | @ -12,11 +12,11 @@ type Protocol string | ||||||
| const ( | const ( | ||||||
| 	VMESS       Protocol = "vmess" | 	VMESS       Protocol = "vmess" | ||||||
| 	VLESS       Protocol = "vless" | 	VLESS       Protocol = "vless" | ||||||
| 	DOKODEMO    Protocol = "dokodemo-door" | 	Tunnel      Protocol = "tunnel" | ||||||
| 	HTTP        Protocol = "http" | 	HTTP        Protocol = "http" | ||||||
| 	Trojan      Protocol = "trojan" | 	Trojan      Protocol = "trojan" | ||||||
| 	Shadowsocks Protocol = "shadowsocks" | 	Shadowsocks Protocol = "shadowsocks" | ||||||
| 	Socks       Protocol = "socks" | 	Mixed       Protocol = "mixed" | ||||||
| 	WireGuard   Protocol = "wireguard" | 	WireGuard   Protocol = "wireguard" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
|   "inbounds": [ |   "inbounds": [ | ||||||
|     { |     { | ||||||
|       "port": 10808, |       "port": 10808, | ||||||
|       "protocol": "socks", |       "protocol": "mixed", | ||||||
|       "settings": { |       "settings": { | ||||||
|         "auth": "noauth", |         "auth": "noauth", | ||||||
|         "udp": true, |         "udp": true, | ||||||
|  | @ -28,7 +28,7 @@ | ||||||
|         ], |         ], | ||||||
|         "enabled": true |         "enabled": true | ||||||
|       }, |       }, | ||||||
|       "tag": "socks" |       "tag": "mixed" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "port": 10809, |       "port": 10809, | ||||||
|  |  | ||||||
|  | @ -50,8 +50,8 @@ class DBInbound { | ||||||
|         return this.protocol === Protocols.SHADOWSOCKS; |         return this.protocol === Protocols.SHADOWSOCKS; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     get isSocks() { |     get isMixed() { | ||||||
|         return this.protocol === Protocols.SOCKS; |         return this.protocol === Protocols.MIXED; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     get isHTTP() { |     get isHTTP() { | ||||||
|  |  | ||||||
|  | @ -3,8 +3,8 @@ const Protocols = { | ||||||
|     VLESS: 'vless', |     VLESS: 'vless', | ||||||
|     TROJAN: 'trojan', |     TROJAN: 'trojan', | ||||||
|     SHADOWSOCKS: 'shadowsocks', |     SHADOWSOCKS: 'shadowsocks', | ||||||
|     DOKODEMO: 'dokodemo-door', |     TUNNEL: 'tunnel', | ||||||
|     SOCKS: 'socks', |     MIXED: 'mixed', | ||||||
|     HTTP: 'http', |     HTTP: 'http', | ||||||
|     WIREGUARD: 'wireguard', |     WIREGUARD: 'wireguard', | ||||||
| }; | }; | ||||||
|  | @ -729,7 +729,7 @@ class RealityStreamSettings extends XrayCommonClass { | ||||||
|     constructor( |     constructor( | ||||||
|         show = false, |         show = false, | ||||||
|         xver = 0, |         xver = 0, | ||||||
|         dest = 'google.com:443', |         target = 'google.com:443', | ||||||
|         serverNames = 'google.com,www.google.com', |         serverNames = 'google.com,www.google.com', | ||||||
|         privateKey = '', |         privateKey = '', | ||||||
|         minClientVer = '', |         minClientVer = '', | ||||||
|  | @ -742,7 +742,7 @@ class RealityStreamSettings extends XrayCommonClass { | ||||||
|         super(); |         super(); | ||||||
|         this.show = show; |         this.show = show; | ||||||
|         this.xver = xver; |         this.xver = xver; | ||||||
|         this.dest = dest; |         this.target = target; | ||||||
|         this.serverNames = Array.isArray(serverNames) ? serverNames.join(",") : serverNames; |         this.serverNames = Array.isArray(serverNames) ? serverNames.join(",") : serverNames; | ||||||
|         this.privateKey = privateKey; |         this.privateKey = privateKey; | ||||||
|         this.minClientVer = minClientVer; |         this.minClientVer = minClientVer; | ||||||
|  | @ -767,7 +767,7 @@ class RealityStreamSettings extends XrayCommonClass { | ||||||
|         return new RealityStreamSettings( |         return new RealityStreamSettings( | ||||||
|             json.show, |             json.show, | ||||||
|             json.xver, |             json.xver, | ||||||
|             json.dest, |             json.target, | ||||||
|             json.serverNames, |             json.serverNames, | ||||||
|             json.privateKey, |             json.privateKey, | ||||||
|             json.minClientVer, |             json.minClientVer, | ||||||
|  | @ -783,7 +783,7 @@ class RealityStreamSettings extends XrayCommonClass { | ||||||
|         return { |         return { | ||||||
|             show: this.show, |             show: this.show, | ||||||
|             xver: this.xver, |             xver: this.xver, | ||||||
|             dest: this.dest, |             target: this.target, | ||||||
|             serverNames: this.serverNames.split(","), |             serverNames: this.serverNames.split(","), | ||||||
|             privateKey: this.privateKey, |             privateKey: this.privateKey, | ||||||
|             minClientVer: this.minClientVer, |             minClientVer: this.minClientVer, | ||||||
|  | @ -1712,8 +1712,8 @@ Inbound.Settings = class extends XrayCommonClass { | ||||||
|             case Protocols.VLESS: return new Inbound.VLESSSettings(protocol); |             case Protocols.VLESS: return new Inbound.VLESSSettings(protocol); | ||||||
|             case Protocols.TROJAN: return new Inbound.TrojanSettings(protocol); |             case Protocols.TROJAN: return new Inbound.TrojanSettings(protocol); | ||||||
|             case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol); |             case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol); | ||||||
|             case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol); |             case Protocols.TUNNEL: return new Inbound.TunnelSettings(protocol); | ||||||
|             case Protocols.SOCKS: return new Inbound.SocksSettings(protocol); |             case Protocols.MIXED: return new Inbound.MixedSettings(protocol); | ||||||
|             case Protocols.HTTP: return new Inbound.HttpSettings(protocol); |             case Protocols.HTTP: return new Inbound.HttpSettings(protocol); | ||||||
|             case Protocols.WIREGUARD: return new Inbound.WireguardSettings(protocol); |             case Protocols.WIREGUARD: return new Inbound.WireguardSettings(protocol); | ||||||
|             default: return null; |             default: return null; | ||||||
|  | @ -1726,8 +1726,8 @@ Inbound.Settings = class extends XrayCommonClass { | ||||||
|             case Protocols.VLESS: return Inbound.VLESSSettings.fromJson(json); |             case Protocols.VLESS: return Inbound.VLESSSettings.fromJson(json); | ||||||
|             case Protocols.TROJAN: return Inbound.TrojanSettings.fromJson(json); |             case Protocols.TROJAN: return Inbound.TrojanSettings.fromJson(json); | ||||||
|             case Protocols.SHADOWSOCKS: return Inbound.ShadowsocksSettings.fromJson(json); |             case Protocols.SHADOWSOCKS: return Inbound.ShadowsocksSettings.fromJson(json); | ||||||
|             case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json); |             case Protocols.TUNNEL: return Inbound.TunnelSettings.fromJson(json); | ||||||
|             case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json); |             case Protocols.MIXED: return Inbound.MixedSettings.fromJson(json); | ||||||
|             case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json); |             case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json); | ||||||
|             case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json); |             case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json); | ||||||
|             default: return null; |             default: return null; | ||||||
|  | @ -2327,7 +2327,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass { | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Inbound.DokodemoSettings = class extends Inbound.Settings { | Inbound.TunnelSettings = class extends Inbound.Settings { | ||||||
|     constructor( |     constructor( | ||||||
|         protocol, |         protocol, | ||||||
|         address, |         address, | ||||||
|  | @ -2345,8 +2345,8 @@ Inbound.DokodemoSettings = class extends Inbound.Settings { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static fromJson(json = {}) { |     static fromJson(json = {}) { | ||||||
|         return new Inbound.DokodemoSettings( |         return new Inbound.TunnelSettings( | ||||||
|             Protocols.DOKODEMO, |             Protocols.TUNNEL, | ||||||
|             json.address, |             json.address, | ||||||
|             json.port, |             json.port, | ||||||
|             XrayCommonClass.toHeaders(json.portMap), |             XrayCommonClass.toHeaders(json.portMap), | ||||||
|  | @ -2366,8 +2366,8 @@ Inbound.DokodemoSettings = class extends Inbound.Settings { | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Inbound.SocksSettings = class extends Inbound.Settings { | Inbound.MixedSettings = class extends Inbound.Settings { | ||||||
|     constructor(protocol, auth = 'password', accounts = [new Inbound.SocksSettings.SocksAccount()], udp = false, ip = '127.0.0.1') { |     constructor(protocol, auth = 'password', accounts = [new Inbound.MixedSettings.SocksAccount()], udp = false, ip = '127.0.0.1') { | ||||||
|         super(protocol); |         super(protocol); | ||||||
|         this.auth = auth; |         this.auth = auth; | ||||||
|         this.accounts = accounts; |         this.accounts = accounts; | ||||||
|  | @ -2387,11 +2387,11 @@ Inbound.SocksSettings = class extends Inbound.Settings { | ||||||
|         let accounts; |         let accounts; | ||||||
|         if (json.auth === 'password') { |         if (json.auth === 'password') { | ||||||
|             accounts = json.accounts.map( |             accounts = json.accounts.map( | ||||||
|                 account => Inbound.SocksSettings.SocksAccount.fromJson(account) |                 account => Inbound.MixedSettings.SocksAccount.fromJson(account) | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
|         return new Inbound.SocksSettings( |         return new Inbound.MixedSettings( | ||||||
|             Protocols.SOCKS, |             Protocols.MIXED, | ||||||
|             json.auth, |             json.auth, | ||||||
|             accounts, |             accounts, | ||||||
|             json.udp, |             json.udp, | ||||||
|  | @ -2408,7 +2408,7 @@ Inbound.SocksSettings = class extends Inbound.Settings { | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| Inbound.SocksSettings.SocksAccount = class extends XrayCommonClass { | Inbound.MixedSettings.SocksAccount = class extends XrayCommonClass { | ||||||
|     constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq(10)) { |     constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq(10)) { | ||||||
|         super(); |         super(); | ||||||
|         this.user = user; |         this.user = user; | ||||||
|  | @ -2416,7 +2416,7 @@ Inbound.SocksSettings.SocksAccount = class extends XrayCommonClass { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static fromJson(json = {}) { |     static fromJson(json = {}) { | ||||||
|         return new Inbound.SocksSettings.SocksAccount(json.user, json.pass); |         return new Inbound.MixedSettings.SocksAccount(json.user, json.pass); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ const Protocols = { | ||||||
|     VLESS: "vless", |     VLESS: "vless", | ||||||
|     Trojan: "trojan", |     Trojan: "trojan", | ||||||
|     Shadowsocks: "shadowsocks", |     Shadowsocks: "shadowsocks", | ||||||
|     Socks: "socks", |     Mixed: "mixed", | ||||||
|     HTTP: "http", |     HTTP: "http", | ||||||
|     Wireguard: "wireguard" |     Wireguard: "wireguard" | ||||||
| }; | }; | ||||||
|  | @ -643,7 +643,7 @@ class Outbound extends CommonClass { | ||||||
|             Protocols.Trojan, |             Protocols.Trojan, | ||||||
|             Protocols.Shadowsocks, |             Protocols.Shadowsocks, | ||||||
|             Protocols.HTTP, |             Protocols.HTTP, | ||||||
|             Protocols.Socks |             Protocols.Mixed | ||||||
|         ].includes(this.protocol); |         ].includes(this.protocol); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -652,7 +652,7 @@ class Outbound extends CommonClass { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     hasServers() { |     hasServers() { | ||||||
|         return [Protocols.Trojan, Protocols.Shadowsocks, Protocols.Socks, Protocols.HTTP].includes(this.protocol); |         return [Protocols.Trojan, Protocols.Shadowsocks, Protocols.Mixed, Protocols.HTTP].includes(this.protocol); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     hasAddressPort() { |     hasAddressPort() { | ||||||
|  | @ -662,13 +662,13 @@ class Outbound extends CommonClass { | ||||||
|             Protocols.VLESS, |             Protocols.VLESS, | ||||||
|             Protocols.Trojan, |             Protocols.Trojan, | ||||||
|             Protocols.Shadowsocks, |             Protocols.Shadowsocks, | ||||||
|             Protocols.Socks, |             Protocols.Mixed, | ||||||
|             Protocols.HTTP |             Protocols.HTTP | ||||||
|         ].includes(this.protocol); |         ].includes(this.protocol); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     hasUsername() { |     hasUsername() { | ||||||
|         return [Protocols.Socks, Protocols.HTTP].includes(this.protocol); |         return [Protocols.Mixed, Protocols.HTTP].includes(this.protocol); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static fromJson(json = {}) { |     static fromJson(json = {}) { | ||||||
|  | @ -847,7 +847,7 @@ Outbound.Settings = class extends CommonClass { | ||||||
|             case Protocols.VLESS: return new Outbound.VLESSSettings(); |             case Protocols.VLESS: return new Outbound.VLESSSettings(); | ||||||
|             case Protocols.Trojan: return new Outbound.TrojanSettings(); |             case Protocols.Trojan: return new Outbound.TrojanSettings(); | ||||||
|             case Protocols.Shadowsocks: return new Outbound.ShadowsocksSettings(); |             case Protocols.Shadowsocks: return new Outbound.ShadowsocksSettings(); | ||||||
|             case Protocols.Socks: return new Outbound.SocksSettings(); |             case Protocols.Mixed: return new Outbound.MixedSettings(); | ||||||
|             case Protocols.HTTP: return new Outbound.HttpSettings(); |             case Protocols.HTTP: return new Outbound.HttpSettings(); | ||||||
|             case Protocols.Wireguard: return new Outbound.WireguardSettings(); |             case Protocols.Wireguard: return new Outbound.WireguardSettings(); | ||||||
|             default: return null; |             default: return null; | ||||||
|  | @ -863,7 +863,7 @@ Outbound.Settings = class extends CommonClass { | ||||||
|             case Protocols.VLESS: return Outbound.VLESSSettings.fromJson(json); |             case Protocols.VLESS: return Outbound.VLESSSettings.fromJson(json); | ||||||
|             case Protocols.Trojan: return Outbound.TrojanSettings.fromJson(json); |             case Protocols.Trojan: return Outbound.TrojanSettings.fromJson(json); | ||||||
|             case Protocols.Shadowsocks: return Outbound.ShadowsocksSettings.fromJson(json); |             case Protocols.Shadowsocks: return Outbound.ShadowsocksSettings.fromJson(json); | ||||||
|             case Protocols.Socks: return Outbound.SocksSettings.fromJson(json); |             case Protocols.Mixed: return Outbound.MixedSettings.fromJson(json); | ||||||
|             case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json); |             case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json); | ||||||
|             case Protocols.Wireguard: return Outbound.WireguardSettings.fromJson(json); |             case Protocols.Wireguard: return Outbound.WireguardSettings.fromJson(json); | ||||||
|             default: return null; |             default: return null; | ||||||
|  | @ -1141,7 +1141,7 @@ Outbound.ShadowsocksSettings = class extends CommonClass { | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Outbound.SocksSettings = class extends CommonClass { | Outbound.MixedSettings = class extends CommonClass { | ||||||
|     constructor(address, port, user, pass) { |     constructor(address, port, user, pass) { | ||||||
|         super(); |         super(); | ||||||
|         this.address = address; |         this.address = address; | ||||||
|  | @ -1153,7 +1153,7 @@ Outbound.SocksSettings = class extends CommonClass { | ||||||
|     static fromJson(json = {}) { |     static fromJson(json = {}) { | ||||||
|         let servers = json.servers; |         let servers = json.servers; | ||||||
|         if (ObjectUtil.isArrEmpty(servers)) servers = [{ users: [{}] }]; |         if (ObjectUtil.isArrEmpty(servers)) servers = [{ users: [{}] }]; | ||||||
|         return new Outbound.SocksSettings( |         return new Outbound.MixedSettings( | ||||||
|             servers[0].address, |             servers[0].address, | ||||||
|             servers[0].port, |             servers[0].port, | ||||||
|             ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user, |             ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user, | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import ( | ||||||
| type APIController struct { | type APIController struct { | ||||||
| 	BaseController | 	BaseController | ||||||
| 	inboundController *InboundController | 	inboundController *InboundController | ||||||
|  | 	serverController  *ServerController | ||||||
| 	Tgbot             service.Tgbot | 	Tgbot             service.Tgbot | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -19,43 +20,22 @@ func NewAPIController(g *gin.RouterGroup) *APIController { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *APIController) initRouter(g *gin.RouterGroup) { | func (a *APIController) initRouter(g *gin.RouterGroup) { | ||||||
| 	g = g.Group("/panel/api/inbounds") | 	// Main API group
 | ||||||
| 	g.Use(a.checkLogin) | 	api := g.Group("/panel/api") | ||||||
|  | 	api.Use(a.checkLogin) | ||||||
| 
 | 
 | ||||||
| 	a.inboundController = NewInboundController(g) | 	// Inbounds API
 | ||||||
|  | 	inbounds := api.Group("/inbounds") | ||||||
|  | 	a.inboundController = NewInboundController(inbounds) | ||||||
| 
 | 
 | ||||||
| 	inboundRoutes := []struct { | 	// Server API
 | ||||||
| 		Method  string | 	server := api.Group("/server") | ||||||
| 		Path    string | 	a.serverController = NewServerController(server) | ||||||
| 		Handler gin.HandlerFunc |  | ||||||
| 	}{ |  | ||||||
| 		{"GET", "/createbackup", a.createBackup}, |  | ||||||
| 		{"GET", "/list", a.inboundController.getInbounds}, |  | ||||||
| 		{"GET", "/get/:id", a.inboundController.getInbound}, |  | ||||||
| 		{"GET", "/getClientTraffics/:email", a.inboundController.getClientTraffics}, |  | ||||||
| 		{"GET", "/getClientTrafficsById/:id", a.inboundController.getClientTrafficsById}, |  | ||||||
| 		{"POST", "/add", a.inboundController.addInbound}, |  | ||||||
| 		{"POST", "/del/:id", a.inboundController.delInbound}, |  | ||||||
| 		{"POST", "/update/:id", a.inboundController.updateInbound}, |  | ||||||
| 		{"POST", "/clientIps/:email", a.inboundController.getClientIps}, |  | ||||||
| 		{"POST", "/clearClientIps/:email", a.inboundController.clearClientIps}, |  | ||||||
| 		{"POST", "/addClient", a.inboundController.addInboundClient}, |  | ||||||
| 		{"POST", "/:id/delClient/:clientId", a.inboundController.delInboundClient}, |  | ||||||
| 		{"POST", "/updateClient/:clientId", a.inboundController.updateInboundClient}, |  | ||||||
| 		{"POST", "/:id/resetClientTraffic/:email", a.inboundController.resetClientTraffic}, |  | ||||||
| 		{"POST", "/resetAllTraffics", a.inboundController.resetAllTraffics}, |  | ||||||
| 		{"POST", "/resetAllClientTraffics/:id", a.inboundController.resetAllClientTraffics}, |  | ||||||
| 		{"POST", "/delDepletedClients/:id", a.inboundController.delDepletedClients}, |  | ||||||
| 		{"POST", "/onlines", a.inboundController.onlines}, |  | ||||||
| 		{"POST", "/lastOnline", a.inboundController.lastOnline}, |  | ||||||
| 		{"POST", "/updateClientTraffic/:email", a.inboundController.updateClientTraffic}, |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	for _, route := range inboundRoutes { | 	// Extra routes
 | ||||||
| 		g.Handle(route.Method, route.Path, route.Handler) | 	api.GET("/backuptotgbot", a.BackuptoTgbot) | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *APIController) createBackup(c *gin.Context) { | func (a *APIController) BackuptoTgbot(c *gin.Context) { | ||||||
| 	a.Tgbot.SendBackupToAdmins() | 	a.Tgbot.SendBackupToAdmins() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,9 +24,12 @@ func NewInboundController(g *gin.RouterGroup) *InboundController { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *InboundController) initRouter(g *gin.RouterGroup) { | func (a *InboundController) initRouter(g *gin.RouterGroup) { | ||||||
| 	g = g.Group("/inbound") |  | ||||||
| 
 | 
 | ||||||
| 	g.POST("/list", a.getInbounds) | 	g.GET("/list", a.getInbounds) | ||||||
|  | 	g.GET("/get/:id", a.getInbound) | ||||||
|  | 	g.GET("/getClientTraffics/:email", a.getClientTraffics) | ||||||
|  | 	g.GET("/getClientTrafficsById/:id", a.getClientTrafficsById) | ||||||
|  | 
 | ||||||
| 	g.POST("/add", a.addInbound) | 	g.POST("/add", a.addInbound) | ||||||
| 	g.POST("/del/:id", a.delInbound) | 	g.POST("/del/:id", a.delInbound) | ||||||
| 	g.POST("/update/:id", a.updateInbound) | 	g.POST("/update/:id", a.updateInbound) | ||||||
|  | @ -41,6 +44,8 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) { | ||||||
| 	g.POST("/delDepletedClients/:id", a.delDepletedClients) | 	g.POST("/delDepletedClients/:id", a.delDepletedClients) | ||||||
| 	g.POST("/import", a.importInbound) | 	g.POST("/import", a.importInbound) | ||||||
| 	g.POST("/onlines", a.onlines) | 	g.POST("/onlines", a.onlines) | ||||||
|  | 	g.POST("/lastOnline", a.lastOnline) | ||||||
|  | 	g.POST("/updateClientTraffic/:email", a.updateClientTraffic) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *InboundController) getInbounds(c *gin.Context) { | func (a *InboundController) getInbounds(c *gin.Context) { | ||||||
|  |  | ||||||
|  | @ -37,11 +37,17 @@ func NewServerController(g *gin.RouterGroup) *ServerController { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *ServerController) initRouter(g *gin.RouterGroup) { | func (a *ServerController) initRouter(g *gin.RouterGroup) { | ||||||
| 	g = g.Group("/server") |  | ||||||
| 
 | 
 | ||||||
| 	g.Use(a.checkLogin) | 	g.GET("/status", a.status) | ||||||
| 	g.POST("/status", a.status) | 	g.GET("/getXrayVersion", a.getXrayVersion) | ||||||
| 	g.POST("/getXrayVersion", a.getXrayVersion) | 	g.GET("/getConfigJson", a.getConfigJson) | ||||||
|  | 	g.GET("/getDb", a.getDb) | ||||||
|  | 	g.GET("/getNewUUID", a.getNewUUID) | ||||||
|  | 	g.GET("/getNewX25519Cert", a.getNewX25519Cert) | ||||||
|  | 	g.GET("/getNewmldsa65", a.getNewmldsa65) | ||||||
|  | 	g.GET("/getNewmlkem768", a.getNewmlkem768) | ||||||
|  | 	g.GET("/getNewVlessEnc", a.getNewVlessEnc) | ||||||
|  | 
 | ||||||
| 	g.POST("/stopXrayService", a.stopXrayService) | 	g.POST("/stopXrayService", a.stopXrayService) | ||||||
| 	g.POST("/restartXrayService", a.restartXrayService) | 	g.POST("/restartXrayService", a.restartXrayService) | ||||||
| 	g.POST("/installXray/:version", a.installXray) | 	g.POST("/installXray/:version", a.installXray) | ||||||
|  | @ -49,13 +55,8 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) { | ||||||
| 	g.POST("/updateGeofile/:fileName", a.updateGeofile) | 	g.POST("/updateGeofile/:fileName", a.updateGeofile) | ||||||
| 	g.POST("/logs/:count", a.getLogs) | 	g.POST("/logs/:count", a.getLogs) | ||||||
| 	g.POST("/xraylogs/:count", a.getXrayLogs) | 	g.POST("/xraylogs/:count", a.getXrayLogs) | ||||||
| 	g.POST("/getConfigJson", a.getConfigJson) |  | ||||||
| 	g.GET("/getDb", a.getDb) |  | ||||||
| 	g.POST("/importDB", a.importDB) | 	g.POST("/importDB", a.importDB) | ||||||
| 	g.POST("/getNewX25519Cert", a.getNewX25519Cert) |  | ||||||
| 	g.POST("/getNewmldsa65", a.getNewmldsa65) |  | ||||||
| 	g.POST("/getNewEchCert", a.getNewEchCert) | 	g.POST("/getNewEchCert", a.getNewEchCert) | ||||||
| 	g.POST("/getNewVlessEnc", a.getNewVlessEnc) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *ServerController) refreshStatus() { | func (a *ServerController) refreshStatus() { | ||||||
|  | @ -276,3 +277,22 @@ func (a *ServerController) getNewVlessEnc(c *gin.Context) { | ||||||
| 	} | 	} | ||||||
| 	jsonObj(c, out, nil) | 	jsonObj(c, out, nil) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (a *ServerController) getNewUUID(c *gin.Context) { | ||||||
|  | 	uuidResp, err := a.serverService.GetNewUUID() | ||||||
|  | 	if err != nil { | ||||||
|  | 		jsonMsg(c, "Failed to generate UUID", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	jsonObj(c, uuidResp, nil) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a *ServerController) getNewmlkem768(c *gin.Context) { | ||||||
|  | 	out, err := a.serverService.GetNewmlkem768() | ||||||
|  | 	if err != nil { | ||||||
|  | 		jsonMsg(c, "Failed to generate mlkem768 keys", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	jsonObj(c, out, nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ type XUIController struct { | ||||||
| 	BaseController | 	BaseController | ||||||
| 
 | 
 | ||||||
| 	inboundController     *InboundController | 	inboundController     *InboundController | ||||||
|  | 	serverController      *ServerController | ||||||
| 	settingController     *SettingController | 	settingController     *SettingController | ||||||
| 	xraySettingController *XraySettingController | 	xraySettingController *XraySettingController | ||||||
| } | } | ||||||
|  | @ -28,6 +29,7 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) { | ||||||
| 	g.GET("/xray", a.xraySettings) | 	g.GET("/xray", a.xraySettings) | ||||||
| 
 | 
 | ||||||
| 	a.inboundController = NewInboundController(g) | 	a.inboundController = NewInboundController(g) | ||||||
|  | 	a.serverController = NewServerController(g) | ||||||
| 	a.settingController = NewSettingController(g) | 	a.settingController = NewSettingController(g) | ||||||
| 	a.xraySettingController = NewXraySettingController(g) | 	a.xraySettingController = NewXraySettingController(g) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -101,14 +101,14 @@ | ||||||
|     {{template "form/shadowsocks"}} |     {{template "form/shadowsocks"}} | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <!-- dokodemo-door --> | <!-- tunnel --> | ||||||
| <template v-if="inbound.protocol === Protocols.DOKODEMO"> | <template v-if="inbound.protocol === Protocols.TUNNEL"> | ||||||
|     {{template "form/dokodemo"}} |     {{template "form/tunnel"}} | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <!-- socks --> | <!-- mixed --> | ||||||
| <template v-if="inbound.protocol === Protocols.SOCKS"> | <template v-if="inbound.protocol === Protocols.MIXED"> | ||||||
|     {{template "form/socks"}} |     {{template "form/mixed"}} | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <!-- http --> | <!-- http --> | ||||||
|  |  | ||||||
|  | @ -241,9 +241,9 @@ | ||||||
|         </template> |         </template> | ||||||
|       </template> |       </template> | ||||||
| 
 | 
 | ||||||
|       <!-- Servers (trojan/shadowsocks/socks/http) settings --> |       <!-- Servers (trojan/shadowsocks/mixed/http) settings --> | ||||||
|       <template v-if="outbound.hasServers()"> |       <template v-if="outbound.hasServers()"> | ||||||
|         <!-- http / socks --> |         <!-- http / mixed --> | ||||||
|         <template v-if="outbound.hasUsername()"> |         <template v-if="outbound.hasUsername()"> | ||||||
|           <a-form-item label='{{ i18n "username" }}'> |           <a-form-item label='{{ i18n "username" }}'> | ||||||
|             <a-input v-model.trim="outbound.settings.user"></a-input> |             <a-input v-model.trim="outbound.settings.user"></a-input> | ||||||
|  | @ -441,6 +441,9 @@ | ||||||
|               <a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option> |               <a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option> | ||||||
|             </a-select> |             </a-select> | ||||||
|           </a-form-item> |           </a-form-item> | ||||||
|  |           <a-form-item label="ECH Config List"> | ||||||
|  |             <a-input v-model.trim="outbound.stream.tls.echConfigList"></a-input> | ||||||
|  |           </a-form-item> | ||||||
|           <a-form-item label="Allow Insecure"> |           <a-form-item label="Allow Insecure"> | ||||||
|             <a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch> |             <a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch> | ||||||
|           </a-form-item> |           </a-form-item> | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| {{define "form/dokodemo"}} | {{define "form/tunnel"}} | ||||||
| <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> | <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> | ||||||
|     <a-form-item label='{{ i18n "pages.inbounds.targetAddress"}}'> |     <a-form-item label='{{ i18n "pages.inbounds.targetAddress"}}'> | ||||||
|         <a-input v-model.trim="inbound.settings.address"></a-input> |         <a-input v-model.trim="inbound.settings.address"></a-input> | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| {{define "form/socks"}} | {{define "form/mixed"}} | ||||||
| <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> | <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> | ||||||
|   <a-form-item label='{{ i18n "pages.inbounds.enable" }} UDP'> |   <a-form-item label='{{ i18n "pages.inbounds.enable" }} UDP'> | ||||||
|     <a-switch v-model="inbound.settings.udp"></a-switch> |     <a-switch v-model="inbound.settings.udp"></a-switch> | ||||||
|  | @ -15,7 +15,7 @@ | ||||||
|         <td width="45%">{{ i18n "username" }}</td> |         <td width="45%">{{ i18n "username" }}</td> | ||||||
|         <td width="45%">{{ i18n "password" }}</td> |         <td width="45%">{{ i18n "password" }}</td> | ||||||
|         <td> |         <td> | ||||||
|           <a-button icon="plus" size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())"></a-button> |           <a-button icon="plus" size="small" @click="inbound.settings.addAccount(new Inbound.MixedSettings.SocksAccount())"></a-button> | ||||||
|         </td> |         </td> | ||||||
|       </tr> |       </tr> | ||||||
|     </table> |     </table> | ||||||
|  |  | ||||||
|  | @ -22,7 +22,6 @@ | ||||||
|   <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> |   <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> | ||||||
|     <a-form-item label="Authentication"> |     <a-form-item label="Authentication"> | ||||||
|       <a-select v-model="inbound.settings.selectedAuth" @change="getNewVlessEnc" :dropdown-class-name="themeSwitcher.currentTheme"> |       <a-select v-model="inbound.settings.selectedAuth" @change="getNewVlessEnc" :dropdown-class-name="themeSwitcher.currentTheme"> | ||||||
|         <a-select-option :value="undefined">None</a-select-option> |  | ||||||
|         <a-select-option value="X25519, not Post-Quantum">X25519 (not Post-Quantum)</a-select-option> |         <a-select-option value="X25519, not Post-Quantum">X25519 (not Post-Quantum)</a-select-option> | ||||||
|         <a-select-option value="ML-KEM-768, Post-Quantum">ML-KEM-768 (Post-Quantum)</a-select-option> |         <a-select-option value="ML-KEM-768, Post-Quantum">ML-KEM-768 (Post-Quantum)</a-select-option> | ||||||
|       </a-select> |       </a-select> | ||||||
|  | @ -31,17 +30,17 @@ | ||||||
|       <a-input v-model.trim="inbound.settings.decryption"></a-input> |       <a-input v-model.trim="inbound.settings.decryption"></a-input> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|     <a-form-item label="encryption"> |     <a-form-item label="encryption"> | ||||||
|       <a-input v-model="inbound.settings.encryption" disabled></a-input> |       <a-input v-model="inbound.settings.encryption"></a-input> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|     <a-form-item label=" "> |     <a-form-item label=" "> | ||||||
|       <a-space> |       <a-space> | ||||||
|         <a-button type="primary" icon="import" @click="getNewVlessEnc">Get New keys</a-button> |         <a-button type="primary" icon="import" @click="getNewVlessEnc">Get New keys</a-button> | ||||||
|         <a-button danger @click="clearKeys">Clear</a-button> |         <a-button danger @click="clearVlessEnc">Clear</a-button> | ||||||
|       </a-space> |       </a-space> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|   </a-form> |   </a-form> | ||||||
| </template> | </template> | ||||||
| <template v-if="inbound.isTcp && !inbound.settings.encryption"> | <template v-if="inbound.isTcp && !inbound.settings.selectedAuth"> | ||||||
|   <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> |   <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> | ||||||
|     <a-form-item label="Fallbacks"> |     <a-form-item label="Fallbacks"> | ||||||
|       <a-button icon="plus" type="primary" size="small" @click="inbound.settings.addFallback()"></a-button> |       <a-button icon="plus" type="primary" size="small" @click="inbound.settings.addFallback()"></a-button> | ||||||
|  |  | ||||||
|  | @ -12,8 +12,8 @@ | ||||||
|             <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option> |             <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option> | ||||||
|         </a-select> |         </a-select> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|     <a-form-item label='Dest (Target)'> |     <a-form-item label='Target'> | ||||||
|         <a-input v-model.trim="inbound.stream.reality.dest"></a-input> |         <a-input v-model.trim="inbound.stream.reality.target"></a-input> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|     <a-form-item label='SNI'> |     <a-form-item label='SNI'> | ||||||
|         <a-input v-model.trim="inbound.stream.reality.serverNames"></a-input> |         <a-input v-model.trim="inbound.stream.reality.serverNames"></a-input> | ||||||
|  | @ -48,7 +48,10 @@ | ||||||
|         <a-textarea v-model="inbound.stream.reality.privateKey"></a-textarea> |         <a-textarea v-model="inbound.stream.reality.privateKey"></a-textarea> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|     <a-form-item label=" "> |     <a-form-item label=" "> | ||||||
|         <a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert</a-button> |         <a-space> | ||||||
|  |             <a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert</a-button> | ||||||
|  |             <a-button danger @click="clearX25519Cert">Clear</a-button> | ||||||
|  |         </a-space> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|     <a-form-item label="mldsa65 Seed"> |     <a-form-item label="mldsa65 Seed"> | ||||||
|         <a-textarea v-model="inbound.stream.reality.mldsa65Seed"></a-textarea> |         <a-textarea v-model="inbound.stream.reality.mldsa65Seed"></a-textarea> | ||||||
|  | @ -57,7 +60,10 @@ | ||||||
|         <a-textarea v-model="inbound.stream.reality.settings.mldsa65Verify"></a-textarea> |         <a-textarea v-model="inbound.stream.reality.settings.mldsa65Verify"></a-textarea> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|     <a-form-item label=" "> |     <a-form-item label=" "> | ||||||
|         <a-button type="primary" icon="import" @click="getNewmldsa65">Get New Seed</a-button> |         <a-space> | ||||||
|  |             <a-button type="primary" icon="import" @click="getNewmldsa65">Get New Seed</a-button> | ||||||
|  |             <a-button danger @click="clearMldsa65">Clear</a-button> | ||||||
|  |         </a-space> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
| </template> | </template> | ||||||
| {{end}} | {{end}} | ||||||
|  | @ -5,13 +5,13 @@ | ||||||
|   <a-form-item label='{{ i18n "security" }}'> |   <a-form-item label='{{ i18n "security" }}'> | ||||||
|     <a-radio-group v-model="inbound.stream.security" button-style="solid"> |     <a-radio-group v-model="inbound.stream.security" button-style="solid"> | ||||||
|       <a-radio-button value="none">{{ i18n "none" }}</a-radio-button> |       <a-radio-button value="none">{{ i18n "none" }}</a-radio-button> | ||||||
|       <a-radio-button v-if="inbound.canEnableReality() && !inbound.settings.encryption" value="reality">Reality</a-radio-button> |       <a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button> | ||||||
|       <a-radio-button v-if="!inbound.settings.encryption" value="tls">TLS</a-radio-button> |       <a-radio-button value="tls">TLS</a-radio-button> | ||||||
|     </a-radio-group> |     </a-radio-group> | ||||||
|   </a-form-item> |   </a-form-item> | ||||||
| 
 | 
 | ||||||
|   <!-- tls settings --> |   <!-- tls settings --> | ||||||
|   <template v-if="inbound.stream.isTls && !inbound.settings.encryption"> |   <template v-if="inbound.stream.isTls"> | ||||||
|     <a-form-item label="SNI" placeholder="Server Name Indication"> |     <a-form-item label="SNI" placeholder="Server Name Indication"> | ||||||
|       <a-input v-model.trim="inbound.stream.tls.sni"></a-input> |       <a-input v-model.trim="inbound.stream.tls.sni"></a-input> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|  | @ -116,12 +116,15 @@ | ||||||
|         </a-select> |         </a-select> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|     <a-form-item label=" "> |     <a-form-item label=" "> | ||||||
|  |       <a-space> | ||||||
|         <a-button type="primary" icon="import" @click="getNewEchCert">Get New ECH Cert</a-button> |         <a-button type="primary" icon="import" @click="getNewEchCert">Get New ECH Cert</a-button> | ||||||
|  |         <a-button danger @click="clearEchCert">Clear</a-button> | ||||||
|  |       </a-space> | ||||||
|     </a-form-item> |     </a-form-item> | ||||||
|   </template> |   </template> | ||||||
| 
 | 
 | ||||||
|   <!-- reality settings --> |   <!-- reality settings --> | ||||||
|   <template v-if="inbound.stream.isReality && !inbound.settings.encryption"> |   <template v-if="inbound.stream.isReality"> | ||||||
|     {{template "form/realitySettings"}} |     {{template "form/realitySettings"}} | ||||||
|   </template> |   </template> | ||||||
| </a-form> | </a-form> | ||||||
|  |  | ||||||
|  | @ -836,7 +836,7 @@ | ||||||
|             }, |             }, | ||||||
|             async getDBInbounds() { |             async getDBInbounds() { | ||||||
|                 this.refreshing = true; |                 this.refreshing = true; | ||||||
|                 const msg = await HttpUtil.post('/panel/inbound/list'); |                 const msg = await HttpUtil.get('/panel/api/inbounds/list'); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     this.refreshing = false; |                     this.refreshing = false; | ||||||
|                     return; |                     return; | ||||||
|  | @ -851,7 +851,7 @@ | ||||||
|                 }, 500); |                 }, 500); | ||||||
|             }, |             }, | ||||||
|             async getOnlineUsers() { |             async getOnlineUsers() { | ||||||
|                 const msg = await HttpUtil.post('/panel/inbound/onlines'); |                 const msg = await HttpUtil.post('/panel/api/inbounds/onlines'); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  | @ -1106,7 +1106,7 @@ | ||||||
|                     streamSettings: baseInbound.stream.toString(), |                     streamSettings: baseInbound.stream.toString(), | ||||||
|                     sniffing: baseInbound.sniffing.toString(), |                     sniffing: baseInbound.sniffing.toString(), | ||||||
|                 }; |                 }; | ||||||
|                 await this.submit('/panel/inbound/add', data, inModal); |                 await this.submit('/panel/api/inbounds/add', data, inModal); | ||||||
|             }, |             }, | ||||||
|             openAddInbound() { |             openAddInbound() { | ||||||
|                 inModal.show({ |                 inModal.show({ | ||||||
|  | @ -1156,7 +1156,7 @@ | ||||||
|                 } |                 } | ||||||
|                 data.sniffing = inbound.sniffing.toString(); |                 data.sniffing = inbound.sniffing.toString(); | ||||||
| 
 | 
 | ||||||
|                 await this.submit('/panel/inbound/add', data, inModal); |                 await this.submit('/panel/api/inbounds/add', data, inModal); | ||||||
|             }, |             }, | ||||||
|             async updateInbound(inbound, dbInbound) { |             async updateInbound(inbound, dbInbound) { | ||||||
|                 const data = { |                 const data = { | ||||||
|  | @ -1180,7 +1180,7 @@ | ||||||
|                 } |                 } | ||||||
|                 data.sniffing = inbound.sniffing.toString(); |                 data.sniffing = inbound.sniffing.toString(); | ||||||
| 
 | 
 | ||||||
|                 await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal); |                 await this.submit(`/panel/api/inbounds/update/${dbInbound.id}`, data, inModal); | ||||||
|             }, |             }, | ||||||
|             openAddClient(dbInboundId) { |             openAddClient(dbInboundId) { | ||||||
|                 dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); |                 dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); | ||||||
|  | @ -1235,14 +1235,14 @@ | ||||||
|                     id: dbInboundId, |                     id: dbInboundId, | ||||||
|                     settings: '{"clients": [' + clients.toString() + ']}', |                     settings: '{"clients": [' + clients.toString() + ']}', | ||||||
|                 }; |                 }; | ||||||
|                 await this.submit(`/panel/inbound/addClient`, data, modal); |                 await this.submit(`/panel/api/inbounds/addClient`, data, modal); | ||||||
|             }, |             }, | ||||||
|             async updateClient(client, dbInboundId, clientId) { |             async updateClient(client, dbInboundId, clientId) { | ||||||
|                 const data = { |                 const data = { | ||||||
|                     id: dbInboundId, |                     id: dbInboundId, | ||||||
|                     settings: '{"clients": [' + client.toString() + ']}', |                     settings: '{"clients": [' + client.toString() + ']}', | ||||||
|                 }; |                 }; | ||||||
|                 await this.submit(`/panel/inbound/updateClient/${clientId}`, data, clientModal); |                 await this.submit(`/panel/api/inbounds/updateClient/${clientId}`, data, clientModal); | ||||||
|             }, |             }, | ||||||
|             resetTraffic(dbInboundId) { |             resetTraffic(dbInboundId) { | ||||||
|                 dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); |                 dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); | ||||||
|  | @ -1267,7 +1267,7 @@ | ||||||
|                     class: themeSwitcher.currentTheme, |                     class: themeSwitcher.currentTheme, | ||||||
|                     okText: '{{ i18n "delete"}}', |                     okText: '{{ i18n "delete"}}', | ||||||
|                     cancelText: '{{ i18n "cancel"}}', |                     cancelText: '{{ i18n "cancel"}}', | ||||||
|                     onOk: () => this.submit('/panel/inbound/del/' + dbInboundId), |                     onOk: () => this.submit('/panel/api/inbounds/del/' + dbInboundId), | ||||||
|                 }); |                 }); | ||||||
|             }, |             }, | ||||||
|             delClient(dbInboundId, client,confirmation = true) { |             delClient(dbInboundId, client,confirmation = true) { | ||||||
|  | @ -1280,10 +1280,10 @@ | ||||||
|                         class: themeSwitcher.currentTheme, |                         class: themeSwitcher.currentTheme, | ||||||
|                         okText: '{{ i18n "delete"}}', |                         okText: '{{ i18n "delete"}}', | ||||||
|                         cancelText: '{{ i18n "cancel"}}', |                         cancelText: '{{ i18n "cancel"}}', | ||||||
|                         onOk: () => this.submit(`/panel/inbound/${dbInboundId}/delClient/${clientId}`), |                         onOk: () => this.submit(`/panel/api/inbounds/${dbInboundId}/delClient/${clientId}`), | ||||||
|                     }); |                     }); | ||||||
|                 } else { |                 } else { | ||||||
|                     this.submit(`/panel/inbound/${dbInboundId}/delClient/${clientId}`); |                     this.submit(`/panel/api/inbounds/${dbInboundId}/delClient/${clientId}`); | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             getSubGroupClients(dbInbounds, currentClient) { |             getSubGroupClients(dbInbounds, currentClient) { | ||||||
|  | @ -1362,7 +1362,7 @@ | ||||||
|             switchEnable(dbInboundId,state) { |             switchEnable(dbInboundId,state) { | ||||||
|               dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); |               dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); | ||||||
|               dbInbound.enable = state; |               dbInbound.enable = state; | ||||||
|               this.submit(`/panel/inbound/update/${dbInboundId}`, dbInbound); |               this.submit(`/panel/api/inbounds/update/${dbInboundId}`, dbInbound); | ||||||
|             }, |             }, | ||||||
|             async switchEnableClient(dbInboundId, client) { |             async switchEnableClient(dbInboundId, client) { | ||||||
|                 this.loading() |                 this.loading() | ||||||
|  | @ -1392,10 +1392,10 @@ | ||||||
|                         class: themeSwitcher.currentTheme, |                         class: themeSwitcher.currentTheme, | ||||||
|                         okText: '{{ i18n "reset"}}', |                         okText: '{{ i18n "reset"}}', | ||||||
|                         cancelText: '{{ i18n "cancel"}}', |                         cancelText: '{{ i18n "cancel"}}', | ||||||
|                         onOk: () => this.submit('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email), |                         onOk: () => this.submit('/panel/api/inbounds/' + dbInboundId + '/resetClientTraffic/' + client.email), | ||||||
|                     }) |                     }) | ||||||
|                 } else { |                 } else { | ||||||
|                     this.submit('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email); |                     this.submit('/panel/api/inbounds/' + dbInboundId + '/resetClientTraffic/' + client.email); | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             resetAllTraffic() { |             resetAllTraffic() { | ||||||
|  | @ -1405,7 +1405,7 @@ | ||||||
|                     class: themeSwitcher.currentTheme, |                     class: themeSwitcher.currentTheme, | ||||||
|                     okText: '{{ i18n "reset"}}', |                     okText: '{{ i18n "reset"}}', | ||||||
|                     cancelText: '{{ i18n "cancel"}}', |                     cancelText: '{{ i18n "cancel"}}', | ||||||
|                     onOk: () => this.submit('/panel/inbound/resetAllTraffics'), |                     onOk: () => this.submit('/panel/api/inbounds/resetAllTraffics'), | ||||||
|                 }); |                 }); | ||||||
|             }, |             }, | ||||||
|             resetAllClientTraffics(dbInboundId) { |             resetAllClientTraffics(dbInboundId) { | ||||||
|  | @ -1415,7 +1415,7 @@ | ||||||
|                     class: themeSwitcher.currentTheme, |                     class: themeSwitcher.currentTheme, | ||||||
|                     okText: '{{ i18n "reset"}}', |                     okText: '{{ i18n "reset"}}', | ||||||
|                     cancelText: '{{ i18n "cancel"}}', |                     cancelText: '{{ i18n "cancel"}}', | ||||||
|                     onOk: () => this.submit('/panel/inbound/resetAllClientTraffics/' + dbInboundId), |                     onOk: () => this.submit('/panel/api/inbounds/resetAllClientTraffics/' + dbInboundId), | ||||||
|                 }) |                 }) | ||||||
|             }, |             }, | ||||||
|             delDepletedClients(dbInboundId) { |             delDepletedClients(dbInboundId) { | ||||||
|  | @ -1425,7 +1425,7 @@ | ||||||
|                     class: themeSwitcher.currentTheme, |                     class: themeSwitcher.currentTheme, | ||||||
|                     okText: '{{ i18n "delete"}}', |                     okText: '{{ i18n "delete"}}', | ||||||
|                     cancelText: '{{ i18n "cancel"}}', |                     cancelText: '{{ i18n "cancel"}}', | ||||||
|                     onOk: () => this.submit('/panel/inbound/delDepletedClients/' + dbInboundId), |                     onOk: () => this.submit('/panel/api/inbounds/delDepletedClients/' + dbInboundId), | ||||||
|                 }) |                 }) | ||||||
|             }, |             }, | ||||||
|             isExpiry(dbInbound, index) { |             isExpiry(dbInbound, index) { | ||||||
|  | @ -1551,7 +1551,7 @@ | ||||||
|                     value: '', |                     value: '', | ||||||
|                     okText: '{{ i18n "pages.inbounds.import" }}', |                     okText: '{{ i18n "pages.inbounds.import" }}', | ||||||
|                     confirm: async (dbInboundText) => { |                     confirm: async (dbInboundText) => { | ||||||
|                         await this.submit('/panel/inbound/import', {data: dbInboundText}, promptModal); |                         await this.submit('/panel/api/inbounds/import', {data: dbInboundText}, promptModal); | ||||||
|                     }, |                     }, | ||||||
|                 }); |                 }); | ||||||
|             }, |             }, | ||||||
|  |  | ||||||
|  | @ -746,7 +746,7 @@ ${dateTime} | ||||||
|             }, |             }, | ||||||
|             async getStatus() { |             async getStatus() { | ||||||
|                 try { |                 try { | ||||||
|                     const msg = await HttpUtil.post('/server/status'); |                     const msg = await HttpUtil.get('/panel/api/server/status'); | ||||||
|                     if (msg.success) { |                     if (msg.success) { | ||||||
|                         if (!this.loadingStates.fetched) { |                         if (!this.loadingStates.fetched) { | ||||||
|                             this.loadingStates.fetched = true; |                             this.loadingStates.fetched = true; | ||||||
|  | @ -763,7 +763,7 @@ ${dateTime} | ||||||
|             }, |             }, | ||||||
|             async openSelectV2rayVersion() { |             async openSelectV2rayVersion() { | ||||||
|                 this.loading(true); |                 this.loading(true); | ||||||
|                 const msg = await HttpUtil.post('server/getXrayVersion'); |                 const msg = await HttpUtil.get('/panel/api/server/getXrayVersion'); | ||||||
|                 this.loading(false); |                 this.loading(false); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     return; |                     return; | ||||||
|  | @ -780,7 +780,7 @@ ${dateTime} | ||||||
|                     onOk: async () => { |                     onOk: async () => { | ||||||
|                         versionModal.hide(); |                         versionModal.hide(); | ||||||
|                         this.loading(true, '{{ i18n "pages.index.dontRefresh"}}'); |                         this.loading(true, '{{ i18n "pages.index.dontRefresh"}}'); | ||||||
|                         await HttpUtil.post(`/server/installXray/${version}`); |                         await HttpUtil.post(`/panel/api/server/installXray/${version}`); | ||||||
|                         this.loading(false); |                         this.loading(false); | ||||||
|                     }, |                     }, | ||||||
|                 }); |                 }); | ||||||
|  | @ -799,8 +799,8 @@ ${dateTime} | ||||||
|                         versionModal.hide(); |                         versionModal.hide(); | ||||||
|                         this.loading(true, '{{ i18n "pages.index.dontRefresh"}}'); |                         this.loading(true, '{{ i18n "pages.index.dontRefresh"}}'); | ||||||
|                         const url = isSingleFile |                         const url = isSingleFile | ||||||
|                             ? `/server/updateGeofile/${fileName}`  |                             ? `/panel/api/server/updateGeofile/${fileName}` | ||||||
|                             : `/server/updateGeofile`; |                             : `/panel/api/server/updateGeofile`; | ||||||
|                         await HttpUtil.post(url); |                         await HttpUtil.post(url); | ||||||
|                         this.loading(false); |                         this.loading(false); | ||||||
|                     }, |                     }, | ||||||
|  | @ -808,7 +808,7 @@ ${dateTime} | ||||||
|             }, |             }, | ||||||
|             async stopXrayService() { |             async stopXrayService() { | ||||||
|                 this.loading(true); |                 this.loading(true); | ||||||
|                 const msg = await HttpUtil.post('server/stopXrayService'); |                 const msg = await HttpUtil.post('/panel/api/server/stopXrayService'); | ||||||
|                 this.loading(false); |                 this.loading(false); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     return; |                     return; | ||||||
|  | @ -816,7 +816,7 @@ ${dateTime} | ||||||
|             }, |             }, | ||||||
|             async restartXrayService() { |             async restartXrayService() { | ||||||
|                 this.loading(true); |                 this.loading(true); | ||||||
|                 const msg = await HttpUtil.post('server/restartXrayService'); |                 const msg = await HttpUtil.post('/panel/api/server/restartXrayService'); | ||||||
|                 this.loading(false); |                 this.loading(false); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     return; |                     return; | ||||||
|  | @ -824,7 +824,7 @@ ${dateTime} | ||||||
|             }, |             }, | ||||||
|             async openLogs(){ |             async openLogs(){ | ||||||
|                 logModal.loading = true; |                 logModal.loading = true; | ||||||
|                 const msg = await HttpUtil.post('server/logs/'+logModal.rows,{level: logModal.level, syslog: logModal.syslog}); |                 const msg = await HttpUtil.post('/panel/api/server/logs/'+logModal.rows,{level: logModal.level, syslog: logModal.syslog}); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  | @ -834,7 +834,7 @@ ${dateTime} | ||||||
|             }, |             }, | ||||||
|             async openXrayLogs(){ |             async openXrayLogs(){ | ||||||
|               xraylogModal.loading = true; |               xraylogModal.loading = true; | ||||||
|                 const msg = await HttpUtil.post('server/xraylogs/'+xraylogModal.rows,{filter: xraylogModal.filter, showDirect: xraylogModal.showDirect, showBlocked: xraylogModal.showBlocked, showProxy: xraylogModal.showProxy}); |                 const msg = await HttpUtil.post('/panel/api/server/xraylogs/'+xraylogModal.rows,{filter: xraylogModal.filter, showDirect: xraylogModal.showDirect, showBlocked: xraylogModal.showBlocked, showProxy: xraylogModal.showProxy}); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  | @ -844,7 +844,7 @@ ${dateTime} | ||||||
|             }, |             }, | ||||||
|             async openConfig() { |             async openConfig() { | ||||||
|                 this.loading(true); |                 this.loading(true); | ||||||
|                 const msg = await HttpUtil.post('server/getConfigJson'); |                 const msg = await HttpUtil.get('/panel/api/server/getConfigJson'); | ||||||
|                 this.loading(false); |                 this.loading(false); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     return; |                     return; | ||||||
|  | @ -855,7 +855,7 @@ ${dateTime} | ||||||
|               backupModal.show(); |               backupModal.show(); | ||||||
|             }, |             }, | ||||||
|             exportDatabase() { |             exportDatabase() { | ||||||
|                 window.location = basePath + 'server/getDb'; |                 window.location = basePath + 'panel/api/server/getDb'; | ||||||
|             }, |             }, | ||||||
|             importDatabase() { |             importDatabase() { | ||||||
|                 const fileInput = document.createElement('input'); |                 const fileInput = document.createElement('input'); | ||||||
|  | @ -868,7 +868,7 @@ ${dateTime} | ||||||
|                         formData.append('db', dbFile); |                         formData.append('db', dbFile); | ||||||
|                         backupModal.hide(); |                         backupModal.hide(); | ||||||
|                         this.loading(true); |                         this.loading(true); | ||||||
|                         const uploadMsg = await HttpUtil.post('server/importDB', formData, { |                         const uploadMsg = await HttpUtil.post('/panel/api/server/importDB', formData, { | ||||||
|                             headers: { |                             headers: { | ||||||
|                                 'Content-Type': 'multipart/form-data', |                                 'Content-Type': 'multipart/form-data', | ||||||
|                             } |                             } | ||||||
|  |  | ||||||
|  | @ -121,7 +121,7 @@ | ||||||
|         }, |         }, | ||||||
|         methods: { |         methods: { | ||||||
|             async getDBClientIps(email) { |             async getDBClientIps(email) { | ||||||
|                 const msg = await HttpUtil.post(`/panel/inbound/clientIps/${email}`); |                 const msg = await HttpUtil.post(`/panel/api/inbounds/clientIps/${email}`); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     document.getElementById("clientIPs").value = msg.obj; |                     document.getElementById("clientIPs").value = msg.obj; | ||||||
|                     return; |                     return; | ||||||
|  | @ -139,7 +139,7 @@ | ||||||
|             }, |             }, | ||||||
|             async clearDBClientIps(email) { |             async clearDBClientIps(email) { | ||||||
|                 try { |                 try { | ||||||
|                     const msg = await HttpUtil.post(`/panel/inbound/clearClientIps/${email}`); |                     const msg = await HttpUtil.post(`/panel/api/inbounds/clearClientIps/${email}`); | ||||||
|                     if (!msg.success) { |                     if (!msg.success) { | ||||||
|                         return; |                         return; | ||||||
|                     } |                     } | ||||||
|  | @ -156,7 +156,7 @@ | ||||||
|                     cancelText: '{{ i18n "cancel"}}', |                     cancelText: '{{ i18n "cancel"}}', | ||||||
|                     onOk: async () => { |                     onOk: async () => { | ||||||
|                         iconElement.disabled = true; |                         iconElement.disabled = true; | ||||||
|                         const msg = await HttpUtil.postWithModal('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + email); |                         const msg = await HttpUtil.postWithModal('/panel/api/inbounds/' + dbInboundId + '/resetClientTraffic/' + email); | ||||||
|                         if (msg.success) { |                         if (msg.success) { | ||||||
|                             this.clientModal.clientStats.up = 0; |                             this.clientModal.clientStats.up = 0; | ||||||
|                             this.clientModal.clientStats.down = 0; |                             this.clientModal.clientStats.down = 0; | ||||||
|  |  | ||||||
|  | @ -360,7 +360,7 @@ | ||||||
|           <code>[[ link.link ]]</code> |           <code>[[ link.link ]]</code> | ||||||
|         </tr-info-row> |         </tr-info-row> | ||||||
|       </template> |       </template> | ||||||
|       <table v-if="inbound.protocol == Protocols.DOKODEMO" class="tr-info-table"> |       <table v-if="inbound.protocol == Protocols.TUNNEL" class="tr-info-table"> | ||||||
|         <tr> |         <tr> | ||||||
|           <th>{{ i18n "pages.inbounds.targetAddress" }}</th> |           <th>{{ i18n "pages.inbounds.targetAddress" }}</th> | ||||||
|           <th>{{ i18n "pages.inbounds.destinationPort" }}</th> |           <th>{{ i18n "pages.inbounds.destinationPort" }}</th> | ||||||
|  | @ -382,7 +382,7 @@ | ||||||
|           </td> |           </td> | ||||||
|         </tr> |         </tr> | ||||||
|       </table> |       </table> | ||||||
|       <table v-if="dbInbound.isSocks" class="tr-info-table"> |       <table v-if="dbInbound.isMixed" class="tr-info-table"> | ||||||
|         <tr> |         <tr> | ||||||
|           <th>{{ i18n "password" }} Auth</th> |           <th>{{ i18n "password" }} Auth</th> | ||||||
|           <th>{{ i18n "pages.inbounds.enable" }} udp</th> |           <th>{{ i18n "pages.inbounds.enable" }} udp</th> | ||||||
|  | @ -498,7 +498,7 @@ | ||||||
| </a-modal> | </a-modal> | ||||||
| <script> | <script> | ||||||
|   function refreshIPs(email) { |   function refreshIPs(email) { | ||||||
|     return HttpUtil.post(`/panel/inbound/clientIps/${email}`).then((msg) => { |     return HttpUtil.post(`/panel/api/inbounds/clientIps/${email}`).then((msg) => { | ||||||
|       if (msg.success) { |       if (msg.success) { | ||||||
|         try { |         try { | ||||||
|           return JSON.parse(msg.obj).join(', '); |           return JSON.parse(msg.obj).join(', '); | ||||||
|  | @ -619,7 +619,7 @@ | ||||||
|           }); |           }); | ||||||
|       }, |       }, | ||||||
|       clearClientIps() { |       clearClientIps() { | ||||||
|         HttpUtil.post(`/panel/inbound/clearClientIps/${this.infoModal.clientStats.email}`) |         HttpUtil.post(`/panel/api/inbounds/clearClientIps/${this.infoModal.clientStats.email}`) | ||||||
|           .then((msg) => { |           .then((msg) => { | ||||||
|             if (!msg.success) { |             if (!msg.success) { | ||||||
|               return; |               return; | ||||||
|  |  | ||||||
|  | @ -132,7 +132,7 @@ | ||||||
|             }, |             }, | ||||||
|             async getNewX25519Cert() { |             async getNewX25519Cert() { | ||||||
|                 inModal.loading(true); |                 inModal.loading(true); | ||||||
|                 const msg = await HttpUtil.post('/server/getNewX25519Cert'); |                 const msg = await HttpUtil.get('/panel/api/server/getNewX25519Cert'); | ||||||
|                 inModal.loading(false); |                 inModal.loading(false); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     return; |                     return; | ||||||
|  | @ -140,9 +140,13 @@ | ||||||
|                 inModal.inbound.stream.reality.privateKey = msg.obj.privateKey; |                 inModal.inbound.stream.reality.privateKey = msg.obj.privateKey; | ||||||
|                 inModal.inbound.stream.reality.settings.publicKey = msg.obj.publicKey; |                 inModal.inbound.stream.reality.settings.publicKey = msg.obj.publicKey; | ||||||
|             }, |             }, | ||||||
|  |             clearX25519Cert() { | ||||||
|  |                 this.inbound.stream.reality.privateKey = ''; | ||||||
|  |                 this.inbound.stream.reality.settings.publicKey = ''; | ||||||
|  |             }, | ||||||
|             async getNewmldsa65() { |             async getNewmldsa65() { | ||||||
|                 inModal.loading(true); |                 inModal.loading(true); | ||||||
|                 const msg = await HttpUtil.post('/server/getNewmldsa65'); |                 const msg = await HttpUtil.get('/panel/api/server/getNewmldsa65'); | ||||||
|                 inModal.loading(false); |                 inModal.loading(false); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     return; |                     return; | ||||||
|  | @ -150,9 +154,13 @@ | ||||||
|                 inModal.inbound.stream.reality.mldsa65Seed = msg.obj.seed; |                 inModal.inbound.stream.reality.mldsa65Seed = msg.obj.seed; | ||||||
|                 inModal.inbound.stream.reality.settings.mldsa65Verify = msg.obj.verify; |                 inModal.inbound.stream.reality.settings.mldsa65Verify = msg.obj.verify; | ||||||
|             }, |             }, | ||||||
|  |             clearMldsa65() { | ||||||
|  |                 this.inbound.stream.reality.mldsa65Seed = ''; | ||||||
|  |                 this.inbound.stream.reality.settings.mldsa65Verify = ''; | ||||||
|  |             }, | ||||||
|             async getNewEchCert() { |             async getNewEchCert() { | ||||||
|                 inModal.loading(true); |                 inModal.loading(true); | ||||||
|                 const msg = await HttpUtil.post('/server/getNewEchCert', { sni: inModal.inbound.stream.tls.sni }); |                 const msg = await HttpUtil.post('/panel/api/server/getNewEchCert', { sni: inModal.inbound.stream.tls.sni }); | ||||||
|                 inModal.loading(false); |                 inModal.loading(false); | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|                     return; |                     return; | ||||||
|  | @ -160,9 +168,13 @@ | ||||||
|                 inModal.inbound.stream.tls.echServerKeys = msg.obj.echServerKeys; |                 inModal.inbound.stream.tls.echServerKeys = msg.obj.echServerKeys; | ||||||
|                 inModal.inbound.stream.tls.settings.echConfigList = msg.obj.echConfigList; |                 inModal.inbound.stream.tls.settings.echConfigList = msg.obj.echConfigList; | ||||||
|             }, |             }, | ||||||
|  |             clearEchCert() { | ||||||
|  |                 this.inbound.stream.tls.echServerKeys = ''; | ||||||
|  |                 this.inbound.stream.tls.settings.echConfigList = ''; | ||||||
|  |             }, | ||||||
|             async getNewVlessEnc() { |             async getNewVlessEnc() { | ||||||
|                 inModal.loading(true); |                 inModal.loading(true); | ||||||
|                 const msg = await HttpUtil.post('/server/getNewVlessEnc'); |                 const msg = await HttpUtil.get('/panel/api/server/getNewVlessEnc'); | ||||||
|                 inModal.loading(false); |                 inModal.loading(false); | ||||||
| 
 | 
 | ||||||
|                 if (!msg.success) { |                 if (!msg.success) { | ||||||
|  | @ -181,7 +193,7 @@ | ||||||
|                 inModal.inbound.settings.decryption = block.decryption; |                 inModal.inbound.settings.decryption = block.decryption; | ||||||
|                 inModal.inbound.settings.encryption = block.encryption; |                 inModal.inbound.settings.encryption = block.encryption; | ||||||
|             }, |             }, | ||||||
|             clearKeys() { |             clearVlessEnc() { | ||||||
|                 this.inbound.settings.decryption = 'none'; |                 this.inbound.settings.decryption = 'none'; | ||||||
|                 this.inbound.settings.encryption = 'none'; |                 this.inbound.settings.encryption = 'none'; | ||||||
|                 this.inbound.settings.selectedAuth = undefined; |                 this.inbound.settings.selectedAuth = undefined; | ||||||
|  |  | ||||||
|  | @ -151,7 +151,7 @@ | ||||||
|     methods: { |     methods: { | ||||||
|       async getStatus() { |       async getStatus() { | ||||||
|         try { |         try { | ||||||
|           const msg = await HttpUtil.post('/server/status'); |           const msg = await HttpUtil.get('/panel/api/server/status'); | ||||||
|           if (msg.success) { |           if (msg.success) { | ||||||
|             this.serverStatus = msg.obj; |             this.serverStatus = msg.obj; | ||||||
|           } |           } | ||||||
|  |  | ||||||
|  | @ -420,7 +420,7 @@ | ||||||
|       }, |       }, | ||||||
|       async restartXray() { |       async restartXray() { | ||||||
|         this.loading(true); |         this.loading(true); | ||||||
|         const msg = await HttpUtil.post("server/restartXrayService"); |         const msg = await HttpUtil.post("/panel/api/server/restartXrayService"); | ||||||
|         this.loading(false); |         this.loading(false); | ||||||
|         if (msg.success) { |         if (msg.success) { | ||||||
|           await PromiseUtil.sleep(500); |           await PromiseUtil.sleep(500); | ||||||
|  | @ -572,7 +572,7 @@ | ||||||
|             serverObj = o.settings.vnext; |             serverObj = o.settings.vnext; | ||||||
|             break; |             break; | ||||||
|           case Protocols.HTTP: |           case Protocols.HTTP: | ||||||
|           case Protocols.Socks: |           case Protocols.Mixed: | ||||||
|           case Protocols.Shadowsocks: |           case Protocols.Shadowsocks: | ||||||
|           case Protocols.Trojan: |           case Protocols.Trojan: | ||||||
|             serverObj = o.settings.servers; |             serverObj = o.settings.servers; | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ | ||||||
|       "tag": "api", |       "tag": "api", | ||||||
|       "listen": "127.0.0.1", |       "listen": "127.0.0.1", | ||||||
|       "port": 62789, |       "port": 62789, | ||||||
|       "protocol": "dokodemo-door", |       "protocol": "tunnel", | ||||||
|       "settings": { |       "settings": { | ||||||
|         "address": "127.0.0.1" |         "address": "127.0.0.1" | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ import ( | ||||||
| 	"x-ui/util/sys" | 	"x-ui/util/sys" | ||||||
| 	"x-ui/xray" | 	"x-ui/xray" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/google/uuid" | ||||||
| 	"github.com/shirou/gopsutil/v4/cpu" | 	"github.com/shirou/gopsutil/v4/cpu" | ||||||
| 	"github.com/shirou/gopsutil/v4/disk" | 	"github.com/shirou/gopsutil/v4/disk" | ||||||
| 	"github.com/shirou/gopsutil/v4/host" | 	"github.com/shirou/gopsutil/v4/host" | ||||||
|  | @ -872,12 +873,6 @@ func (s *ServerService) GetNewEchCert(sni string) (interface{}, error) { | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type AuthBlock struct { |  | ||||||
| 	Label      string `json:"label"` |  | ||||||
| 	Decryption string `json:"decryption"` |  | ||||||
| 	Encryption string `json:"encryption"` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (s *ServerService) GetNewVlessEnc() (any, error) { | func (s *ServerService) GetNewVlessEnc() (any, error) { | ||||||
| 	cmd := exec.Command(xray.GetBinaryPath(), "vlessenc") | 	cmd := exec.Command(xray.GetBinaryPath(), "vlessenc") | ||||||
| 	var out bytes.Buffer | 	var out bytes.Buffer | ||||||
|  | @ -887,37 +882,70 @@ func (s *ServerService) GetNewVlessEnc() (any, error) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	lines := strings.Split(out.String(), "\n") | 	lines := strings.Split(out.String(), "\n") | ||||||
| 
 | 	var auths []map[string]string | ||||||
| 	var blocks []AuthBlock | 	var current map[string]string | ||||||
| 	var current *AuthBlock |  | ||||||
| 
 | 
 | ||||||
| 	for _, line := range lines { | 	for _, line := range lines { | ||||||
| 		line = strings.TrimSpace(line) | 		line = strings.TrimSpace(line) | ||||||
| 		if strings.HasPrefix(line, "Authentication:") { | 		if strings.HasPrefix(line, "Authentication:") { | ||||||
| 			if current != nil { | 			if current != nil { | ||||||
| 				blocks = append(blocks, *current) | 				auths = append(auths, current) | ||||||
|  | 			} | ||||||
|  | 			current = map[string]string{ | ||||||
|  | 				"label": strings.TrimSpace(strings.TrimPrefix(line, "Authentication:")), | ||||||
| 			} | 			} | ||||||
| 			current = &AuthBlock{Label: strings.TrimSpace(strings.TrimPrefix(line, "Authentication:"))} |  | ||||||
| 		} else if strings.HasPrefix(line, `"decryption"`) || strings.HasPrefix(line, `"encryption"`) { | 		} else if strings.HasPrefix(line, `"decryption"`) || strings.HasPrefix(line, `"encryption"`) { | ||||||
| 			parts := strings.SplitN(line, ":", 2) | 			parts := strings.SplitN(line, ":", 2) | ||||||
| 			if len(parts) == 2 && current != nil { | 			if len(parts) == 2 && current != nil { | ||||||
| 				key := strings.Trim(parts[0], `" `) | 				key := strings.Trim(parts[0], `" `) | ||||||
| 				val := strings.Trim(parts[1], `" `) | 				val := strings.Trim(parts[1], `" `) | ||||||
| 				switch key { | 				current[key] = val | ||||||
| 				case "decryption": |  | ||||||
| 					current.Decryption = val |  | ||||||
| 				case "encryption": |  | ||||||
| 					current.Encryption = val |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if current != nil { | 	if current != nil { | ||||||
| 		blocks = append(blocks, *current) | 		auths = append(auths, current) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return map[string]any{ | 	return map[string]any{ | ||||||
| 		"auths": blocks, | 		"auths": auths, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (s *ServerService) GetNewUUID() (map[string]string, error) { | ||||||
|  | 	newUUID, err := uuid.NewRandom() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("failed to generate UUID: %w", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return map[string]string{ | ||||||
|  | 		"uuid": newUUID.String(), | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (s *ServerService) GetNewmlkem768() (any, error) { | ||||||
|  | 	// Run the command
 | ||||||
|  | 	cmd := exec.Command(xray.GetBinaryPath(), "mlkem768") | ||||||
|  | 	var out bytes.Buffer | ||||||
|  | 	cmd.Stdout = &out | ||||||
|  | 	err := cmd.Run() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	lines := strings.Split(out.String(), "\n") | ||||||
|  | 
 | ||||||
|  | 	SeedLine := strings.Split(lines[0], ":") | ||||||
|  | 	ClientLine := strings.Split(lines[1], ":") | ||||||
|  | 
 | ||||||
|  | 	seed := strings.TrimSpace(SeedLine[1]) | ||||||
|  | 	client := strings.TrimSpace(ClientLine[1]) | ||||||
|  | 
 | ||||||
|  | 	keyPair := map[string]any{ | ||||||
|  | 		"seed":   seed, | ||||||
|  | 		"client": client, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return keyPair, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2129,8 +2129,8 @@ func (t *Tgbot) getInboundsAddClient() (*telego.InlineKeyboardMarkup, error) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	excludedProtocols := map[model.Protocol]bool{ | 	excludedProtocols := map[model.Protocol]bool{ | ||||||
| 		model.DOKODEMO:  true, | 		model.Tunnel:    true, | ||||||
| 		model.Socks:     true, | 		model.Mixed:     true, | ||||||
| 		model.WireGuard: true, | 		model.WireGuard: true, | ||||||
| 		model.HTTP:      true, | 		model.HTTP:      true, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -176,7 +176,7 @@ func (s *Server) initRouter() (*gin.Engine, error) { | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	engine.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{basePath + "panel/API/"}))) | 	engine.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{basePath + "panel/api/"}))) | ||||||
| 	assetsBasePath := basePath + "assets/" | 	assetsBasePath := basePath + "assets/" | ||||||
| 
 | 
 | ||||||
| 	store := cookie.NewStore(secret) | 	store := cookie.NewStore(secret) | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								windows_files/SSL/Win64OpenSSL_Light-3_5_2.exe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								windows_files/SSL/Win64OpenSSL_Light-3_5_2.exe
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										13
									
								
								windows_files/readme.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								windows_files/readme.txt
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | ||||||
|  | you can't install fail2ban on windows | ||||||
|  | we don't have bash menu for windows | ||||||
|  | if you forgot your password you need to check your database with https://sqlitebrowser.org/ | ||||||
|  | the app need to be open all the time | ||||||
|  | 
 | ||||||
|  | default setting: | ||||||
|  | http://localhost:2053/ | ||||||
|  | user: admin | ||||||
|  | pass: admin | ||||||
|  | port: 2053 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout localhost.key -out localhost.crt | ||||||
		Loading…
	
		Reference in a new issue
	
	 Sanaei
						Sanaei