mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-04-19 09:58:38 +00:00
feat: 多密钥管理新增针对单个密钥的删除操作
This commit is contained in:
@@ -1101,8 +1101,8 @@ func CopyChannel(c *gin.Context) {
|
|||||||
// MultiKeyManageRequest represents the request for multi-key management operations
|
// MultiKeyManageRequest represents the request for multi-key management operations
|
||||||
type MultiKeyManageRequest struct {
|
type MultiKeyManageRequest struct {
|
||||||
ChannelId int `json:"channel_id"`
|
ChannelId int `json:"channel_id"`
|
||||||
Action string `json:"action"` // "disable_key", "enable_key", "delete_disabled_keys", "get_key_status"
|
Action string `json:"action"` // "disable_key", "enable_key", "delete_key", "delete_disabled_keys", "get_key_status"
|
||||||
KeyIndex *int `json:"key_index,omitempty"` // for disable_key and enable_key actions
|
KeyIndex *int `json:"key_index,omitempty"` // for disable_key, enable_key, and delete_key actions
|
||||||
Page int `json:"page,omitempty"` // for get_key_status pagination
|
Page int `json:"page,omitempty"` // for get_key_status pagination
|
||||||
PageSize int `json:"page_size,omitempty"` // for get_key_status pagination
|
PageSize int `json:"page_size,omitempty"` // for get_key_status pagination
|
||||||
Status *int `json:"status,omitempty"` // for get_key_status filtering: 1=enabled, 2=manual_disabled, 3=auto_disabled, nil=all
|
Status *int `json:"status,omitempty"` // for get_key_status filtering: 1=enabled, 2=manual_disabled, 3=auto_disabled, nil=all
|
||||||
@@ -1430,6 +1430,86 @@ func ManageMultiKeys(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
|
||||||
|
case "delete_key":
|
||||||
|
if request.KeyIndex == nil {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": "未指定要删除的密钥索引",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keyIndex := *request.KeyIndex
|
||||||
|
if keyIndex < 0 || keyIndex >= channel.ChannelInfo.MultiKeySize {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": "密钥索引超出范围",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := channel.GetKeys()
|
||||||
|
var remainingKeys []string
|
||||||
|
var newStatusList = make(map[int]int)
|
||||||
|
var newDisabledTime = make(map[int]int64)
|
||||||
|
var newDisabledReason = make(map[int]string)
|
||||||
|
|
||||||
|
newIndex := 0
|
||||||
|
for i, key := range keys {
|
||||||
|
// 跳过要删除的密钥
|
||||||
|
if i == keyIndex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingKeys = append(remainingKeys, key)
|
||||||
|
|
||||||
|
// 保留其他密钥的状态信息,重新索引
|
||||||
|
if channel.ChannelInfo.MultiKeyStatusList != nil {
|
||||||
|
if status, exists := channel.ChannelInfo.MultiKeyStatusList[i]; exists && status != 1 {
|
||||||
|
newStatusList[newIndex] = status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if channel.ChannelInfo.MultiKeyDisabledTime != nil {
|
||||||
|
if t, exists := channel.ChannelInfo.MultiKeyDisabledTime[i]; exists {
|
||||||
|
newDisabledTime[newIndex] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if channel.ChannelInfo.MultiKeyDisabledReason != nil {
|
||||||
|
if r, exists := channel.ChannelInfo.MultiKeyDisabledReason[i]; exists {
|
||||||
|
newDisabledReason[newIndex] = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(remainingKeys) == 0 {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": "不能删除最后一个密钥",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update channel with remaining keys
|
||||||
|
channel.Key = strings.Join(remainingKeys, "\n")
|
||||||
|
channel.ChannelInfo.MultiKeySize = len(remainingKeys)
|
||||||
|
channel.ChannelInfo.MultiKeyStatusList = newStatusList
|
||||||
|
channel.ChannelInfo.MultiKeyDisabledTime = newDisabledTime
|
||||||
|
channel.ChannelInfo.MultiKeyDisabledReason = newDisabledReason
|
||||||
|
|
||||||
|
err = channel.Update()
|
||||||
|
if err != nil {
|
||||||
|
common.ApiError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
model.InitChannelCache()
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"message": "密钥已删除",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
|
||||||
case "delete_disabled_keys":
|
case "delete_disabled_keys":
|
||||||
keys := channel.GetKeys()
|
keys := channel.GetKeys()
|
||||||
var remainingKeys []string
|
var remainingKeys []string
|
||||||
|
|||||||
@@ -247,6 +247,32 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Delete a specific key
|
||||||
|
const handleDeleteKey = async (keyIndex) => {
|
||||||
|
const operationId = `delete_${keyIndex}`;
|
||||||
|
setOperationLoading((prev) => ({ ...prev, [operationId]: true }));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await API.post('/api/channel/multi_key/manage', {
|
||||||
|
channel_id: channel.id,
|
||||||
|
action: 'delete_key',
|
||||||
|
key_index: keyIndex,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.data.success) {
|
||||||
|
showSuccess(t('密钥已删除'));
|
||||||
|
await loadKeyStatus(currentPage, pageSize); // Reload current page
|
||||||
|
onRefresh && onRefresh(); // Refresh parent component
|
||||||
|
} else {
|
||||||
|
showError(res.data.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showError(t('删除密钥失败'));
|
||||||
|
} finally {
|
||||||
|
setOperationLoading((prev) => ({ ...prev, [operationId]: false }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Handle page change
|
// Handle page change
|
||||||
const handlePageChange = (page) => {
|
const handlePageChange = (page) => {
|
||||||
setCurrentPage(page);
|
setCurrentPage(page);
|
||||||
@@ -384,7 +410,7 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
|
|||||||
title: t('操作'),
|
title: t('操作'),
|
||||||
key: 'action',
|
key: 'action',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
width: 100,
|
width: 150,
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space>
|
<Space>
|
||||||
{record.status === 1 ? (
|
{record.status === 1 ? (
|
||||||
@@ -406,6 +432,21 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
|
|||||||
{t('启用')}
|
{t('启用')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
<Popconfirm
|
||||||
|
title={t('确定要删除此密钥吗?')}
|
||||||
|
content={t('此操作不可撤销,将永久删除该密钥')}
|
||||||
|
onConfirm={() => handleDeleteKey(record.index)}
|
||||||
|
okType={'danger'}
|
||||||
|
position={'topRight'}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type='danger'
|
||||||
|
size='small'
|
||||||
|
loading={operationLoading[`delete_${record.index}`]}
|
||||||
|
>
|
||||||
|
{t('删除')}
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1889,6 +1889,10 @@
|
|||||||
"确定要删除所有已自动禁用的密钥吗?": "Are you sure you want to delete all automatically disabled keys?",
|
"确定要删除所有已自动禁用的密钥吗?": "Are you sure you want to delete all automatically disabled keys?",
|
||||||
"此操作不可撤销,将永久删除已自动禁用的密钥": "This operation cannot be undone, and all automatically disabled keys will be permanently deleted.",
|
"此操作不可撤销,将永久删除已自动禁用的密钥": "This operation cannot be undone, and all automatically disabled keys will be permanently deleted.",
|
||||||
"删除自动禁用密钥": "Delete auto disabled keys",
|
"删除自动禁用密钥": "Delete auto disabled keys",
|
||||||
|
"确定要删除此密钥吗?": "Are you sure you want to delete this key?",
|
||||||
|
"此操作不可撤销,将永久删除该密钥": "This operation cannot be undone, and the key will be permanently deleted.",
|
||||||
|
"密钥已删除": "Key has been deleted",
|
||||||
|
"删除密钥失败": "Failed to delete key",
|
||||||
"图标": "Icon",
|
"图标": "Icon",
|
||||||
"模型图标": "Model icon",
|
"模型图标": "Model icon",
|
||||||
"请输入图标名称": "Please enter the icon name",
|
"请输入图标名称": "Please enter the icon name",
|
||||||
|
|||||||
Reference in New Issue
Block a user