Compare commits

...

70 Commits

Author SHA1 Message Date
MHSanaei
045c549937 v1.7.8 2023-08-29 23:19:39 +03:30
MHSanaei
25430b7818 [ss] fix adding ietf clients by api
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-29 22:32:49 +03:30
MHSanaei
6aeedac051 optimized finding client index
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-29 22:30:52 +03:30
MHSanaei
9c231a41d1 change xray default to v1.8.4 2023-08-29 22:17:55 +03:30
MHSanaei
ba673778ea fixed - Transparent Proxy button 2023-08-29 22:14:59 +03:30
MHSanaei
8698024b61 upgrade go to 1.21 2023-08-29 22:14:12 +03:30
dependabot[bot]
e8b576fa65 Bump docker/setup-buildx-action from 2.9.1 to 2.10.0 (#979)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.9.1 to 2.10.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2.9.1...v2.10.0)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-29 10:19:10 +03:30
Karo Yousefi
c49147c5db Setting up a Telegram bot README.md (#972)
* Update README.md

* Uploaded media

Uploaded 5 photos related to setting up the Telegram bot section.

* Update README.md
2023-08-27 02:18:09 +03:30
MHSanaei
149bd0ec51 v1.7.7 2023-08-26 22:24:56 +03:30
MHSanaei
f3280b46fe pic v1.7.7 2023-08-26 21:06:14 +03:30
MHSanaei
0f91e4d5f6 update dependencies 2023-08-26 20:20:21 +03:30
MHSanaei
18617afd43 add tls ocspStapling
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-26 18:52:38 +03:30
MHSanaei
731e83a7da Clean legacy vmess
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-26 16:58:55 +03:30
MHSanaei
1c1f53267a Add encrypt subscription ON/OFF switch
Co-Authored-By: SudoSpace <79229394+sudospaes@users.noreply.github.com>
2023-08-26 16:54:01 +03:30
MHSanaei
8489f5f528 some changes
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-26 16:07:02 +03:30
MHSanaei
872974910c Show ALPN order
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-26 15:45:53 +03:30
MHSanaei
147999dd88 [sub] support client-side group name
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-26 15:41:52 +03:30
MHSanaei
fe22cbd0e5 Transparent Proxy with sockopt Stream Setting
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-26 15:39:18 +03:30
MHSanaei
1277285d08 [db] Enbancement add traffic fully transactional
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-26 15:19:51 +03:30
MHSanaei
75df8a05f1 [ss] add ietf methods
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-26 15:12:32 +03:30
MHSanaei
38e1d0f94e [sub] improve usage info in Remark
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-26 15:11:12 +03:30
dependabot[bot]
71c1a05386 Bump actions/checkout from 3.5.3 to 3.6.0 (#960)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3.5.3...v3.6.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-25 14:51:41 +03:30
Hamidreza
4a6bd23873 daily clear logs #952 2023-08-24 17:14:51 +03:30
dependabot[bot]
8a1d647547 Bump gorm.io/gorm from 1.25.3 to 1.25.4 (#942)
Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.25.3 to 1.25.4.
- [Release notes](https://github.com/go-gorm/gorm/releases)
- [Commits](https://github.com/go-gorm/gorm/compare/v1.25.3...v1.25.4)

---
updated-dependencies:
- dependency-name: gorm.io/gorm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-21 14:32:07 +03:30
dependabot[bot]
fb5180a890 Bump github.com/mymmrac/telego from 0.26.0 to 0.26.1 (#943)
Bumps [github.com/mymmrac/telego](https://github.com/mymmrac/telego) from 0.26.0 to 0.26.1.
- [Release notes](https://github.com/mymmrac/telego/releases)
- [Commits](https://github.com/mymmrac/telego/compare/v0.26.0...v0.26.1)

---
updated-dependencies:
- dependency-name: github.com/mymmrac/telego
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-21 14:31:51 +03:30
quydang
d2c75b94cf Added Vietnamese Language (#939) 2023-08-21 11:32:01 +03:30
MHSanaei
edfc2d8d93 fix switch enable function for clients
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-17 15:26:32 +03:30
dependabot[bot]
dd177b19f7 Bump gorm.io/driver/sqlite from 1.5.2 to 1.5.3 (#919)
Bumps [gorm.io/driver/sqlite](https://github.com/go-gorm/sqlite) from 1.5.2 to 1.5.3.
- [Commits](https://github.com/go-gorm/sqlite/compare/v1.5.2...v1.5.3)

---
updated-dependencies:
- dependency-name: gorm.io/driver/sqlite
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-17 15:21:15 +03:30
MHSanaei
ae7283fc73 fix finding client issue #884
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-12 17:18:24 +03:30
dependabot[bot]
51a7c56cae Bump gorm.io/gorm from 1.25.2 to 1.25.3 (#889)
Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.25.2 to 1.25.3.
- [Release notes](https://github.com/go-gorm/gorm/releases)
- [Commits](https://github.com/go-gorm/gorm/compare/v1.25.2...v1.25.3)

---
updated-dependencies:
- dependency-name: gorm.io/gorm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-12 12:52:09 +03:30
MasterKia
c812168d6e Attribution (#887) 2023-08-10 03:36:42 +03:30
MHSanaei
fa10ae14f7 v1.7.6 2023-08-09 15:30:32 +03:30
MHSanaei
ed53df5ef3 multi user HTTP & Socks inbounds
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-09 01:43:58 +03:30
MHSanaei
b541290ded [front] better info modal
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-09 01:03:52 +03:30
MHSanaei
792fe6d9ef [front] better info modal
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-09 00:58:53 +03:30
MHSanaei
eb0c1dabf1 bash - Cloudflare SSL Certificate
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-09 00:52:40 +03:30
MHSanaei
e00c3f1823 add panel usage to main page 2023-08-09 00:37:05 +03:30
MHSanaei
05bc655e16 update dependencies 2023-08-09 00:34:09 +03:30
dependabot[bot]
7cddada8b4 Bump actions/setup-go from 4.0.1 to 4.1.0 (#879)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4.0.1 to 4.1.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4.0.1...v4.1.0)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-08 22:23:08 +03:30
Hamidreza
24eb36715a Add iran.dat when updating xray (#870)
we can download iran.dat, because it's one of the required files in our settings
2023-08-08 22:21:02 +03:30
Hiradpi
22cf278ce2 add Arch Linux support to install.sh (#873)
* Update install.sh

Arch linux support added

* Update install.sh
2023-08-08 22:20:16 +03:30
dependabot[bot]
5ab5986bd0 Bump golang.org/x/text from 0.11.0 to 0.12.0 (#876)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.11.0 to 0.12.0.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-08 22:03:38 +03:30
MHSanaei
f0775abc67 [install] stop service after download
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-08 22:01:49 +03:30
MHSanaei
d424b4bc32 [ss] fix 2022 links #878
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-08-08 21:58:41 +03:30
MHSanaei
329889ec00 v1.7.5 2023-08-02 17:19:50 +03:30
MHSanaei
95268dbc61 update dependencies 2023-08-02 17:02:32 +03:30
dependabot[bot]
9a3200c9b5 Bump github.com/mymmrac/telego from 0.25.1 to 0.26.0 (#852)
Bumps [github.com/mymmrac/telego](https://github.com/mymmrac/telego) from 0.25.1 to 0.26.0.
- [Release notes](https://github.com/mymmrac/telego/releases)
- [Commits](https://github.com/mymmrac/telego/compare/v0.25.1...v0.26.0)

---
updated-dependencies:
- dependency-name: github.com/mymmrac/telego
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-02 14:23:19 +03:30
TeleDark
c213fb6216 Display 'terminated' in title when service is terminated (#839) 2023-08-02 00:28:51 +03:30
somebodywashere
dd0217b46b IP Limit Tweaks to reduce false bans (#850)
* IP Limit Tweaks to reduce false bans
1) Check IPs every 10s instead of 20s
2) F2B jail: maxretry 3 -> 4, findtime 100 -> 60

* USERS SHOULD UPDATE BANTIME ONCE AFTER UPDATE
to recreate jail for Ip Limit
2023-08-02 00:28:16 +03:30
MMR
b805bf6222 change bootmortis project to MasterKia fork (#849)
* Update x-ui.sh

change bootmortis project to MasterKia fork

* Update DockerInit.sh

replace bootmortis with MasterKia

* Update release.yml

Replace bootmortis with MasterKia
2023-08-02 00:27:12 +03:30
dependabot[bot]
07b9474212 Bump github.com/shirou/gopsutil/v3 from 3.23.6 to 3.23.7 (#845)
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.23.6 to 3.23.7.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.23.6...v3.23.7)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-01 13:18:29 +03:30
MHSanaei
bf971911d5 log level & syslog
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-07-31 20:11:47 +03:30
MHSanaei
c46ced0c4c fix logs in api
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-07-31 19:52:28 +03:30
MHSanaei
dd3bbbc4af [SS] fix bulk creation
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-07-31 19:49:27 +03:30
dependabot[bot]
a97f90d225 Bump svenstaro/upload-release-action from 2.6.1 to 2.7.0 (#834)
Bumps [svenstaro/upload-release-action](https://github.com/svenstaro/upload-release-action) from 2.6.1 to 2.7.0.
- [Release notes](https://github.com/svenstaro/upload-release-action/releases)
- [Changelog](https://github.com/svenstaro/upload-release-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/svenstaro/upload-release-action/compare/2.6.1...2.7.0)

---
updated-dependencies:
- dependency-name: svenstaro/upload-release-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-29 16:01:33 +03:30
MHSanaei
6066edd510 sockopt acceptProxyProtocol for h2 , gRPC #773 2023-07-29 15:52:02 +03:30
MHSanaei
eaec9e54ad random password button for kcp , quic 2023-07-28 18:27:04 +03:30
dependabot[bot]
fafcb2e8e7 Bump google.golang.org/grpc from 1.56.2 to 1.57.0 (#823)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.56.2 to 1.57.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.56.2...v1.57.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-27 17:55:28 +03:30
MHSanaei
145ea1e6f1 full multiuser shadowsocks
full multiuser shadowsocks +
fix logs after api changes

Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-07-27 11:58:46 +03:30
MHSanaei
4cfed17650 [api] fix actions for shadowsocks
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2023-07-27 11:34:46 +03:30
MHSanaei
c2b1fb4855 because of #815 now we can use XTLS/Xray-core
change MHSanaei/Xray-core to XTLS/Xray-core
2023-07-27 11:06:27 +03:30
somebodywashere
09807b39aa No reason to write to BlockedIPs (#815) 2023-07-26 20:03:28 +03:30
Hamidreza
a0ec2f3972 Merge pull request #798 from hamid-gh98/main
Update README.md
2023-07-21 18:46:40 +03:30
Hamidreza Ghavami
e4b1dc20c3 Update README.md 2023-07-21 19:45:20 +04:30
Hamidreza
2f05d4960e silence allowipv6 warning in docker 2023-07-20 21:54:51 +03:30
Hamidreza
e63d2644bd Fix fail2ban inside DockerEntrypoint.sh 2023-07-20 21:48:55 +03:30
MHSanaei
56e4d13179 change date format to days remaining
example:
kkv4fhs4: 5.00GB📊- 6 Days
far6160p: 2.00GB📊- 23 Hours
Co-Authored-By: somebodywashere <68244480+somebodywashere@users.noreply.github.com>
2023-07-19 20:46:06 +03:30
MHSanaei
0dd0ba717f fail2ban status + apt-get purge 2023-07-19 15:06:55 +03:30
Hamidreza
8050330e2e Fix x-ui.sh bug #778 2023-07-19 12:34:38 +03:30
MHSanaei
65e35c1711 v1.7.1 2023-07-18 19:20:32 +03:30
67 changed files with 1537 additions and 635 deletions

View File

@@ -11,12 +11,12 @@ jobs:
steps:
- name: Check out the code
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.9.1
uses: docker/setup-buildx-action@v2.10.0
- name: Log in to GitHub Container Registry
uses: docker/login-action@v2.2.0

View File

@@ -14,12 +14,12 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout repository
uses: actions/checkout@v3.5.3
uses: actions/checkout@v3.6.0
- name: Setup Go
uses: actions/setup-go@v4.0.1
uses: actions/setup-go@v4.1.0
with:
go-version: 'stable'
go-version: '1.21'
- name: Install dependencies for arm64
if: matrix.platform == 'arm64'
@@ -47,18 +47,18 @@ jobs:
# Download dependencies
if [ "${{ matrix.platform }}" == "amd64" ]; then
wget https://github.com/mhsanaei/Xray-core/releases/latest/download/Xray-linux-64.zip
wget https://github.com/XTLS/Xray-core/releases/download/v1.8.4/Xray-linux-64.zip
unzip Xray-linux-64.zip
rm -f Xray-linux-64.zip
else
wget https://github.com/mhsanaei/Xray-core/releases/latest/download/Xray-linux-arm64-v8a.zip
wget https://github.com/XTLS/Xray-core/releases/download/v1.8.4/Xray-linux-arm64-v8a.zip
unzip Xray-linux-arm64-v8a.zip
rm -f Xray-linux-arm64-v8a.zip
fi
rm -f geoip.dat geosite.dat iran.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 https://github.com/bootmortis/iran-hosted-domains/releases/latest/download/iran.dat
wget https://github.com/MasterKia/iran-hosted-domains/releases/latest/download/iran.dat
mv xray xray-linux-${{ matrix.platform }}
cd ../..
@@ -66,7 +66,7 @@ jobs:
run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui
- name: Upload
uses: svenstaro/upload-release-action@2.6.1
uses: svenstaro/upload-release-action@2.7.0
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}

View File

@@ -1,7 +1,7 @@
#!/bin/sh
# Start fail2ban
fail2ban-client -x -f start
fail2ban-client -x start
# Run x-ui
exec /app/x-ui

View File

@@ -18,11 +18,11 @@ esac
mkdir -p build/bin
cd build/bin
wget "https://github.com/mhsanaei/xray-core/releases/latest/download/Xray-linux-${ARCH}.zip"
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.4/Xray-linux-${ARCH}.zip"
unzip "Xray-linux-${ARCH}.zip"
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat iran.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"
wget "https://github.com/bootmortis/iran-hosted-domains/releases/latest/download/iran.dat"
wget "https://github.com/MasterKia/iran-hosted-domains/releases/latest/download/iran.dat"

View File

@@ -1,7 +1,7 @@
# ========================================================
# Stage: Builder
# ========================================================
FROM --platform=$BUILDPLATFORM golang:1.20.4-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:alpine AS builder
WORKDIR /app
ARG TARGETARCH
ENV CGO_ENABLED=1
@@ -36,7 +36,9 @@ COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
# Configure fail2ban
RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \
&& cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local \
&& sed -i "s/^\[ssh\]$/&\nenabled = false/" /etc/fail2ban/jail.local
&& sed -i "s/^\[ssh\]$/&\nenabled = false/" /etc/fail2ban/jail.local \
&& sed -i "s/^\[sshd\]$/&\nenabled = false/" /etc/fail2ban/jail.local \
&& sed -i "s/#allowipv6 = auto/allowipv6 = auto/g" /etc/fail2ban/fail2ban.conf
RUN chmod +x \
/app/DockerEntrypoint.sh \

View File

@@ -8,7 +8,7 @@
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](#)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
3x-ui panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese,Russian)**
3x-ui panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese,Russian,Vietnamese)**
**If you think this project is helpful to you, you may wish to give a** :star2:
**Buy Me a Coffee :**
@@ -23,10 +23,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
# Install custom version
To install your desired version you can add the version to the end of install command. Example for ver `v1.6.1`:
To install your desired version you can add the version to the end of install command. Example for ver `v1.7.7`:
```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v1.6.1
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v1.7.7
```
# SSL
@@ -37,7 +37,7 @@ certbot certonly --standalone --agree-tos --register-unsafely-without-email -d y
certbot renew --dry-run
```
You also can use `x-ui` menu then select `16. SSL Certificate Management`
You also can use `x-ui` menu then select `SSL Certificate Management`
# Features
@@ -191,6 +191,27 @@ If you want to use routing to WARP follow steps as below:
</details>
# IP Limit
<details>
<summary>Click for IP Limit details</summary>
**Note: IP Limit won't work correctly when using IP Tunnel**
- For versions up to `v1.6.1`:
- IP limit is built-in into the panel.
- For versions `v1.7.0` and newer:
- To make IP Limit work properly, you need to install fail2ban and its required files by following these steps:
1. Use the `x-ui` command inside the shell.
2. Select `IP Limit Management`.
3. Choose the appropriate options based on your needs.
</details>
# Telegram Bot
<details>
@@ -233,6 +254,27 @@ Reference syntax:
- Multi language bot
</details>
# Setting up Telegram bot
- Start [Botfather](https://t.me/BotFather) in your Telegram account:
![Botfather](./media/botfather.png)
- Create a new Bot using /newbot command: It will ask you 2 questions, A name and a username for your bot. Note that the username has to end with the word "bot".
![Create new bot](./media/newbot.png)
- Start the bot you've just created. You can find the link to your bot here.
![token](./media/token.png)
- Enter your panel and config Telegram bot settings like below:
![Panel Config](./media/panel-bot-config.png)
Enter your bot token in input field number 3.
Enter the user ID in input field number 4. The Telegram accounts with this id will be the bot admin. (You can enter more than one, Just separate them with ,)
- How to get Telegram user ID? Use this [bot](https://t.me/useridinfobot), Start the bot and it will give you the Telegram user ID.
![User ID](./media/user-id.png)
# API routes
<details>
@@ -294,12 +336,18 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
- [alireza0](https://github.com/alireza0/)
# Acknowledgment
- [Iran Hosted Domains](https://github.com/bootmortis/iran-hosted-domains) (License: **MIT**): _A comprehensive list of Iranian domains and services that are hosted within the country._
- [PersianBlocker](https://github.com/MasterKia/PersianBlocker) (License: **AGPLv3**): _An optimal and extensive list to block ads and trackers on Persian websites._
# Suggestion System
- Ubuntu 20.04+
- Debian 10+
- CentOS 8+
- Fedora 36+
- Arch Linux
# Pictures

View File

@@ -1 +1 @@
1.7.0
1.7.8

69
go.mod
View File

@@ -1,47 +1,49 @@
module x-ui
go 1.20
go 1.21
toolchain go1.21.0
require (
github.com/Calidity/gin-sessions v1.3.1
github.com/Workiva/go-datastructures v1.1.0
github.com/gin-gonic/gin v1.9.1
github.com/goccy/go-json v0.10.2
github.com/mymmrac/telego v0.25.1
github.com/mymmrac/telego v0.26.1
github.com/nicksnyder/go-i18n/v2 v2.2.1
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pelletier/go-toml/v2 v2.0.9
github.com/pelletier/go-toml/v2 v2.1.0
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v3 v3.23.6
github.com/xtls/xray-core v1.8.3
github.com/yaa110/go-persian-calendar v1.1.5
github.com/shirou/gopsutil/v3 v3.23.7
github.com/xtls/xray-core v1.8.4
go.uber.org/atomic v1.11.0
golang.org/x/text v0.11.0
google.golang.org/grpc v1.56.2
gorm.io/driver/sqlite v1.5.2
gorm.io/gorm v1.25.2
golang.org/x/text v0.12.0
google.golang.org/grpc v1.57.0
gorm.io/driver/sqlite v1.5.3
gorm.io/gorm v1.25.4
)
require (
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/bytedance/sonic v1.9.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/bytedance/sonic v1.10.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.0 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/fasthttp/router v1.4.19 // indirect
github.com/fasthttp/router v1.4.20 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
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.1 // indirect
github.com/go-playground/validator/v10 v10.15.2 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // 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
@@ -51,47 +53,48 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/kr/text v0.2.0 // 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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
github.com/onsi/ginkgo/v2 v2.12.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-19 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
github.com/quic-go/quic-go v0.35.1 // indirect
github.com/refraction-networking/utls v1.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
github.com/quic-go/quic-go v0.38.1 // indirect
github.com/refraction-networking/utls v1.4.3 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/sagernet/sing v0.2.7 // indirect
github.com/sagernet/sing-shadowsocks v0.2.2 // indirect
github.com/sagernet/sing v0.2.9 // indirect
github.com/sagernet/sing-shadowsocks v0.2.4 // indirect
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // 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.11 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.48.0 // indirect
github.com/xtls/reality v0.0.0-20230613075828-e07c3b04b983 // indirect
github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
golang.org/x/arch v0.4.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.10.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
golang.org/x/tools v0.12.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
gvisor.dev/gvisor v0.0.0-20230822212503-5bf4e5f98744 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
)

151
go.sum
View File

@@ -22,13 +22,18 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
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.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM=
github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.0 h1:qtNZduETEIWJVIyDl01BeNxur2rW9OwTQ/yBqFRkKEk=
github.com/bytedance/sonic v1.10.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 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
@@ -36,8 +41,8 @@ github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fp
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fasthttp/router v1.4.19 h1:RLE539IU/S4kfb4MP56zgP0TIBU9kEg0ID9GpWO0vqk=
github.com/fasthttp/router v1.4.19/go.mod h1:+Fh3YOd8x1+he6ZS+d2iUDBH9MGGZ1xQFUor0DE9rKE=
github.com/fasthttp/router v1.4.20 h1:yPeNxz5WxZGojzolKqiP15DTXnxZce9Drv577GBrDgU=
github.com/fasthttp/router v1.4.20/go.mod h1:um867yNQKtERxBm+C+yzgWxjspTiQoA8z86Ec3fK/tc=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
@@ -48,6 +53,7 @@ github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXb
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
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/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.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
@@ -55,15 +61,18 @@ github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SU
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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.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.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.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.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.15.2 h1:Ra5cll2/eF8X0Ff2+8SMD7euo2nenQ8WEpgqfy4NhHU=
github.com/go-playground/validator/v10 v10.15.2/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
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/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
@@ -93,8 +102,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
github.com/google/gofuzz v1.0.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-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ=
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/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=
@@ -123,12 +132,15 @@ github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
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.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
@@ -142,28 +154,31 @@ github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
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 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=
github.com/mymmrac/telego v0.25.1 h1:tMNmrRm0YGyLS56CBi0NDHwO1ZI6V7QMgX4KWSWuT1U=
github.com/mymmrac/telego v0.25.1/go.mod h1:nBO4SUqRV8j60JOS7trIr6bHPofwYCGJxYeqtQWgu2c=
github.com/mymmrac/telego v0.26.1 h1:BzWWzDOkov0SMVnSF+mLJ5ChYcTqmhTIyBWBGyi51hw=
github.com/mymmrac/telego v0.26.1/go.mod h1:kizipjY3MhxmkcGvyz8jiw/26vEKAhR2V7YTE69iqvw=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
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.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
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=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
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=
@@ -177,14 +192,12 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
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/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo=
github.com/quic-go/quic-go v0.35.1/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=
github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8=
github.com/refraction-networking/utls v1.3.2/go.mod h1:fmoaOww2bxzzEpIKOebIsnBvjQpqP7L2vcm/9KUfm/E=
github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM=
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.38.1 h1:M36YWA5dEhEeT+slOu/SwMEucbYd0YFidxG3KlGPZaE=
github.com/quic-go/quic-go v0.38.1/go.mod h1:ijnZM7JsFIkp4cRyjxJNIzdSfCLmUMg9wdyhGmg+SN4=
github.com/refraction-networking/utls v1.4.3 h1:BdWS3BSzCwWCFfMIXP3mjLAyQkdmog7diaD/OqFbAzM=
github.com/refraction-networking/utls v1.4.3/go.mod h1:4u9V/awOSBrRw6+federGmVJQfPtemEqLBXkML1b0bo=
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=
@@ -192,10 +205,10 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sagernet/sing v0.2.7 h1:cOy0FfPS8q7m0aJ51wS7LRQAGc9wF+fWhHtBDj99wy8=
github.com/sagernet/sing v0.2.7/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-shadowsocks v0.2.2 h1:ezSdVhrmIcwDXmCZF3bOJVMuVtTQWpda+1Op+Ie2TA4=
github.com/sagernet/sing-shadowsocks v0.2.2/go.mod h1:JIBWG6a7orB2HxBxYElViQFLUQxFVG7DuqIj8gD7uCQ=
github.com/sagernet/sing v0.2.9 h1:3wsTz+JG5Wzy65eZnh6AuCrD2QqcRF6Iq6f7ttmJsAo=
github.com/sagernet/sing v0.2.9/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
github.com/sagernet/sing-shadowsocks v0.2.4 h1:s/CqXlvFAZhlIoHWUwPw5CoNnQ9Ibki9pckjuugtVfY=
github.com/sagernet/sing-shadowsocks v0.2.4/go.mod h1:80fNKP0wnqlu85GZXV1H1vDPC/2t+dQbFggOw4XuFUM=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
@@ -203,8 +216,8 @@ github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJ
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil/v3 v3.23.6 h1:5y46WPI9QBKBbK7EEccUPNXpJpNrvPuTD0O2zHEHT08=
github.com/shirou/gopsutil/v3 v3.23.6/go.mod h1:j7QX50DrXYggrpN30W0Mo+I4/8U2UUIQrnrhqUeWrAU=
github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4=
github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -248,8 +261,9 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
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/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
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.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
@@ -266,12 +280,10 @@ github.com/valyala/fasthttp v1.48.0 h1:oJWvHb9BIZToTQS3MuQ2R3bJZiNSa2KiNdeI8A+79
github.com/valyala/fasthttp v1.48.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/xtls/reality v0.0.0-20230613075828-e07c3b04b983 h1:AMyzgjkh54WocjQSlCnT1LhDc/BKiUqtNOv40AkpURs=
github.com/xtls/reality v0.0.0-20230613075828-e07c3b04b983/go.mod h1:rkuAY1S9F8eI8gDiPDYvACE8e2uwkyg8qoOTuwWov7Y=
github.com/xtls/xray-core v1.8.3 h1:lxaVklPjLKqUU4ua4qH8SBaRcAaNHlH+LmXOx0U/Ejg=
github.com/xtls/xray-core v1.8.3/go.mod h1:i7t4JFnq828P2+XK0XjGQ8W9x78iu+EJ7jI4l3sonIw=
github.com/yaa110/go-persian-calendar v1.1.5 h1:EUipRRhzE6bR2NZaSyZ5BEOP46LGbUjzQgdC+Ivrbe4=
github.com/yaa110/go-persian-calendar v1.1.5/go.mod h1:qtnmHCS9u1EiwzzSCSttGoxD5NfV9ZMzymxFCBYmqfg=
github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6 h1:T+YCYGfFdzyaKTDCdZn/hEiKvsw6yUfd+e4hze0rCUw=
github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6/go.mod h1:rkuAY1S9F8eI8gDiPDYvACE8e2uwkyg8qoOTuwWov7Y=
github.com/xtls/xray-core v1.8.4 h1:YEoY3iLx/5zoNbt5HORG5LtPyzwICInFfoS+oPkYDIw=
github.com/xtls/xray-core v1.8.4/go.mod h1:GGD9elFSHa4IqOArW8gzMsEksPIqK/jdNLo8RcSMfnI=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -280,7 +292,11 @@ github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ
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=
go.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU=
go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=
go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -291,19 +307,19 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk
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.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/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.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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=
@@ -318,8 +334,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
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=
@@ -334,6 +350,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -349,13 +366,14 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.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.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.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=
@@ -363,8 +381,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@@ -378,8 +396,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
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=
@@ -396,41 +414,44 @@ 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-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
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=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/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.3.0/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=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.2 h1:TpQ+/dqCY4uCigCFyrfnrJnrW9zjpelWVoEVNy5qJkc=
gorm.io/driver/sqlite v1.5.2/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/driver/sqlite v1.5.3 h1:7/0dUgX28KAcopdfbRWWl68Rflh6osa4rDh+m51KL2g=
gorm.io/driver/sqlite v1.5.3/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
gvisor.dev/gvisor v0.0.0-20230822212503-5bf4e5f98744 h1:tE44CyJgxEGzoPtHs9GI7ddKdgEGCREQBP54AmaVM+I=
gvisor.dev/gvisor v0.0.0-20230822212503-5bf4e5f98744/go.mod h1:lYEMhXbxgudVhALYsMQrBaUAjM3NMinh8mKL1CJv7rc=
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=

View File

@@ -53,21 +53,28 @@ elif [[ "${release}" == "debian" ]]; then
if [[ ${os_version} -lt 10 ]]; then
echo -e "${red} Please use Debian 10 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "arch" ]]; then
echo "OS is ArchLinux"
else
echo -e "${red}Failed to check the OS version, please contact the author!${plain}" && exit 1
fi
install_base() {
case "${release}" in
centos | fedora)
yum install -y -q wget curl tar
;;
*)
apt install -y -q wget curl tar
;;
centos|fedora)
yum install -y -q wget curl tar
;;
arch)
pacman -Syu --noconfirm wget curl tar
;;
*)
apt install -y -q wget curl tar
;;
esac
}
# This function will be called when user installed x-ui out of sercurity
config_after_install() {
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
@@ -104,7 +111,6 @@ config_after_install() {
}
install_x-ui() {
systemctl stop x-ui
cd /usr/local/
if [ $# == 0 ]; then
@@ -131,6 +137,7 @@ install_x-ui() {
fi
if [[ -e /usr/local/x-ui/ ]]; then
systemctl stop x-ui
rm /usr/local/x-ui/ -rf
fi

View File

@@ -1,98 +1,118 @@
package logger
import (
"fmt"
"os"
"sync"
"time"
"github.com/op/go-logging"
)
var (
logger *logging.Logger
mu sync.Mutex
)
var logger *logging.Logger
var logBuffer []struct {
time string
level logging.Level
log string
}
func init() {
InitLogger(logging.INFO)
}
func InitLogger(level logging.Level) {
mu.Lock()
defer mu.Unlock()
newLogger := logging.MustGetLogger("x-ui")
var err error
var backend logging.Backend
var format logging.Formatter
ppid := os.Getppid()
if logger != nil {
return
if ppid == 1 {
backend, err = logging.NewSyslogBackend("")
format = logging.MustStringFormatter(
`%{level} - %{message}`,
)
}
if err != nil || ppid != 1 {
backend = logging.NewLogBackend(os.Stderr, "", 0)
format = logging.MustStringFormatter(
`%{time:2006/01/02 15:04:05} %{level} - %{message}`,
)
}
format := logging.MustStringFormatter(
`%{time:2006/01/02 15:04:05} %{level} - %{message}`,
)
newLogger := logging.MustGetLogger("x-ui")
backend := logging.NewLogBackend(os.Stderr, "", 0)
backendFormatter := logging.NewBackendFormatter(backend, format)
backendLeveled := logging.AddModuleLevel(backendFormatter)
backendLeveled.SetLevel(level, "")
newLogger.SetBackend(logging.MultiLogger(backendLeveled))
backendLeveled.SetLevel(level, "x-ui")
newLogger.SetBackend(backendLeveled)
logger = newLogger
}
func Debug(args ...interface{}) {
if logger != nil {
logger.Debug(args...)
}
logger.Debug(args...)
addToBuffer("DEBUG", fmt.Sprint(args...))
}
func Debugf(format string, args ...interface{}) {
if logger != nil {
logger.Debugf(format, args...)
}
logger.Debugf(format, args...)
addToBuffer("DEBUG", fmt.Sprintf(format, args...))
}
func Info(args ...interface{}) {
if logger != nil {
logger.Info(args...)
}
logger.Info(args...)
addToBuffer("INFO", fmt.Sprint(args...))
}
func Infof(format string, args ...interface{}) {
if logger != nil {
logger.Infof(format, args...)
}
logger.Infof(format, args...)
addToBuffer("INFO", fmt.Sprintf(format, args...))
}
func Warning(args ...interface{}) {
if logger != nil {
logger.Warning(args...)
}
logger.Warning(args...)
addToBuffer("WARNING", fmt.Sprint(args...))
}
func Warningf(format string, args ...interface{}) {
if logger != nil {
logger.Warningf(format, args...)
}
logger.Warningf(format, args...)
addToBuffer("WARNING", fmt.Sprintf(format, args...))
}
func Error(args ...interface{}) {
if logger != nil {
logger.Error(args...)
}
logger.Error(args...)
addToBuffer("ERROR", fmt.Sprint(args...))
}
func Errorf(format string, args ...interface{}) {
if logger != nil {
logger.Errorf(format, args...)
}
logger.Errorf(format, args...)
addToBuffer("ERROR", fmt.Sprintf(format, args...))
}
func Notice(args ...interface{}) {
if logger != nil {
logger.Notice(args...)
func addToBuffer(level string, newLog string) {
t := time.Now()
if len(logBuffer) >= 10240 {
logBuffer = logBuffer[1:]
}
logLevel, _ := logging.LogLevel(level)
logBuffer = append(logBuffer, struct {
time string
level logging.Level
log string
}{
time: t.Format("2006/01/02 15:04:05"),
level: logLevel,
log: newLog,
})
}
func Noticef(format string, args ...interface{}) {
if logger != nil {
logger.Noticef(format, args...)
func GetLogs(c int, level string) []string {
var output []string
logLevel, _ := logging.LogLevel(level)
for i := len(logBuffer) - 1; i >= 0 && len(output) <= c; i-- {
if logBuffer[i].level <= logLevel {
output = append(output, fmt.Sprintf("%s %s - %s", logBuffer[i].time, logBuffer[i].level, logBuffer[i].log))
}
}
return output
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 54 KiB

BIN
media/botfather.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 KiB

BIN
media/newbot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 KiB

BIN
media/panel-bot-config.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
media/token.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
media/user-id.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -3,12 +3,14 @@ package sub
import (
"encoding/base64"
"strings"
"x-ui/web/service"
"github.com/gin-gonic/gin"
)
type SUBController struct {
subService SubService
subService SubService
settingService service.SettingService
}
func NewSUBController(g *gin.RouterGroup) *SUBController {
@@ -24,9 +26,11 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) {
}
func (a *SUBController) subs(c *gin.Context) {
subEncrypt, _ := a.settingService.GetSubEncrypt()
subShowInfo, _ := a.settingService.GetSubShowInfo()
subId := c.Param("subid")
host := strings.Split(c.Request.Host, ":")[0]
subs, headers, err := a.subService.GetSubs(subId, host)
subs, headers, err := a.subService.GetSubs(subId, host, subShowInfo)
if err != nil || len(subs) == 0 {
c.String(400, "Error!")
} else {
@@ -40,6 +44,10 @@ func (a *SUBController) subs(c *gin.Context) {
c.Writer.Header().Set("Profile-Update-Interval", headers[1])
c.Writer.Header().Set("Profile-Title", headers[2])
c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
if subEncrypt {
c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
} else {
c.String(200, result)
}
}
}

View File

@@ -14,17 +14,18 @@ import (
"x-ui/xray"
"github.com/goccy/go-json"
ptime "github.com/yaa110/go-persian-calendar"
)
type SubService struct {
address string
showInfo bool
inboundService service.InboundService
settingServics service.SettingService
}
func (s *SubService) GetSubs(subId string, host string) ([]string, []string, error) {
func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string, []string, error) {
s.address = host
s.showInfo = showInfo
var result []string
var headers []string
var traffic xray.ClientTraffic
@@ -58,7 +59,7 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, []string, err
}
for _, client := range clients {
if client.Enable && client.SubID == subId {
link := s.getLink(inbound, client.Email, client.ExpiryTime)
link := s.getLink(inbound, client.Email)
result = append(result, link)
clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email))
}
@@ -124,32 +125,26 @@ func (s *SubService) getFallbackMaster(dest string) (*model.Inbound, error) {
return inbound, nil
}
func (s *SubService) getLink(inbound *model.Inbound, email string, expiryTime int64) string {
func (s *SubService) getLink(inbound *model.Inbound, email string) string {
switch inbound.Protocol {
case "vmess":
return s.genVmessLink(inbound, email, expiryTime)
return s.genVmessLink(inbound, email)
case "vless":
return s.genVlessLink(inbound, email, expiryTime)
return s.genVlessLink(inbound, email)
case "trojan":
return s.genTrojanLink(inbound, email, expiryTime)
return s.genTrojanLink(inbound, email)
case "shadowsocks":
return s.genShadowsocksLink(inbound, email, expiryTime)
return s.genShadowsocksLink(inbound, email)
}
return ""
}
func (s *SubService) genVmessLink(inbound *model.Inbound, email string, expiryTime int64) string {
func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
if inbound.Protocol != model.VMess {
return ""
}
remainedTraffic := s.getRemainedTraffic(email)
expiryTimeString := getExpiryTime(expiryTime)
remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString)
obj := map[string]interface{}{
"v": "2",
"ps": remark,
"add": s.address,
"port": inbound.Port,
"type": "none",
@@ -248,7 +243,7 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string, expiryTi
links := ""
for index, d := range domains {
domain := d.(map[string]interface{})
obj["ps"] = remark + "-" + domain["remark"].(string)
obj["ps"] = s.genRemark(inbound, email, domain["remark"].(string))
obj["add"] = domain["domain"].(string)
if index > 0 {
links += "\n"
@@ -259,11 +254,13 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string, expiryTi
return links
}
obj["ps"] = s.genRemark(inbound, email, "")
jsonStr, _ := json.MarshalIndent(obj, "", " ")
return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
}
func (s *SubService) genVlessLink(inbound *model.Inbound, email string, expiryTime int64) string {
func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
address := s.address
if inbound.Protocol != model.VLESS {
return ""
@@ -456,16 +453,11 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string, expiryTi
// Set the new query values on the URL
url.RawQuery = q.Encode()
remainedTraffic := s.getRemainedTraffic(email)
expiryTimeString := getExpiryTime(expiryTime)
remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString)
if len(domains) > 0 {
links := ""
for index, d := range domains {
domain := d.(map[string]interface{})
url.Fragment = remark + "-" + domain["remark"].(string)
url.Fragment = s.genRemark(inbound, email, domain["remark"].(string))
url.Host = fmt.Sprintf("%s:%d", domain["domain"].(string), port)
if index > 0 {
links += "\n"
@@ -474,11 +466,12 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string, expiryTi
}
return links
}
url.Fragment = remark
url.Fragment = s.genRemark(inbound, email, "")
return url.String()
}
func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, expiryTime int64) string {
func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string {
address := s.address
if inbound.Protocol != model.Trojan {
return ""
@@ -668,16 +661,11 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, expiryT
// Set the new query values on the URL
url.RawQuery = q.Encode()
remainedTraffic := s.getRemainedTraffic(email)
expiryTimeString := getExpiryTime(expiryTime)
remark := fmt.Sprintf("%s: %s- %s", email, remainedTraffic, expiryTimeString)
if len(domains) > 0 {
links := ""
for index, d := range domains {
domain := d.(map[string]interface{})
url.Fragment = remark + "-" + domain["remark"].(string)
url.Fragment = s.genRemark(inbound, email, domain["remark"].(string))
url.Host = fmt.Sprintf("%s:%d", domain["domain"].(string), port)
if index > 0 {
links += "\n"
@@ -687,11 +675,11 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string, expiryT
return links
}
url.Fragment = remark
url.Fragment = s.genRemark(inbound, email, "")
return url.String()
}
func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, expiryTime int64) string {
func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) string {
address := s.address
if inbound.Protocol != model.Shadowsocks {
return ""
@@ -756,7 +744,10 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, ex
}
}
encPart := fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
encPart := fmt.Sprintf("%s:%s", method, clients[clientIndex].Password)
if method[0] == '2' {
encPart = fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
}
link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port)
url, _ := url.Parse(link)
q := url.Query()
@@ -767,15 +758,55 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string, ex
// Set the new query values on the URL
url.RawQuery = q.Encode()
remainedTraffic := s.getRemainedTraffic(email)
expiryTimeString := getExpiryTime(expiryTime)
remark := fmt.Sprintf("%s: %s- %s", clients[clientIndex].Email, remainedTraffic, expiryTimeString)
url.Fragment = remark
url.Fragment = s.genRemark(inbound, email, "")
return url.String()
}
func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string {
var remark []string
if len(email) > 0 {
if len(inbound.Remark) > 0 {
remark = append(remark, inbound.Remark)
}
remark = append(remark, email)
if len(extra) > 0 {
remark = append(remark, extra)
}
} else {
return inbound.Remark
}
if s.showInfo {
statsExist := false
var stats xray.ClientTraffic
for _, clientStat := range inbound.ClientStats {
if clientStat.Email == email {
stats = clientStat
statsExist = true
break
}
}
// Get remained days
if statsExist {
if !stats.Enable {
return fmt.Sprintf("⛔N/A-%s", strings.Join(remark, "-"))
}
if vol := stats.Total - (stats.Up + stats.Down); vol > 0 {
remark = append(remark, fmt.Sprintf("%s%s", common.FormatTraffic(vol), "📊"))
}
now := time.Now().Unix()
switch exp := stats.ExpiryTime / 1000; {
case exp > 0:
remark = append(remark, fmt.Sprintf("%d%s⏳", (exp-now)/86400, "Days"))
case exp < 0:
remark = append(remark, fmt.Sprintf("%d%s⏳", exp/-86400, "Days"))
}
}
}
return strings.Join(remark, " : ")
}
func searchKey(data interface{}, key string) (interface{}, bool) {
switch val := data.(type) {
case map[string]interface{}:
@@ -817,38 +848,3 @@ func searchHost(headers interface{}) string {
return ""
}
func getExpiryTime(expiryTime int64) string {
now := time.Now().Unix()
expiryString := ""
timeDifference := expiryTime/1000 - now
if expiryTime == 0 {
expiryString = "♾ ⏳"
} else if timeDifference > 172800 {
expiryString = fmt.Sprintf("%s ⏳", ptime.Unix((expiryTime/1000), 0).Format("yy-MM-dd hh:mm"))
} else if expiryTime < 0 {
expiryString = fmt.Sprintf("%d ⏳", expiryTime/-86400000)
} else {
expiryString = fmt.Sprintf("%s %d ⏳", "ساعت", timeDifference/3600)
}
return expiryString
}
func (s *SubService) getRemainedTraffic(email string) string {
traffic, err := s.inboundService.GetClientTrafficByEmail(email)
if err != nil {
logger.Warning(err)
}
remainedTraffic := ""
if traffic.Total == 0 {
remainedTraffic = "♾ 📊"
} else {
remainedTraffic = fmt.Sprintf("%s%s", common.FormatTraffic(traffic.Total-(traffic.Up+traffic.Down)), "📊")
}
return remainedTraffic
}

View File

@@ -262,6 +262,7 @@ body {
.ant-card-dark .ant-collapse-content,
.ant-card-dark .ant-calendar,
.ant-card-dark .ant-table-placeholder,
.ant-card-dark .ant-select-selection__choice,
.ant-card-dark .ant-input-group-addon {
color: hsla(0,0%,100%,.65);
background-color: #262f3d;

View File

@@ -19,6 +19,11 @@ const supportLangs = [
value: 'ru-RU',
icon: '🇷🇺',
},
{
name: 'Tiếng Việt',
value: 'vi-VN',
icon: '🇻🇳',
},
];
function getLang() {

View File

@@ -181,7 +181,7 @@ class AllSetting {
this.tgBotChatId = "";
this.tgRunTime = "@daily";
this.tgBotBackup = false;
this.tgBotLoginNotify = false;
this.tgBotLoginNotify = true;
this.tgCpu = "";
this.tgLang = "en-US";
this.xrayTemplateConfig = "";
@@ -194,6 +194,8 @@ class AllSetting {
this.subCertFile = "";
this.subKeyFile = "";
this.subUpdates = 0;
this.subEncrypt = true;
this.subShowInfo = true;
this.timeLocation = "Asia/Tehran";

View File

@@ -16,9 +16,12 @@ const VmessMethods = {
};
const SSMethods = {
CHACHA20_POLY1305: 'chacha20-poly1305',
AES_256_GCM: 'aes-256-gcm',
AES_128_GCM: 'aes-128-gcm',
CHACHA20_POLY1305: 'chacha20-poly1305',
CHACHA20_IETF_POLY1305: 'chacha20-ietf-poly1305',
XCHACHA20_POLY1305: 'xchacha20-poly1305',
XCHACHA20_IETF_POLY1305: 'xchacha20-ietf-poly1305',
BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm',
BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm',
BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305',
@@ -75,9 +78,9 @@ const UTLS_FINGERPRINT = {
};
const ALPN_OPTION = {
HTTP1: "http/1.1",
H2: "h2",
H3: "h3",
H2: "h2",
HTTP1: "http/1.1",
};
const SNIFFING_OPTION = {
@@ -386,12 +389,10 @@ class HttpStreamSettings extends XrayCommonClass {
constructor(
path='/',
host=[''],
sockopt={acceptProxyProtocol: false}
) {
super();
this.path = path;
this.host = host.length === 0 ? [''] : host;
this.sockopt = sockopt;
}
addHost(host) {
@@ -403,7 +404,7 @@ class HttpStreamSettings extends XrayCommonClass {
}
static fromJson(json={}) {
return new HttpStreamSettings(json.path, json.host, json.sockopt);
return new HttpStreamSettings(json.path, json.host);
}
toJson() {
@@ -416,15 +417,13 @@ class HttpStreamSettings extends XrayCommonClass {
return {
path: this.path,
host: host,
sockopt: this.sockopt,
}
}
}
class QuicStreamSettings extends XrayCommonClass {
constructor(security=VmessMethods.NONE,
key='', type='none') {
key=RandomUtil.randomSeq(10), type='none') {
super();
this.security = security;
this.key = key;
@@ -454,19 +453,16 @@ class GrpcStreamSettings extends XrayCommonClass {
constructor(
serviceName="",
multiMode=false,
sockopt={acceptProxyProtocol: false}
) {
super();
this.serviceName = serviceName;
this.multiMode = multiMode;
this.sockopt = sockopt;
}
static fromJson(json={}) {
return new GrpcStreamSettings(
json.serviceName,
json.multiMode,
json.sockopt
json.multiMode
);
}
@@ -474,7 +470,6 @@ class GrpcStreamSettings extends XrayCommonClass {
return {
serviceName: this.serviceName,
multiMode: this.multiMode,
sockopt: this.sockopt
}
}
}
@@ -487,7 +482,7 @@ class TlsStreamSettings extends XrayCommonClass {
cipherSuites = '',
rejectUnknownSni = false,
certificates=[new TlsStreamSettings.Cert()],
alpn=[ALPN_OPTION.H2,ALPN_OPTION.HTTP1],
alpn=[ALPN_OPTION.HTTP1,ALPN_OPTION.H2],
settings=new TlsStreamSettings.Settings()) {
super();
this.server = serverName;
@@ -545,13 +540,14 @@ class TlsStreamSettings extends XrayCommonClass {
}
TlsStreamSettings.Cert = class extends XrayCommonClass {
constructor(useFile=true, certificateFile='', keyFile='', certificate='', key='') {
constructor(useFile=true, certificateFile='', keyFile='', certificate='', key='', ocspStapling=3600) {
super();
this.useFile = useFile;
this.certFile = certificateFile;
this.keyFile = keyFile;
this.cert = certificate instanceof Array ? certificate.join('\n') : certificate;
this.key = key instanceof Array ? key.join('\n') : key;
this.ocspStapling = ocspStapling;
}
static fromJson(json={}) {
@@ -559,13 +555,15 @@ TlsStreamSettings.Cert = class extends XrayCommonClass {
return new TlsStreamSettings.Cert(
true,
json.certificateFile,
json.keyFile,
json.keyFile, '', '',
json.ocspStapling,
);
} else {
return new TlsStreamSettings.Cert(
false, '', '',
json.certificate.join('\n'),
json.key.join('\n'),
json.ocspStapling,
);
}
}
@@ -575,11 +573,13 @@ TlsStreamSettings.Cert = class extends XrayCommonClass {
return {
certificateFile: this.certFile,
keyFile: this.keyFile,
ocspStapling: this.ocspStapling,
};
} else {
return {
certificate: this.cert.split('\n'),
key: this.key.split('\n'),
ocspStapling: this.ocspStapling,
};
}
}
@@ -806,6 +806,34 @@ RealityStreamSettings.Settings = class extends XrayCommonClass {
}
};
class SockoptStreamSettings extends XrayCommonClass {
constructor(acceptProxyProtocol = false, tcpFastOpen = false, mark = 0, tproxy="off") {
super();
this.acceptProxyProtocol = acceptProxyProtocol;
this.tcpFastOpen = tcpFastOpen;
this.mark = mark;
this.tproxy = tproxy;
}
static fromJson(json = {}) {
if (Object.keys(json).length === 0) return undefined;
return new SockoptStreamSettings(
json.acceptProxyProtocol,
json.tcpFastOpen,
json.mark,
json.tproxy,
);
}
toJson() {
return {
acceptProxyProtocol: this.acceptProxyProtocol,
tcpFastOpen: this.tcpFastOpen,
mark: this.mark,
tproxy: this.tproxy,
};
}
}
class StreamSettings extends XrayCommonClass {
constructor(network='tcp',
security='none',
@@ -818,6 +846,7 @@ class StreamSettings extends XrayCommonClass {
httpSettings=new HttpStreamSettings(),
quicSettings=new QuicStreamSettings(),
grpcSettings=new GrpcStreamSettings(),
sockopt = undefined,
) {
super();
this.network = network;
@@ -831,6 +860,7 @@ class StreamSettings extends XrayCommonClass {
this.http = httpSettings;
this.quic = quicSettings;
this.grpc = grpcSettings;
this.sockopt = sockopt;
}
get isTls() {
@@ -870,6 +900,14 @@ class StreamSettings extends XrayCommonClass {
}
}
get sockoptSwitch() {
return this.sockopt != undefined;
}
set sockoptSwitch(value) {
this.sockopt = value ? new SockoptStreamSettings() : undefined;
}
static fromJson(json={}) {
return new StreamSettings(
@@ -884,6 +922,7 @@ class StreamSettings extends XrayCommonClass {
HttpStreamSettings.fromJson(json.httpSettings),
QuicStreamSettings.fromJson(json.quicSettings),
GrpcStreamSettings.fromJson(json.grpcSettings),
SockoptStreamSettings.fromJson(json.sockopt),
);
}
@@ -901,6 +940,7 @@ class StreamSettings extends XrayCommonClass {
httpSettings: network === 'http' ? this.http.toJson() : undefined,
quicSettings: network === 'quic' ? this.quic.toJson() : undefined,
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
};
}
}
@@ -1040,7 +1080,10 @@ class Inbound extends XrayCommonClass {
}
}
get isSSMultiUser() {
return [SSMethods.BLAKE3_AES_128_GCM,SSMethods.BLAKE3_AES_256_GCM].includes(this.method);
return this.method != SSMethods.BLAKE3_CHACHA20_POLY1305;
}
get isSS2022(){
return this.method.substring(0,4) === "2022";
}
get serverName() {
@@ -1067,7 +1110,7 @@ class Inbound extends XrayCommonClass {
} else if (this.isWs) {
return this.stream.ws.path;
} else if (this.isH2) {
return this.stream.http.path[0];
return this.stream.http.path;
}
return null;
}
@@ -1470,9 +1513,11 @@ class Inbound extends XrayCommonClass {
break;
}
let clientPassword = this.isSSMultiUser ? ':' + settings.shadowsockses[clientIndex].password : '';
let password = new Array();
if (this.isSS2022) password.push(settings.password);
if (this.isSSMultiUser) password.push(settings.shadowsockses[clientIndex].password);
let link = `ss://${safeBase64(settings.method + ':' + settings.password + clientPassword)}@${address}:${this.port}`;
let link = `ss://${safeBase64(settings.method + ':' + password.join(':'))}@${address}:${this.port}`;
const url = new URL(link);
for (const [key, value] of params) {
url.searchParams.set(key, value)
@@ -1620,10 +1665,10 @@ class Inbound extends XrayCommonClass {
JSON.parse(this.settings).clients.forEach((client,index) => {
if(this.tls && !ObjectUtil.isArrEmpty(this.stream.tls.settings.domains)){
this.stream.tls.settings.domains.forEach((domain) => {
link += this.genLink(domain.domain, remark + '-' + client.email + '-' + domain.remark, index) + '\r\n';
link += this.genLink(domain.domain, [remark, client.email, domain.remark].filter(x => x.length > 0).join('-'), index) + '\r\n';
});
} else {
link += this.genLink(address, remark + '-' + client.email, index) + '\r\n';
link += this.genLink(address, [remark, client.email].filter(x => x.length > 0).join('-'), index) + '\r\n';
}
});
return link;
@@ -1701,11 +1746,9 @@ Inbound.Settings = class extends XrayCommonClass {
Inbound.VmessSettings = class extends Inbound.Settings {
constructor(protocol,
vmesses=[new Inbound.VmessSettings.Vmess()],
disableInsecureEncryption=false) {
vmesses=[new Inbound.VmessSettings.Vmess()]) {
super(protocol);
this.vmesses = vmesses;
this.disableInsecure = disableInsecureEncryption;
}
indexOfVmessById(id) {
@@ -1730,14 +1773,12 @@ Inbound.VmessSettings = class extends Inbound.Settings {
return new Inbound.VmessSettings(
Protocols.VMESS,
json.clients.map(client => Inbound.VmessSettings.Vmess.fromJson(client)),
ObjectUtil.isEmpty(json.disableInsecureEncryption) ? false : json.disableInsecureEncryption,
);
}
toJson() {
return {
clients: Inbound.VmessSettings.toJsonArray(this.vmesses),
disableInsecureEncryption: this.disableInsecure,
};
}
};
@@ -2097,8 +2138,9 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
};
Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
constructor(password=RandomUtil.randomShadowsocksPassword(), email=RandomUtil.randomLowerAndNum(8),limitIp=0, totalGB=0, expiryTime=0, enable=true, tgId='', subId=RandomUtil.randomLowerAndNum(16)) {
constructor(method='', password=RandomUtil.randomShadowsocksPassword(), email=RandomUtil.randomLowerAndNum(8),limitIp=0, totalGB=0, expiryTime=0, enable=true, tgId='', subId=RandomUtil.randomLowerAndNum(16)) {
super();
this.method = method;
this.password = password;
this.email = email;
this.limitIp = limitIp;
@@ -2111,6 +2153,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
toJson() {
return {
method: this.method,
password: this.password,
email: this.email,
limitIp: this.limitIp,
@@ -2124,6 +2167,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
static fromJson(json = {}) {
return new Inbound.ShadowsocksSettings.Shadowsocks(
json.method,
json.password,
json.email,
json.limitIp,

View File

@@ -118,12 +118,9 @@ func (a *ServerController) restartXrayService(c *gin.Context) {
func (a *ServerController) getLogs(c *gin.Context) {
count := c.Param("count")
logLevel := c.PostForm("logLevel")
logs, err := a.serverService.GetLogs(count, logLevel)
if err != nil {
jsonMsg(c, "getLogs", err)
return
}
level := c.PostForm("level")
syslog := c.PostForm("syslog")
logs := a.serverService.GetLogs(count, level, syslog)
jsonObj(c, logs, nil)
}

View File

@@ -79,6 +79,8 @@ func (a *SettingController) getDefaultSettings(c *gin.Context) {
"subDomain": func() (interface{}, error) { return a.settingService.GetSubDomain() },
"subKeyFile": func() (interface{}, error) { return a.settingService.GetSubKeyFile() },
"subCertFile": func() (interface{}, error) { return a.settingService.GetSubCertFile() },
"subEncrypt": func() (interface{}, error) { return a.settingService.GetSubEncrypt() },
"subShowInfo": func() (interface{}, error) { return a.settingService.GetSubShowInfo() },
}
result := make(map[string]interface{})

View File

@@ -55,6 +55,8 @@ type AllSetting struct {
SubCertFile string `json:"subCertFile" form:"subCertFile"`
SubKeyFile string `json:"subKeyFile" form:"subKeyFile"`
SubUpdates int `json:"subUpdates" form:"subUpdates"`
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"`
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"`
}
func (s *AllSetting) CheckValid() error {

View File

@@ -37,15 +37,16 @@
this.inbound = dbInbound.toInbound();
settings = JSON.parse(this.inbound.settings);
this.client = settings.clients[clientIndex];
remark = this.dbInbound.remark + ( this.client ? "-" + this.client.email : '');
remark = [this.dbInbound.remark, ( this.client ? this.client.email : '')].filter(Boolean).join('-');
address = this.dbInbound.address;
this.subId = '';
this.qrcodes = [];
if (this.inbound.tls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) {
this.inbound.stream.tls.settings.domains.forEach((domain) => {
remarkText = [remark, domain.remark].filter(Boolean).join('-');
this.qrcodes.push({
remark: remark + "-" + domain.remark,
link: this.inbound.genLink(domain.domain, remark + "-" + domain.remark, clientIndex)
remark: remarkText,
link: this.inbound.genLink(domain.domain, remarkText, clientIndex)
});
});
} else {
@@ -86,7 +87,7 @@
},
genSubLink(subID) {
const { domain: host, port, tls: isTLS, path: base } = app.subSettings;
return buildURL({ host, port, isTLS, base, path: subID });
return buildURL({ host, port, isTLS, base, path: subID+'?name='+remark });
}
},
updated() {

View File

@@ -200,21 +200,12 @@
this.inbound = dbInbound.toInbound();
this.delayedStart = false;
},
getClients(protocol, clientSettings) {
switch (protocol) {
case Protocols.VMESS: return clientSettings.vmesses;
case Protocols.VLESS: return clientSettings.vlesses;
case Protocols.TROJAN: return clientSettings.trojans;
case Protocols.SHADOWSOCKS: return clientSettings.shadowsockses;
default: return null;
}
},
newClient(protocol) {
switch (protocol) {
case Protocols.VMESS: return new Inbound.VmessSettings.Vmess();
case Protocols.VLESS: return new Inbound.VLESSSettings.VLESS();
case Protocols.TROJAN: return new Inbound.TrojanSettings.Trojan();
case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings.Shadowsocks();
case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings.Shadowsocks(clientsBulkModal.inbound.settings.shadowsockses[0].method);
default: return null;
}
},

View File

@@ -70,7 +70,7 @@
case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.Vmess());
case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS());
case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan());
case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks());
case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks(clients[0].method));
default: return null;
}
},

View File

@@ -1,10 +1,19 @@
{{define "form/http"}}
<a-form layout="inline">
<a-form-item label='{{ i18n "username"}}'>
<a-input v-model.trim="inbound.settings.accounts[0].user"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "password" }}'>
<a-input v-model.trim="inbound.settings.accounts[0].pass"></a-input>
<a-form-item>
<a-row>
<a-button type="primary" size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button>
</a-row>
<a-input-group v-for="(account, index) in inbound.settings.accounts">
<a-input style="width: 45%" v-model.trim="account.user"
addon-before='{{ i18n "username" }}'></a-input>
<a-input style="width: 55%" v-model.trim="account.pass"
addon-before='{{ i18n "password" }}'>
<template slot="addonAfter">
<a-button type="primary" size="small" @click="inbound.settings.delAccount(index)">-</a-button>
</template>
</a-input>
</a-input-group>
</a-form-item>
</a-form>
{{end}}

View File

@@ -115,7 +115,7 @@
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='{{ i18n "password" }}'>
<a-form-item v-if="inbound.isSS2022" label='{{ i18n "password" }}'>
<a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
<a-input v-model.trim="inbound.settings.password" style="width: 250px;"></a-input>
</a-form-item>

View File

@@ -6,11 +6,20 @@
</a-form-item>
<br>
<template v-if="inbound.settings.auth === 'password'">
<a-form-item label='{{ i18n "username" }}'>
<a-input v-model.trim="inbound.settings.accounts[0].user"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "password" }}'>
<a-input v-model.trim="inbound.settings.accounts[0].pass"></a-input>
<a-form-item>
<a-row>
<a-button type="primary" size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button>
</a-row>
<a-input-group v-for="(account, index) in inbound.settings.accounts">
<a-input style="width: 45%" v-model.trim="account.user"
addon-before='{{ i18n "username" }}'></a-input>
<a-input style="width: 55%" v-model.trim="account.pass"
addon-before='{{ i18n "password" }}'>
<template slot="addonAfter">
<a-button type="primary" size="small" @click="inbound.settings.delAccount(index)">-</a-button>
</template>
</a-input>
</a-input-group>
</a-form-item>
</template>
<br>

View File

@@ -108,9 +108,4 @@
</a-collapse-panel>
</a-collapse>
</a-form>
<a-form layout="inline">
<a-form-item label='{{ i18n "pages.inbounds.disableInsecureEncryption" }}'>
<a-switch v-model.number="inbound.settings.disableInsecure"></a-switch>
</a-form-item>
</a-form>
{{end}}

View File

@@ -5,7 +5,7 @@
Sniffing
<a-tooltip>
<template slot="title">
<span >{{ i18n "pages.inbounds.noRecommendKeepDefault" }}</span>
<span>{{ i18n "pages.inbounds.noRecommendKeepDefault" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>

View File

@@ -1,9 +1,5 @@
{{define "form/streamGRPC"}}
<a-form layout="inline">
<a-form-item label="AcceptProxyProtocol">
<a-switch v-model="inbound.stream.grpc.sockopt.acceptProxyProtocol"></a-switch>
</a-form-item>
<br>
<a-form-item label="ServiceName">
<a-input v-model.trim="inbound.stream.grpc.serviceName"></a-input>
</a-form-item>

View File

@@ -1,9 +1,5 @@
{{define "form/streamHTTP"}}
<a-form layout="inline">
<a-form-item label="AcceptProxyProtocol">
<a-switch v-model="inbound.stream.http.sockopt.acceptProxyProtocol"></a-switch>
</a-form-item>
<br>
<a-form-item label='{{ i18n "path" }}'>
<a-input v-model.trim="inbound.stream.http.path"></a-input>
</a-form-item>

View File

@@ -12,7 +12,8 @@
</a-form-item>
<br>
<a-form-item label='{{ i18n "password" }}'>
<a-input v-model="inbound.stream.kcp.seed"></a-input>
<a-icon @click="inbound.stream.kcp.seed = RandomUtil.randomSeq(10)" type="sync"> </a-icon>
<a-input v-model="inbound.stream.kcp.seed" style="width: 150px;" ></a-input>
</a-form-item>
<br>
<a-form-item label="MTU">

View File

@@ -8,7 +8,8 @@
</a-select>
</a-form-item>
<a-form-item label='{{ i18n "password" }}'>
<a-input v-model.trim="inbound.stream.quic.key"></a-input>
<a-icon @click="inbound.stream.quic.key = RandomUtil.randomSeq(10)" type="sync"> </a-icon>
<a-input v-model.trim="inbound.stream.quic.key" style="width: 150px;"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "camouflage" }}'>
<a-select v-model="inbound.stream.quic.type" style="width: 280px;" :dropdown-class-name="themeSwitcher.darkCardClass">

View File

@@ -42,4 +42,9 @@
<template v-if="inbound.stream.network === 'grpc'">
{{template "form/streamGRPC"}}
</template>
<!-- sockopt -->
<template>
{{template "form/streamSockopt"}}
</template>
{{end}}

View File

@@ -0,0 +1,46 @@
{{define "form/streamSockopt"}}
<a-form layout="inline">
<a-form-item label="Transparent Proxy">
<a-switch v-model="inbound.stream.sockoptSwitch"></a-switch>
</a-form-item>
<table width="100%" class="ant-table-tbody" v-if="inbound.stream.sockoptSwitch">
<tr>
<td>Accept Proxy Protocol</td>
<td>
<a-form-item>
<a-switch v-model="inbound.stream.sockopt.acceptProxyProtocol"></a-switch>
</a-form-item>
</td>
</tr>
<tr>
<td>TCP FastOpen</td>
<td>
<a-form-item>
<a-switch v-model.trim="inbound.stream.sockopt.tcpFastOpen"></a-switch>
</a-form-item>
</td>
</tr>
<tr>
<td>Route Mark</td>
<td>
<a-form-item>
<a-input-number v-model="inbound.stream.sockopt.mark" :min="0"></a-input-number>
</a-form-item>
</td>
</tr>
<tr>
<td>T-Proxy</td>
<td>
<a-form-item>
<a-select v-model="inbound.stream.sockopt.tproxy" style="width: 250px;"
:dropdown-class-name="themeSwitcher.darkCardClass">
<a-select-option value="off">OFF</a-select-option>
<a-select-option value="redirect">Redirect</a-select-option>
<a-select-option value="tproxy">T-Proxy</a-select-option>
</a-select>
</a-form-item>
</td>
</tr>
</table>
</a-form>
{{end}}

View File

@@ -80,9 +80,13 @@
</a-select>
</a-form-item>
<a-form-item label="Alpn">
<a-checkbox-group v-model="inbound.stream.tls.alpn" style="width:200px">
<a-checkbox v-for="key,value in ALPN_OPTION" :value="key">[[ value ]]</a-checkbox>
</a-checkbox-group>
<a-select
mode="multiple"
style="width: 250px"
:dropdown-class-name="themeSwitcher.darkCardClass"
v-model="inbound.stream.tls.alpn">
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
</a-select>
</a-form-item>
<br>
<a-form-item label="Allow insecure">
@@ -118,6 +122,10 @@
<a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.key"></a-input>
</a-form-item>
</template>
<br>
<a-form-item label="ocspStapling">
<a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number>
</a-form-item>
</template>
</a-form>

View File

@@ -58,8 +58,19 @@
reality: <a-tag color="green">{{ i18n "enabled" }}</a-tag><br />
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>
<td v-else>tls: <a-tag color="red">{{ i18n "disabled" }}</a-tag></td>
</tr>
</table>
<table v-if="dbInbound.isSS" style="margin-bottom: 10px; width: 100%;">
<tr>
<td>{{ i18n "encryption" }}</td>
<td><a-tag color="green">[[ inbound.settings.method ]]</a-tag></td>
</tr><tr v-if="inbound.isSS2022">
<td>{{ i18n "password" }}</td>
<td><a-tag color="blue">[[ inbound.settings.password ]]</a-tag></td>
</tr><tr>
<td>{{ i18n "pages.inbounds.network" }}</td>
<td><a-tag color="green">[[ inbound.settings.network ]]</a-tag></td>
</tr>
</table>
<template v-if="infoModal.clientSettings">
@@ -167,19 +178,7 @@
</template>
</template>
<template v-else>
<a-divider></a-divider>
<table v-if="inbound.protocol == Protocols.SHADOWSOCKS" style="margin-bottom: 10px; width: 100%;">
<tr>
<th>{{ i18n "encryption" }}</th>
<th>{{ i18n "password" }}</th>
<th>{{ i18n "pages.inbounds.network" }}</th>
</tr><tr>
<td><a-tag color="green">[[ inbound.settings.method ]]</a-tag></td>
<td><a-tag color="blue">[[ inbound.settings.password ]]</a-tag></td>
<td><a-tag color="green">[[ inbound.settings.network ]]</a-tag></td>
</tr>
</table>
<template v-if="inbound.protocol == Protocols.SHADOWSOCKS && !inbound.isSSMultiUser">
<template v-if="dbInbound.isSS && !inbound.isSSMultiUser">
<a-divider>URL</a-divider>
<a-row v-for="(link,index) in infoModal.links">
<a-col :span="22"><a-tag color="cyan">[[ link.remark ]]</a-tag><br />[[ link.link ]]</a-col>
@@ -205,17 +204,19 @@
<td><a-tag color="blue">[[ inbound.settings.followRedirect ]]</a-tag></td>
</tr>
</table>
</table>
<table v-if="inbound.protocol == Protocols.SOCKS" style="margin-bottom: 10px; width: 100%;">
<table v-if="dbInbound.isSocks" style="margin-bottom: 10px; width: 100%;">
<tr>
<th>{{ i18n "password" }} Auth</th>
<th>{{ i18n "pages.inbounds.enable" }} udp</th>
<th>IP</th>
</tr><tr>
</tr>
<tr>
<td><a-tag color="green">[[ inbound.settings.auth ]]</a-tag></td>
<td><a-tag color="blue">[[ inbound.settings.udp]]</a-tag></td>
<td><a-tag color="green">[[ inbound.settings.ip ]]</a-tag></td>
</tr><tr v-if="inbound.settings.auth == 'password'">
</tr>
<template v-if="inbound.settings.auth == 'password'">
<tr>
<td> </td>
<td>{{ i18n "username" }}</td>
<td>{{ i18n "password" }}</td>
@@ -224,9 +225,9 @@
<td><a-tag color="blue">[[ account.user ]]</a-tag></td>
<td><a-tag color="green">[[ account.pass ]]</a-tag></td>
</tr>
</table>
</table>
<table v-if="inbound.protocol == Protocols.HTTP" style="margin-bottom: 10px; width: 100%;">
</template>
</table>
<table v-if="dbInbound.isHTTP" style="margin-bottom: 10px; width: 100%;">
<tr>
<th> </th>
<th>{{ i18n "username" }}</th>
@@ -237,7 +238,6 @@
<td><a-tag color="green">[[ account.pass ]]</a-tag></td>
</tr>
</table>
</table>
</template>
</a-modal>
<script>
@@ -264,14 +264,15 @@
this.clientSettings = this.settings.clients ? Object.values(this.settings.clients)[index] : null;
this.isExpired = this.inbound.isExpiry(index);
this.clientStats = this.settings.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
remark = this.dbInbound.remark + ( this.clientSettings ? "-" + this.clientSettings.email : '');
remark = [this.dbInbound.remark, ( this.clientSettings ? this.clientSettings.email : '')].filter(Boolean).join('-');
address = this.dbInbound.address;
this.links = [];
if (this.inbound.tls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) {
this.inbound.stream.tls.settings.domains.forEach((domain) => {
remarkText = [remark, domain.remark].filter(Boolean).join('-');
this.links.push({
remark: remark + "-" + domain.remark,
link: this.inbound.genLink(domain.domain, remark + "-" + domain.remark, index)
remark: remarkText,
link: this.inbound.genLink(domain.domain, remarkText, index)
});
});
} else {
@@ -295,7 +296,7 @@
},
genSubLink(subID) {
const { domain: host, port, tls: isTLS, path: base } = app.subSettings;
return buildURL({ host, port, isTLS, base, path: subID });
return buildURL({ host, port, isTLS, base, path: subID+'?name='+remark });
}
};

View File

@@ -110,6 +110,15 @@
if (this.inModal.inbound.settings.shadowsockses.length ==0){
this.inModal.inbound.settings.shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()];
}
if (!this.inModal.inbound.isSS2022) {
this.inModal.inbound.settings.shadowsockses.forEach(client => {
client.method = this.inModal.inbound.settings.method;
})
} else {
this.inModal.inbound.settings.shadowsockses.forEach(client => {
client.method = "";
})
}
} else {
if (this.inModal.inbound.settings.shadowsockses.length > 0){
this.inModal.inbound.settings.shadowsockses = [];

View File

@@ -11,6 +11,9 @@
.ant-col-sm-24 {
margin-top: 10px;
}
tr.hideExpandIcon .ant-table-row-expand-icon {
display: none;
}
</style>
<body>
@@ -118,8 +121,12 @@
</a-radio-group>
<a-table :columns="columns" :row-key="dbInbound => dbInbound.id"
:data-source="searchedInbounds"
:loading="spinning" :scroll="{ x: 1300 }"
:loading="spinning" :scroll="{ x: 1200 }"
:pagination="false"
:expand-icon-as-cell="false"
:expand-row-by-click="false"
:expand-icon-column-index="0"
:row-class-name="dbInbound => (dbInbound.isTrojan || dbInbound.isVLess || dbInbound.isVMess || (dbInbound.isSS && dbInbound.toInbound().isSSMultiUser) ? '' : 'hideExpandIcon')"
style="margin-top: 20px"
@change="() => getDBInbounds()">
<template slot="action" slot-scope="text, dbInbound">
@@ -135,7 +142,7 @@
<a-icon type="qrcode"></a-icon>
{{ i18n "qrCode" }}
</a-menu-item>
<template v-if="dbInbound.isTrojan || dbInbound.isVLess || dbInbound.isVMess || dbInbound.toInbound().isSSMultiUser">
<template v-if="dbInbound.isTrojan || dbInbound.isVLess || dbInbound.isVMess || (dbInbound.isSS && dbInbound.toInbound().isSSMultiUser)">
<a-menu-item key="addClient">
<a-icon type="user-add"></a-icon>
{{ i18n "pages.client.add"}}
@@ -255,6 +262,7 @@
:columns="innerColumns"
:data-source="getInboundClients(record)"
:pagination="false"
style="margin-left: 20px;"
>
{{template "client_table"}}
</a-table>
@@ -264,6 +272,7 @@
:columns="innerTrojanColumns"
:data-source="getInboundClients(record)"
:pagination="false"
style="margin-left: 20px;"
>
{{template "client_table"}}
</a-table>
@@ -279,20 +288,20 @@
{{template "component/themeSwitcher" .}}
<script>
const columns = [{
title: "ID",
align: 'right',
dataIndex: "id",
width: 30,
}, {
title: '{{ i18n "pages.inbounds.operate" }}',
align: 'center',
width: 60,
width: 40,
scopedSlots: { customRender: 'action' },
}, {
title: '{{ i18n "pages.inbounds.enable" }}',
align: 'center',
width: 40,
scopedSlots: { customRender: 'enable' },
}, {
title: "ID",
align: 'center',
dataIndex: "id",
width: 40,
}, {
title: '{{ i18n "pages.inbounds.remark" }}',
align: 'center',
@@ -340,7 +349,7 @@
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 50, scopedSlots: { customRender: 'traffic' } },
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 50, scopedSlots: { customRender: 'expiryTime' } },
{ title: 'Password', width: 170, dataIndex: "password" },
{ title: '{{ i18n "password" }}', width: 170, dataIndex: "password" },
];
const app = new Vue({
@@ -702,7 +711,7 @@
openEditClient(dbInboundId, client) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
clients = this.getInboundClients(dbInbound);
index = this.findIndexOfClient(clients, client);
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
clientModal.show({
title: '{{ i18n "pages.client.edit"}}',
okText: '{{ i18n "pages.client.submitEdit"}}',
@@ -716,9 +725,13 @@
isEdit: true
});
},
findIndexOfClient(clients, client) {
firstKey = Object.keys(client)[0];
return clients.findIndex(c => c[firstKey] === client[firstKey]);
findIndexOfClient(protocol, clients, client) {
switch (protocol) {
case Protocols.TROJAN:
case Protocols.SHADOWSOCKS:
return clients.findIndex(item => item.password === client.password && item.email === client.email);
default: return clients.findIndex(item => item.id === client.id && item.email === client.email);
}
},
async addClient(clients, dbInboundId) {
const data = {
@@ -824,7 +837,7 @@
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
inbound = dbInbound.toInbound();
clients = this.getClients(dbInbound.protocol, inbound.settings);
index = this.findIndexOfClient(clients, client);
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
clients[index].enable = !clients[index].enable;
clientId = this.getClientId(dbInbound.protocol, clients[index]);
await this.updateClient(clients[index], dbInboundId, clientId);

View File

@@ -86,7 +86,7 @@
<a-col :sm="24" :md="12">
<a-card hoverable :class="themeSwitcher.darkCardClass">
{{ i18n "menu.link" }}:
<a-tag color="blue" style="cursor: pointer;" @click="openLogs(logModal.rows, logModal.logLevel)">{{ i18n "pages.index.logs" }}</a-tag>
<a-tag color="blue" style="cursor: pointer;" @click="openLogs()">{{ i18n "pages.index.logs" }}</a-tag>
<a-tag color="blue" style="cursor: pointer;" @click="openConfig">{{ i18n "pages.index.config" }}</a-tag>
<a-tag color="blue" style="cursor: pointer;" @click="openBackup">{{ i18n "pages.index.backup" }}</a-tag>
</a-card>
@@ -108,21 +108,30 @@
</a-col>
<a-col :sm="24" :md="12">
<a-card hoverable :class="themeSwitcher.darkCardClass">
<a-row>
<a-col :span="12">
{{ i18n "pages.index.systemLoad" }}: [[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]]
<a-tooltip>
<template slot="title">
{{ i18n "pages.index.systemLoadDesc" }}
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</a-col>
<a-col :span="12">
{{ i18n "pages.index.operationHours" }}:
<a-tag color="green">[[ formatSecond(status.uptime) ]]</a-tag>
</a-col>
</a-row>
{{ i18n "pages.index.operationHours" }}:
Xray:
<a-tag color="green">[[ formatSecond(status.appStats.uptime) ]]</a-tag>
OS:
<a-tag color="green">[[ formatSecond(status.uptime) ]]</a-tag>
</a-card>
</a-col>
<a-col :sm="24" :md="12">
<a-card hoverable :class="themeSwitcher.darkCardClass">
{{ i18n "pages.index.systemLoad" }}: [[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]]
<a-tooltip>
<template slot="title">
{{ i18n "pages.index.systemLoadDesc" }}
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</a-card>
</a-col>
<a-col :sm="24" :md="12">
<a-card hoverable :class="themeSwitcher.darkCardClass">
{{ i18n "usage"}}:
Memory [[ sizeFormat(status.appStats.mem) ]] -
Threads [[ status.appStats.threads ]]
</a-tooltip>
</a-card>
</a-col>
<a-col :sm="24" :md="12">
@@ -253,7 +262,7 @@
<a-form-item label="Count">
<a-select v-model="logModal.rows"
style="width: 80px"
@change="openLogs(logModal.rows, logModal.logLevel)"
@change="openLogs()"
:dropdown-class-name="themeSwitcher.darkCardClass">
<a-select-option value="10">10</a-select-option>
<a-select-option value="20">20</a-select-option>
@@ -262,9 +271,9 @@
</a-select>
</a-form-item>
<a-form-item label="Log Level">
<a-select v-model="logModal.logLevel"
<a-select v-model="logModal.level"
style="width: 120px"
@change="openLogs(logModal.rows, logModal.logLevel)"
@change="openLogs()"
:dropdown-class-name="themeSwitcher.darkCardClass">
<a-select-option value="debug">Debug</a-select-option>
<a-select-option value="info">Info</a-select-option>
@@ -273,8 +282,11 @@
<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-form-item>
<button class="ant-btn ant-btn-primary" @click="openLogs(logModal.rows, logModal.logLevel)"><a-icon type="sync"></a-icon> Reload</button>
<button class="ant-btn ant-btn-primary" @click="openLogs()"><a-icon type="sync"></a-icon> Reload</button>
</a-form-item>
<a-form-item>
<a-button type="primary" style="margin-bottom: 10px;"
@@ -358,6 +370,8 @@
this.tcpCount = 0;
this.udpCount = 0;
this.uptime = 0;
this.appUptime = 0;
this.appStats = {threads: 0, mem: 0, uptime: 0};
this.xray = { state: State.Stop, errorMsg: "", version: "", color: "" };
if (data == null) {
@@ -376,6 +390,8 @@
this.tcpCount = data.tcpCount;
this.udpCount = data.udpCount;
this.uptime = data.uptime;
this.appUptime = data.appUptime;
this.appStats = data.appStats;
this.xray = data.xray;
switch (this.xray.state) {
case State.Running:
@@ -409,11 +425,11 @@
visible: false,
logs: '',
rows: 20,
logLevel: 'info',
show(logs, rows) {
level: 'info',
syslog: false,
show(logs) {
this.visible = true;
this.rows = rows;
this.logs = logs.join("\n");
this.logs = logs? logs.join("\n"): "No Record...";
},
hide() {
this.visible = false;
@@ -514,14 +530,14 @@
return;
}
},
async openLogs(rows, logLevel) {
async openLogs(){
this.loading(true);
const msg = await HttpUtil.post('server/logs/' + rows, { logLevel: `${logLevel}` });
const msg = await HttpUtil.post('server/logs/'+logModal.rows,{level: logModal.level, syslog: logModal.syslog});
this.loading(false);
if (!msg.success) {
return;
}
logModal.show(msg.obj, rows);
logModal.show(msg.obj);
},
async openConfig() {
this.loading(true);

View File

@@ -406,6 +406,8 @@
</a-row>
<a-list item-layout="horizontal" :style="themeSwitcher.textStyle">
<setting-list-item type="switch" title='{{ i18n "pages.settings.subEnable"}}' desc='{{ i18n "pages.settings.subEnableDesc"}}' v-model="allSetting.subEnable"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.settings.subEncrypt"}}' desc='{{ i18n "pages.settings.subEncryptDesc"}}' v-model="allSetting.subEncrypt"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.settings.subShowInfo"}}' desc='{{ i18n "pages.settings.subShowInfoDesc"}}' v-model="allSetting.subShowInfo"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.subListen"}}' desc='{{ i18n "pages.settings.subListenDesc"}}' v-model="allSetting.subListen"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.subDomain"}}' desc='{{ i18n "pages.settings.subDomainDesc"}}' v-model="allSetting.subDomain"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.settings.subPort"}}' desc='{{ i18n "pages.settings.subPortDesc"}}' v-model.number="allSetting.subPort"></setting-list-item>

View File

@@ -15,12 +15,11 @@ import (
"x-ui/xray"
)
type CheckClientIpJob struct {}
type CheckClientIpJob struct{}
var job *CheckClientIpJob
var disAllowedIps []string
var ipFiles = []string{
xray.GetBlockedIPsPath(),
xray.GetIPLimitLogPath(),
xray.GetIPLimitBannedLogPath(),
xray.GetAccessPersistentLogPath(),
@@ -32,7 +31,6 @@ func NewCheckClientIpJob() *CheckClientIpJob {
}
func (j *CheckClientIpJob) Run() {
logger.Debug("Check Client IP Job...")
// create files required for iplimit if not exists
for i := 0; i < len(ipFiles); i++ {
@@ -45,11 +43,6 @@ func (j *CheckClientIpJob) Run() {
if j.hasLimitIp() {
j.processLogFile()
}
// write to blocked ips
blockedIps := []byte(strings.Join(disAllowedIps, ","))
err := os.WriteFile(xray.GetBlockedIPsPath(), blockedIps, 0644)
j.checkError(err)
}
func (j *CheckClientIpJob) hasLimitIp() bool {
@@ -136,8 +129,8 @@ func (j *CheckClientIpJob) processLogFile() {
}
// added 3 seconds delay before cleaning logs to reduce chance of logging IP that already has been banned
time.Sleep(time.Second * 3)
// added delay before cleaning logs to reduce chance of logging IP that already has been banned
time.Sleep(time.Second * 2)
if shouldCleanLog {
// copy access log to persistent file

View File

@@ -1,37 +0,0 @@
package job
import (
"x-ui/logger"
"x-ui/web/service"
)
type CheckInboundJob struct {
xrayService service.XrayService
inboundService service.InboundService
}
func NewCheckInboundJob() *CheckInboundJob {
return new(CheckInboundJob)
}
func (j *CheckInboundJob) Run() {
needRestart, count, err := j.inboundService.DisableInvalidClients()
if err != nil {
logger.Warning("Error in disabling invalid clients:", err)
} else if count > 0 {
logger.Debugf("%v clients disabled", count)
if needRestart {
j.xrayService.SetToNeedRestart()
}
}
needRestart, count, err = j.inboundService.DisableInvalidInbounds()
if err != nil {
logger.Warning("Error in disabling invalid inbounds:", err)
} else if count > 0 {
logger.Debugf("%v inbounds disabled", count)
if needRestart {
j.xrayService.SetToNeedRestart()
}
}
}

View File

@@ -24,14 +24,12 @@ func (j *XrayTrafficJob) Run() {
logger.Warning("get xray traffic failed:", err)
return
}
err = j.inboundService.AddTraffic(traffics)
err, needRestart := j.inboundService.AddTraffic(traffics, clientTraffics)
if err != nil {
logger.Warning("add traffic failed:", err)
}
err = j.inboundService.AddClientTraffic(clientTraffics)
if err != nil {
logger.Warning("add client traffic failed:", err)
if needRestart {
j.xrayService.SetToNeedRestart()
}
}

View File

@@ -194,38 +194,6 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, boo
return inbound, needRestart, err
}
func (s *InboundService) AddInbounds(inbounds []*model.Inbound) error {
for _, inbound := range inbounds {
exist, err := s.checkPortExist(inbound.Port, 0)
if err != nil {
return err
}
if exist {
return common.NewError("Port already exists:", inbound.Port)
}
}
db := database.GetDB()
tx := db.Begin()
var err error
defer func() {
if err == nil {
tx.Commit()
} else {
tx.Rollback()
}
}()
for _, inbound := range inbounds {
err = tx.Save(inbound).Error
if err != nil {
return err
}
}
return nil
}
func (s *InboundService) DelInbound(id int) (bool, error) {
db := database.GetDB()
@@ -316,21 +284,17 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
needRestart := false
s.xrayApi.Init(p.GetAPIPort())
err1 := s.xrayApi.DelInbound(tag)
if err1 != nil {
logger.Debug("Unable to delete old inbound by api:", err1)
needRestart = true
} else {
if s.xrayApi.DelInbound(tag) == nil {
logger.Debug("Old inbound deleted by api:", tag)
if inbound.Enable {
inboundJson, err2 := json.MarshalIndent(oldInbound.GenXrayInboundConfig(), "", " ")
if err2 != nil {
logger.Debug("Unable to marshal updated inbound config:", err2)
}
}
if inbound.Enable {
inboundJson, err2 := json.MarshalIndent(oldInbound.GenXrayInboundConfig(), "", " ")
if err2 != nil {
logger.Debug("Unable to marshal updated inbound config:", err2)
needRestart = true
} else {
err2 = s.xrayApi.AddInbound(inboundJson)
if err1 == nil {
if err2 == nil {
logger.Debug("Updated inbound added by api:", oldInbound.Tag)
} else {
logger.Debug("Unable to update inbound by api:", err2)
@@ -461,15 +425,21 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
if len(client.Email) > 0 {
s.AddClientStat(tx, data.Id, &client)
if client.Enable {
cipher := ""
if oldInbound.Protocol == "shadowsocks" {
cipher = oldSettings["method"].(string)
}
err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{
"email": client.Email,
"id": client.ID,
"flow": client.Flow,
"password": client.Password,
"cipher": cipher,
})
if err1 == nil {
logger.Debug("Client added by api:", client.Email)
} else {
logger.Debug("Error in adding client by api:", err1)
needRestart = true
}
}
@@ -536,15 +506,18 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
return false, err
}
needRestart := false
s.xrayApi.Init(p.GetAPIPort())
if len(email) > 0 {
err = s.xrayApi.RemoveUser(oldInbound.Tag, email)
if err == nil {
s.xrayApi.Init(p.GetAPIPort())
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, email)
if err1 == nil {
logger.Debug("Client deleted by api:", email)
needRestart = false
} else {
logger.Debug("Unable to del client by api:", err1)
needRestart = true
}
s.xrayApi.Close()
}
s.xrayApi.Close()
return needRestart, db.Save(oldInbound).Error
}
@@ -650,58 +623,40 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
}
}
needRestart := false
s.xrayApi.Init(p.GetAPIPort())
if len(oldEmail) > 0 {
s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail)
s.xrayApi.Init(p.GetAPIPort())
if s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail) == nil {
logger.Debug("Old client deleted by api:", clients[0].Email)
}
if clients[0].Enable {
cipher := ""
if oldInbound.Protocol == "shadowsocks" {
cipher = oldSettings["method"].(string)
}
err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{
"email": clients[0].Email,
"id": clients[0].ID,
"flow": clients[0].Flow,
"password": clients[0].Password,
"cipher": cipher,
})
if err1 == nil {
logger.Debug("Client edited by api:", clients[0].Email)
needRestart = false
} else {
logger.Debug("Error in adding client by api:", err1)
needRestart = true
}
} else {
logger.Debug("Client disabled by api:", clients[0].Email)
needRestart = false
}
s.xrayApi.Close()
} else {
logger.Debug("Client old email not found")
needRestart = true
}
s.xrayApi.Close()
return needRestart, tx.Save(oldInbound).Error
}
func (s *InboundService) AddTraffic(traffics []*xray.Traffic) error {
if len(traffics) == 0 {
return nil
}
// 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 nil
})
return err
}
func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err error) {
if len(traffics) == 0 {
return nil
}
func (s *InboundService) AddTraffic(inboundTraffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) (error, bool) {
var err error
db := database.GetDB()
tx := db.Begin()
@@ -712,17 +667,73 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
tx.Commit()
}
}()
err = s.addInboundTraffic(tx, inboundTraffics)
if err != nil {
return err, false
}
err = s.addClientTraffic(tx, clientTraffics)
if err != nil {
return err, false
}
needRestart1, count, err := s.disableInvalidClients(tx)
if err != nil {
logger.Warning("Error in disabling invalid clients:", err)
} else if count > 0 {
logger.Debugf("%v clients disabled", count)
}
needRestart2, count, err := s.disableInvalidInbounds(tx)
if err != nil {
logger.Warning("Error in disabling invalid inbounds:", err)
} else if count > 0 {
logger.Debugf("%v inbounds disabled", count)
}
return nil, (needRestart1 || needRestart2)
}
func (s *InboundService) addInboundTraffic(tx *gorm.DB, traffics []*xray.Traffic) error {
if len(traffics) == 0 {
return nil
}
var err error
for _, traffic := range traffics {
if traffic.IsInbound {
err = tx.Model(&model.Inbound{}).Where("tag = ?", traffic.Tag).
Updates(map[string]interface{}{
"up": gorm.Expr("up + ?", traffic.Up),
"down": gorm.Expr("down + ?", traffic.Down),
}).Error
if err != nil {
return err
}
}
}
return nil
}
func (s *InboundService) addClientTraffic(tx *gorm.DB, traffics []*xray.ClientTraffic) (err error) {
if len(traffics) == 0 {
return nil
}
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
err = tx.Model(xray.ClientTraffic{}).Where("email IN (?)", emails).Find(&dbClientTraffics).Error
if err != nil {
return err
}
// Avoid empty slice error
if len(dbClientTraffics) == 0 {
return nil
}
dbClientTraffics, err = s.adjustTraffics(tx, dbClientTraffics)
if err != nil {
return err
@@ -798,14 +809,13 @@ func (s *InboundService) adjustTraffics(tx *gorm.DB, dbClientTraffics []*xray.Cl
return dbClientTraffics, nil
}
func (s *InboundService) DisableInvalidInbounds() (bool, int64, error) {
db := database.GetDB()
func (s *InboundService) disableInvalidInbounds(tx *gorm.DB) (bool, int64, error) {
now := time.Now().Unix() * 1000
needRestart := false
if p != nil {
var tags []string
err := db.Table("inbounds").
err := tx.Table("inbounds").
Select("inbounds.tag").
Where("((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?)) and enable = ?", now, true).
Scan(&tags).Error
@@ -814,17 +824,18 @@ func (s *InboundService) DisableInvalidInbounds() (bool, int64, error) {
}
s.xrayApi.Init(p.GetAPIPort())
for _, tag := range tags {
err = s.xrayApi.DelInbound(tag)
err1 := s.xrayApi.DelInbound(tag)
if err == nil {
logger.Debug("Inbound disabled by api:", tag)
} else {
logger.Debug("Error in disabling inbound by api:", err1)
needRestart = true
}
}
s.xrayApi.Close()
}
result := db.Model(model.Inbound{}).
result := tx.Model(model.Inbound{}).
Where("((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?)) and enable = ?", now, true).
Update("enable", false)
err := result.Error
@@ -832,8 +843,7 @@ func (s *InboundService) DisableInvalidInbounds() (bool, int64, error) {
return needRestart, count, err
}
func (s *InboundService) DisableInvalidClients() (bool, int64, error) {
db := database.GetDB()
func (s *InboundService) disableInvalidClients(tx *gorm.DB) (bool, int64, error) {
now := time.Now().Unix() * 1000
needRestart := false
@@ -843,7 +853,7 @@ func (s *InboundService) DisableInvalidClients() (bool, int64, error) {
Email string
}
err := db.Table("inbounds").
err := tx.Table("inbounds").
Select("inbounds.tag, client_traffics.email").
Joins("JOIN client_traffics ON inbounds.id = client_traffics.inbound_id").
Where("((client_traffics.total > 0 AND client_traffics.up + client_traffics.down >= client_traffics.total) OR (client_traffics.expiry_time > 0 AND client_traffics.expiry_time <= ?)) AND client_traffics.enable = ?", now, true).
@@ -853,16 +863,17 @@ func (s *InboundService) DisableInvalidClients() (bool, int64, error) {
}
s.xrayApi.Init(p.GetAPIPort())
for _, result := range results {
err = s.xrayApi.RemoveUser(result.Tag, result.Email)
if err == nil {
err1 := s.xrayApi.RemoveUser(result.Tag, result.Email)
if err1 == nil {
logger.Debug("Client disabled by api:", result.Email)
} else {
logger.Debug("Error in disabling client by api:", err1)
needRestart = true
}
}
s.xrayApi.Close()
}
result := db.Model(xray.ClientTraffic{}).
result := tx.Model(xray.ClientTraffic{}).
Where("((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?)) and enable = ?", now, true).
Update("enable", false)
err := result.Error
@@ -1254,15 +1265,26 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, e
for _, client := range clients {
if client.Email == clientEmail {
s.xrayApi.Init(p.GetAPIPort())
cipher := ""
if string(inbound.Protocol) == "shadowsocks" {
var oldSettings map[string]interface{}
err = json.Unmarshal([]byte(inbound.Settings), &oldSettings)
if err != nil {
return false, err
}
cipher = oldSettings["method"].(string)
}
err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]interface{}{
"email": client.Email,
"id": client.ID,
"flow": client.Flow,
"password": client.Password,
"cipher": cipher,
})
if err1 == nil {
logger.Debug("Client enabled due to reset traffic:", clientEmail)
} else {
logger.Debug("Error in enabling client by api:", err1)
needRestart = true
}
s.xrayApi.Close()

View File

@@ -12,6 +12,7 @@ import (
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"time"
@@ -76,6 +77,11 @@ type Status struct {
IPv4 string `json:"ipv4"`
IPv6 string `json:"ipv6"`
} `json:"publicIP"`
AppStats struct {
Threads uint32 `json:"threads"`
Mem uint64 `json:"mem"`
Uptime uint64 `json:"uptime"`
} `json:"appStats"`
}
type Release struct {
@@ -219,12 +225,22 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
status.Xray.ErrorMsg = s.xrayService.GetXrayResult()
}
status.Xray.Version = s.xrayService.GetXrayVersion()
var rtm runtime.MemStats
runtime.ReadMemStats(&rtm)
status.AppStats.Mem = rtm.Sys
status.AppStats.Threads = uint32(runtime.NumGoroutine())
if p.IsRunning() {
status.AppStats.Uptime = p.GetUptime()
} else {
status.AppStats.Uptime = 0
}
return status
}
func (s *ServerService) GetXrayVersions() ([]string, error) {
url := "https://api.github.com/repos/MHSanaei/Xray-core/releases"
url := "https://api.github.com/repos/XTLS/Xray-core/releases"
resp, err := http.Get(url)
if err != nil {
return nil, err
@@ -243,9 +259,11 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
if err != nil {
return nil, err
}
versions := make([]string, 0, len(releases))
var versions []string
for _, release := range releases {
versions = append(versions, release.TagName)
if release.TagName >= "v1.7.5" {
versions = append(versions, release.TagName)
}
}
return versions, nil
}
@@ -289,7 +307,7 @@ func (s *ServerService) downloadXRay(version string) (string, error) {
}
fileName := fmt.Sprintf("Xray-%s-%s.zip", osName, arch)
url := fmt.Sprintf("https://github.com/MHSanaei/Xray-core/releases/download/%s/%s", version, fileName)
url := fmt.Sprintf("https://github.com/XTLS/Xray-core/releases/download/%s/%s", version, fileName)
resp, err := http.Get(url)
if err != nil {
return "", err
@@ -358,48 +376,72 @@ func (s *ServerService) UpdateXray(version string) error {
return err
}
err = copyZipFile("xray", xray.GetBinaryPath())
if err != nil {
downloadFile := func(fileName string, url string) error {
os.Remove(fileName)
file, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR|os.O_TRUNC, fs.ModePerm)
if err != nil {
return err
}
defer file.Close()
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("download file failed: %s", resp.Status)
}
_, err = io.Copy(file, resp.Body)
return err
}
err = copyZipFile("geosite.dat", xray.GetGeositePath())
if err != nil {
return err
copyFiles := map[string]string{
"xray": xray.GetBinaryPath(),
"geosite.dat": xray.GetGeositePath(),
"geoip.dat": xray.GetGeoipPath(),
}
err = copyZipFile("geoip.dat", xray.GetGeoipPath())
if err != nil {
return err
downloadFiles := map[string]string{
xray.GetIranPath(): "https://github.com/MasterKia/iran-hosted-domains/releases/latest/download/iran.dat",
}
err = copyZipFile("iran.dat", xray.GetIranPath())
if err != nil {
return err
for fileName, filePath := range copyFiles {
err := copyZipFile(fileName, filePath)
if err != nil {
return err
}
}
for fileName, filePath := range downloadFiles {
err := downloadFile(fileName, filePath)
if err != nil {
return err
}
}
return nil
}
func (s *ServerService) GetLogs(count string, logLevel string) ([]string, error) {
var cmdArgs []string
if runtime.GOOS == "linux" {
cmdArgs = []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", count}
if logLevel != "" {
cmdArgs = append(cmdArgs, "-p", logLevel)
func (s *ServerService) GetLogs(count string, level string, syslog string) []string {
c, _ := strconv.Atoi(count)
var lines []string
if syslog == "true" {
cmdArgs := []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", count, "-p", level}
// Run the command
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return []string{"Failed to run journalctl command!"}
}
lines = strings.Split(out.String(), "\n")
} else {
return []string{"Unsupported operating system"}, nil
lines = logger.GetLogs(c, level)
}
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return nil, err
}
lines := strings.Split(out.String(), "\n")
return lines, nil
return lines
}
func (s *ServerService) GetConfigJson() (interface{}, error) {
@@ -417,6 +459,7 @@ func (s *ServerService) GetConfigJson() (interface{}, error) {
if err != nil {
return nil, err
}
return jsonData, nil
}

View File

@@ -39,7 +39,7 @@ var defaultValueMap = map[string]string{
"tgBotChatId": "",
"tgRunTime": "@daily",
"tgBotBackup": "false",
"tgBotLoginNotify": "false",
"tgBotLoginNotify": "true",
"tgCpu": "0",
"tgLang": "en-US",
"secretEnable": "false",
@@ -51,6 +51,8 @@ var defaultValueMap = map[string]string{
"subCertFile": "",
"subKeyFile": "",
"subUpdates": "12",
"subEncrypt": "true",
"subShowInfo": "true",
}
type SettingService struct {
@@ -396,6 +398,14 @@ func (s *SettingService) GetSubUpdates() (int, error) {
return s.getInt("subUpdates")
}
func (s *SettingService) GetSubEncrypt() (bool, error) {
return s.getBool("subEncrypt")
}
func (s *SettingService) GetSubShowInfo() (bool, error) {
return s.getBool("subShowInfo")
}
func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error {
if err := allSetting.CheckValid(); err != nil {
return err

View File

@@ -827,7 +827,7 @@ func (t *Tgbot) clientTelegramUserInfo(chatId int64, email string, messageID ...
t.SendMsgToTgbot(chatId, output, inlineKeyboard)
requestUser := telego.KeyboardButtonRequestUser{
RequestID: int32(traffic.Id),
UserIsBot: false,
UserIsBot: new(bool),
}
keyboard := tu.Keyboard(
tu.KeyboardRow(

View File

@@ -69,7 +69,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
return nil, err
}
s.inboundService.DisableInvalidClients()
s.inboundService.AddTraffic(nil, nil)
inbounds, err := s.inboundService.GetAllInbounds()
if err != nil {
@@ -116,7 +116,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
}
}
for key := range c {
if key != "email" && key != "id" && key != "password" && key != "flow" {
if key != "email" && key != "id" && key != "password" && key != "flow" && key != "method" {
delete(c, key)
}
if c["flow"] == "xtls-rprx-vision-udp443" {

View File

@@ -127,7 +127,6 @@
"network" = "Network"
"destinationPort" = "Destination Port"
"targetAddress" = "Target Address"
"disableInsecureEncryption" = "Disable Insecure Encryption"
"monitorDesc" = "Leave blank by default"
"meansNoLimit" = "Means No Limit"
"totalFlow" = "Total Flow"
@@ -274,6 +273,10 @@
"subDomainDesc" = "Leave blank by default to monitor all domains and IPs"
"subUpdates" = "Subscription update intervals"
"subUpdatesDesc" = "Interval hours between updates in client application"
"subEncrypt" = "Encrypt configs"
"subEncryptDesc" = "Encrypt the returned configs in subscription"
"subShowInfo" = "Show usage info"
"subShowInfoDesc" = "Show remianed traffic and date after config name"
[pages.settings.templates]
"title" = "Templates"

View File

@@ -127,7 +127,6 @@
"network" = "شبکه"
"destinationPort" = "پورت مقصد"
"targetAddress" = "آدرس مقصد"
"disableInsecureEncryption" = "رمزگذاری ناامن را غیرفعال کنید"
"monitorDesc" = "به طور پیش فرض خالی بگذارید"
"meansNoLimit" = "یعنی بدون محدودیت"
"totalFlow" = "کل ترافیک"
@@ -274,6 +273,10 @@
"subDomainDesc" = "برای نظارت بر همه دامنه ها و آی‌پی ها به طور پیش فرض خالی بگذارید"
"subUpdates" = "فاصله به روز رسانی های سابسکریپشن"
"subUpdatesDesc" = "ساعت های فاصله بین به روز رسانی در برنامه کاربر"
"subEncrypt" = "رمزگذاری کانفیگ ها"
"subEncryptDesc" = "رمزگذاری کانفیگ های بازگشتی سابسکریپشن"
"subShowInfo" = "نمایش اطلاعات مصرف"
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در هر کانفیگ نمایش میدهد"
[pages.settings.templates]
"title" = "الگوها"

View File

@@ -127,7 +127,6 @@
"network" = "Сеть"
"destinationPort" = "Порт назначения"
"targetAddress" = "Целевой адрес"
"disableInsecureEncryption" = "Отключить небезопасное шифрование"
"monitorDesc" = "Оставьте пустым по умолчанию"
"meansNoLimit" = "Значит без ограничений"
"totalFlow" = "Общий расход"
@@ -274,6 +273,10 @@
"subDomainDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все домены и IP-адреса"
"subUpdates" = "Интервалы обновления подписки"
"subUpdatesDesc" = "Часовой интервал между обновлениями в клиентском приложении"
"subEncrypt" = "Шифровать конфиги"
"subEncryptDesc" = "Шифровать возвращенные конфиги в подписке"
"subShowInfo" = "Показать информацию об использовании"
"subShowInfoDesc" = "Показывать восстановленный трафик и дату после имени конфигурации"
[pages.settings.templates]
"title" = "Шаблоны"

View File

@@ -0,0 +1,495 @@
"username" = "Tên người dùng"
"password" = "Mật khẩu"
"login" = "Đăng nhập"
"confirm" = "Xác nhận"
"cancel" = "Hủy bỏ"
"close" = "Đóng"
"copy" = "Sao chép"
"copied" = "Đã sao chép"
"download" = "Tải xuống"
"remark" = "Ghi chú"
"enable" = "Kích hoạt"
"protocol" = "Giao thức"
"search" = "Tìm kiếm"
"filter" = "Bộ lọc"
"loading" = "Đang tải"
"second" = "Giây"
"minute" = "Phút"
"hour" = "Giờ"
"day" = "Ngày"
"check" = "Kiểm tra"
"indefinite" = "Không xác định"
"unlimited" = "Không giới hạn"
"none" = "Không có"
"qrCode" = "Mã QR"
"info" = "Thông tin thêm"
"edit" = "Chỉnh sửa"
"delete" = "Xóa"
"reset" = "Đặt lại"
"copySuccess" = "Đã sao chép thành công"
"sure" = "Chắc chắn"
"encryption" = "Mã hóa"
"transmission" = "Truyền tải"
"host" = "Máy chủ"
"path" = "Đường dẫn"
"camouflage" = "camouflage"
"status" = "Trạng thái"
"enabled" = "Đã kích hoạt"
"disabled" = "Đã tắt"
"depleted" = "Đã cạn kiệt"
"depletingSoon" = "Sắp cạn kiệt"
"domainName" = "Tên miền"
"monitor" = "Listening IP"
"certificate" = "Chứng chỉ"
"fail" = "Thất bại"
"success" = "Thành công"
"getVersion" = "Lấy phiên bản"
"install" = "Cài đặt"
"clients" = "Khách hàng"
"usage" = "Sử dụng"
"secretToken" = "secretToken"
"remained" = "Còn lại"
[menu]
"dashboard" = "Trạng thái hệ thống"
"inbounds" = "Inbounds"
"settings" = "Cài đặt bảng điều khiển"
"logout" = "Đăng xuất"
"link" = "Khác"
[pages.login]
"title" = "Đăng nhập"
"loginAgain" = "Thời hạn đăng nhập đã hết. Vui lòng đăng nhập lại."
[pages.login.toasts]
"invalidFormData" = "Dạng dữ liệu nhập không hợp lệ."
"emptyUsername" = "Vui lòng nhập tên người dùng."
"emptyPassword" = "Vui lòng nhập mật khẩu."
"wrongUsernameOrPassword" = "Tên người dùng hoặc mật khẩu không đúng."
"successLogin" = "Đăng nhập thành công."
[pages.index]
"title" = "Trạng thái hệ thống"
"memory" = "Bộ nhớ"
"hard" = "Ổ cứng"
"xrayStatus" = "Trạng thái của Xray"
"stopXray" = "Dừng Xray"
"restartXray" = "Khởi động lại Xray"
"xraySwitch" = "Chuyển đổi phiên bản"
"xraySwitchClick" = "Chọn phiên bản mà bạn muốn chuyển đổi sang."
"xraySwitchClickDesk" = "Hãy lựa chọn thận trọng, vì các phiên bản cũ có thể không tương thích với các cấu hình hiện tại."
"operationHours" = "Thời gian hoạt động"
"systemLoad" = "Tải hệ thống"
"systemLoadDesc" = "trung bình tải hệ thống trong 1, 5 và 15 phút qua"
"connectionTcpCountDesc" = "Tổng số kết nối TCP trên tất cả các card mạng."
"connectionUdpCountDesc" = "Tổng số kết nối UDP trên tất cả các card mạng."
"connectionCount" = "Số lượng kết nối"
"upSpeed" = "Tổng tốc độ tải lên cho tất cả các card mạng."
"downSpeed" = "Tổng tốc độ tải xuống cho tất cả các card mạng."
"totalSent" = "Tổng lưu lượng tải lên của tất cả các thẻ mạng kể từ khi hệ thống khởi động."
"totalReceive" = "Tổng lưu lượng tải xuống trên tất cả các thẻ mạng kể từ khi hệ thống khởi động."
"xraySwitchVersionDialog" = "Chuyển đổi Phiên bản Xray"
"xraySwitchVersionDialogDesc" = "Bạn có chắc chắn muốn chuyển đổi phiên bản Xray sang"
"dontRefresh" = "Đang tiến hành cài đặt, vui lòng không làm mới trang này."
"logs" = "Nhật ký"
"config" = "Cấu hình"
"backup" = "Sao lưu & Khôi phục"
"backupTitle" = "Sao lưu & Khôi phục Cơ sở dữ liệu"
"backupDescription" = "Hãy nhớ sao lưu trước khi nhập cơ sở dữ liệu mới."
"exportDatabase" = "Tải về Cơ sở dữ liệu"
"importDatabase" = "Tải lên Cơ sở dữ liệu"
[pages.inbounds]
"title" = "Điểm vào (Inbounds)"
"totalDownUp" = "Tổng tải lên/tải xuống"
"totalUsage" = "Tổng sử dụng"
"inboundCount" = "Số lượng điểm vào"
"operate" = "Thao tác"
"enable" = "Kích hoạt"
"remark" = "Chú thích"
"protocol" = "Giao thức"
"port" = "Cổng"
"traffic" = "Lưu lượng"
"details" = "Chi tiết"
"transportConfig" = "Giao vận"
"expireDate" = "Ngày hết hạn"
"resetTraffic" = "Đặt lại lưu lượng"
"addInbound" = "Thêm điểm vào"
"generalActions" = "Hành động chung"
"create" = "Tạo mới"
"update" = "Cập nhật"
"modifyInbound" = "Chỉnh sửa điểm vào (Inbound)"
"deleteInbound" = "Xóa điểm vào (Inbound)"
"deleteInboundContent" = "Xác nhận xóa điểm vào? (Inbound)"
"resetTrafficContent" = "Xác nhận đặt lại lưu lượng?"
"copyLink" = "Sao chép liên kết"
"address" = "Địa chỉ"
"network" = "Mạng"
"destinationPort" = "Cổng đích"
"targetAddress" = "Địa chỉ mục tiêu"
"monitorDesc" = "Mặc định để trống"
"meansNoLimit" = "Nghĩa là không giới hạn"
"totalFlow" = "Tổng lưu lượng"
"leaveBlankToNeverExpire" = "Để trống để không bao giờ hết hạn"
"noRecommendKeepDefault" = "Không yêu cầu đặc biệt để giữ nguyên cài đặt mặc định"
"certificatePath" = "Đường dẫn tập tin chứng chỉ"
"certificateContent" = "Nội dung tập tin chứng chỉ"
"publicKeyPath" = "Đường dẫn khóa công khai"
"publicKeyContent" = "Nội dung khóa công khai"
"keyPath" = "Đường dẫn khóa riêng tư"
"keyContent" = "Nội dung khóa riêng tư"
"clickOnQRcode" = "Nhấn vào Mã QR để sao chép"
"client" = "Client"
"export" = "Xuất liên kết"
"clone" = "Sao chép"
"cloneInbound" = "Sao chép điểm vào (Inbound)"
"cloneInboundContent" = "Tất cả cài đặt của điểm vào này, trừ Cổng, IP nghe và Clients, sẽ được áp dụng cho bản sao."
"cloneInboundOk" = "Sao chép"
"resetAllTraffic" = "Đặt lại lưu lượng cho tất cả điểm vào"
"resetAllTrafficTitle" = "Đặt lại lưu lượng cho tất cả điểm vào"
"resetAllTrafficContent" = "Bạn có chắc chắn muốn đặt lại lưu lượng cho tất cả điểm vào không?"
"resetInboundClientTraffics" = "Đặt lại lưu lượng cho các client của điểm vào"
"resetInboundClientTrafficTitle" = "Đặt lại lưu lượng cho tất cả lưu lượng của client"
"resetInboundClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho các client của điểm vào này không?"
"resetAllClientTraffics" = "Đặt lại lưu lượng cho tất cả client"
"resetAllClientTrafficTitle" = "Đặt lại lưu lượng cho tất cả client"
"resetAllClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho tất cả client không?"
"delDepletedClients" = "Xóa các client đã cạn kiệt"
"delDepletedClientsTitle" = "Xóa các client đã cạn kiệt"
"delDepletedClientsContent" = "Bạn có chắc chắn muốn xóa tất cả các client đã cạn kiệt không?"
"email" = "Email"
"emailDesc" = "Vui lòng cung cấp một địa chỉ email duy nhất."
"IPLimit" = "Giới hạn IP"
"IPLimitDesc" = "Vô hiệu hóa điểm vào nếu số lượng vượt quá giá trị đã nhập (nhập 0 để vô hiệu hóa giới hạn IP)."
"IPLimitlog" = "Lịch sử IP"
"IPLimitlogDesc" = "Lịch sử đăng nhập IP (trước khi kích hoạt điểm vào sau khi bị vô hiệu hóa bởi giới hạn IP, bạn nên xóa lịch sử)."
"IPLimitlogclear" = "Xóa Lịch sử"
"setDefaultCert" = "Đặt chứng chỉ từ bảng điều khiển"
"xtlsDesc" = "Xray core cần phiên bản 1.7.5"
"realityDesc" = "Xray core cần phiên bản 1.8.0 hoặc cao hơn."
"telegramDesc" = "Sử dụng Telegram ID mà không cần ký hiệu @ hoặc chat IDs (bạn có thể nhận được nó ở đây @userinfobot hoặc sử dụng lệnh '/id' trong bot)"
"subscriptionDesc" = "Bạn có thể tìm liên kết đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau"
[pages.client]
"add" = "Thêm Client"
"edit" = "Chỉnh sửa Client"
"submitAdd" = "Thêm Client"
"submitEdit" = "Lưu thay đổi"
"clientCount" = "Số lượng Client"
"bulk" = "Thêm hàng loạt"
"method" = "Phương pháp"
"first" = "Đầu tiên"
"last" = "Cuối cùng"
"prefix" = "Tiền tố"
"postfix" = "Hậu tố"
"delayedStart" = "Bắt đầu sau khi sử dụng lần đầu"
"expireDays" = "Số ngày hết hạn"
"days" = "ngày"
[pages.inbounds.toasts]
"obtain" = "Nhận"
[pages.inbounds.stream.general]
"requestHeader" = "Tiêu đề yêu cầu"
"name" = "Tên"
"value" = "Giá trị"
[pages.inbounds.stream.tcp]
"requestVersion" = "Phiên bản yêu cầu"
"requestMethod" = "Phương thức yêu cầu"
"requestPath" = "Đường dẫn yêu cầu"
"responseVersion" = "Phiên bản phản hồi"
"responseStatus" = "Trạng thái phản hồi"
"responseStatusDescription" = "Mô tả trạng thái phản hồi"
"responseHeader" = "Tiêu đề phản hồi"
[pages.inbounds.stream.quic]
"encryption" = "Mã hóa"
[pages.settings]
"title" = "Cài đặt"
"save" = "Lưu"
"infoDesc" = "Mọi thay đổi được thực hiện ở đây cần phải được lưu. Vui lòng khởi động lại bảng điều khiển để áp dụng các thay đổi."
"restartPanel" = "Khởi động lại Bảng điều khiển"
"restartPanelDesc" = "Bạn có chắc chắn muốn khởi động lại bảng điều khiển? Nhấn OK để khởi động lại sau 3 giây. Nếu bạn không thể truy cập bảng điều khiển sau khi khởi động lại, vui lòng xem thông tin nhật ký của bảng điều khiển trên máy chủ."
"actions" = "Hành động"
"resetDefaultConfig" = "Đặt lại Cấu hình Mặc định"
"panelSettings" = "Cài đặt Bảng điều khiển"
"securitySettings" = "Cài đặt Bảo mật"
"xrayConfiguration" = "Cấu hình Xray"
"TGBotSettings" = "Cài đặt Bot Telegram"
"panelListeningIP" = "IP Nghe của Bảng điều khiển"
"panelListeningIPDesc" = "Mặc định để trống để nghe tất cả các IP."
"panelListeningDomain" = "Tên miền của nghe Bảng điều khiển"
"panelListeningDomainDesc" = "Mặc định để trống để nghe tất cả các tên miền và IP"
"panelPort" = "Cổng Bảng điều khiển"
"panelPortDesc" = "Cổng được sử dụng để hiển thị bảng điều khiển này"
"publicKeyPath" = "Đường dẫn tập tin khóa công khai Chứng chỉ Bảng điều khiển"
"publicKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với."
"privateKeyPath" = "Đường dẫn tập tin khóa riêng tư Chứng chỉ Bảng điều khiển"
"privateKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với."
"panelUrlPath" = "Đường dẫn gốc URL Bảng điều khiển"
"panelUrlPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng."
"oldUsername" = "Tên người dùng hiện tại"
"currentPassword" = "Mật khẩu hiện tại"
"newUsername" = "Tên người dùng mới"
"newPassword" = "Mật khẩu mới"
"telegramBotEnable" = "Bật Bot Telegram"
"telegramBotEnableDesc" = "Kết nối với các tính năng của bảng điều khiển này thông qua bot Telegram"
"telegramToken" = "Token Telegram"
"telegramTokenDesc" = "Bạn phải nhận token từ quản lý bot Telegram @botfather"
"telegramChatId" = "Chat ID Telegram của quản trị viên"
"telegramChatIdDesc" = "Nhiều Chat ID phân tách bằng dấu phẩy. Sử dụng @userinfobot hoặc sử dụng lệnh '/id' trong bot để lấy Chat ID của bạn."
"telegramNotifyTime" = "Thời gian thông báo của bot Telegram"
"telegramNotifyTimeDesc" = "Sử dụng định dạng thời gian Crontab."
"tgNotifyBackup" = "Sao lưu Cơ sở dữ liệu"
"tgNotifyBackupDesc" = "Bao gồm tệp sao lưu cơ sở dữ liệu với thông báo báo cáo."
"tgNotifyLogin" = "Thông báo Đăng nhập"
"tgNotifyLoginDesc" = "Hiển thị tên người dùng, địa chỉ IP và thời gian khi ai đó cố gắng đăng nhập vào bảng điều khiển của bạn."
"sessionMaxAge" = "Tuổi tối đa của phiên"
"sessionMaxAgeDesc" = "Thời gian của phiên đăng nhập (đơn vị: phút)"
"expireTimeDiff" = "Ngưỡng hết hạn cho thông báo"
"expireTimeDiffDesc" = "Nhận thông báo về việc hết hạn tài khoản trước ngưỡng này (đơn vị: ngày)"
"trafficDiff" = "Ngưỡng lưu lượng cho thông báo"
"trafficDiffDesc" = "Nhận thông báo về việc cạn kiệt lưu lượng trước khi đạt đến ngưỡng này (đơn vị: GB)"
"tgNotifyCpu" = "Ngưỡng cảnh báo tỷ lệ CPU"
"tgNotifyCpuDesc" = "Nhận thông báo nếu tỷ lệ sử dụng CPU vượt quá ngưỡng này (đơn vị: %)"
"timeZone" = "Múi giờ"
"timeZoneDesc" = "Các tác vụ được lên lịch chạy theo thời gian trong múi giờ này."
"subSettings" = "Đăng ký"
"subEnable" = "Bật dịch vụ"
"subEnableDesc" = "Tính năng đăng ký với cấu hình riêng"
"subListen" = "Listening IP"
"subListenDesc" = "Mặc định để trống để nghe tất cả các IP"
"subPort" = "Cổng Đăng ký"
"subPortDesc" = "Số cổng dịch vụ đăng ký phải chưa được sử dụng trên máy chủ"
"subCertPath" = "Đường dẫn tập tin khóa công khai Chứng chỉ Đăng ký"
"subCertPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với '/'"
"subKeyPath" = "Đường dẫn tập tin khóa riêng tư Chứng chỉ Đăng ký"
"subKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với '/'"
"subPath" = "Đường dẫn gốc URL Đăng ký"
"subPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng '/'"
"subDomain" = "Tên miền con"
"subDomainDesc" = "Mặc định để trống để nghe tất cả các tên miền và IP"
"subUpdates" = "Khoảng thời gian cập nhật đăng ký"
"subUpdatesDesc" = "Số giờ giữa các cập nhật trong ứng dụng khách"
"subEncrypt" = "Mã hóa cấu hình"
"subEncryptDesc" = "Mã hóa các cấu hình được trả về trong đăng ký"
"subShowInfo" = "Hiển thị thông tin sử dụng"
"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"
[pages.settings.templates]
"title" = "Mẫu"
"basicTemplate" = "Mẫu Cơ bản"
"advancedTemplate" = "Mẫu Nâng cao"
"completeTemplate" = "Mẫu Đầy đủ"
"generalConfigs" = "Cấu hình Chung"
"generalConfigsDesc" = "Những tùy chọn này sẽ cung cấp điều chỉnh tổng quát."
"blockConfigs" = "Cấu hình Chặn"
"blockConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các giao thức và trang web cụ thể."
"blockCountryConfigs" = "Cấu hình Chặn Quốc gia"
"blockCountryConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các tên miền quốc gia cụ thể."
"directCountryConfigs" = "Cấu hình Kết nối Trực tiếp Quốc gia"
"directCountryConfigsDesc" = "Những tùy chọn này sẽ kết nối người dùng trực tiếp đến các tên miền quốc gia cụ thể."
"ipv4Configs" = "Cấu hình IPv4"
"ipv4ConfigsDesc" = "Những tùy chọn này sẽ chỉ định kết nối đến các tên miền mục tiêu qua IPv4."
"warpConfigs" = "Cấu hình WARP"
"warpConfigsDesc" = "Cảnh báo: Trước khi sử dụng những tùy chọn này, hãy cài đặt WARP ở chế độ proxy socks5 trên máy chủ của bạn bằng cách làm theo các bước trên GitHub của bảng điều khiển. WARP sẽ định tuyến lưu lượng đến các trang web qua máy chủ Cloudflare."
"xrayConfigTemplate" = "Mẫu Cấu hình Xray"
"xrayConfigTemplateDesc" = "Tạo tệp cấu hình Xray cuối cùng dựa trên mẫu này."
"xrayConfigFreedomStrategy" = "Cấu hình Chiến lược cho Giao thức Freedom"
"xrayConfigFreedomStrategyDesc" = "Đặt chiến lược đầu ra của mạng trong Giao thức Freedom."
"xrayConfigRoutingStrategy" = "Cấu hình Chiến lược Định tuyến Tên miền"
"xrayConfigRoutingStrategyDesc" = "Đặt chiến lược định tuyến tổng thể cho việc giải quyết DNS."
"xrayConfigTorrent" = "Cấu hình sử dụng BitTorrent"
"xrayConfigTorrentDesc" = "Thay đổi mẫu cấu hình để tránh việc người dùng sử dụng BitTorrent."
"xrayConfigPrivateIp" = "Cấm kết nối đến dải IP Riêng tư"
"xrayConfigPrivateIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP riêng tư."
"xrayConfigAds" = "Chặn Quảng cáo"
"xrayConfigAdsDesc" = "Thay đổi mẫu cấu hình để chặn quảng cáo."
"xrayConfigFamily" = "Chặn Phần mềm độc hại và Nội dung cho Người lớn"
"xrayConfigFamilyDesc" = "Các trình giải quyết DNS để chặn phần mềm độc hại và nội dung cho bảo vệ gia đình."
"xrayConfigSpeedtest" = "Chặn Trang web Speedtest"
"xrayConfigSpeedtestDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các trang web Speedtest."
"xrayConfigIRIp" = "Vô hiệu hóa kết nối đến dải IP của Iran"
"xrayConfigIRIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP của Iran."
"xrayConfigIRDomain" = "Vô hiệu hóa kết nối đến tên miền của Iran"
"xrayConfigIRDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các tên miền của Iran."
"xrayConfigChinaIp" = "Vô hiệu hóa kết nối đến dải IP của Trung Quốc"
"xrayConfigChinaIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP của Trung Quốc."
"xrayConfigChinaDomain" = "Vô hiệu hóa kết nối đến tên miền của Trung Quốc"
"xrayConfigChinaDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các tên miền của Trung Quốc."
"xrayConfigRussiaIp" = "Vô hiệu hóa kết nối đến dải IP của Nga"
"xrayConfigRussiaIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP của Nga."
"xrayConfigRussiaDomain" = "Vô hiệu hóa kết nối đến tên miền của Nga"
"xrayConfigRussiaDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các tên miền của Nga."
"xrayConfigDirectIRIp" = "Kết nối trực tiếp đến dải IP của Iran"
"xrayConfigDirectIRIpDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến dải IP của Iran."
"xrayConfigDirectIRDomain" = "Kết nối trực tiếp đến tên miền của Iran"
"xrayConfigDirectIRDomainDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến các tên miền của Iran."
"xrayConfigDirectChinaIp" = "Kết nối trực tiếp đến dải IP của Trung Quốc"
"xrayConfigDirectChinaIpDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến dải IP của Trung Quốc."
"xrayConfigDirectChinaDomain" = "Kết nối trực tiếp đến tên miền của Trung Quốc"
"xrayConfigDirectChinaDomainDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến các tên miền của Trung Quốc."
"xrayConfigDirectRussiaIp" = "Kết nối trực tiếp đến dải IP của Nga"
"xrayConfigDirectRussiaIpDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến dải IP của Nga."
"xrayConfigDirectRussiaDomain" = "Kết nối trực tiếp đến tên miền của Nga"
"xrayConfigDirectRussiaDomainDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến các tên miền của Nga."
"xrayConfigGoogleIPv4" = "Sử dụng IPv4 cho Google"
"xrayConfigGoogleIPv4Desc" = "Thêm định tuyến cho Google để kết nối qua IPv4."
"xrayConfigNetflixIPv4" = "Sử dụng IPv4 cho Netflix"
"xrayConfigNetflixIPv4Desc" = "Thêm định tuyến cho Netflix để kết nối qua IPv4."
"xrayConfigGoogleWARP" = "Định tuyến Google qua WARP."
"xrayConfigGoogleWARPDesc" = "Thêm định tuyến cho Google qua WARP."
"xrayConfigOpenAIWARP" = "Định tuyến OpenAI (ChatGPT) qua WARP."
"xrayConfigOpenAIWARPDesc" = "Thêm định tuyến cho OpenAI (ChatGPT) qua WARP."
"xrayConfigNetflixWARP" = "Định tuyến Netflix qua WARP."
"xrayConfigNetflixWARPDesc" = "Thêm định tuyến cho Netflix qua WARP."
"xrayConfigSpotifyWARP" = "Định tuyến Spotify qua WARP."
"xrayConfigSpotifyWARPDesc" = "Thêm định tuyến cho Spotify qua WARP."
"xrayConfigIRWARP" = "Định tuyến tên miền của Iran qua WARP."
"xrayConfigIRWARPDesc" = "Thêm định tuyến cho các tên miền của Iran qua WARP."
"xrayConfigInbounds" = "Cấu hình của Inbounds"
"xrayConfigInboundsDesc" = "Thay đổi mẫu cấu hình để chấp nhận các máy khách cụ thể."
"xrayConfigOutbounds" = "Cấu hình của Outbounds"
"xrayConfigOutboundsDesc" = "Thay đổi mẫu cấu hình để xác định các cách ra đi cho máy chủ này."
"xrayConfigRoutings" = "Cấu hình của Luật Định tuyến."
"xrayConfigRoutingsDesc" = "Thay đổi mẫu cấu hình để xác định luật định tuyến cho máy chủ này."
"manualLists" = "Danh sách Thủ công"
"manualListsDesc" = "Vui lòng sử dụng định dạng mảng JSON."
"manualBlockedIPs" = "Danh sách IP bị Chặn"
"manualBlockedDomains" = "Danh sách Tên miền bị Chặn"
"manualDirectIPs" = "Danh sách IP Trực tiếp"
"manualDirectDomains" = "Danh sách Tên miền Trực tiếp"
"manualIPv4Domains" = "Danh sách Tên miền IPv4"
"manualWARPDomains" = "Danh sách Tên miền WARP"
[pages.settings.security]
"admin" = "Quản trị viên"
"secret" = "Mã thông báo bí mật"
"loginSecurity" = "Bảo mật đăng nhập"
"loginSecurityDesc" = "Bật bước bảo mật đăng nhập bổ sung cho người dùng"
"secretToken" = "Mã thông báo bí mật"
"secretTokenDesc" = "Vui lòng sao chép và lưu trữ mã thông báo này một cách an toàn ở nơi an toàn. Mã thông báo này cần thiết để đăng nhập và không thể phục hồi từ công cụ lệnh x-ui."
[pages.settings.toasts]
"modifySettings" = "Chỉnh sửa cài đặt "
"getSettings" = "Lấy cài đặt "
"modifyUser" = "Chỉnh sửa người dùng "
"originalUserPassIncorrect" = "Tên người dùng hoặc mật khẩu gốc không đúng"
"userPassMustBeNotEmpty" = "Tên người dùng mới và mật khẩu mới không thể để trống"
[tgbot]
"keyboardClosed" = "❌ Bàn phím tùy chỉnh đã đóng!"
"noResult" = "❗ Không có kết quả!"
"noQuery" = "❌ Không tìm thấy truy vấn! Vui lòng sử dụng lệnh lại!"
"wentWrong" = "❌ Đã xảy ra lỗi!"
"noIpRecord" = "❗ Không có bản ghi IP!"
"noInbounds" = "❗ Không tìm thấy inbound!"
"unlimited" = "♾ Không giới hạn"
"month" = "Tháng"
"months" = "Tháng"
"day" = "Ngày"
"days" = "Ngày"
"hours" = "Giờ"
"unknown" = "Không rõ"
"inbounds" = "Inbounds"
"clients" = "Khách hàng"
[tgbot.commands]
"unknown" = "❗ Lệnh không rõ"
"pleaseChoose" = "👇 Vui lòng chọn:\r\n"
"help" = "🤖 Chào mừng bạn đến với bot này! Bot được thiết kế để cung cấp cho bạn dữ liệu cụ thể từ máy chủ và cho phép bạn thực hiện các thay đổi cần thiết.\r\n\r\n"
"start" = "👋 Xin chào <i>{{ .Firstname }}</i>.\r\n"
"welcome" = "🤖 Chào mừng đến với bot quản lý của <b>{{ .Hostname }}</b>.\r\n"
"status" = "✅ Bot hoạt động bình thường!"
"usage" = "❗ Vui lòng cung cấp văn bản để tìm kiếm!"
"getID" = "🆔 ID của bạn: <code>{{ .ID }}</code>"
"helpAdminCommands" = "Tìm kiếm email của khách hàng:\r\n<code>/usage [Email]</code>\r\n \r\nTìm kiếm inbounds (với thống kê của khách hàng):\r\n<code>/inbound [Ghi chú]</code>"
"helpClientCommands" = "Để tìm kiếm thống kê, hãy sử dụng lệnh sau:\r\n \r\n<code>/usage [UUID|Mật khẩu]</code>\r\n \r\nSử dụng UUID cho vmess/vless và Mật khẩu cho Trojan."
[tgbot.messages]
"cpuThreshold" = "🔴 Sử dụng CPU {{ .Percent }}% vượt quá ngưỡng {{ .Threshold }}%"
"selectUserFailed" = "❌ Lỗi khi chọn người dùng!"
"userSaved" = "✅ Người dùng Telegram đã được lưu."
"loginSuccess" = "✅ Đăng nhập thành công vào bảng điều khiển.\r\n"
"loginFailed" = "❗️ Đăng nhập vào bảng điều khiển thất bại.\r\n"
"report" = "🕰 Báo cáo định kỳ: {{ .RunTime }}\r\n"
"datetime" = "⏰ Ngày-Giờ: {{ .DateTime }}\r\n"
"hostname" = "💻 Tên máy chủ: {{ .Hostname }}\r\n"
"version" = "🚀 Phiên bản X-UI: {{ .Version }}\r\n"
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
"ip" = "🌐 IP: {{ .IP }}\r\n"
"ips" = "🔢 Các IP: \r\n{{ .IPs }}\r\n"
"serverUpTime" = "⏳ Thời gian hoạt động của máy chủ: {{ .UpTime }} {{ .Unit }}\r\n"
"serverLoad" = "📈 Tải máy chủ: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
"serverMemory" = "📋 Bộ nhớ máy chủ: {{ .Current }}/{{ .Total }}\r\n"
"tcpCount" = "🔹 Số lượng kết nối TCP: {{ .Count }}\r\n"
"udpCount" = "🔸 Số lượng kết nối UDP: {{ .Count }}\r\n"
"traffic" = "🚦 Lưu lượng: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
"xrayStatus" = " Trạng thái Xray: {{ .State }}\r\n"
"username" = "👤 Tên người dùng: {{ .Username }}\r\n"
"time" = "⏰ Thời gian: {{ .Time }}\r\n"
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
"port" = "🔌 Cổng: {{ .Port }}\r\n"
"expire" = "📅 Ngày hết hạn: {{ .Time }}\r\n"
"expireIn" = "📅 Hết hạn sau: {{ .Time }}\r\n"
"active" = "💡 Hoạt động: ✅ Có\r\n"
"inactive" = "💡 Hoạt động: ❌ Không\r\n"
"email" = "📧 Email: {{ .Email }}\r\n"
"upload" = "🔼 Tải lên: ↑{{ .Upload }}\r\n"
"download" = "🔽 Tải xuống: ↓{{ .Download }}\r\n"
"total" = "📊 Tổng cộng: ↑↓{{ .UpDown }} / {{ .Total }}\r\n"
"TGUser" = "👤 Người dùng Telegram: {{ .TelegramID }}\r\n"
"exhaustedMsg" = "🚨 Sự cạn kiệt {{ .Type }}:\r\n"
"exhaustedCount" = "🚨 Số lần cạn kiệt {{ .Type }}:\r\n"
"disabled" = "🛑 Vô hiệu hóa: {{ .Disabled }}\r\n"
"depleteSoon" = "🔜 Sắp cạn kiệt: {{ .Deplete }}\r\n \r\n"
"backupTime" = "🗄 Thời gian sao lưu: {{ .Time }}\r\n"
"refreshedOn" = "\r\n📋🔄 Đã cập nhật lần cuối vào: {{ .Time }}\r\n \r\n"
[tgbot.buttons]
"closeKeyboard" = "❌ Đóng Bàn Phím"
"cancel" = "❌ Hủy"
"cancelReset" = "❌ Hủy Đặt Lại"
"cancelIpLimit" = "❌ Hủy Giới Hạn IP"
"confirmResetTraffic" = "✅ Xác Nhận Đặt Lại Lưu Lượng?"
"confirmClearIps" = "✅ Xác Nhận Xóa Các IP?"
"confirmRemoveTGUser" = "✅ Xác Nhận Xóa Người Dùng Telegram?"
"dbBackup" = "Tải Backup DB"
"serverUsage" = "Sử Dụng Máy Chủ"
"getInbounds" = "Lấy Inbounds"
"depleteSoon" = "Sắp Cạn Kiệt"
"clientUsage" = "Lấy Sử Dụng"
"commands" = "Lệnh"
"refresh" = "🔄 Cập Nhật"
"clearIPs" = "❌ Xóa IP"
"removeTGUser" = "❌ Xóa Người Dùng Telegram"
"selectTGUser" = "👤 Chọn Người Dùng Telegram"
"selectOneTGUser" = "👤 Chọn một người dùng telegram:"
"resetTraffic" = "📈 Đặt Lại Lưu Lượng"
"resetExpire" = "📅 Đặt Lại Ngày Hết Hạn"
"ipLog" = "🔢 Log IP"
"ipLimit" = "🔢 Giới Hạn IP"
"setTGUser" = "👤 Đặt Người Dùng Telegram"
"toggle" = "🔘 Bật / Tắt"
[tgbot.answers]
"errorOperation" = "❗ Lỗi Trong Quá Trình Thực Hiện."
"getInboundsFailed" = "❌ Không Thể Lấy Được Inbounds"
"canceled" = "❌ {{ .Email }} : Thao Tác Đã Bị Hủy."
"clientRefreshSuccess" = "✅ {{ .Email }} : Cập Nhật Thành Công Cho Khách Hàng."
"IpRefreshSuccess" = "✅ {{ .Email }} : Cập Nhật Thành Công Cho IPs."
"TGIdRefreshSuccess" = "✅ {{ .Email }} : Cập Nhật Thành Công Cho Người Dùng Telegram."
"resetTrafficSuccess" = "✅ {{ .Email }} : Đặt Lại Lưu Lượng Thành Công."
"expireResetSuccess" = "✅ {{ .Email }} : Đặt Lại Ngày Hết Hạn Thành Công."
"resetIpSuccess" = "✅ {{ .Email }} : Giới Hạn IP {{ .Count }} Đã Được Lưu Thành Công."
"clearIpSuccess" = "✅ {{ .Email }} : IPs Đã Được Xóa Thành Công."
"getIpLog" = "✅ {{ .Email }} : Lấy Log IP Thành Công."
"getUserInfo" = "✅ {{ .Email }} : Lấy Thông Tin Người Dùng Telegram Thành Công."
"removedTGUserSuccess" = "✅ {{ .Email }} : Người Dùng Telegram Đã Được Xóa Thành Công."
"enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công."
"disableSuccess" = "✅ {{ .Email }} : Đã Tắt Thành Công."
"askToAddUserId" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nID người dùng của bạn: <b>{{ .TgUserID }}</b>"
"askToAddUserName" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng tên người dùng hoặc ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nTên người dùng của bạn: <b>@{{ .TgUserName }}</b>\r\n\r\nID người dùng của bạn: <b>{{ .TgUserID }}</b>"

View File

@@ -127,7 +127,6 @@
"network" = "网络"
"destinationPort" = "目标端口"
"targetAddress" = "目标地址"
"disableInsecureEncryption" = "禁用不安全加密"
"monitorDesc" = "默认留空即可"
"meansNoLimit" = "表示不限制"
"totalFlow" = "总流量"
@@ -274,6 +273,10 @@
"subDomainDesc" = "留空默认监控所有域名和IP"
"subUpdates" = "订阅更新间隔"
"subUpdatesDesc" = "客户端应用程序更新之间的间隔时间"
"subEncrypt" = "加密配置"
"subEncryptDesc" = "在订阅中加密返回的配置"
"subShowInfo" = "显示使用信息"
"subShowInfoDesc" = "在配置名称后显示剩余流量和日期"
[pages.settings.templates]
"title" = "模板"

View File

@@ -247,14 +247,11 @@ func (s *Server) startTask() {
s.cron.AddJob("@every 10s", job.NewXrayTrafficJob())
}()
// Check the inbound traffic every 30 seconds that the traffic exceeds and expires
s.cron.AddJob("@every 30s", job.NewCheckInboundJob())
// check client ips from log file every 10 sec
s.cron.AddJob("@every 10s", job.NewCheckClientIpJob())
// check client ips from log file every 20 sec
s.cron.AddJob("@every 20s", job.NewCheckClientIpJob())
// check client ips from log file every 3 day
s.cron.AddJob("@every 3d", job.NewClearLogsJob())
// check client ips from log file every day
s.cron.AddJob("@daily", job.NewClearLogsJob())
// Make a traffic condition every day, 8:30
var entry cron.EntryID

136
x-ui.sh
View File

@@ -54,6 +54,8 @@ elif [[ "${release}" == "debian" ]]; then
if [[ ${os_version} -lt 10 ]]; then
echo -e "${red} Please use Debian 10 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "arch" ]]; then
echo "OS is ArchLinux"
fi
@@ -508,7 +510,7 @@ update_geo() {
rm -f geoip.dat geosite.dat iran.dat
wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
wget -N https://github.com/bootmortis/iran-hosted-domains/releases/latest/download/iran.dat
wget -N https://github.com/MasterKia/iran-hosted-domains/releases/latest/download/iran.dat
systemctl start x-ui
echo -e "${green}Geosite.dat + Geoip.dat + Iran.dat have been updated successfully in bin folder '${binfolder}'!${plain}"
before_show_menu
@@ -646,6 +648,83 @@ ssl_cert_issue() {
fi
}
ssl_cert_issue_CF() {
echo -E ""
LOGD "******Instructions for use******"
LOGI "This Acme script requires the following data:"
LOGI "1.Cloudflare Registered e-mail"
LOGI "2.Cloudflare Global API Key"
LOGI "3.The domain name that has been resolved dns to the current server by Cloudflare"
LOGI "4.The script applies for a certificate. The default installation path is /root/cert "
confirm "Confirmed?[y/n]" "y"
if [ $? -eq 0 ]; then
# check for acme.sh first
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
echo "acme.sh could not be found. we will install it"
install_acme
if [ $? -ne 0 ]; then
LOGE "install acme failed, please check logs"
exit 1
fi
fi
CF_Domain=""
CF_GlobalKey=""
CF_AccountEmail=""
certPath=/root/cert
if [ ! -d "$certPath" ]; then
mkdir $certPath
else
rm -rf $certPath
mkdir $certPath
fi
LOGD "Please set a domain name:"
read -p "Input your domain here:" CF_Domain
LOGD "Your domain name is set to:${CF_Domain}"
LOGD "Please set the API key:"
read -p "Input your key here:" CF_GlobalKey
LOGD "Your API key is:${CF_GlobalKey}"
LOGD "Please set up registered email:"
read -p "Input your email here:" CF_AccountEmail
LOGD "Your registered email address is:${CF_AccountEmail}"
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
if [ $? -ne 0 ]; then
LOGE "Default CA, Lets'Encrypt fail, script exiting..."
exit 1
fi
export CF_Key="${CF_GlobalKey}"
export CF_Email=${CF_AccountEmail}
~/.acme.sh/acme.sh --issue --dns dns_cf -d ${CF_Domain} -d *.${CF_Domain} --log
if [ $? -ne 0 ]; then
LOGE "Certificate issuance failed, script exiting..."
exit 1
else
LOGI "Certificate issued Successfully, Installing..."
fi
~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} --ca-file /root/cert/ca.cer \
--cert-file /root/cert/${CF_Domain}.cer --key-file /root/cert/${CF_Domain}.key \
--fullchain-file /root/cert/fullchain.cer
if [ $? -ne 0 ]; then
LOGE "Certificate installation failed, script exiting..."
exit 1
else
LOGI "Certificate installed Successfully,Turning on automatic updates..."
fi
~/.acme.sh/acme.sh --upgrade --auto-upgrade
if [ $? -ne 0 ]; then
LOGE "Auto update setup Failed, script exiting..."
ls -lah cert
chmod 755 $certPath
exit 1
else
LOGI "The certificate is installed and auto-renewal is turned on, Specific information is as follows"
ls -lah cert
chmod 755 $certPath
fi
else
show_menu
fi
}
warp_cloudflare() {
echo -e "${green}\t1.${plain} Install WARP socks5 proxy"
echo -e "${green}\t2.${plain} Account Type (free, plus, team)"
@@ -713,8 +792,8 @@ enabled=true
filter=3x-ipl
action=3x-ipl
logpath=${iplimit_log_path}
maxretry=3
findtime=100
maxretry=4
findtime=60
bantime=${bantime}m
EOF
@@ -772,7 +851,8 @@ iplimit_main() {
echo -e "${green}\t2.${plain} Change Ban Duration"
echo -e "${green}\t3.${plain} Unban Everyone"
echo -e "${green}\t4.${plain} Check Logs"
echo -e "${green}\t5.${plain} Uninstall IP Limit"
echo -e "${green}\t5.${plain} fail2ban status"
echo -e "${green}\t6.${plain} Uninstall IP Limit"
echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice
case "$choice" in
@@ -788,7 +868,7 @@ iplimit_main() {
2)
read -rp "Please enter new Ban Duration in Minutes [default 5]: " NUM
if [[ $NUM =~ ^[0-9]+$ ]]; then
create_iplimit_jail ${NUM}
create_iplimit_jails ${NUM}
systemctl restart fail2ban
else
echo -e "${red}${NUM} is not a number! Please, try again.${plain}"
@@ -816,6 +896,10 @@ iplimit_main() {
iplimit_main
fi ;;
5)
service fail2ban status
;;
6)
remove_iplimit ;;
*) echo "Invalid choice" ;;
esac
@@ -886,23 +970,19 @@ remove_iplimit(){
echo -e "${green}IP Limit removed successfully!${plain}\n"
before_show_menu ;;
2)
rm -f /etc/fail2ban/filter.d/3x-ipl.conf
rm -f /etc/fail2ban/action.d/3x-ipl.conf
rm -f /etc/fail2ban/jail.d/3x-ipl.conf
rm -rf /etc/fail2ban
systemctl stop fail2ban
systemctl disable fail2ban
case "${release}" in
ubuntu|debian)
apt remove fail2ban -y ;;
apt-get purge fail2ban -y;;
centos)
yum -y remove fail2ban ;;
yum remove fail2ban -y;;
fedora)
dnf -y remove fail2ban ;;
dnf remove fail2ban -y;;
*)
echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n"
exit 1 ;;
esac
rm -rf /etc/fail2ban
echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n"
before_show_menu ;;
0)
@@ -955,16 +1035,17 @@ show_menu() {
${green}14.${plain} Disable x-ui On System Startup
————————————————
${green}15.${plain} SSL Certificate Management
${green}16.${plain} IP Limit Management
${green}17.${plain} WARP Management
${green}16.${plain} Cloudflare SSL Certificate
${green}17.${plain} IP Limit Management
${green}18.${plain} WARP Management
————————————————
${green}18.${plain} Enable BBR
${green}19.${plain} Update Geo Files
${green}20.${plain} Active Firewall and open ports
${green}21.${plain} Speedtest by Ookla
${green}19.${plain} Enable BBR
${green}20.${plain} Update Geo Files
${green}21.${plain} Active Firewall and open ports
${green}22.${plain} Speedtest by Ookla
"
show_status
echo && read -p "Please enter your selection [0-21]: " num
echo && read -p "Please enter your selection [0-22]: " num
case "${num}" in
0)
@@ -1016,25 +1097,28 @@ show_menu() {
ssl_cert_issue_main
;;
16)
iplimit_main
ssl_cert_issue_CF
;;
17)
warp_cloudflare
iplimit_main
;;
18)
enable_bbr
warp_cloudflare
;;
19)
update_geo
enable_bbr
;;
20)
open_ports
update_geo
;;
21)
open_ports
;;
22)
run_speedtest
;;
*)
LOGE "Please enter the correct number [0-21]"
LOGE "Please enter the correct number [0-22]"
;;
esac
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/xtls/xray-core/common/serial"
"github.com/xtls/xray-core/infra/conf"
"github.com/xtls/xray-core/proxy/shadowsocks"
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
"github.com/xtls/xray-core/proxy/trojan"
"github.com/xtls/xray-core/proxy/vless"
"github.com/xtls/xray-core/proxy/vmess"
@@ -62,10 +63,12 @@ func (x *XrayAPI) AddInbound(inbound []byte) error {
err := json.Unmarshal(inbound, conf)
if err != nil {
logger.Debug("Failed to unmarshal inbound:", err)
return err
}
config, err := conf.Build()
if err != nil {
logger.Debug("Failed to build inbound Detur:", err)
return err
}
inboundConfig := command.AddInboundRequest{Inbound: config}
@@ -99,9 +102,31 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]in
Password: user["password"].(string),
})
case "shadowsocks":
account = serial.ToTypedMessage(&shadowsocks.Account{
Password: user["password"].(string),
})
var ssCipherType shadowsocks.CipherType
switch user["cipher"].(string) {
case "aes-128-gcm":
ssCipherType = shadowsocks.CipherType_AES_128_GCM
case "aes-256-gcm":
ssCipherType = shadowsocks.CipherType_AES_256_GCM
case "chacha20-poly1305", "chacha20-ietf-poly1305":
ssCipherType = shadowsocks.CipherType_CHACHA20_POLY1305
case "xchacha20-poly1305", "xchacha20-ietf-poly1305":
ssCipherType = shadowsocks.CipherType_XCHACHA20_POLY1305
default:
ssCipherType = shadowsocks.CipherType_NONE
}
if ssCipherType != shadowsocks.CipherType_NONE {
account = serial.ToTypedMessage(&shadowsocks.Account{
Password: user["password"].(string),
CipherType: ssCipherType,
})
} else {
account = serial.ToTypedMessage(&shadowsocks_2022.User{
Key: user["password"].(string),
Email: user["email"].(string),
})
}
default:
return nil
}

View File

@@ -13,6 +13,8 @@ import (
"strings"
"sync"
"syscall"
"time"
"x-ui/config"
"x-ui/logger"
"x-ui/util/common"
@@ -44,10 +46,6 @@ func GetIranPath() string {
return config.GetBinFolderPath() + "/iran.dat"
}
func GetBlockedIPsPath() string {
return config.GetBinFolderPath() + "/BlockedIps"
}
func GetIPLimitLogPath() string {
return config.GetLogFolder() + "/3xipl.log"
}
@@ -88,7 +86,6 @@ func stopProcess(p *Process) {
p.Stop()
}
type Process struct {
*process
}
@@ -105,16 +102,18 @@ type process struct {
version string
apiPort int
config *Config
lines *queue.Queue
exitErr error
config *Config
lines *queue.Queue
exitErr error
startTime time.Time
}
func newProcess(config *Config) *process {
return &process{
version: "Unknown",
config: config,
lines: queue.New(100),
version: "Unknown",
config: config,
lines: queue.New(100),
startTime: time.Now(),
}
}
@@ -158,6 +157,10 @@ func (p *Process) GetConfig() *Config {
return p.config
}
func (p *Process) GetUptime() uint64 {
return uint64(time.Since(p.startTime).Seconds())
}
func (p *process) refreshAPIPort() {
for _, inbound := range p.config.InboundConfigs {
if inbound.Tag == "api" {
@@ -203,7 +206,7 @@ func (p *process) Start() (err error) {
return common.NewErrorf("Failed to write configuration file: %v", err)
}
cmd := exec.Command(GetBinaryPath(), "-c", configPath, "-restrictedIPsPath", GetBlockedIPsPath())
cmd := exec.Command(GetBinaryPath(), "-c", configPath)
p.cmd = cmd
stdReader, err := cmd.StdoutPipe()