95 lines
2.2 KiB
Go
95 lines
2.2 KiB
Go
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
|
|
}
|