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/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/xray.html b/web/html/xui/xray.html
index d6f0c0f8..d00b73cc 100644
--- a/web/html/xui/xray.html
+++ b/web/html/xui/xray.html
@@ -341,8 +341,15 @@
- {{ i18n "pages.xray.outbound.addOutbound" }}
- WARP
+
+
+ {{ i18n "pages.xray.outbound.addOutbound" }}
+ WARP
+
+
+
+
+
reality
+
+ [[ findOutboundTraffic(outbound) ]]
+
@@ -463,6 +473,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 +494,9 @@
oldXraySetting: '',
xraySetting: '',
inboundTags: [],
+ outboundsTraffic: [],
saveBtnDisable: true,
+ refreshing: false,
restartResult: '',
isMobile: window.innerWidth <= 768,
advSettings: 'xraySetting',
@@ -581,6 +594,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 +778,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 +843,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 +992,7 @@
async mounted() {
await this.getXraySetting();
await this.getXrayResult();
+ await this.getOutboundsTraffic();
while (true) {
await PromiseUtil.sleep(800);
this.saveBtnDisable = this.oldXraySetting === this.xraySetting;
diff --git a/web/job/xray_traffic_job.go b/web/job/xray_traffic_job.go
index 158930a4..c0de4428 100644
--- a/web/job/xray_traffic_job.go
+++ b/web/job/xray_traffic_job.go
@@ -6,8 +6,9 @@ import (
)
type XrayTrafficJob struct {
- xrayService service.XrayService
- inboundService service.InboundService
+ xrayService service.XrayService
+ inboundService service.InboundService
+ outboundService service.OutboundService
}
func NewXrayTrafficJob() *XrayTrafficJob {
@@ -24,11 +25,15 @@ func (j *XrayTrafficJob) Run() {
logger.Warning("get xray traffic failed:", err)
return
}
- err, needRestart := j.inboundService.AddTraffic(traffics, clientTraffics)
+ err, needRestart0 := j.inboundService.AddTraffic(traffics, clientTraffics)
if err != nil {
- logger.Warning("add traffic failed:", err)
+ logger.Warning("add inbound traffic failed:", err)
}
- if needRestart {
+ err, needRestart1 := j.outboundService.AddTraffic(traffics, clientTraffics)
+ if err != nil {
+ logger.Warning("add outbound traffic failed:", err)
+ }
+ if needRestart0 || needRestart1 {
j.xrayService.SetToNeedRestart()
}
diff --git a/web/service/inbound.go b/web/service/inbound.go
index f3445101..291c0dee 100644
--- a/web/service/inbound.go
+++ b/web/service/inbound.go
@@ -682,7 +682,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
return needRestart, tx.Save(oldInbound).Error
}
-func (s *InboundService) AddTraffic(inboundTraffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) (error, bool) {
+func (s *InboundService) AddTraffic(traffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) (error, bool) {
var err error
db := database.GetDB()
tx := db.Begin()
@@ -694,7 +694,7 @@ func (s *InboundService) AddTraffic(inboundTraffics []*xray.Traffic, clientTraff
tx.Commit()
}
}()
- err = s.addInboundTraffic(tx, inboundTraffics)
+ err = s.addInboundTraffic(tx, traffics)
if err != nil {
return err, false
}
diff --git a/web/service/outbound.go b/web/service/outbound.go
new file mode 100644
index 00000000..dc0e0742
--- /dev/null
+++ b/web/service/outbound.go
@@ -0,0 +1,80 @@
+package service
+
+import (
+ "x-ui/database"
+ "x-ui/database/model"
+ "x-ui/logger"
+ "x-ui/xray"
+
+ "gorm.io/gorm"
+)
+
+type OutboundService struct {
+ xrayApi xray.XrayAPI
+}
+
+func (s *OutboundService) AddTraffic(traffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) (error, bool) {
+ var err error
+ db := database.GetDB()
+ tx := db.Begin()
+
+ defer func() {
+ if err != nil {
+ tx.Rollback()
+ } else {
+ tx.Commit()
+ }
+ }()
+
+ err = s.addOutboundTraffic(tx, traffics)
+ if err != nil {
+ return err, false
+ }
+
+ return nil, false
+}
+
+func (s *OutboundService) addOutboundTraffic(tx *gorm.DB, traffics []*xray.Traffic) error {
+ if len(traffics) == 0 {
+ return nil
+ }
+
+ var err error
+
+ for _, traffic := range traffics {
+ if traffic.IsOutbound {
+
+ var outbound model.OutboundTraffics
+
+ err = tx.Model(&model.OutboundTraffics{}).Where("tag = ?", traffic.Tag).
+ FirstOrCreate(&outbound).Error
+ if err != nil {
+ return err
+ }
+
+ outbound.Tag = traffic.Tag
+ outbound.Up = outbound.Up + traffic.Up
+ outbound.Down = outbound.Down + traffic.Down
+ outbound.Total = outbound.Up + outbound.Down
+
+ err = tx.Save(&outbound).Error
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (s *OutboundService) GetOutboundsTraffic() ([]*model.OutboundTraffics, error) {
+ db := database.GetDB()
+ var traffics []*model.OutboundTraffics
+
+ err := db.Model(model.OutboundTraffics{}).Find(&traffics).Error
+ if err != nil {
+ logger.Warning(err)
+ return nil, err
+ }
+
+ return traffics, nil
+}
diff --git a/xray/api.go b/xray/api.go
index 36b19875..1ce5afa1 100644
--- a/xray/api.go
+++ b/xray/api.go
@@ -213,6 +213,7 @@ func (x *XrayAPI) GetTraffic(reset bool) ([]*Traffic, []*ClientTraffic, error) {
continue
}
isInbound := matchs[1] == "inbound"
+ isOutbound := matchs[1] == "outbound"
tag := matchs[2]
isDown := matchs[3] == "downlink"
if tag == "api" {
@@ -221,8 +222,9 @@ func (x *XrayAPI) GetTraffic(reset bool) ([]*Traffic, []*ClientTraffic, error) {
traffic, ok := tagTrafficMap[tag]
if !ok {
traffic = &Traffic{
- IsInbound: isInbound,
- Tag: tag,
+ IsInbound: isInbound,
+ IsOutbound: isOutbound,
+ Tag: tag,
}
tagTrafficMap[tag] = traffic
traffics = append(traffics, traffic)
diff --git a/xray/traffic.go b/xray/traffic.go
index a1ef5186..7b907bae 100644
--- a/xray/traffic.go
+++ b/xray/traffic.go
@@ -1,8 +1,9 @@
package xray
type Traffic struct {
- IsInbound bool
- Tag string
- Up int64
- Down int64
+ IsInbound bool
+ IsOutbound bool
+ Tag string
+ Up int64
+ Down int64
}