feat(server): 按用户精确统计流量 — agent 改读 v2ray_api StatsService
节点 sing-box 编入 with_v2ray_api 后,每个 dp_uuid 有独立计数器
user>>>{dp_uuid}>>>traffic>>>uplink|downlink。agent 用 QueryStats(reset=true)
取窗口 delta,按用户精确上报(替代旧 clash 节点总量分摊:单用户准、多用户近似)。
- render.go: experimental.v2ray_api(loopback :19091)+ stats.users 列全部 dp_uuid
- v2rayapi/: vendor sing-box stats.pb.go(消息)+ 手写 client(用 v2ray 规范
ServiceName 路径,绕开生成代码的 experimental.v2rayapi.* 误名)
- usage_v2ray.go: V2RayUsageSource,uplink/downlink 天然用户视角(无需 swap)
- agent.UseV2RayUsage() 取代 UseClashUsage();clash_api 保留作本地调试
节点需部署带 with_v2ray_api 的 sing-box(已编好 linux/amd64 二进制)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -12,12 +12,15 @@ import (
|
||||
// derived password (DeriveHy2Password) — both from the same dp_uuid source.
|
||||
//
|
||||
// Only the opaque dp_uuid is ever written; no account identity touches the node.
|
||||
// clash_api endpoint(loopback only)供 agent 读取节点累计流量做用量统计。
|
||||
// v2ray_api 未编入该 sing-box 构建、clash /connections 又不暴露用户,故只能
|
||||
// 取节点总量(downloadTotal/uploadTotal),由 agent 按当前 dp_uuid 归属/分摊。
|
||||
//
|
||||
// 用量统计走 v2ray_api StatsService(loopback gRPC):节点 sing-box 编入
|
||||
// with_v2ray_api,stats.users 列出全部 dp_uuid → 每个用户独立的
|
||||
// user>>>{dp_uuid}>>>traffic>>>uplink|downlink 计数器,agent 按用户精确读取
|
||||
// (替代旧的 clash 节点总量分摊)。clash_api 仍保留作本地调试。
|
||||
const (
|
||||
clashAPIAddr = "127.0.0.1:19090"
|
||||
clashAPISecret = "pangolin-local-stats"
|
||||
v2rayAPIAddr = "127.0.0.1:19091"
|
||||
)
|
||||
|
||||
func renderSingboxConfig(creds []Cred, reality *agentv1.RealityInbound, hy2 *agentv1.Hy2Inbound, deriveKey string) ([]byte, error) {
|
||||
@@ -30,11 +33,35 @@ func renderSingboxConfig(creds []Cred, reality *agentv1.RealityInbound, hy2 *age
|
||||
"external_controller": clashAPIAddr,
|
||||
"secret": clashAPISecret,
|
||||
},
|
||||
"v2ray_api": map[string]any{
|
||||
"listen": v2rayAPIAddr,
|
||||
"stats": map[string]any{
|
||||
"enabled": true,
|
||||
"users": statsUsers(creds),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return json.MarshalIndent(cfg, "", " ")
|
||||
}
|
||||
|
||||
// statsUsers 收集所有去重 dp_uuid,供 v2ray_api stats.users 按用户开启流量计数器。
|
||||
func statsUsers(creds []Cred) []string {
|
||||
seen := make(map[string]struct{}, len(creds))
|
||||
users := make([]string, 0, len(creds))
|
||||
for _, c := range creds {
|
||||
if c.DpUUID == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[c.DpUUID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[c.DpUUID] = struct{}{}
|
||||
users = append(users, c.DpUUID)
|
||||
}
|
||||
return users
|
||||
}
|
||||
|
||||
func buildInbounds(creds []Cred, reality *agentv1.RealityInbound, hy2 *agentv1.Hy2Inbound, deriveKey string) []any {
|
||||
inbounds := make([]any, 0, 2)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user