Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/docker.yml6
-rw-r--r--Dockerfile5
-rw-r--r--README.md66
-rw-r--r--config/version2
-rw-r--r--database/db.go5
-rw-r--r--database/model/model.go9
-rw-r--r--go.mod4
-rw-r--r--go.sum9
-rw-r--r--logger/logger.go10
-rw-r--r--web/assets/js/model/outbound.js14
-rw-r--r--web/controller/xray_setting.go11
-rw-r--r--web/html/xui/form/stream/stream_ws.html2
-rw-r--r--web/html/xui/warp_modal.html2
-rw-r--r--web/html/xui/xray.html106
-rw-r--r--web/job/check_client_ip_job.go50
-rw-r--r--web/job/xray_traffic_job.go15
-rw-r--r--web/service/config.json5
-rw-r--r--web/service/inbound.go4
-rw-r--r--web/service/outbound.go80
-rw-r--r--web/service/tgbot.go67
-rw-r--r--web/service/xray.go2
-rw-r--r--web/translation/translate.en_US.toml10
-rw-r--r--web/translation/translate.es_ES.toml6
-rw-r--r--web/translation/translate.fa_IR.toml6
-rw-r--r--web/translation/translate.ru_RU.toml4
-rw-r--r--web/translation/translate.vi_VN.toml4
-rw-r--r--web/translation/translate.zh_Hans.toml4
-rw-r--r--xray/api.go6
-rw-r--r--xray/log_writer.go2
-rw-r--r--xray/traffic.go9
30 files changed, 404 insertions, 121 deletions
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 1115de37..00ac9169 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -18,7 +18,7 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0
- - name: Log in to GitHub Container Registry
+ - name: Login to GHCR
uses: docker/login-action@v3.0.0
with:
registry: ghcr.io
@@ -27,7 +27,7 @@ jobs:
- name: Docker meta
id: meta
- uses: docker/metadata-action@v5.5.0
+ uses: docker/metadata-action@v5.5.1
with:
images: ghcr.io/${{ github.repository }}
@@ -38,4 +38,4 @@ jobs:
push: ${{ github.event_name != 'pull_request' }}
platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386
tags: ${{ steps.meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
+ labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 7c3e64b2..5f4a36c3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
# ========================================================
# Stage: Builder
# ========================================================
-FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder
+FROM golang:1.21-alpine AS builder
WORKDIR /app
ARG TARGETARCH
@@ -33,7 +33,8 @@ RUN apk add --no-cache --update \
COPY --from=builder /app/build/ /app/
COPY --from=builder /app/DockerEntrypoint.sh /app/
-COPY --from=builder /app/x-ui /usr/bin/x-ui
+COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
+
# Configure fail2ban
RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \
diff --git a/README.md b/README.md
index ec1e5a15..a1c902e2 100644
--- a/README.md
+++ b/README.md
@@ -25,11 +25,39 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
## Install Custom Version
-To install your desired version, add the version to the end of the installation command. e.g., ver `v2.1.2`:
+To install your desired version, add the version to the end of the installation command. e.g., ver `v2.1.3`:
```
-bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.1.2
+bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.1.3
```
+
+## SSL Certificate
+
+<details>
+ <summary>Click for SSL Certificate</summary>
+
+### Cloudflare
+
+The Management script has a built-in SSL certificate application for Cloudflare. To use this script to apply for a certificate, you need the following:
+
+- Cloudflare registered email
+- Cloudflare Global API Key
+- The domain name has been resolved to the current server through cloudflare
+
+**1:** Run the`x-ui`command on the terminal, then choose `Cloudflare SSL Certificate`.
+
+
+### Certbot
+```
+apt-get install certbot -y
+certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
+certbot renew --dry-run
+```
+
+***Tip:*** *Certbot is also built into the Management script. You can run the `x-ui` command, then choose `SSL Certificate Management`.*
+
+</details>
+
## Manual Install & Upgrade
<details>
@@ -201,34 +229,6 @@ Supports a variety of different architectures and devices. Here are some of the
</details>
-
-## SSL Certificate
-
-<details>
- <summary>Click for SSL Certificate</summary>
-
-### Cloudflare
-
-The Management script has a built-in SSL certificate application for Cloudflare. To use this script to apply for a certificate, you need the following:
-
-- Cloudflare registered email
-- Cloudflare Global API Key
-- The domain name has been resolved to the current server through cloudflare
-
-**1:** Run the`x-ui`command on the terminal, then choose `Cloudflare SSL Certificate`.
-
-
-### Certbot
-```
-apt-get install certbot -y
-certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
-certbot renew --dry-run
-```
-
-***Tip:*** *Certbot is also built into the Management script. You can run the `x-ui` command, then choose `SSL Certificate Management`.*
-
-</details>
-
## [WARP Configuration](https://gitlab.com/fscarmen/warp)
<details>
@@ -281,13 +281,13 @@ If you want to use routing to WARP before v2.1.0 follow steps as below:
2. Select `IP Limit Management`.
3. Choose the appropriate options based on your needs.
- - make sure you have access.log on your Xray Configuration
+ - make sure you have ./access.log on your Xray Configuration after v2.1.3 we have an option for it
```sh
"log": {
- "loglevel": "warning",
"access": "./access.log",
- "error": "./error.log"
+ "dnsLog": false,
+ "loglevel": "warning"
},
```
diff --git a/config/version b/config/version
index 8f9174b4..abae0d9a 100644
--- a/config/version
+++ b/config/version
@@ -1 +1 @@
-2.1.2 \ No newline at end of file
+2.1.3 \ No newline at end of file
diff --git a/database/db.go b/database/db.go
index 8bd0fb49..c75953f0 100644
--- a/database/db.go
+++ b/database/db.go
@@ -21,6 +21,7 @@ var db *gorm.DB
var initializers = []func() error{
initUser,
initInbound,
+ initOutbound,
initSetting,
initInboundClientIps,
initClientTraffic,
@@ -51,6 +52,10 @@ func initInbound() error {
return db.AutoMigrate(&model.Inbound{})
}
+func initOutbound() error {
+ return db.AutoMigrate(&model.OutboundTraffics{})
+}
+
func initSetting() error {
return db.AutoMigrate(&model.Setting{})
}
diff --git a/database/model/model.go b/database/model/model.go
index e2d54436..32ab255f 100644
--- a/database/model/model.go
+++ b/database/model/model.go
@@ -44,6 +44,15 @@ type Inbound struct {
Tag string `json:"tag" form:"tag" gorm:"unique"`
Sniffing string `json:"sniffing" form:"sniffing"`
}
+
+type OutboundTraffics struct {
+ Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"`
+ Tag string `json:"tag" form:"tag" gorm:"unique"`
+ Up int64 `json:"up" form:"up" gorm:"default:0"`
+ Down int64 `json:"down" form:"down" gorm:"default:0"`
+ Total int64 `json:"total" form:"total" gorm:"default:0"`
+}
+
type InboundClientIps struct {
Id int `json:"id" gorm:"primaryKey;autoIncrement"`
ClientEmail string `json:"clientEmail" form:"clientEmail" gorm:"unique"`
diff --git a/go.mod b/go.mod
index d46ed1fe..76d5f8eb 100644
--- a/go.mod
+++ b/go.mod
@@ -8,11 +8,11 @@ require (
github.com/gin-gonic/gin v1.9.1
github.com/goccy/go-json v0.10.2
github.com/mymmrac/telego v0.28.0
- github.com/nicksnyder/go-i18n/v2 v2.3.0
+ github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pelletier/go-toml/v2 v2.1.1
github.com/robfig/cron/v3 v3.0.1
- github.com/shirou/gopsutil/v3 v3.23.12
+ github.com/shirou/gopsutil/v3 v3.24.1
github.com/valyala/fasthttp v1.51.0
github.com/xtls/xray-core v1.8.7
go.uber.org/atomic v1.11.0
diff --git a/go.sum b/go.sum
index 8b6465d7..8e8b0db6 100644
--- a/go.sum
+++ b/go.sum
@@ -179,8 +179,8 @@ github.com/mymmrac/telego v0.28.0 h1:DNXaYISeZw1J9oB81vCNdskLow8gCRRUJxufqLuH3XE
github.com/mymmrac/telego v0.28.0/go.mod h1:oRperySNzJq8dRTl24+uBF1Uy7tlQGIjid/JQtHDsZg=
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.3.0 h1:2NPsCsNFCVd7i+Su0xYsBrIhS3bE2XMv5gNTft2O+PQ=
-github.com/nicksnyder/go-i18n/v2 v2.3.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
+github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
+github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
@@ -230,8 +230,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.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
-github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
+github.com/shirou/gopsutil/v3 v3.24.1 h1:R3t6ondCEvmARp3wxODhXMTLC/klMa87h2PHUw5m7QI=
+github.com/shirou/gopsutil/v3 v3.24.1/go.mod h1:UU7a2MSBQa+kW1uuDq8DeEBS8kmrnQwsv2b5O513rwU=
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=
@@ -369,7 +369,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
diff --git a/logger/logger.go b/logger/logger.go
index a1386b05..ca047cbc 100644
--- a/logger/logger.go
+++ b/logger/logger.go
@@ -65,6 +65,16 @@ func Infof(format string, args ...interface{}) {
addToBuffer("INFO", fmt.Sprintf(format, args...))
}
+func Notice(args ...interface{}) {
+ logger.Notice(args...)
+ addToBuffer("NOTICE", fmt.Sprint(args...))
+}
+
+func Noticef(format string, args ...interface{}) {
+ logger.Noticef(format, args...)
+ addToBuffer("NOTICE", fmt.Sprintf(format, args...))
+}
+
func Warning(args ...interface{}) {
logger.Warning(args...)
addToBuffer("WARNING", fmt.Sprint(args...))
diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js
index 42fb50a1..dc02d91b 100644
--- a/web/assets/js/model/outbound.js
+++ b/web/assets/js/model/outbound.js
@@ -861,13 +861,13 @@ Outbound.SocksSettings = class extends CommonClass {
}
static fromJson(json={}) {
- servers = json.servers;
+ let servers = json.servers;
if(ObjectUtil.isArrEmpty(servers)) servers=[{users: [{}]}];
return new Outbound.SocksSettings(
servers[0].address,
servers[0].port,
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
- ObjectUtil.isArrEmpty(servers[0].pass) ? '' : servers[0].users[0].pass,
+ ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].pass,
);
}
@@ -891,13 +891,13 @@ Outbound.HttpSettings = class extends CommonClass {
}
static fromJson(json={}) {
- servers = json.servers;
+ let servers = json.servers;
if(ObjectUtil.isArrEmpty(servers)) servers=[{users: [{}]}];
return new Outbound.HttpSettings(
servers[0].address,
servers[0].port,
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
- ObjectUtil.isArrEmpty(servers[0].pass) ? '' : servers[0].users[0].pass,
+ ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].pass,
);
}
@@ -914,8 +914,8 @@ Outbound.HttpSettings = class extends CommonClass {
Outbound.WireguardSettings = class extends CommonClass {
constructor(
- mtu=1420, secretKey=Wireguard.generateKeypair().privateKey,
- address=[''], workers=2, domainStrategy='ForceIPv6v4', reserved='',
+ mtu=1420, secretKey='',
+ address=[''], workers=2, domainStrategy='', reserved='',
peers=[new Outbound.WireguardSettings.Peer()], kernelMode=false) {
super();
this.mtu = mtu;
@@ -965,7 +965,7 @@ Outbound.WireguardSettings = class extends CommonClass {
};
Outbound.WireguardSettings.Peer = class extends CommonClass {
- constructor(publicKey=Wireguard.generateKeypair().publicKey, psk='', allowedIPs=['0.0.0.0/0','::/0'], endpoint='', keepAlive=0) {
+ constructor(publicKey='', psk='', allowedIPs=['0.0.0.0/0','::/0'], endpoint='', keepAlive=0) {
super();
this.publicKey = publicKey;
this.psk = psk;
diff --git a/web/controller/xray_setting.go b/web/controller/xray_setting.go
index 09e9115f..430cc77b 100644
--- a/web/controller/xray_setting.go
+++ b/web/controller/xray_setting.go
@@ -10,6 +10,7 @@ type XraySettingController struct {
XraySettingService service.XraySettingService
SettingService service.SettingService
InboundService service.InboundService
+ OutboundService service.OutboundService
XrayService service.XrayService
}
@@ -27,6 +28,7 @@ func (a *XraySettingController) initRouter(g *gin.RouterGroup) {
g.GET("/getXrayResult", a.getXrayResult)
g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig)
g.POST("/warp/:action", a.warp)
+ g.GET("/getOutboundsTraffic", a.getOutboundsTraffic)
}
func (a *XraySettingController) getXraySetting(c *gin.Context) {
@@ -84,3 +86,12 @@ func (a *XraySettingController) warp(c *gin.Context) {
jsonObj(c, resp, err)
}
+
+func (a *XraySettingController) getOutboundsTraffic(c *gin.Context) {
+ outboundsTraffic, err := a.OutboundService.GetOutboundsTraffic()
+ if err != nil {
+ jsonMsg(c, "Error getting traffics", err)
+ return
+ }
+ jsonObj(c, outboundsTraffic, nil)
+}
diff --git a/web/html/xui/form/stream/stream_ws.html b/web/html/xui/form/stream/stream_ws.html
index 1222124c..00b64167 100644
--- a/web/html/xui/form/stream/stream_ws.html
+++ b/web/html/xui/form/stream/stream_ws.html
@@ -7,7 +7,7 @@
<a-input v-model.trim="inbound.stream.ws.path"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
- <a-button size="small" @click="inbound.stream.ws.addHeader()">+</a-button>
+ <a-button size="small" @click="inbound.stream.ws.addHeader('host', '')">+</a-button>
</a-form-item>
<a-form-item :wrapper-col="{span:24}">
<a-input-group compact v-for="(header, index) in inbound.stream.ws.headers">
diff --git a/web/html/xui/warp_modal.html b/web/html/xui/warp_modal.html
index 4e6dd4fa..38310a69 100644
--- a/web/html/xui/warp_modal.html
+++ b/web/html/xui/warp_modal.html
@@ -140,7 +140,7 @@
mtu: 1420,
secretKey: warpModal.warpData.private_key,
address: Object.values(config.interface.addresses),
- domainStrategy: 'ForceIPv6v4',
+ domainStrategy: 'ForceIP',
peers: [{
publicKey: peer.public_key,
endpoint: peer.endpoint.host,
diff --git a/web/html/xui/xray.html b/web/html/xui/xray.html
index d6f0c0f8..267103cb 100644
--- a/web/html/xui/xray.html
+++ b/web/html/xui/xray.html
@@ -147,6 +147,40 @@
</template>
</a-col>
</a-row>
+ <a-row style="padding: 20px">
+ <a-col :lg="24" :xl="12">
+ <a-list-item-meta
+ title='{{ i18n "pages.xray.logLevel" }}'
+ description='{{ i18n "pages.xray.logLevelDesc" }}'/>
+ </a-col>
+ <a-col :lg="24" :xl="12">
+ <template>
+ <a-select
+ v-model="setLogLevel"
+ :dropdown-class-name="themeSwitcher.currentTheme"
+ style="width: 100%">
+ <a-select-option v-for="s in logLevel" :value="s">[[ s ]]</a-select-option>
+ </a-select>
+ </template>
+ </a-col>
+ </a-row>
+ <a-row style="padding: 20px">
+ <a-col :lg="24" :xl="12">
+ <a-list-item-meta
+ title='{{ i18n "pages.xray.accessLog" }}'
+ description='{{ i18n "pages.xray.accessLogDesc" }}'/>
+ </a-col>
+ <a-col :lg="24" :xl="12">
+ <template>
+ <a-select
+ v-model="setAccessLog"
+ :dropdown-class-name="themeSwitcher.currentTheme"
+ style="width: 100%">
+ <a-select-option v-for="s in access" :value="s">[[ s ]]</a-select-option>
+ </a-select>
+ </template>
+ </a-col>
+ </a-row>
</a-list-item>
</a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.blockConfigs"}}'>
@@ -341,8 +375,15 @@
</a-table>
</a-tab-pane>
<a-tab-pane key="tpl-3" tab='{{ i18n "pages.xray.Outbounds"}}' style="padding-top: 20px;" force-render="true">
- <a-button type="primary" icon="plus" @click="addOutbound()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
- <a-button type="primary" @click="showWarp()" style="margin-bottom: 10px;">WARP</a-button>
+ <a-row>
+ <a-col :xs="12" :sm="12" :lg="12">
+ <a-button type="primary" icon="plus" @click="addOutbound()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
+ <a-button type="primary" @click="showWarp()" style="margin-bottom: 10px;">WARP</a-button>
+ </a-col>
+ <a-col :xs="12" :sm="12" :lg="12" style="text-align: right;">
+ <a-icon type="sync" :spin="refreshing" @click="refreshOutboundTraffic()" style="margin: 0 5px;"/>
+ </a-col>
+ </a-row>
<a-table :columns="outboundColumns" bordered
:row-key="r => r.key"
:data-source="outboundData"
@@ -378,6 +419,9 @@
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='reality'" color="green">reality</a-tag>
</template>
</template>
+ <template slot="traffic" slot-scope="text, outbound, index">
+ <a-tag color="green">[[ findOutboundTraffic(outbound) ]]</a-tag>
+ </template>
</a-table>
</a-tab-pane>
<a-tab-pane key="tpl-4" tab='{{ i18n "pages.xray.outbound.reverse"}}' style="padding-top: 20px;" force-render="true">
@@ -463,6 +507,7 @@
{ title: '{{ i18n "pages.xray.outbound.tag"}}', dataIndex: 'tag', align: 'center', width: 50 },
{ title: '{{ i18n "protocol"}}', align: 'center', width: 50, scopedSlots: { customRender: 'protocol' } },
{ title: '{{ i18n "pages.xray.outbound.address"}}', align: 'center', width: 50, scopedSlots: { customRender: 'address' } },
+ { title: '{{ i18n "pages.inbounds.traffic" }}', align: 'center', width: 50, scopedSlots: { customRender: 'traffic' } },
];
const reverseColumns = [
@@ -483,7 +528,9 @@
oldXraySetting: '',
xraySetting: '',
inboundTags: [],
+ outboundsTraffic: [],
saveBtnDisable: true,
+ refreshing: false,
restartResult: '',
isMobile: window.innerWidth <= 768,
advSettings: 'xraySetting',
@@ -521,6 +568,8 @@
protocol: "freedom"
},
routingDomainStrategies: ["AsIs", "IPIfNonMatch", "IPOnDemand"],
+ logLevel: ["none" , "debug" , "info" , "warning", "error"],
+ access: ["none" , "./access.log" ],
settingsData: {
protocols: {
bittorrent: ["bittorrent"],
@@ -581,6 +630,12 @@
loading(spinning = true) {
this.spinning = spinning;
},
+ async getOutboundsTraffic() {
+ const msg = await HttpUtil.get("/panel/xray/getOutboundsTraffic");
+ if (msg.success) {
+ this.outboundsTraffic = msg.obj;
+ }
+ },
async getXraySetting() {
this.loading(true);
const msg = await HttpUtil.post("/panel/xray/");
@@ -759,6 +814,14 @@
}
return true;
},
+ findOutboundTraffic(o) {
+ for (const otraffic of this.outboundsTraffic) {
+ if (otraffic.tag == o.tag) {
+ return sizeFormat(otraffic.up) + ' / ' + sizeFormat(otraffic.down);
+ }
+ }
+ return sizeFormat(0) + ' / ' + sizeFormat(0);
+ },
findOutboundAddress(o) {
serverObj = null;
switch(o.protocol){
@@ -816,6 +879,22 @@
outbounds.splice(index,1);
this.outboundSettings = JSON.stringify(outbounds);
},
+ async refreshOutboundTraffic() {
+ if (!this.refreshing) {
+ this.refreshing = true;
+ await this.getOutboundsTraffic();
+
+ data = []
+ if (this.templateSettings != null) {
+ this.templateSettings.outbounds.forEach((o, index) => {
+ data.push({'key': index, ...o});
+ });
+ }
+
+ this.outboundData = data;
+ this.refreshing = false;
+ }
+ },
addReverse(){
reverseModal.show({
title: '{{ i18n "pages.xray.outbound.addReverse"}}',
@@ -949,6 +1028,7 @@
async mounted() {
await this.getXraySetting();
await this.getXrayResult();
+ await this.getOutboundsTraffic();
while (true) {
await PromiseUtil.sleep(800);
this.saveBtnDisable = this.oldXraySetting === this.xraySetting;
@@ -1065,6 +1145,28 @@
this.templateSettings = newTemplateSettings;
}
},
+ setLogLevel: {
+ get: function () {
+ if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.loglevel) return "warning";
+ return this.templateSettings.log.loglevel;
+ },
+ set: function (newValue) {
+ newTemplateSettings = this.templateSettings;
+ newTemplateSettings.log.loglevel = newValue;
+ this.templateSettings = newTemplateSettings;
+ }
+ },
+ setAccessLog: {
+ get: function () {
+ if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.access) return "none";
+ return this.templateSettings.log.access;
+ },
+ set: function (newValue) {
+ newTemplateSettings = this.templateSettings;
+ newTemplateSettings.log.access = newValue;
+ this.templateSettings = newTemplateSettings;
+ }
+ },
blockedIPs: {
get: function () {
return this.templateRuleGetter({ outboundTag: "blocked", property: "ip" });
diff --git a/web/job/check_client_ip_job.go b/web/job/check_client_ip_job.go
index b393e68d..905a8cc7 100644
--- a/web/job/check_client_ip_job.go
+++ b/web/job/check_client_ip_job.go
@@ -1,7 +1,9 @@
package job
import (
+ "bufio"
"encoding/json"
+ "io"
"log"
"os"
"os/exec"
@@ -92,24 +94,34 @@ func (j *CheckClientIpJob) checkFail2BanInstalled() {
func (j *CheckClientIpJob) processLogFile() {
accessLogPath := xray.GetAccessLogPath()
+
+ if accessLogPath == "none" {
+ logger.Warning("Access log is set to 'none' check your Xray Configs")
+ return
+ }
+
if accessLogPath == "" {
- logger.Warning("access.log doesn't exist in your config.json")
+ logger.Warning("Access log doesn't exist in your Xray Configs")
return
}
- data, err := os.ReadFile(accessLogPath)
- InboundClientIps := make(map[string][]string)
+ file, err := os.Open(accessLogPath)
j.checkError(err)
+ defer file.Close()
- lines := strings.Split(string(data), "\n")
- for _, line := range lines {
- ipRegx, _ := regexp.Compile(`[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+`)
+ InboundClientIps := make(map[string][]string)
+
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ line := scanner.Text()
+
+ ipRegx, _ := regexp.Compile(`(\d+\.\d+\.\d+\.\d+).* accepted`)
emailRegx, _ := regexp.Compile(`email:.+`)
- matchesIp := ipRegx.FindString(line)
- if len(matchesIp) > 0 {
- ip := string(matchesIp)
- if ip == "127.0.0.1" || ip == "1.1.1.1" {
+ matches := ipRegx.FindStringSubmatch(line)
+ if len(matches) > 1 {
+ ip := matches[1]
+ if ip == "127.0.0.1" || ip == "[::1]" {
continue
}
@@ -124,13 +136,14 @@ func (j *CheckClientIpJob) processLo