fix(密钥检测): 直连失败重试一次,超时/网络提示中文化
上游 ai.t8star.org 实测约 1/8 单请求完全不应答(主机 A 上 JDK 客户端 HTTP/1.1 与 HTTP/2 均复现),检测只有一次机会时用户会看到 「网络不可达:HttpTimeoutException: request timed out」。 - 传输层失败(超时/网络不可达)对直连最后一跳重试一次(800ms 间隔); 代理模块不重试——jikip 按提取次数计费,重试会多扣一次 - 失败文案中文化(新增 CODE_TIMEOUT),英文异常串只进服务端日志 - 探测提问改「你好」(最简一次调用) - 检测面板标明检测对象(配置密钥 sk-**** / 输入值(未保存)), 检测接口超时单独放宽(客户端 60s / 后台 180s),避免重试期间前端先超时
This commit is contained in:
@@ -71,11 +71,13 @@ export function deleteMyApiSecret(moduleKey: string) {
|
||||
}
|
||||
|
||||
/** value 非空时检测输入值(不落库);为空时检测服务端已存密钥并把结果落库。 */
|
||||
/** 检测连通性:服务端最多「首查 + 传输层失败重试一次」,最坏约 31s,故单独放宽超时。 */
|
||||
export function checkMyApiSecret(moduleKey: string, value?: string) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<UserApiSecretCheckResult>, { value?: string }>(
|
||||
buildJavaUrl(API_ENDPOINTS.userSecret.check.replace('{moduleKey}', encodeURIComponent(moduleKey))),
|
||||
{ value },
|
||||
{ timeout: 60000 },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
:disabled="busy || moduleStates[module.moduleKey].checking"
|
||||
@click="runCheck(module.moduleKey as ApiSecretModuleKey)"
|
||||
>
|
||||
{{ moduleStates[module.moduleKey].checking ? '检测中...' : (moduleStates[module.moduleKey].input.trim() ? '检测输入值' : '检测已存密钥') }}
|
||||
{{ moduleStates[module.moduleKey].checking ? '检测中...' : (moduleStates[module.moduleKey].input.trim() ? '检测输入值(未保存)' : '检测配置密钥') }}
|
||||
</button>
|
||||
<span class="check-result" :class="resultClassOf(module.moduleKey)">
|
||||
{{ statusTextOf(module.moduleKey) }}
|
||||
@@ -93,7 +93,7 @@
|
||||
:disabled="!proxyReady || busy || proxyChecking"
|
||||
@click="runProxyCheck"
|
||||
>
|
||||
{{ proxyChecking ? '检测中...' : (proxyUrl.trim() && proxyDirty ? '检测输入地址' : '检测已存代理') }}
|
||||
{{ proxyChecking ? '检测中...' : (proxyUrl.trim() && proxyDirty ? '检测输入地址(未保存)' : '检测配置代理') }}
|
||||
</button>
|
||||
<span class="check-result" :class="proxyResultClass">
|
||||
{{ proxyStatusText }}
|
||||
@@ -147,13 +147,15 @@ type ModuleState = {
|
||||
checking: boolean
|
||||
result: UserApiSecretCheckResult | null
|
||||
error: string
|
||||
/** 最近一次检测的来源:saved=密钥配置里已保存的密钥,input=输入框里的未保存值。 */
|
||||
lastCheckSource: 'saved' | 'input' | ''
|
||||
}
|
||||
|
||||
const modules = ref(listApiSecretModules())
|
||||
const moduleStates = reactive<Record<string, ModuleState>>({})
|
||||
// 首次渲染即需可读:按模块清单预初始化状态(后续 refreshSnapshots 兜底补齐)
|
||||
for (const module of modules.value) {
|
||||
moduleStates[module.moduleKey] = { input: '', checking: false, result: null, error: '' }
|
||||
moduleStates[module.moduleKey] = { input: '', checking: false, result: null, error: '', lastCheckSource: '' }
|
||||
}
|
||||
const snapshots = ref<Record<string, ApiSecretSnapshot>>({})
|
||||
const busy = ref(false)
|
||||
@@ -240,7 +242,7 @@ const balanceText = computed(() => {
|
||||
|
||||
function ensureModuleState(moduleKey: string) {
|
||||
if (!moduleStates[moduleKey]) {
|
||||
moduleStates[moduleKey] = { input: '', checking: false, result: null, error: '' }
|
||||
moduleStates[moduleKey] = { input: '', checking: false, result: null, error: '', lastCheckSource: '' }
|
||||
}
|
||||
return moduleStates[moduleKey]
|
||||
}
|
||||
@@ -288,13 +290,18 @@ function formatTime(millis: number | null) {
|
||||
|
||||
function statusTextOf(moduleKey: string) {
|
||||
const state = ensureModuleState(moduleKey)
|
||||
if (state.checking) return '正在检测...'
|
||||
if (state.checking) return state.input.trim() ? '正在检测输入值...' : '正在检测配置密钥...'
|
||||
if (state.result) {
|
||||
const latency = state.result.checkLatencyMs != null ? `(${state.result.checkLatencyMs}ms)` : ''
|
||||
const suffix = state.result.viaProxy === true ? ' · 经代理' : ''
|
||||
if (state.result.checkStatus === 'passed') return `检测通过${latency}${suffix}`
|
||||
if (state.result.checkCode === 'insufficient_balance') return `欠费:${state.result.checkMessage || '代理服务商余额不足'}${suffix}`
|
||||
return `${state.result.checkMessage || '检测失败'}${suffix}`
|
||||
const snapshot = snapshotOf(moduleKey)
|
||||
// 明确展示本次检测的对象:输入值(未保存)或该用户密钥配置里保存的密钥(脱敏)
|
||||
const prefix = state.lastCheckSource === 'input'
|
||||
? '输入值(未保存):'
|
||||
: `配置密钥${snapshot.masked ? ` ${snapshot.masked}` : ''}:`
|
||||
if (state.result.checkStatus === 'passed') return `${prefix}检测通过${latency}${suffix}`
|
||||
if (state.result.checkCode === 'insufficient_balance') return `${prefix}欠费:${state.result.checkMessage || '代理服务商余额不足'}${suffix}`
|
||||
return `${prefix}${state.result.checkMessage || '检测失败'}${suffix}`
|
||||
}
|
||||
if (state.error) return state.error
|
||||
const snapshot = snapshotOf(moduleKey)
|
||||
@@ -302,7 +309,8 @@ function statusTextOf(moduleKey: string) {
|
||||
const status = CHECK_STATUS_TEXT[snapshot.checkStatus] || '已保存'
|
||||
const time = formatTime(snapshot.checkedAt)
|
||||
const message = snapshot.checkStatus === 'unknown' ? '' : `:${snapshot.checkMessage || ''}`
|
||||
return `${status}${message}${time ? ` · ${time}` : ''}`
|
||||
const masked = snapshot.masked ? `配置密钥 ${snapshot.masked} · ` : ''
|
||||
return `${masked}${status}${message}${time ? ` · ${time}` : ''}`
|
||||
}
|
||||
|
||||
function resultClassOf(moduleKey: string) {
|
||||
@@ -322,7 +330,9 @@ async function runCheck(moduleKey: ApiSecretModuleKey) {
|
||||
state.error = ''
|
||||
state.result = null
|
||||
const inputValue = state.input.trim()
|
||||
state.lastCheckSource = inputValue ? 'input' : 'saved'
|
||||
try {
|
||||
// 不传 override 时服务端检测的是当前登录用户在密钥配置里保存的密钥(按 uid 绑定)
|
||||
const result = await checkApiSecret(moduleKey, inputValue || undefined)
|
||||
state.result = result
|
||||
if (result.checkStatus === 'failed') {
|
||||
@@ -330,7 +340,7 @@ async function runCheck(moduleKey: ApiSecretModuleKey) {
|
||||
} else if (result.checkStatus === 'error') {
|
||||
ElMessage.warning(result.checkMessage || '暂时无法判定密钥有效性')
|
||||
} else {
|
||||
ElMessage.success(inputValue ? '输入值检测通过(未保存)' : '密钥检测通过')
|
||||
ElMessage.success(inputValue ? '输入值检测通过(未保存)' : '配置密钥检测通过')
|
||||
}
|
||||
} catch (error) {
|
||||
state.error = error instanceof Error ? error.message : '检测失败'
|
||||
|
||||
Reference in New Issue
Block a user