/* Copyright (C) 2025 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Modal, Button, Table, Tag, Typography, Space, Tooltip, Popconfirm, Empty, Spin, Banner } from '@douyinfe/semi-ui'; import { IconRefresh, IconDelete, IconClose, IconSave, IconSetting } from '@douyinfe/semi-icons'; import { API, showError, showSuccess, timestamp2string } from '../../../../helpers/index.js'; const { Text, Title } = Typography; const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => { const { t } = useTranslation(); const [loading, setLoading] = useState(false); const [keyStatusList, setKeyStatusList] = useState([]); const [operationLoading, setOperationLoading] = useState({}); // Load key status data const loadKeyStatus = async () => { if (!channel?.id) return; setLoading(true); try { const res = await API.post('/api/channel/multi_key/manage', { channel_id: channel.id, action: 'get_key_status' }); if (res.data.success) { setKeyStatusList(res.data.data.keys || []); } else { showError(res.data.message); } } catch (error) { showError(t('获取密钥状态失败')); } finally { setLoading(false); } }; // Disable a specific key const handleDisableKey = async (keyIndex) => { const operationId = `disable_${keyIndex}`; setOperationLoading(prev => ({ ...prev, [operationId]: true })); try { const res = await API.post('/api/channel/multi_key/manage', { channel_id: channel.id, action: 'disable_key', key_index: keyIndex }); if (res.data.success) { showSuccess(t('密钥已禁用')); await loadKeyStatus(); // Reload data onRefresh && onRefresh(); // Refresh parent component } else { showError(res.data.message); } } catch (error) { showError(t('禁用密钥失败')); } finally { setOperationLoading(prev => ({ ...prev, [operationId]: false })); } }; // Enable a specific key const handleEnableKey = async (keyIndex) => { const operationId = `enable_${keyIndex}`; setOperationLoading(prev => ({ ...prev, [operationId]: true })); try { const res = await API.post('/api/channel/multi_key/manage', { channel_id: channel.id, action: 'enable_key', key_index: keyIndex }); if (res.data.success) { showSuccess(t('密钥已启用')); await loadKeyStatus(); // Reload data onRefresh && onRefresh(); // Refresh parent component } else { showError(res.data.message); } } catch (error) { showError(t('启用密钥失败')); } finally { setOperationLoading(prev => ({ ...prev, [operationId]: false })); } }; // Delete all disabled keys const handleDeleteDisabledKeys = async () => { setOperationLoading(prev => ({ ...prev, delete_disabled: true })); try { const res = await API.post('/api/channel/multi_key/manage', { channel_id: channel.id, action: 'delete_disabled_keys' }); if (res.data.success) { showSuccess(res.data.message); await loadKeyStatus(); // Reload data onRefresh && onRefresh(); // Refresh parent component } else { showError(res.data.message); } } catch (error) { showError(t('删除禁用密钥失败')); } finally { setOperationLoading(prev => ({ ...prev, delete_disabled: false })); } }; // Effect to load data when modal opens useEffect(() => { if (visible && channel?.id) { loadKeyStatus(); } }, [visible, channel?.id]); // Get status tag component const renderStatusTag = (status) => { switch (status) { case 1: return {t('已启用')}; case 2: return {t('已禁用')}; case 3: return {t('自动禁用')}; default: return {t('未知状态')}; } }; // Table columns definition const columns = [ { title: t('索引'), dataIndex: 'index', render: (text) => `#${text}`, }, { title: t('密钥预览'), dataIndex: 'key_preview', render: (text) => ( {text} ), }, { title: t('状态'), dataIndex: 'status', width: 100, render: (status) => renderStatusTag(status), }, { title: t('禁用原因'), dataIndex: 'reason', width: 220, render: (reason, record) => { if (record.status === 1 || !reason) { return -; } return ( {reason} ); }, }, { title: t('禁用时间'), dataIndex: 'disabled_time', width: 150, render: (time, record) => { if (record.status === 1 || !time) { return -; } return ( {timestamp2string(time)} ); }, }, { title: t('操作'), key: 'action', width: 120, render: (_, record) => ( {record.status === 1 ? ( handleDisableKey(record.index)} > ) : ( handleEnableKey(record.index)} > )} ), }, ]; // Calculate statistics const enabledCount = keyStatusList.filter(key => key.status === 1).length; const manualDisabledCount = keyStatusList.filter(key => key.status === 2).length; const autoDisabledCount = keyStatusList.filter(key => key.status === 3).length; const totalCount = keyStatusList.length; return ( {t('多密钥管理')} - {channel?.name} } visible={visible} onCancel={onCancel} width={800} height={600} footer={ {autoDisabledCount > 0 && ( )} } >
{/* Statistics Banner */} {t('总共 {{total}} 个密钥,{{enabled}} 个已启用,{{manual}} 个手动禁用,{{auto}} 个自动禁用', { total: totalCount, enabled: enabledCount, manual: manualDisabledCount, auto: autoDisabledCount })} {channel?.channel_info?.multi_key_mode && (
{t('多密钥模式')}: {channel.channel_info.multi_key_mode === 'random' ? t('随机') : t('轮询')}
)}
} /> {/* Key Status Table */} {keyStatusList.length > 0 ? ( ) : ( !loading && ( ) )} ); }; export default MultiKeyManageModal;