From 12d84c2a46597047ff133a1986f83fe06fe123a4 Mon Sep 17 00:00:00 2001 From: Hamed <62533356+younesvatan78@users.noreply.github.com> Date: Fri, 5 Jun 2026 01:24:29 +0330 Subject: [PATCH] fix(node-traffic): prevent stale node snapshot from re-enabling disabled client (#4917) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a remote node syncs traffic back to the panel, the UPDATE in setRemoteTrafficLocked wrote cs.Enable directly into client_traffics.enable. If a snapshot carrying enable=true arrived after the central panel had already set enable=false (due to the client reaching their traffic limit), it silently re-enabled the client — letting them consume 2-3x their allotted quota before the next disable cycle caught up. Fix: replace the unconditional SET enable = ? with a CASE expression that only allows a disable (0->0), never a re-enable (0->1). The central panel remains the sole authority for turning a client back on. Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Sanaei --- web/service/inbound.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/web/service/inbound.go b/web/service/inbound.go index 3e513bc3..09b2a8bc 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -1615,12 +1615,19 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi structuralChange = true } + // Only allow the node to disable a client (cs.Enable=false), never + // to re-enable one the panel has already disabled. A stale snapshot + // from the node arriving after a central disable would otherwise + // overwrite enable=false back to true, letting the client accumulate + // far more traffic than their limit before being disabled again. + enableExpr := "CASE WHEN ? = 0 THEN 0 ELSE enable END" if err := tx.Exec( fmt.Sprintf( `UPDATE client_traffics - SET up = up + ?, down = down + ?, enable = ?, total = ?, expiry_time = ?, reset = ?, + SET up = up + ?, down = down + ?, enable = %s, total = ?, expiry_time = ?, reset = ?, last_online = %s WHERE email = ?`, + enableExpr, database.GreatestExpr("last_online", "?"), ), deltaUp, deltaDown, cs.Enable, cs.Total, cs.ExpiryTime, cs.Reset,