1
0

client_traffic.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. package service
  2. import (
  3. "time"
  4. "github.com/mhsanaei/3x-ui/v3/internal/database"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  6. "github.com/mhsanaei/3x-ui/v3/internal/logger"
  7. "github.com/mhsanaei/3x-ui/v3/internal/util/common"
  8. "github.com/mhsanaei/3x-ui/v3/internal/xray"
  9. "gorm.io/gorm"
  10. )
  11. func (s *ClientService) ResetTrafficByEmail(inboundSvc *InboundService, email string) (bool, error) {
  12. if email == "" {
  13. return false, common.NewError("client email is required")
  14. }
  15. rec, err := s.GetRecordByEmail(nil, email)
  16. if err != nil {
  17. return false, err
  18. }
  19. inboundIds, err := s.GetInboundIdsForRecord(rec.Id)
  20. if err != nil {
  21. return false, err
  22. }
  23. needRestart := false
  24. if !rec.Enable {
  25. updated := rec.ToClient()
  26. updated.Enable = true
  27. nr, uErr := s.Update(inboundSvc, rec.Id, *updated, rec.LimitHwid)
  28. if uErr != nil {
  29. logger.Warning("Failed to auto-enable client during traffic reset:", uErr)
  30. }
  31. if nr {
  32. needRestart = true
  33. }
  34. }
  35. if len(inboundIds) == 0 {
  36. if rErr := inboundSvc.ResetClientTrafficByEmail(email); rErr != nil {
  37. return false, rErr
  38. }
  39. return needRestart, nil
  40. }
  41. applies := make([]inboundApply, 0, len(inboundIds))
  42. for _, ibId := range inboundIds {
  43. applies = append(applies, inboundApply{id: ibId, run: func() (bool, error) {
  44. return inboundSvc.ResetClientTraffic(ibId, email)
  45. }})
  46. }
  47. nr, applyErr := fanoutInboundApplies(applies)
  48. return needRestart || nr, applyErr
  49. }
  50. func (s *ClientService) BulkResetTraffic(inboundSvc *InboundService, emails []string) (int, error) {
  51. if len(emails) == 0 {
  52. return 0, nil
  53. }
  54. cleanEmails := trimmedUniqueEmails(emails)
  55. if len(cleanEmails) == 0 {
  56. return 0, nil
  57. }
  58. recordsByEmail, err := clientRecordsByEmail(nil, cleanEmails)
  59. if err != nil {
  60. return 0, err
  61. }
  62. for _, e := range cleanEmails {
  63. rec := recordsByEmail[e]
  64. if rec == nil || rec.Enable {
  65. continue
  66. }
  67. updated := rec.ToClient()
  68. updated.Enable = true
  69. if _, uErr := s.Update(inboundSvc, rec.Id, *updated, rec.LimitHwid); uErr != nil {
  70. logger.Warning("Failed to auto-enable client during bulk traffic reset:", uErr)
  71. }
  72. }
  73. affected := 0
  74. err = submitTrafficWrite(func() error {
  75. db := database.GetDB()
  76. return db.Transaction(func(tx *gorm.DB) error {
  77. if err := adjustGroupBaselinesForRemovedTraffic(tx, cleanEmails); err != nil {
  78. return err
  79. }
  80. for _, batch := range chunkStrings(cleanEmails, sqlInChunk) {
  81. res := tx.Model(xray.ClientTraffic{}).
  82. Where("email IN ?", batch).
  83. Updates(map[string]any{"enable": true, "up": 0, "down": 0})
  84. if res.Error != nil {
  85. return res.Error
  86. }
  87. affected += int(res.RowsAffected)
  88. }
  89. if err := clearGlobalTraffic(tx, cleanEmails...); err != nil {
  90. return err
  91. }
  92. for _, batch := range chunkStrings(cleanEmails, sqlInChunk) {
  93. if err := tx.Where("email IN ?", batch).Delete(&model.NodeClientTraffic{}).Error; err != nil {
  94. return err
  95. }
  96. }
  97. return nil
  98. })
  99. })
  100. if err != nil {
  101. return 0, err
  102. }
  103. return affected, nil
  104. }
  105. func (s *ClientService) ResetAllClientTraffics(inboundSvc *InboundService, id int) error {
  106. err := submitTrafficWrite(func() error {
  107. return s.resetAllClientTrafficsLocked(id)
  108. })
  109. if err == nil {
  110. inboundSvc.resetAllMtprotoQuotas()
  111. }
  112. return err
  113. }
  114. func (s *ClientService) resetAllClientTrafficsLocked(id int) error {
  115. db := database.GetDB()
  116. now := time.Now().Unix() * 1000
  117. if err := db.Transaction(func(tx *gorm.DB) error {
  118. // client_traffics.inbound_id is stale: it reflects the inbound the row was
  119. // first inserted under and is never refreshed. Use the client_inbounds join
  120. // as the authoritative source for which emails belong to a given inbound.
  121. var resetEmails []string
  122. if id == -1 {
  123. if err := tx.Model(xray.ClientTraffic{}).Pluck("email", &resetEmails).Error; err != nil {
  124. return err
  125. }
  126. } else {
  127. if err := tx.Table("client_inbounds ci").
  128. Select("c.email").
  129. Joins("JOIN clients c ON c.id = ci.client_id").
  130. Where("ci.inbound_id = ?", id).
  131. Pluck("c.email", &resetEmails).Error; err != nil {
  132. return err
  133. }
  134. }
  135. if len(resetEmails) == 0 {
  136. return nil
  137. }
  138. if err := adjustGroupBaselinesForRemovedTraffic(tx, resetEmails); err != nil {
  139. return err
  140. }
  141. result := tx.Model(xray.ClientTraffic{}).
  142. Where("email IN ?", resetEmails).
  143. Updates(map[string]any{"enable": true, "up": 0, "down": 0})
  144. if result.Error != nil {
  145. return result.Error
  146. }
  147. if err := clearGlobalTraffic(tx, resetEmails...); err != nil {
  148. return err
  149. }
  150. for _, batch := range chunkStrings(resetEmails, sqlInChunk) {
  151. if err := tx.Where("email IN ?", batch).Delete(&model.NodeClientTraffic{}).Error; err != nil {
  152. return err
  153. }
  154. }
  155. inboundWhereText := "id "
  156. if id == -1 {
  157. inboundWhereText += " > ?"
  158. } else {
  159. inboundWhereText += " = ?"
  160. }
  161. result = tx.Model(model.Inbound{}).
  162. Where(inboundWhereText, id).
  163. Update("last_traffic_reset_time", now)
  164. return result.Error
  165. }); err != nil {
  166. return err
  167. }
  168. return nil
  169. }
  170. func (s *ClientService) ResetAllTraffics() (bool, error) {
  171. var affected int64
  172. err := submitTrafficWrite(func() error {
  173. return database.GetDB().Transaction(func(tx *gorm.DB) error {
  174. res := tx.Model(&xray.ClientTraffic{}).
  175. Where("1 = 1").
  176. Updates(map[string]any{"enable": true, "up": 0, "down": 0})
  177. if res.Error != nil {
  178. return res.Error
  179. }
  180. affected = res.RowsAffected
  181. if err := tx.Where("1 = 1").Delete(&model.ClientGlobalTraffic{}).Error; err != nil {
  182. return err
  183. }
  184. return tx.Where("1 = 1").Delete(&model.NodeClientTraffic{}).Error
  185. })
  186. })
  187. if err != nil {
  188. return false, err
  189. }
  190. return affected > 0, nil
  191. }