package agentd import ( "context" "time" agentv1 "github.com/wangjia/pangolin/server/internal/pb/agentv1" ) // UsageSource yields per-dp_uuid aggregate counters for the elapsed window. It // MUST return only dp_uuids and byte/minute counts — never user ids, addresses or // DNS. The default returns nothing (no stats backend wired); production plugs in a // sing-box stats reader. type UsageSource interface { // Collect drains and returns usage accumulated since the previous call. Collect() []*agentv1.UsageEntry } // nopUsageSource reports nothing. type nopUsageSource struct{} func (nopUsageSource) Collect() []*agentv1.UsageEntry { return nil } // runUsage periodically aggregates and uploads usage. Reporting failures are // logged but do not tear down the session (usage is best-effort, at-least-once). func (a *Agent) runUsage(ctx context.Context, client agentv1.AgentServiceClient) error { ticker := time.NewTicker(a.cfg.UsageInterval) defer ticker.Stop() windowStart := a.clock().Unix() for { select { case <-ctx.Done(): return ctx.Err() case <-ticker.C: } entries := a.usage.Collect() if len(entries) == 0 { windowStart = a.clock().Unix() continue } now := a.clock().Unix() _, err := client.ReportUsage(ctx, &agentv1.UsageReport{ NodeUUID: a.NodeUUID(), WindowStartUnix: windowStart, WindowEndUnix: now, Entries: entries, }) if err != nil { // Connection-level errors should restart the session; surface them. return err } windowStart = now } }