Files
renjue fe8ea784d0
CI / docker (push) Successful in 2m54s
feat: Prism HTTP/SOCKS5 代理网关及管理台
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 15:58:28 +08:00

174 lines
4.1 KiB
Go

package metrics
import (
"github.com/prism/proxy/internal/mihomoapi"
"github.com/prism/proxy/internal/store"
)
type TrafficItem struct {
Key string `json:"key"`
Label string `json:"label"`
Node string `json:"node,omitempty"`
NodeKey string `json:"node_key,omitempty"`
Upload int64 `json:"upload"`
Download int64 `json:"download"`
Total int64 `json:"total"`
Connections int `json:"connections"`
}
func AggregateConnections(conns []map[string]any, policyGroups map[string]bool) (domains, nodes []TrafficItem) {
domainMap := map[string]*TrafficItem{}
nodeMap := map[string]*TrafficItem{}
for _, conn := range conns {
host := connHost(conn)
nodeKey := connLeafNode(conn, policyGroups)
up := toInt64(conn["upload"])
down := toInt64(conn["download"])
if host != "" {
key := host + "\x00" + nodeKey
acc(domainMap, key, up, down)
item := domainMap[key]
item.Label = host
item.NodeKey = nodeKey
}
if nodeKey != "" {
acc(nodeMap, nodeKey, up, down)
}
}
return sortTrafficItems(domainMap), sortTrafficItems(nodeMap)
}
func acc(m map[string]*TrafficItem, key string, up, down int64) {
item, ok := m[key]
if !ok {
item = &TrafficItem{Key: key, Label: key}
m[key] = item
}
item.Upload += up
item.Download += down
item.Total += up + down
item.Connections++
}
func sortTrafficItems(m map[string]*TrafficItem) []TrafficItem {
out := make([]TrafficItem, 0, len(m))
for _, v := range m {
out = append(out, *v)
}
for i := 0; i < len(out); i++ {
for j := i + 1; j < len(out); j++ {
if out[j].Total > out[i].Total {
out[i], out[j] = out[j], out[i]
}
}
}
return out
}
func connHost(m map[string]any) string {
meta, _ := m["metadata"].(map[string]any)
if meta == nil {
return ""
}
if h, ok := meta["host"].(string); ok && h != "" {
return h
}
if h, ok := meta["sniffHost"].(string); ok && h != "" {
return h
}
if h, ok := meta["remoteDestination"].(string); ok && h != "" {
return h
}
if ip, ok := meta["destinationIP"].(string); ok && ip != "" {
port, _ := meta["destinationPort"].(string)
if port != "" {
return ip + ":" + port
}
return ip
}
return ""
}
func connLeafNode(m map[string]any, policyGroups map[string]bool) string {
return mihomoapi.ResolveLeafOutbound(mihomoapi.ChainStringsFromAny(m["chains"]), policyGroups)
}
func toInt64(v any) int64 {
switch n := v.(type) {
case float64:
return int64(n)
case int64:
return n
case int:
return int64(n)
default:
return 0
}
}
func ApplyTrafficLabels(items []TrafficItem, names map[string]string) []TrafficItem {
out := make([]TrafficItem, len(items))
for i, item := range items {
out[i] = item
if label, ok := names[item.Key]; ok && label != "" {
out[i].Label = label
}
}
return out
}
func ApplyDomainLabels(items []TrafficItem, names map[string]string) []TrafficItem {
out := make([]TrafficItem, len(items))
for i, item := range items {
out[i] = item
if label, ok := names[item.NodeKey]; ok && label != "" {
out[i].Node = label
} else if item.NodeKey != "" {
out[i].Node = item.NodeKey
}
}
return out
}
func TrafficAggToItems(aggs []store.TrafficAgg, names map[string]string) []TrafficItem {
out := make([]TrafficItem, 0, len(aggs))
for _, a := range aggs {
label := a.Key
if n, ok := names[a.Key]; ok && n != "" {
label = n
}
out = append(out, TrafficItem{
Key: a.Key,
Label: label,
Upload: a.Upload,
Download: a.Download,
Total: a.Upload + a.Download,
Connections: a.Connections,
})
}
return out
}
func TrafficHostNodeAggToItems(aggs []store.TrafficHostNodeAgg, names map[string]string) []TrafficItem {
out := make([]TrafficItem, 0, len(aggs))
for _, a := range aggs {
nodeLabel := a.NodeKey
if n, ok := names[a.NodeKey]; ok && n != "" {
nodeLabel = n
}
out = append(out, TrafficItem{
Key: a.Host + "\x00" + a.NodeKey,
Label: a.Host,
Node: nodeLabel,
NodeKey: a.NodeKey,
Upload: a.Upload,
Download: a.Download,
Total: a.Upload + a.Download,
Connections: a.Connections,
})
}
return out
}