mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-15 21:56:10 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47efac270d | ||
|
|
368eb89cd9 | ||
|
|
83e360ea1b | ||
|
|
6cdeb7ebeb | ||
|
|
035a1a7b5e | ||
|
|
857f0cb64c | ||
|
|
849a1249ac | ||
|
|
d608961af8 | ||
|
|
343e7a9f15 | ||
|
|
6ad558bd36 | ||
|
|
d6bf64f760 | ||
|
|
f8d20c8303 | ||
|
|
08403bc8f9 | ||
|
|
cdb90da138 | ||
|
|
c3d498f9ee | ||
|
|
0621eb0670 | ||
|
|
577c534e4c | ||
|
|
17dae4a563 | ||
|
|
9f1f841666 | ||
|
|
6b66c250b5 | ||
|
|
0829116fc1 | ||
|
|
aae0011a4f | ||
|
|
f4c565b208 | ||
|
|
2520994b13 | ||
|
|
d3fd56fc97 | ||
|
|
d4df614a9f | ||
|
|
640eb538a4 | ||
|
|
08153d45d1 | ||
|
|
843bb5f3ce |
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -50,6 +50,6 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64/v8, linux/arm/v7
|
||||
platforms: linux/amd64,linux/arm64/v8, linux/arm/v7, linux/386
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
41
.github/workflows/release.yml
vendored
41
.github/workflows/release.yml
vendored
@@ -10,7 +10,11 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [amd64, arm64, arm]
|
||||
platform:
|
||||
- amd64
|
||||
- arm64
|
||||
- armv7
|
||||
- 386
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -19,15 +23,17 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5.0.0
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Install dependencies for arm64 and arm
|
||||
if: matrix.platform == 'arm64' || matrix.platform == 'arm'
|
||||
go-version: '1.22'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt install gcc-aarch64-linux-gnu
|
||||
if [ "${{ matrix.platform }}" == "arm" ]; then
|
||||
if [ "${{ matrix.platform }}" == "arm64" ]; then
|
||||
sudo apt install gcc-aarch64-linux-gnu
|
||||
elif [ "${{ matrix.platform }}" == "armv7" ]; then
|
||||
sudo apt install gcc-arm-linux-gnueabihf
|
||||
elif [ "${{ matrix.platform }}" == "386" ]; then
|
||||
sudo apt install gcc-i686-linux-gnu
|
||||
fi
|
||||
|
||||
- name: Build x-ui
|
||||
@@ -36,9 +42,15 @@ jobs:
|
||||
export GOOS=linux
|
||||
export GOARCH=${{ matrix.platform }}
|
||||
if [ "${{ matrix.platform }}" == "arm64" ]; then
|
||||
export GOARCH=arm64
|
||||
export CC=aarch64-linux-gnu-gcc
|
||||
elif [ "${{ matrix.platform }}" == "arm" ]; then
|
||||
elif [ "${{ matrix.platform }}" == "armv7" ]; then
|
||||
export GOARCH=arm
|
||||
export GOARM=7
|
||||
export CC=arm-linux-gnueabihf-gcc
|
||||
elif [ "${{ matrix.platform }}" == "386" ]; then
|
||||
export GOARCH=386
|
||||
export CC=i686-linux-gnu-gcc
|
||||
fi
|
||||
go build -o xui-release -v main.go
|
||||
|
||||
@@ -60,12 +72,16 @@ jobs:
|
||||
wget ${Xray_URL}Xray-linux-arm64-v8a.zip
|
||||
unzip Xray-linux-arm64-v8a.zip
|
||||
rm -f Xray-linux-arm64-v8a.zip
|
||||
else
|
||||
elif [ "${{ matrix.platform }}" == "armv7" ]; then
|
||||
wget ${Xray_URL}Xray-linux-arm32-v7a.zip
|
||||
unzip Xray-linux-arm32-v7a.zip
|
||||
rm -f Xray-linux-arm32-v7a.zip
|
||||
elif [ "${{ matrix.platform }}" == "386" ]; then
|
||||
wget ${Xray_URL}Xray-linux-32.zip
|
||||
unzip Xray-linux-32.zip
|
||||
rm -f Xray-linux-32.zip
|
||||
fi
|
||||
rm -f geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat
|
||||
rm -f geoip.dat geosite.dat
|
||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
wget -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||
@@ -76,12 +92,11 @@ jobs:
|
||||
- name: Package
|
||||
run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui
|
||||
|
||||
- name: Upload
|
||||
uses: svenstaro/upload-release-action@2.7.0
|
||||
- name: Upload files to GH release
|
||||
uses: MHSanaei/upload-release-action@2.8.0
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
file: x-ui-linux-${{ matrix.platform }}.tar.gz
|
||||
asset_name: x-ui-linux-${{ matrix.platform }}.tar.gz
|
||||
prerelease: true
|
||||
overwrite: true
|
||||
|
||||
@@ -4,6 +4,10 @@ case $1 in
|
||||
ARCH="64"
|
||||
FNAME="amd64"
|
||||
;;
|
||||
i386)
|
||||
ARCH="32"
|
||||
FNAME="i386"
|
||||
;;
|
||||
armv8 | arm64 | aarch64)
|
||||
ARCH="arm64-v8a"
|
||||
FNAME="arm64"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.21-alpine AS builder
|
||||
FROM golang:1.22-alpine AS builder
|
||||
WORKDIR /app
|
||||
ARG TARGETARCH
|
||||
RUN apk --no-cache --update add build-base gcc wget unzip
|
||||
|
||||
66
README.md
66
README.md
@@ -43,10 +43,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.s
|
||||
|
||||
## Install Custom Version
|
||||
|
||||
**Step 1:** To install your desired version, add the version to the end of the installation command. e.g., ver `1.6.4`:
|
||||
**Step 1:** To install your desired version, add the version to the end of the installation command. e.g., ver `1.7.1`:
|
||||
|
||||
```sh
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh) 1.6.4
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh) 1.7.1
|
||||
```
|
||||
|
||||
## Manual Install & Upgrade
|
||||
@@ -56,15 +56,32 @@ bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.s
|
||||
|
||||
### Usage
|
||||
|
||||
**Step 1:** First download the latest compressed package from https://github.com/alireza0/x-ui/releases, generally choose Architecture `amd64`
|
||||
|
||||
**Step 2:** Then upload the compressed package to the server's `/root/` directory and login to the server with user `root`
|
||||
|
||||
> If your server CPU architecture is not `amd64` replace it with the appropriate architecture
|
||||
1. To download the latest version of the compressed package directly to your server, run the following command:
|
||||
|
||||
```sh
|
||||
ARCH=$(uname -m)
|
||||
[[ "${ARCH}" == "s390x" ]] && XUI_ARCH="s390x" || [[ "${ARCH}" == "aarch64" || "${ARCH}" == "arm64" ]] && XUI_ARCH="arm64" || XUI_ARCH="amd64"
|
||||
case "${ARCH}" in
|
||||
x86_64 | x64 | amd64) XUI_ARCH="amd64" ;;
|
||||
i*86 | x86) XUI_ARCH="386" ;;
|
||||
armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;;
|
||||
armv7* | armv7) XUI_ARCH="armv7" ;;
|
||||
*) XUI_ARCH="amd64" ;;
|
||||
esac
|
||||
|
||||
wget https://github.com/alireza0/x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz
|
||||
```
|
||||
|
||||
2. Once the compressed package is downloaded, execute the following commands to install or upgrade x-ui:
|
||||
|
||||
```sh
|
||||
ARCH=$(uname -m)
|
||||
case "${ARCH}" in
|
||||
x86_64 | x64 | amd64) XUI_ARCH="amd64" ;;
|
||||
i*86 | x86) XUI_ARCH="386" ;;
|
||||
armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;;
|
||||
armv7* | armv7) XUI_ARCH="armv7" ;;
|
||||
*) XUI_ARCH="amd64" ;;
|
||||
esac
|
||||
cd /root/
|
||||
rm x-ui/ /usr/local/x-ui/ /usr/bin/x-ui -rf
|
||||
tar zxvf x-ui-linux-${XUI_ARCH}.tar.gz
|
||||
@@ -92,7 +109,20 @@ systemctl restart x-ui
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
```
|
||||
|
||||
**Step 2:** Install X-UI
|
||||
**Step 2:** Clone the Project Repository:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/alireza0/x-ui.git
|
||||
cd x-ui
|
||||
```
|
||||
|
||||
**Step 3:** Start the Service
|
||||
|
||||
```sh
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
OR
|
||||
|
||||
```shell
|
||||
mkdir x-ui && cd x-ui
|
||||
@@ -105,6 +135,24 @@ docker run -itd \
|
||||
alireza7/x-ui:latest
|
||||
```
|
||||
|
||||
update to latest version
|
||||
|
||||
```sh
|
||||
cd x-ui
|
||||
docker compose down
|
||||
docker compose pull x-ui
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
remove x-ui from docker
|
||||
|
||||
```sh
|
||||
docker stop x-ui
|
||||
docker rm x-ui
|
||||
cd --
|
||||
rm -r x-ui
|
||||
```
|
||||
|
||||
> Build your own image
|
||||
|
||||
```shell
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.7.1
|
||||
1.7.2
|
||||
69
go.mod
69
go.mod
@@ -1,9 +1,9 @@
|
||||
module x-ui
|
||||
|
||||
go 1.21.4
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/sessions v0.0.4
|
||||
github.com/Calidity/gin-sessions v1.3.1
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||
github.com/goccy/go-json v0.10.2
|
||||
@@ -20,72 +20,73 @@ require (
|
||||
gorm.io/gorm v1.25.7
|
||||
)
|
||||
|
||||
require github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/bytedance/sonic v1.11.0 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/gzip v0.0.6
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.18.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect
|
||||
github.com/gorilla/context v1.1.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/gorilla/sessions v1.2.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.2.2 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/klauspost/compress v1.17.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
|
||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||
github.com/quic-go/quic-go v0.40.1 // indirect
|
||||
github.com/refraction-networking/utls v1.6.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240219145905-2259734c190a // indirect
|
||||
github.com/quic-go/quic-go v0.41.0 // indirect
|
||||
github.com/refraction-networking/utls v1.6.2 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/sagernet/sing v0.3.0 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.13 // indirect
|
||||
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/arch v0.7.0 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect
|
||||
golang.org/x/mod v0.15.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.16.1 // indirect
|
||||
golang.org/x/tools v0.18.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect
|
||||
|
||||
173
go.sum
173
go.sum
@@ -10,22 +10,25 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/Calidity/gin-sessions v1.3.1 h1:nF3dCBWa7TZ4j26iYLwGRmzZy9YODhWoOS3fmi+snyE=
|
||||
github.com/Calidity/gin-sessions v1.3.1/go.mod h1:I0+QE6qkO50TeN/n6If6novvxHk4Isvr23U8EdvPdns=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/antonlindstrom/pgstore v0.0.0-20200229204646-b08ebf1105e0/go.mod h1:2Ti6VUHVxpC0VSmTZzEvpzysnaGAfGBOoMIz5ykPyyw=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI=
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||
github.com/bytedance/sonic v1.11.0 h1:FwNNv6Vu4z2Onf1++LNzxB/QhitD8wuTdpZzMTGITWo=
|
||||
github.com/bytedance/sonic v1.11.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
||||
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
@@ -42,43 +45,37 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
|
||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||
github.com/gin-contrib/sessions v0.0.4 h1:gq4fNa1Zmp564iHP5G6EBuktilEos8VKhe2sza1KMgo=
|
||||
github.com/gin-contrib/sessions v0.0.4/go.mod h1:pQ3sIyviBBGcxgyR8mkeJuXbeV3h3NYmhJADQTq5+Vo=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U=
|
||||
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||
@@ -95,11 +92,9 @@ github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
@@ -112,21 +107,21 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8=
|
||||
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
|
||||
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
||||
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
@@ -137,17 +132,16 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
|
||||
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
@@ -157,31 +151,28 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik=
|
||||
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
|
||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0=
|
||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
@@ -189,10 +180,10 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
|
||||
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
|
||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
@@ -205,23 +196,19 @@ github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwy
|
||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/power-devops/perfstat v0.0.0-20240219145905-2259734c190a h1:XCUtNgBnZfUBhdfCX2QK+fslr9vevSsUg3W3peZwlak=
|
||||
github.com/power-devops/perfstat v0.0.0-20240219145905-2259734c190a/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q=
|
||||
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
|
||||
github.com/refraction-networking/utls v1.6.0 h1:X5vQMqVx7dY7ehxxqkFER/W6DSjy8TMqSItXm8hRDYQ=
|
||||
github.com/refraction-networking/utls v1.6.0/go.mod h1:kHJ6R9DFFA0WsRgBM35iiDku4O7AqPR6y79iuzW7b10=
|
||||
github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
|
||||
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
|
||||
github.com/refraction-networking/utls v1.6.2 h1:iTeeGY0o6nMNcGyirxkD5bFIsVctP5InGZ3E0HrzS7k=
|
||||
github.com/refraction-networking/utls v1.6.2/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
@@ -272,28 +259,26 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
|
||||
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
|
||||
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
@@ -307,8 +292,9 @@ github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3
|
||||
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
||||
github.com/xtls/xray-core v1.8.7 h1:lb8O1l3/eAg3YAXA6tLm5M6N7BsX8wxW9sJLjU3dHkA=
|
||||
github.com/xtls/xray-core v1.8.7/go.mod h1:9rFpflfQbgFeH1VKJw7yUmEy7myOyDCgNXXl0bmmyOo=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
@@ -318,24 +304,23 @@ go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
|
||||
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -344,10 +329,9 @@ golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -364,9 +348,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -374,16 +356,17 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
@@ -397,8 +380,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
|
||||
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
@@ -416,8 +399,8 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
|
||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
@@ -437,7 +420,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -452,11 +434,14 @@ gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b h1:yqkg3pTifuKukuWanp8spDsL4irJkHF5WI0J47hU87o=
|
||||
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
|
||||
gvisor.dev/gvisor v0.0.0-20240216214558-53d2b511e78e h1:BAmOamSGzOqvWRcyoAZvud9SuInvUn2qYIK495rjF+4=
|
||||
gvisor.dev/gvisor v0.0.0-20240216214558-53d2b511e78e/go.mod h1:YcCCAniKhCIGGvWxOobcre6euvNQON7nZCtMcVYO9rA=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
|
||||
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
|
||||
54
install.sh
54
install.sh
@@ -26,8 +26,9 @@ echo "The OS release is: $release"
|
||||
arch() {
|
||||
case "$(uname -m)" in
|
||||
x86_64 | x64 | amd64) echo 'amd64' ;;
|
||||
i*86 | x86) echo '386' ;;
|
||||
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
|
||||
armv7* | armv7 | arm | arm32 ) echo 'arm' ;;
|
||||
armv7* | armv7 | arm) echo 'armv7' ;;
|
||||
*) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;;
|
||||
esac
|
||||
}
|
||||
@@ -40,7 +41,7 @@ if [[ "${release}" == "centos" ]]; then
|
||||
if [[ ${os_version} -lt 8 ]]; then
|
||||
echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
elif [[ "${release}" == "ubuntu" ]]; then
|
||||
elif [[ "${release}" == "ubuntu" ]]; then
|
||||
if [[ ${os_version} -lt 20 ]]; then
|
||||
echo -e "${red}please use Ubuntu 20 or higher version! ${plain}\n" && exit 1
|
||||
fi
|
||||
@@ -58,13 +59,18 @@ else
|
||||
echo -e "${red}Failed to check the OS version, please contact the author!${plain}" && exit 1
|
||||
fi
|
||||
|
||||
|
||||
install_base() {
|
||||
if [[ "${release}" == "centos" ]] || [[ "${release}" == "fedora" ]] ; then
|
||||
yum install wget curl tar -y
|
||||
else
|
||||
apt install wget curl tar -y
|
||||
fi
|
||||
install_dependencies() {
|
||||
case "${release}" in
|
||||
centos)
|
||||
yum -y update && yum install -y -q wget curl tar tzdata
|
||||
;;
|
||||
fedora)
|
||||
dnf -y update && dnf install -y -q wget curl tar tzdata
|
||||
;;
|
||||
*)
|
||||
apt-get update && apt install -y -q wget curl tar tzdata
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
#This function will be called when user installed x-ui out of sercurity
|
||||
@@ -103,6 +109,21 @@ config_after_install() {
|
||||
}
|
||||
|
||||
install_x-ui() {
|
||||
# checks if the installation backup dir exist. if existed then ask user if they want to restore it else continue installation.
|
||||
if [[ -e /usr/local/x-ui-backup/ ]]; then
|
||||
read -p "Failed installation detected. Do you want to restore previously installed version? [y/n]? ": restore_confirm
|
||||
if [[ "${restore_confirm}" == "y" || "${restore_confirm}" == "Y" ]]; then
|
||||
systemctl stop x-ui
|
||||
mv /usr/local/x-ui-backup/x-ui.db /etc/x-ui/ -f
|
||||
mv /usr/local/x-ui-backup/ /usr/local/x-ui/ -f
|
||||
systemctl start x-ui
|
||||
echo -e "${green}previous installed x-ui restored successfully${plain}, it is up and running now..."
|
||||
exit 0
|
||||
else
|
||||
echo -e "Continuing installing x-ui ..."
|
||||
fi
|
||||
fi
|
||||
|
||||
cd /usr/local/
|
||||
|
||||
if [ $# == 0 ]; then
|
||||
@@ -130,18 +151,27 @@ install_x-ui() {
|
||||
|
||||
if [[ -e /usr/local/x-ui/ ]]; then
|
||||
systemctl stop x-ui
|
||||
rm /usr/local/x-ui/ -rf
|
||||
mv /usr/local/x-ui/ /usr/local/x-ui-backup/ -f
|
||||
cp /etc/x-ui/x-ui.db /usr/local/x-ui-backup/ -f
|
||||
fi
|
||||
|
||||
tar zxvf x-ui-linux-$(arch).tar.gz
|
||||
rm x-ui-linux-$(arch).tar.gz -f
|
||||
cd x-ui
|
||||
chmod +x x-ui
|
||||
|
||||
# Check the system's architecture and rename the file accordingly
|
||||
if [[ $(arch) == "armv7" ]]; then
|
||||
mv bin/xray-linux-$(arch) bin/xray-linux-arm
|
||||
chmod +x bin/xray-linux-arm
|
||||
fi
|
||||
chmod +x x-ui bin/xray-linux-$(arch)
|
||||
cp -f x-ui.service /etc/systemd/system/
|
||||
wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/alireza0/x-ui/main/x-ui.sh
|
||||
chmod +x /usr/local/x-ui/x-ui.sh
|
||||
chmod +x /usr/bin/x-ui
|
||||
config_after_install
|
||||
rm /usr/local/x-ui-backup/ -rf
|
||||
#echo -e "If it is a new installation, the default web port is ${green}54321${plain}, The username and password are ${green}admin${plain} by default"
|
||||
#echo -e "Please make sure that this port is not occupied by other procedures,${yellow} And make sure that port 54321 has been released${plain}"
|
||||
# echo -e "If you want to modify the 54321 to other ports and enter the x-ui command to modify it, you must also ensure that the port you modify is also released"
|
||||
@@ -155,7 +185,7 @@ install_x-ui() {
|
||||
echo -e ""
|
||||
echo "X-UI Control Menu Usage"
|
||||
echo "------------------------------------------"
|
||||
echo "SUBCOMMANDS:"
|
||||
echo "SUBCOMMANDS:"
|
||||
echo "x-ui - Admin Management Script"
|
||||
echo "x-ui start - Start"
|
||||
echo "x-ui stop - Stop"
|
||||
@@ -172,5 +202,5 @@ install_x-ui() {
|
||||
}
|
||||
|
||||
echo -e "${green}Running...${plain}"
|
||||
install_base
|
||||
install_dependencies
|
||||
install_x-ui $1
|
||||
|
||||
105
sub/default.json
Normal file
105
sub/default.json
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"dns": {
|
||||
"tag": "dns_out",
|
||||
"queryStrategy": "UseIP",
|
||||
"servers": [
|
||||
{
|
||||
"address": "8.8.8.8",
|
||||
"skipFallback": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"port": 10808,
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"auth": "noauth",
|
||||
"udp": true,
|
||||
"userLevel": 8
|
||||
},
|
||||
"sniffing": {
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls",
|
||||
"fakedns"
|
||||
],
|
||||
"enabled": true
|
||||
},
|
||||
"tag": "socks"
|
||||
},
|
||||
{
|
||||
"port": 10809,
|
||||
"protocol": "http",
|
||||
"settings": {
|
||||
"userLevel": 8
|
||||
},
|
||||
"tag": "http"
|
||||
}
|
||||
],
|
||||
"log": {
|
||||
"loglevel": "warning"
|
||||
},
|
||||
"outbounds": [
|
||||
{
|
||||
"tag": "direct",
|
||||
"protocol": "freedom",
|
||||
"settings": {
|
||||
"domainStrategy": "UseIP"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "block",
|
||||
"protocol": "blackhole",
|
||||
"settings": {
|
||||
"response": {
|
||||
"type": "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"policy": {
|
||||
"levels": {
|
||||
"8": {
|
||||
"connIdle": 300,
|
||||
"downlinkOnly": 1,
|
||||
"handshake": 4,
|
||||
"uplinkOnly": 1
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"statsOutboundUplink": true,
|
||||
"statsOutboundDownlink": true
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"domainStrategy": "AsIs",
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"network": "tcp,udp",
|
||||
"balancerTag": "all"
|
||||
}
|
||||
],
|
||||
"balancers": [
|
||||
{
|
||||
"tag": "all",
|
||||
"selector": [
|
||||
"proxy"
|
||||
],
|
||||
"strategy": {
|
||||
"type": "leastPing"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"observatory": {
|
||||
"probeInterval": "5m",
|
||||
"probeURL": "https://api.github.com/_private/browser/stats",
|
||||
"subjectSelector": [
|
||||
"proxy"
|
||||
],
|
||||
"EnableConcurrency": true
|
||||
},
|
||||
"stats": {}
|
||||
}
|
||||
44
sub/sub.go
44
sub/sub.go
@@ -47,11 +47,6 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
||||
|
||||
engine := gin.Default()
|
||||
|
||||
subPath, err := s.settingService.GetSubPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subDomain, err := s.settingService.GetSubDomain()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -61,9 +56,44 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
||||
engine.Use(middleware.DomainValidatorMiddleware(subDomain))
|
||||
}
|
||||
|
||||
g := engine.Group(subPath)
|
||||
LinksPath, err := s.settingService.GetSubPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.sub = NewSUBController(g)
|
||||
JsonPath, err := s.settingService.GetSubJsonPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Encrypt, err := s.settingService.GetSubEncrypt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ShowInfo, err := s.settingService.GetSubShowInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
RemarkModel, err := s.settingService.GetRemarkModel()
|
||||
if err != nil {
|
||||
RemarkModel = "-ieo"
|
||||
}
|
||||
|
||||
SubUpdates, err := s.settingService.GetSubUpdates()
|
||||
if err != nil {
|
||||
SubUpdates = "10"
|
||||
}
|
||||
|
||||
SubJsonFragment, err := s.settingService.GetSubJsonFragment()
|
||||
if err != nil {
|
||||
SubJsonFragment = ""
|
||||
}
|
||||
|
||||
g := engine.Group("/")
|
||||
|
||||
s.sub = NewSUBController(g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates, SubJsonFragment)
|
||||
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
@@ -3,34 +3,57 @@ package sub
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
"x-ui/web/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SUBController struct {
|
||||
subService SubService
|
||||
settingService service.SettingService
|
||||
subPath string
|
||||
subJsonPath string
|
||||
subEncrypt bool
|
||||
updateInterval string
|
||||
|
||||
subService *SubService
|
||||
subJsonService *SubJsonService
|
||||
}
|
||||
|
||||
func NewSUBController(g *gin.RouterGroup) *SUBController {
|
||||
a := &SUBController{}
|
||||
func NewSUBController(
|
||||
g *gin.RouterGroup,
|
||||
subPath string,
|
||||
jsonPath string,
|
||||
encrypt bool,
|
||||
showInfo bool,
|
||||
rModel string,
|
||||
update string,
|
||||
jsonFragment string) *SUBController {
|
||||
|
||||
a := &SUBController{
|
||||
subPath: subPath,
|
||||
subJsonPath: jsonPath,
|
||||
subEncrypt: encrypt,
|
||||
updateInterval: update,
|
||||
|
||||
subService: NewSubService(showInfo, rModel),
|
||||
subJsonService: NewSubJsonService(jsonFragment),
|
||||
}
|
||||
a.initRouter(g)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *SUBController) initRouter(g *gin.RouterGroup) {
|
||||
g = g.Group("/")
|
||||
gLink := g.Group(a.subPath)
|
||||
gJson := g.Group(a.subJsonPath)
|
||||
|
||||
g.GET("/:subid", a.subs)
|
||||
gLink.GET(":subid", a.subs)
|
||||
|
||||
gJson.GET(":subid", a.subJsons)
|
||||
}
|
||||
|
||||
func (a *SUBController) subs(c *gin.Context) {
|
||||
subEncrypt, _ := a.settingService.GetSubEncrypt()
|
||||
subShowInfo, _ := a.settingService.GetSubShowInfo()
|
||||
println(c.Request.Header["User-Agent"][0])
|
||||
subId := c.Param("subid")
|
||||
host := strings.Split(c.Request.Host, ":")[0]
|
||||
subs, headers, err := a.subService.GetSubs(subId, host, subShowInfo)
|
||||
subs, header, err := a.subService.GetSubs(subId, host)
|
||||
if err != nil || len(subs) == 0 {
|
||||
c.String(400, "Error!")
|
||||
} else {
|
||||
@@ -40,14 +63,32 @@ func (a *SUBController) subs(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Add headers
|
||||
c.Writer.Header().Set("Subscription-Userinfo", headers[0])
|
||||
c.Writer.Header().Set("Profile-Update-Interval", headers[1])
|
||||
c.Writer.Header().Set("Profile-Title", headers[2])
|
||||
c.Writer.Header().Set("Subscription-Userinfo", header)
|
||||
c.Writer.Header().Set("Profile-Update-Interval", a.updateInterval)
|
||||
c.Writer.Header().Set("Profile-Title", subId)
|
||||
|
||||
if subEncrypt {
|
||||
if a.subEncrypt {
|
||||
c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
|
||||
} else {
|
||||
c.String(200, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *SUBController) subJsons(c *gin.Context) {
|
||||
println(c.Request.Header["User-Agent"][0])
|
||||
subId := c.Param("subid")
|
||||
host := strings.Split(c.Request.Host, ":")[0]
|
||||
jsonSub, header, err := a.subJsonService.GetJson(subId, host)
|
||||
if err != nil || len(jsonSub) == 0 {
|
||||
c.String(400, "Error!")
|
||||
} else {
|
||||
|
||||
// Add headers
|
||||
c.Writer.Header().Set("Subscription-Userinfo", header)
|
||||
c.Writer.Header().Set("Profile-Update-Interval", a.updateInterval)
|
||||
c.Writer.Header().Set("Profile-Title", subId)
|
||||
|
||||
c.String(200, jsonSub)
|
||||
}
|
||||
}
|
||||
|
||||
359
sub/subJsonService.go
Normal file
359
sub/subJsonService.go
Normal file
@@ -0,0 +1,359 @@
|
||||
package sub
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"x-ui/database/model"
|
||||
"x-ui/logger"
|
||||
"x-ui/util/json_util"
|
||||
"x-ui/util/random"
|
||||
"x-ui/web/service"
|
||||
"x-ui/xray"
|
||||
)
|
||||
|
||||
//go:embed default.json
|
||||
var defaultJson string
|
||||
|
||||
type SubJsonService struct {
|
||||
fragmanet string
|
||||
|
||||
inboundService service.InboundService
|
||||
SubService
|
||||
}
|
||||
|
||||
func NewSubJsonService(fragment string) *SubJsonService {
|
||||
return &SubJsonService{
|
||||
fragmanet: fragment,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SubJsonService) GetJson(subId string, host string) (string, string, error) {
|
||||
inbounds, err := s.SubService.getInboundsBySubId(subId)
|
||||
if err != nil || len(inbounds) == 0 {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var header string
|
||||
var traffic xray.ClientTraffic
|
||||
var clientTraffics []xray.ClientTraffic
|
||||
var configJson map[string]interface{}
|
||||
var defaultOutbounds []json_util.RawMessage
|
||||
|
||||
json.Unmarshal([]byte(defaultJson), &configJson)
|
||||
if outboundSlices, ok := configJson["outbounds"].([]interface{}); ok {
|
||||
for _, defaultOutbound := range outboundSlices {
|
||||
jsonBytes, _ := json.Marshal(defaultOutbound)
|
||||
defaultOutbounds = append(defaultOutbounds, jsonBytes)
|
||||
}
|
||||
}
|
||||
|
||||
outbounds := []json_util.RawMessage{}
|
||||
startIndex := 0
|
||||
// Prepare Inbounds
|
||||
for _, inbound := range inbounds {
|
||||
clients, err := s.inboundService.GetClients(inbound)
|
||||
if err != nil {
|
||||
logger.Error("SubJsonService - GetClients: Unable to get clients from inbound")
|
||||
}
|
||||
if clients == nil {
|
||||
continue
|
||||
}
|
||||
if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' {
|
||||
listen, port, streamSettings, err := s.getFallbackMaster(inbound.Listen, inbound.StreamSettings)
|
||||
if err == nil {
|
||||
inbound.Listen = listen
|
||||
inbound.Port = port
|
||||
inbound.StreamSettings = streamSettings
|
||||
}
|
||||
}
|
||||
|
||||
var subClients []model.Client
|
||||
for _, client := range clients {
|
||||
if client.Enable && client.SubID == subId {
|
||||
subClients = append(subClients, client)
|
||||
clientTraffics = append(clientTraffics, s.SubService.getClientTraffics(inbound.ClientStats, client.Email))
|
||||
}
|
||||
}
|
||||
|
||||
outbound := s.getOutbound(inbound, subClients, host, startIndex)
|
||||
if outbound != nil {
|
||||
outbounds = append(outbounds, outbound...)
|
||||
startIndex += len(outbound)
|
||||
}
|
||||
}
|
||||
|
||||
if len(outbounds) == 0 {
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
// Prepare statistics
|
||||
for index, clientTraffic := range clientTraffics {
|
||||
if index == 0 {
|
||||
traffic.Up = clientTraffic.Up
|
||||
traffic.Down = clientTraffic.Down
|
||||
traffic.Total = clientTraffic.Total
|
||||
if clientTraffic.ExpiryTime > 0 {
|
||||
traffic.ExpiryTime = clientTraffic.ExpiryTime
|
||||
}
|
||||
} else {
|
||||
traffic.Up += clientTraffic.Up
|
||||
traffic.Down += clientTraffic.Down
|
||||
if traffic.Total == 0 || clientTraffic.Total == 0 {
|
||||
traffic.Total = 0
|
||||
} else {
|
||||
traffic.Total += clientTraffic.Total
|
||||
}
|
||||
if clientTraffic.ExpiryTime != traffic.ExpiryTime {
|
||||
traffic.ExpiryTime = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.fragmanet != "" {
|
||||
outbounds = append(outbounds, json_util.RawMessage(s.fragmanet))
|
||||
}
|
||||
|
||||
// Combile outbounds
|
||||
outbounds = append(outbounds, defaultOutbounds...)
|
||||
var outboundStrings []json_util.RawMessage
|
||||
for _, outbound := range outbounds {
|
||||
outboundStrings = append(outboundStrings, outbound)
|
||||
}
|
||||
configJson["outbounds"] = outboundStrings
|
||||
finalJson, _ := json.MarshalIndent(configJson, "", " ")
|
||||
|
||||
header = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000)
|
||||
return string(finalJson), header, nil
|
||||
}
|
||||
|
||||
func (s *SubJsonService) getOutbound(inbound *model.Inbound, clients []model.Client, host string, startIndex int) []json_util.RawMessage {
|
||||
var newOutbounds []json_util.RawMessage
|
||||
stream := s.streamData(inbound.StreamSettings)
|
||||
|
||||
externalProxies, ok := stream["externalProxy"].([]interface{})
|
||||
if !ok || len(externalProxies) == 0 {
|
||||
externalProxies = []interface{}{
|
||||
map[string]interface{}{
|
||||
"forceTls": "same",
|
||||
"dest": host,
|
||||
"port": float64(inbound.Port),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
delete(stream, "externalProxy")
|
||||
|
||||
config_index := startIndex
|
||||
for _, ep := range externalProxies {
|
||||
extPrxy := ep.(map[string]interface{})
|
||||
inbound.Listen = extPrxy["dest"].(string)
|
||||
inbound.Port = int(extPrxy["port"].(float64))
|
||||
newStream := stream
|
||||
switch extPrxy["forceTls"].(string) {
|
||||
case "tls":
|
||||
if newStream["security"] != "tls" {
|
||||
newStream["security"] = "tls"
|
||||
newStream["tslSettings"] = map[string]interface{}{}
|
||||
}
|
||||
case "none":
|
||||
if newStream["security"] != "none" {
|
||||
newStream["security"] = "none"
|
||||
delete(newStream, "tslSettings")
|
||||
}
|
||||
}
|
||||
streamSettings, _ := json.MarshalIndent(newStream, "", " ")
|
||||
inbound.StreamSettings = string(streamSettings)
|
||||
|
||||
for _, client := range clients {
|
||||
inbound.Tag = fmt.Sprintf("proxy_%d", config_index)
|
||||
switch inbound.Protocol {
|
||||
case "vmess", "vless":
|
||||
newOutbounds = append(newOutbounds, s.genVnext(inbound, client))
|
||||
case "trojan", "shadowsocks":
|
||||
newOutbounds = append(newOutbounds, s.genServer(inbound, client))
|
||||
}
|
||||
config_index += 1
|
||||
}
|
||||
}
|
||||
|
||||
return newOutbounds
|
||||
}
|
||||
|
||||
func (s *SubJsonService) streamData(stream string) map[string]interface{} {
|
||||
var streamSettings map[string]interface{}
|
||||
json.Unmarshal([]byte(stream), &streamSettings)
|
||||
security, _ := streamSettings["security"].(string)
|
||||
if security == "tls" {
|
||||
streamSettings["tlsSettings"] = s.tlsData(streamSettings["tlsSettings"].(map[string]interface{}))
|
||||
} else if security == "reality" {
|
||||
streamSettings["realitySettings"] = s.realityData(streamSettings["realitySettings"].(map[string]interface{}))
|
||||
}
|
||||
delete(streamSettings, "sockopt")
|
||||
|
||||
if s.fragmanet != "" {
|
||||
streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "TcpNoDelay": true}`)
|
||||
}
|
||||
|
||||
// remove proxy protocol
|
||||
network, _ := streamSettings["network"].(string)
|
||||
switch network {
|
||||
case "tcp":
|
||||
streamSettings["tcpSettings"] = s.removeAcceptProxy(streamSettings["tcpSettings"])
|
||||
case "ws":
|
||||
streamSettings["wsSettings"] = s.removeAcceptProxy(streamSettings["wsSettings"])
|
||||
}
|
||||
|
||||
return streamSettings
|
||||
}
|
||||
|
||||
func (s *SubJsonService) removeAcceptProxy(setting interface{}) map[string]interface{} {
|
||||
netSettings, ok := setting.(map[string]interface{})
|
||||
if ok {
|
||||
delete(netSettings, "acceptProxyProtocol")
|
||||
}
|
||||
return netSettings
|
||||
}
|
||||
|
||||
func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interface{} {
|
||||
tlsData := make(map[string]interface{}, 1)
|
||||
tlsClientSettings := tData["settings"].(map[string]interface{})
|
||||
|
||||
tlsData["serverName"] = tData["serverName"]
|
||||
tlsData["alpn"] = tData["alpn"]
|
||||
if allowInsecure, ok := tlsClientSettings["allowInsecure"].(string); ok {
|
||||
tlsData["allowInsecure"] = allowInsecure
|
||||
}
|
||||
if fingerprint, ok := tlsClientSettings["fingerprint"].(string); ok {
|
||||
tlsData["fingerprint"] = fingerprint
|
||||
}
|
||||
return tlsData
|
||||
}
|
||||
|
||||
func (s *SubJsonService) realityData(rData map[string]interface{}) map[string]interface{} {
|
||||
rltyData := make(map[string]interface{}, 1)
|
||||
rltyClientSettings := rData["settings"].(map[string]interface{})
|
||||
|
||||
rltyData["show"] = false
|
||||
rltyData["publicKey"] = rltyClientSettings["publicKey"]
|
||||
rltyData["fingerprint"] = rltyClientSettings["fingerprint"]
|
||||
|
||||
// Set random data
|
||||
rltyData["spiderX"] = "/" + random.Seq(15)
|
||||
shortIds, ok := rData["shortIds"].([]interface{})
|
||||
if ok && len(shortIds) > 0 {
|
||||
rltyData["shortId"] = shortIds[random.Num(len(shortIds))].(string)
|
||||
} else {
|
||||
rltyData["shortId"] = ""
|
||||
}
|
||||
serverNames, ok := rData["serverNames"].([]interface{})
|
||||
if ok && len(serverNames) > 0 {
|
||||
rltyData["serverName"] = serverNames[random.Num(len(serverNames))].(string)
|
||||
} else {
|
||||
rltyData["serverName"] = ""
|
||||
}
|
||||
|
||||
return rltyData
|
||||
}
|
||||
|
||||
func (s *SubJsonService) genVnext(inbound *model.Inbound, client model.Client) json_util.RawMessage {
|
||||
outbound := Outbound{}
|
||||
usersData := make([]UserVnext, 1)
|
||||
|
||||
usersData[0].ID = client.ID
|
||||
usersData[0].Level = 8
|
||||
if inbound.Protocol == model.VLESS {
|
||||
usersData[0].Flow = client.Flow
|
||||
usersData[0].Encryption = "none"
|
||||
}
|
||||
|
||||
vnextData := make([]VnextSetting, 1)
|
||||
vnextData[0] = VnextSetting{
|
||||
Address: inbound.Listen,
|
||||
Port: inbound.Port,
|
||||
Users: usersData,
|
||||
}
|
||||
|
||||
outbound.Protocol = string(inbound.Protocol)
|
||||
outbound.Tag = inbound.Tag
|
||||
outbound.StreamSettings = json_util.RawMessage(inbound.StreamSettings)
|
||||
outbound.Settings = OutboundSettings{
|
||||
Vnext: vnextData,
|
||||
}
|
||||
|
||||
result, _ := json.MarshalIndent(outbound, "", " ")
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SubJsonService) genServer(inbound *model.Inbound, client model.Client) json_util.RawMessage {
|
||||
outbound := Outbound{}
|
||||
|
||||
serverData := make([]ServerSetting, 1)
|
||||
serverData[0] = ServerSetting{
|
||||
Address: inbound.Listen,
|
||||
Port: inbound.Port,
|
||||
Level: 8,
|
||||
Password: client.Password,
|
||||
}
|
||||
|
||||
if inbound.Protocol == model.Shadowsocks {
|
||||
var inboundSettings map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
|
||||
method, _ := inboundSettings["method"].(string)
|
||||
serverData[0].Method = method
|
||||
|
||||
// server password in multi-user 2022 protocols
|
||||
if strings.HasPrefix(method, "2022") {
|
||||
if serverPassword, ok := inboundSettings["password"].(string); ok {
|
||||
serverData[0].Password = fmt.Sprintf("%s:%s", serverPassword, client.Password)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outbound.Protocol = string(inbound.Protocol)
|
||||
outbound.Tag = inbound.Tag
|
||||
outbound.StreamSettings = json_util.RawMessage(inbound.StreamSettings)
|
||||
outbound.Settings = OutboundSettings{
|
||||
Servers: serverData,
|
||||
}
|
||||
|
||||
result, _ := json.MarshalIndent(outbound, "", " ")
|
||||
return result
|
||||
}
|
||||
|
||||
type Outbound struct {
|
||||
Protocol string `json:"protocol"`
|
||||
Tag string `json:"tag"`
|
||||
StreamSettings json_util.RawMessage `json:"streamSettings"`
|
||||
Mux map[string]interface{} `json:"mux,omitempty"`
|
||||
ProxySettings map[string]interface{} `json:"proxySettings,omitempty"`
|
||||
Settings OutboundSettings `json:"settings,omitempty"`
|
||||
}
|
||||
|
||||
type OutboundSettings struct {
|
||||
Vnext []VnextSetting `json:"vnext,omitempty"`
|
||||
Servers []ServerSetting `json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
type VnextSetting struct {
|
||||
Address string `json:"address"`
|
||||
Port int `json:"port"`
|
||||
Users []UserVnext `json:"users"`
|
||||
}
|
||||
|
||||
type UserVnext struct {
|
||||
Encryption string `json:"encryption,omitempty"`
|
||||
Flow string `json:"flow,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
type ServerSetting struct {
|
||||
Password string `json:"password"`
|
||||
Level int `json:"level"`
|
||||
Address string `json:"address"`
|
||||
Port int `json:"port"`
|
||||
Flow string `json:"flow,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
}
|
||||
@@ -18,50 +18,46 @@ import (
|
||||
)
|
||||
|
||||
type SubService struct {
|
||||
address string
|
||||
showInfo bool
|
||||
remarkModel string
|
||||
address string
|
||||
showInfo bool
|
||||
remarkModel string
|
||||
|
||||
inboundService service.InboundService
|
||||
settingService service.SettingService
|
||||
}
|
||||
|
||||
func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string, []string, error) {
|
||||
func NewSubService(showInfo bool, remarkModel string) *SubService {
|
||||
return &SubService{
|
||||
showInfo: showInfo,
|
||||
remarkModel: remarkModel,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SubService) GetSubs(subId string, host string) ([]string, string, error) {
|
||||
s.address = host
|
||||
s.showInfo = showInfo
|
||||
var result []string
|
||||
var headers []string
|
||||
var header string
|
||||
var traffic xray.ClientTraffic
|
||||
var clientTraffics []xray.ClientTraffic
|
||||
inbounds, err := s.getInboundsBySubId(subId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
s.remarkModel, err = s.settingService.GetRemarkModel()
|
||||
if err != nil {
|
||||
s.remarkModel = "-ieo"
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// Prepare Inbounds
|
||||
for _, inbound := range inbounds {
|
||||
clients, err := s.inboundService.GetClients(inbound)
|
||||
if err != nil {
|
||||
logger.Error("SubService - GetSub: Unable to get clients from inbound")
|
||||
logger.Error("SubService - GetClients: Unable to get clients from inbound")
|
||||
}
|
||||
if clients == nil {
|
||||
continue
|
||||
}
|
||||
if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' {
|
||||
fallbackMaster, err := s.getFallbackMaster(inbound.Listen)
|
||||
listen, port, streamSettings, err := s.getFallbackMaster(inbound.Listen, inbound.StreamSettings)
|
||||
if err == nil {
|
||||
inbound.Listen = fallbackMaster.Listen
|
||||
inbound.Port = fallbackMaster.Port
|
||||
var stream map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
||||
var masterStream map[string]interface{}
|
||||
json.Unmarshal([]byte(fallbackMaster.StreamSettings), &masterStream)
|
||||
stream["security"] = masterStream["security"]
|
||||
stream["tlsSettings"] = masterStream["tlsSettings"]
|
||||
stream["externalProxy"] = masterStream["externalProxy"]
|
||||
modifiedStream, _ := json.MarshalIndent(stream, "", " ")
|
||||
inbound.StreamSettings = string(modifiedStream)
|
||||
inbound.Listen = listen
|
||||
inbound.Port = port
|
||||
inbound.StreamSettings = streamSettings
|
||||
}
|
||||
}
|
||||
for _, client := range clients {
|
||||
@@ -72,6 +68,8 @@ func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare statistics
|
||||
for index, clientTraffic := range clientTraffics {
|
||||
if index == 0 {
|
||||
traffic.Up = clientTraffic.Up
|
||||
@@ -93,11 +91,8 @@ func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string
|
||||
}
|
||||
}
|
||||
}
|
||||
headers = append(headers, fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000))
|
||||
updateInterval, _ := s.settingService.GetSubUpdates()
|
||||
headers = append(headers, fmt.Sprintf("%d", updateInterval))
|
||||
headers = append(headers, subId)
|
||||
return result, headers, nil
|
||||
header = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000)
|
||||
return result, header, nil
|
||||
}
|
||||
|
||||
func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
|
||||
@@ -126,7 +121,7 @@ func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, email stri
|
||||
return xray.ClientTraffic{}
|
||||
}
|
||||
|
||||
func (s *SubService) getFallbackMaster(dest string) (*model.Inbound, error) {
|
||||
func (s *SubService) getFallbackMaster(dest string, streamSettings string) (string, int, string, error) {
|
||||
db := database.GetDB()
|
||||
var inbound *model.Inbound
|
||||
err := db.Model(model.Inbound{}).
|
||||
@@ -134,9 +129,19 @@ func (s *SubService) getFallbackMaster(dest string) (*model.Inbound, error) {
|
||||
Where("EXISTS (SELECT * FROM json_each(settings, '$.fallbacks') WHERE json_extract(value, '$.dest') = ?)", dest).
|
||||
Find(&inbound).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", 0, "", err
|
||||
}
|
||||
return inbound, nil
|
||||
|
||||
var stream map[string]interface{}
|
||||
json.Unmarshal([]byte(streamSettings), &stream)
|
||||
var masterStream map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.StreamSettings), &masterStream)
|
||||
stream["security"] = masterStream["security"]
|
||||
stream["tlsSettings"] = masterStream["tlsSettings"]
|
||||
stream["externalProxy"] = masterStream["externalProxy"]
|
||||
modifiedStream, _ := json.MarshalIndent(stream, "", " ")
|
||||
|
||||
return inbound.Listen, inbound.Port, string(modifiedStream), nil
|
||||
}
|
||||
|
||||
func (s *SubService) getLink(inbound *model.Inbound, email string) string {
|
||||
|
||||
@@ -1038,6 +1038,12 @@ li.ant-select-dropdown-menu-item:empty:after {
|
||||
color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.dark .ant-message-notice-content {
|
||||
background-color: #222d42;
|
||||
border: 1px solid #2c3950;
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
}
|
||||
|
||||
.ant-input-number-handler-wrap {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -24,6 +24,7 @@ class AllSetting {
|
||||
this.subListen = "";
|
||||
this.subPort = "2096";
|
||||
this.subPath = "/sub/";
|
||||
this.subJsonPath = "/json/";
|
||||
this.subDomain = "";
|
||||
this.subCertFile = "";
|
||||
this.subKeyFile = "";
|
||||
@@ -31,6 +32,8 @@ class AllSetting {
|
||||
this.subEncrypt = true;
|
||||
this.subShowInfo = false;
|
||||
this.subURI = '';
|
||||
this.subJsonURI = '';
|
||||
this.subJsonFragment = '';
|
||||
|
||||
this.timeLocation = "Asia/Tehran";
|
||||
|
||||
|
||||
@@ -984,10 +984,6 @@ class Inbound extends XrayCommonClass {
|
||||
return [Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol);
|
||||
}
|
||||
|
||||
canSniffing() {
|
||||
return [Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.port = RandomUtil.randomIntRange(10000, 60000);
|
||||
this.listen = '';
|
||||
@@ -2100,7 +2096,7 @@ Inbound.WireguardSettings = class extends XrayCommonClass {
|
||||
}
|
||||
|
||||
addPeer() {
|
||||
this.peers.push(new Inbound.WireguardSettings.Peer());
|
||||
this.peers.push(new Inbound.WireguardSettings.Peer(null,null,'',['10.0.0.' + (this.peers.length+2)]));
|
||||
}
|
||||
|
||||
delPeer(index) {
|
||||
@@ -2128,7 +2124,7 @@ Inbound.WireguardSettings = class extends XrayCommonClass {
|
||||
};
|
||||
|
||||
Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
|
||||
constructor(privateKey, publicKey, psk='', allowedIPs=['10.0.0.0/24'], keepAlive=0) {
|
||||
constructor(privateKey, publicKey, psk='', allowedIPs=['10.0.0.2/32'], keepAlive=0) {
|
||||
super();
|
||||
this.privateKey = privateKey
|
||||
this.publicKey = publicKey;
|
||||
@@ -2136,6 +2132,9 @@ Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
|
||||
[this.publicKey, this.privateKey] = Object.values(Wireguard.generateKeypair())
|
||||
}
|
||||
this.psk = psk;
|
||||
allowedIPs.forEach((a,index) => {
|
||||
if (a.length>0 && !a.includes('/')) allowedIPs[index] += '/32';
|
||||
})
|
||||
this.allowedIPs = allowedIPs;
|
||||
this.keepAlive = keepAlive;
|
||||
}
|
||||
@@ -2151,6 +2150,9 @@ Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
|
||||
}
|
||||
|
||||
toJson() {
|
||||
this.allowedIPs.forEach((a,index) => {
|
||||
if (a.length>0 && !a.includes('/')) this.allowedIPs[index] += '/32';
|
||||
});
|
||||
return {
|
||||
privateKey: this.privateKey,
|
||||
publicKey: this.publicKey,
|
||||
|
||||
@@ -78,7 +78,6 @@ func (a *XraySettingController) warp(c *gin.Context) {
|
||||
resp, err = a.XraySettingService.RegWarp(skey, pkey)
|
||||
case "license":
|
||||
license := c.PostForm("license")
|
||||
println(license)
|
||||
resp, err = a.XraySettingService.SetWarpLicence(license)
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ type AllSetting struct {
|
||||
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"`
|
||||
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"`
|
||||
SubURI string `json:"subURI" form:"subURI"`
|
||||
SubJsonPath string `json:"subJsonPath" form:"subJsonPath"`
|
||||
SubJsonURI string `json:"subJsonURI" form:"subJsonURI"`
|
||||
SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"`
|
||||
}
|
||||
|
||||
func (s *AllSetting) CheckValid() error {
|
||||
@@ -103,6 +106,13 @@ func (s *AllSetting) CheckValid() error {
|
||||
s.SubPath += "/"
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(s.SubJsonPath, "/") {
|
||||
s.SubJsonPath = "/" + s.SubJsonPath
|
||||
}
|
||||
if !strings.HasSuffix(s.SubJsonPath, "/") {
|
||||
s.SubJsonPath += "/"
|
||||
}
|
||||
|
||||
_, err := time.LoadLocation(s.TimeLocation)
|
||||
if err != nil {
|
||||
return common.NewError("time location not exist:", s.TimeLocation)
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
<link rel="stylesheet" href="{{ .base_path }}assets/ant-design-vue@1.7.8/antd.min.css">
|
||||
<link rel="stylesheet" href="{{ .base_path }}assets/element-ui@2.15.0/theme-chalk/display.css">
|
||||
<link rel="stylesheet" href="{{ .base_path }}assets/css/custom.css?{{ .cur_ver }}">
|
||||
<link rel=”icon” type=”image/x-icon” href="{{ .base_path }}assets/favicon.ico">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="{{ .base_path }}assets/favicon.ico">
|
||||
<style>
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
@@ -30,4 +28,5 @@
|
||||
</style>
|
||||
<title>{{ .host }}-{{ i18n .title}}</title>
|
||||
</head>
|
||||
<div id="message"></div>
|
||||
{{end}}
|
||||
@@ -5,10 +5,16 @@
|
||||
width="300px" :class="themeSwitcher.currentTheme">
|
||||
<a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;" >{{ i18n "pages.inbounds.clickOnQRcode" }}</a-tag>
|
||||
<template v-if="app.subSettings.enable && qrModal.subId">
|
||||
<a-divider>Subscription</a-divider>
|
||||
<a-divider>{{ i18n "pages.settings.subSettings"}}</a-divider>
|
||||
<canvas @click="copyToClipboard('qrCode-sub',genSubLink(qrModal.client.subId))"
|
||||
id="qrCode-sub"
|
||||
style="width: 100%; height: 100%; display: flex; border-radius: 1rem;"></canvas>
|
||||
style="width: 100%; height: 100%; display: flex; border-radius: 1rem;">
|
||||
</canvas>
|
||||
<a-divider>{{ i18n "pages.settings.subSettings"}} Json</a-divider>
|
||||
<canvas @click="copyToClipboard('qrCode-subJson',genSubJsonLink(qrModal.client.subId))"
|
||||
id="qrCode-subJson"
|
||||
style="width: 100%; height: 100%; display: flex; border-radius: 1rem;">
|
||||
</canvas>
|
||||
</template>
|
||||
<a-divider>{{ i18n "pages.inbounds.client" }}</a-divider>
|
||||
<template v-for="(row, index) in qrModal.qrcodes">
|
||||
@@ -83,12 +89,16 @@
|
||||
},
|
||||
genSubLink(subID) {
|
||||
return app.subSettings.subURI+subID+'?name='+subID;
|
||||
},
|
||||
genSubJsonLink(subID) {
|
||||
return app.subSettings.subJsonURI+subID;
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
if (qrModal.client && qrModal.client.subId) {
|
||||
qrModal.subId = qrModal.client.subId;
|
||||
this.setQrCode("qrCode-sub", this.genSubLink(qrModal.subId));
|
||||
this.setQrCode("qrCode-subJson", this.genSubJsonLink(qrModal.subId));
|
||||
}
|
||||
qrModal.qrcodes.forEach((element, index) => {
|
||||
this.setQrCode("qrCode-" + index, element.link);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
{{define "textModal"}}
|
||||
<a-modal id="text-modal" v-model="txtModal.visible" :title="txtModal.title"
|
||||
:closable="true" ok-text='{{ i18n "copy" }}' cancel-text='{{ i18n "close" }}'
|
||||
:ok-button-props="{attrs:{id:'txt-modal-ok-btn'}}" :class="themeSwitcher.currentTheme">
|
||||
<a-button v-if="!ObjectUtil.isEmpty(txtModal.fileName)" type="primary" style="margin-bottom: 10px;"
|
||||
:href="'data:application/text;charset=utf-8,' + encodeURIComponent(txtModal.content)"
|
||||
:download="txtModal.fileName">
|
||||
{{ i18n "download" }} [[ txtModal.fileName ]]
|
||||
</a-button>
|
||||
:closable="true"
|
||||
:class="themeSwitcher.currentTheme">
|
||||
<template slot="footer">
|
||||
<a-button v-if="!ObjectUtil.isEmpty(txtModal.fileName)" icon="download"
|
||||
:href="'data:application/text;charset=utf-8,' + encodeURIComponent(txtModal.content)"
|
||||
:download="txtModal.fileName">[[ txtModal.fileName ]]
|
||||
</a-button>
|
||||
<a-button type="primary" id="copy-btn">{{ i18n "copy" }}</a-button>
|
||||
</template>
|
||||
<a-input type="textarea" v-model="txtModal.content"
|
||||
:autosize="{ minRows: 10, maxRows: 20}"></a-input>
|
||||
:autosize="{ minRows: 10, maxRows: 20}"></a-input>
|
||||
</a-modal>
|
||||
|
||||
<script>
|
||||
@@ -27,7 +29,7 @@
|
||||
this.visible = true;
|
||||
textModalApp.$nextTick(() => {
|
||||
if (this.clipboard === null) {
|
||||
this.clipboard = new ClipboardJS('#txt-modal-ok-btn', {
|
||||
this.clipboard = new ClipboardJS('#copy-btn', {
|
||||
text: () => this.content,
|
||||
});
|
||||
this.clipboard.on('success', () => {
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
}
|
||||
ObjectUtil.execute(clientsBulkModal.confirm, clients, clientsBulkModal.dbInbound.id);
|
||||
},
|
||||
show({ title='', okText='{{ i18n "sure" }}', dbInbound=null, confirm=(inbound, dbInbound)=>{} }) {
|
||||
show({ title='', okText='{{ i18n "confirm" }}', dbInbound=null, confirm=(inbound, dbInbound)=>{} }) {
|
||||
this.visible = true;
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
@@ -189,7 +189,7 @@
|
||||
clientsBulkModal.visible = false;
|
||||
clientsBulkModal.loading(false);
|
||||
},
|
||||
loading(loading) {
|
||||
loading(loading=true) {
|
||||
clientsBulkModal.confirmLoading = loading;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
|
||||
}
|
||||
},
|
||||
show({ title='', okText='{{ i18n "sure" }}', index=null, dbInbound=null, confirm=()=>{}, isEdit=false }) {
|
||||
show({ title='', okText='{{ i18n "confirm" }}', index=null, dbInbound=null, confirm=()=>{}, isEdit=false }) {
|
||||
this.visible = true;
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
@@ -70,7 +70,7 @@
|
||||
clientModal.visible = false;
|
||||
clientModal.loading(false);
|
||||
},
|
||||
loading(loading) {
|
||||
loading(loading=true) {
|
||||
clientModal.confirmLoading = loading;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
toggleTheme() {
|
||||
this.isDarkTheme = !this.isDarkTheme;
|
||||
localStorage.setItem('dark-mode', this.isDarkTheme);
|
||||
document.getElementById('message').className = themeSwitcher.currentTheme;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -29,6 +30,10 @@
|
||||
props: [],
|
||||
template: `{{template "component/themeSwitchTemplate"}}`,
|
||||
data: () => ({ themeSwitcher }),
|
||||
mounted() {
|
||||
this.$message.config({getContainer: () => document.getElementById('message')});
|
||||
document.getElementById('message').className = themeSwitcher.currentTheme;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
86
web/html/xui/dns_modal.html
Normal file
86
web/html/xui/dns_modal.html
Normal file
@@ -0,0 +1,86 @@
|
||||
{{define "dnsModal"}}
|
||||
<a-modal id="dns-modal" v-model="dnsModal.visible" :title="dnsModal.title" @ok="dnsModal.ok"
|
||||
:closable="true" :mask-closable="false"
|
||||
:ok-text="dnsModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.address" }}'>
|
||||
<a-input v-model.trim="dnsModal.dnsServer.address"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.dns.domains" }}'>
|
||||
<a-button size="small" type="primary" @click="dnsModal.dnsServer.domains.push('')">+</a-button>
|
||||
<template v-for="(domain, index) in dnsModal.dnsServer.domains">
|
||||
<a-input v-model.trim="dnsModal.dnsServer.domains[index]">
|
||||
<a-button size="small" slot="addonAfter" @click="dnsModal.dnsServer.domains.splice(index,1)">-</a-button>
|
||||
</a-input>
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.dns.strategy" }}' v-if="isAdvanced">
|
||||
<a-select
|
||||
v-model="dnsModal.dnsServer.queryStrategy"
|
||||
style="width: 100%"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']">
|
||||
[[ l ]]
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<script>
|
||||
const dnsModal = {
|
||||
title: '',
|
||||
visible: false,
|
||||
okText: '{{ i18n "confirm" }}',
|
||||
isEdit: false,
|
||||
confirm: null,
|
||||
dnsServer: {
|
||||
address: "localhost",
|
||||
domains: [],
|
||||
queryStrategy: 'UseIP',
|
||||
},
|
||||
ok() {
|
||||
domains = dnsModal.dnsServer.domains.filter(d => d.length>0);
|
||||
dnsModal.dnsServer.domains = domains;
|
||||
newDnsServer = domains.length > 0 ? dnsModal.dnsServer : dnsModal.dnsServer.address;
|
||||
ObjectUtil.execute(dnsModal.confirm, newDnsServer);
|
||||
},
|
||||
show({ title='', okText='{{ i18n "confirm" }}', dnsServer, confirm=(dnsServer)=>{}, isEdit=false }) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.confirm = confirm;
|
||||
this.visible = true;
|
||||
if(isEdit) {
|
||||
if (typeof dnsServer == 'object'){
|
||||
this.dnsServer = dnsServer;
|
||||
} else {
|
||||
this.dnsServer.address = dnsServer?? '';
|
||||
}
|
||||
} else {
|
||||
this.dnsServer = {
|
||||
address: "localhost",
|
||||
domains: [],
|
||||
queryStrategy: 'UseIP',
|
||||
}
|
||||
}
|
||||
this.isEdit = isEdit;
|
||||
},
|
||||
close() {
|
||||
dnsModal.visible = false;
|
||||
},
|
||||
};
|
||||
|
||||
new Vue({
|
||||
delimiters: ['[[', ']]'],
|
||||
el: '#dns-modal',
|
||||
data: {
|
||||
dnsModal: dnsModal,
|
||||
},
|
||||
computed: {
|
||||
isAdvanced: {
|
||||
get: function () { return dnsModal.dnsServer.domains.length>0 }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
57
web/html/xui/fakedns_modal.html
Normal file
57
web/html/xui/fakedns_modal.html
Normal file
@@ -0,0 +1,57 @@
|
||||
{{define "fakednsModal"}}
|
||||
<a-modal id="fakedns-modal" v-model="fakednsModal.visible" :title="fakednsModal.title" @ok="fakednsModal.ok"
|
||||
:closable="true" :mask-closable="false"
|
||||
:ok-text="fakednsModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.xray.fakedns.ipPool" }}'>
|
||||
<a-input v-model.trim="fakednsModal.fakeDns.ipPool"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.fakedns.poolSize" }}'>
|
||||
<a-input type="number" min="1" v-model.trim="fakednsModal.fakeDns.poolSize"></a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<script>
|
||||
const fakednsModal = {
|
||||
title: '',
|
||||
visible: false,
|
||||
okText: '{{ i18n "confirm" }}',
|
||||
isEdit: false,
|
||||
confirm: null,
|
||||
fakeDns: {
|
||||
ipPool: "198.18.0.0/16",
|
||||
poolSize: 65535,
|
||||
},
|
||||
ok() {
|
||||
ObjectUtil.execute(fakednsModal.confirm, fakednsModal.fakeDns);
|
||||
},
|
||||
show({ title='', okText='{{ i18n "confirm" }}', fakeDns, confirm=(fakeDns)=>{}, isEdit=false }) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.confirm = confirm;
|
||||
this.visible = true;
|
||||
if(isEdit) {
|
||||
this.fakeDns = fakeDns;
|
||||
} else {
|
||||
this.fakeDns = {
|
||||
ipPool: "198.18.0.0/16",
|
||||
poolSize: 65535,
|
||||
}
|
||||
}
|
||||
this.isEdit = isEdit;
|
||||
},
|
||||
close() {
|
||||
fakednsModal.visible = false;
|
||||
},
|
||||
};
|
||||
|
||||
new Vue({
|
||||
delimiters: ['[[', ']]'],
|
||||
el: '#fakedns-modal',
|
||||
data: {
|
||||
fakednsModal: fakednsModal,
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -112,7 +112,7 @@
|
||||
</template>
|
||||
|
||||
<!-- sniffing -->
|
||||
<template v-if="inbound.canSniffing()">
|
||||
<template>
|
||||
{{template "form/sniffing"}}
|
||||
</template>
|
||||
{{end}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{{define "form/sniffing"}}
|
||||
<a-divider style="margin:0;"></a-divider>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
Sniffing
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
</a-form-item>
|
||||
<a-form-item label="Min/Max Version">
|
||||
<a-input-group compact>
|
||||
<a-select v-model="inbound.stream.tls.minVersion" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select v-model="inbound.stream.tls.minVersion" style="width:100px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model="inbound.stream.tls.maxVersion" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select v-model="inbound.stream.tls.maxVersion" style="width:100px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-input-group>
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
<template v-if="app.subSettings.enable && infoModal.clientSettings.subId">
|
||||
<a-divider>Subscription URL</a-divider>
|
||||
<a-row>
|
||||
<a-col :sx="24" :md="22"><a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a></a-col>
|
||||
<a-col :sx="24" :md="22">SUB: <a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a></a-col>
|
||||
<a-col :sx="24" :md="2" style="text-align: right;">
|
||||
<a-tooltip title='{{ i18n "copy" }}'>
|
||||
<button class="ant-btn ant-btn-primary" id="copy-sub-link" @click="copyToClipboard('copy-sub-link', infoModal.subLink)">
|
||||
@@ -171,6 +171,16 @@
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row>
|
||||
<a-col :sx="24" :md="22">JSON: <a :href="[[ infoModal.subJsonLink ]]" target="_blank">[[ infoModal.subJsonLink ]]</a></a-col>
|
||||
<a-col :sx="24" :md="2" style="text-align: right;">
|
||||
<a-tooltip title='{{ i18n "copy" }}'>
|
||||
<button class="ant-btn ant-btn-primary" id="copy-subJson-link" @click="copyToClipboard('copy-subJson-link', infoModal.subJsonLink)">
|
||||
<a-icon type="snippets"></a-icon>
|
||||
</button>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<template v-if="app.tgBotEnable && infoModal.clientSettings.tgId">
|
||||
<a-divider>Telegram ID</a-divider>
|
||||
@@ -341,6 +351,7 @@
|
||||
index: null,
|
||||
isExpired: false,
|
||||
subLink: '',
|
||||
subJsonLink: '',
|
||||
tgLink: '',
|
||||
show(dbInbound, index) {
|
||||
this.index = index;
|
||||
@@ -357,6 +368,7 @@
|
||||
if (this.clientSettings) {
|
||||
if (this.clientSettings.subId) {
|
||||
this.subLink = this.genSubLink(this.clientSettings.subId);
|
||||
this.subJsonLink = this.genSubJsonLink(this.clientSettings.subId);
|
||||
}
|
||||
if (this.clientSettings.tgId) {
|
||||
this.tgLink = "https://t.me/" + this.clientSettings.tgId;
|
||||
@@ -369,6 +381,9 @@
|
||||
},
|
||||
genSubLink(subID) {
|
||||
return app.subSettings.subURI+subID+'?name='+subID;
|
||||
},
|
||||
genSubJsonLink(subID) {
|
||||
return app.subSettings.subJsonURI+subID+'?name='+subID;;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
title: '',
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
okText: '{{ i18n "sure" }}',
|
||||
okText: '{{ i18n "confirm" }}',
|
||||
isEdit: false,
|
||||
confirm: null,
|
||||
inbound: new Inbound(),
|
||||
@@ -18,7 +18,7 @@
|
||||
ok() {
|
||||
ObjectUtil.execute(inModal.confirm, inModal.inbound, inModal.dbInbound);
|
||||
},
|
||||
show({ title='', okText='{{ i18n "sure" }}', inbound=null, dbInbound=null, confirm=(inbound, dbInbound)=>{}, isEdit=false }) {
|
||||
show({ title='', okText='{{ i18n "confirm" }}', inbound=null, dbInbound=null, confirm=(inbound, dbInbound)=>{}, isEdit=false }) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
if (inbound) {
|
||||
@@ -39,7 +39,7 @@
|
||||
inModal.visible = false;
|
||||
inModal.loading(false);
|
||||
},
|
||||
loading(loading) {
|
||||
loading(loading=true) {
|
||||
inModal.confirmLoading = loading;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
<a-icon type="export"></a-icon>
|
||||
{{ i18n "pages.inbounds.export" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="subs">
|
||||
<a-menu-item key="subs" v-if="subSettings.enable">
|
||||
<a-icon type="export"></a-icon>
|
||||
{{ i18n "pages.inbounds.export" }} - {{ i18n "pages.settings.subSettings" }}
|
||||
</a-menu-item>
|
||||
@@ -224,7 +224,7 @@
|
||||
<a-icon type="export"></a-icon>
|
||||
{{ i18n "pages.inbounds.export"}}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="subs">
|
||||
<a-menu-item key="subs" v-if="subSettings.enable">
|
||||
<a-icon type="export"></a-icon>
|
||||
{{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }}
|
||||
</a-menu-item>
|
||||
@@ -564,7 +564,8 @@
|
||||
refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000,
|
||||
subSettings: {
|
||||
enable : false,
|
||||
subURI : ''
|
||||
subURI : '',
|
||||
subJsonURI : '',
|
||||
},
|
||||
remarkModel: '-ieo',
|
||||
tgBotEnable: false,
|
||||
@@ -609,7 +610,8 @@
|
||||
this.tgBotEnable = tgBotEnable;
|
||||
this.subSettings = {
|
||||
enable : subEnable,
|
||||
subURI: subURI
|
||||
subURI: subURI,
|
||||
subJsonURI: subJsonURI
|
||||
};
|
||||
this.pageSize = pageSize;
|
||||
this.remarkModel = remarkModel;
|
||||
@@ -646,8 +648,12 @@
|
||||
clientCount = clients.length;
|
||||
if (dbInbound.enable) {
|
||||
clients.forEach(client => {
|
||||
client.enable ? active.push(client.email) : deactive.push(client.email);
|
||||
if(this.isClientOnline(client.email)) online.push(client.email);
|
||||
if (client.enable) {
|
||||
active.push(client.email);
|
||||
if(this.isClientOnline(client.email)) online.push(client.email);
|
||||
} else {
|
||||
deactive.push(client.email);
|
||||
}
|
||||
});
|
||||
clientStats.forEach(client => {
|
||||
if (!client.enable) {
|
||||
@@ -850,8 +856,8 @@
|
||||
port: RandomUtil.randomIntRange(10000, 60000),
|
||||
protocol: baseInbound.protocol,
|
||||
settings: Inbound.Settings.getSettings(baseInbound.protocol).toString(),
|
||||
streamSettings: baseInbound.stream.toString(),
|
||||
sniffing: baseInbound.canSniffing() ? baseInbound.sniffing.toString() : '{}',
|
||||
streamSettings: baseInbound.stream.toString(),
|
||||
sniffing: baseInbound.sniffing.toString(),
|
||||
};
|
||||
await this.submit('/xui/inbound/add', data, inModal);
|
||||
},
|
||||
@@ -870,7 +876,7 @@
|
||||
settings: inbound.settings.toString(),
|
||||
};
|
||||
if (inbound.canEnableStream()) data.streamSettings = inbound.stream.toString();
|
||||
if (inbound.canSniffing()) data.sniffing = inbound.sniffing.toString();
|
||||
data.sniffing = inbound.sniffing.toString();
|
||||
|
||||
await this.submit('/xui/inbound/add', data, inModal);
|
||||
},
|
||||
@@ -889,7 +895,7 @@
|
||||
settings: inbound.settings.toString(),
|
||||
};
|
||||
if (inbound.canEnableStream()) data.streamSettings = inbound.stream.toString();
|
||||
if (inbound.canSniffing()) data.sniffing = inbound.sniffing.toString();
|
||||
data.sniffing = inbound.sniffing.toString();
|
||||
|
||||
await this.submit(`/xui/inbound/update/${dbInbound.id}`, data, inModal);
|
||||
},
|
||||
|
||||
@@ -83,23 +83,14 @@
|
||||
<transition name="list" appear>
|
||||
<a-row>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
<strong>{{ i18n "pages.index.machineInfo" }}:</strong>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.hostname" }}
|
||||
</template>
|
||||
<a-tag color="blue" style="margin-right: 3px;">[[ status.hostInfo.hostname ]]</a-tag>
|
||||
<a-card hoverable>
|
||||
<strong>{{ i18n "pages.inbounds.stream.tcp.version" }}:</strong>
|
||||
<a href="https://github.com/alireza0/x-ui/releases" target="_blank">
|
||||
<a-tag color="purple" style="cursor: pointer;">X-UI {{ .cur_ver }}</a-tag>
|
||||
</a>
|
||||
<a-tooltip title='{{ i18n "pages.index.xraySwitch" }}'>
|
||||
<a-tag color="purple" style="cursor: pointer;" @click="openSelectV2rayVersion">Xray [[ status.xray.version ]]</a-tag>
|
||||
</a-tooltip>
|
||||
<template v-if="status.hostInfo.ipv4">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
IPv4:<br>[[ status.hostInfo.ipv4 ]]<br>IPv6:<br>[[ status.hostInfo.ipv6 ]]
|
||||
</template>
|
||||
<a-tag color="blue" style="margin-right: 3px;">IPv4/v6</a-tag>
|
||||
</a-tooltip>
|
||||
<a href="https://github.com/alireza0/x-ui/releases" target="_blank"><a-tag color="purple">X-UI {{ .cur_ver }}</a-tag></a>
|
||||
</template>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
@@ -134,10 +125,7 @@
|
||||
<a-icon type="exclamation-circle"></a-icon>
|
||||
</a-popover>
|
||||
<a-tag color="purple" style="cursor: pointer; margin-right: 3px;" @click="stopXrayService">{{ i18n "pages.index.stopXray" }}</a-tag>
|
||||
<a-tag color="purple" style="cursor: pointer; margin-right: 3px;" @click="restartXrayService">{{ i18n "pages.index.restartXray" }}</a-tag>
|
||||
<a-tooltip title='{{ i18n "pages.index.xraySwitch" }}'>
|
||||
<a-tag color="purple" style="cursor: pointer;" @click="openSelectV2rayVersion">[[ status.xray.version ]]</a-tag>
|
||||
</a-tooltip>
|
||||
<a-tag color="purple" style="cursor: pointer; margin-right: 3px;" @click="restartXrayService">{{ i18n "pages.index.restartXray" }}</a-tag>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
@@ -154,20 +142,43 @@
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.systemLoadDesc" }}
|
||||
</template>
|
||||
<a-tag color="blue">[[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]]</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-tag color="blue">[[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]]</a-tag>
|
||||
</a-tooltip>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
<strong>{{ i18n "usage" }}:</strong>
|
||||
<a-tooltip>
|
||||
<a-tag color="blue" style="margin-right: 3px;">RAM [[ sizeFormat(status.appStats.mem) ]]</a-tag>
|
||||
</a-tooltip>
|
||||
<a-tag color="blue">Threads [[ status.appStats.threads ]]</a-tag>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
<strong>{{ i18n "pages.index.serverInfo" }}:</strong>
|
||||
<a-tooltip>
|
||||
<a-tag color="blue">Threads [[ status.appStats.threads ]]</a-tag>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.hostname" }}
|
||||
</template>
|
||||
<a-tag color="blue" style="margin-right: 3px;">[[ status.hostInfo.hostname ]]</a-tag>
|
||||
</a-tooltip>
|
||||
<template v-if="status.hostInfo.ipv4">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
[[ status.hostInfo.ipv4 ]]
|
||||
</template>
|
||||
<a-tag color="blue" style="margin-right: 3px;">IPv4</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-if="status.hostInfo.ipv6">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
[[ status.hostInfo.ipv6 ]]
|
||||
</template>
|
||||
<a-tag color="blue" style="margin-right: 3px;">IPv6</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
@@ -179,7 +190,7 @@
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.connectionTcpCountDesc" }}
|
||||
</template>
|
||||
<strong>TCP:</Strong> [[ status.tcpCount ]]
|
||||
<strong>TCP:</Strong> <a-tag>[[ status.tcpCount ]]</a-tag>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
@@ -188,7 +199,7 @@
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.connectionUdpCountDesc" }}
|
||||
</template>
|
||||
<strong>UDP:</strong> [[ status.udpCount ]]
|
||||
<strong>UDP:</strong> <a-tag>[[ status.udpCount ]]</a-tag>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -203,7 +214,7 @@
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.upSpeed" }}
|
||||
</template>
|
||||
<strong>UL:</strong> [[ sizeFormat(status.netIO.up) ]]/s
|
||||
<strong>Up:</strong> [[ sizeFormat(status.netIO.up) ]]/s
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
@@ -212,7 +223,7 @@
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.downSpeed" }}
|
||||
</template>
|
||||
<strong>DL:</strong> [[ sizeFormat(status.netIO.down) ]]/s
|
||||
<strong>Down:</strong> [[ sizeFormat(status.netIO.down) ]]/s
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -263,52 +274,53 @@
|
||||
</template>
|
||||
</a-modal>
|
||||
|
||||
<a-modal id="log-modal" v-model="logModal.visible" title="Logs"
|
||||
:closable="true" @ok="() => logModal.visible = false" @cancel="() => logModal.visible = false"
|
||||
<a-modal id="log-modal" v-model="logModal.visible"
|
||||
:closable="true" @cancel="() => logModal.visible = false"
|
||||
:class="themeSwitcher.currentTheme"
|
||||
width="800px"
|
||||
footer="">
|
||||
width="800px" footer="">
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.logs" }}
|
||||
<a-icon :spin="logModal.loading"
|
||||
type="sync"
|
||||
style="vertical-align: middle; margin-left: 10px;"
|
||||
:disabled="logModal.loading"
|
||||
@click="openLogs()">
|
||||
</a-icon>
|
||||
</template>
|
||||
<a-form layout="inline">
|
||||
<a-form-item label="Count">
|
||||
<a-select v-model="logModal.rows"
|
||||
style="width: 80px"
|
||||
@change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="10">10</a-select-option>
|
||||
<a-select-option value="20">20</a-select-option>
|
||||
<a-select-option value="50">50</a-select-option>
|
||||
<a-select-option value="100">100</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Log Level">
|
||||
<a-select v-model="logModal.level"
|
||||
style="width: 120px"
|
||||
@change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="debug">Debug</a-select-option>
|
||||
<a-select-option value="info">Info</a-select-option>
|
||||
<a-select-option value="warning">Warning</a-select-option>
|
||||
<a-select-option value="err">Error</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="SysLog">
|
||||
<a-checkbox v-model="logModal.syslog" @change="openLogs()"></a-checkbox>
|
||||
<a-form-item>
|
||||
<a-input-group compact>
|
||||
<a-select v-model="logModal.rows" style="width:70px;"
|
||||
@change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="10">10</a-select-option>
|
||||
<a-select-option value="20">20</a-select-option>
|
||||
<a-select-option value="50">50</a-select-option>
|
||||
<a-select-option value="100">100</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model="logModal.level" style="width:100px;"
|
||||
@change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="debug">Debug</a-select-option>
|
||||
<a-select-option value="info">Info</a-select-option>
|
||||
<a-select-option value="warning">Warning</a-select-option>
|
||||
<a-select-option value="err">Error</a-select-option>
|
||||
</a-select>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button class="ant-btn ant-btn-primary" :loading="logModal.loading" @click="openLogs()"><a-icon :spin="logModal.loading" type="sync"></a-icon> Reload</a-button>
|
||||
<a-checkbox v-model="logModal.syslog" @change="openLogs()">SysLog</a-checkbox>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button type="primary" style="margin-bottom: 10px;"
|
||||
<a-form-item style="float: right;">
|
||||
<a-button type="primary" icon="download"
|
||||
:href="'data:application/text;charset=utf-8,' + encodeURIComponent(logModal.logs)" download="x-ui.log">
|
||||
{{ i18n "download" }} x-ui.log
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-form>
|
||||
<div class="ant-input" style="height: auto; max-height: 500px; overflow: auto;" v-html="logModal.formattedLogs"></div>
|
||||
</a-modal>
|
||||
|
||||
<a-modal id="backup-modal" v-model="backupModal.visible" :title="backupModal.title"
|
||||
:closable="true"
|
||||
:class="themeSwitcher.currentTheme"
|
||||
@ok="() => backupModal.hide()" @cancel="() => backupModal.hide()">
|
||||
:closable="true" footer=""
|
||||
:class="themeSwitcher.currentTheme">
|
||||
<a-alert type="warning" style="margin-bottom: 10px; width: fit-content"
|
||||
:message="backupModal.description"
|
||||
show-icon
|
||||
@@ -400,7 +412,7 @@
|
||||
this.xray = data.xray;
|
||||
switch (this.xray.state) {
|
||||
case State.Running:
|
||||
this.xray.color = '#3dbd7d';
|
||||
this.xray.color = 'blue';
|
||||
break;
|
||||
case State.Stop:
|
||||
this.xray.color = "orange";
|
||||
@@ -436,7 +448,7 @@
|
||||
show(logs) {
|
||||
this.visible = true;
|
||||
this.logs = logs;
|
||||
this.formattedLogs = this.logs.length > 0 ? this.formatLogs(this.logs) : "No Record...";
|
||||
this.formattedLogs = this.logs?.length > 0 ? this.formatLogs(this.logs) : "No Record...";
|
||||
},
|
||||
formatLogs(logs) {
|
||||
let formattedLogs = '';
|
||||
|
||||
@@ -51,6 +51,16 @@
|
||||
show-icon closable
|
||||
>
|
||||
</a-alert>
|
||||
<a-alert type="error" v-if="confAlerts.length>0" style="margin-bottom: 10px"
|
||||
message='{{ i18n "secAlertTitle" }}'
|
||||
color="red"
|
||||
show-icon closable
|
||||
>
|
||||
<template slot="description">
|
||||
{{ i18n "secAlertConf" }}
|
||||
<li v-for="a in confAlerts">- [[ a ]]</li>
|
||||
</template>
|
||||
</a-alert>
|
||||
</transition>
|
||||
<a-space direction="vertical">
|
||||
<a-card hoverable style="margin-bottom: .5rem;">
|
||||
@@ -201,6 +211,17 @@
|
||||
<setting-list-item type="number" title='{{ i18n "pages.settings.subUpdates"}}' desc='{{ i18n "pages.settings.subUpdatesDesc"}}' v-model="allSetting.subUpdates"></setting-list-item>
|
||||
</a-list>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="5" tab='{{ i18n "pages.settings.subSettings" }} Json' v-if="allSetting.subEnable">
|
||||
<a-list item-layout="horizontal">
|
||||
<setting-list-item type="text" title='{{ i18n "pages.settings.subPath"}}' desc='{{ i18n "pages.settings.subPathDesc"}}' v-model="allSetting.subJsonPath"></setting-list-item>
|
||||
<setting-list-item type="text" title='{{ i18n "pages.settings.subURI"}}' desc='{{ i18n "pages.settings.subURIDesc"}}' v-model="allSetting.subJsonURI" placeholder="(http|https)://domain[:port]/path/"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.fragment"}}' desc='{{ i18n "pages.settings.fragmentDesc"}}' v-model="fragment"></setting-list-item>
|
||||
<template v-if="fragment">
|
||||
<setting-list-item type="text" title='length' v-model="fragmentLength" placeholder="100-200"></setting-list-item>
|
||||
<setting-list-item type="text" title='Interval' v-model="fragmentInterval" placeholder="10-20"></setting-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-space>
|
||||
</a-spin>
|
||||
@@ -230,6 +251,24 @@
|
||||
remarkModels: {i:'Inbound',e:'Email',o:'Other'},
|
||||
remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'],
|
||||
remarkSample: '',
|
||||
defaultFragment: {
|
||||
tag: "fragment",
|
||||
protocol: "freedom",
|
||||
settings: {
|
||||
domainStrategy: "AsIs",
|
||||
fragment: {
|
||||
packets: "tlshello",
|
||||
length: "100-200",
|
||||
interval: "10-20"
|
||||
}
|
||||
},
|
||||
streamSettings: {
|
||||
sockopt: {
|
||||
tcpKeepAliveIdle: 100,
|
||||
TcpNoDelay: true
|
||||
}
|
||||
}
|
||||
},
|
||||
get remarkModel() {
|
||||
rm = this.allSetting.remarkModel;
|
||||
return rm.length>1 ? rm.substring(1).split('') : [];
|
||||
@@ -290,7 +329,7 @@
|
||||
title: '{{ i18n "pages.settings.restartPanel" }}',
|
||||
content: '{{ i18n "pages.settings.restartPanelDesc" }}',
|
||||
class: themeSwitcher.currentTheme,
|
||||
okText: '{{ i18n "sure" }}',
|
||||
okText: '{{ i18n "confirm" }}',
|
||||
cancelText: '{{ i18n "cancel" }}',
|
||||
onOk: () => resolve(),
|
||||
});
|
||||
@@ -310,6 +349,50 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fragment: {
|
||||
get: function() { return this.allSetting?.subJsonFragment != ""; },
|
||||
set: function (v) {
|
||||
this.allSetting.subJsonFragment = v ? JSON.stringify(this.defaultFragment) : "";
|
||||
}
|
||||
},
|
||||
fragmentLength: {
|
||||
get: function() { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.length : ""; },
|
||||
set: function(v) {
|
||||
if (v != ""){
|
||||
newFragment = JSON.parse(this.allSetting.subJsonFragment);
|
||||
newFragment.settings.fragment.length = v;
|
||||
this.allSetting.subJsonFragment = JSON.stringify(newFragment);
|
||||
}
|
||||
}
|
||||
},
|
||||
fragmentInterval: {
|
||||
get: function() { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.interval : ""; },
|
||||
set: function(v) {
|
||||
if (v != ""){
|
||||
newFragment = JSON.parse(this.allSetting.subJsonFragment);
|
||||
newFragment.settings.fragment.interval = v;
|
||||
this.allSetting.subJsonFragment = JSON.stringify(newFragment);
|
||||
}
|
||||
}
|
||||
},
|
||||
confAlerts: {
|
||||
get: function() {
|
||||
if (!this.allSetting) return [];
|
||||
var alerts = []
|
||||
if (this.allSetting.port == 54321) alerts.push('{{ i18n "pages.settings.panelPort"}}');
|
||||
panelPath = window.location.pathname.split('/').length<4
|
||||
if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "pages.settings.panelConfig"}} {{ i18n "pages.settings.panelUrlPath"}}');
|
||||
if (this.allSetting.subEnable) {
|
||||
subPath = this.allSetting.subURI.length >0 ? new URL(this.allSetting.subURI).pathname : this.allSetting.subPath;
|
||||
if (subPath == '/sub/') alerts.push('{{ i18n "pages.settings.subSettings"}} {{ i18n "pages.settings.subPath"}}');
|
||||
subJsonPath = this.allSetting.subJsonURI.length >0 ? new URL(this.allSetting.subJsonURI).pathname : this.allSetting.subJsonPath;
|
||||
if (subJsonPath == '/json/') alerts.push('JSON {{ i18n "pages.settings.subPath"}}');
|
||||
}
|
||||
return alerts
|
||||
}
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (window.location.protocol !== "https:") {
|
||||
this.showAlert = true;
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
this.visible = false;
|
||||
this.loading(false);
|
||||
},
|
||||
loading(loading) {
|
||||
loading(loading=true) {
|
||||
this.confirmLoading = loading;
|
||||
},
|
||||
async getData(){
|
||||
|
||||
@@ -141,13 +141,11 @@
|
||||
description='{{ i18n "pages.xray.RoutingStrategyDesc" }}'/>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<a-select
|
||||
v-model="routingStrategy"
|
||||
style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="s in routingDomainStrategies" :value="s">[[ s ]]</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<a-select
|
||||
v-model="routingStrategy"
|
||||
style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="s in routingDomainStrategies" :value="s">[[ s ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
@@ -339,6 +337,14 @@
|
||||
[[ rule.outboundTag ]]
|
||||
</a-popover>
|
||||
</template>
|
||||
<template slot="balancer" slot-scope="text, rule, index">
|
||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<p v-if="rule.balancerTag">Balancer Tag: [[ rule.balancerTag ]]</p>
|
||||
</template>
|
||||
[[ rule.balancerTag ]]
|
||||
</a-popover>
|
||||
</template>
|
||||
<template slot="info" slot-scope="text, rule, index">
|
||||
<a-popover placement="bottomRight"
|
||||
v-if="(rule.source+rule.sourcePort+rule.network+rule.protocol+rule.attrs+rule.ip+rule.domain+rule.port).length>0"
|
||||
@@ -377,6 +383,10 @@
|
||||
<td>Port</td>
|
||||
<td><a-tag color="green" v-for="r in rule.port.split(',')">[[ r ]]</a-tag></td>
|
||||
</tr>
|
||||
<tr v-if="rule.balancerTag">
|
||||
<td>Balancer Tag</td>
|
||||
<td><a-tag color="blue">[[ rule.balancerTag ]]</a-tag></td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
<a-button shape="round" size="small" style="font-size: 14px; padding: 0 10px;">
|
||||
@@ -458,6 +468,124 @@
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="tpl-5" tab='{{ i18n "pages.xray.Balancers"}}' style="padding-top: 20px;" force-render="true">
|
||||
<a-button type="primary" icon="plus" @click="addBalancer()" style="margin-bottom: 10px;">{{ i18n "pages.xray.balancer.addBalancer"}}</a-button>
|
||||
<a-table :columns="balancerColumns" bordered v-if="balancersData.length>0"
|
||||
:row-key="r => r.key"
|
||||
:data-source="balancersData"
|
||||
:scroll="isMobile ? {} : { x: 200 }"
|
||||
:pagination="false"
|
||||
:indent-size="0"
|
||||
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
|
||||
<template slot="action" slot-scope="text, balancer, index">
|
||||
[[ index+1 ]]
|
||||
<a-dropdown :trigger="['click']">
|
||||
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
|
||||
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||
<a-menu-item @click="editBalancer(index)">
|
||||
<a-icon type="edit"></a-icon>
|
||||
{{ i18n "edit" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="deleteBalancer(index)">
|
||||
<span style="color: #FF4D4F">
|
||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
||||
</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template slot="strategy" slot-scope="text, balancer, index">
|
||||
<a-tag style="margin:0;" v-if="balancer.strategy=='random'" color="purple">Random</a-tag>
|
||||
<a-tag style="margin:0;" v-if="balancer.strategy=='roundRobin'" color="green">Round Robin</a-tag>
|
||||
</template>
|
||||
<template slot="selector" slot-scope="text, balancer, index">
|
||||
<a-tag class="info-large-tag" style="margin:1;" v-for="sel in balancer.selector">[[ sel ]]</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="tpl-6" tab='DNS' style="padding-top: 20px;" force-render="true">
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.dns.enable" }}' desc='{{ i18n "pages.xray.dns.enableDesc" }}' v-model="enableDNS"></setting-list-item>
|
||||
<template v-if="enableDNS">
|
||||
<a-list-item>
|
||||
<a-row style="padding: 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.xray.dns.strategy" }}' description='{{ i18n "pages.xray.dns.strategyDesc" }}' />
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-select
|
||||
v-model="dnsStrategy"
|
||||
style="width: 100%"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']">
|
||||
[[ l ]]
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
<a-divider>DNS</a-divider>
|
||||
<a-button type="primary" icon="plus" @click="addDNSServer()" style="margin-bottom: 10px;">{{ i18n "pages.xray.dns.add" }}</a-button>
|
||||
<a-table :columns="dnsColumns" bordered v-if="dnsServers.length>0"
|
||||
:row-key="r => r.key"
|
||||
:data-source="dnsServers"
|
||||
:scroll="isMobile ? {} : { x: 200 }"
|
||||
:pagination="false"
|
||||
:indent-size="0"
|
||||
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
|
||||
<template slot="action" slot-scope="text,dns,index">
|
||||
[[ index+1 ]]
|
||||
<a-dropdown :trigger="['click']">
|
||||
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
|
||||
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||
<a-menu-item @click="editDNSServer(index)">
|
||||
<a-icon type="edit"></a-icon>
|
||||
{{ i18n "edit" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="deleteDNSServer(index)">
|
||||
<span style="color: #FF4D4F">
|
||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
||||
</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template slot="address" slot-scope="dns,index">
|
||||
<span v-if="typeof dns == 'object'">[[ dns.address ]]</span>
|
||||
<span v-else>[[ dns ]]</span>
|
||||
</template>
|
||||
<template slot="domain" slot-scope="dns,index">
|
||||
<span v-if="typeof dns == 'object'">[[ dns.domains.join(",") ]]</span>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-divider>Fake DNS</a-divider>
|
||||
<a-button type="primary" icon="plus" @click="addFakedns()" style="margin-bottom: 10px;">{{ i18n "pages.xray.fakedns.add" }}</a-button>
|
||||
<a-table :columns="fakednsColumns" bordered v-if="fakeDns && fakeDns.length>0"
|
||||
:row-key="r => r.key"
|
||||
:data-source="fakeDns"
|
||||
:scroll="isMobile ? {} : { x: 200 }"
|
||||
:pagination="false"
|
||||
:indent-size="0"
|
||||
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
|
||||
<template slot="action" slot-scope="text,fakedns,index">
|
||||
[[ index+1 ]]
|
||||
<a-dropdown :trigger="['click']">
|
||||
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
|
||||
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||
<a-menu-item @click="editFakedns(index)">
|
||||
<a-icon type="edit"></a-icon>
|
||||
{{ i18n "edit" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="deleteFakedns(index)">
|
||||
<span style="color: #FF4D4F">
|
||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
||||
</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="tpl-advanced" tab='{{ i18n "pages.xray.advancedTemplate"}}' style="padding-top: 20px;" force-render="true">
|
||||
<a-list-item-meta title='{{ i18n "pages.xray.Template"}}' description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
|
||||
<a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" style="margin: 10px 0;" :size="isMobile ? 'small' : ''">
|
||||
@@ -480,6 +608,9 @@
|
||||
{{template "ruleModal"}}
|
||||
{{template "outModal"}}
|
||||
{{template "reverseModal"}}
|
||||
{{template "balancerModal"}}
|
||||
{{template "dnsModal"}}
|
||||
{{template "fakednsModal"}}
|
||||
{{template "warpModal"}}
|
||||
<script>
|
||||
const rulesColumns = [
|
||||
@@ -499,6 +630,7 @@
|
||||
{ title: 'Inbound Tag', dataIndex: 'inboundTag', align: 'center', width: 20, ellipsis: true },
|
||||
{ title: 'Client Email', dataIndex: 'user', align: 'center', width: 20, ellipsis: true }]},
|
||||
{ title: '{{ i18n "pages.xray.rules.outbound"}}', dataIndex: 'outboundTag', align: 'center', width: 20 },
|
||||
{ title: '{{ i18n "pages.xray.rules.balancer"}}', dataIndex: 'balancerTag', align: 'center', width: 15 },
|
||||
];
|
||||
|
||||
const rulesMobileColumns = [
|
||||
@@ -522,6 +654,25 @@
|
||||
{ title: '{{ i18n "pages.xray.outbound.domain"}}', dataIndex: 'domain', align: 'center', width: 50 },
|
||||
];
|
||||
|
||||
const balancerColumns = [
|
||||
{ title: "#", align: 'center', width: 20, scopedSlots: { customRender: 'action' } },
|
||||
{ title: '{{ i18n "pages.xray.balancer.tag"}}', dataIndex: 'tag', align: 'center', width: 50 },
|
||||
{ title: '{{ i18n "pages.xray.balancer.balancerStrategy"}}', align: 'center', width: 50, scopedSlots: { customRender: 'strategy' }},
|
||||
{ title: '{{ i18n "pages.xray.balancer.balancerSelectors"}}', align: 'center', width: 100, scopedSlots: { customRender: 'selector' }},
|
||||
];
|
||||
|
||||
const dnsColumns = [
|
||||
{ title: "#", align: 'center', width: 20, scopedSlots: { customRender: 'action' } },
|
||||
{ title: '{{ i18n "pages.xray.outbound.address"}}', align: 'center', width: 50, scopedSlots: { customRender: 'address' } },
|
||||
{ title: '{{ i18n "pages.xray.dns.domains"}}', align: 'center', width: 50, scopedSlots: { customRender: 'domain' } },
|
||||
];
|
||||
|
||||
const fakednsColumns = [
|
||||
{ title: "#", align: 'center', width: 20, scopedSlots: { customRender: 'action' } },
|
||||
{ title: '{{ i18n "pages.xray.fakedns.ipPool"}}', dataIndex: 'ipPool', align: 'center', width: 50 },
|
||||
{ title: '{{ i18n "pages.xray.fakedns.poolSize"}}', dataIndex: 'poolSize', align: 'center', width: 50 },
|
||||
];
|
||||
|
||||
const app = new Vue({
|
||||
delimiters: ['[[', ']]'],
|
||||
el: '#app',
|
||||
@@ -904,6 +1055,155 @@
|
||||
|
||||
this.templateSettings = newTemplateSettings;
|
||||
},
|
||||
addBalancer() {
|
||||
balancerModal.show({
|
||||
title: '{{ i18n "pages.xray.balancer.addBalancer"}}',
|
||||
okText: '{{ i18n "pages.xray.balancer.addBalancer"}}',
|
||||
balancerTags: this.balancersData.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag),
|
||||
balancer: {
|
||||
tag: '',
|
||||
strategy: 'random',
|
||||
selector: []
|
||||
},
|
||||
confirm: (balancer) => {
|
||||
balancerModal.loading();
|
||||
newTemplateSettings = this.templateSettings;
|
||||
if (newTemplateSettings.routing.balancers == undefined) {
|
||||
newTemplateSettings.routing.balancers = [];
|
||||
}
|
||||
let tmpBalancer = {
|
||||
'tag': balancer.tag,
|
||||
'selector': balancer.selector
|
||||
};
|
||||
if (balancer.strategy == 'roundRobin') {
|
||||
tmpBalancer.strategy = {
|
||||
'type': balancer.strategy
|
||||
};
|
||||
}
|
||||
newTemplateSettings.routing.balancers.push(tmpBalancer);
|
||||
this.templateSettings = newTemplateSettings;
|
||||
balancerModal.close();
|
||||
},
|
||||
isEdit: false
|
||||
});
|
||||
},
|
||||
editBalancer(index) {
|
||||
const oldTag = this.balancersData[index].tag;
|
||||
balancerModal.show({
|
||||
title: '{{ i18n "pages.xray.balancer.editBalancer"}}',
|
||||
okText: '{{ i18n "sure" }}',
|
||||
balancerTags: this.balancersData.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag),
|
||||
balancer: this.balancersData[index],
|
||||
confirm: (balancer) => {
|
||||
balancerModal.loading();
|
||||
newTemplateSettings = this.templateSettings;
|
||||
|
||||
let tmpBalancer = {
|
||||
'tag': balancer.tag,
|
||||
'selector': balancer.selector
|
||||
};
|
||||
if (balancer.strategy == 'roundRobin') {
|
||||
tmpBalancer.strategy = {
|
||||
'type': balancer.strategy
|
||||
};
|
||||
}
|
||||
|
||||
newTemplateSettings.routing.balancers[index] = tmpBalancer;
|
||||
// change edited tag if used in rule section
|
||||
if (oldTag != balancer.tag) {
|
||||
newTemplateSettings.routing.rules.forEach((rule) => {
|
||||
if (rule.balancerTag && rule.balancerTag == oldTag) {
|
||||
rule.balancerTag = balancer.tag;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.templateSettings = newTemplateSettings;
|
||||
balancerModal.close();
|
||||
},
|
||||
isEdit: true
|
||||
});
|
||||
},
|
||||
deleteBalancer(index) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
|
||||
//remove from balancers
|
||||
const oldTag = this.balancersData[index].tag;
|
||||
this.balancersData.splice(index, 1);
|
||||
|
||||
// remove from settings
|
||||
let realIndex = newTemplateSettings.routing.balancers.findIndex((b) => b.tag == oldTag);
|
||||
newTemplateSettings.routing.balancers.splice(realIndex, 1);
|
||||
|
||||
// remove related routing rules
|
||||
let rules = [];
|
||||
newTemplateSettings.routing.rules.forEach((r) => {
|
||||
if (!r.balancerTag || r.balancerTag != oldTag) {
|
||||
rules.push(r);
|
||||
}
|
||||
});
|
||||
newTemplateSettings.routing.rules = rules;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
},
|
||||
addDNSServer(){
|
||||
dnsModal.show({
|
||||
title: '{{ i18n "pages.xray.dns.add" }}',
|
||||
confirm: (dnsServer) => {
|
||||
dnsServers = this.dnsServers;
|
||||
dnsServers.push(dnsServer);
|
||||
this.dnsServers = dnsServers;
|
||||
dnsModal.close();
|
||||
},
|
||||
isEdit: false
|
||||
});
|
||||
},
|
||||
editDNSServer(index){
|
||||
dnsModal.show({
|
||||
title: '{{ i18n "pages.xray.dns.edit" }} #' + (index+1),
|
||||
dnsServer: this.dnsServers[index],
|
||||
confirm: (dnsServer) => {
|
||||
dnsServers = this.dnsServers;
|
||||
dnsServers[index] = dnsServer;
|
||||
this.dnsServers = dnsServers;
|
||||
dnsModal.close();
|
||||
},
|
||||
isEdit: true
|
||||
});
|
||||
},
|
||||
deleteDNSServer(index){
|
||||
newDnsServers = this.dnsServers;
|
||||
newDnsServers.splice(index,1);
|
||||
this.dnsServers = newDnsServers;
|
||||
},
|
||||
addFakedns() {
|
||||
fakednsModal.show({
|
||||
title: '{{ i18n "pages.xray.fakedns.add" }}',
|
||||
confirm: (item) => {
|
||||
fakeDns = this.fakeDns?? [];
|
||||
fakeDns.push(item);
|
||||
this.fakeDns = fakeDns;
|
||||
fakednsModal.close();
|
||||
},
|
||||
isEdit: false
|
||||
});
|
||||
},
|
||||
editFakedns(index){
|
||||
fakednsModal.show({
|
||||
title: '{{ i18n "pages.xray.fakedns.edit" }} #' + (index+1),
|
||||
fakeDns: this.fakeDns[index],
|
||||
confirm: (item) => {
|
||||
fakeDns = this.fakeDns;
|
||||
fakeDns[index] = item;
|
||||
this.fakeDns = fakeDns;
|
||||
fakednsModal.close();
|
||||
},
|
||||
isEdit: true
|
||||
});
|
||||
},
|
||||
deleteFakedns(index){
|
||||
fakeDns = this.fakeDns;
|
||||
fakeDns.splice(index,1);
|
||||
this.fakeDns = fakeDns;
|
||||
},
|
||||
addRule(){
|
||||
ruleModal.show({
|
||||
title: '{{ i18n "pages.xray.rules.add"}}',
|
||||
@@ -1071,6 +1371,27 @@
|
||||
return data;
|
||||
}
|
||||
},
|
||||
balancersData: {
|
||||
get: function () {
|
||||
data = []
|
||||
if (this.templateSettings != null && this.templateSettings.routing != null && this.templateSettings.routing.balancers != null) {
|
||||
this.templateSettings.routing.balancers.forEach((o, index) => {
|
||||
let strategy = "random"
|
||||
if (o.strategy && o.strategy.type == "roundRobin") {
|
||||
strategy = o.strategy.type
|
||||
}
|
||||
|
||||
data.push({
|
||||
'key': index,
|
||||
'tag': o.tag ? o.tag : "",
|
||||
'strategy': strategy,
|
||||
'selector': o.selector ? o.selector : []
|
||||
});
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
freedomStrategy: {
|
||||
get: function () {
|
||||
if (!this.templateSettings) return "AsIs";
|
||||
@@ -1201,14 +1522,14 @@
|
||||
familyProtectSettings: {
|
||||
get: function () {
|
||||
if (!this.templateSettings || !this.templateSettings.dns || !this.templateSettings.dns.servers) return false;
|
||||
return doAllItemsExist(this.templateSettings.dns.servers, this.settingsData.familyProtectDNS.servers);
|
||||
return doAllItemsExist(this.settingsData.familyProtectDNS.servers, this.templateSettings.dns.servers);
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
if (newValue) {
|
||||
newTemplateSettings.dns = this.settingsData.familyProtectDNS;
|
||||
} else {
|
||||
delete newTemplateSettings.dns;
|
||||
newTemplateSettings.dns.servers = newTemplateSettings.dns?.servers?.filter(data => !this.settingsData.familyProtectDNS.servers.includes(data))
|
||||
}
|
||||
this.templateSettings = newTemplateSettings;
|
||||
},
|
||||
@@ -1446,6 +1767,42 @@
|
||||
}
|
||||
},
|
||||
},
|
||||
enableDNS: {
|
||||
get: function () {
|
||||
return this.templateSettings ? this.templateSettings.dns != null : false;
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
newTemplateSettings.dns = newValue ? { servers: [], queryStrategy: "UseIP" } : null;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
dnsStrategy: {
|
||||
get: function () {
|
||||
return this.enableDNS ? this.templateSettings.dns.queryStrategy : null;
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
newTemplateSettings.dns.queryStrategy = newValue;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
dnsServers: {
|
||||
get: function () { return this.enableDNS ? this.templateSettings.dns.servers : []; },
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
newTemplateSettings.dns.servers = newValue;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
fakeDns: {
|
||||
get: function () { return this.templateSettings && this.templateSettings.fakedns ? this.templateSettings.fakedns : []; },
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
newTemplateSettings.fakedns = newValue.length >0 ? newValue : null;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
111
web/html/xui/xray_balancer_modal.html
Normal file
111
web/html/xui/xray_balancer_modal.html
Normal file
@@ -0,0 +1,111 @@
|
||||
{{define "balancerModal"}}
|
||||
<a-modal
|
||||
id="balancer-modal"
|
||||
v-model="balancerModal.visible"
|
||||
:title="balancerModal.title"
|
||||
@ok="balancerModal.ok"
|
||||
:confirm-loading="balancerModal.confirmLoading"
|
||||
:ok-button-props="{ props: { disabled: !balancerModal.isValid } }"
|
||||
:closable="true"
|
||||
:mask-closable="false"
|
||||
:ok-text="balancerModal.okText"
|
||||
cancel-text='{{ i18n "close" }}'
|
||||
:class="themeSwitcher.currentTheme">
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.xray.balancer.tag" }}' has-feedback
|
||||
:validate-status="balancerModal.duplicateTag? 'warning' : 'success'">
|
||||
<a-input v-model.trim="balancerModal.balancer.tag" @change="balancerModal.check()"
|
||||
placeholder='{{ i18n "pages.xray.balancer.tagDesc" }}'></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.balancer.balancerStrategy" }}'>
|
||||
<a-select v-model="balancerModal.balancer.strategy" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="random">Random</a-select-option>
|
||||
<a-select-option value="roundRobin">Round Robin</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.balancer.balancerSelectors" }}' has-feedback :validate-status="balancerModal.emptySelector? 'warning' : 'success'">
|
||||
<a-select v-model="balancerModal.balancer.selector" mode="tags" @change="balancerModal.checkSelector()"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="tag in balancerModal.outboundTags" :value="tag">[[ tag ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</table>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<script>
|
||||
const balancerModal = {
|
||||
title: '',
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
okText: '{{ i18n "sure" }}',
|
||||
isEdit: false,
|
||||
confirm: null,
|
||||
duplicateTag: false,
|
||||
emptySelector: false,
|
||||
balancer: {
|
||||
tag: '',
|
||||
strategy: 'random',
|
||||
selector: []
|
||||
},
|
||||
outboundTags: [],
|
||||
balancerTags:[],
|
||||
ok() {
|
||||
if (balancerModal.balancer.selector.length == 0) {
|
||||
balancerModal.emptySelector = true;
|
||||
return;
|
||||
}
|
||||
balancerModal.emptySelector = false;
|
||||
ObjectUtil.execute(balancerModal.confirm, balancerModal.balancer);
|
||||
},
|
||||
show({ title = '', okText = '{{ i18n "sure" }}', balancerTags = [], balancer, confirm = (balancer) => { }, isEdit = false }) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.confirm = confirm;
|
||||
this.visible = true;
|
||||
if (isEdit) {
|
||||
balancerModal.balancer = balancer;
|
||||
} else {
|
||||
balancerModal.balancer = {
|
||||
tag: '',
|
||||
strategy: 'random',
|
||||
selector: []
|
||||
};
|
||||
}
|
||||
this.balancerTags = balancerTags.filter((tag) => tag != balancer.tag);
|
||||
this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag);
|
||||
this.isEdit = isEdit;
|
||||
this.check()
|
||||
},
|
||||
close() {
|
||||
balancerModal.visible = false;
|
||||
balancerModal.loading(false);
|
||||
},
|
||||
loading(loading=true) {
|
||||
balancerModal.confirmLoading = loading;
|
||||
},
|
||||
check() {
|
||||
if (balancerModal.balancer.tag == '' || balancerModal.balancerTags.includes(balancerModal.balancer.tag)) {
|
||||
this.duplicateTag = true;
|
||||
this.isValid = false;
|
||||
} else {
|
||||
this.duplicateTag = false;
|
||||
this.isValid = true;
|
||||
}
|
||||
},
|
||||
checkSelector() {
|
||||
balancerModal.emptySelector = balancerModal.balancer.selector.length == 0;
|
||||
}
|
||||
};
|
||||
|
||||
new Vue({
|
||||
delimiters: ['[[', ']]'],
|
||||
el: '#balancer-modal',
|
||||
data: {
|
||||
balancerModal: balancerModal
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -11,7 +11,7 @@
|
||||
title: '',
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
okText: '{{ i18n "sure" }}',
|
||||
okText: '{{ i18n "confirm" }}',
|
||||
isEdit: false,
|
||||
confirm: null,
|
||||
outbound: new Outbound(),
|
||||
@@ -25,7 +25,7 @@
|
||||
ok() {
|
||||
ObjectUtil.execute(outModal.confirm, outModal.outbound.toJson());
|
||||
},
|
||||
show({ title='', okText='{{ i18n "sure" }}', outbound, confirm=(outbound)=>{}, isEdit=false, tags=[] }) {
|
||||
show({ title='', okText='{{ i18n "confirm" }}', outbound, confirm=(outbound)=>{}, isEdit=false, tags=[] }) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.confirm = confirm;
|
||||
@@ -42,7 +42,7 @@
|
||||
outModal.visible = false;
|
||||
outModal.loading(false);
|
||||
},
|
||||
loading(loading) {
|
||||
loading(loading=true) {
|
||||
outModal.confirmLoading = loading;
|
||||
},
|
||||
check(){
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
:options="reverseModal.inboundTags"></a-checkbox-group>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</table>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<script>
|
||||
@@ -46,7 +45,7 @@
|
||||
title: '',
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
okText: '{{ i18n "sure" }}',
|
||||
okText: '{{ i18n "confirm" }}',
|
||||
isEdit: false,
|
||||
confirm: null,
|
||||
reverse: {
|
||||
@@ -74,7 +73,7 @@
|
||||
}
|
||||
ObjectUtil.execute(reverseModal.confirm, reverseModal.reverse, reverseModal.rules);
|
||||
},
|
||||
show({ title='', okText='{{ i18n "sure" }}', reverse, rules, confirm=(reverse, rules)=>{}, isEdit=false }) {
|
||||
show({ title='', okText='{{ i18n "confirm" }}', reverse, rules, confirm=(reverse, rules)=>{}, isEdit=false }) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.confirm = confirm;
|
||||
@@ -120,7 +119,7 @@
|
||||
reverseModal.visible = false;
|
||||
reverseModal.loading(false);
|
||||
},
|
||||
loading(loading) {
|
||||
loading(loading=true) {
|
||||
reverseModal.confirmLoading = loading;
|
||||
},
|
||||
};
|
||||
@@ -131,8 +130,6 @@
|
||||
data: {
|
||||
reverseModal: reverseModal,
|
||||
reverseTypes: { bridge: '{{ i18n "pages.xray.outbound.bridge" }}', portal:'{{ i18n "pages.xray.outbound.portal" }}'},
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -107,6 +107,19 @@
|
||||
<a-select-option v-for="tag in ruleModal.outboundTags" :value="tag">[[ tag ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.xray.balancer.balancerDesc" }}</span>
|
||||
</template>
|
||||
Balancer Tag <a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-select v-model="ruleModal.rule.balancerTag" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="tag in ruleModal.balancerTags" :value="tag">[[ tag ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</table>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
@@ -116,7 +129,7 @@
|
||||
title: '',
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
okText: '{{ i18n "sure" }}',
|
||||
okText: '{{ i18n "confirm" }}',
|
||||
isEdit: false,
|
||||
confirm: null,
|
||||
rule: {
|
||||
@@ -133,6 +146,7 @@
|
||||
protocol: [],
|
||||
attrs: [],
|
||||
outboundTag: "",
|
||||
balancerTag: "",
|
||||
},
|
||||
inboundTags: [],
|
||||
outboundTags: [],
|
||||
@@ -142,7 +156,7 @@
|
||||
newRule = ruleModal.getResult();
|
||||
ObjectUtil.execute(ruleModal.confirm, newRule);
|
||||
},
|
||||
show({ title='', okText='{{ i18n "sure" }}', rule, confirm=(rule)=>{}, isEdit=false }) {
|
||||
show({ title='', okText='{{ i18n "confirm" }}', rule, confirm=(rule)=>{}, isEdit=false }) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.confirm = confirm;
|
||||
@@ -160,6 +174,7 @@
|
||||
this.rule.protocol = rule.protocol;
|
||||
this.rule.attrs = rule.attrs ? Object.entries(rule.attrs) : [];
|
||||
this.rule.outboundTag = rule.outboundTag;
|
||||
this.rule.balancerTag = rule.balancerTag ? rule.balancerTag : "";
|
||||
} else {
|
||||
this.rule = {
|
||||
domainMatcher: "",
|
||||
@@ -174,6 +189,7 @@
|
||||
protocol: [],
|
||||
attrs: [],
|
||||
outboundTag: "",
|
||||
balancerTag: "",
|
||||
}
|
||||
}
|
||||
this.isEdit = isEdit;
|
||||
@@ -186,12 +202,15 @@
|
||||
}
|
||||
if(app.templateSettings.reverse.portals) this.outboundTags.push(...app.templateSettings.reverse.portals.map(b => b.tag));
|
||||
}
|
||||
if (app.templateSettings.routing && app.templateSettings.routing.balancers) {
|
||||
this.balancerTags = app.templateSettings.routing.balancers.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag)
|
||||
}
|
||||
},
|
||||
close() {
|
||||
ruleModal.visible = false;
|
||||
ruleModal.loading(false);
|
||||
},
|
||||
loading(loading) {
|
||||
loading(loading=true) {
|
||||
ruleModal.confirmLoading = loading;
|
||||
},
|
||||
getResult() {
|
||||
@@ -211,6 +230,7 @@
|
||||
rule.protocol = value.protocol;
|
||||
rule.attrs = Object.fromEntries(value.attrs);
|
||||
rule.outboundTag = value.outboundTag;
|
||||
rule.balancerTag = value.balancerTag;
|
||||
|
||||
for (const [key, value] of Object.entries(rule)) {
|
||||
if (
|
||||
|
||||
@@ -55,6 +55,9 @@ var defaultValueMap = map[string]string{
|
||||
"subEncrypt": "true",
|
||||
"subShowInfo": "false",
|
||||
"subURI": "",
|
||||
"subJsonPath": "/json/",
|
||||
"subJsonURI": "",
|
||||
"subJsonFragment": "",
|
||||
"warp": "",
|
||||
}
|
||||
|
||||
@@ -353,17 +356,11 @@ func (s *SettingService) GetSubPort() (int, error) {
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubPath() (string, error) {
|
||||
subPath, err := s.getString("subPath")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !strings.HasPrefix(subPath, "/") {
|
||||
subPath = "/" + subPath
|
||||
}
|
||||
if !strings.HasSuffix(subPath, "/") {
|
||||
subPath += "/"
|
||||
}
|
||||
return subPath, nil
|
||||
return s.getString("subPath")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubJsonPath() (string, error) {
|
||||
return s.getString("subJsonPath")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubDomain() (string, error) {
|
||||
@@ -378,8 +375,8 @@ func (s *SettingService) GetSubKeyFile() (string, error) {
|
||||
return s.getString("subKeyFile")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubUpdates() (int, error) {
|
||||
return s.getInt("subUpdates")
|
||||
func (s *SettingService) GetSubUpdates() (string, error) {
|
||||
return s.getString("subUpdates")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubEncrypt() (bool, error) {
|
||||
@@ -398,6 +395,14 @@ func (s *SettingService) GetSubURI() (string, error) {
|
||||
return s.getString("subURI")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubJsonURI() (string, error) {
|
||||
return s.getString("subJsonURI")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubJsonFragment() (string, error) {
|
||||
return s.getString("subJsonFragment")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetWarp() (string, error) {
|
||||
return s.getString("warp")
|
||||
}
|
||||
@@ -446,6 +451,7 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) {
|
||||
"tgBotEnable": func() (interface{}, error) { return s.GetTgbotenabled() },
|
||||
"subEnable": func() (interface{}, error) { return s.GetSubEnable() },
|
||||
"subURI": func() (interface{}, error) { return s.GetSubURI() },
|
||||
"subJsonURI": func() (interface{}, error) { return s.GetSubJsonURI() },
|
||||
"remarkModel": func() (interface{}, error) { return s.GetRemarkModel() },
|
||||
}
|
||||
|
||||
@@ -459,10 +465,11 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) {
|
||||
result[key] = value
|
||||
}
|
||||
|
||||
if result["subEnable"].(bool) && result["subURI"].(string) == "" {
|
||||
if result["subEnable"].(bool) && (result["subURI"].(string) == "" || result["subJsonURI"].(string) == "") {
|
||||
subURI := ""
|
||||
subPort, _ := s.GetSubPort()
|
||||
subPath, _ := s.GetSubPath()
|
||||
subJsonPath, _ := s.GetSubJsonPath()
|
||||
subDomain, _ := s.GetSubDomain()
|
||||
subKeyFile, _ := s.GetSubKeyFile()
|
||||
subCertFile, _ := s.GetSubCertFile()
|
||||
@@ -483,12 +490,12 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) {
|
||||
} else {
|
||||
subURI += fmt.Sprintf("%s:%d", subDomain, subPort)
|
||||
}
|
||||
if subPath[0] == byte('/') {
|
||||
subURI += subPath
|
||||
} else {
|
||||
subURI += "/" + subPath
|
||||
if result["subURI"].(string) == "" {
|
||||
result["subURI"] = subURI + subPath
|
||||
}
|
||||
if result["subJsonURI"].(string) == "" {
|
||||
result["subJsonURI"] = subURI + subJsonPath
|
||||
}
|
||||
result["subURI"] = subURI
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/gob"
|
||||
"x-ui/database/model"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
sessions "github.com/Calidity/gin-sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"remained" = "Remaining"
|
||||
"secAlertTitle" = "Security Alert"
|
||||
"secAlertSsl" = "This connection is not secure. Please avoid entering sensitive information until TLS is activated for data protection."
|
||||
"secAlertConf" = "Certain configurations have been identified as susceptible to attacks, prompting immediate action to reinforce security protocols and safeguard against potential security breaches."
|
||||
"security" = "Security"
|
||||
|
||||
[menu]
|
||||
@@ -77,7 +78,7 @@
|
||||
"title" = "Overview"
|
||||
"memory" = "RAM"
|
||||
"hard" = "Disk"
|
||||
"machineInfo" = "Machine"
|
||||
"serverInfo" = "Server"
|
||||
"hostname" = "Hostname"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "Stop"
|
||||
@@ -293,6 +294,8 @@
|
||||
"subShowInfoDesc" = "The remaining traffic and date will be displayed in the client apps."
|
||||
"subURI" = "Reverse Proxy URI"
|
||||
"subURIDesc" = "The subscription service will use the URI that has been set up behind reverse proxies."
|
||||
"fragment" = "Fragmentation"
|
||||
"fragmentDesc" = "Enable fragmentation for TLS hello packet"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "Modify Settings"
|
||||
@@ -378,6 +381,7 @@
|
||||
"Outbounds" = "Outbounds"
|
||||
"Routings" = "Routing Rules"
|
||||
"RoutingsDesc" = "The priority of each rule is important!"
|
||||
"Balancers" = "Balancers"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "First"
|
||||
@@ -392,6 +396,7 @@
|
||||
"add" = "Add Rule"
|
||||
"edit" = "Edit Rule"
|
||||
"useComma" = "Comma-separated items"
|
||||
"balancer" = "Balancer"
|
||||
|
||||
[pages.xray.outbound]
|
||||
"addOutbound" = "Add Outbound"
|
||||
@@ -411,6 +416,15 @@
|
||||
"accountInfo" = "Account Information"
|
||||
"outboundStatus" = "Outbound Status"
|
||||
|
||||
[pages.xray.balancer]
|
||||
"addBalancer" = "Add Balancer"
|
||||
"editBalancer" = "Edit Balancer"
|
||||
"balancerStrategy" = "Strategy"
|
||||
"balancerSelectors" = "Selectors"
|
||||
"tag" = "Tag"
|
||||
"tagDesc" = "Unique Tag"
|
||||
"balancerDesc" = "It is not possible to use balancerTag and outboundTag at the same time. If used at the same time, only outboundTag will work."
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "Secret Key"
|
||||
"publicKey" = "Public Key"
|
||||
@@ -419,6 +433,21 @@
|
||||
"psk" = "PreShared Key"
|
||||
"domainStrategy" = "Domain Strategy"
|
||||
|
||||
[pages.xray.dns]
|
||||
"enable" = "Enable DNS"
|
||||
"enableDesc" = "Enable built-in DNS server"
|
||||
"strategy" = "Query Strategy"
|
||||
"strategyDesc" = "Overall strategy to resolve domain names"
|
||||
"add" = "Add Server"
|
||||
"edit" = "Edit Server"
|
||||
"domains" = "Domains"
|
||||
|
||||
[pages.xray.fakedns]
|
||||
"add" = "Add Fake DNS"
|
||||
"edit" = "Edit Fake DNS"
|
||||
"ipPool" = "IP Pool Subnet"
|
||||
"poolSize" = "Pool Size"
|
||||
|
||||
[tgbot]
|
||||
"noResult" = "❗ No result!"
|
||||
"wentWrong" = "❌ Something went wrong!"
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"remained" = "باقیمانده"
|
||||
"secAlertTitle" = "هشدارامنیتی"
|
||||
"secAlertSsl" = "ایناتصالامن نیست. لطفا تازمانیکه تیالاس برای محافظت از دادهها فعال نشدهاست، از وارد کردن اطلاعات حساس خودداری کنید"
|
||||
"secAlertConf" = "پیکربندیهای خاصی مستعد حملات سایبری شناسایی شدهاند، اقدام فوری برای تقویت پروتکلهای امنیتی و محافظت در برابر نقضهای امنیتی لازم است"
|
||||
"security" = "امنیت"
|
||||
|
||||
[menu]
|
||||
@@ -77,7 +78,7 @@
|
||||
"title" = "نمای کلی"
|
||||
"memory" = "RAM"
|
||||
"hard" = "Disk"
|
||||
"machineInfo" = "ماشین"
|
||||
"serverInfo" = "سرور"
|
||||
"hostname" = "نام میزبان"
|
||||
"xrayStatus" = "ایکسری"
|
||||
"stopXray" = "توقف"
|
||||
@@ -292,6 +293,8 @@
|
||||
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در برنامههای کاربری نمایش میدهد"
|
||||
"subURI" = "پراکسی معکوس URI"
|
||||
"subURIDesc" = "سابسکریپشن از لینکی که در پشت پراکسیهای معکوس تنظیم شده، استفاده خواهدکرد"
|
||||
"fragment" = "تکهتکه شدن"
|
||||
"fragmentDesc" = "فعال کردن تکه تکه شدن برای بسته نخست تیالاس"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "ویرایش تنظیمات"
|
||||
@@ -308,8 +311,8 @@
|
||||
"advancedTemplate" = "پیشرفته"
|
||||
"generalConfigs" = "استراتژی کلی"
|
||||
"generalConfigsDesc" = "این گزینهها استراتژی کلی ترافیک را تعیین میکنند"
|
||||
"logConfigs" = "لاگ"
|
||||
"logConfigsDesc" = "لاگ ها ممکن است بر کارایی سرور شما تأثیر بگذارند. توصیه می شود فقط در صورت نیاز آن را آگاهانه فعال کنید"
|
||||
"logConfigs" = "گزارشها"
|
||||
"logConfigsDesc" = "گزارشها ممکن است بر کارایی سرور شما تأثیر بگذارند. توصیه می شود فقط در صورت نیاز آن را آگاهانه فعال کنید"
|
||||
"blockConfigs" = "سپر محافظ"
|
||||
"blockConfigsDesc" = "این گزینهها ترافیک را بر اساس پروتکلهای درخواستی خاص، و وب سایتها مسدود میکند"
|
||||
"blockCountryConfigs" = "مسدودسازی کشور"
|
||||
@@ -377,6 +380,7 @@
|
||||
"Outbounds" = "خروجیها"
|
||||
"Routings" = "قوانین مسیریابی"
|
||||
"RoutingsDesc" = "اولویت هر قانون مهم است"
|
||||
"Balancers" = "بالانسرها"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "اولین"
|
||||
@@ -391,6 +395,7 @@
|
||||
"add" = "افزودن قانون"
|
||||
"edit" = "ویرایش قانون"
|
||||
"useComma" = "موارد جداشده با کاما"
|
||||
"balancer" = "بالانسر"
|
||||
|
||||
[pages.xray.outbound]
|
||||
"addOutbound" = "افزودن خروجی"
|
||||
@@ -410,6 +415,15 @@
|
||||
"accountInfo" = "اطلاعات حساب"
|
||||
"outboundStatus" = "وضعیت خروجی"
|
||||
|
||||
[pages.xray.balancer]
|
||||
"addBalancer" = "افزودن بالانسر"
|
||||
"editBalancer" = "ویرایش بالانسر"
|
||||
"balancerStrategy" = "استراتژی"
|
||||
"balancerSelectors" = "انتخابگرها"
|
||||
"tag" = "برچسب"
|
||||
"tagDesc" = "برچسب یگانه"
|
||||
"balancerDesc" = "امکان استفاده همزمان برچسب خروجی و برچسب بالانسر باهم وجود ندارد. درصورت استفاده همزمان فقط برجسب خروجی عمل خواهد کرد."
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "کلید شخصی"
|
||||
"publicKey" = "کلید عمومی"
|
||||
@@ -418,6 +432,21 @@
|
||||
"psk" = "کلید مشترک"
|
||||
"domainStrategy" = "استراتژی حل دامنه"
|
||||
|
||||
[pages.xray.dns]
|
||||
"enable" = "فعال کردن حل دامنه"
|
||||
"enableDesc" = "سرور حل دامنه داخلی را فعال کنید"
|
||||
"strategy" = "استراتژی پرسوجو"
|
||||
"strategyDesc" = "استراتژی کلی برای حل نام دامنه"
|
||||
"add" = "افزودن سرور"
|
||||
"edit" = "ویرایش سرور"
|
||||
"domains" = "دامنهها"
|
||||
|
||||
[pages.xray.fakedns]
|
||||
"add" = "افزودن دیاناس جعلی"
|
||||
"edit" = "ویرایش دیاناس جعلی"
|
||||
"ipPool" = "زیرشبکه استخر آیپی"
|
||||
"poolSize" = "اندازه استخر"
|
||||
|
||||
[tgbot]
|
||||
"noResult" = "❗نتیجهای یافت نشد"
|
||||
"wentWrong" = "❌ مشکلی رخ دادهاست"
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"remained" = "Осталось"
|
||||
"secAlertTitle" = "Предупреждение системы безопасности"
|
||||
"secAlertSsl" = "Это соединение не защищено. Пожалуйста, воздержитесь от ввода конфиденциальной информации до тех пор, пока не будет активирован TLS для защиты данных"
|
||||
"secAlertConf" = "Некоторые конфигурации были определены как уязвимые для атак, что требует немедленных действий по усилению протоколов безопасности и защите от потенциальных нарушений безопасности."
|
||||
"security" = "Безопасность"
|
||||
|
||||
[menu]
|
||||
@@ -77,7 +78,7 @@
|
||||
"title" = "Статус системы"
|
||||
"memory" = "ОЗУ"
|
||||
"hard" = "Место на диске"
|
||||
"machineInfo" = "Машина"
|
||||
"serverInfo" = "Сервер"
|
||||
"hostname" = "Имя хоста"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "Остановка"
|
||||
@@ -293,6 +294,8 @@
|
||||
"subShowInfoDesc" = "Показывать восстановленный трафик и дату после имени конфигурации"
|
||||
"subURI" = "URI обратного прокси"
|
||||
"subURIDesc" = "Изменить базовый URI URL-адреса подписки для использования за прокси-серверами"
|
||||
"fragment" = "Фрагментация"
|
||||
"fragmentDesc" = "Включить фрагментацию для пакета приветствия TLS"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "Изменение настроек"
|
||||
@@ -378,6 +381,7 @@
|
||||
"Outbounds" = "Исходящие"
|
||||
"Routings" = "Правила маршрутизации"
|
||||
"RoutingsDesc" = "Важен приоритет каждого правила!"
|
||||
"Balancers" = "Балансиры"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "Первый"
|
||||
@@ -392,6 +396,7 @@
|
||||
"add" = "Добавить правило"
|
||||
"edit" = "Редактировать правило"
|
||||
"useComma" = "Элементы, разделенные запятыми"
|
||||
"balancer" = "балансир"
|
||||
|
||||
[pages.xray.outbound]
|
||||
"addOutbound" = "Добавить исходящий"
|
||||
@@ -411,6 +416,15 @@
|
||||
"accountInfo" = "Информация Об Учетной Записи"
|
||||
"outboundStatus" = "Исходящий статус"
|
||||
|
||||
[pages.xray.balancer]
|
||||
"addBalancer" = "Добавить балансир"
|
||||
"editBalancer" = "Редактировать балансир"
|
||||
"balancerStrategy" = "Стратегия"
|
||||
"balancerSelectors" = "Селекторы"
|
||||
"tag" = "Тег"
|
||||
"tagDesc" = "уникальный тег"
|
||||
"balancerDesc" = "Невозможно одновременно использовать balancerTag и outboundTag. При одновременном использовании будет работать только outboundTag."
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "Секретный ключ"
|
||||
"publicKey" = "Открытый ключ"
|
||||
@@ -419,6 +433,21 @@
|
||||
"psk" = "Общий ключ"
|
||||
"domainStrategy" = "Стратегия домена"
|
||||
|
||||
[pages.xray.dns]
|
||||
"enable" = "Включить DNS"
|
||||
"enableDesc" = "Включить встроенный DNS-сервер"
|
||||
"strategy" = "Стратегия запроса"
|
||||
"strategyDesc" = "Общая стратегия разрешения доменных имен"
|
||||
"add" = "Добавить сервер"
|
||||
"edit" = "Редактировать сервер"
|
||||
"domains" = "Домены"
|
||||
|
||||
[pages.xray.fakedns]
|
||||
"add" = "Добавить поддельный DNS"
|
||||
"edit" = "Редактировать поддельный DNS"
|
||||
"ipPool" = "Подсеть пула IP"
|
||||
"poolSize" = "Размер пула"
|
||||
|
||||
[tgbot]
|
||||
"noResult" = "❗ Нет результатов!"
|
||||
"wentWrong" = "❌ Что-то пошло не так!"
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"remained" = "Còn lại"
|
||||
"secAlertTitle" = "Cảnh báo an ninh-Tiếng Việt by Ohoang7"
|
||||
"secAlertSsl" = "Kết nối này không an toàn; Vui lòng không nhập thông tin nhạy cảm cho đến khi TLS được kích hoạt để bảo vệ dữ liệu của Bạn"
|
||||
"secAlertConf" = "Một số cấu hình nhất định đã được xác định là dễ bị tấn công, thúc đẩy hành động ngay lập tức để củng cố các giao thức bảo mật và bảo vệ chống lại các vi phạm bảo mật tiềm ẩn."
|
||||
"security" = "Bảo vệ"
|
||||
|
||||
[menu]
|
||||
@@ -77,7 +78,7 @@
|
||||
"title" = "Trạng thái hệ thống"
|
||||
"memory" = "Bộ nhớ"
|
||||
"hard" = "Ổ cứng"
|
||||
"machineInfo" = "Máy"
|
||||
"serverInfo" = "Máy chủ"
|
||||
"hostname" = "Tên máy chủ"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "Dừng"
|
||||
@@ -293,6 +294,8 @@
|
||||
"subShowInfoDesc" = "Hiển thị lưu lượng truy cập còn lại và ngày sau tên cấu hình"
|
||||
"subURI" = "URI proxy ngược"
|
||||
"subURIDesc" = "Thay đổi URI cơ sở của URL đăng ký để sử dụng ở phía sau proxy"
|
||||
"fragment" = "Sự phân mảnh"
|
||||
"fragmentDesc" = "Kích hoạt phân mảnh cho gói TLS hello"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "Sửa đổi cài đặt"
|
||||
@@ -378,6 +381,7 @@
|
||||
"Outbounds" = "Đầu ra"
|
||||
"Routings" = "Quy tắc định tuyến"
|
||||
"RoutingsDesc" = "Mức độ ưu tiên của mỗi quy tắc là quan trọng!"
|
||||
"Balancers" = "Cân bằng"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "Đầu tiên"
|
||||
@@ -392,6 +396,7 @@
|
||||
"add" = "Thêm quy tắc"
|
||||
"edit" = "Chỉnh sửa quy tắc"
|
||||
"useComma" = "Các mục được phân tách bằng dấu phẩy"
|
||||
"balancer" = "Cân bằng"
|
||||
|
||||
[pages.xray.outbound]
|
||||
"addOutbound" = "Thêm Đầu vào"
|
||||
@@ -411,6 +416,15 @@
|
||||
"accountInfo" = "Thông tin tài khoản"
|
||||
"outboundStatus" = "Trạng thái đầu ra"
|
||||
|
||||
[pages.xray.balancer]
|
||||
"addBalancer" = "Thêm cân bằng"
|
||||
"editBalancer" = "Chỉnh sửa cân bằng"
|
||||
"balancerStrategy" = "Chiến lược"
|
||||
"balancerSelectors" = "Bộ chọn"
|
||||
"tag" = "Thẻ"
|
||||
"tagDesc" = "thẻ duy nhất"
|
||||
"balancerDesc" = "Không thể sử dụng balancerTag và outboundTag cùng một lúc. Nếu sử dụng cùng lúc thì chỉ outboundTag mới hoạt động."
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "Chìa khoá bí mật"
|
||||
"publicKey" = "Khóa công khai"
|
||||
@@ -419,6 +433,21 @@
|
||||
"psk" = "Khóa chia sẻ"
|
||||
"domainStrategy" = "Chiến lược tên miền"
|
||||
|
||||
[pages.xray.dns]
|
||||
"enable" = "Kích hoạt DNS"
|
||||
"enableDesc" = "Kích hoạt máy chủ DNS tích hợp"
|
||||
"strategy" = "Chiến lược truy vấn"
|
||||
"strategyDesc" = "Chiến lược tổng thể để phân giải tên miền"
|
||||
"add" = "Thêm máy chủ"
|
||||
"edit" = "Chỉnh sửa máy chủ"
|
||||
"domains" = "Tên miền"
|
||||
|
||||
[pages.xray.fakedns]
|
||||
"add" = "Thêm DNS giả"
|
||||
"edit" = "Chỉnh sửa DNS giả"
|
||||
"ipPool" = "Mạng con nhóm IP"
|
||||
"poolSize" = "Kích thước bể bơi"
|
||||
|
||||
[tgbot]
|
||||
"noResult" = "❗ Không có kết quả!"
|
||||
"wentWrong" = "❌ Đã xảy ra lỗi!"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"remark" = "备注"
|
||||
"enable" = "启用"
|
||||
"protocol" = "协议"
|
||||
"search" = "搜尋"
|
||||
"search" = "查找"
|
||||
"filter" = "过滤器"
|
||||
"loading" = "加载中..."
|
||||
"second" = "秒"
|
||||
@@ -30,8 +30,8 @@
|
||||
"sure" = "确定"
|
||||
"encryption" = "加密"
|
||||
"transmission" = "传输"
|
||||
"host" = "主持人"
|
||||
"path" = "小路"
|
||||
"host" = "主机"
|
||||
"path" = "路径"
|
||||
"camouflage" = "混淆"
|
||||
"status" = "状态"
|
||||
"enabled" = "开启"
|
||||
@@ -52,6 +52,7 @@
|
||||
"remained" = "仍然存在"
|
||||
"secAlertTitle" = "安全警报"
|
||||
"secAlertSsl" = "此连接不安全;在激活 TLS 进行数据保护之前,请勿输入敏感信息"
|
||||
"secAlertConf" = "某些配置已被确定为容易受到攻击,促使立即采取行动以加强安全协议并防范潜在的安全漏洞。"
|
||||
"security" = "安全"
|
||||
|
||||
[menu]
|
||||
@@ -64,20 +65,20 @@
|
||||
|
||||
[pages.login]
|
||||
"title" = "欢迎"
|
||||
"loginAgain" = "登录时效已过,请重新登录"
|
||||
"loginAgain" = "会话过期,请重新登录"
|
||||
|
||||
[pages.login.toasts]
|
||||
"invalidFormData" = "数据格式错误"
|
||||
"emptyUsername" = "请输入用户名"
|
||||
"emptyPassword" = "请输入密码"
|
||||
"wrongUsernameOrPassword" = "用户名或密码错误"
|
||||
"successLogin" = "登录"
|
||||
"successLogin" = "登录成功"
|
||||
|
||||
[pages.index]
|
||||
"title" = "系统状态"
|
||||
"memory" = "内存"
|
||||
"hard" = "硬盘"
|
||||
"machineInfo" = "机器"
|
||||
"serverInfo" = "服务器"
|
||||
"hostname" = "主机名"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "停止"
|
||||
@@ -98,7 +99,7 @@
|
||||
"totalReceive" = "系统启动以来所有网卡的总下载流量"
|
||||
"xraySwitchVersionDialog" = "切换 Xray 版本"
|
||||
"xraySwitchVersionDialogDesc" = "是否切换 Xray 版本至"
|
||||
"dontRefresh" = "安装中,请不要刷新此页面"
|
||||
"dontRefresh" = "安装中,请勿刷新此页面"
|
||||
"logs" = "日志"
|
||||
"config" = "配置"
|
||||
"backup" = "备份"
|
||||
@@ -122,7 +123,7 @@
|
||||
"transportConfig" = "传输配置"
|
||||
"expireDate" = "到期时间"
|
||||
"resetTraffic" = "重置流量"
|
||||
"addInbound" = "添加入"
|
||||
"addInbound" = "添加入站"
|
||||
"generalActions" = "通用操作"
|
||||
"create" = "添加"
|
||||
"update" = "修改"
|
||||
@@ -152,9 +153,9 @@
|
||||
"client" = "客户"
|
||||
"export" = "导出链接"
|
||||
"clone" = "克隆"
|
||||
"cloneInbound" = "创造"
|
||||
"cloneInbound" = "创建"
|
||||
"cloneInboundContent" = "此入站的所有项目除 Port、Listening IP、Clients 将应用于克隆"
|
||||
"cloneInboundOk" = "创造"
|
||||
"cloneInboundOk" = "创建"
|
||||
"resetAllTraffic" = "重置所有入站流量"
|
||||
"resetAllTrafficTitle" = "重置所有入站流量"
|
||||
"resetAllTrafficContent" = "您确定要重置所有入站流量吗?"
|
||||
@@ -168,16 +169,16 @@
|
||||
"delDepletedClientsTitle" = "删除耗尽的客户"
|
||||
"delDepletedClientsContent" = "你确定要删除所有耗尽的客户端吗?"
|
||||
"email" = "电子邮件"
|
||||
"emailDesc" = "电子邮件必须完全唯"
|
||||
"emailDesc" = "电子邮件必须完全唯一"
|
||||
"setDefaultCert" = "从面板设置证书"
|
||||
"telegramDesc" = "使用 Telegram ID,不包含 @ 符号或聊天 ID(可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
|
||||
"subscriptionDesc" = "您可以在详细信息上找到您的子链接,你可以为多个客户端使用相同的名称"
|
||||
"info" = "信息"
|
||||
"same" = "相同"
|
||||
"inboundData" = "入站数据"
|
||||
"exportInbound" = "出口 入境"
|
||||
"exportInbound" = "导出入站数据"
|
||||
"import"="导入"
|
||||
"importInbound" = "导入入站"
|
||||
"importInbound" = "导入入站数据"
|
||||
|
||||
[pages.client]
|
||||
"add" = "添加客户端"
|
||||
@@ -187,7 +188,7 @@
|
||||
"clientCount" = "客户数量"
|
||||
"bulk" = "批量创建"
|
||||
"method" = "方法"
|
||||
"first" = "第一"
|
||||
"first" = "第一个"
|
||||
"last" = "最后"
|
||||
"prefix" = "前缀"
|
||||
"postfix" = "后缀"
|
||||
@@ -201,17 +202,17 @@
|
||||
"obtain" = "获取"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"request" = "要求"
|
||||
"response" = "回复"
|
||||
"name" = "姓名"
|
||||
"value" = "价值"
|
||||
"request" = "请求"
|
||||
"response" = "响应"
|
||||
"name" = "名称"
|
||||
"value" = "值"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"version" = "版本"
|
||||
"method" = "方法"
|
||||
"path" = "小路"
|
||||
"status" = "地位"
|
||||
"statusDescription" = "状态说明"
|
||||
"path" = "路径"
|
||||
"status" = "状态"
|
||||
"statusDescription" = "状态描述"
|
||||
"requestHeader" = "请求头"
|
||||
"responseHeader" = "响应头"
|
||||
|
||||
@@ -223,15 +224,15 @@
|
||||
"save" = "保存配置"
|
||||
"infoDesc" = "此处的所有更改都需要保存并重启面板才能生效"
|
||||
"restartPanel" = "重启面板"
|
||||
"restartPanelDesc" = "确定要重启面板吗?点击确定将于 3 秒后重启,若重启后无法访问面板,请前往服务器查看面板日志信息"
|
||||
"restartPanelDesc" = "是否重启面板?点击确定将于 3 秒后重启,若重启后无法访问面板,请前往服务器查看面板日志信息"
|
||||
"resetDefaultConfig" = "重置为默认配置"
|
||||
"panelConfig" = "面板配置"
|
||||
"userSettings" = "用户设置"
|
||||
"TGBotSettings" = "TG提醒相关设置"
|
||||
"TGBotSettings" = "TG 提醒相关设置"
|
||||
"panelListeningIP" = "面板监听 IP"
|
||||
"panelListeningIPDesc" = "默认留空监听所有 IP"
|
||||
"panelListeningDomain" = "面板监听域名"
|
||||
"panelListeningDomainDesc" = "默认情况下留空以监视所有域名和 IP 地址"
|
||||
"panelListeningDomainDesc" = "默认留空以监视所有域名和 IP 地址"
|
||||
"panelPort" = "面板监听端口"
|
||||
"panelPortDesc" = "重启面板生效"
|
||||
"publicKeyPath" = "面板证书公钥文件路径"
|
||||
@@ -255,13 +256,13 @@
|
||||
"telegramChatId" = "以逗号分隔的多个 chatID"
|
||||
"telegramChatIdDesc" = "多个聊天 ID 用逗号分隔。使用 @userinfobot 或在机器人中使用'/id'命令获取您的聊天 ID。"
|
||||
"telegramNotifyTime" = "电报机器人通知时间"
|
||||
"telegramNotifyTimeDesc" = "采用Crontab定时格式"
|
||||
"telegramNotifyTimeDesc" = "采用 Crontab 定时格式"
|
||||
"tgNotifyBackup" = "数据库备份"
|
||||
"tgNotifyBackupDesc" = "正在发送数据库备份文件和报告通知"
|
||||
"tgNotifyLogin" = "登录通知"
|
||||
"tgNotifyLoginDesc" = "当有人试图登录您的面板时显示用户名、IP 地址和时间"
|
||||
"sessionMaxAge" = "会话最大年龄"
|
||||
"sessionMaxAgeDesc" = "您可以保持登录状态的时间(单位:分钟)"
|
||||
"tgNotifyLoginDesc" = "有登录面板请求时显示用户名、IP 地址和时间"
|
||||
"sessionMaxAge" = "会话最大时长"
|
||||
"sessionMaxAgeDesc" = "您可以保持登录状态的最长时间(单位:分钟)"
|
||||
"expireTimeDiff" = "耗尽时间阈值"
|
||||
"expireTimeDiffDesc" = "到期前检测耗尽(单位:天)"
|
||||
"trafficDiff" = "耗尽流量阈值"
|
||||
@@ -293,6 +294,8 @@
|
||||
"subShowInfoDesc" = "在配置名称后显示剩余流量和日期"
|
||||
"subURI" = "反向代理 URI"
|
||||
"subURIDesc" = "更改订阅 URL 的基本 URI 以在代理后面使用"
|
||||
"fragment" = "碎片"
|
||||
"fragmentDesc" = "启用 TLS hello 数据包分段"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "修改设置"
|
||||
@@ -308,14 +311,14 @@
|
||||
"basicTemplate" = "基本模板"
|
||||
"advancedTemplate" = "高级模板部件"
|
||||
"generalConfigs" = "通用配置"
|
||||
"generalConfigsDesc" = "这些选项将提供一般调整"
|
||||
"generalConfigsDesc" = "这些选项提供通用设置调整"
|
||||
"logConfigs"="日志"
|
||||
"logConfigsDesc" = "日志可能会影响您服务器的效率。建议仅在您需要时明智地启用它"
|
||||
"blockConfigs" = "阻塞配置"
|
||||
"blockConfigsDesc" = "这些选项将阻止用户连接到特定协议和网站"
|
||||
"blockCountryConfigs" = "阻止国家配置"
|
||||
"blockCountryConfigsDesc" = "这些选项将阻止用户连接到特定国家/地区的域。"
|
||||
"directCountryConfigs" = "直接国家配置"
|
||||
"blockConfigsDesc" = "这些选项将禁止用户连接到特定协议和网站"
|
||||
"blockCountryConfigs" = "禁连国家配置"
|
||||
"blockCountryConfigsDesc" = "这些选项将禁止用户连接到特定国家/地区的域。"
|
||||
"directCountryConfigs" = "直连国家配置"
|
||||
"directCountryConfigsDesc" = "这些选项会将用户直接连接到特定国家/地区的域。"
|
||||
"ipv4Configs" = "IPv4 配置"
|
||||
"ipv4ConfigsDesc" = "此选项将仅通过 IPv4 路由到目标域"
|
||||
@@ -327,8 +330,8 @@
|
||||
"FreedomStrategyDesc" = "在自由协议中设置网络输出策略"
|
||||
"RoutingStrategy" = "配置路由域策略"
|
||||
"RoutingStrategyDesc" = "设置DNS解析的整体路由策略"
|
||||
"Torrent" = "禁止使用 bittorrent"
|
||||
"TorrentDesc" = "更改配置模板避免用户使用bittorrent"
|
||||
"Torrent" = "禁止使用 bitTorrent"
|
||||
"TorrentDesc" = "更改配置模板避免用户使用 bitTorrent"
|
||||
"PrivateIp" = "禁止私人 IP 范围连接"
|
||||
"PrivateIpDesc" = "更改配置模板以避免连接私有 IP 范围"
|
||||
"Ads" = "屏蔽广告"
|
||||
@@ -378,6 +381,7 @@
|
||||
"Outbounds" = "出站"
|
||||
"Routings" = "路由规则"
|
||||
"RoutingsDesc" = "每条规则的优先级都很重要"
|
||||
"Balancers" = "平衡器"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "第一个"
|
||||
@@ -392,6 +396,7 @@
|
||||
"add" = "添加规则"
|
||||
"edit" = "编辑规则"
|
||||
"useComma" = "逗号分隔的项目"
|
||||
"balancer" = "平衡器"
|
||||
|
||||
[pages.xray.outbound]
|
||||
"addOutbound" = "添加出站"
|
||||
@@ -411,6 +416,15 @@
|
||||
"accountInfo" = "帐户信息"
|
||||
"outboundStatus" = "出站状态"
|
||||
|
||||
[pages.xray.balancer]
|
||||
"addBalancer" = "添加平衡器"
|
||||
"editBalancer" = "编辑平衡器"
|
||||
"balancerStrategy" = "战略"
|
||||
"balancerSelectors" = "选择器"
|
||||
"tag" = "标签"
|
||||
"tagDesc" = "唯一标记"
|
||||
"balancerDesc" = "不能同时使用balancerTag和outboundTag。 如果同时使用,则只有outboundTag起作用。"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "密钥"
|
||||
"publicKey" = "公钥"
|
||||
@@ -419,6 +433,21 @@
|
||||
"psk" = "共享密钥"
|
||||
"domainStrategy" = "域策略"
|
||||
|
||||
[pages.xray.dns]
|
||||
"enable" = "启用 DNS"
|
||||
"enableDesc" = "启用内置 DNS 服务器"
|
||||
"strategy" = "查询策略"
|
||||
"strategyDesc" = "解析域名的总体策略"
|
||||
"add" = "添加服务器"
|
||||
"edit" = "编辑服务器"
|
||||
"domains" = "域"
|
||||
|
||||
[pages.xray.fakedns]
|
||||
"add" = "添加假 DNS"
|
||||
"edit" = "编辑假 DNS"
|
||||
"ipPool" = "IP 池子网"
|
||||
"poolSize" = "池大小"
|
||||
|
||||
[tgbot]
|
||||
"noResult" = "❗ 没有结果!"
|
||||
"wentWrong" = "❌ 出了点问题!"
|
||||
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
"x-ui/web/network"
|
||||
"x-ui/web/service"
|
||||
|
||||
sessions "github.com/Calidity/gin-sessions"
|
||||
"github.com/Calidity/gin-sessions/cookie"
|
||||
"github.com/gin-contrib/gzip"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
@@ -16,7 +16,8 @@ type Config struct {
|
||||
API json_util.RawMessage `json:"api"`
|
||||
Stats json_util.RawMessage `json:"stats"`
|
||||
Reverse json_util.RawMessage `json:"reverse"`
|
||||
FakeDNS json_util.RawMessage `json:"fakeDns"`
|
||||
FakeDNS json_util.RawMessage `json:"fakedns"`
|
||||
Observatory json_util.RawMessage `json:"observatory"`
|
||||
}
|
||||
|
||||
func (c *Config) Equals(other *Config) bool {
|
||||
|
||||
@@ -33,13 +33,13 @@ func (lw *LogWriter) Write(m []byte) (n int, err error) {
|
||||
// Map the level to the appropriate logger function
|
||||
switch level {
|
||||
case "Debug":
|
||||
logger.Debug(msgBody)
|
||||
logger.Debug("XRAY: " + msgBody)
|
||||
case "Info":
|
||||
logger.Info(msgBody)
|
||||
logger.Info("XRAY: " + msgBody)
|
||||
case "Warning":
|
||||
logger.Warning(msgBody)
|
||||
logger.Warning("XRAY: " + msgBody)
|
||||
case "Error":
|
||||
logger.Error(msgBody)
|
||||
logger.Error("XRAY: " + msgBody)
|
||||
default:
|
||||
logger.Debug("XRAY: " + msg)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user