feat: add multi-key management - #1498
Conversation
|
Caution Review failedThe pull request is closed. WalkthroughThis update introduces comprehensive multi-key management for channels in both backend and frontend. The backend adds new API endpoints, data structures, and logic for managing multiple keys per channel, including enabling, disabling, and deleting keys, as well as tracking reasons and timestamps for key status changes. The frontend incorporates a new modal interface for multi-key management, updates table components to support modal control, and extends hooks and state management accordingly. Changes
Sequence Diagram(s)sequenceDiagram
participant AdminUser as Admin User
participant Frontend as Frontend (Channels Table & Modal)
participant API as Backend API (ManageMultiKeys)
participant DB as Database
AdminUser->>Frontend: Click "Multi-key management" on a channel
Frontend->>API: POST /channel/multi_key/manage (action: get_key_status, channel_id, pagination)
API->>DB: Fetch channel and key status
API->>Frontend: Return key statuses, reasons, times, stats
AdminUser->>Frontend: Click "Disable" or "Enable" on a key
Frontend->>API: POST /channel/multi_key/manage (action: disable_key/enable_key, channel_id, key_index, reason)
API->>DB: Update key status, reason, timestamp
API->>Frontend: Return success/error
AdminUser->>Frontend: Click "Delete auto-disabled keys"
Frontend->>API: POST /channel/multi_key/manage (action: delete_disabled_keys, channel_id)
API->>DB: Remove auto-disabled keys, update channel
API->>Frontend: Return success/error
Frontend->>AdminUser: Update modal UI with latest key statuses
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (2)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 11
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
controller/channel.go(6 hunks)model/channel.go(8 hunks)model/channel_cache.go(1 hunks)router/api-router.go(1 hunks)web/src/components/table/channels/ChannelsColumnDefs.js(3 hunks)web/src/components/table/channels/ChannelsTable.jsx(3 hunks)web/src/components/table/channels/index.jsx(2 hunks)web/src/components/table/channels/modals/MultiKeyManageModal.jsx(1 hunks)web/src/hooks/channels/useChannelsData.js(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
controller/channel.go (4)
model/channel.go (3)
Channel(19-53)ChannelInfo(55-63)GetChannelById(322-337)common/gin.go (1)
ApiError(91-96)common/utils.go (1)
GetTimestamp(192-194)model/channel_cache.go (1)
InitChannelCache(22-87)
router/api-router.go (1)
controller/channel.go (1)
ManageMultiKeys(1084-1368)
model/channel.go (2)
constant/multi_key_mode.go (1)
MultiKeyMode(3-3)common/utils.go (1)
GetTimestamp(192-194)
web/src/components/table/channels/ChannelsColumnDefs.js (1)
web/src/hooks/channels/useChannelsData.js (1)
manageChannel(373-417)
🔇 Additional comments (19)
router/api-router.go (1)
123-123: LGTM!The new multi-key management route is properly integrated under the existing channel group with appropriate admin authentication middleware. The route follows established naming conventions and patterns.
model/channel_cache.go (1)
73-73: LGTM!The method call change from
getKeys()toGetKeys()correctly updates to use the newly exported method, maintaining the same functionality while enabling cross-package access for multi-key management features.web/src/components/table/channels/ChannelsTable.jsx (3)
60-62: LGTM!The new multi-key management props are properly destructured from the input parameters with clear, descriptive names.
85-87: LGTM!The props are correctly passed to the
getChannelsColumnsfunction, enabling the column definitions to access multi-key management functionality.
106-108: LGTM!The new props are properly included in the dependency array, ensuring the columns will re-render when multi-key management state changes.
web/src/components/table/channels/index.jsx (2)
33-33: LGTM!The import is properly placed alongside other modal imports, following the established pattern.
58-63: LGTM!The
MultiKeyManageModalis properly integrated with correct props for visibility control, current channel data, and event handlers. The structure follows the existing modal pattern consistently.web/src/hooks/channels/useChannelsData.js (2)
86-88: LGTM!The new state variables for multi-key management are properly initialized following React best practices and established naming conventions in the codebase.
892-896: LGTM!The multi-key management states are correctly added to the hook's return object, making them available to consuming components with proper state and setter access.
web/src/components/table/channels/modals/MultiKeyManageModal.jsx (1)
328-465: Extensive hardcoded Chinese text in modal UIThe modal component has numerous hardcoded Chinese strings that should use internationalization.
Key areas needing translation:
- Modal title (line 333):
'多密钥管理'→'Multi-key Management'- Button labels (lines 342, 348, 361):
'关闭'→'Close','刷新'→'Refresh', etc.- Confirmation dialogs (lines 352-354)
- Statistics template (line 376)
- Multi-key mode labels (line 386):
'随机'→'Random','轮询'→'Polling'- Pagination text (lines 412-416, 421, 444-446)
- Empty state messages (lines 458-459)
Example fixes:
-<span>{t('多密钥管理')} - {channel?.name}</span> +<span>{t('Multi-key Management')} - {channel?.name}</span> -<Button onClick={onCancel}>{t('关闭')}</Button> +<Button onClick={onCancel}>{t('Close')}</Button> -title={t('确定要删除所有已自动禁用的密钥吗?')} +title={t('Are you sure you want to delete all auto-disabled keys?')}Likely an incorrect or invalid review comment.
model/channel.go (9)
44-44: LGTM: New Settings field added appropriately.The addition of the Settings field follows the existing pattern in the Channel struct and supports the multi-key management functionality.
59-60: LGTM: Multi-key tracking fields properly designed.The new optional fields
MultiKeyDisabledReasonandMultiKeyDisabledTimeare well-designed:
- Use appropriate data types (map[int]string and map[int]int64)
- Include
omitemptyJSON tags to keep responses clean when empty- Follow consistent naming conventions with existing fields
76-76: LGTM: Method exported correctly.The change from
getKeys()toGetKeys()properly exports the method to support external usage while maintaining the same functionality.
107-107: LGTM: Method call updated correctly.The call to the newly exported
GetKeys()method is correctly updated.
534-534: LGTM: Function signature enhanced appropriately.The addition of the
reasonparameter tohandlerMultiKeyUpdatesupports the new requirement to track disable reasons.
535-535: LGTM: Method call updated correctly.The call to the newly exported
GetKeys()method is correctly updated.
553-560: LGTM: Disabled key tracking implementation is solid.The implementation properly:
- Initializes maps when nil to avoid panics
- Records both reason and timestamp for disabled keys
- Uses
common.GetTimestamp()for consistent time tracking- Only tracks disable information when status is not enabled
The logic is thread-safe within the context of the existing
channelStatusLockusage.
583-583: LGTM: Function call updated with reason parameter.The call to
handlerMultiKeyUpdatecorrectly passes thereasonparameter to support the enhanced functionality.
614-614: LGTM: Function call updated with reason parameter.The call to
handlerMultiKeyUpdatecorrectly passes thereasonparameter to support the enhanced functionality.
| "success": false, | ||
| "message": "渠道不存在", | ||
| }) | ||
| return | ||
| } | ||
|
|
||
| if !channel.ChannelInfo.IsMultiKey { | ||
| c.JSON(http.StatusOK, gin.H{ | ||
| "success": false, | ||
| "message": "该渠道不是多密钥模式", | ||
| }) |
There was a problem hiding this comment.
Hardcoded Chinese error messages
Error messages should use internationalization for consistency.
Apply this diff to fix the error messages:
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
- "message": "渠道不存在",
+ "message": "Channel not found",
})
return
}
if !channel.ChannelInfo.IsMultiKey {
c.JSON(http.StatusOK, gin.H{
"success": false,
- "message": "该渠道不是多密钥模式",
+ "message": "This channel is not in multi-key mode",
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "success": false, | |
| "message": "渠道不存在", | |
| }) | |
| return | |
| } | |
| if !channel.ChannelInfo.IsMultiKey { | |
| c.JSON(http.StatusOK, gin.H{ | |
| "success": false, | |
| "message": "该渠道不是多密钥模式", | |
| }) | |
| if err != nil { | |
| c.JSON(http.StatusOK, gin.H{ | |
| "success": false, | |
| "message": "Channel not found", | |
| }) | |
| return | |
| } | |
| if !channel.ChannelInfo.IsMultiKey { | |
| c.JSON(http.StatusOK, gin.H{ | |
| "success": false, | |
| "message": "This channel is not in multi-key mode", | |
| }) | |
| } |
🤖 Prompt for AI Agents
In controller/channel.go around lines 1095 to 1105, the error messages are
hardcoded in Chinese, which breaks internationalization consistency. Replace the
hardcoded Chinese strings with calls to the internationalization function (e.g.,
i18n.T or similar) to fetch the localized message keys instead of literal
strings. This ensures error messages are properly translated based on user
locale settings.
| case 1: | ||
| enabledCount++ | ||
| case 2: | ||
| manualDisabledCount++ | ||
| case 3: | ||
| autoDisabledCount++ | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Magic numbers for key status values
The code uses magic numbers (1, 2, 3) for key status values without constants.
Consider defining constants for better maintainability:
const (
KeyStatusEnabled = 1
KeyStatusManuallyDisabled = 2
KeyStatusAutoDisabled = 3
)Then use these constants throughout the code instead of magic numbers.
🤖 Prompt for AI Agents
In controller/channel.go around lines 1154 to 1160, replace the magic numbers 1,
2, and 3 used for key status values with defined constants. Define constants
such as KeyStatusEnabled = 1, KeyStatusManuallyDisabled = 2, and
KeyStatusAutoDisabled = 3 at the top of the file or in a relevant constants
section, then update the switch cases to use these constants instead of raw
numbers for better readability and maintainability.
| 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 | ||
| } |
There was a problem hiding this comment.
More hardcoded Chinese text in API responses
Multiple locations have hardcoded Chinese text that should be in English for API consistency.
Key locations needing fixes:
- Line 1209:
"未指定要禁用的密钥索引"→"Key index not specified for disabling" - Line 1218:
"密钥索引超出范围"→"Key index out of range" - Line 1235:
"手动禁用"→"Manually disabled" - Line 1246:
"密钥已禁用"→"Key disabled successfully" - Line 1254:
"未指定要启用的密钥索引"→"Key index not specified for enabling" - Line 1263:
"密钥索引超出范围"→"Key index out of range" - Line 1288:
"密钥已启用"→"Key enabled successfully" - Line 1335:
"没有需要删除的自动禁用密钥"→"No auto-disabled keys to delete" - Line 1356:
"已删除 %d 个自动禁用的密钥"→"Deleted %d auto-disabled keys" - Line 1364:
"不支持的操作"→"Unsupported action"
Also applies to: 1235-1235, 1252-1265, 1309-1310, 1364-1364
🤖 Prompt for AI Agents
In controller/channel.go from lines 1207 to 1221 and other specified lines
(1235, 1246, 1254, 1263, 1288, 1335, 1356, 1364), replace all hardcoded Chinese
text in API JSON response messages with their English equivalents for
consistency. For example, change "未指定要禁用的密钥索引" to "Key index not specified for
disabling", "密钥索引超出范围" to "Key index out of range", and similarly update all
other listed messages accordingly.
| node: 'item', | ||
| name: t('启用全部密钥'), | ||
| onClick: () => manageChannel(record.id, 'enable_all', record), | ||
| name: t('多key管理'), |
There was a problem hiding this comment.
Hardcoded Chinese text in dropdown menu
The menu item name should use translation for consistency with the rest of the UI.
Apply this diff to fix the internationalization:
-name: t('多key管理'),
+name: t('Multi-key Management'),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| name: t('多key管理'), | |
| name: t('Multi-key Management'), |
🤖 Prompt for AI Agents
In web/src/components/table/channels/ChannelsColumnDefs.js at line 547, the menu
item name is hardcoded in Chinese, which breaks UI consistency. Replace the
hardcoded string with a call to the translation function t() using the
appropriate translation key to ensure the text is internationalized like the
rest of the UI.
| } catch (error) { | ||
| console.error(error); | ||
| showError(t('获取密钥状态失败')); | ||
| } finally { |
There was a problem hiding this comment.
Inconsistent internationalization
The error message is hardcoded in Chinese while the rest of the component uses the translation function t().
Apply this diff to fix the internationalization:
} catch (error) {
console.error(error);
- showError(t('获取密钥状态失败'));
+ showError(t('Failed to get key status'));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (error) { | |
| console.error(error); | |
| showError(t('获取密钥状态失败')); | |
| } finally { | |
| } catch (error) { | |
| console.error(error); | |
| showError(t('Failed to get key status')); | |
| } finally { |
🤖 Prompt for AI Agents
In web/src/components/table/channels/modals/MultiKeyManageModal.jsx around lines
98 to 101, the error message inside showError is hardcoded in Chinese instead of
using the translation function t(). Replace the hardcoded string with a call to
t() passing the Chinese message as the key to ensure consistent
internationalization across the component.
| showSuccess(t('密钥已启用')); | ||
| await loadKeyStatus(currentPage, pageSize); // Reload current page | ||
| onRefresh && onRefresh(); // Refresh parent component | ||
| } else { | ||
| showError(res.data.message); | ||
| } | ||
| } catch (error) { | ||
| showError(t('启用密钥失败')); | ||
| } finally { |
There was a problem hiding this comment.
Inconsistent internationalization in enable key messages
The success and error messages are hardcoded in Chinese.
Apply this diff to fix the internationalization:
if (res.data.success) {
- showSuccess(t('密钥已启用'));
+ showSuccess(t('Key enabled successfully'));
await loadKeyStatus(currentPage, pageSize); // Reload current page
onRefresh && onRefresh(); // Refresh parent component
} else {
showError(res.data.message);
}
} catch (error) {
- showError(t('启用密钥失败'));
+ showError(t('Failed to enable key'));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| showSuccess(t('密钥已启用')); | |
| await loadKeyStatus(currentPage, pageSize); // Reload current page | |
| onRefresh && onRefresh(); // Refresh parent component | |
| } else { | |
| showError(res.data.message); | |
| } | |
| } catch (error) { | |
| showError(t('启用密钥失败')); | |
| } finally { | |
| if (res.data.success) { | |
| - showSuccess(t('密钥已启用')); | |
| + showSuccess(t('Key enabled successfully')); | |
| await loadKeyStatus(currentPage, pageSize); // Reload current page | |
| onRefresh && onRefresh(); // Refresh parent component | |
| } else { | |
| showError(res.data.message); | |
| } | |
| } catch (error) { | |
| - showError(t('启用密钥失败')); | |
| + showError(t('Failed to enable key')); | |
| } finally { |
🤖 Prompt for AI Agents
In web/src/components/table/channels/modals/MultiKeyManageModal.jsx around lines
145 to 153, the success and error messages for enabling keys are hardcoded in
Chinese. Replace the hardcoded Chinese strings with calls to the translation
function t() using appropriate translation keys to ensure consistent
internationalization across the application.
| } catch (error) { | ||
| showError(t('删除禁用密钥失败')); | ||
| } finally { |
There was a problem hiding this comment.
Inconsistent internationalization in delete error message
The error message is hardcoded in Chinese.
Apply this diff to fix the internationalization:
} catch (error) {
- showError(t('删除禁用密钥失败'));
+ showError(t('Failed to delete disabled keys'));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (error) { | |
| showError(t('删除禁用密钥失败')); | |
| } finally { | |
| } catch (error) { | |
| showError(t('Failed to delete disabled keys')); | |
| } finally { |
🤖 Prompt for AI Agents
In web/src/components/table/channels/modals/MultiKeyManageModal.jsx around lines
177 to 179, the error message in the catch block is hardcoded in Chinese, which
breaks internationalization consistency. Replace the hardcoded Chinese string
with a call to the translation function t() passing the appropriate key for the
delete error message, ensuring the message is properly internationalized.
| const renderStatusTag = (status) => { | ||
| switch (status) { | ||
| case 1: | ||
| return <Tag color='green' shape='circle'>{t('已启用')}</Tag>; | ||
| case 2: | ||
| return <Tag color='red' shape='circle'>{t('已禁用')}</Tag>; | ||
| case 3: | ||
| return <Tag color='orange' shape='circle'>{t('自动禁用')}</Tag>; | ||
| default: | ||
| return <Tag color='grey' shape='circle'>{t('未知状态')}</Tag>; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Hardcoded Chinese text in status tags
All status tag labels are hardcoded in Chinese instead of using translations.
Apply this diff to fix the internationalization:
const renderStatusTag = (status) => {
switch (status) {
case 1:
- return <Tag color='green' shape='circle'>{t('已启用')}</Tag>;
+ return <Tag color='green' shape='circle'>{t('Enabled')}</Tag>;
case 2:
- return <Tag color='red' shape='circle'>{t('已禁用')}</Tag>;
+ return <Tag color='red' shape='circle'>{t('Disabled')}</Tag>;
case 3:
- return <Tag color='orange' shape='circle'>{t('自动禁用')}</Tag>;
+ return <Tag color='orange' shape='circle'>{t('Auto-disabled')}</Tag>;
default:
- return <Tag color='grey' shape='circle'>{t('未知状态')}</Tag>;
+ return <Tag color='grey' shape='circle'>{t('Unknown status')}</Tag>;
}
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const renderStatusTag = (status) => { | |
| switch (status) { | |
| case 1: | |
| return <Tag color='green' shape='circle'>{t('已启用')}</Tag>; | |
| case 2: | |
| return <Tag color='red' shape='circle'>{t('已禁用')}</Tag>; | |
| case 3: | |
| return <Tag color='orange' shape='circle'>{t('自动禁用')}</Tag>; | |
| default: | |
| return <Tag color='grey' shape='circle'>{t('未知状态')}</Tag>; | |
| } | |
| }; | |
| const renderStatusTag = (status) => { | |
| switch (status) { | |
| case 1: | |
| return <Tag color='green' shape='circle'>{t('Enabled')}</Tag>; | |
| case 2: | |
| return <Tag color='red' shape='circle'>{t('Disabled')}</Tag>; | |
| case 3: | |
| return <Tag color='orange' shape='circle'>{t('Auto-disabled')}</Tag>; | |
| default: | |
| return <Tag color='grey' shape='circle'>{t('Unknown status')}</Tag>; | |
| } | |
| }; |
🤖 Prompt for AI Agents
In web/src/components/table/channels/modals/MultiKeyManageModal.jsx around lines
219 to 230, the status tag labels are hardcoded in Chinese. To fix this, replace
the hardcoded Chinese strings with calls to the translation function t() using
appropriate translation keys instead of direct Chinese text. This ensures the
labels are internationalized and can be translated based on the user's locale.
feat: add multi-key management
do not normalize model for openai API token based accounts
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
User Interface