fix(traffic): keep enforcing limits when one inbound's settings are broken

Disabling depleted clients rewrote each affected inbound's settings and
aborted the whole lifecycle tick when one blob did not parse, so a node
storing a malformed blob on the master switched expiry and quota checks
off for every inbound. Unparseable node settings are now treated like a
snapshot that drops the hub's clients (the hub keeps its copy), and an
unparseable stored blob is left as it is while the client's rows are
still disabled; the rewrite also works one level deep like the others.
This commit is contained in:
MHSanaei
2026-10-31 10:43:40 +01:00
parent a203056949
commit f67111a08a
4 changed files with 85 additions and 18 deletions
@@ -213,3 +213,24 @@ func TestRepeatedSnapshotEntriesMergeTheInboundOnce(t *testing.T) {
t.Fatalf("remarks = %v, want the first entry merged once", remarks)
}
}
// Settings a node sends that do not parse must not replace the hub's copy: every later
// reader of the row (expiry, quota, renewals, subscriptions) would get a blob it cannot use.
func TestUnparseableSnapshotSettingsKeepTheHubCopy(t *testing.T) {
db := initTrafficTestDB(t)
svc := &InboundService{}
seedNodeRow(t, db, &model.Node{Id: 1, Name: "n1", Address: "127.0.0.1", Port: 2096, ApiToken: "tok", Enable: true})
createNodeInboundWithClient(t, db, 1, "keep", 41301, "kept@x")
good := `{"clients":[{"email":"kept@x","enable":true}]}`
if _, err := svc.setRemoteTrafficLocked(1, snapshotWithClients(t, "keep", good, xray.ClientTraffic{Email: "kept@x", Enable: true}), false, false); err != nil {
t.Fatalf("seed sync: %v", err)
}
if _, err := svc.setRemoteTrafficLocked(1, snapshotWithClients(t, "keep", `{"clients":[`, xray.ClientTraffic{Email: "kept@x", Enable: true}), false, false); err != nil {
t.Fatalf("broken sync: %v", err)
}
var ib model.Inbound
db.Where("tag = ?", "keep").First(&ib)
if _, err := ParseInboundSettingsClients(ib.Settings); err != nil {
t.Fatalf("stored settings %q no longer parse: %v", ib.Settings, err)
}
}