feat(usage): resolve credential names from auth files by auth_index

This commit is contained in:
Supra4E8C
2026-02-13 13:44:12 +08:00
parent d027d04f64
commit de0753f0ce
2 changed files with 71 additions and 3 deletions

View File

@@ -1,7 +1,9 @@
import { useMemo } from 'react'; import { useMemo, useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Card } from '@/components/ui/Card'; import { Card } from '@/components/ui/Card';
import { computeKeyStats, formatCompactNumber } from '@/utils/usage'; import { computeKeyStats, formatCompactNumber } from '@/utils/usage';
import { authFilesApi } from '@/services/api/authFiles';
import type { AuthFileItem } from '@/types/authFile';
import type { UsagePayload } from './hooks/useUsageData'; import type { UsagePayload } from './hooks/useUsageData';
import styles from '@/pages/UsagePage.module.scss'; import styles from '@/pages/UsagePage.module.scss';
@@ -12,14 +14,59 @@ export interface CredentialStatsCardProps {
interface CredentialRow { interface CredentialRow {
key: string; key: string;
displayName: string;
type: string;
success: number; success: number;
failure: number; failure: number;
total: number; total: number;
successRate: number; successRate: 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;
}
function buildAuthIndexMap(files: AuthFileItem[]): Map<string, { name: string; type: string }> {
const map = new Map<string, { name: string; type: string }>();
files.forEach((file) => {
const rawAuthIndex = file['auth_index'] ?? file.authIndex;
const key = normalizeAuthIndexValue(rawAuthIndex);
if (key) {
map.set(key, {
name: file.name || key,
type: (file.type || file.provider || '').toString(),
});
}
});
return map;
}
export function CredentialStatsCard({ usage, loading }: CredentialStatsCardProps) { export function CredentialStatsCard({ usage, loading }: CredentialStatsCardProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const [authFiles, setAuthFiles] = useState<AuthFileItem[]>([]);
useEffect(() => {
let cancelled = false;
authFilesApi.list().then((res) => {
if (cancelled) return;
const files = Array.isArray(res) ? res : (res as { files?: AuthFileItem[] })?.files;
if (Array.isArray(files)) {
setAuthFiles(files);
}
}).catch(() => {
// silently ignore - credential names will just show raw auth_index
});
return () => { cancelled = true; };
}, []);
const authIndexMap = useMemo(() => buildAuthIndexMap(authFiles), [authFiles]);
const rows = useMemo((): CredentialRow[] => { const rows = useMemo((): CredentialRow[] => {
if (!usage) return []; if (!usage) return [];
@@ -27,8 +74,11 @@ export function CredentialStatsCard({ usage, loading }: CredentialStatsCardProps
return Object.entries(byAuthIndex) return Object.entries(byAuthIndex)
.map(([key, bucket]) => { .map(([key, bucket]) => {
const total = bucket.success + bucket.failure; const total = bucket.success + bucket.failure;
const mapped = authIndexMap.get(key);
return { return {
key, key,
displayName: mapped?.name || key,
type: mapped?.type || '',
success: bucket.success, success: bucket.success,
failure: bucket.failure, failure: bucket.failure,
total, total,
@@ -36,7 +86,7 @@ export function CredentialStatsCard({ usage, loading }: CredentialStatsCardProps
}; };
}) })
.sort((a, b) => b.total - a.total); .sort((a, b) => b.total - a.total);
}, [usage]); }, [usage, authIndexMap]);
return ( return (
<Card title={t('usage_stats.credential_stats')}> <Card title={t('usage_stats.credential_stats')}>
@@ -55,7 +105,12 @@ export function CredentialStatsCard({ usage, loading }: CredentialStatsCardProps
<tbody> <tbody>
{rows.map((row) => ( {rows.map((row) => (
<tr key={row.key}> <tr key={row.key}>
<td className={styles.modelCell}>{row.key}</td> <td className={styles.modelCell}>
<span>{row.displayName}</span>
{row.type && (
<span className={styles.credentialType}>{row.type}</span>
)}
</td>
<td> <td>
<span className={styles.requestCountCell}> <span className={styles.requestCountCell}>
<span>{formatCompactNumber(row.total)}</span> <span>{formatCompactNumber(row.total)}</span>

View File

@@ -612,6 +612,19 @@
word-break: break-all; word-break: break-all;
} }
.credentialType {
display: inline-block;
margin-left: 6px;
padding: 1px 6px;
border-radius: $radius-full;
font-size: 10px;
font-weight: 600;
color: var(--text-secondary);
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
vertical-align: middle;
}
.requestCountCell { .requestCountCell {
display: inline-flex; display: inline-flex;
align-items: baseline; align-items: baseline;