mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-17 15:47:14 +00:00
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:
@@ -288,6 +288,37 @@ func (h *Hub) Broadcast(messageType MessageType, payload any) {
|
||||
h.enqueue(data)
|
||||
}
|
||||
|
||||
// BroadcastUnthrottled behaves like Broadcast but skips the per-type rate
|
||||
// limit in shouldThrottle. Use for message types whose callers are already
|
||||
// self-limited by their own poll cadence (e.g. the AmneziaWG/MTProto sidecar
|
||||
// traffic jobs, both "@every 10s" in internal/web/web.go), so a same-tick
|
||||
// collision with another caller of the same MessageType (see
|
||||
// throttledMessageTypes) never silently drops one side. robfig/cron's
|
||||
// "@every" schedules are lastRun+interval grids anchored at AddJob time, not
|
||||
// wall-clock-aligned, so two same-cadence jobs registered milliseconds apart
|
||||
// stay in lockstep indefinitely -- with the throttled path, one of them
|
||||
// would lose almost every tick, forever, not just occasionally.
|
||||
func (h *Hub) BroadcastUnthrottled(messageType MessageType, payload any) {
|
||||
if h == nil || payload == nil || h.GetClientCount() == 0 {
|
||||
return
|
||||
}
|
||||
data, err := json.Marshal(Message{
|
||||
Type: messageType,
|
||||
Payload: payload,
|
||||
Time: time.Now().UnixMilli(),
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("WebSocket marshal failed:", err)
|
||||
return
|
||||
}
|
||||
if len(data) > maxMessageSize {
|
||||
logger.Debugf("WebSocket payload %d bytes exceeds limit, sending invalidate for %s", len(data), messageType)
|
||||
h.broadcastInvalidate(messageType)
|
||||
return
|
||||
}
|
||||
h.enqueue(data)
|
||||
}
|
||||
|
||||
// broadcastInvalidate queues a lightweight signal telling clients to re-fetch
|
||||
// the named data type via REST.
|
||||
func (h *Hub) broadcastInvalidate(originalType MessageType) {
|
||||
|
||||
Reference in New Issue
Block a user