inbound_mtproto.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package service
  2. import (
  3. "context"
  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/mtproto"
  8. )
  9. // DesiredMtprotoInstances derives the mtg sidecar configs this panel should be
  10. // running: one instance per enabled local mtproto inbound, serving only the
  11. // secrets of clients that are both enabled in the inbound settings and not
  12. // depletion-disabled in client_traffics. That is the same effective client set
  13. // buildInboundForLocalRuntime pushes on interactive edits, so the reconcile job
  14. // and the push paths agree on one fingerprint — a disagreement would surface
  15. // as a needless mtg restart, and a job that read only the raw settings would
  16. // keep serving depleted clients until an unrelated restart. Inbounds whose
  17. // every secret is filtered away are omitted so Reconcile stops their sidecar.
  18. func (s *InboundService) DesiredMtprotoInstances() ([]mtproto.Instance, error) {
  19. db := database.GetDB()
  20. var inbounds []*model.Inbound
  21. err := db.Model(model.Inbound{}).
  22. Where("protocol = ? AND enable = ? AND node_id IS NULL", model.MTProto, true).
  23. Find(&inbounds).Error
  24. if err != nil {
  25. return nil, err
  26. }
  27. if len(inbounds) == 0 {
  28. return nil, nil
  29. }
  30. instances := make([]mtproto.Instance, 0, len(inbounds))
  31. for _, ib := range inbounds {
  32. inst, ok := mtproto.InstanceFromInbound(ib)
  33. if !ok {
  34. continue
  35. }
  36. instances = append(instances, inst)
  37. }
  38. emails := make([]string, 0)
  39. for _, inst := range instances {
  40. for _, e := range inst.Secrets {
  41. emails = append(emails, e.Name)
  42. }
  43. }
  44. disabled, err := trafficDisabledEmails(db, emails)
  45. if err != nil {
  46. return nil, err
  47. }
  48. served := instances[:0]
  49. for _, inst := range instances {
  50. kept := make([]mtproto.SecretEntry, 0, len(inst.Secrets))
  51. for _, e := range inst.Secrets {
  52. if _, off := disabled[e.Name]; !off {
  53. kept = append(kept, e)
  54. }
  55. }
  56. inst.Secrets = kept
  57. if len(kept) > 0 {
  58. served = append(served, inst)
  59. }
  60. }
  61. return served, nil
  62. }
  63. // applyLocalMtproto pushes a single local mtproto inbound's current client set
  64. // to its mtg sidecar right after a client edit commits, so an add, removal,
  65. // re-key or enable-toggle takes effect immediately instead of waiting up to
  66. // 10s for the reconcile job. With a reload-capable mtg the change is applied in
  67. // place without dropping other clients; older binaries fall back to a restart
  68. // inside the manager. It re-reads the inbound so it sees the committed settings,
  69. // filters depleted clients exactly like the reconcile job, and is a no-op for
  70. // node-owned or non-mtproto inbounds. Failures are logged and swallowed: the
  71. // reconcile job is the backstop, and an xray restart cannot help the sidecar.
  72. func (s *InboundService) applyLocalMtproto(inboundId int) {
  73. inbound, err := s.GetInbound(inboundId)
  74. if err != nil || inbound == nil || inbound.Protocol != model.MTProto || inbound.NodeID != nil {
  75. return
  76. }
  77. rt, err := s.runtimeFor(inbound)
  78. if err != nil {
  79. return
  80. }
  81. payload := inbound
  82. if inbound.Enable {
  83. if built, bErr := s.buildInboundForLocalRuntime(database.GetDB(), inbound); bErr == nil {
  84. payload = built
  85. }
  86. }
  87. if err := rt.UpdateInbound(context.Background(), inbound, payload); err != nil {
  88. logger.Debug("mtproto: immediate client apply failed for inbound", inboundId, ":", err)
  89. }
  90. }
  91. func (s *InboundService) resetMtprotoClientQuota(email string) {
  92. mgr := mtproto.GetManager()
  93. if !mgr.HasRunning() {
  94. return
  95. }
  96. id, ok := s.localMtprotoInboundIdForEmail(email)
  97. if !ok {
  98. return
  99. }
  100. s.applyLocalMtproto(id)
  101. mgr.ResetQuota(email)
  102. }
  103. func (s *InboundService) resetAllMtprotoQuotas() {
  104. mgr := mtproto.GetManager()
  105. if !mgr.HasRunning() {
  106. return
  107. }
  108. desired, err := s.DesiredMtprotoInstances()
  109. if err != nil {
  110. return
  111. }
  112. mgr.Reconcile(desired)
  113. for _, inst := range desired {
  114. for _, sec := range inst.Secrets {
  115. mgr.ResetQuota(sec.Name)
  116. }
  117. }
  118. }
  119. func (s *InboundService) localMtprotoInboundIdForEmail(email string) (int, bool) {
  120. db := database.GetDB()
  121. var inbounds []*model.Inbound
  122. if err := db.Model(model.Inbound{}).
  123. Where("protocol = ? AND node_id IS NULL", model.MTProto).
  124. Find(&inbounds).Error; err != nil {
  125. return 0, false
  126. }
  127. for _, ib := range inbounds {
  128. inst, ok := mtproto.InstanceFromInbound(ib)
  129. if !ok {
  130. continue
  131. }
  132. for _, sec := range inst.Secrets {
  133. if sec.Name == email {
  134. return ib.Id, true
  135. }
  136. }
  137. }
  138. return 0, false
  139. }