From c25e7556ed062cff22975b62ad5b8720993785d8 Mon Sep 17 00:00:00 2001 From: Supra4E8C Date: Sun, 22 Feb 2026 13:52:39 +0800 Subject: [PATCH] refactor(source): extract buildSourceInfoMap --- .../usage/RequestEventsDetailsCard.tsx | 84 +++---------------- src/pages/LogsPage.tsx | 67 +-------------- src/utils/sourceResolver.ts | 59 +++++++++++++ 3 files changed, 74 insertions(+), 136 deletions(-) create mode 100644 src/utils/sourceResolver.ts diff --git a/src/components/usage/RequestEventsDetailsCard.tsx b/src/components/usage/RequestEventsDetailsCard.tsx index 511f098..d541750 100644 --- a/src/components/usage/RequestEventsDetailsCard.tsx +++ b/src/components/usage/RequestEventsDetailsCard.tsx @@ -7,9 +7,9 @@ import { Select } from '@/components/ui/Select'; import { authFilesApi } from '@/services/api/authFiles'; import type { GeminiKeyConfig, ProviderKeyConfig, OpenAIProviderConfig } from '@/types'; import type { AuthFileItem } from '@/types/authFile'; -import type { CredentialInfo, SourceInfo } from '@/types/sourceInfo'; +import type { CredentialInfo } from '@/types/sourceInfo'; +import { buildSourceInfoMap } from '@/utils/sourceResolver'; import { - buildCandidateUsageSourceIds, collectUsageDetails, extractTotalTokens, normalizeAuthIndex @@ -102,75 +102,17 @@ export function RequestEventsDetailsCard({ }; }, []); - const sourceInfoMap = useMemo(() => { - const map = new Map(); - - const registerSource = (sourceId: string, displayName: string, type: string) => { - if (!sourceId || !displayName) return; - if (map.has(sourceId)) return; - map.set(sourceId, { displayName, type }); - }; - - const registerCandidates = ( - displayName: string, - type: string, - candidates: string[] - ) => { - candidates.forEach((sourceId) => registerSource(sourceId, displayName, type)); - }; - - geminiKeys.forEach((config, index) => { - const displayName = config.prefix?.trim() || `Gemini #${index + 1}`; - registerCandidates( - displayName, - 'gemini', - buildCandidateUsageSourceIds({ apiKey: config.apiKey, prefix: config.prefix }) - ); - }); - - claudeConfigs.forEach((config, index) => { - const displayName = config.prefix?.trim() || `Claude #${index + 1}`; - registerCandidates( - displayName, - 'claude', - buildCandidateUsageSourceIds({ apiKey: config.apiKey, prefix: config.prefix }) - ); - }); - - codexConfigs.forEach((config, index) => { - const displayName = config.prefix?.trim() || `Codex #${index + 1}`; - registerCandidates( - displayName, - 'codex', - buildCandidateUsageSourceIds({ apiKey: config.apiKey, prefix: config.prefix }) - ); - }); - - vertexConfigs.forEach((config, index) => { - const displayName = config.prefix?.trim() || `Vertex #${index + 1}`; - registerCandidates( - displayName, - 'vertex', - buildCandidateUsageSourceIds({ apiKey: config.apiKey, prefix: config.prefix }) - ); - }); - - openaiProviders.forEach((provider, providerIndex) => { - const displayName = provider.prefix?.trim() || provider.name || `OpenAI #${providerIndex + 1}`; - const candidates = new Set(); - buildCandidateUsageSourceIds({ prefix: provider.prefix }).forEach((sourceId) => - candidates.add(sourceId) - ); - (provider.apiKeyEntries || []).forEach((entry) => { - buildCandidateUsageSourceIds({ apiKey: entry.apiKey }).forEach((sourceId) => - candidates.add(sourceId) - ); - }); - registerCandidates(displayName, 'openai', Array.from(candidates)); - }); - - return map; - }, [claudeConfigs, codexConfigs, geminiKeys, openaiProviders, vertexConfigs]); + const sourceInfoMap = useMemo( + () => + buildSourceInfoMap({ + geminiApiKeys: geminiKeys, + claudeApiKeys: claudeConfigs, + codexApiKeys: codexConfigs, + vertexApiKeys: vertexConfigs, + openaiCompatibility: openaiProviders, + }), + [claudeConfigs, codexConfigs, geminiKeys, openaiProviders, vertexConfigs] + ); const rows = useMemo(() => { const details = collectUsageDetails(usage); diff --git a/src/pages/LogsPage.tsx b/src/pages/LogsPage.tsx index fcabb80..0ea004d 100644 --- a/src/pages/LogsPage.tsx +++ b/src/pages/LogsPage.tsx @@ -28,8 +28,8 @@ import { copyToClipboard } from '@/utils/clipboard'; import { downloadBlob } from '@/utils/download'; import { MANAGEMENT_API_PREFIX } from '@/utils/constants'; import { formatUnixTimestamp } from '@/utils/format'; +import { buildSourceInfoMap } from '@/utils/sourceResolver'; import { - buildCandidateUsageSourceIds, collectUsageDetailsWithEndpoint, normalizeAuthIndex, type UsageDetailWithEndpoint @@ -519,70 +519,7 @@ export function LogsPage() { const latestTimestampRef = useRef(0); const disableControls = connectionStatus !== 'connected'; - const traceSourceInfoMap = useMemo(() => { - const map = new Map(); - - const registerSource = (sourceId: string, displayName: string, type: string) => { - if (!sourceId || !displayName || map.has(sourceId)) return; - map.set(sourceId, { displayName, type }); - }; - - const registerCandidates = (displayName: string, type: string, candidates: string[]) => { - candidates.forEach((sourceId) => registerSource(sourceId, displayName, type)); - }; - - (config?.geminiApiKeys || []).forEach((item, index) => { - const displayName = item.prefix?.trim() || `Gemini #${index + 1}`; - registerCandidates( - displayName, - 'gemini', - buildCandidateUsageSourceIds({ apiKey: item.apiKey, prefix: item.prefix }) - ); - }); - - (config?.claudeApiKeys || []).forEach((item, index) => { - const displayName = item.prefix?.trim() || `Claude #${index + 1}`; - registerCandidates( - displayName, - 'claude', - buildCandidateUsageSourceIds({ apiKey: item.apiKey, prefix: item.prefix }) - ); - }); - - (config?.codexApiKeys || []).forEach((item, index) => { - const displayName = item.prefix?.trim() || `Codex #${index + 1}`; - registerCandidates( - displayName, - 'codex', - buildCandidateUsageSourceIds({ apiKey: item.apiKey, prefix: item.prefix }) - ); - }); - - (config?.vertexApiKeys || []).forEach((item, index) => { - const displayName = item.prefix?.trim() || `Vertex #${index + 1}`; - registerCandidates( - displayName, - 'vertex', - buildCandidateUsageSourceIds({ apiKey: item.apiKey, prefix: item.prefix }) - ); - }); - - (config?.openaiCompatibility || []).forEach((provider, providerIndex) => { - const displayName = provider.prefix?.trim() || provider.name || `OpenAI #${providerIndex + 1}`; - const candidates = new Set(); - buildCandidateUsageSourceIds({ prefix: provider.prefix }).forEach((sourceId) => - candidates.add(sourceId) - ); - (provider.apiKeyEntries || []).forEach((entry) => { - buildCandidateUsageSourceIds({ apiKey: entry.apiKey }).forEach((sourceId) => - candidates.add(sourceId) - ); - }); - registerCandidates(displayName, 'openai', Array.from(candidates)); - }); - - return map; - }, [config]); + const traceSourceInfoMap = useMemo(() => buildSourceInfoMap(config ?? {}), [config]); const isNearBottom = (node: HTMLDivElement | null) => { if (!node) return true; diff --git a/src/utils/sourceResolver.ts b/src/utils/sourceResolver.ts new file mode 100644 index 0000000..e593815 --- /dev/null +++ b/src/utils/sourceResolver.ts @@ -0,0 +1,59 @@ +import type { GeminiKeyConfig, OpenAIProviderConfig, ProviderKeyConfig } from '@/types'; +import type { SourceInfo } from '@/types/sourceInfo'; +import { buildCandidateUsageSourceIds } from '@/utils/usage'; + +export interface SourceInfoMapInput { + geminiApiKeys?: GeminiKeyConfig[]; + claudeApiKeys?: ProviderKeyConfig[]; + codexApiKeys?: ProviderKeyConfig[]; + vertexApiKeys?: ProviderKeyConfig[]; + openaiCompatibility?: OpenAIProviderConfig[]; +} + +export function buildSourceInfoMap(input: SourceInfoMapInput): Map { + const map = new Map(); + + const registerSource = (sourceId: string, displayName: string, type: string) => { + if (!sourceId || !displayName || map.has(sourceId)) return; + map.set(sourceId, { displayName, type }); + }; + + const registerCandidates = (displayName: string, type: string, candidates: string[]) => { + candidates.forEach((sourceId) => registerSource(sourceId, displayName, type)); + }; + + const providers: Array<{ + items: Array<{ apiKey?: string; prefix?: string }>; + type: string; + label: string; + }> = [ + { items: input.geminiApiKeys || [], type: 'gemini', label: 'Gemini' }, + { items: input.claudeApiKeys || [], type: 'claude', label: 'Claude' }, + { items: input.codexApiKeys || [], type: 'codex', label: 'Codex' }, + { items: input.vertexApiKeys || [], type: 'vertex', label: 'Vertex' }, + ]; + + providers.forEach(({ items, type, label }) => { + items.forEach((item, index) => { + const displayName = item.prefix?.trim() || `${label} #${index + 1}`; + registerCandidates( + displayName, + type, + buildCandidateUsageSourceIds({ apiKey: item.apiKey, prefix: item.prefix }) + ); + }); + }); + + // OpenAI 特殊处理:多 apiKeyEntries + (input.openaiCompatibility || []).forEach((provider, providerIndex) => { + const displayName = provider.prefix?.trim() || provider.name || `OpenAI #${providerIndex + 1}`; + const candidates = new Set(); + buildCandidateUsageSourceIds({ prefix: provider.prefix }).forEach((id) => candidates.add(id)); + (provider.apiKeyEntries || []).forEach((entry) => { + buildCandidateUsageSourceIds({ apiKey: entry.apiKey }).forEach((id) => candidates.add(id)); + }); + registerCandidates(displayName, 'openai', Array.from(candidates)); + }); + + return map; +}