feat(panel): reskin backend — node sortIndex, presence/speed, /api/system, credentials, subscription aggregation (Node+Go parity)
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user