(ctx context.Context, userID, agentID, domain, direction string)
| 21 | } |
| 22 | |
| 23 | func (t *LiveUsageTracker) RecordAndCheck(ctx context.Context, userID, agentID, domain, direction string) (bool, error) { |
| 24 | // Metering gate: resolve the account class once and short-circuit |
| 25 | // non-standard accounts (internal/system/demo) BEFORE any write, so probe |
| 26 | // and internal traffic never lands in usage_events/usage_summaries and thus |
| 27 | // never counts against quota. On a lookup error we fail toward metering |
| 28 | // (GetAccountClass returns ClassStandard) — a real customer is never |
| 29 | // silently exempted from billing. |
| 30 | class, err := t.store.GetAccountClass(ctx, userID) |
| 31 | if err != nil { |
| 32 | log.Printf("[billing] account class lookup failed for user %s, metering as standard: %v", userID, err) |
| 33 | } |
| 34 | if !PolicyFor(class).Meter { |
| 35 | return true, nil |
| 36 | } |
| 37 | |
| 38 | // Record the event |
| 39 | event := &UsageEvent{ |
| 40 | UserID: userID, |
| 41 | AgentID: agentID, |
| 42 | Domain: domain, |
| 43 | Direction: direction, |
| 44 | } |
| 45 | if err := t.store.RecordUsageEvent(ctx, event); err != nil { |
| 46 | log.Printf("[billing] failed to record usage event: %v", err) |
| 47 | // Don't block on recording failure |
| 48 | } |
| 49 | |
| 50 | bucketDate := CurrentDate() |
| 51 | if err := t.store.IncrementUsageSummary(ctx, userID, bucketDate, direction); err != nil { |
| 52 | log.Printf("[billing] failed to increment usage summary: %v", err) |
| 53 | } |
| 54 | |
| 55 | return true, nil |
| 56 | } |
| 57 | |
| 58 | // NoopUsageTracker always allows everything. Used when billing is disabled. |
| 59 | type NoopUsageTracker struct{} |
nothing calls this directly
no test coverage detected