mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
feat: 删除端点时自动清理关联密钥的 API 格式配置
- 端点删除添加确认弹窗,提示用户关联密钥将受影响 - 密钥表单添加 canSave 验证,禁用按钮替代弹窗提示 - 后端删除端点时自动从关联密钥移除对应 API 格式
This commit is contained in:
@@ -339,6 +339,19 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<!-- 删除端点确认弹窗 -->
|
||||||
|
<AlertDialog
|
||||||
|
:model-value="deleteConfirmOpen"
|
||||||
|
title="删除端点"
|
||||||
|
:description="deleteConfirmDescription"
|
||||||
|
confirm-text="删除"
|
||||||
|
cancel-text="取消"
|
||||||
|
type="danger"
|
||||||
|
@update:model-value="deleteConfirmOpen = $event"
|
||||||
|
@confirm="confirmDeleteEndpoint"
|
||||||
|
@cancel="deleteConfirmOpen = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -360,6 +373,7 @@ import {
|
|||||||
import { Settings, Edit, Trash2, Check, X, Power, ChevronRight, Plus, ArrowRight } from 'lucide-vue-next'
|
import { Settings, Edit, Trash2, Check, X, Power, ChevronRight, Plus, ArrowRight } from 'lucide-vue-next'
|
||||||
import { useToast } from '@/composables/useToast'
|
import { useToast } from '@/composables/useToast'
|
||||||
import { log } from '@/utils/logger'
|
import { log } from '@/utils/logger'
|
||||||
|
import AlertDialog from '@/components/common/AlertDialog.vue'
|
||||||
import {
|
import {
|
||||||
createEndpoint,
|
createEndpoint,
|
||||||
updateEndpoint,
|
updateEndpoint,
|
||||||
@@ -404,6 +418,10 @@ const deletingEndpointId = ref<string | null>(null)
|
|||||||
const togglingEndpointId = ref<string | null>(null)
|
const togglingEndpointId = ref<string | null>(null)
|
||||||
const formatSelectOpen = ref(false)
|
const formatSelectOpen = ref(false)
|
||||||
|
|
||||||
|
// 删除确认弹窗状态
|
||||||
|
const deleteConfirmOpen = ref(false)
|
||||||
|
const endpointToDelete = ref<ProviderEndpoint | null>(null)
|
||||||
|
|
||||||
// 请求头规则编辑状态
|
// 请求头规则编辑状态
|
||||||
const editingRules = ref<EditableRule[]>([])
|
const editingRules = ref<EditableRule[]>([])
|
||||||
const rulesExpanded = ref(false)
|
const rulesExpanded = ref(false)
|
||||||
@@ -441,6 +459,13 @@ const availableFormats = computed(() => {
|
|||||||
return apiFormats.value.filter(f => !existingFormats.includes(f.value))
|
return apiFormats.value.filter(f => !existingFormats.includes(f.value))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 删除确认弹窗描述
|
||||||
|
const deleteConfirmDescription = computed(() => {
|
||||||
|
if (!endpointToDelete.value) return ''
|
||||||
|
const formatLabel = API_FORMAT_LABELS[endpointToDelete.value.api_format] || endpointToDelete.value.api_format
|
||||||
|
return `确定要删除 ${formatLabel} 端点吗?关联密钥将移除对该 API 格式的支持。`
|
||||||
|
})
|
||||||
|
|
||||||
// 获取指定 API 格式的默认路径
|
// 获取指定 API 格式的默认路径
|
||||||
function getDefaultPath(apiFormat: string): string {
|
function getDefaultPath(apiFormat: string): string {
|
||||||
const format = apiFormats.value.find(f => f.value === apiFormat)
|
const format = apiFormats.value.find(f => f.value === apiFormat)
|
||||||
@@ -721,9 +746,20 @@ async function handleToggleEndpoint(endpoint: ProviderEndpoint) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除端点
|
// 删除端点 - 打开确认弹窗
|
||||||
async function handleDeleteEndpoint(endpoint: ProviderEndpoint) {
|
function handleDeleteEndpoint(endpoint: ProviderEndpoint) {
|
||||||
|
endpointToDelete.value = endpoint
|
||||||
|
deleteConfirmOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认删除端点
|
||||||
|
async function confirmDeleteEndpoint() {
|
||||||
|
if (!endpointToDelete.value) return
|
||||||
|
|
||||||
|
const endpoint = endpointToDelete.value
|
||||||
|
deleteConfirmOpen.value = false
|
||||||
deletingEndpointId.value = endpoint.id
|
deletingEndpointId.value = endpoint.id
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteEndpoint(endpoint.id)
|
await deleteEndpoint(endpoint.id)
|
||||||
success(`已删除 ${API_FORMAT_LABELS[endpoint.api_format] || endpoint.api_format} 端点`)
|
success(`已删除 ${API_FORMAT_LABELS[endpoint.api_format] || endpoint.api_format} 端点`)
|
||||||
@@ -732,6 +768,7 @@ async function handleDeleteEndpoint(endpoint: ProviderEndpoint) {
|
|||||||
showError(error.response?.data?.detail || '删除失败', '错误')
|
showError(error.response?.data?.detail || '删除失败', '错误')
|
||||||
} finally {
|
} finally {
|
||||||
deletingEndpointId.value = null
|
deletingEndpointId.value = null
|
||||||
|
endpointToDelete.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -246,7 +246,7 @@
|
|||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
:disabled="saving"
|
:disabled="saving || !canSave"
|
||||||
@click="handleSave"
|
@click="handleSave"
|
||||||
>
|
>
|
||||||
{{ saving ? (isEditMode ? '保存中...' : '添加中...') : (isEditMode ? '保存' : '添加') }}
|
{{ saving ? (isEditMode ? '保存中...' : '添加中...') : (isEditMode ? '保存' : '添加') }}
|
||||||
@@ -309,6 +309,19 @@ const showAutoFetchWarning = computed(() => {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 表单是否可以保存
|
||||||
|
const canSave = computed(() => {
|
||||||
|
// 必须填写密钥名称
|
||||||
|
if (!form.value.name.trim()) return false
|
||||||
|
// 新增模式下必须填写 API 密钥
|
||||||
|
if (!props.editingKey && !form.value.api_key.trim()) return false
|
||||||
|
// 必须至少选择一个 API 格式
|
||||||
|
if (form.value.api_formats.length === 0) return false
|
||||||
|
// API 密钥格式验证(如果有输入)
|
||||||
|
if (form.value.api_key.trim() && form.value.api_key.trim().length < 3) return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
const isOpen = computed(() => props.open)
|
const isOpen = computed(() => props.open)
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const formNonce = ref(createFieldNonce())
|
const formNonce = ref(createFieldNonce())
|
||||||
@@ -359,11 +372,6 @@ function toggleApiFormat(format: string) {
|
|||||||
// 添加格式
|
// 添加格式
|
||||||
form.value.api_formats.push(format)
|
form.value.api_formats.push(format)
|
||||||
} else {
|
} else {
|
||||||
// 移除格式前检查:至少保留一个格式
|
|
||||||
if (form.value.api_formats.length <= 1) {
|
|
||||||
showError('至少需要选择一个 API 格式', '验证失败')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 移除格式,但保留倍率配置(用户可能只是临时取消)
|
// 移除格式,但保留倍率配置(用户可能只是临时取消)
|
||||||
form.value.api_formats.splice(index, 1)
|
form.value.api_formats.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from typing import List, Optional
|
|||||||
from fastapi import APIRouter, Depends, Query, Request
|
from fastapi import APIRouter, Depends, Query, Request
|
||||||
from sqlalchemy import and_, func
|
from sqlalchemy import and_, func
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlalchemy.orm.attributes import flag_modified
|
||||||
|
|
||||||
from src.api.base.admin_adapter import AdminApiAdapter
|
from src.api.base.admin_adapter import AdminApiAdapter
|
||||||
from src.api.base.pipeline import ApiRequestPipeline
|
from src.api.base.pipeline import ApiRequestPipeline
|
||||||
@@ -462,14 +463,22 @@ class AdminDeleteProviderEndpointAdapter(AdminApiAdapter):
|
|||||||
endpoint_format = (
|
endpoint_format = (
|
||||||
endpoint.api_format if isinstance(endpoint.api_format, str) else endpoint.api_format.value
|
endpoint.api_format if isinstance(endpoint.api_format, str) else endpoint.api_format.value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 查询包含该格式的所有 Key,并从 api_formats 中移除该格式
|
||||||
keys = (
|
keys = (
|
||||||
db.query(ProviderAPIKey.api_formats)
|
db.query(ProviderAPIKey)
|
||||||
.filter(ProviderAPIKey.provider_id == endpoint.provider_id)
|
.filter(ProviderAPIKey.provider_id == endpoint.provider_id)
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
affected_keys_count = sum(
|
affected_keys_count = 0
|
||||||
1 for (api_formats,) in keys if endpoint_format in (api_formats or [])
|
for key in keys:
|
||||||
)
|
if key.api_formats and endpoint_format in key.api_formats:
|
||||||
|
affected_keys_count += 1
|
||||||
|
# 移除该格式
|
||||||
|
new_formats = [f for f in key.api_formats if f != endpoint_format]
|
||||||
|
key.api_formats = new_formats if new_formats else []
|
||||||
|
flag_modified(key, 'api_formats')
|
||||||
|
|
||||||
db.delete(endpoint)
|
db.delete(endpoint)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user