Browse Source

Show outbound traffic in outbounds table (#1711)

* store outbound traffic in database

* show outbound traffic in outbounds table

* add refresh button
Saeid 1 year ago
parent
commit
6c0775b120

+ 5 - 0
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{})
 }

+ 9 - 0
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"`

+ 11 - 0
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)
+}

+ 46 - 2
web/html/xui/xray.html

@@ -341,8 +341,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 +385,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 +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;

+ 10 - 5
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()
 	}
 

+ 2 - 2
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
 	}

+ 80 - 0
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
+}

+ 4 - 2
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)

+ 5 - 4
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
 }