mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-09-24 16:04:35 +08:00
refactor(usage): centralize normalizeAuthIndex
This commit is contained in:
@@ -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'));
|
||||
}
|
||||
|
||||
@@ -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'] ?? '')
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user