mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-14 13:31:41 +00:00
Compare commits
25 Commits
1.0.0_beta
...
1.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7aee9fe18 | ||
|
|
a19b58675b | ||
|
|
7a229b27f5 | ||
|
|
abf68446e5 | ||
|
|
b4b7ec565e | ||
|
|
4b1b920bf4 | ||
|
|
11bff57f23 | ||
|
|
4838b0f6f0 | ||
|
|
bb8807129a | ||
|
|
cb050bfe34 | ||
|
|
76a996cc2d | ||
|
|
7fec1dd5cf | ||
|
|
a24e9089b6 | ||
|
|
b6474eaef6 | ||
|
|
76f90b0a46 | ||
|
|
42abffdaef | ||
|
|
73d518dfe3 | ||
|
|
4592a8d201 | ||
|
|
6ab99326ce | ||
|
|
6c5b098e1d | ||
|
|
fe670ae885 | ||
|
|
f4f1d477b3 | ||
|
|
7455a2baa3 | ||
|
|
4c5576c38b | ||
|
|
18ee201412 |
20
DockerInitFiles.sh
Executable file
20
DockerInitFiles.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
if [ $1 == "amd64" ]; then
|
||||
ARCH="64";
|
||||
FNAME="amd64";
|
||||
elif [ $1 == "arm64" ]; then
|
||||
ARCH="arm64-v8a"
|
||||
FNAME="arm64";
|
||||
else
|
||||
ARCH="64";
|
||||
FNAME="amd64";
|
||||
fi
|
||||
mkdir -p build/bin
|
||||
cd build/bin
|
||||
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.1/Xray-linux-${ARCH}.zip"
|
||||
unzip "Xray-linux-${ARCH}.zip"
|
||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
||||
mv xray "xray-linux-${FNAME}"
|
||||
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"
|
||||
cd ../../
|
||||
16
Dockerfile
16
Dockerfile
@@ -1,22 +1,18 @@
|
||||
FROM golang:1.20-alpine AS builder
|
||||
WORKDIR /app
|
||||
ENV CGO_ENABLED 1
|
||||
RUN apk add gcc && apk --no-cache --update add build-base
|
||||
ARG TARGETARCH
|
||||
RUN apk --no-cache --update add build-base gcc wget unzip
|
||||
COPY . .
|
||||
RUN go build main.go
|
||||
RUN env CGO_ENABLED=1 go build -o build/x-ui main.go
|
||||
RUN ./DockerInitFiles.sh "$TARGETARCH"
|
||||
|
||||
FROM alpine
|
||||
LABEL org.opencontainers.image.authors="alireza7@gmail.com"
|
||||
ENV TZ=Asia/Tehran
|
||||
WORKDIR /app
|
||||
|
||||
RUN apk add ca-certificates tzdata && mkdir bin
|
||||
RUN apk add ca-certificates tzdata
|
||||
|
||||
# Download latest rule files
|
||||
ADD https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat \
|
||||
https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat \
|
||||
bin/
|
||||
|
||||
COPY --from=builder /app/main /app/x-ui
|
||||
COPY --from=builder /app/build/ /app/
|
||||
VOLUME [ "/etc/x-ui" ]
|
||||
CMD [ "./x-ui" ]
|
||||
32
README.md
32
README.md
@@ -20,7 +20,7 @@ xray panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese)**
|
||||
| REST API | :heavy_check_mark: |
|
||||
| Telegram BOT (admin + clients) | :heavy_check_mark: |
|
||||
| Backup database using Telegram BOT | :heavy_check_mark: |
|
||||
| Subscription link | :heavy_check_mark: |
|
||||
| Subscription link + userInfo | :heavy_check_mark: |
|
||||
| Calculate expire date on first usage | :heavy_check_mark: |
|
||||
|
||||
**If you think this project is helpful to you, you may wish to give a** :star2:
|
||||
@@ -46,19 +46,19 @@ xray panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese)**
|
||||
- `/login` with `PUSH` user data: `{username: '', password: ''}` for login
|
||||
- `/xui/API/inbounds` base for following actions:
|
||||
|
||||
| Method | Path | Action |
|
||||
| ------ | -------------------------------- | ------------------------------------------- |
|
||||
| GET | "/" | Get all inbounds |
|
||||
| GET | "/get/:id" | Get inbound with inbound.id |
|
||||
| POST | "/add" | Add inbound |
|
||||
| POST | "/del/:id" | Delete Inbound |
|
||||
| POST | "/update/:id" | Update Inbound |
|
||||
| POST | "/addClient/" | Add Client to inbound |
|
||||
| POST | "/delClient/:email" | Delete Client |
|
||||
| POST | "/updateClient/:index" | Update Client |
|
||||
| POST | "/:id/resetClientTraffic/:email" | Reset Client's Traffic |
|
||||
| POST | "/resetAllTraffics" | Reset traffics of all inbounds |
|
||||
| POST | "/resetAllClientTraffics/:id" | Reset traffics of all clients in an inbound |
|
||||
| Method | Path | Action |
|
||||
| :----: | --------------------------------- | ------------------------------------------- |
|
||||
| `GET` | `"/"` | Get all inbounds |
|
||||
| `GET` | `"/get/:id"` | Get inbound with inbound.id |
|
||||
| `POST` | `"/add"` | Add inbound |
|
||||
| `POST` | `"/del/:id"` | Delete Inbound |
|
||||
| `POST` | `"/update/:id"` | Update Inbound |
|
||||
| `POST` | `"/addClient/"` | Add Client to inbound |
|
||||
| `POST` | `"/:id/delClient/:clientId"` | Delete Client by UID/Password as clientId |
|
||||
| `POST` | `"/updateClient/:index"` | Update Client |
|
||||
| `POST` | `"/:id/resetClientTraffic/:email"` | Reset Client's Traffic |
|
||||
| `POST` | `"/resetAllTraffics"` | Reset traffics of all inbounds |
|
||||
| `POST` | `"/resetAllClientTraffics/:id"` | Reset traffics of all clients in an inbound |
|
||||
|
||||
# Environment Variables
|
||||
|
||||
@@ -78,7 +78,7 @@ xray panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese)**
|
||||
|
||||
- CentOS 8+
|
||||
- Ubuntu 20+
|
||||
- Debian 8+
|
||||
- Debian 10+
|
||||
- Fedora 36+
|
||||
|
||||
# Install & Upgrade to latest version
|
||||
@@ -176,7 +176,7 @@ Set the robot-related parameters in the panel background, including:
|
||||
Reference syntax:
|
||||
|
||||
- 30 \* \* \* \* \* //Notify at the 30s of each point
|
||||
- 0 */10 \* \* \* \* //Notify at the first second of each 10 minutes
|
||||
- 0 \*/10 \* \* \* \* //Notify at the first second of each 10 minutes
|
||||
- @hourly // hourly notification
|
||||
- @daily // Daily notification (00:00 in the morning)
|
||||
- @every 8h // notify every 8 hours
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.0.0_beta1
|
||||
1.0.2
|
||||
@@ -7,4 +7,6 @@ services:
|
||||
- $PWD/db/:/etc/x-ui/
|
||||
- $PWD/cert/:/root/cert/
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
ports:
|
||||
- 54321:54321
|
||||
- 443:443
|
||||
|
||||
32
go.mod
32
go.mod
@@ -13,7 +13,7 @@ require (
|
||||
github.com/pelletier/go-toml/v2 v2.0.7
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/shirou/gopsutil/v3 v3.23.3
|
||||
github.com/xtls/xray-core v1.8.0
|
||||
github.com/xtls/xray-core v1.8.1
|
||||
go.uber.org/atomic v1.10.0
|
||||
golang.org/x/text v0.9.0
|
||||
google.golang.org/grpc v1.54.0
|
||||
@@ -23,13 +23,13 @@ require (
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/bytedance/sonic v1.8.0 // indirect
|
||||
github.com/bytedance/sonic v1.8.7 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // 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.11.2 // indirect
|
||||
github.com/go-playground/validator/v10 v10.12.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/gorilla/context v1.1.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
@@ -38,25 +38,25 @@ require (
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/leodido/go-urn v1.2.3 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pires/go-proxyproto v0.6.2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.4 // indirect
|
||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.5 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
|
||||
google.golang.org/protobuf v1.29.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.8.0 // indirect
|
||||
golang.org/x/net v0.9.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
79
go.sum
79
go.sum
@@ -11,6 +11,8 @@ github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
|
||||
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/bytedance/sonic v1.8.7 h1:d3sry5vGgVq/OpgozRUNP6xBsSo0mtNdwliApw+SAMQ=
|
||||
github.com/bytedance/sonic v1.8.7/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
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=
|
||||
@@ -19,9 +21,12 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
|
||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||
github.com/gaukas/godicttls v0.0.3 h1:YNDIf0d9adcxOijiLrEzpfZGAkNwLRzPaG6OjU7EITk=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||
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/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE=
|
||||
github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY=
|
||||
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=
|
||||
@@ -41,7 +46,9 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
|
||||
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI=
|
||||
github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
@@ -58,7 +65,7 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso=
|
||||
github.com/google/pprof v0.0.0-20230406165453-00490a63f317 h1:hFhpt7CTmR3DX+b4R19ydQFtofxT0Sv3QsKNMVQYTMQ=
|
||||
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=
|
||||
@@ -76,7 +83,7 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
||||
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/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
|
||||
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
|
||||
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
@@ -85,17 +92,25 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA=
|
||||
github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
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/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
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/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
|
||||
github.com/miekg/dns v1.1.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo=
|
||||
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
|
||||
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=
|
||||
@@ -104,37 +119,41 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
|
||||
github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
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/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
|
||||
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
|
||||
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||
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 h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
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/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk=
|
||||
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
|
||||
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
|
||||
github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0=
|
||||
github.com/refraction-networking/utls v1.2.3-0.20230308205431-4f1df6c200db h1:ULRv/GPW5KYDafE0FACN2no+HTCyQLUtfyOIeyp3GNc=
|
||||
github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/sagernet/sing v0.1.7 h1:g4vjr3q8SUlBZSx97Emz5OBfSMBxxW5Q8C2PfdoSo08=
|
||||
github.com/sagernet/sing-shadowsocks v0.1.1 h1:uFK2rlVeD/b1xhDwSMbUI2goWc6fOKxp+ZeKHZq6C9Q=
|
||||
github.com/sagernet/sing v0.2.3 h1:V50MvZ4c3Iij2lYFWPlzL1PyipwSzjGeN9x+Ox89vpk=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.1 h1:FvdLQOqpvxHBJUcUe4fvgiYP2XLLwH5i1DtXQviVEPw=
|
||||
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
||||
github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
|
||||
github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU=
|
||||
github.com/shoenig/go-m1cpu v0.1.4 h1:SZPIgRM2sEF9NJy50mRHu9PKGwxyyTTJIWvCtgVbozs=
|
||||
github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
||||
github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
|
||||
github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
|
||||
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
|
||||
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -161,10 +180,12 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU=
|
||||
github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
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/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
|
||||
github.com/xtls/reality v0.0.0-20230309125256-0d0713b108c8 h1:LLtLxEe3S0Ko+ckqt4t29RLskpNdOZfgjZCC2/Byr50=
|
||||
github.com/xtls/xray-core v1.8.0 h1:/OD0sDv6YIBqvE+cVfnqlKrtbMs0Fm9IP5BR5d8Eu4k=
|
||||
github.com/xtls/xray-core v1.8.0/go.mod h1:i9KWgbLyxg/NT+3+g4nE74Zp3DgTCP3X04YkSfsJeDI=
|
||||
github.com/xtls/reality v0.0.0-20230331223127-176a94313eda h1:psRJD2RrZbnI0OWyHvXfgYCPqlRM5q5SPDcjDoDBWhE=
|
||||
github.com/xtls/xray-core v1.8.1 h1:iSTTqXj82ZdwC1ah+eV331X4JTcnrDz+WuKuB/EB3P4=
|
||||
github.com/xtls/xray-core v1.8.1/go.mod h1:AXxSso0MZwUE4NhRocCfHCg73BtJ+T2dSpQVo1Cg9VM=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
@@ -174,23 +195,25 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||
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/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -208,8 +231,9 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -224,19 +248,20 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA=
|
||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
|
||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0=
|
||||
google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -61,8 +61,8 @@ elif [[ "${release}" == "fedora" ]]; then
|
||||
fi
|
||||
|
||||
elif [[ "${release}" == "debian" ]]; then
|
||||
if [[ ${os_version} -lt 8 ]]; then
|
||||
echo -e "${red} Please use Debian 8 or higher ${plain}\n" && exit 1
|
||||
if [[ ${os_version} -lt 10 ]]; then
|
||||
echo -e "${red} Please use Debian 10 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${red}Failed to check the OS version, please contact the author!${plain}" && exit 1
|
||||
@@ -79,6 +79,7 @@ install_base() {
|
||||
|
||||
#This function will be called when user installed x-ui out of sercurity
|
||||
config_after_install() {
|
||||
/usr/local/x-ui/x-ui migrate
|
||||
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
|
||||
read -p "Do you want to continue with the modification [y/n]? ": config_confirm
|
||||
if [[ x"${config_confirm}" == x"y" || x"${config_confirm}" == x"Y" ]]; then
|
||||
|
||||
34
main.go
34
main.go
@@ -51,8 +51,8 @@ func runWebServer() {
|
||||
}
|
||||
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
//信号量捕获处理
|
||||
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGKILL)
|
||||
// Trap shutdown signals
|
||||
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGTERM)
|
||||
for {
|
||||
sig := <-sigCh
|
||||
|
||||
@@ -97,7 +97,7 @@ func showSetting(show bool) {
|
||||
settingService := service.SettingService{}
|
||||
port, err := settingService.GetPort()
|
||||
if err != nil {
|
||||
fmt.Println("get current port fialed,error info:", err)
|
||||
fmt.Println("get current port failed,error info:", err)
|
||||
}
|
||||
userService := service.UserService{}
|
||||
userModel, err := userService.GetFirstUser()
|
||||
@@ -109,7 +109,7 @@ func showSetting(show bool) {
|
||||
if (username == "") || (userpasswd == "") {
|
||||
fmt.Println("current username or password is empty")
|
||||
}
|
||||
fmt.Println("current pannel settings as follows:")
|
||||
fmt.Println("current panel settings as follows:")
|
||||
fmt.Println("username:", username)
|
||||
fmt.Println("userpasswd:", userpasswd)
|
||||
fmt.Println("port:", port)
|
||||
@@ -133,7 +133,6 @@ func updateTgbotEnableSts(status bool) {
|
||||
logger.Infof("SetTgbotenabled[%v] success", status)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime string) {
|
||||
@@ -204,6 +203,19 @@ func updateSetting(port int, username string, password string) {
|
||||
}
|
||||
}
|
||||
|
||||
func migrateDb() {
|
||||
inboundService := service.InboundService{}
|
||||
|
||||
err := database.InitDB(config.GetDBPath())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("Start migrating database...")
|
||||
inboundService.MigrationRequirements()
|
||||
inboundService.RemoveOrphanedTraffics()
|
||||
fmt.Println("Migration done!")
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
runWebServer()
|
||||
@@ -234,9 +246,9 @@ func main() {
|
||||
settingCmd.IntVar(&port, "port", 0, "set panel port")
|
||||
settingCmd.StringVar(&username, "username", "", "set login username")
|
||||
settingCmd.StringVar(&password, "password", "", "set login password")
|
||||
settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "set telegrame bot token")
|
||||
settingCmd.StringVar(&tgbotRuntime, "tgbotRuntime", "", "set telegrame bot cron time")
|
||||
settingCmd.StringVar(&tgbotchatid, "tgbotchatid", "", "set telegrame bot chat id")
|
||||
settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "set telegram bot token")
|
||||
settingCmd.StringVar(&tgbotRuntime, "tgbotRuntime", "", "set telegram bot cron time")
|
||||
settingCmd.StringVar(&tgbotchatid, "tgbotchatid", "", "set telegram bot chat id")
|
||||
settingCmd.BoolVar(&enabletgbot, "enabletgbot", false, "enable telegram bot notify")
|
||||
|
||||
oldUsage := flag.Usage
|
||||
@@ -246,6 +258,7 @@ func main() {
|
||||
fmt.Println("Commands:")
|
||||
fmt.Println(" run run web panel")
|
||||
fmt.Println(" v2-ui migrate form v2-ui")
|
||||
fmt.Println(" migrate migrate form other/old x-ui")
|
||||
fmt.Println(" setting set settings")
|
||||
}
|
||||
|
||||
@@ -263,6 +276,8 @@ func main() {
|
||||
return
|
||||
}
|
||||
runWebServer()
|
||||
case "migrate":
|
||||
migrateDb()
|
||||
case "v2-ui":
|
||||
err := v2uiCmd.Parse(os.Args[2:])
|
||||
if err != nil {
|
||||
@@ -290,6 +305,9 @@ func main() {
|
||||
if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") {
|
||||
updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
|
||||
}
|
||||
if enabletgbot {
|
||||
updateTgbotEnableSts(enabletgbot)
|
||||
}
|
||||
default:
|
||||
fmt.Println("except 'run' or 'v2-ui' or 'setting' subcommands")
|
||||
fmt.Println()
|
||||
|
||||
@@ -647,7 +647,7 @@ class RealityStreamSettings extends XrayCommonClass {
|
||||
}
|
||||
|
||||
RealityStreamSettings.Settings = class extends XrayCommonClass {
|
||||
constructor(publicKey = '', fingerprint = '', serverName = '', spiderX= '/') {
|
||||
constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, serverName = '', spiderX= '/') {
|
||||
super();
|
||||
this.publicKey = publicKey;
|
||||
this.fingerprint = fingerprint;
|
||||
@@ -1243,22 +1243,20 @@ class Inbound extends XrayCommonClass {
|
||||
if (this.reality) {
|
||||
params.set("security", "reality");
|
||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
||||
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
||||
}
|
||||
if (this.stream.reality.shortIds != "") {
|
||||
if (this.stream.reality.shortIds.length > 0) {
|
||||
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
||||
}
|
||||
if (this.stream.reality.fingerprint != "") {
|
||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
||||
address = this.stream.reality.settings.serverName;
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
||||
params.set("spx", this.stream.reality.settings.spiderX);
|
||||
}
|
||||
if (this.stream.network === 'tcp') {
|
||||
if (this.stream.network === 'tcp' && !ObjectUtil.isEmpty(this.settings.vlesses[clientIndex].flow)) {
|
||||
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
||||
}
|
||||
}
|
||||
@@ -1355,23 +1353,21 @@ class Inbound extends XrayCommonClass {
|
||||
if (this.reality) {
|
||||
params.set("security", "reality");
|
||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||
if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
||||
params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
||||
}
|
||||
if (this.stream.reality.shortIds != "") {
|
||||
if (this.stream.reality.shortIds.length > 0) {
|
||||
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
||||
}
|
||||
if (this.stream.reality.fingerprint != "") {
|
||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
||||
address = this.stream.reality.settings.serverName;
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
||||
params.set("spx", this.stream.reality.settings.spiderX);
|
||||
}
|
||||
if (this.stream.network === 'tcp') {
|
||||
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
||||
if (this.stream.network === 'tcp' && !ObjectUtil.isEmpty(this.settings.trojans[clientIndex].flow)) {
|
||||
params.set("flow", this.settings.trojans[clientIndex].flow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,12 @@ func (a *APIController) initRouter(g *gin.RouterGroup) {
|
||||
|
||||
g.GET("/", a.inbounds)
|
||||
g.GET("/get/:id", a.inbound)
|
||||
g.GET("/getClientTraffics/:email", a.getClientTraffics)
|
||||
g.POST("/add", a.addInbound)
|
||||
g.POST("/del/:id", a.delInbound)
|
||||
g.POST("/update/:id", a.updateInbound)
|
||||
g.POST("/addClient/", a.addInboundClient)
|
||||
g.POST("/delClient/:email", a.delInboundClient)
|
||||
g.POST("/:id/delClient/:clientId", a.delInboundClient)
|
||||
g.POST("/updateClient/:index", a.updateInboundClient)
|
||||
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
|
||||
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
||||
@@ -38,6 +39,9 @@ func (a *APIController) inbounds(c *gin.Context) {
|
||||
func (a *APIController) inbound(c *gin.Context) {
|
||||
a.inboundController.getInbound(c)
|
||||
}
|
||||
func (a *APIController) getClientTraffics(c *gin.Context) {
|
||||
a.inboundController.getClientTraffics(c)
|
||||
}
|
||||
func (a *APIController) addInbound(c *gin.Context) {
|
||||
a.inboundController.addInbound(c)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
|
||||
g.POST("/del/:id", a.delInbound)
|
||||
g.POST("/update/:id", a.updateInbound)
|
||||
g.POST("/addClient", a.addInboundClient)
|
||||
g.POST("/delClient/:email", a.delInboundClient)
|
||||
g.POST("/:id/delClient/:clientId", a.delInboundClient)
|
||||
g.POST("/updateClient/:index", a.updateInboundClient)
|
||||
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
|
||||
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
||||
@@ -75,6 +75,15 @@ func (a *InboundController) getInbound(c *gin.Context) {
|
||||
}
|
||||
jsonObj(c, inbound, nil)
|
||||
}
|
||||
func (a *InboundController) getClientTraffics(c *gin.Context) {
|
||||
email := c.Param("email")
|
||||
clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email)
|
||||
if err != nil {
|
||||
jsonMsg(c, "Error getting traffics", err)
|
||||
return
|
||||
}
|
||||
jsonObj(c, clientTraffics, nil)
|
||||
}
|
||||
|
||||
func (a *InboundController) addInbound(c *gin.Context) {
|
||||
inbound := &model.Inbound{}
|
||||
@@ -148,15 +157,14 @@ func (a *InboundController) addInboundClient(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (a *InboundController) delInboundClient(c *gin.Context) {
|
||||
email := c.Param("email")
|
||||
inbound := &model.Inbound{}
|
||||
err := c.ShouldBind(inbound)
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
|
||||
return
|
||||
}
|
||||
clientId := c.Param("clientId")
|
||||
|
||||
err = a.inboundService.DelInboundClient(inbound, email)
|
||||
err = a.inboundService.DelInboundClient(id, clientId)
|
||||
if err != nil {
|
||||
jsonMsg(c, "something worng!", err)
|
||||
return
|
||||
|
||||
@@ -29,14 +29,18 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) {
|
||||
func (a *SUBController) subs(c *gin.Context) {
|
||||
subId := c.Param("subid")
|
||||
host := strings.Split(c.Request.Host, ":")[0]
|
||||
subs, err := a.subService.GetSubs(subId, host)
|
||||
if err != nil {
|
||||
subs, header, err := a.subService.GetSubs(subId, host)
|
||||
if err != nil || len(subs) == 0 {
|
||||
c.String(400, "Error!")
|
||||
} else {
|
||||
result := ""
|
||||
for _, sub := range subs {
|
||||
result += sub + "\n"
|
||||
}
|
||||
|
||||
// Add subscription-userinfo
|
||||
c.Writer.Header().Set("Subscription-Userinfo", header)
|
||||
|
||||
c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,24 @@
|
||||
}
|
||||
client.email = string;
|
||||
},
|
||||
resetClientTraffic(email,dbInboundId,iconElement) {
|
||||
this.$confirm({
|
||||
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
|
||||
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
||||
class: siderDrawer.isDarkTheme ? darkClass : '',
|
||||
okText: '{{ i18n "reset"}}',
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
onOk: async () => {
|
||||
iconElement.disabled = true;
|
||||
const msg = await HttpUtil.postWithModal('/xui/inbound/' + dbInboundId + '/resetClientTraffic/'+ email);
|
||||
if (msg.success) {
|
||||
this.clientModal.clientStats.up = 0;
|
||||
this.clientModal.clientStats.down = 0;
|
||||
}
|
||||
iconElement.disabled = false;
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<a-input :value="value" @input="$emit('input', $event.target.value)"></a-input>
|
||||
</template>
|
||||
<template v-else-if="type === 'number'">
|
||||
<a-input-number :value="value" @input="$emit('input', $event.target.value)"></a-input-number>
|
||||
<a-input type="number" :value="value" @input="$emit('input', $event.target.value)"></a-input>
|
||||
</template>
|
||||
<template v-else-if="type === 'textarea'">
|
||||
<a-textarea :value="value" @input="$emit('input', $event.target.value)" :auto-size="{ minRows: 10, maxRows: 10 }"></a-textarea>
|
||||
|
||||
@@ -103,6 +103,10 @@
|
||||
[[ sizeFormat(clientStats.down) ]]
|
||||
([[ sizeFormat(clientStats.up + clientStats.down) ]])
|
||||
</a-tag>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
||||
<a-icon type="retweet" @click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)" v-if="client.email.length > 0"></a-icon>
|
||||
</a-tooltip>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -1,24 +1,17 @@
|
||||
{{define "form/streamSettings"}}
|
||||
<!-- select stream network -->
|
||||
<a-form layout="inline">
|
||||
<table width="100%" class="ant-table-tbody">
|
||||
<tr>
|
||||
<td>{{ i18n "transmission" }}</td>
|
||||
<td>
|
||||
<a-form-item>
|
||||
<a-select v-model="inbound.stream.network" @change="streamNetworkChange"
|
||||
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||
<a-select-option value="tcp">tcp</a-select-option>
|
||||
<a-select-option value="kcp">kcp</a-select-option>
|
||||
<a-select-option value="ws">ws</a-select-option>
|
||||
<a-select-option value="http">http</a-select-option>
|
||||
<a-select-option value="quic">quic</a-select-option>
|
||||
<a-select-option value="grpc">grpc</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<a-form-item label="{{ i18n "transmission" }}">
|
||||
<a-select v-model="inbound.stream.network" @change="streamNetworkChange"
|
||||
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
|
||||
<a-select-option value="tcp">tcp</a-select-option>
|
||||
<a-select-option value="kcp">kcp</a-select-option>
|
||||
<a-select-option value="ws">ws</a-select-option>
|
||||
<a-select-option value="http">http</a-select-option>
|
||||
<a-select-option value="quic">quic</a-select-option>
|
||||
<a-select-option value="grpc">grpc</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- tcp -->
|
||||
|
||||
@@ -176,7 +176,6 @@
|
||||
<td>
|
||||
<a-form-item >
|
||||
<a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 250px">
|
||||
<a-select-option value=''>None</a-select-option>
|
||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
</td>
|
||||
<td v-else-if="inbound.reality">
|
||||
reality: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
|
||||
reality {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
|
||||
reality Destination: <a-tag :color="inbound.stream.reality.dest ? 'green' : 'orange'">[[ inbound.stream.reality.dest ]]</a-tag>
|
||||
</td>
|
||||
<td v-else>tls: <a-tag color="red">{{ i18n "disabled" }}</a-tag>
|
||||
</td>
|
||||
|
||||
@@ -612,22 +612,14 @@
|
||||
},
|
||||
delClient(dbInboundId,client) {
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
newDbInbound = new DBInbound(dbInbound);
|
||||
inbound = newDbInbound.toInbound();
|
||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
||||
index = this.findIndexOfClient(clients, client);
|
||||
clients.splice(index, 1);
|
||||
const data = {
|
||||
id: dbInboundId,
|
||||
settings: inbound.settings.toString(),
|
||||
};
|
||||
clientId = dbInbound.protocol == "trojan" ? client.password : client.id;
|
||||
this.$confirm({
|
||||
title: '{{ i18n "pages.inbounds.deleteInbound"}}',
|
||||
content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
|
||||
class: siderDrawer.isDarkTheme ? darkClass : '',
|
||||
okText: '{{ i18n "delete"}}',
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
onOk: () => this.submit('/xui/inbound/delClient/' + client.email, data),
|
||||
onOk: () => this.submit(`/xui/inbound/${dbInboundId}/delClient/${clientId}`),
|
||||
});
|
||||
},
|
||||
getClients(protocol, clientSettings) {
|
||||
@@ -655,8 +647,8 @@
|
||||
inbound = dbInbound.toInbound();
|
||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
||||
index = this.findIndexOfClient(clients, client);
|
||||
clients[index].enable = ! clients[index].enable
|
||||
await this.updateClient(inbound, dbInbound, index);
|
||||
clients[index].enable = !clients[index].enable;
|
||||
await this.updateClient(clients[index],dbInboundId, index);
|
||||
this.loading(false);
|
||||
},
|
||||
async submit(url, data) {
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
</a-col>
|
||||
|
||||
<a-col :lg="24" :xl="12">
|
||||
<temlate>
|
||||
<template>
|
||||
<a-select
|
||||
ref="selectLang"
|
||||
v-model="lang"
|
||||
@@ -66,7 +66,7 @@
|
||||
<span v-text="l.name"></span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</temlate>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
|
||||
@@ -252,11 +252,18 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
existEmail, err := s.checkEmailsExistForClients(clients)
|
||||
|
||||
var settings map[string]interface{}
|
||||
err = json.Unmarshal([]byte(data.Settings), &settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
interfaceClients := settings["clients"].([]interface{})
|
||||
existEmail, err := s.checkEmailsExistForClients(clients)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existEmail != "" {
|
||||
return common.NewError("Duplicate email:", existEmail)
|
||||
}
|
||||
@@ -266,21 +273,18 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var settings map[string]interface{}
|
||||
err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
|
||||
var oldSettings map[string]interface{}
|
||||
err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldClients := settings["clients"].([]interface{})
|
||||
var newClients []interface{}
|
||||
for _, client := range clients {
|
||||
newClients = append(newClients, client)
|
||||
}
|
||||
oldClients := oldSettings["clients"].([]interface{})
|
||||
oldClients = append(oldClients, interfaceClients...)
|
||||
|
||||
settings["clients"] = append(oldClients, newClients...)
|
||||
oldSettings["clients"] = oldClients
|
||||
|
||||
newSettings, err := json.MarshalIndent(settings, "", " ")
|
||||
newSettings, err := json.MarshalIndent(oldSettings, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -296,22 +300,50 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) error {
|
||||
return db.Save(oldInbound).Error
|
||||
}
|
||||
|
||||
func (s *InboundService) DelInboundClient(inbound *model.Inbound, email string) error {
|
||||
db := database.GetDB()
|
||||
err := s.DelClientStat(db, email)
|
||||
if err != nil {
|
||||
logger.Error("Delete stats Data Error")
|
||||
return err
|
||||
}
|
||||
|
||||
oldInbound, err := s.GetInbound(inbound.Id)
|
||||
func (s *InboundService) DelInboundClient(inboundId int, clientId string) error {
|
||||
oldInbound, err := s.GetInbound(inboundId)
|
||||
if err != nil {
|
||||
logger.Error("Load Old Data Error")
|
||||
return err
|
||||
}
|
||||
var settings map[string]interface{}
|
||||
err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldInbound.Settings = inbound.Settings
|
||||
email := ""
|
||||
client_key := "id"
|
||||
if oldInbound.Protocol == "trojan" {
|
||||
client_key = "password"
|
||||
}
|
||||
|
||||
inerfaceClients := settings["clients"].([]interface{})
|
||||
var newClients []interface{}
|
||||
for _, client := range inerfaceClients {
|
||||
c := client.(map[string]interface{})
|
||||
c_id := c[client_key].(string)
|
||||
if c_id == clientId {
|
||||
email = c["email"].(string)
|
||||
} else {
|
||||
newClients = append(newClients, client)
|
||||
}
|
||||
}
|
||||
|
||||
settings["clients"] = newClients
|
||||
newSettings, err := json.MarshalIndent(settings, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldInbound.Settings = string(newSettings)
|
||||
|
||||
db := database.GetDB()
|
||||
err = s.DelClientStat(db, email)
|
||||
if err != nil {
|
||||
logger.Error("Delete stats Data Error")
|
||||
return err
|
||||
}
|
||||
return db.Save(oldInbound).Error
|
||||
}
|
||||
|
||||
@@ -321,6 +353,14 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, index int) err
|
||||
return err
|
||||
}
|
||||
|
||||
var settings map[string]interface{}
|
||||
err = json.Unmarshal([]byte(data.Settings), &settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inerfaceClients := settings["clients"].([]interface{})
|
||||
|
||||
oldInbound, err := s.GetInbound(data.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -341,20 +381,18 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, index int) err
|
||||
}
|
||||
}
|
||||
|
||||
var settings map[string]interface{}
|
||||
err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
|
||||
var oldSettings map[string]interface{}
|
||||
err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settingsClients := settings["clients"].([]interface{})
|
||||
var newClients []interface{}
|
||||
newClients = append(newClients, clients[0])
|
||||
settingsClients[index] = newClients[0]
|
||||
settingsClients := oldSettings["clients"].([]interface{})
|
||||
settingsClients[index] = inerfaceClients[0]
|
||||
|
||||
settings["clients"] = settingsClients
|
||||
oldSettings["clients"] = settingsClients
|
||||
|
||||
newSettings, err := json.MarshalIndent(settings, "", " ")
|
||||
newSettings, err := json.MarshalIndent(oldSettings, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -380,45 +418,35 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, index int) err
|
||||
return db.Save(oldInbound).Error
|
||||
}
|
||||
|
||||
func (s *InboundService) AddTraffic(traffics []*xray.Traffic) (err error) {
|
||||
func (s *InboundService) AddTraffic(traffics []*xray.Traffic) error {
|
||||
if len(traffics) == 0 {
|
||||
return nil
|
||||
}
|
||||
db := database.GetDB()
|
||||
db = db.Model(model.Inbound{})
|
||||
tx := db.Begin()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
} else {
|
||||
tx.Commit()
|
||||
}
|
||||
}()
|
||||
for _, traffic := range traffics {
|
||||
if traffic.IsInbound {
|
||||
err = tx.Where("tag = ?", traffic.Tag).
|
||||
UpdateColumns(map[string]interface{}{
|
||||
"up": gorm.Expr("up + ?", traffic.Up),
|
||||
"down": gorm.Expr("down + ?", traffic.Down)}).Error
|
||||
if err != nil {
|
||||
return
|
||||
// Update traffics in a single transaction
|
||||
err := database.GetDB().Transaction(func(tx *gorm.DB) error {
|
||||
for _, traffic := range traffics {
|
||||
if traffic.IsInbound {
|
||||
update := tx.Model(&model.Inbound{}).Where("tag = ?", traffic.Tag).
|
||||
Updates(map[string]interface{}{
|
||||
"up": gorm.Expr("up + ?", traffic.Up),
|
||||
"down": gorm.Expr("down + ?", traffic.Down),
|
||||
})
|
||||
if update.Error != nil {
|
||||
return update.Error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err error) {
|
||||
if len(traffics) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
traffics, err = s.adjustTraffics(traffics)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db := database.GetDB()
|
||||
db = db.Model(xray.ClientTraffic{})
|
||||
tx := db.Begin()
|
||||
|
||||
defer func() {
|
||||
@@ -429,7 +457,32 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
|
||||
}
|
||||
}()
|
||||
|
||||
err = tx.Save(traffics).Error
|
||||
emails := make([]string, 0, len(traffics))
|
||||
for _, traffic := range traffics {
|
||||
emails = append(emails, traffic.Email)
|
||||
}
|
||||
dbClientTraffics := make([]*xray.ClientTraffic, 0, len(traffics))
|
||||
err = db.Model(xray.ClientTraffic{}).Where("email IN (?)", emails).Find(&dbClientTraffics).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dbClientTraffics, err = s.adjustTraffics(tx, dbClientTraffics)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for dbTraffic_index := range dbClientTraffics {
|
||||
for traffic_index := range traffics {
|
||||
if dbClientTraffics[dbTraffic_index].Email == traffics[traffic_index].Email {
|
||||
dbClientTraffics[dbTraffic_index].Up += traffics[traffic_index].Up
|
||||
dbClientTraffics[dbTraffic_index].Down += traffics[traffic_index].Down
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Save(dbClientTraffics).Error
|
||||
if err != nil {
|
||||
logger.Warning("AddClientTraffic update data ", err)
|
||||
}
|
||||
@@ -437,81 +490,56 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *InboundService) adjustTraffics(traffics []*xray.ClientTraffic) (full_traffics []*xray.ClientTraffic, err error) {
|
||||
db := database.GetDB()
|
||||
dbInbound := db.Model(model.Inbound{})
|
||||
txInbound := dbInbound.Begin()
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
txInbound.Rollback()
|
||||
} else {
|
||||
txInbound.Commit()
|
||||
func (s *InboundService) adjustTraffics(tx *gorm.DB, dbClientTraffics []*xray.ClientTraffic) ([]*xray.ClientTraffic, error) {
|
||||
inboundIds := make([]int, 0, len(dbClientTraffics))
|
||||
for _, dbClientTraffic := range dbClientTraffics {
|
||||
if dbClientTraffic.ExpiryTime < 0 {
|
||||
inboundIds = append(inboundIds, dbClientTraffic.InboundId)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, traffic := range traffics {
|
||||
inbound := &model.Inbound{}
|
||||
client_traffic := &xray.ClientTraffic{}
|
||||
err := db.Model(xray.ClientTraffic{}).Where("email = ?", traffic.Email).First(client_traffic).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
logger.Warning(err, traffic.Email)
|
||||
}
|
||||
continue
|
||||
}
|
||||
client_traffic.Up += traffic.Up
|
||||
client_traffic.Down += traffic.Down
|
||||
|
||||
err = txInbound.Where("id=?", client_traffic.InboundId).First(inbound).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
logger.Warning(err, traffic.Email)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// get clients
|
||||
clients, err := s.getClients(inbound)
|
||||
needUpdate := false
|
||||
if err == nil {
|
||||
for client_index, client := range clients {
|
||||
if traffic.Email == client.Email {
|
||||
if client.ExpiryTime < 0 {
|
||||
clients[client_index].ExpiryTime = (time.Now().Unix() * 1000) - client.ExpiryTime
|
||||
needUpdate = true
|
||||
}
|
||||
client_traffic.ExpiryTime = client.ExpiryTime
|
||||
client_traffic.Total = client.TotalGB
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if needUpdate {
|
||||
settings := map[string]interface{}{}
|
||||
json.Unmarshal([]byte(inbound.Settings), &settings)
|
||||
|
||||
// Convert clients to []interface to update clients in settings
|
||||
var clientsInterface []interface{}
|
||||
for _, c := range clients {
|
||||
clientsInterface = append(clientsInterface, interface{}(c))
|
||||
}
|
||||
|
||||
settings["clients"] = clientsInterface
|
||||
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = txInbound.Where("id=?", inbound.Id).Update("settings", string(modifiedSettings)).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
full_traffics = append(full_traffics, client_traffic)
|
||||
}
|
||||
return full_traffics, nil
|
||||
|
||||
if len(inboundIds) > 0 {
|
||||
var inbounds []*model.Inbound
|
||||
err := tx.Model(model.Inbound{}).Where("id IN (?)", inboundIds).Find(&inbounds).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for inbound_index := range inbounds {
|
||||
settings := map[string]interface{}{}
|
||||
json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
|
||||
clients, ok := settings["clients"].([]interface{})
|
||||
if ok {
|
||||
var newClients []interface{}
|
||||
for client_index := range clients {
|
||||
c := clients[client_index].(map[string]interface{})
|
||||
for traffic_index := range dbClientTraffics {
|
||||
if c["email"] == dbClientTraffics[traffic_index].Email {
|
||||
oldExpiryTime := c["expiryTime"].(float64)
|
||||
newExpiryTime := (time.Now().Unix() * 1000) - int64(oldExpiryTime)
|
||||
c["expiryTime"] = newExpiryTime
|
||||
dbClientTraffics[traffic_index].ExpiryTime = newExpiryTime
|
||||
break
|
||||
}
|
||||
}
|
||||
newClients = append(newClients, interface{}(c))
|
||||
}
|
||||
settings["clients"] = newClients
|
||||
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inbounds[inbound_index].Settings = string(modifiedSettings)
|
||||
}
|
||||
}
|
||||
err = tx.Save(inbounds).Error
|
||||
if err != nil {
|
||||
logger.Warning("AddClientTraffic update inbounds ", err)
|
||||
logger.Error(inbounds)
|
||||
}
|
||||
}
|
||||
|
||||
return dbClientTraffics, nil
|
||||
}
|
||||
|
||||
func (s *InboundService) DisableInvalidInbounds() (int64, error) {
|
||||
@@ -658,18 +686,18 @@ func (s *InboundService) GetClientTrafficTgBot(tguname string) ([]*xray.ClientTr
|
||||
return traffics, err
|
||||
}
|
||||
|
||||
func (s *InboundService) GetClientTrafficByEmail(email string) (traffic []*xray.ClientTraffic, err error) {
|
||||
func (s *InboundService) GetClientTrafficByEmail(email string) (traffic *xray.ClientTraffic, err error) {
|
||||
db := database.GetDB()
|
||||
var traffics []*xray.ClientTraffic
|
||||
|
||||
err = db.Model(xray.ClientTraffic{}).Where("email like ?", "%"+email+"%").Find(&traffics).Error
|
||||
err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Find(&traffics).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
logger.Warning(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return traffics, err
|
||||
return traffics[0], err
|
||||
}
|
||||
|
||||
func (s *InboundService) SearchClientTraffic(query string) (traffic *xray.ClientTraffic, err error) {
|
||||
@@ -720,3 +748,44 @@ func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error)
|
||||
}
|
||||
return inbounds, nil
|
||||
}
|
||||
|
||||
func (s *InboundService) MigrationRequirements() {
|
||||
db := database.GetDB()
|
||||
var inbounds []*model.Inbound
|
||||
err := db.Model(model.Inbound{}).Where("protocol IN (?)", []string{"vmess", "vless", "trojan"}).Find(&inbounds).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return
|
||||
}
|
||||
for inbound_index := range inbounds {
|
||||
settings := map[string]interface{}{}
|
||||
json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
|
||||
clients, ok := settings["clients"].([]interface{})
|
||||
if ok {
|
||||
var newClients []interface{}
|
||||
for client_index := range clients {
|
||||
c := clients[client_index].(map[string]interface{})
|
||||
|
||||
// Add email='' if it is not exists
|
||||
if _, ok := c["email"]; !ok {
|
||||
c["email"] = ""
|
||||
}
|
||||
|
||||
// Remove "flow": "xtls-rprx-direct"
|
||||
if _, ok := c["flow"]; ok {
|
||||
if c["flow"] == "xtls-rprx-direct" {
|
||||
c["flow"] = ""
|
||||
}
|
||||
}
|
||||
newClients = append(newClients, interface{}(c))
|
||||
}
|
||||
settings["clients"] = newClients
|
||||
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
inbounds[inbound_index].Settings = string(modifiedSettings)
|
||||
}
|
||||
}
|
||||
db.Save(inbounds)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"x-ui/database"
|
||||
"x-ui/database/model"
|
||||
"x-ui/logger"
|
||||
"x-ui/xray"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
"gorm.io/gorm"
|
||||
@@ -18,12 +19,15 @@ type SubService struct {
|
||||
inboundService InboundService
|
||||
}
|
||||
|
||||
func (s *SubService) GetSubs(subId string, host string) ([]string, error) {
|
||||
func (s *SubService) GetSubs(subId string, host string) ([]string, string, error) {
|
||||
s.address = host
|
||||
var result []string
|
||||
var header string
|
||||
var traffic xray.ClientTraffic
|
||||
var clientTraffics []xray.ClientTraffic
|
||||
inbounds, err := s.getInboundsBySubId(subId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
for _, inbound := range inbounds {
|
||||
clients, err := s.inboundService.getClients(inbound)
|
||||
@@ -37,22 +41,54 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, error) {
|
||||
if client.SubID == subId {
|
||||
link := s.getLink(inbound, client.Email)
|
||||
result = append(result, link)
|
||||
clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
db := database.GetDB()
|
||||
var inbounds []*model.Inbound
|
||||
err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"subId": "%s"%%`, subId)).Find(&inbounds).Error
|
||||
err := db.Model(model.Inbound{}).Preload("ClientStats").Where("settings like ?", fmt.Sprintf(`%%"subId": "%s"%%`, subId)).Find(&inbounds).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
return inbounds, nil
|
||||
}
|
||||
|
||||
func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, email string) xray.ClientTraffic {
|
||||
for _, traffic := range traffics {
|
||||
if traffic.Email == email {
|
||||
return traffic
|
||||
}
|
||||
}
|
||||
return xray.ClientTraffic{}
|
||||
}
|
||||
|
||||
func (s *SubService) getLink(inbound *model.Inbound, email string) string {
|
||||
switch inbound.Protocol {
|
||||
case "vmess":
|
||||
@@ -269,29 +305,35 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
}
|
||||
}
|
||||
|
||||
if security == "xtls" {
|
||||
params["security"] = "xtls"
|
||||
xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
|
||||
alpns, _ := xtlsSetting["alpn"].([]interface{})
|
||||
var alpn []string
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
if len(alpn) > 0 {
|
||||
params["alpn"] = strings.Join(alpn, ",")
|
||||
}
|
||||
|
||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
||||
if xtlsSetting != nil {
|
||||
if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
|
||||
params["sni"], _ = sniValue.(string)
|
||||
if security == "reality" {
|
||||
params["security"] = "reality"
|
||||
realitySetting, _ := stream["realitySettings"].(map[string]interface{})
|
||||
realitySettings, _ := searchKey(realitySetting, "settings")
|
||||
if realitySetting != nil {
|
||||
if sniValue, ok := searchKey(realitySetting, "serverNames"); ok {
|
||||
sNames, _ := sniValue.([]interface{})
|
||||
params["sni"], _ = sNames[0].(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
||||
params["fp"], _ = fpValue.(string)
|
||||
if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok {
|
||||
params["pbk"], _ = pbkValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
|
||||
if insecure.(bool) {
|
||||
params["allowInsecure"] = "1"
|
||||
if sidValue, ok := searchKey(realitySetting, "shortIds"); ok {
|
||||
shortIds, _ := sidValue.([]interface{})
|
||||
params["sid"], _ = shortIds[0].(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok {
|
||||
if fp, ok := fpValue.(string); ok && len(fp) > 0 {
|
||||
params["fp"] = fp
|
||||
}
|
||||
}
|
||||
if spxValue, ok := searchKey(realitySettings, "spiderX"); ok {
|
||||
if spx, ok := spxValue.(string); ok && len(spx) > 0 {
|
||||
params["spx"] = spx
|
||||
}
|
||||
}
|
||||
if serverName, ok := searchKey(realitySettings, "serverName"); ok {
|
||||
if sname, ok := serverName.(string); ok && len(sname) > 0 {
|
||||
address = sname
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,11 +341,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
|
||||
serverName, _ := xtlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
link := fmt.Sprintf("vless://%s@%s:%d", uuid, address, port)
|
||||
@@ -413,29 +450,35 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||
}
|
||||
}
|
||||
|
||||
if security == "xtls" {
|
||||
params["security"] = "xtls"
|
||||
xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
|
||||
alpns, _ := xtlsSetting["alpn"].([]interface{})
|
||||
var alpn []string
|
||||
for _, a := range alpns {
|
||||
alpn = append(alpn, a.(string))
|
||||
}
|
||||
if len(alpn) > 0 {
|
||||
params["alpn"] = strings.Join(alpn, ",")
|
||||
}
|
||||
|
||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
||||
if xtlsSetting != nil {
|
||||
if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
|
||||
params["sni"], _ = sniValue.(string)
|
||||
if security == "reality" {
|
||||
params["security"] = "reality"
|
||||
realitySetting, _ := stream["realitySettings"].(map[string]interface{})
|
||||
realitySettings, _ := searchKey(realitySetting, "settings")
|
||||
if realitySetting != nil {
|
||||
if sniValue, ok := searchKey(realitySetting, "serverNames"); ok {
|
||||
sNames, _ := sniValue.([]interface{})
|
||||
params["sni"], _ = sNames[0].(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
||||
params["fp"], _ = fpValue.(string)
|
||||
if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok {
|
||||
params["pbk"], _ = pbkValue.(string)
|
||||
}
|
||||
if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
|
||||
if insecure.(bool) {
|
||||
params["allowInsecure"] = "1"
|
||||
if sidValue, ok := searchKey(realitySettings, "shortIds"); ok {
|
||||
shortIds, _ := sidValue.([]interface{})
|
||||
params["sid"], _ = shortIds[0].(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok {
|
||||
if fp, ok := fpValue.(string); ok && len(fp) > 0 {
|
||||
params["fp"] = fp
|
||||
}
|
||||
}
|
||||
if spxValue, ok := searchKey(realitySettings, "spiderX"); ok {
|
||||
if spx, ok := spxValue.(string); ok && len(spx) > 0 {
|
||||
params["spx"] = spx
|
||||
}
|
||||
}
|
||||
if serverName, ok := searchKey(realitySettings, "serverName"); ok {
|
||||
if sname, ok := serverName.(string); ok && len(sname) > 0 {
|
||||
address = sname
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -443,11 +486,6 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
|
||||
serverName, _ := xtlsSetting["serverName"].(string)
|
||||
if serverName != "" {
|
||||
address = serverName
|
||||
}
|
||||
}
|
||||
|
||||
link := fmt.Sprintf("trojan://%s@%s:%d", password, address, port)
|
||||
|
||||
@@ -404,38 +404,36 @@ func (t *Tgbot) getClientUsage(chatId int64, tgUserName string) {
|
||||
}
|
||||
|
||||
func (t *Tgbot) searchClient(chatId int64, email string) {
|
||||
traffics, err := t.inboundService.GetClientTrafficByEmail(email)
|
||||
traffic, err := t.inboundService.GetClientTrafficByEmail(email)
|
||||
if err != nil {
|
||||
logger.Warning(err)
|
||||
msg := "❌ Something went wrong!"
|
||||
t.SendMsgToTgbot(chatId, msg)
|
||||
return
|
||||
}
|
||||
if len(traffics) == 0 {
|
||||
if traffic == nil {
|
||||
msg := "No result!"
|
||||
t.SendMsgToTgbot(chatId, msg)
|
||||
return
|
||||
}
|
||||
for _, traffic := range traffics {
|
||||
expiryTime := ""
|
||||
if traffic.ExpiryTime == 0 {
|
||||
expiryTime = "♾Unlimited"
|
||||
} else if traffic.ExpiryTime < 0 {
|
||||
expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-86400000)
|
||||
} else {
|
||||
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
total := ""
|
||||
if traffic.Total == 0 {
|
||||
total = "♾Unlimited"
|
||||
} else {
|
||||
total = common.FormatTraffic((traffic.Total))
|
||||
}
|
||||
output := fmt.Sprintf("💡 Active: %t\r\n📧 Email: %s\r\n🔼 Upload↑: %s\r\n🔽 Download↓: %s\r\n🔄 Total: %s / %s\r\n📅 Expire in: %s\r\n",
|
||||
traffic.Enable, traffic.Email, common.FormatTraffic(traffic.Up), common.FormatTraffic(traffic.Down), common.FormatTraffic((traffic.Up + traffic.Down)),
|
||||
total, expiryTime)
|
||||
t.SendMsgToTgbot(chatId, output)
|
||||
expiryTime := ""
|
||||
if traffic.ExpiryTime == 0 {
|
||||
expiryTime = "♾Unlimited"
|
||||
} else if traffic.ExpiryTime < 0 {
|
||||
expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-86400000)
|
||||
} else {
|
||||
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
total := ""
|
||||
if traffic.Total == 0 {
|
||||
total = "♾Unlimited"
|
||||
} else {
|
||||
total = common.FormatTraffic((traffic.Total))
|
||||
}
|
||||
output := fmt.Sprintf("💡 Active: %t\r\n📧 Email: %s\r\n🔼 Upload↑: %s\r\n🔽 Download↓: %s\r\n🔄 Total: %s / %s\r\n📅 Expire in: %s\r\n",
|
||||
traffic.Enable, traffic.Email, common.FormatTraffic(traffic.Up), common.FormatTraffic(traffic.Down), common.FormatTraffic((traffic.Up + traffic.Down)),
|
||||
total, expiryTime)
|
||||
t.SendMsgToTgbot(chatId, output)
|
||||
}
|
||||
|
||||
func (t *Tgbot) searchInbound(chatId int64, remark string) {
|
||||
|
||||
@@ -69,7 +69,6 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
||||
}
|
||||
|
||||
s.inboundService.DisableInvalidClients()
|
||||
s.inboundService.RemoveOrphanedTraffics()
|
||||
|
||||
inbounds, err := s.inboundService.GetAllInbounds()
|
||||
if err != nil {
|
||||
@@ -124,7 +123,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
||||
}
|
||||
|
||||
settings["clients"] = final_clients
|
||||
modifiedSettings, err := json.Marshal(settings)
|
||||
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"domainName" = "Domain name"
|
||||
"additional" = "Alter ID"
|
||||
"monitor" = "Listen IP"
|
||||
"certificate" = "Certificat"
|
||||
"certificate" = "Certificate"
|
||||
"fail" = "Fail"
|
||||
"success" = " Success"
|
||||
"getVersion" = "Get version"
|
||||
@@ -210,15 +210,15 @@
|
||||
"xrayConfigTemplate" = "Xray Configuration Template"
|
||||
"xrayConfigTemplateDesc" = "Generate the final xray configuration file based on this template, restart the panel to take effect."
|
||||
"xrayConfigTorrent" = "Ban bittorrent usage"
|
||||
"xrayConfigTorrentDesc" = "Change the configuration temlate to avoid using bittorrent by users, restart the panel to take effect"
|
||||
"xrayConfigTorrentDesc" = "Change the configuration template to avoid using bittorrent by users, restart the panel to take effect"
|
||||
"xrayConfigPrivateIp" = "Ban private ip range to connect"
|
||||
"xrayConfigPrivateIpDesc" = "Change the configuration temlate to avoid connecting with private IP ranges, restart the panel to take effect"
|
||||
"xrayConfigPrivateIpDesc" = "Change the configuration template to avoid connecting with private IP ranges, restart the panel to take effect"
|
||||
"xrayConfigInbounds" = "Configuration of Inbounds"
|
||||
"xrayConfigInboundsDesc" = "Change the configuration temlate to accept special clients, restart the panel to take effect"
|
||||
"xrayConfigInboundsDesc" = "Change the configuration template to accept special clients, restart the panel to take effect"
|
||||
"xrayConfigOutbounds" = "Configuration of Outbounds"
|
||||
"xrayConfigOutboundsDesc" = "Change the configuration temlate to define outgoing ways for this server, restart the panel to take effect"
|
||||
"xrayConfigOutboundsDesc" = "Change the configuration template to define outgoing ways for this server, restart the panel to take effect"
|
||||
"xrayConfigRoutings" = "Configuration of Routing rules"
|
||||
"xrayConfigRoutingsDesc" = "Change the configuration temlate to define Routing rules for this server, restart the panel to take effect"
|
||||
"xrayConfigRoutingsDesc" = "Change the configuration template to define Routing rules for this server, restart the panel to take effect"
|
||||
"telegramBotEnable" = "Enable telegram bot"
|
||||
"telegramBotEnableDesc" = "Restart the panel to take effect"
|
||||
"telegramToken" = "Telegram Token"
|
||||
|
||||
4
x-ui.sh
4
x-ui.sh
@@ -53,8 +53,8 @@ elif [[ "${release}" == "fedora" ]]; then
|
||||
fi
|
||||
|
||||
elif [[ "${release}" == "debian" ]]; then
|
||||
if [[ ${os_version} -lt 8 ]]; then
|
||||
echo -e "${red} Please use Debian 8 or higher ${plain}\n" && exit 1
|
||||
if [[ ${os_version} -lt 10 ]]; then
|
||||
echo -e "${red} Please use Debian 10 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user