The 403 detection PR changed the 401 handler condition from
`account.Type == AccountTypeOAuth` to
`account.Type == AccountTypeOAuth && account.Platform == PlatformOpenAI`,
which accidentally excluded Gemini OAuth from the temp-unschedulable path.
Fix: use `!= PlatformAntigravity` instead, preserving Gemini behavior
while correctly excluding Antigravity (whose 401 is handled by
applyErrorPolicy's temp_unschedulable_rules).
Update tests to reflect Antigravity's new 401 semantics:
- HandleUpstreamError: Antigravity OAuth 401 now uses SetError
- CheckErrorPolicy: Antigravity 401 second hit stays TempUnscheduled
- DB fallback: split into Gemini (escalates) and Antigravity (stays temp)
Add group_id and validity_days fields to CreateAndRedeemCodeRequest,
enabling subscription-type redemption codes to be created and redeemed
in a single API call.
- Type defaults to "balance" when omitted for backward compatibility
- Subscription type requires group_id (non-nil) and validity_days (>0)
- Existing balance/concurrency callers are unaffected
Add group_id and validity_days fields to CreateAndRedeemCodeRequest for
subscription redemption. Type defaults to "balance" for backward
compatibility with existing callers that don't send the type field.
Backend:
- Detect and classify 403 responses into three types:
validation (account needs Google verification),
violation (terms of service / banned),
forbidden (generic 403)
- Extract verification/appeal URLs from 403 response body
(structured JSON parsing with regex fallback)
- Add needs_verify, is_banned, needs_reauth, error_code fields
to UsageInfo (omitempty for zero impact on other platforms)
- Handle 403 in request path: classify and permanently set account error
- Save validation_url in error_message for degraded path recovery
- Enrich usage with account error on both success and degraded paths
- Add singleflight dedup for usage requests with independent context
- Differentiate cache TTL: success/403 → 3min, errors → 1min
- Return degraded UsageInfo instead of HTTP 500 on quota fetch errors
Frontend:
- Display forbidden status badges with color coding (red for banned,
amber for needs verification, gray for generic)
- Show clickable verification/appeal URL links
- Display needs_reauth and degraded error states in usage cell
- Add Antigravity tier label badge next to platform type
Tests:
- Comprehensive unit tests for classifyForbiddenType (7 cases)
- Unit tests for extractValidationURL (8 cases including unicode escapes)
- Integration test for FetchQuota forbidden path
Add a dedicated modal in group management for viewing, adding, editing,
and deleting per-user rate multipliers within a group.
Backend:
- GET /admin/groups/:id/rate-multipliers - list entries with user details
- PUT /admin/groups/:id/rate-multipliers - batch sync (full replace)
- DELETE /admin/groups/:id/rate-multipliers - clear all entries
- Repository: GetByGroupID, SyncGroupRateMultipliers methods on
user_group_rate_multipliers table (same table as user-side rates)
Frontend:
- New GroupRateMultipliersModal component with:
- User search and add with email autocomplete
- Editable rate column with local edit mode (cancel/save)
- Batch adjust: multiply all rates by a factor
- Clear all (local operation, requires save to persist)
- Pagination (10/20/50 per page)
- Platform icon with brand colors in group info bar
- Unsaved changes indicator with revert option
- Unit tests for all three backend endpoints
- Always show close button at bottom
- Add "revert changes" link when dirty
- Save now closes dialog on success
- Button styles match group edit dialog
chore: bump version to 0.1.96.8
Recovers features lost during 0.1.91->0.1.95 merge (0.1.92-0.1.94
customizations were skipped because main branch was not updated).
Recovered features:
- Antigravity 403 forbidden/validation/ban status detection and display
- Subscription tier badge in account list
- Client affinity scheduling for Anthropic accounts
- Sora async task API and GDrive storage backend
- Swipe-to-select for admin tables
- OpenAI passthrough account selection fix
- Various quota and usage enhancements
- Rate column is now editable input (local state, not auto-saved)
- Add/delete/clear-all are local operations until Save is clicked
- Batch adjust shows "最终倍率" preview column before applying
- Bottom cancel/save bar appears when there are unsaved changes
- Save sends all entries at once via batch API
- Move clear-all button next to add button
- Always show pagination bar (not hidden when entries <= pageSize)
- Change page size options to 10/20/50, default 10
- Move email column to first position
- Add sticky table header with scrollable body
- Add page size selector (20/50/100)
- Use ConfirmDialog for clear all confirmation
- Disable browser autocomplete on inputs
- Enlarge clear all button styling
- Widen modal to 'wide' size for table layout
- Separate columns: ID, username, email, notes, status, rate
- Use standard Pagination component for consistent style
- Add user_notes and user_status to backend GroupRateMultiplierEntry
- Add unit tests for GetGroupRateMultipliers and ClearGroupRateMultipliers
- Allow duplicate user add (overwrites existing rate)