Compare commits

...

12 Commits

Author SHA1 Message Date
mhsanaei
b1ea8005e4 v2.7.0 2025-09-10 08:43:46 +02:00
mhsanaei
3f0bfa2472 Remove the buggy version of Xray core 2025-09-10 08:43:10 +02:00
mhsanaei
1e2ff650ad Xray Core v25.9.10 + GO v1.25.1 2025-09-10 08:40:08 +02:00
Sanaei
c2d6dd923f windows workflow (#3439) 2025-09-09 18:41:44 +02:00
mhsanaei
723ec25fb2 renamed dest to target 2025-09-09 14:35:21 +02:00
mhsanaei
7dc52e9a53 dokodemo-door, socks renamed to mixed, tunnel 2025-09-09 13:57:40 +02:00
Sanaei
fe9f0d1d0e api (#3434) 2025-09-09 02:32:05 +02:00
mhsanaei
18d74d54ca outbound: ECH Config List 2025-09-08 21:25:30 +02:00
mhsanaei
c7ba6ae909 add clear button 2025-09-08 21:17:48 +02:00
mhsanaei
3edf79e589 actions/setup-go@v6 2025-09-08 14:33:04 +02:00
mhsanaei
5420e643cf minor change 2025-09-08 14:32:49 +02:00
mhsanaei
9fcd0387ca Update release.yml 2025-09-08 01:12:27 +02:00
34 changed files with 361 additions and 209 deletions

View File

@@ -10,7 +10,6 @@ on:
tags: tags:
- "v*.*.*" - "v*.*.*"
paths: paths:
- '.github/workflows/release.yml'
- '**.js' - '**.js'
- '**.css' - '**.css'
- '**.html' - '**.html'
@@ -40,7 +39,7 @@ jobs:
uses: actions/checkout@v5 uses: actions/checkout@v5
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v6
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
@@ -86,7 +85,7 @@ jobs:
cd x-ui/bin cd x-ui/bin
# Download dependencies # Download dependencies
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.9.5/" Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.9.10/"
if [ "${{ matrix.platform }}" == "amd64" ]; then if [ "${{ matrix.platform }}" == "amd64" ]; then
wget -q ${Xray_URL}Xray-linux-64.zip wget -q ${Xray_URL}Xray-linux-64.zip
unzip Xray-linux-64.zip unzip Xray-linux-64.zip
@@ -137,10 +136,89 @@ jobs:
- name: Upload files to GH release - name: Upload files to GH release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
if: github.event_name == 'release' && github.event.action == 'published' if: |
(github.event_name == 'release' && github.event.action == 'published') ||
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
with: with:
repo_token: ${{ secrets.GITHUB_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }} tag: ${{ github.ref }}
file: x-ui-linux-${{ matrix.platform }}.tar.gz file: x-ui-linux-${{ matrix.platform }}.tar.gz
asset_name: x-ui-linux-${{ matrix.platform }}.tar.gz asset_name: x-ui-linux-${{ matrix.platform }}.tar.gz
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.9.10/"
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

View File

@@ -27,7 +27,7 @@ case $1 in
esac esac
mkdir -p build/bin mkdir -p build/bin
cd build/bin cd build/bin
wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.9.5/Xray-linux-${ARCH}.zip" wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.9.10/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}"

View File

@@ -1 +1 @@
2.6.8 2.7.0

View File

@@ -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"
) )

20
go.mod
View File

