fix(sub): recover {{TRAFFIC_USED}} for clients with orphaned traffic rows

statsForClient resolved usage only through paths keyed by client_traffics.inbound_id (preloaded ClientStats + the statsByEmail index). That id is written once by AddClientStat and never updated, so an inbound delete+recreate orphans the row from every loaded inbound, both paths miss, and the zero-traffic placeholder makes {{TRAFFIC_USED}} read 0.00B for pre-existing clients while the sub-info header (AggregateTrafficByEmails, email-keyed) stays correct.

Add a last-resort lookup by the globally-unique email, cached into statsByEmail for the request. Closes #5567.
This commit is contained in:
MHSanaei
2026-06-25 18:18:47 +02:00
parent e4b881e58a
commit a4be5a0deb
3 changed files with 87 additions and 0 deletions
+7
View File
@@ -441,6 +441,13 @@ func (s *SubService) statsForClient(inbound *model.Inbound, client model.Client)
if stats, ok := s.statsByEmail[client.Email]; ok {
return stats
}
// Both in-memory paths key off client_traffics.inbound_id, which goes stale
// when an inbound is deleted and recreated, orphaning the row from every
// loaded inbound. Fall back to a direct lookup by the globally-unique email
// so usage still resolves for clients predating that recreation (#5567).
if stats, ok := s.statsByEmailFromDB(client.Email); ok {
return stats
}
return xray.ClientTraffic{
Enable: client.Enable,
ExpiryTime: client.ExpiryTime,