refactor(usage): centralize normalizeAuthIndex

This commit is contained in:
Supra4E8C
2026-02-22 16:08:06 +08:00
parent 0d7d7471f3
commit c9e0c5d73e
9 changed files with 26 additions and 79 deletions
+5 -5
View File
@@ -34,7 +34,6 @@ import {
CODEX_REQUEST_HEADERS,
GEMINI_CLI_QUOTA_URL,
GEMINI_CLI_REQUEST_HEADERS,
normalizeAuthIndexValue,
normalizeGeminiCliModelId,
normalizeNumberValue,
normalizePlanType,
@@ -60,6 +59,7 @@ import {
isGeminiCliFile,
isRuntimeOnlyAuthFile,
} from '@/utils/quota';
import { normalizeAuthIndex } from '@/utils/usage';
import type { QuotaRenderHelpers } from './QuotaCard';
import styles from '@/pages/QuotaPage.module.scss';
@@ -135,7 +135,7 @@ const fetchAntigravityQuota = async (
t: TFunction
): Promise<AntigravityQuotaGroup[]> => {
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
const authIndex = normalizeAuthIndexValue(rawAuthIndex);
const authIndex = normalizeAuthIndex(rawAuthIndex);
if (!authIndex) {
throw new Error(t('antigravity_quota.missing_auth_index'));
}
@@ -377,7 +377,7 @@ const fetchCodexQuota = async (
t: TFunction
): Promise<{ planType: string | null; windows: CodexQuotaWindow[] }> => {
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
const authIndex = normalizeAuthIndexValue(rawAuthIndex);
const authIndex = normalizeAuthIndex(rawAuthIndex);
if (!authIndex) {
throw new Error(t('codex_quota.missing_auth_index'));
}
@@ -419,7 +419,7 @@ const fetchGeminiCliQuota = async (
t: TFunction
): Promise<GeminiCliQuotaBucketState[]> => {
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
const authIndex = normalizeAuthIndexValue(rawAuthIndex);
const authIndex = normalizeAuthIndex(rawAuthIndex);
if (!authIndex) {
throw new Error(t('gemini_cli_quota.missing_auth_index'));
}
@@ -667,7 +667,7 @@ const fetchClaudeQuota = async (
t: TFunction
): Promise<{ windows: ClaudeQuotaWindow[]; extraUsage?: ClaudeExtraUsage | null }> => {
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
const authIndex = normalizeAuthIndexValue(rawAuthIndex);
const authIndex = normalizeAuthIndex(rawAuthIndex);
if (!authIndex) {
throw new Error(t('claude_quota.missing_auth_index'));
}
+4 -14
View File
@@ -5,7 +5,8 @@ import {
computeKeyStats,
collectUsageDetails,
buildCandidateUsageSourceIds,
formatCompactNumber
formatCompactNumber,
normalizeAuthIndex
} from '@/utils/usage';
import { authFilesApi } from '@/services/api/authFiles';
import type { GeminiKeyConfig, ProviderKeyConfig, OpenAIProviderConfig } from '@/types';
@@ -43,17 +44,6 @@ interface CredentialBucket {
failure: number;
}
function normalizeAuthIndexValue(value: unknown): string | null {
if (typeof value === 'number' && Number.isFinite(value)) {
return value.toString();
}
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed || null;
}
return null;
}
export function CredentialStatsCard({
usage,
loading,
@@ -78,7 +68,7 @@ export function CredentialStatsCard({
const map = new Map<string, CredentialInfo>();
files.forEach((file) => {
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
const key = normalizeAuthIndexValue(rawAuthIndex);
const key = normalizeAuthIndex(rawAuthIndex);
if (key) {
map.set(key, {
name: file.name || key,
@@ -195,7 +185,7 @@ export function CredentialStatsCard({
// Also collect fallback stats for details without source but with auth_index.
const sourceToAuthFile = new Map<string, CredentialInfo>();
details.forEach((d) => {
const authIdx = normalizeAuthIndexValue(d.auth_index);
const authIdx = normalizeAuthIndex(d.auth_index);
if (!d.source) {
if (!authIdx) return;
const fallback = fallbackByAuthIndex.get(authIdx) ?? { success: 0, failure: 0 };
@@ -10,7 +10,8 @@ import type { AuthFileItem } from '@/types/authFile';
import {
buildCandidateUsageSourceIds,
collectUsageDetails,
extractTotalTokens
extractTotalTokens,
normalizeAuthIndex
} from '@/utils/usage';
import { downloadBlob } from '@/utils/download';
import styles from '@/pages/UsagePage.module.scss';
@@ -69,17 +70,6 @@ type SourceInfo = {
type: string;
};
function normalizeAuthIndexValue(value: unknown): string | null {
if (typeof value === 'number' && Number.isFinite(value)) {
return value.toString();
}
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed || null;
}
return null;
}
export function RequestEventsDetailsCard({
usage,
loading,
@@ -106,7 +96,7 @@ export function RequestEventsDetailsCard({
if (!Array.isArray(files)) return;
const map = new Map<string, CredentialInfo>();
files.forEach((file) => {
const key = normalizeAuthIndexValue(file['auth_index'] ?? file.authIndex);
const key = normalizeAuthIndex(file['auth_index'] ?? file.authIndex);
if (!key) return;
map.set(key, {
name: file.name || key,
@@ -205,7 +195,7 @@ export function RequestEventsDetailsCard({
authIndexRaw === null || authIndexRaw === undefined || authIndexRaw === ''
? '-'
: String(authIndexRaw);
const normalizedAuthIndex = normalizeAuthIndexValue(authIndexRaw);
const normalizedAuthIndex = normalizeAuthIndex(authIndexRaw);
const sourceInfo = sourceInfoMap.get(sourceRaw);
const authInfo = normalizedAuthIndex ? authFileMap.get(normalizedAuthIndex) : undefined;
const source = sourceInfo?.displayName
@@ -6,7 +6,7 @@ import { IconBot, IconCheck, IconCode, IconDownload, IconInfo, IconTrash2 } from
import { ProviderStatusBar } from '@/components/providers/ProviderStatusBar';
import type { AuthFileItem } from '@/types';
import { resolveAuthProvider } from '@/utils/quota';
import { calculateStatusBarData, type KeyStats } from '@/utils/usage';
import { calculateStatusBarData, normalizeAuthIndex, type KeyStats } from '@/utils/usage';
import { formatFileSize } from '@/utils/format';
import {
AUTH_FILE_REFRESH_WARNING_MS,
@@ -15,7 +15,6 @@ import {
getTypeColor,
getTypeLabel,
isRuntimeOnlyAuthFile,
normalizeAuthIndexValue,
resolveAuthFileStats,
type QuotaProviderType,
type ResolvedTheme
@@ -108,7 +107,7 @@ export function AuthFileCard(props: AuthFileCardProps) {
: '';
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
const authIndexKey = normalizeAuthIndexValue(rawAuthIndex);
const authIndexKey = normalizeAuthIndex(rawAuthIndex);
const statusData =
(authIndexKey && statusBarCache.get(authIndexKey)) || calculateStatusBarData([]);
const rawStatus = String(file.status ?? file['status'] ?? '')
+2 -13
View File
@@ -1,6 +1,7 @@
import type { TFunction } from 'i18next';
import type { AuthFileItem } from '@/types';
import {
normalizeAuthIndex,
normalizeUsageSourceId,
type KeyStatBucket,
type KeyStats
@@ -143,18 +144,6 @@ export const parseDisableCoolingValue = (value: unknown): boolean | undefined =>
return undefined;
};
// 标准化 auth_index 值(与 usage.ts 中的 normalizeAuthIndex 保持一致)
export function normalizeAuthIndexValue(value: unknown): string | null {
if (typeof value === 'number' && Number.isFinite(value)) {
return value.toString();
}
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed ? trimmed : null;
}
return null;
}
export function isRuntimeOnlyAuthFile(file: AuthFileItem): boolean {
const raw = file['runtime_only'] ?? file.runtimeOnly;
if (typeof raw === 'boolean') return raw;
@@ -168,7 +157,7 @@ export function resolveAuthFileStats(file: AuthFileItem, stats: KeyStats): KeySt
// 兼容 auth_index 和 authIndex 两种字段名(API 返回的是 auth_index)
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
const authIndexKey = normalizeAuthIndexValue(rawAuthIndex);
const authIndexKey = normalizeAuthIndex(rawAuthIndex);
// 尝试根据 authIndex 匹配
if (authIndexKey && stats.byAuthIndex?.[authIndexKey]) {
@@ -1,7 +1,6 @@
import { useMemo } from 'react';
import type { AuthFileItem } from '@/types';
import { calculateStatusBarData, type UsageDetail } from '@/utils/usage';
import { normalizeAuthIndexValue } from '@/features/authFiles/constants';
import { calculateStatusBarData, normalizeAuthIndex, type UsageDetail } from '@/utils/usage';
export type AuthFileStatusBarData = ReturnType<typeof calculateStatusBarData>;
@@ -11,11 +10,11 @@ export function useAuthFilesStatusBarCache(files: AuthFileItem[], usageDetails:
files.forEach((file) => {
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
const authIndexKey = normalizeAuthIndexValue(rawAuthIndex);
const authIndexKey = normalizeAuthIndex(rawAuthIndex);
if (authIndexKey) {
const filteredDetails = usageDetails.filter((detail) => {
const detailAuthIndex = normalizeAuthIndexValue(detail.auth_index);
const detailAuthIndex = normalizeAuthIndex(detail.auth_index);
return detailAuthIndex !== null && detailAuthIndex === authIndexKey;
});
cache.set(authIndexKey, calculateStatusBarData(filteredDetails));
@@ -25,4 +24,3 @@ export function useAuthFilesStatusBarCache(files: AuthFileItem[], usageDetails:
return cache;
}, [files, usageDetails]);
}
+3 -13
View File
@@ -30,6 +30,7 @@ import { formatUnixTimestamp } from '@/utils/format';
import {
buildCandidateUsageSourceIds,
collectUsageDetailsWithEndpoint,
normalizeAuthIndex,
type UsageDetailWithEndpoint
} from '@/utils/usage';
import styles from './LogsPage.module.scss';
@@ -171,17 +172,6 @@ const TRACE_MATCH_STRONG_WINDOW_MS = 3 * 1000;
const TRACE_MATCH_WINDOW_MS = 10 * 1000;
const TRACE_MATCH_MAX_WINDOW_MS = 30 * 1000;
const normalizeTraceAuthIndex = (value: unknown): string | null => {
if (typeof value === 'number' && Number.isFinite(value)) {
return value.toString();
}
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed || null;
}
return null;
};
const normalizeTracePath = (value?: string) =>
String(value ?? '')
.replace(/^"+|"+$/g, '')
@@ -798,7 +788,7 @@ export function LogsPage() {
if (Array.isArray(files)) {
const map = new Map<string, TraceCredentialInfo>();
files.forEach((file) => {
const key = normalizeTraceAuthIndex(file['auth_index'] ?? file.authIndex);
const key = normalizeAuthIndex(file['auth_index'] ?? file.authIndex);
if (!key) return;
map.set(key, {
name: file.name || key,
@@ -949,7 +939,7 @@ export function LogsPage() {
return matchedSource;
}
const authIndexKey = normalizeTraceAuthIndex(authIndex);
const authIndexKey = normalizeAuthIndex(authIndex);
if (authIndexKey) {
const authInfo = traceAuthFileMap.get(authIndexKey);
if (authInfo) {
+2 -11
View File
@@ -3,19 +3,10 @@
*/
import type { ClaudeUsagePayload, CodexUsagePayload, GeminiCliQuotaPayload } from '@/types';
import { normalizeAuthIndex } from '@/utils/usage';
const GEMINI_CLI_MODEL_SUFFIX = '_vertex';
export function normalizeAuthIndexValue(value: unknown): string | null {
if (typeof value === 'number' && Number.isFinite(value)) {
return value.toString();
}
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed ? trimmed : null;
}
return null;
}
export { normalizeAuthIndex };
export function normalizeStringValue(value: unknown): string | null {
if (typeof value === 'string') {
+1 -1
View File
@@ -222,7 +222,7 @@ export function filterUsageByTimeRange<T>(usageData: T, range: UsageTimeRange, n
} as T;
}
const normalizeAuthIndex = (value: unknown) => {
export const normalizeAuthIndex = (value: unknown) => {
if (typeof value === 'number' && Number.isFinite(value)) {
return value.toString();
}