mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
Add admin user key IP whitelist UI
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user