package main // Presence & speed derivation — mirrors lib/presence.js. See that file // for the signal model (per-server user counters, per-node inbound // counters, AnyTLS observations; 3-minute window; server-level activity // attributed to the customer's nodes on that server). import "time" const PresenceWindowMs = 180000 const SpeedStaleMs = 150000 func parseISOMsOr0(value *string) int64 { if value == nil { return 0 } ms, ok := ParseISOMs(*value) if !ok { return 0 } return ms } func CustomerOnline(customer *Customer, nowMs int64) bool { ms := parseISOMsOr0(customer.LastOnlineAt) return ms > 0 && nowMs-ms <= PresenceWindowMs } func CustomerSpeedBps(customer *Customer, nowMs int64) (float64, float64) { var up, down float64 for _, slot := range customer.ServerUsage { if slot == nil || slot.SpeedAt == nil { continue } if nowMs-parseISOMsOr0(slot.SpeedAt) > SpeedStaleMs { continue } if slot.SpeedUpBps != nil { up += *slot.SpeedUpBps } if slot.SpeedDownBps != nil { down += *slot.SpeedDownBps } } return up, down } func NodeSpeedBps(node *Node, nowMs int64) (float64, float64) { if node.SpeedAt == nil || nowMs-parseISOMsOr0(node.SpeedAt) > SpeedStaleMs { return 0, 0 } var up, down float64 if node.SpeedUpBps != nil { up = *node.SpeedUpBps } if node.SpeedDownBps != nil { down = *node.SpeedDownBps } return up, down } // NodeOnlineCustomerIds mirrors nodeOnlineCustomerIds in lib/presence.js. func NodeOnlineCustomerIds(d *Data, node *Node, nowMs int64) []string { out := []string{} for _, nc := range d.NodeClients { if nc.NodeID != node.ID || nc.Status != "active" { continue } customer := findCustomer(d, nc.CustomerID) if customer == nil { continue } var ms int64 if slot := customer.ServerUsage["srv:"+node.ServerID]; slot != nil && slot.LastActiveAt != nil { ms = parseISOMsOr0(slot.LastActiveAt) } else { ms = parseISOMsOr0(customer.LastOnlineAt) } if ms > 0 && nowMs-ms <= PresenceWindowMs { out = append(out, customer.ID) } } return out } func CountOnlineCustomers(d *Data, now time.Time) int { count := 0 for _, customer := range d.Customers { if CustomerOnline(customer, now.UnixMilli()) { count++ } } return count }