diff --git a/web/controller/sub.go b/web/controller/sub.go index 5695f032..9a8dfc19 100644 --- a/web/controller/sub.go +++ b/web/controller/sub.go @@ -29,14 +29,18 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) { func (a *SUBController) subs(c *gin.Context) { subId := c.Param("subid") host := strings.Split(c.Request.Host, ":")[0] - subs, err := a.subService.GetSubs(subId, host) - if err != nil { + subs, header, err := a.subService.GetSubs(subId, host) + if err != nil || len(subs) == 0 { c.String(400, "Error!") } else { result := "" for _, sub := range subs { result += sub + "\n" } + + // Add subscription-userinfo + c.Writer.Header().Set("subscription-userinfo", header) + c.String(200, base64.StdEncoding.EncodeToString([]byte(result))) } } diff --git a/web/service/sub.go b/web/service/sub.go index 875a5b53..35ec4990 100644 --- a/web/service/sub.go +++ b/web/service/sub.go @@ -8,6 +8,7 @@ import ( "x-ui/database" "x-ui/database/model" "x-ui/logger" + "x-ui/xray" "github.com/goccy/go-json" "gorm.io/gorm" @@ -18,12 +19,15 @@ type SubService struct { inboundService InboundService } -func (s *SubService) GetSubs(subId string, host string) ([]string, error) { +func (s *SubService) GetSubs(subId string, host string) ([]string, string, error) { s.address = host var result []string + var header string + var traffic xray.ClientTraffic + var clientTraffics []xray.ClientTraffic inbounds, err := s.getInboundsBySubId(subId) if err != nil { - return nil, err + return nil, "", err } for _, inbound := range inbounds { clients, err := s.inboundService.getClients(inbound) @@ -37,22 +41,60 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, error) { if client.SubID == subId { link := s.getLink(inbound, client.Email) result = append(result, link) + clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email)) } } } - return result, nil + for index, clientTraffic := range clientTraffics { + if index == 0 { + traffic.Up = clientTraffic.Up + traffic.Down = clientTraffic.Down + traffic.Total = clientTraffic.Total + if clientTraffic.ExpiryTime > 0 { + traffic.ExpiryTime = clientTraffic.ExpiryTime + } + } else { + traffic.Up += clientTraffic.Up + traffic.Down += clientTraffic.Down + if traffic.Total == 0 || clientTraffic.Total == 0 { + traffic.Total = 0 + } else { + traffic.Total += clientTraffic.Total + } + if clientTraffic.ExpiryTime != traffic.ExpiryTime { + traffic.ExpiryTime = 0 + } + } + } + header = fmt.Sprintf("upload=%d;download=%d", traffic.Up, traffic.Down) + if traffic.Total > 0 { + header = header + fmt.Sprintf(";total=%d", traffic.Total) + } + if traffic.ExpiryTime > 0 { + header = header + fmt.Sprintf(";expire=%d", traffic.ExpiryTime) + } + return result, header, nil } func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) { db := database.GetDB() var inbounds []*model.Inbound - err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"subId": "%s"%%`, subId)).Find(&inbounds).Error + err := db.Model(model.Inbound{}).Preload("ClientStats").Where("settings like ?", fmt.Sprintf(`%%"subId": "%s"%%`, subId)).Find(&inbounds).Error if err != nil && err != gorm.ErrRecordNotFound { return nil, err } return inbounds, nil } +func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, email string) xray.ClientTraffic { + for _, traffic := range traffics { + if traffic.Email == email { + return traffic + } + } + return xray.ClientTraffic{} +} + func (s *SubService) getLink(inbound *model.Inbound, email string) string { switch inbound.Protocol { case "vmess":