mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-12 15:46:06 +00:00
fix(sub): {{INBOUND}} = inbound remark, fix {{TRAFFIC_LEFT}} across inbounds (#5443)
Issue 1: the host endpoint remark no longer substitutes the inbound remark
as the config name. {{INBOUND}} always resolves to the inbound's own remark
and {{HOST}} to the host remark, so both can be shown side by side instead
of the host name appearing twice. configName() drops hostRemark entirely;
token help text updated in all locales.
Issue 2: client_traffics.email is globally unique, so a client shared across
several inbounds of one subscription has a single traffic row owned by one
inbound. statsForClient only searched the current inbound's preloaded
ClientStats, missing on every other inbound's link and falling back to
Up=Down=0 -- so {{TRAFFIC_LEFT}} printed the full quota. Build a per-request
email->stats map from all the subscription's inbounds (no extra queries) and
fall back to it.
This commit is contained in:
@@ -47,6 +47,12 @@ type SubService struct {
|
||||
// inbound whose NodeID is set. Keeps the per-link host derivation
|
||||
// O(1) instead of O(N) DB hits.
|
||||
nodesByID map[int]*model.Node
|
||||
// statsByEmail maps a client email to its traffic row across ALL inbounds
|
||||
// loaded for the request. client_traffics.email is globally unique, so this
|
||||
// lets statsForClient resolve usage for a client even on an inbound that
|
||||
// doesn't own its row (multi-inbound subscriptions). Filled in
|
||||
// getInboundsBySubId; reset per request in PrepareForRequest.
|
||||
statsByEmail map[string]xray.ClientTraffic
|
||||
}
|
||||
|
||||
// NewSubService creates a new subscription service with the given configuration.
|
||||
@@ -78,6 +84,7 @@ func (s *SubService) PrepareForRequest(host string) {
|
||||
}
|
||||
s.address = host
|
||||
s.usageShown = map[string]bool{}
|
||||
s.statsByEmail = map[string]xray.ClientTraffic{}
|
||||
s.loadNodes()
|
||||
s.loadRemarkSettings()
|
||||
}
|
||||
@@ -335,9 +342,24 @@ func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.indexStatsByEmail(inbounds)
|
||||
return inbounds, nil
|
||||
}
|
||||
|
||||
// indexStatsByEmail records every loaded inbound's client traffic rows keyed by
|
||||
// email so statsForClient can resolve a client's usage even on an inbound that
|
||||
// doesn't own its (globally unique) client_traffics row. See statsByEmail.
|
||||
func (s *SubService) indexStatsByEmail(inbounds []*model.Inbound) {
|
||||
if s.statsByEmail == nil {
|
||||
s.statsByEmail = map[string]xray.ClientTraffic{}
|
||||
}
|
||||
for _, inbound := range inbounds {
|
||||
for _, st := range inbound.ClientStats {
|
||||
s.statsByEmail[st.Email] = st
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// projectThroughFallbackMaster mutates the inbound in place so its
|
||||
// Listen/Port/StreamSettings reflect the externally reachable master
|
||||
// when applicable. Covers both fallback mechanisms:
|
||||
|
||||
Reference in New Issue
Block a user