mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-29 18:52:08 +03:00
fix(mtproto): zero sidecar quotas only for clients whose usage was reset
Invariant: the sidecar's quota counter for a client is zeroed exactly when the panel zeroes that client's usage. InboundService.ResetAllTraffics resets only inbound counters, yet it cleared every MTProto client's sidecar quota - on the master and, through its node propagation, on every node - handing out a fresh quota while the panel still counted the old usage. ResetAllClientTraffics for one inbound likewise cleared the quotas of MTProto clients on every other inbound. The inbound-level reset no longer touches sidecar quotas, and the per-inbound client reset zeroes only the clients it reset.
This commit is contained in:
@@ -126,22 +126,24 @@ func (s *ClientService) BulkResetTraffic(inboundSvc *InboundService, emails []st
|
||||
|
||||
func (s *ClientService) ResetAllClientTraffics(inboundSvc *InboundService, id int) error {
|
||||
var resetNodes []int
|
||||
var resetEmails []string
|
||||
err := submitTrafficWrite(func() error {
|
||||
var inner error
|
||||
resetNodes, inner = s.resetAllClientTrafficsLocked(id)
|
||||
resetEmails, resetNodes, inner = s.resetAllClientTrafficsLocked(id)
|
||||
return inner
|
||||
})
|
||||
if err == nil {
|
||||
inboundSvc.resetAllMtprotoQuotas()
|
||||
inboundSvc.resetMtprotoClientQuotas(resetEmails)
|
||||
inboundSvc.deliverNodeResetsNow(resetNodes)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *ClientService) resetAllClientTrafficsLocked(id int) ([]int, error) {
|
||||
func (s *ClientService) resetAllClientTrafficsLocked(id int) ([]string, []int, error) {
|
||||
db := database.GetDB()
|
||||
now := time.Now().Unix() * 1000
|
||||
var resetNodes []int
|
||||
var reset []string
|
||||
|
||||
if err := db.Transaction(func(tx *gorm.DB) error {
|
||||
// client_traffics.inbound_id is stale: it reflects the inbound the row was
|
||||
@@ -164,6 +166,7 @@ func (s *ClientService) resetAllClientTrafficsLocked(id int) ([]int, error) {
|
||||
if len(resetEmails) == 0 {
|
||||
return nil
|
||||
}
|
||||
reset = resetEmails
|
||||
|
||||
if err := adjustGroupBaselinesForRemovedTraffic(tx, resetEmails); err != nil {
|
||||
return err
|
||||
@@ -204,9 +207,9 @@ func (s *ClientService) resetAllClientTrafficsLocked(id int) ([]int, error) {
|
||||
|
||||
return result.Error
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
return resetNodes, nil
|
||||
return reset, resetNodes, nil
|
||||
}
|
||||
|
||||
func (s *ClientService) ResetAllTraffics() (bool, error) {
|
||||
|
||||
@@ -783,7 +783,6 @@ func (s *InboundService) ResetAllTraffics() error {
|
||||
})
|
||||
if err == nil {
|
||||
s.propagateResetAllTrafficsToNodes()
|
||||
s.resetAllMtprotoQuotas()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -78,6 +78,15 @@ func TestPanelResetsZeroSidecarQuota(t *testing.T) {
|
||||
t.Fatalf("sidecar quota resets %v, want [mtga]", got)
|
||||
}
|
||||
})
|
||||
t.Run("inbound clients", func(t *testing.T) {
|
||||
ib, logPath := startQuotaSidecar(t, 46207, model.Client{Enable: true})
|
||||
if err := (&ClientService{}).ResetAllClientTraffics(&InboundService{}, ib.Id); err != nil {
|
||||
t.Fatalf("ResetAllClientTraffics: %v", err)
|
||||
}
|
||||
if got := quotaResets(t, logPath); !slices.Equal(got, []string{"mtga", "mtgb"}) {
|
||||
t.Fatalf("sidecar quota resets %v, want [mtga mtgb]", got)
|
||||
}
|
||||
})
|
||||
t.Run("reset all", func(t *testing.T) {
|
||||
_, logPath := startQuotaSidecar(t, 46202, model.Client{Enable: true})
|
||||
if _, err := (&ClientService{}).ResetAllTraffics(); err != nil {
|
||||
@@ -98,3 +107,31 @@ func TestPanelResetsZeroSidecarQuota(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Resetting inbound counters leaves every client's usage in place, so the
|
||||
// sidecar's quota counters must stay too or clients get their quota again free.
|
||||
func TestInboundResetAllKeepsSidecarQuota(t *testing.T) {
|
||||
_, logPath := startQuotaSidecar(t, 46204, model.Client{Enable: true})
|
||||
if err := (&InboundService{}).ResetAllTraffics(); err != nil {
|
||||
t.Fatalf("ResetAllTraffics: %v", err)
|
||||
}
|
||||
if got := quotaResets(t, logPath); len(got) != 0 {
|
||||
t.Fatalf("inbound reset zeroed sidecar quotas %v, want none", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Resetting one inbound's clients zeroes only their sidecar quotas, not those
|
||||
// of MTProto clients whose usage the reset left in place.
|
||||
func TestInboundClientResetKeepsOtherSidecarQuotas(t *testing.T) {
|
||||
_, logPath := startQuotaSidecar(t, 46205, model.Client{Enable: true})
|
||||
other := mkInbound(t, 46206, model.VLESS, clientsSettings(t, []model.Client{{Email: "vless-only", ID: "11111111-1111-1111-1111-1111111111ab", Enable: true}}))
|
||||
if err := (&ClientService{}).SyncInbound(nil, other.Id, []model.Client{{Email: "vless-only", ID: "11111111-1111-1111-1111-1111111111ab", Enable: true}}); err != nil {
|
||||
t.Fatalf("SyncInbound: %v", err)
|
||||
}
|
||||
if err := (&ClientService{}).ResetAllClientTraffics(&InboundService{}, other.Id); err != nil {
|
||||
t.Fatalf("ResetAllClientTraffics: %v", err)
|
||||
}
|
||||
if got := quotaResets(t, logPath); len(got) != 0 {
|
||||
t.Fatalf("resetting another inbound zeroed sidecar quotas %v, want none", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user