mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
refactor: 前端全面替换 any 为 unknown 并统一错误处理,后端用量记录补写请求头/体
- 前端 API 层、stores、conversation 解析器、组件全面替换 any 为 unknown/具体类型 - 错误处理统一使用 parseApiError/getErrorStatus 替代 err.response?.data?.detail 模式 - 后端 handler/TaskService/UsageLifecycle/StreamTracker 链路传递 request_headers/request_body - streaming/pending 状态更新时可补写客户端和提供商的请求头及请求体 - 新增 TaskService 和 UsageService 相关测试
This commit is contained in:
@@ -527,10 +527,12 @@
|
||||
<span>{{ formatFullDate(viewingAnnouncement.created_at) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div
|
||||
class="prose prose-sm dark:prose-invert max-w-none"
|
||||
v-html="renderMarkdown(viewingAnnouncement.content)"
|
||||
/>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
|
||||
@@ -545,6 +545,7 @@ import {
|
||||
} from 'lucide-vue-next'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import { log } from '@/utils/logger'
|
||||
import { parseApiError } from '@/utils/errorParser'
|
||||
|
||||
const { success, error: showError } = useToast()
|
||||
|
||||
@@ -640,13 +641,9 @@ async function loadTokens() {
|
||||
if (tokens.value.length === 0 && currentPage.value > 1) {
|
||||
currentPage.value = 1
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
log.error('加载 Management Tokens 失败:', err)
|
||||
if (!err.response) {
|
||||
showError('无法连接到服务器')
|
||||
} else {
|
||||
showError(`加载失败:${err.response?.data?.detail || err.message}`)
|
||||
}
|
||||
showError(parseApiError(err, '加载失败'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -717,12 +714,9 @@ async function saveToken() {
|
||||
|
||||
closeDialog()
|
||||
await loadTokens()
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
log.error('保存 Token 失败:', err)
|
||||
const message = err.response?.data?.error?.message
|
||||
|| err.response?.data?.detail
|
||||
|| '保存失败'
|
||||
showError(message)
|
||||
showError(parseApiError(err, '保存失败'))
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
@@ -738,7 +732,7 @@ async function toggleToken(token: ManagementToken) {
|
||||
tokens.value[index] = result.data
|
||||
}
|
||||
success(result.data.is_active ? '令牌已启用' : '令牌已禁用')
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
log.error('切换状态失败:', err)
|
||||
showError('操作失败')
|
||||
}
|
||||
@@ -760,7 +754,7 @@ async function deleteToken() {
|
||||
showDeleteDialog.value = false
|
||||
success('令牌已删除')
|
||||
await loadTokens()
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
log.error('删除 Token 失败:', err)
|
||||
showError('删除失败')
|
||||
} finally {
|
||||
@@ -787,7 +781,7 @@ async function regenerateToken() {
|
||||
showTokenDialog.value = true
|
||||
await loadTokens()
|
||||
success('令牌已重新生成')
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
log.error('重新生成失败:', err)
|
||||
showError('重新生成失败')
|
||||
} finally {
|
||||
|
||||
@@ -292,6 +292,7 @@ import {
|
||||
import UserModelDetailDrawer from './components/UserModelDetailDrawer.vue'
|
||||
import { useRowClick } from '@/composables/useRowClick'
|
||||
import { log } from '@/utils/logger'
|
||||
import { parseApiError } from '@/utils/errorParser'
|
||||
|
||||
const { error: showError } = useToast()
|
||||
const { copyToClipboard } = useClipboard()
|
||||
@@ -440,9 +441,9 @@ async function loadModels() {
|
||||
// 使用用户认证端点,只获取用户有权限使用的模型
|
||||
const response = await meApi.getAvailableModels({ limit: 1000 })
|
||||
models.value = (response.models || []) as PublicGlobalModel[]
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
log.error('加载模型失败:', err)
|
||||
showError(err.response?.data?.detail || err.message, '加载模型失败')
|
||||
showError(parseApiError(err, ''), '加载模型失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -498,6 +498,8 @@ import RefreshButton from '@/components/ui/refresh-button.vue'
|
||||
import { Plus, Key, Copy, Trash2, Loader2, Activity, CheckCircle, Power, Check } from 'lucide-vue-next'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import { log } from '@/utils/logger'
|
||||
import { parseApiError } from '@/utils/errorParser'
|
||||
import { getErrorStatus } from '@/types/api-error'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const { success, error: showError } = useToast()
|
||||
@@ -548,14 +550,15 @@ async function loadApiKeys() {
|
||||
loading.value = true
|
||||
try {
|
||||
apiKeys.value = await meApi.getApiKeys()
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
log.error('加载 API 密钥失败:', error)
|
||||
if (!error.response) {
|
||||
const status = getErrorStatus(error)
|
||||
if (status === undefined) {
|
||||
showError('无法连接到服务器,请检查后端服务是否运行')
|
||||
} else if (error.response.status === 401) {
|
||||
} else if (status === 401) {
|
||||
showError('认证失败,请重新登录')
|
||||
} else {
|
||||
showError(`加载 API 密钥失败:${ error.response?.data?.detail || error.message}`)
|
||||
showError(parseApiError(error, '加载 API 密钥失败'))
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -596,7 +599,7 @@ async function deleteApiKey() {
|
||||
deleting.value = true
|
||||
try {
|
||||
await meApi.deleteApiKey(keyToDelete.value.id)
|
||||
apiKeys.value = apiKeys.value.filter(k => k.id !== keyToDelete.value!.id)
|
||||
apiKeys.value = apiKeys.value.filter(k => k.id !== keyToDelete.value?.id)
|
||||
showDeleteDialog.value = false
|
||||
success('API 密钥已删除')
|
||||
} catch (error) {
|
||||
|
||||
@@ -174,10 +174,12 @@
|
||||
class="flex items-center justify-between gap-3 rounded-lg border border-border bg-muted/30 p-4"
|
||||
>
|
||||
<div class="flex items-center gap-3 min-w-0 flex-1">
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div
|
||||
class="oauth-icon shrink-0"
|
||||
v-html="getOAuthIcon(link.provider_type)"
|
||||
/>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<div class="min-w-0">
|
||||
<div class="text-sm font-medium truncate">
|
||||
{{ link.display_name }}
|
||||
@@ -204,10 +206,12 @@
|
||||
class="flex items-center justify-between gap-3 rounded-lg border border-dashed border-border p-4 hover:border-primary/50 transition-colors"
|
||||
>
|
||||
<div class="flex items-center gap-3 min-w-0 flex-1">
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div
|
||||
class="oauth-icon shrink-0"
|
||||
v-html="getOAuthIcon(p.provider_type)"
|
||||
/>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<div class="min-w-0">
|
||||
<div class="text-sm font-medium truncate">
|
||||
{{ p.display_name }}
|
||||
@@ -462,7 +466,7 @@ import { useToast } from '@/composables/useToast'
|
||||
import { formatCurrency } from '@/utils/format'
|
||||
import { getApiUrl } from '@/utils/url'
|
||||
import { log } from '@/utils/logger'
|
||||
import { getErrorMessage } from '@/types/api-error'
|
||||
import { getErrorMessage, getErrorStatus } from '@/types/api-error'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const route = useRoute()
|
||||
@@ -601,8 +605,8 @@ async function loadOAuthBindings() {
|
||||
])
|
||||
oauthLinks.value = links
|
||||
bindableProviders.value = providers
|
||||
} catch (err: any) {
|
||||
if (err?.response?.status === 503) {
|
||||
} catch (err: unknown) {
|
||||
if (getErrorStatus(err) === 503) {
|
||||
oauthUnavailable.value = true
|
||||
return
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ interface Props {
|
||||
function getModelUserConfigurableCapabilities(): CapabilityDefinition[] {
|
||||
if (!props.model?.supported_capabilities || !props.userConfigurableCapabilities) return []
|
||||
return props.userConfigurableCapabilities.filter(cap =>
|
||||
props.model!.supported_capabilities!.includes(cap.name)
|
||||
props.model?.supported_capabilities?.includes(cap.name)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user