mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-11 15:45:00 +03:00
fix(traffic): restore quota-disabled inbounds when their traffic is reset (#6962)
An inbound switched off by its own traffic cap came back on the periodic reset, but the admin per-inbound reset and Reset All Traffic only zeroed the counters. After that nothing could tell it from a hand-disabled inbound, so it stayed off with every client on it. Both admin resets now re-enable the inbounds their cap switched off, using the predicate the periodic job used, now in the service next to disableInvalidInbounds; expired and hand-disabled inbounds stay off.
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/web/runtime"
|
||||
)
|
||||
|
||||
// setupQuotaRestoreTest wires a temp DB and a local runtime that accepts
|
||||
// inbound changes, so a reset can be driven end to end.
|
||||
func setupQuotaRestoreTest(t *testing.T) {
|
||||
t.Helper()
|
||||
initTrafficTestDB(t)
|
||||
resetTrafficWriterForTest(t)
|
||||
|
||||
mgr := runtime.NewManager(runtime.LocalDeps{APIPort: func() int { return 0 }})
|
||||
mgr.SetLocalRuntimeOverride(&fakeNodeRuntime{})
|
||||
runtime.SetManager(mgr)
|
||||
t.Cleanup(func() { runtime.SetManager(nil) })
|
||||
}
|
||||
|
||||
// seedQuotaInbound creates an inbound in the state a traffic cap leaves it in:
|
||||
// switched off, with its counters past the cap.
|
||||
func seedQuotaInbound(t *testing.T, tag string, port int, total, up int64, expiryTime int64) *model.Inbound {
|
||||
t.Helper()
|
||||
ib := &model.Inbound{
|
||||
UserId: 1,
|
||||
Tag: tag,
|
||||
Enable: false,
|
||||
Listen: "0.0.0.0",
|
||||
Port: port,
|
||||
Protocol: model.VLESS,
|
||||
StreamSettings: `{"network":"tcp"}`,
|
||||
Settings: `{}`,
|
||||
Total: total,
|
||||
Up: up,
|
||||
ExpiryTime: expiryTime,
|
||||
}
|
||||
if err := database.GetDB().Create(ib).Error; err != nil {
|
||||
t.Fatalf("seed inbound %s: %v", tag, err)
|
||||
}
|
||||
return ib
|
||||
}
|
||||
|
||||
// A cap switches an inbound off and out of the core; zeroing the counters lifts
|
||||
// the cap, so the inbound must come back or every client on it stays down.
|
||||
func TestResetAllTrafficsRestoresQuotaDisabledInbound(t *testing.T) {
|
||||
setupQuotaRestoreTest(t)
|
||||
const gb = int64(1024 * 1024 * 1024)
|
||||
seedQuotaInbound(t, "capped", 45501, gb, gb, 0)
|
||||
|
||||
if _, err := (&InboundService{}).ResetAllTraffics(); err != nil {
|
||||
t.Fatalf("ResetAllTraffics: %v", err)
|
||||
}
|
||||
|
||||
after := loadInboundByTag(t, "capped")
|
||||
if !after.Enable {
|
||||
t.Fatal("an inbound whose traffic cap was lifted must be enabled again")
|
||||
}
|
||||
if after.Up != 0 || after.Down != 0 {
|
||||
t.Fatalf("counters = up %d / down %d, want both zero", after.Up, after.Down)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetInboundTrafficRestoresQuotaDisabledInbound(t *testing.T) {
|
||||
setupQuotaRestoreTest(t)
|
||||
const gb = int64(1024 * 1024 * 1024)
|
||||
ib := seedQuotaInbound(t, "capped", 45502, gb, gb, 0)
|
||||
|
||||
if _, err := (&InboundService{}).ResetInboundTraffic(ib.Id); err != nil {
|
||||
t.Fatalf("ResetInboundTraffic: %v", err)
|
||||
}
|
||||
|
||||
after := loadInboundByTag(t, "capped")
|
||||
if !after.Enable {
|
||||
t.Fatal("an inbound whose traffic cap was lifted must be enabled again")
|
||||
}
|
||||
if after.Up != 0 || after.Down != 0 {
|
||||
t.Fatalf("counters = up %d / down %d, want both zero", after.Up, after.Down)
|
||||
}
|
||||
}
|
||||
|
||||
// Only a cap may bring an inbound back. One an operator switched off by hand,
|
||||
// and one that expired, stay off — the reset has nothing to do with either.
|
||||
func TestResetAllTrafficsLeavesHandDisabledAndExpiredInboundsOff(t *testing.T) {
|
||||
setupQuotaRestoreTest(t)
|
||||
const gb = int64(1024 * 1024 * 1024)
|
||||
seedQuotaInbound(t, "hand-disabled", 45503, 0, 0, 0)
|
||||
seedQuotaInbound(t, "expired", 45504, gb, gb, time.Now().Add(-time.Hour).UnixMilli())
|
||||
|
||||
if _, err := (&InboundService{}).ResetAllTraffics(); err != nil {
|
||||
t.Fatalf("ResetAllTraffics: %v", err)
|
||||
}
|
||||
|
||||
for _, tag := range []string{"hand-disabled", "expired"} {
|
||||
if after := loadInboundByTag(t, tag); after.Enable {
|
||||
t.Fatalf("inbound %q must stay disabled", tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user