fix(traffic): show live Speed for AmneziaWG and MTProto inbounds/clients

The Speed column showed "--" for AmneziaWG (and MTProto, which has the
identical gap) even while cumulative traffic totals were correct.
XrayTrafficJob drives live speed by querying xray-core's own stats API
and broadcasting the delta over websocket -- but AmneziaWG/MTProto never
run inside xray-core's own runtime inbounds, so they're invisible to
that API. Their own jobs already compute the same per-poll delta shape
(that's what keeps cumulative totals correct) but never broadcast it.

Reusing the existing "traffics"/"clientTraffics" broadcast would have
two real bugs: the frontend's existing scope/replace logic would let
each side clobber the other's speed on its next unrelated tick, and the
websocket hub's per-message-type throttle is keyed only by message type,
not caller -- since both sidecar jobs run on identical "@every 10s"
grids registered milliseconds apart, one would silently lose almost
every broadcast if both protocols were ever configured together.

Fixed with a small unthrottled broadcast path (both sidecar jobs are
already self-rate-limited by their own cron cadence) and protocol-
namespaced wire keys, tracked in their own frontend state and merged
into the existing inboundSpeed/clientSpeed only at read time -- so every
existing consumer needs zero changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kuzz007
2026-07-26 18:27:23 +03:00
parent 0436ac641f
commit 1aa81428b8
10 changed files with 348 additions and 4 deletions
+13
View File
@@ -45,6 +45,19 @@ func BroadcastTraffic(traffic any) {
}
}
// BroadcastSidecarTraffic broadcasts an AmneziaWG/MTProto traffic delta under
// the same "traffic" message type BroadcastTraffic uses, but bypasses the
// hub's per-type throttle (see Hub.BroadcastUnthrottled) so the two sidecar
// jobs' independent ~10s broadcasts can never starve each other. The payload
// carries protocol-namespaced keys (see internal/web/job/sidecar_traffic.go),
// so no new frontend message-type wiring is needed -- applyTrafficEvent
// already receives every "traffic" message.
func BroadcastSidecarTraffic(traffic any) {
if hub := GetHub(); hub != nil {
hub.BroadcastUnthrottled(MessageTypeTraffic, traffic)
}
}
// BroadcastClientStats broadcasts absolute per-client traffic counters. Small
// installs send the complete row set each cycle (payload key snapshot=true);
// above the traffic job's snapshot threshold only the rows active in the