diff --git a/internal/web/service/client_traffic.go b/internal/web/service/client_traffic.go index 539be2b05..78a25cc09 100644 --- a/internal/web/service/client_traffic.go +++ b/internal/web/service/client_traffic.go @@ -200,15 +200,21 @@ func (s *ClientService) resetAllClientTrafficsLocked(id int) error { } func (s *ClientService) ResetAllTraffics() (bool, error) { - db := database.GetDB() - res := db.Model(&xray.ClientTraffic{}). - Where("1 = 1"). - Updates(map[string]any{"up": 0, "down": 0}) - if res.Error != nil { - return false, res.Error - } - if err := db.Where("1 = 1").Delete(&model.ClientGlobalTraffic{}).Error; err != nil { + var affected int64 + err := submitTrafficWrite(func() error { + return database.GetDB().Transaction(func(tx *gorm.DB) error { + res := tx.Model(&xray.ClientTraffic{}). + Where("1 = 1"). + Updates(map[string]any{"enable": true, "up": 0, "down": 0}) + if res.Error != nil { + return res.Error + } + affected = res.RowsAffected + return tx.Where("1 = 1").Delete(&model.ClientGlobalTraffic{}).Error + }) + }) + if err != nil { return false, err } - return res.RowsAffected > 0, nil + return affected > 0, nil } diff --git a/internal/web/service/traffic_commit_test.go b/internal/web/service/traffic_commit_test.go index 8e5b65820..a59824601 100644 --- a/internal/web/service/traffic_commit_test.go +++ b/internal/web/service/traffic_commit_test.go @@ -52,3 +52,27 @@ func TestAddTrafficCommitsDespiteDisableHelperError(t *testing.T) { t.Fatalf("traffic tick was rolled back by a best-effort disable-helper error: up=%d down=%d, want 500/700", reloaded.Up, reloaded.Down) } } + +// Reset All Client Traffic must re-enable clients that were auto-disabled for +// exceeding their quota, matching every other reset path; otherwise a reset +// leaves depleted clients cut with zero usage. +func TestResetAllTrafficsReenablesDepletedClients(t *testing.T) { + db := initTrafficTestDB(t) + svc := &ClientService{} + + if err := db.Create(&xray.ClientTraffic{InboundId: 1, Email: "spent@x", Enable: false, Up: 60, Down: 60, Total: 100}).Error; err != nil { + t.Fatalf("seed: %v", err) + } + + if _, err := svc.ResetAllTraffics(); err != nil { + t.Fatalf("ResetAllTraffics: %v", err) + } + + row := readTraffic(t, db, "spent@x") + if row.Up != 0 || row.Down != 0 { + t.Fatalf("usage not reset: up=%d down=%d", row.Up, row.Down) + } + if !row.Enable { + t.Fatal("a depleted client must be re-enabled after Reset All Client Traffic, matching every other reset path") + } +}