diff --git a/backend/internal/handler/dto/mappers.go b/backend/internal/handler/dto/mappers.go index 886a5535..5663e299 100644 --- a/backend/internal/handler/dto/mappers.go +++ b/backend/internal/handler/dto/mappers.go @@ -204,6 +204,17 @@ func AccountFromServiceShallow(a *service.Account) *Account { } } + if scopeLimits := a.GetAntigravityScopeRateLimits(); len(scopeLimits) > 0 { + out.ScopeRateLimits = make(map[string]ScopeRateLimitInfo, len(scopeLimits)) + now := time.Now() + for scope, remainingSec := range scopeLimits { + out.ScopeRateLimits[scope] = ScopeRateLimitInfo{ + ResetAt: now.Add(time.Duration(remainingSec) * time.Second), + RemainingSec: remainingSec, + } + } + } + return out } diff --git a/backend/internal/handler/dto/types.go b/backend/internal/handler/dto/types.go index 4cfaef5f..0a58391c 100644 --- a/backend/internal/handler/dto/types.go +++ b/backend/internal/handler/dto/types.go @@ -2,6 +2,11 @@ package dto import "time" +type ScopeRateLimitInfo struct { + ResetAt time.Time `json:"reset_at"` + RemainingSec int64 `json:"remaining_sec"` +} + type User struct { ID int64 `json:"id"` Email string `json:"email"` @@ -108,6 +113,9 @@ type Account struct { RateLimitResetAt *time.Time `json:"rate_limit_reset_at"` OverloadUntil *time.Time `json:"overload_until"` + // Antigravity scope 级限流状态(从 extra 提取) + ScopeRateLimits map[string]ScopeRateLimitInfo `json:"scope_rate_limits,omitempty"` + TempUnschedulableUntil *time.Time `json:"temp_unschedulable_until"` TempUnschedulableReason string `json:"temp_unschedulable_reason"` diff --git a/frontend/src/components/account/AccountStatusIndicator.vue b/frontend/src/components/account/AccountStatusIndicator.vue index 02c962f1..6bb19b87 100644 --- a/frontend/src/components/account/AccountStatusIndicator.vue +++ b/frontend/src/components/account/AccountStatusIndicator.vue @@ -56,6 +56,65 @@ > + + +
+ + + 429 + + +
+ {{ t('admin.accounts.status.rateLimitedUntil', { time: formatTime(account.rate_limit_reset_at) }) }} +
+
+
+ + + + + +
+ + + 529 + + +
+ {{ t('admin.accounts.status.overloadedUntil', { time: formatTime(account.overload_until) }) }} +
+
+
@@ -81,6 +140,25 @@ const isRateLimited = computed(() => { return new Date(props.account.rate_limit_reset_at) > new Date() }) +// Computed: active scope rate limits (Antigravity) +const activeScopeRateLimits = computed(() => { + const scopeLimits = props.account.scope_rate_limits + if (!scopeLimits) return [] + const now = new Date() + return Object.entries(scopeLimits) + .filter(([, info]) => new Date(info.reset_at) > now) + .map(([scope, info]) => ({ scope, reset_at: info.reset_at })) +}) + +const formatScopeName = (scope: string): string => { + const names: Record = { + claude: 'Claude', + gemini_text: 'Gemini', + gemini_image: 'Image' + } + return names[scope] || scope +} + // Computed: is overloaded (529) const isOverloaded = computed(() => { if (!props.account.overload_until) return false diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index d0b38605..beb0bdf3 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -1183,6 +1183,7 @@ export default { overloaded: 'Overloaded', tempUnschedulable: 'Temp Unschedulable', rateLimitedUntil: 'Rate limited until {time}', + scopeRateLimitedUntil: '{scope} rate limited until {time}', overloadedUntil: 'Overloaded until {time}', viewTempUnschedDetails: 'View temp unschedulable details' }, diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index a1067689..328ad1bc 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -1305,6 +1305,7 @@ export default { overloaded: '过载中', tempUnschedulable: '临时不可调度', rateLimitedUntil: '限流中,重置时间:{time}', + scopeRateLimitedUntil: '{scope} 限流中,重置时间:{time}', overloadedUntil: '负载过重,重置时间:{time}', viewTempUnschedDetails: '查看临时不可调度详情' }, diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 7c6cbf52..0d904ab5 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -559,6 +559,9 @@ export interface Account { temp_unschedulable_until: string | null temp_unschedulable_reason: string | null + // Antigravity scope 级限流状态 + scope_rate_limits?: Record + // Session window fields (5-hour window) session_window_start: string | null session_window_end: string | null