Add admin user key IP whitelist UI

This commit is contained in:
RWDai
2026-05-18 20:48:28 +08:00
parent 460cd63d3a
commit c6ee558180
2 changed files with 39 additions and 0 deletions

View File

@@ -221,6 +221,7 @@ export interface ApiKey {
feature_settings?: FeatureSettings | null
rate_limit?: number | null // 普通Key: 0 = 不限制,历史 null 视为跟随系统默认
concurrent_limit?: number | null // 普通Key: 0 = 不限制并发,历史 null 兼容
allowed_ips?: string[] | null
total_requests?: number // 总请求数
total_cost_usd?: number // 总费用
}
@@ -229,6 +230,7 @@ export interface UpsertUserApiKeyRequest {
name?: string
rate_limit?: number | null
concurrent_limit?: number | null
allowed_ips?: string[] | null
feature_settings?: FeatureSettings | null
}

View File

@@ -1089,6 +1089,9 @@
<code class="text-xs font-mono text-muted-foreground">
{{ apiKey.key_display || '****' }}
</code>
<span class="text-xs text-muted-foreground">
IP 白名单{{ formatAllowedIps(apiKey.allowed_ips) }}
</span>
<button
class="p-0.5 hover:bg-muted rounded transition-colors"
title="复制完整密钥"
@@ -1258,6 +1261,21 @@
{{ editingUserApiKey ? '留空表示保持当前值,填 0 表示不限并发' : '留空表示不限并发,填 0 也表示不限并发' }}
</p>
</div>
<div class="space-y-2">
<Label
for="admin-user-key-allowed-ips"
class="text-sm font-medium"
>IP 白名单</Label>
<Input
id="admin-user-key-allowed-ips"
v-model="userApiKeyForm.allowed_ips_text"
class="h-10"
placeholder="例如203.0.113.10, 10.0.0.0/24"
/>
<p class="text-xs text-muted-foreground">
留空表示不限制来源 IP支持 IP 或 CIDR多个值用英文逗号分隔
</p>
</div>
<div class="rounded-lg border border-border bg-muted/30 p-3 space-y-3">
<div class="flex items-center justify-between gap-3">
@@ -1562,6 +1580,7 @@ const userApiKeyForm = ref({
name: '',
rate_limit: undefined as number | undefined,
concurrent_limit: undefined as number | undefined,
allowed_ips_text: '',
chat_pii_redaction_enabled: false,
chat_pii_redaction_placeholder_notice: true,
})
@@ -1869,6 +1888,18 @@ function formatConcurrentLimitSimple(concurrentLimit?: number | null): string {
return `${concurrentLimit} 并发`
}
function formatAllowedIps(allowedIps?: string[] | null): string {
return allowedIps && allowedIps.length > 0 ? allowedIps.join(', ') : '不限制'
}
function parseAllowedIpsInput(value: string): string[] | null {
const items = value
.split(',')
.map((item) => item.trim())
.filter(Boolean)
return items.length > 0 ? items : null
}
function formatUserEffectiveRateLimitSource(user: User): string {
const source = user.effective_policy?.rate_limit
if (!source) return ''
@@ -2079,6 +2110,7 @@ function openCreateUserApiKeyDialog() {
name: `Key-${new Date().toISOString().split('T')[0]}`,
rate_limit: undefined,
concurrent_limit: undefined,
allowed_ips_text: '',
chat_pii_redaction_enabled: redactionFeature.enabled,
chat_pii_redaction_placeholder_notice: redactionFeature.inject_model_instruction,
}
@@ -2093,6 +2125,7 @@ function openEditUserApiKeyDialog(apiKey: ApiKey) {
name: apiKey.name || '',
rate_limit: apiKey.rate_limit ?? undefined,
concurrent_limit: apiKey.concurrent_limit ?? undefined,
allowed_ips_text: apiKey.allowed_ips?.join(', ') ?? '',
chat_pii_redaction_enabled: redactionFeature.enabled,
chat_pii_redaction_placeholder_notice: redactionFeature.inject_model_instruction,
}
@@ -2106,6 +2139,7 @@ function closeUserApiKeyFormDialog() {
name: '',
rate_limit: undefined,
concurrent_limit: undefined,
allowed_ips_text: '',
chat_pii_redaction_enabled: false,
chat_pii_redaction_placeholder_notice: true,
}
@@ -2120,11 +2154,13 @@ async function submitUserApiKeyForm() {
creatingApiKey.value = true
try {
const allowedIps = parseAllowedIpsInput(userApiKeyForm.value.allowed_ips_text)
if (editingUserApiKey.value) {
await usersStore.updateApiKey(selectedUser.value.id, editingUserApiKey.value.id, {
name: userApiKeyForm.value.name,
rate_limit: userApiKeyForm.value.rate_limit ?? 0,
concurrent_limit: userApiKeyForm.value.concurrent_limit,
allowed_ips: allowedIps,
feature_settings: mergeChatPiiRedactionFeatureSettings(editingUserApiKey.value.feature_settings, {
enabled: userApiKeyForm.value.chat_pii_redaction_enabled,
inject_model_instruction: userApiKeyForm.value.chat_pii_redaction_placeholder_notice,
@@ -2136,6 +2172,7 @@ async function submitUserApiKeyForm() {
name: userApiKeyForm.value.name,
rate_limit: userApiKeyForm.value.rate_limit ?? 0,
concurrent_limit: userApiKeyForm.value.concurrent_limit,
allowed_ips: allowedIps,
feature_settings: mergeChatPiiRedactionFeatureSettings(null, {
enabled: userApiKeyForm.value.chat_pii_redaction_enabled,
inject_model_instruction: userApiKeyForm.value.chat_pii_redaction_placeholder_notice,