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