From e92784f951593ff133b0e2b413b33ed92f8ef51d Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Tue, 21 Oct 2025 21:59:08 +0800 Subject: [PATCH] feat(ui): add auth file success/failure stats; expand list height --- app.js | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++---- styles.css | 9 ++++++-- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index 0345324..4943951 100644 --- a/app.js +++ b/app.js @@ -3045,14 +3045,14 @@ class CLIProxyManager { async loadAuthFiles() { try { const data = await this.makeRequest('/auth-files'); - this.renderAuthFiles(data.files || []); + await this.renderAuthFiles(data.files || []); } catch (error) { console.error('加载认证文件失败:', error); } } // 渲染认证文件列表 - renderAuthFiles(files) { + async renderAuthFiles(files) { const container = document.getElementById('auth-files-list'); if (files.length === 0) { @@ -3066,12 +3066,70 @@ class CLIProxyManager { return; } - container.innerHTML = files.map(file => ` + // 获取使用统计,按 source 聚合 + const stats = await this.getKeyStats(); + + container.innerHTML = files.map(file => { + // 认证文件的统计匹配逻辑: + // 1. 首先尝试完整文件名匹配 + // 2. 如果没有匹配,尝试脱敏文件名匹配(去掉扩展名后的脱敏版本) + let fileStats = stats[file.name] || { success: 0, failure: 0 }; + + // 如果完整文件名没有统计,尝试基于文件名的脱敏版本匹配 + if (fileStats.success === 0 && fileStats.failure === 0) { + const nameWithoutExt = file.name.replace(/\.[^/.]+$/, ""); // 去掉扩展名 + + // 后端有两种脱敏规则,都要尝试: + // 规则1:完整描述脱敏 - mikiunameina@gmail.com (ethereal-advice-465201-t0) -> 脱敏 -> miki...-t0) + // 规则2:直接整体脱敏 - mikiunameina@gmail.com-ethereal-advice-465201-t0 -> 脱敏 -> ??? + + const possibleSources = []; + + // 规则1:尝试完整描述脱敏 + const match = nameWithoutExt.match(/^([^@]+@[^-]+)-(.+)$/); + if (match) { + const email = match[1]; // mikiunameina@gmail.com + const projectName = match[2]; // ethereal-advice-465201-t0 + + // 组合成完整的描述格式 + const fullDescription = `${email} (${projectName})`; + + // 对完整描述进行脱敏 + const maskedDescription = this.maskApiKey(fullDescription); + possibleSources.push(maskedDescription); + } + + // 规则2:类型-个人标识.json 格式,去掉类型前缀后脱敏 + const typeMatch = nameWithoutExt.match(/^[^-]+-(.+)$/); + if (typeMatch) { + const personalId = typeMatch[1]; // 个人标识部分 + const maskedPersonalId = this.maskApiKey(personalId); + possibleSources.push(maskedPersonalId); + } + + // 查找第一个有统计数据的匹配 + for (const source of possibleSources) { + if (stats[source] && (stats[source].success > 0 || stats[source].failure > 0)) { + fileStats = stats[source]; + break; + } + } + } + + return `