@@ -1,6 +1,6 @@
module x-ui module x-ui
go 1.25.0 go 1.25.1
require ( require (
github.com/gin-contrib/gzip v1.2.3 github.com/gin-contrib/gzip v1.2.3
@@ -17,13 +17,13 @@ require (
github.com/shirou/gopsutil/v4 v4.25.8 github.com/shirou/gopsutil/v4 v4.25.8
github.com/valyala/fasthttp v1.65.0 github.com/valyala/fasthttp v1.65.0
github.com/xlzd/gotp v0.1.0 github.com/xlzd/gotp v0.1.0
github.com/xtls/xray-core v1.250905.0 github.com/xtls/xray-core v1.250910.0
go.uber.org/atomic v1.11.0 go.uber.org/atomic v1.11.0
golang.org/x/crypto v0.41.0 golang.org/x/crypto v0.42.0
golang.org/x/text v0.28.0 golang.org/x/text v0.29.0
google.golang.org/grpc v1.75.0 google.golang.org/grpc v1.75.1
gorm.io/driver/sqlite v1.6.0 gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.30.2 gorm.io/gorm v1.30.5
) )
require ( require (
@@ -85,16 +85,16 @@ require (
go.uber.org/mock v0.6.0 // indirect go.uber.org/mock v0.6.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/arch v0.21.0 // indirect golang.org/x/arch v0.21.0 // indirect
golang.org/x/mod v0.27.0 // indirect golang.org/x/mod v0.28.0 // indirect
golang.org/x/net v0.43.0 // indirect golang.org/x/net v0.44.0 // indirect
golang.org/x/sync v0.17.0 // indirect golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect golang.org/x/sys v0.36.0 // indirect
golang.org/x/time v0.13.0 // indirect golang.org/x/time v0.13.0 // indirect
golang.org/x/tools v0.36.0 // indirect golang.org/x/tools v0.36.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-20250521234502-f333402bd9cb // indirect golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect
google.golang.org/protobuf v1.36.8 // indirect google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c // indirect gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c // indirect
lukechampine.com/blake3 v1.4.1 // indirect lukechampine.com/blake3 v1.4.1 // indirect

36
go.sum
View File

@@ -176,8 +176,8 @@ github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg= github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
github.com/xtls/reality v0.0.0-20250904214705-431b6ff8c67c h1:LHLhQY3mKXSpTcQAkjFR4/6ar3rXjQryNeM7khK3AHU= github.com/xtls/reality v0.0.0-20250904214705-431b6ff8c67c h1:LHLhQY3mKXSpTcQAkjFR4/6ar3rXjQryNeM7khK3AHU=
github.com/xtls/reality v0.0.0-20250904214705-431b6ff8c67c/go.mod h1:XxvnCCgBee4WWE0bc4E+a7wbk8gkJ/rS0vNVNtC5qp0= github.com/xtls/reality v0.0.0-20250904214705-431b6ff8c67c/go.mod h1:XxvnCCgBee4WWE0bc4E+a7wbk8gkJ/rS0vNVNtC5qp0=
github.com/xtls/xray-core v1.250905.0 h1:VNL3l/6fcwyeYXJTRbf+TYqPfJYkk0Wmmz7qoQNkxY8= github.com/xtls/xray-core v1.250910.0 h1:9KzqL9Ulosp/JVXOMizTZxyQvqv4wkxKDcU5QZcio3s=
github.com/xtls/xray-core v1.250905.0/go.mod h1:WB/73DmN9Vs7lxtx4Xc/D0Ub1VUu06hAh1mMh8JN2uM= github.com/xtls/xray-core v1.250910.0/go.mod h1:LkqA/BFVtPS2e5fRzg/bkYas9nQu4Uztlx+/fjlLM9k=
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=
@@ -202,12 +202,12 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw= golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw=
golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
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=
@@ -218,8 +218,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
@@ -230,12 +230,12 @@ golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+Z
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA= google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 h1:/OQuEa4YWtDt7uQWHd3q3sUMb+QOLQUg1xa8CEsRv5w=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
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=
@@ -247,8 +247,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.30.2 h1:f7bevlVoVe4Byu3pmbWPVHnPsLoWaMjEb7/clyr9Ivs= gorm.io/gorm v1.30.5 h1:dvEfYwxL+i+xgCNSGGBT1lDjCzfELK8fHZxL3Ee9X0s=
gorm.io/gorm v1.30.2/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= gorm.io/gorm v1.30.5/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c h1:m/r7OM+Y2Ty1sgBQ7Qb27VgIMBW8ZZhT4gLnUyDIhzI= gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c h1:m/r7OM+Y2Ty1sgBQ7Qb27VgIMBW8ZZhT4gLnUyDIhzI=
gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g= gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=

View File

@@ -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,

View File

@@ -49,8 +49,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() {

View File

@@ -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;
@@ -1859,15 +1859,16 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
protocol, protocol,
vlesses = [new Inbound.VLESSSettings.VLESS()], vlesses = [new Inbound.VLESSSettings.VLESS()],
decryption = "none", decryption = "none",
encryption = "", encryption = "none",
fallbacks = [], fallbacks = [],
selectedAuth = undefined,
) { ) {
super(protocol); super(protocol);
this.vlesses = vlesses; this.vlesses = vlesses;
this.decryption = decryption; this.decryption = decryption;
this.encryption = encryption; this.encryption = encryption;
this.fallbacks = fallbacks; this.fallbacks = fallbacks;
this.selectedAuth = "X25519, not Post-Quantum"; this.selectedAuth = selectedAuth;
} }
addFallback() { addFallback() {
@@ -1884,9 +1885,9 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
(json.clients || []).map(client => Inbound.VLESSSettings.VLESS.fromJson(client)), (json.clients || []).map(client => Inbound.VLESSSettings.VLESS.fromJson(client)),
json.decryption, json.decryption,
json.encryption, json.encryption,
Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks || []) Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks || []),
json.selectedAuth
); );
obj.selectedAuth = json.selectedAuth || "X25519, not Post-Quantum";
return obj; return obj;
} }
@@ -2326,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,
@@ -2344,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),
@@ -2365,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;
@@ -2386,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,
@@ -2407,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;
@@ -2415,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);
} }
}; };

