feat: 用户导出支持独立余额Key,新增系统版本接口

- 用户导出/导入支持独立余额 Key (standalone_keys)
- API Key 导出增加 expires_at 字段
- 新增 /api/admin/system/version 接口获取版本信息
- 前端系统设置页面显示当前版本
- 移除导入对话框中多余的 bg-muted 背景样式
This commit is contained in:
fawney19
2026-01-05 18:18:45 +08:00
parent 35e29d46bd
commit bfa0a26d41
3 changed files with 237 additions and 80 deletions

View File

@@ -13,6 +13,7 @@ export interface UsersExportData {
version: string
exported_at: string
users: UserExport[]
standalone_keys?: StandaloneKeyExport[]
}
export interface UserExport {
@@ -46,11 +47,15 @@ export interface UserApiKeyExport {
concurrent_limit?: number | null
force_capabilities?: any
is_active: boolean
expires_at?: string | null
auto_delete_on_expiry?: boolean
total_requests?: number
total_cost_usd?: number
}
// 独立余额 Key 导出结构(与 UserApiKeyExport 相同,但不包含 is_standalone
export type StandaloneKeyExport = Omit<UserApiKeyExport, 'is_standalone'>
export interface GlobalModelExport {
name: string
display_name: string
@@ -189,6 +194,7 @@ export interface UsersImportResponse {
stats: {
users: { created: number; updated: number; skipped: number }
api_keys: { created: number; skipped: number }
standalone_keys?: { created: number; skipped: number }
errors: string[]
}
}
@@ -473,5 +479,13 @@ export const adminApi = {
`/api/admin/system/email/templates/${templateType}/reset`
)
return response.data
},
// 获取系统版本信息
async getSystemVersion(): Promise<{ version: string }> {
const response = await apiClient.get<{ version: string }>(
'/api/admin/system/version'
)
return response.data
}
}

View File

@@ -464,6 +464,30 @@
</div>
</div>
</CardSection>
<!-- 系统版本信息 -->
<CardSection
title="系统信息"
description="当前系统版本和构建信息"
>
<div class="flex items-center gap-4">
<div class="flex items-center gap-2">
<Label class="text-sm font-medium text-muted-foreground">版本:</Label>
<span
v-if="systemVersion"
class="text-sm font-mono"
>
{{ systemVersion }}
</span>
<span
v-else
class="text-sm text-muted-foreground"
>
加载中...
</span>
</div>
</div>
</CardSection>
</div>
<!-- 导入配置对话框 -->
@@ -475,7 +499,7 @@
<div class="space-y-4">
<div
v-if="importPreview"
class="p-3 bg-muted rounded-lg text-sm"
class="text-sm"
>
<p class="font-medium mb-2">
配置预览
@@ -557,7 +581,7 @@
class="space-y-4"
>
<div class="grid grid-cols-2 gap-4 text-sm">
<div class="p-3 bg-muted rounded-lg">
<div>
<p class="font-medium">
全局模型
</p>
@@ -567,7 +591,7 @@
跳过: {{ importResult.stats.global_models.skipped }}
</p>
</div>
<div class="p-3 bg-muted rounded-lg">
<div>
<p class="font-medium">
提供商
</p>
@@ -577,7 +601,7 @@
跳过: {{ importResult.stats.providers.skipped }}
</p>
</div>
<div class="p-3 bg-muted rounded-lg">
<div>
<p class="font-medium">
端点
</p>
@@ -587,7 +611,7 @@
跳过: {{ importResult.stats.endpoints.skipped }}
</p>
</div>
<div class="p-3 bg-muted rounded-lg">
<div>
<p class="font-medium">
API Keys
</p>
@@ -596,7 +620,7 @@
跳过: {{ importResult.stats.keys.skipped }}
</p>
</div>
<div class="p-3 bg-muted rounded-lg col-span-2">
<div class="col-span-2">
<p class="font-medium">
模型配置
</p>
@@ -642,7 +666,7 @@
<div class="space-y-4">
<div
v-if="importUsersPreview"
class="p-3 bg-muted rounded-lg text-sm"
class="text-sm"
>
<p class="font-medium mb-2">
数据预览
@@ -652,6 +676,9 @@
<li>
API Keys: {{ importUsersPreview.users?.reduce((sum: number, u: any) => sum + (u.api_keys?.length || 0), 0) }}
</li>
<li v-if="importUsersPreview.standalone_keys?.length">
独立余额 Keys: {{ importUsersPreview.standalone_keys.length }}
</li>
</ul>
</div>
@@ -720,7 +747,7 @@
class="space-y-4"
>
<div class="grid grid-cols-2 gap-4 text-sm">
<div class="p-3 bg-muted rounded-lg">
<div>
<p class="font-medium">
用户
</p>
@@ -730,7 +757,7 @@
跳过: {{ importUsersResult.stats.users.skipped }}
</p>
</div>
<div class="p-3 bg-muted rounded-lg">
<div>
<p class="font-medium">
API Keys
</p>
@@ -739,6 +766,18 @@
跳过: {{ importUsersResult.stats.api_keys.skipped }}
</p>
</div>
<div
v-if="importUsersResult.stats.standalone_keys"
class="col-span-2"
>
<p class="font-medium">
独立余额 Keys
</p>
<p class="text-muted-foreground">
创建: {{ importUsersResult.stats.standalone_keys.created }},
跳过: {{ importUsersResult.stats.standalone_keys.skipped }}
</p>
</div>
</div>
<div
@@ -839,6 +878,9 @@ const importUsersResult = ref<UsersImportResponse | null>(null)
const usersMergeMode = ref<'skip' | 'overwrite' | 'error'>('skip')
const usersMergeModeSelectOpen = ref(false)
// 系统版本信息
const systemVersion = ref<string>('')
const systemConfig = ref<SystemConfig>({
// 基础配置
default_user_quota_usd: 10.0,
@@ -890,9 +932,21 @@ const sensitiveHeadersStr = computed({
})
onMounted(async () => {
await loadSystemConfig()
await Promise.all([
loadSystemConfig(),
loadSystemVersion()
])
})
async function loadSystemVersion() {
try {
const data = await adminApi.getSystemVersion()
systemVersion.value = data.version
} catch (err) {
log.error('加载系统版本失败:', err)
}
}
async function loadSystemConfig() {
try {
const configs = [
@@ -1178,12 +1232,6 @@ function handleUsersFileSelect(event: Event) {
const content = e.target?.result as string
const data = JSON.parse(content) as UsersExportData
// 验证版本
if (data.version !== '1.0') {
error(`不支持的配置版本: ${data.version}`)
return
}
importUsersPreview.value = data
usersMergeMode.value = 'skip'
importUsersDialogOpen.value = true