mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +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
|
feature_settings?: FeatureSettings | null
|
||||||
rate_limit?: number | null // 普通Key: 0 = 不限制,历史 null 视为跟随系统默认
|
rate_limit?: number | null // 普通Key: 0 = 不限制,历史 null 视为跟随系统默认
|
||||||
concurrent_limit?: number | null // 普通Key: 0 = 不限制并发,历史 null 兼容
|
concurrent_limit?: number | null // 普通Key: 0 = 不限制并发,历史 null 兼容
|
||||||
|
allowed_ips?: string[] | null
|
||||||
total_requests?: number // 总请求数
|
total_requests?: number // 总请求数
|
||||||
total_cost_usd?: number // 总费用
|
total_cost_usd?: number // 总费用
|
||||||
}
|
}
|
||||||
@@ -229,6 +230,7 @@ export interface UpsertUserApiKeyRequest {
|
|||||||
name?: string
|
name?: string
|
||||||
rate_limit?: number | null
|
rate_limit?: number | null
|
||||||
concurrent_limit?: number | null
|
concurrent_limit?: number | null
|
||||||
|
allowed_ips?: string[] | null
|
||||||
feature_settings?: FeatureSettings | null
|
feature_settings?: FeatureSettings | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1089,6 +1089,9 @@
|
|||||||
<code class="text-xs font-mono text-muted-foreground">
|
<code class="text-xs font-mono text-muted-foreground">
|
||||||
{{ apiKey.key_display || '****' }}
|
{{ apiKey.key_display || '****' }}
|
||||||
</code>
|
</code>
|
||||||
|
<span class="text-xs text-muted-foreground">
|
||||||
|
IP 白名单:{{ formatAllowedIps(apiKey.allowed_ips) }}
|
||||||
|
</span>
|
||||||
<button
|
<button
|
||||||
class="p-0.5 hover:bg-muted rounded transition-colors"
|
class="p-0.5 hover:bg-muted rounded transition-colors"
|
||||||
title="复制完整密钥"
|
title="复制完整密钥"
|
||||||
@@ -1258,6 +1261,21 @@
|
|||||||
{{ editingUserApiKey ? '留空表示保持当前值,填 0 表示不限并发' : '留空表示不限并发,填 0 也表示不限并发' }}
|
{{ editingUserApiKey ? '留空表示保持当前值,填 0 表示不限并发' : '留空表示不限并发,填 0 也表示不限并发' }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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="rounded-lg border border-border bg-muted/30 p-3 space-y-3">
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
@@ -1562,6 +1580,7 @@ const userApiKeyForm = ref({
|
|||||||
name: '',
|
name: '',
|
||||||
rate_limit: undefined as number | undefined,
|
rate_limit: undefined as number | undefined,
|
||||||
concurrent_limit: undefined as number | undefined,
|
concurrent_limit: undefined as number | undefined,
|
||||||
|
allowed_ips_text: '',
|
||||||
chat_pii_redaction_enabled: false,
|
chat_pii_redaction_enabled: false,
|
||||||
chat_pii_redaction_placeholder_notice: true,
|
chat_pii_redaction_placeholder_notice: true,
|
||||||
})
|
})
|
||||||
@@ -1869,6 +1888,18 @@ function formatConcurrentLimitSimple(concurrentLimit?: number | null): string {
|
|||||||
return `${concurrentLimit} 并发`
|
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 {
|
function formatUserEffectiveRateLimitSource(user: User): string {
|
||||||
const source = user.effective_policy?.rate_limit
|
const source = user.effective_policy?.rate_limit
|
||||||
if (!source) return ''
|
if (!source) return ''
|
||||||
@@ -2079,6 +2110,7 @@ function openCreateUserApiKeyDialog() {
|
|||||||
name: `Key-${new Date().toISOString().split('T')[0]}`,
|
name: `Key-${new Date().toISOString().split('T')[0]}`,
|
||||||
rate_limit: undefined,
|
rate_limit: undefined,
|
||||||
concurrent_limit: undefined,
|
concurrent_limit: undefined,
|
||||||
|
allowed_ips_text: '',
|
||||||
chat_pii_redaction_enabled: redactionFeature.enabled,
|
chat_pii_redaction_enabled: redactionFeature.enabled,
|
||||||
chat_pii_redaction_placeholder_notice: redactionFeature.inject_model_instruction,
|
chat_pii_redaction_placeholder_notice: redactionFeature.inject_model_instruction,
|
||||||
}
|
}
|
||||||
@@ -2093,6 +2125,7 @@ function openEditUserApiKeyDialog(apiKey: ApiKey) {
|
|||||||
name: apiKey.name || '',
|
name: apiKey.name || '',
|
||||||
rate_limit: apiKey.rate_limit ?? undefined,
|
rate_limit: apiKey.rate_limit ?? undefined,
|
||||||
concurrent_limit: apiKey.concurrent_limit ?? undefined,
|
concurrent_limit: apiKey.concurrent_limit ?? undefined,
|
||||||
|
allowed_ips_text: apiKey.allowed_ips?.join(', ') ?? '',
|
||||||
chat_pii_redaction_enabled: redactionFeature.enabled,
|
chat_pii_redaction_enabled: redactionFeature.enabled,
|
||||||
chat_pii_redaction_placeholder_notice: redactionFeature.inject_model_instruction,
|
chat_pii_redaction_placeholder_notice: redactionFeature.inject_model_instruction,
|
||||||
}
|
}
|
||||||
@@ -2106,6 +2139,7 @@ function closeUserApiKeyFormDialog() {
|
|||||||
name: '',
|
name: '',
|
||||||
rate_limit: undefined,
|
rate_limit: undefined,
|
||||||
concurrent_limit: undefined,
|
concurrent_limit: undefined,
|
||||||
|
allowed_ips_text: '',
|
||||||
chat_pii_redaction_enabled: false,
|
chat_pii_redaction_enabled: false,
|
||||||
chat_pii_redaction_placeholder_notice: true,
|
chat_pii_redaction_placeholder_notice: true,
|
||||||
}
|
}
|
||||||
@@ -2120,11 +2154,13 @@ async function submitUserApiKeyForm() {
|
|||||||
|
|
||||||
creatingApiKey.value = true
|
creatingApiKey.value = true
|
||||||
try {
|
try {
|
||||||
|
const allowedIps = parseAllowedIpsInput(userApiKeyForm.value.allowed_ips_text)
|
||||||
if (editingUserApiKey.value) {
|
if (editingUserApiKey.value) {
|
||||||
await usersStore.updateApiKey(selectedUser.value.id, editingUserApiKey.value.id, {
|
await usersStore.updateApiKey(selectedUser.value.id, editingUserApiKey.value.id, {
|
||||||
name: userApiKeyForm.value.name,
|
name: userApiKeyForm.value.name,
|
||||||
rate_limit: userApiKeyForm.value.rate_limit ?? 0,
|
rate_limit: userApiKeyForm.value.rate_limit ?? 0,
|
||||||
concurrent_limit: userApiKeyForm.value.concurrent_limit,
|
concurrent_limit: userApiKeyForm.value.concurrent_limit,
|
||||||
|
allowed_ips: allowedIps,
|
||||||
feature_settings: mergeChatPiiRedactionFeatureSettings(editingUserApiKey.value.feature_settings, {
|
feature_settings: mergeChatPiiRedactionFeatureSettings(editingUserApiKey.value.feature_settings, {
|
||||||
enabled: userApiKeyForm.value.chat_pii_redaction_enabled,
|
enabled: userApiKeyForm.value.chat_pii_redaction_enabled,
|
||||||
inject_model_instruction: userApiKeyForm.value.chat_pii_redaction_placeholder_notice,
|
inject_model_instruction: userApiKeyForm.value.chat_pii_redaction_placeholder_notice,
|
||||||
@@ -2136,6 +2172,7 @@ async function submitUserApiKeyForm() {
|
|||||||
name: userApiKeyForm.value.name,
|
name: userApiKeyForm.value.name,
|
||||||
rate_limit: userApiKeyForm.value.rate_limit ?? 0,
|
rate_limit: userApiKeyForm.value.rate_limit ?? 0,
|
||||||
concurrent_limit: userApiKeyForm.value.concurrent_limit,
|
concurrent_limit: userApiKeyForm.value.concurrent_limit,
|
||||||
|
allowed_ips: allowedIps,
|
||||||
feature_settings: mergeChatPiiRedactionFeatureSettings(null, {
|
feature_settings: mergeChatPiiRedactionFeatureSettings(null, {
|
||||||
enabled: userApiKeyForm.value.chat_pii_redaction_enabled,
|
enabled: userApiKeyForm.value.chat_pii_redaction_enabled,
|
||||||
inject_model_instruction: userApiKeyForm.value.chat_pii_redaction_placeholder_notice,
|
inject_model_instruction: userApiKeyForm.value.chat_pii_redaction_placeholder_notice,
|
||||||
|
|||||||
Reference in New Issue
Block a user