From cec922d33531135d62015f72e934e1f200753bf3 Mon Sep 17 00:00:00 2001 From: IanShaw027 Date: Sat, 8 Aug 2026 12:56:40 +0800 Subject: [PATCH] fix(grok): clear golangci-lint findings on complete-integration branch Check Close/CloseNow errors, drop unused helpers and dead constants, lowercase ST1005 error strings, and stop discarding unwrap status as an unused assignment so CI golangci-lint passes. --- backend/internal/handler/grok_audio.go | 2 +- backend/internal/service/account.go | 20 -------------- ...t_scheduling_threshold_snapshot_cleanup.go | 18 ------------- ...eduling_threshold_snapshot_cleanup_test.go | 21 --------------- .../internal/service/account_test_service.go | 8 +++--- .../internal/service/grok_free_quota_gate.go | 7 +---- backend/internal/service/grok_media.go | 12 --------- .../internal/service/grok_observed_models.go | 2 +- backend/internal/service/grok_p2_test.go | 2 +- .../internal/service/grok_spending_reauth.go | 27 ------------------- .../internal/service/grok_upstream_failure.go | 12 ++++----- .../internal/service/grok_upstream_headers.go | 4 --- 12 files changed, 13 insertions(+), 122 deletions(-) delete mode 100644 backend/internal/service/account_scheduling_threshold_snapshot_cleanup.go delete mode 100644 backend/internal/service/account_scheduling_threshold_snapshot_cleanup_test.go diff --git a/backend/internal/handler/grok_audio.go b/backend/internal/handler/grok_audio.go index f1e002be1b..d721bcab3c 100644 --- a/backend/internal/handler/grok_audio.go +++ b/backend/internal/handler/grok_audio.go @@ -81,7 +81,7 @@ func (h *OpenAIGatewayHandler) GrokRealtime(c *gin.Context) { if err != nil { return } - defer conn.CloseNow() + defer func() { _ = conn.CloseNow() }() model := c.Query("model") if strings.TrimSpace(model) == "" { diff --git a/backend/internal/service/account.go b/backend/internal/service/account.go index 4253a87b2c..cb74aaf50f 100644 --- a/backend/internal/service/account.go +++ b/backend/internal/service/account.go @@ -1359,26 +1359,6 @@ func (a *Account) GetGrokBaseURLOr(defaultBaseURL string) string { return defaultBaseURL } -func isOfficialGrokAPIBaseURL(raw string) bool { - return isOfficialGrokBaseURL(raw, xai.DefaultBaseURL) -} - -func isOfficialGrokBaseURL(raw, expected string) bool { - parsed, err := url.Parse(strings.TrimSpace(raw)) - if err != nil || parsed == nil || parsed.Opaque != "" || parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" { - return false - } - want, err := url.Parse(expected) - if err != nil || !strings.EqualFold(parsed.Scheme, want.Scheme) || !strings.EqualFold(parsed.Hostname(), want.Hostname()) { - return false - } - if port := parsed.Port(); port != "" && port != "443" { - return false - } - path := strings.TrimRight(parsed.Path, "/") - return path == "" || path == strings.TrimRight(want.Path, "/") -} - // GetGrokMediaBaseURL selects the upstream used by Grok Imagine APIs. // The subscription CLI gateway enforces a small request-body limit that // rejects large Base64 media payloads, so OAuth media leaves for api.x.ai diff --git a/backend/internal/service/account_scheduling_threshold_snapshot_cleanup.go b/backend/internal/service/account_scheduling_threshold_snapshot_cleanup.go deleted file mode 100644 index 80c8fb2af1..0000000000 --- a/backend/internal/service/account_scheduling_threshold_snapshot_cleanup.go +++ /dev/null @@ -1,18 +0,0 @@ -package service - -import ( - "context" - "fmt" -) - -type accountSchedulingThresholdSnapshotCleaner interface { - ClearAccountSchedulingThresholdSnapshots(ctx context.Context, id int64) error -} - -func clearAccountSchedulingThresholdSnapshots(ctx context.Context, repo AccountRepository, id int64) error { - cleaner, ok := repo.(accountSchedulingThresholdSnapshotCleaner) - if !ok { - return fmt.Errorf("account repository does not support account scheduling threshold snapshot cleanup") - } - return cleaner.ClearAccountSchedulingThresholdSnapshots(ctx, id) -} diff --git a/backend/internal/service/account_scheduling_threshold_snapshot_cleanup_test.go b/backend/internal/service/account_scheduling_threshold_snapshot_cleanup_test.go deleted file mode 100644 index 59c7bdc4d2..0000000000 --- a/backend/internal/service/account_scheduling_threshold_snapshot_cleanup_test.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build unit - -package service - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" -) - -type missingThresholdSnapshotCleanerRepo struct { - AccountRepository -} - -func TestClearAccountSchedulingThresholdSnapshots_RequiresRepositorySupport(t *testing.T) { - err := clearAccountSchedulingThresholdSnapshots(context.Background(), missingThresholdSnapshotCleanerRepo{}, 1) - - require.Error(t, err) - require.Contains(t, err.Error(), "does not support account scheduling threshold snapshot cleanup") -} diff --git a/backend/internal/service/account_test_service.go b/backend/internal/service/account_test_service.go index ed06bd51f4..5a62598db2 100644 --- a/backend/internal/service/account_test_service.go +++ b/backend/internal/service/account_test_service.go @@ -903,23 +903,23 @@ func (s *AccountTestService) grokTestAccessToken(ctx context.Context, account *A switch account.Type { case AccountTypeOAuth: if s.grokTokenProvider == nil { - return "", fmt.Errorf("Grok token provider not configured") + return "", fmt.Errorf("grok token provider not configured") } // Manual tests skip production scheduling eligibility so paused/rate-limited // accounts can still be probed by admins (same as Codex/OpenAI tests). token, err := s.grokTokenProvider.GetAccessTokenForManualTest(ctx, account) if err != nil { - return "", fmt.Errorf("Failed to get Grok access token: %s", err.Error()) + return "", fmt.Errorf("failed to get grok access token: %s", err.Error()) } return token, nil case AccountTypeAPIKey: authToken := strings.TrimSpace(account.GetCredential("api_key")) if authToken == "" { - return "", fmt.Errorf("Grok API key is missing") + return "", fmt.Errorf("grok api key is missing") } return authToken, nil default: - return "", fmt.Errorf("Unsupported Grok account type: %s", account.Type) + return "", fmt.Errorf("unsupported grok account type: %s", account.Type) } } diff --git a/backend/internal/service/grok_free_quota_gate.go b/backend/internal/service/grok_free_quota_gate.go index c7f6bf8805..323776cd31 100644 --- a/backend/internal/service/grok_free_quota_gate.go +++ b/backend/internal/service/grok_free_quota_gate.go @@ -24,12 +24,7 @@ import ( // Soft-gate applies only to *explicit* free OAuth (subscription_tier/plan_type == // "free"). Media/cache free detection uses isKnownGrokFreeAccount instead. // Admin paths (QueryQuota / import probe) never call this filter. - -const ( - defaultGrokFreeQuotaTokenLimit int64 = 500_000 - defaultGrokFreeQuotaSoftGatePercent = 95 - defaultGrokFreeQuotaWindowHours = 24 -) +// Defaults live on config.Gateway.Grok (see config load defaults / tests). type GrokFreeQuotaPolicy struct { Enabled bool `json:"enabled"` diff --git a/backend/internal/service/grok_media.go b/backend/internal/service/grok_media.go index 196ea567ce..953d3db32a 100644 --- a/backend/internal/service/grok_media.go +++ b/backend/internal/service/grok_media.go @@ -525,18 +525,6 @@ func isOfficialGrokVideoStatusDone(statusBody []byte) bool { return strings.EqualFold(strings.TrimSpace(gjson.GetBytes(statusBody, "status").String()), "done") } -// extractGrokVideoStatusContentURL returns official video.url when present. -// Kept for content/proxy helpers that still accept rewritten proxy paths after rewrite. -func extractGrokVideoStatusContentURL(body []byte) string { - if len(body) == 0 || !gjson.ValidBytes(body) { - return "" - } - if u := strings.TrimSpace(gjson.GetBytes(body, "video.url").String()); u != "" { - return u - } - return "" -} - // ExtractGrokVideoBillingFromStatusBody builds usage units from an official done status. // Field priority (official docs): // - duration: video.duration (seconds) diff --git a/backend/internal/service/grok_observed_models.go b/backend/internal/service/grok_observed_models.go index 9d554b5674..480ba457bd 100644 --- a/backend/internal/service/grok_observed_models.go +++ b/backend/internal/service/grok_observed_models.go @@ -119,7 +119,7 @@ func (s *GrokQuotaService) syncGrokObservedModels(ctx context.Context, account * if err != nil { return err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) if err != nil { return err diff --git a/backend/internal/service/grok_p2_test.go b/backend/internal/service/grok_p2_test.go index b9bb4f5020..2be382d26e 100644 --- a/backend/internal/service/grok_p2_test.go +++ b/backend/internal/service/grok_p2_test.go @@ -70,7 +70,7 @@ func TestAccountGrokNeedsReauth(t *testing.T) { })) require.True(t, accountGrokNeedsReauth(&Account{ Status: StatusError, - ErrorMessage: grokSpendingLimitErrorMessage, + ErrorMessage: "Grok spending limit reached; reauthorize or wait for billing reset", })) } diff --git a/backend/internal/service/grok_spending_reauth.go b/backend/internal/service/grok_spending_reauth.go index f17de36e13..2b1f3a0973 100644 --- a/backend/internal/service/grok_spending_reauth.go +++ b/backend/internal/service/grok_spending_reauth.go @@ -2,7 +2,6 @@ package service import ( "context" - "log/slog" "strings" "time" ) @@ -10,11 +9,8 @@ import ( // Spending-limit is recoverable at the end of the observed billing period. // When no billing snapshot is available, use a short probe rather than // fabricating a 24h boundary from the error arrival time. -const grokSpendingLimitCooldown = 24 * time.Hour const grokSpendingLimitProbeCooldown = 10 * time.Minute -const grokSpendingLimitErrorMessage = "Grok spending limit reached; reauthorize or wait for billing reset" - func grokSpendingLimitResetAt(account *Account, now time.Time) time.Time { if account != nil { if billing, err := grokBillingSnapshotFromExtra(account.Extra); err == nil && billing != nil { @@ -28,29 +24,6 @@ func grokSpendingLimitResetAt(account *Account, now time.Time) time.Time { return now.Add(grokSpendingLimitProbeCooldown) } -// markGrokSpendingLimitReauth applies a long temp-unsched cool and durable -// SetError so ops sees reauth-required without wiping OAuth credentials. -func (s *OpenAIGatewayService) markGrokSpendingLimitReauth(ctx context.Context, account *Account) { - if s == nil || account == nil || account.IsPoolMode() { - return - } - s.tempUnscheduleGrok(ctx, account, grokSpendingLimitCooldown, "grok spending limit") - if s.accountRepo == nil { - return - } - stateCtx, cancel := openAIAccountStateContext(ctx) - defer cancel() - if err := s.accountRepo.SetError(stateCtx, account.ID, grokSpendingLimitErrorMessage); err != nil { - slog.Warn("grok_spending_limit_set_error_failed", "account_id", account.ID, "error", err) - } - // Soft flag in extra for UI / usage fetcher without requiring status poll. - _ = s.accountRepo.UpdateExtra(stateCtx, account.ID, map[string]any{ - "grok_needs_reauth": true, - "grok_needs_reauth_reason": "spending_limit", - "grok_needs_reauth_at": time.Now().UTC().Format(time.RFC3339), - }) -} - // clearGrokNeedsReauthExtra drops the soft reauth flag after successful refresh // or reauth. Best-effort; never fails the request path. func clearGrokNeedsReauthExtra(ctx context.Context, repo AccountRepository, accountID int64) { diff --git a/backend/internal/service/grok_upstream_failure.go b/backend/internal/service/grok_upstream_failure.go index c76c24f99b..1348d068c5 100644 --- a/backend/internal/service/grok_upstream_failure.go +++ b/backend/internal/service/grok_upstream_failure.go @@ -50,9 +50,8 @@ type GrokUpstreamFailureDecision struct { } var ( - reGrokTokenPair = regexp.MustCompile(`(?i)tokens?\s*(?:\(actual\s*/\s*limit\))?\s*[:=]?\s*(\d+)\s*/\s*(\d+)`) - reGrokModelFor = regexp.MustCompile(`(?i)(?:for\s+model|model|模型)\s*[::]?\s*([a-z0-9][a-z0-9._-]{2,80})`) - reGrokResetsWindow = regexp.MustCompile(`(?i)resets?\s+over\s+a\s+rolling\s+(\d+)\s*-\s*hour`) + reGrokTokenPair = regexp.MustCompile(`(?i)tokens?\s*(?:\(actual\s*/\s*limit\))?\s*[:=]?\s*(\d+)\s*/\s*(\d+)`) + reGrokModelFor = regexp.MustCompile(`(?i)(?:for\s+model|model|模型)\s*[::]?\s*([a-z0-9][a-z0-9._-]{2,80})`) ) // classifyGrokUpstreamFailure decides cooldown/failover from status + body. @@ -166,11 +165,10 @@ func classifyGrokUpstreamFailure(statusCode int, responseBody []byte, requestedM } func grokUpstreamErrorCorpus(statusCode int, responseBody []byte) (text, code, low string) { + _ = statusCode // classifier already has the transport status; corpus is body-only raw := strings.TrimSpace(string(responseBody)) - if unwrappedStatus, unwrappedBody, ok := unwrapGrokUpstreamErrorText(raw); ok { - if statusCode <= 0 { - statusCode = unwrappedStatus - } + // Strip "upstream status NNN: ..." prefixes so free-usage / quota language is visible. + if _, unwrappedBody, ok := unwrapGrokUpstreamErrorText(raw); ok { raw = unwrappedBody } text = raw diff --git a/backend/internal/service/grok_upstream_headers.go b/backend/internal/service/grok_upstream_headers.go index ad0ba21a2c..cdfb54bcbf 100644 --- a/backend/internal/service/grok_upstream_headers.go +++ b/backend/internal/service/grok_upstream_headers.go @@ -14,10 +14,6 @@ import ( // tests. Current requests use the pinned default UA from this package. const grokUpstreamUserAgent = "sub2api-grok/1.0" -// defaultBrowserLikeUpstreamUserAgent remains available for non-Grok OpenAI-like -// fingerprint templates that historically shared this constant. -const defaultBrowserLikeUpstreamUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" - // Fixed CLI identity aliases — single source of truth is internal/pkg/xai. const ( grokClientVersionHeader = xai.CLIStableVersion