diff options
| author | Chia <Chia@93.nz> | 2026-08-06 09:29:41 +1200 |
|---|---|---|
| committer | Chia <Chia@93.nz> | 2026-08-06 09:32:46 +1200 |
| commit | 41e322c53d7b4b796eb377d0df9c29ecd10ba431 (patch) | |
| tree | c730526150e55e39b822d5197e4a20318ecaa449 /internal/controlplane/snapshot.go | |
| parent | eadb2ffe85c43cf6fc741c9823cd28eedb4a844c (diff) | |
feat: complete commercial control plane, billing, auth, and model catalog
- add PostgreSQL control-plane persistence with Redis-degraded hot reload
- implement prepaid balance, usage ledger, Stripe top-up and reconciliation
- add registration, email verification, password reset, invitations and RBAC
- support TOTP, Passkey MFA, device sessions, quotas and rate limits
- add tenant billing profiles, audit logs and operational readiness checks
- build authenticated admin console, Quickstart, Playground and usage analytics
- add public model catalog with pricing, filtering and cost estimation
- support OpenAI Responses providers and provider health failover
- validate real upstream usage reporting and balance settlement
Diffstat (limited to '')
| -rw-r--r-- | internal/controlplane/snapshot.go | 35 |
1 files changed, 27 insertions, 8 deletions
diff --git a/internal/controlplane/snapshot.go b/internal/controlplane/snapshot.go index b9bddaf..75a3a80 100644 --- a/internal/controlplane/snapshot.go +++ b/internal/controlplane/snapshot.go @@ -74,7 +74,7 @@ func loadLimitPolicies(ctx context.Context, tx pgx.Tx) ([]domain.LimitPolicy, er func (s *Store) loadProviders(ctx context.Context, tx pgx.Tx) (map[string]domain.Provider, error) { rows, err := tx.Query(ctx, ` - SELECT id::text, name, protocol, base_url, api_key_ciphertext + SELECT id::text, slug, name, protocol, wire_api, base_url, api_key_ciphertext FROM providers WHERE enabled = TRUE ORDER BY name`) @@ -84,16 +84,16 @@ func (s *Store) loadProviders(ctx context.Context, tx pgx.Tx) (map[string]domain defer rows.Close() providers := make(map[string]domain.Provider) for rows.Next() { - var id, name, protocol, baseURL string + var id, slug, name, protocol, wireAPI, baseURL string var ciphertext []byte - if err := rows.Scan(&id, &name, &protocol, &baseURL, &ciphertext); err != nil { + if err := rows.Scan(&id, &slug, &name, &protocol, &wireAPI, &baseURL, &ciphertext); err != nil { return nil, fmt.Errorf("scan provider: %w", err) } apiKey, err := s.cipher.Decrypt(ciphertext) if err != nil { return nil, fmt.Errorf("decrypt provider %q credential: %w", name, err) } - providers[id] = domain.Provider{ID: id, Protocol: domain.Protocol(protocol), BaseURL: strings.TrimRight(baseURL, "/"), APIKey: apiKey} + providers[id] = domain.Provider{ID: id, Slug: slug, Protocol: domain.Protocol(protocol), WireAPI: wireAPI, BaseURL: strings.TrimRight(baseURL, "/"), APIKey: apiKey} } if err := rows.Err(); err != nil { return nil, fmt.Errorf("read providers: %w", err) @@ -245,11 +245,18 @@ func loadModels(ctx context.Context, tx pgx.Tx, providers map[string]domain.Prov func loadAPIKeys(ctx context.Context, tx pgx.Tx) ([]auth.HashedKeyRecord, error) { rows, err := tx.Query(ctx, ` - SELECT k.id::text, k.key_hash, k.tenant_id::text, k.project_id::text, k.scopes + SELECT k.id::text, k.key_hash, k.tenant_id::text, k.project_id::text, k.scopes, + k.monthly_spend_micros, k.expires_at, + COALESCE(( + SELECT jsonb_agg(m.public_id ORDER BY m.public_id) + FROM api_key_model_restrictions r + JOIN models m ON m.id = r.model_id + WHERE r.api_key_id = k.id + ), '[]'::jsonb) FROM api_keys k JOIN tenants t ON t.id = k.tenant_id AND t.status = 'active' JOIN projects p ON p.id = k.project_id AND p.status = 'active' - WHERE k.status = 'active'`) + WHERE k.status = 'active' AND (k.expires_at IS NULL OR k.expires_at > now())`) if err != nil { return nil, fmt.Errorf("query API keys: %w", err) } @@ -257,8 +264,11 @@ func loadAPIKeys(ctx context.Context, tx pgx.Tx) ([]auth.HashedKeyRecord, error) records := make([]auth.HashedKeyRecord, 0) for rows.Next() { var keyID, tenantID, projectID string - var hashBytes, scopesJSON []byte - if err := rows.Scan(&keyID, &hashBytes, &tenantID, &projectID, &scopesJSON); err != nil { + var hashBytes, scopesJSON, allowedModelsJSON []byte + var monthlySpendMicros int64 + var expiresAt *time.Time + if err := rows.Scan(&keyID, &hashBytes, &tenantID, &projectID, &scopesJSON, + &monthlySpendMicros, &expiresAt, &allowedModelsJSON); err != nil { return nil, fmt.Errorf("scan API key: %w", err) } if len(hashBytes) != sha256.Size { @@ -270,8 +280,17 @@ func loadAPIKeys(ctx context.Context, tx pgx.Tx) ([]auth.HashedKeyRecord, error) if err := json.Unmarshal(scopesJSON, &scopes); err != nil { return nil, fmt.Errorf("decode API key %s scopes: %w", keyID, err) } + var modelIDs []string + if err := json.Unmarshal(allowedModelsJSON, &modelIDs); err != nil { + return nil, fmt.Errorf("decode API key %s model restrictions: %w", keyID, err) + } + allowedModels := make(map[string]struct{}, len(modelIDs)) + for _, modelID := range modelIDs { + allowedModels[modelID] = struct{}{} + } records = append(records, auth.HashedKeyRecord{Hash: hash, Principal: domain.Principal{ KeyID: keyID, TenantID: tenantID, ProjectID: projectID, Scopes: scopes, + AllowedModels: allowedModels, MonthlySpendMicros: monthlySpendMicros, ExpiresAt: expiresAt, }}) } if err := rows.Err(); err != nil { |
