feat(keys): add api key concurrency stats

This commit is contained in:
Bestony@Homelab
2026-07-02 15:58:29 +08:00
parent 7dc7cfce1d
commit 089a7b7fae
20 changed files with 569 additions and 59 deletions
+1
View File
@@ -743,6 +743,7 @@ export default {
deleteConfirmMessage: "Are you sure you want to delete '{name}'? This action cannot be undone.",
apiKey: 'API Key',
group: 'Group',
currentConcurrency: 'Current Concurrency',
noGroup: 'No group',
searchGroup: 'Search groups...',
noGroupFound: 'No groups found',
+1
View File
@@ -742,6 +742,7 @@ export default {
deleteConfirmMessage: "确定要删除 '{name}' 吗?此操作无法撤销。",
apiKey: 'API 密钥',
group: '分组',
currentConcurrency: '当前并发',
noGroup: '无分组',
searchGroup: '搜索分组...',
noGroupFound: '未找到匹配的分组',
+1
View File
@@ -577,6 +577,7 @@ export interface ApiKey {
expires_at: string | null // Expiration time (null = never expires)
created_at: string
updated_at: string
current_concurrency: number
group?: Group
rate_limit_5h: number
rate_limit_1d: number
+14
View File
@@ -166,6 +166,19 @@
</div>
</template>
<template #cell-current_concurrency="{ value }">
<span
:class="[
'inline-flex min-w-8 items-center justify-center rounded px-2 py-1 text-sm font-semibold tabular-nums',
(value ?? 0) > 0
? 'bg-emerald-50 text-emerald-700 ring-1 ring-emerald-200 dark:bg-emerald-900/25 dark:text-emerald-300 dark:ring-emerald-800'
: 'bg-gray-100 text-gray-500 dark:bg-dark-700 dark:text-dark-400'
]"
>
{{ value ?? 0 }}
</span>
</template>
<template #cell-usage="{ row }">
<div class="text-sm">
<div class="flex items-center gap-1.5">
@@ -1135,6 +1148,7 @@ const allColumns = computed<Column[]>(() => [
{ key: 'name', label: t('common.name'), sortable: true },
{ key: 'key', label: t('keys.apiKey'), sortable: false },
{ key: 'group', label: t('keys.group'), sortable: false },
{ key: 'current_concurrency', label: t('keys.currentConcurrency'), sortable: false },
{ key: 'usage', label: t('keys.usage'), sortable: false },
{ key: 'rate_limit', label: t('keys.rateLimitColumn'), sortable: false },
{ key: 'expires_at', label: t('keys.expiresAt'), sortable: true },
@@ -42,6 +42,7 @@ const messages: Record<string, string> = {
'keys.created': 'Created',
'keys.expiresAt': 'Expires',
'keys.group': 'Group',
'keys.currentConcurrency': 'Current Concurrency',
'keys.lastUsedAt': 'Last Used',
'keys.rateLimitColumn': 'Rate Limit',
'keys.searchPlaceholder': 'Search name or key...',
@@ -117,6 +118,7 @@ const createApiKey = (): ApiKey => ({
expires_at: null,
created_at: '2026-06-27T00:00:00Z',
updated_at: '2026-06-27T00:00:00Z',
current_concurrency: 3,
rate_limit_5h: 0,
rate_limit_1d: 0,
rate_limit_7d: 0,
@@ -154,6 +156,9 @@ const DataTableStub = {
<div data-test="columns">{{ columns.map((col) => col.key).join(',') }}</div>
<div v-for="row in data" :key="row.id">
<slot name="cell-name" :value="row.name" :row="row" />
<div data-test="current-concurrency">
<slot name="cell-current_concurrency" :value="row.current_concurrency" :row="row" />
</div>
</div>
<slot name="empty" />
</div>
@@ -251,6 +256,7 @@ describe('user KeysView column settings', () => {
'name',
'key',
'group',
'current_concurrency',
'usage',
'expires_at',
'status',
@@ -282,6 +288,7 @@ describe('user KeysView column settings', () => {
expect(visibleColumnKeys(wrapper)).toEqual([
'name',
'key',
'current_concurrency',
'usage',
'rate_limit',
'expires_at',
@@ -299,8 +306,15 @@ describe('user KeysView column settings', () => {
const columnMenuText = wrapper.text()
expect(columnMenuText).toContain('API Key')
expect(columnMenuText).toContain('Current Concurrency')
expect(columnMenuText).toContain('Rate Limit')
expect(columnMenuText).not.toContain('Name')
expect(columnMenuText).not.toContain('Actions')
})
it('renders the current concurrency value', async () => {
const wrapper = await mountView()
expect(wrapper.get('[data-test="current-concurrency"]').text()).toBe('3')
})
})