From a73e0d51dbc675ce42262a519a5ea828210ab80b Mon Sep 17 00:00:00 2001 From: fawney19 Date: Fri, 12 Dec 2025 18:15:46 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=93=BE=E8=B7=AF?= =?UTF-8?q?=E8=BF=BD=E8=B8=AA=E5=AF=86=E9=92=A5=E6=98=BE=E7=A4=BA=E5=92=8C?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E9=80=89=E4=B8=AD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复密钥脱敏显示问题:先解密再脱敏,避免显示加密后的 base64 数据 2. 优化详情默认选中逻辑:优先显示最后一个有效结果(成功/失败),而非未执行/跳过 --- .../components/HorizontalRequestTimeline.vue | 23 ++++++++++--- src/api/admin/monitoring/trace.py | 34 +++++++++++-------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/frontend/src/features/usage/components/HorizontalRequestTimeline.vue b/frontend/src/features/usage/components/HorizontalRequestTimeline.vue index e60633c..eaa0987 100644 --- a/frontend/src/features/usage/components/HorizontalRequestTimeline.vue +++ b/frontend/src/features/usage/components/HorizontalRequestTimeline.vue @@ -552,8 +552,8 @@ const isCapabilityUsed = (cap: string): boolean => { return activeCapabilities.value.includes(cap) } -// 检查某个能力是否被 Key 支持 -const isCapabilitySupported = (cap: string): boolean => { +// 检查某个能力是否被 Key 支持(保留以备后用) +const _isCapabilitySupported = (cap: string): boolean => { return keyCapabilities.value.includes(cap) } @@ -653,7 +653,20 @@ watch(groupedTimeline, (newGroups) => { return } - // 默认选择最后一个组 + // 查找最后一个有效结果的组(failed 优先于 skipped/available) + // 从后往前找第一个 failed 的组 + for (let i = newGroups.length - 1; i >= 0; i--) { + const group = newGroups[i] + if (group.primaryStatus === 'failed') { + selectedGroupIndex.value = i + // 选中最后一个失败的尝试 + const failedIdx = group.allAttempts.findLastIndex((a: CandidateRecord) => a.status === 'failed') + selectedAttemptIndex.value = failedIdx >= 0 ? failedIdx : group.allAttempts.length - 1 + return + } + } + + // 都没有则选择最后一个组 selectedGroupIndex.value = newGroups.length - 1 selectedAttemptIndex.value = newGroups[newGroups.length - 1].allAttempts.length - 1 }, { immediate: true }) @@ -702,8 +715,8 @@ const getStatusLabel = (status: string) => { return labels[status] || status } -// 获取状态徽章样式 -const getStatusBadgeVariant = (status: string): any => { +// 获取状态徽章样式(保留以备后用) +const _getStatusBadgeVariant = (status: string): string => { const variants: Record = { available: 'outline', pending: 'secondary', diff --git a/src/api/admin/monitoring/trace.py b/src/api/admin/monitoring/trace.py index 11d0ca0..de89a26 100644 --- a/src/api/admin/monitoring/trace.py +++ b/src/api/admin/monitoring/trace.py @@ -14,6 +14,7 @@ from src.api.base.admin_adapter import AdminApiAdapter from src.api.base.pipeline import ApiRequestPipeline from src.database import get_db from src.models.database import Provider, ProviderEndpoint, ProviderAPIKey +from src.core.crypto import crypto_service from src.services.request.candidate import RequestCandidateService router = APIRouter(prefix="/api/admin/monitoring/trace", tags=["Admin - Monitoring: Trace"]) @@ -171,22 +172,25 @@ class AdminGetRequestTraceAdapter(AdminApiAdapter): for k in keys: key_map[k.id] = k.name key_capabilities_map[k.id] = k.capabilities - # 生成脱敏预览:保留前缀和最后4位 - api_key = k.api_key or "" - if len(api_key) > 8: - # 检测常见前缀模式 - prefix_end = 0 - for prefix in ["sk-", "key-", "api-", "ak-"]: - if api_key.lower().startswith(prefix): - prefix_end = len(prefix) - break - if prefix_end > 0: - key_preview_map[k.id] = f"{api_key[:prefix_end]}***{api_key[-4:]}" + # 生成脱敏预览:先解密再脱敏 + try: + decrypted_key = crypto_service.decrypt(k.api_key) + if len(decrypted_key) > 8: + # 检测常见前缀模式 + prefix_end = 0 + for prefix in ["sk-", "key-", "api-", "ak-"]: + if decrypted_key.lower().startswith(prefix): + prefix_end = len(prefix) + break + if prefix_end > 0: + key_preview_map[k.id] = f"{decrypted_key[:prefix_end]}***{decrypted_key[-4:]}" + else: + key_preview_map[k.id] = f"{decrypted_key[:4]}***{decrypted_key[-4:]}" + elif len(decrypted_key) > 4: + key_preview_map[k.id] = f"***{decrypted_key[-4:]}" else: - key_preview_map[k.id] = f"{api_key[:3]}***{api_key[-4:]}" - elif len(api_key) > 4: - key_preview_map[k.id] = f"***{api_key[-4:]}" - else: + key_preview_map[k.id] = "***" + except Exception: key_preview_map[k.id] = "***" # 构建 candidate 响应列表