View File

@@ -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,

View File

@@ -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()
} }

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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)
} }

View File

@@ -83,14 +83,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 -->

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -30,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>

View File

@@ -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}}

View File

@@ -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>

View File

@@ -830,7 +830,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;
@@ -845,7 +845,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;
} }
@@ -1099,7 +1099,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({
@@ -1148,7 +1148,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 = {
@@ -1171,7 +1171,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);
@@ -1226,14 +1226,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);
@@ -1258,7 +1258,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) {
@@ -1271,10 +1271,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) {
@@ -1353,7 +1353,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()
@@ -1383,10 +1383,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() {
@@ -1396,7 +1396,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) {
@@ -1406,7 +1406,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) {
@@ -1416,7 +1416,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) {
@@ -1542,7 +1542,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);
}, },
}); });
}, },

View File

@@ -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);
}, },
}); });
@@ -798,9 +798,9 @@ ${dateTime}
onOk: async () => { onOk: async () => {
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',
} }

View File

@@ -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;

View File

@@ -354,7 +354,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>
@@ -376,7 +376,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>
@@ -492,7 +492,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(', ');
@@ -613,7 +613,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;

View File

@@ -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,9 +193,10 @@
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 = ''; this.inbound.settings.encryption = 'none';
this.inbound.settings.selectedAuth = undefined;
} }
}, },

View File

@@ -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;
} }

View File

@@ -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;

View File

@@ -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"
} }

View File

@@ -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"
@@ -343,7 +344,7 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
continue continue
} }
if major > 25 || (major == 25 && minor > 8) || (major == 25 && minor == 8 && patch >= 3) { if major > 25 || (major == 25 && minor > 9) || (major == 25 && minor == 9 && patch >= 10) {
versions = append(versions, release.TagName) versions = append(versions, release.TagName)
} }
} }
@@ -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
}

View File

@@ -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,
} }

View File

@@ -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)

Binary file not shown.

13
windows_files/readme.txt Normal file
View 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