feat: implement token key fetching and masking in API responses
This commit is contained in:
@@ -29,7 +29,6 @@ const TokensActions = ({
|
||||
setShowEdit,
|
||||
batchCopyTokens,
|
||||
batchDeleteTokens,
|
||||
copyText,
|
||||
t,
|
||||
}) => {
|
||||
// Modal states
|
||||
@@ -99,8 +98,7 @@ const TokensActions = ({
|
||||
<CopyTokensModal
|
||||
visible={showCopyModal}
|
||||
onCancel={() => setShowCopyModal(false)}
|
||||
selectedKeys={selectedKeys}
|
||||
copyText={copyText}
|
||||
batchCopyTokens={batchCopyTokens}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
|
||||
@@ -108,17 +108,28 @@ const renderGroupColumn = (text, record, t) => {
|
||||
};
|
||||
|
||||
// Render token key column with show/hide and copy functionality
|
||||
const renderTokenKey = (text, record, showKeys, setShowKeys, copyText) => {
|
||||
const fullKey = 'sk-' + record.key;
|
||||
const maskedKey =
|
||||
'sk-' + record.key.slice(0, 4) + '**********' + record.key.slice(-4);
|
||||
const renderTokenKey = (
|
||||
text,
|
||||
record,
|
||||
showKeys,
|
||||
resolvedTokenKeys,
|
||||
loadingTokenKeys,
|
||||
toggleTokenVisibility,
|
||||
copyTokenKey,
|
||||
) => {
|
||||
const revealed = !!showKeys[record.id];
|
||||
const loading = !!loadingTokenKeys[record.id];
|
||||
const keyValue =
|
||||
revealed && resolvedTokenKeys[record.id]
|
||||
? resolvedTokenKeys[record.id]
|
||||
: record.key || '';
|
||||
const displayedKey = keyValue ? `sk-${keyValue}` : '';
|
||||
|
||||
return (
|
||||
<div className='w-[200px]'>
|
||||
<Input
|
||||
readOnly
|
||||
value={revealed ? fullKey : maskedKey}
|
||||
value={displayedKey}
|
||||
size='small'
|
||||
suffix={
|
||||
<div className='flex items-center'>
|
||||
@@ -127,10 +138,11 @@ const renderTokenKey = (text, record, showKeys, setShowKeys, copyText) => {
|
||||
size='small'
|
||||
type='tertiary'
|
||||
icon={revealed ? <IconEyeClosed /> : <IconEyeOpened />}
|
||||
loading={loading}
|
||||
aria-label='toggle token visibility'
|
||||
onClick={(e) => {
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
setShowKeys((prev) => ({ ...prev, [record.id]: !revealed }));
|
||||
await toggleTokenVisibility(record);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -138,10 +150,11 @@ const renderTokenKey = (text, record, showKeys, setShowKeys, copyText) => {
|
||||
size='small'
|
||||
type='tertiary'
|
||||
icon={<IconCopy />}
|
||||
loading={loading}
|
||||
aria-label='copy token key'
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
await copyText(fullKey);
|
||||
await copyTokenKey(record);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -427,8 +440,10 @@ const renderOperations = (
|
||||
export const getTokensColumns = ({
|
||||
t,
|
||||
showKeys,
|
||||
setShowKeys,
|
||||
copyText,
|
||||
resolvedTokenKeys,
|
||||
loadingTokenKeys,
|
||||
toggleTokenVisibility,
|
||||
copyTokenKey,
|
||||
manageToken,
|
||||
onOpenLink,
|
||||
setEditingToken,
|
||||
@@ -461,7 +476,15 @@ export const getTokensColumns = ({
|
||||
title: t('密钥'),
|
||||
key: 'token_key',
|
||||
render: (text, record) =>
|
||||
renderTokenKey(text, record, showKeys, setShowKeys, copyText),
|
||||
renderTokenKey(
|
||||
text,
|
||||
record,
|
||||
showKeys,
|
||||
resolvedTokenKeys,
|
||||
loadingTokenKeys,
|
||||
toggleTokenVisibility,
|
||||
copyTokenKey,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('可用模型'),
|
||||
|
||||
@@ -39,8 +39,10 @@ const TokensTable = (tokensData) => {
|
||||
rowSelection,
|
||||
handleRow,
|
||||
showKeys,
|
||||
setShowKeys,
|
||||
copyText,
|
||||
resolvedTokenKeys,
|
||||
loadingTokenKeys,
|
||||
toggleTokenVisibility,
|
||||
copyTokenKey,
|
||||
manageToken,
|
||||
onOpenLink,
|
||||
setEditingToken,
|
||||
@@ -54,8 +56,10 @@ const TokensTable = (tokensData) => {
|
||||
return getTokensColumns({
|
||||
t,
|
||||
showKeys,
|
||||
setShowKeys,
|
||||
copyText,
|
||||
resolvedTokenKeys,
|
||||
loadingTokenKeys,
|
||||
toggleTokenVisibility,
|
||||
copyTokenKey,
|
||||
manageToken,
|
||||
onOpenLink,
|
||||
setEditingToken,
|
||||
@@ -65,8 +69,10 @@ const TokensTable = (tokensData) => {
|
||||
}, [
|
||||
t,
|
||||
showKeys,
|
||||
setShowKeys,
|
||||
copyText,
|
||||
resolvedTokenKeys,
|
||||
loadingTokenKeys,
|
||||
toggleTokenVisibility,
|
||||
copyTokenKey,
|
||||
manageToken,
|
||||
onOpenLink,
|
||||
setEditingToken,
|
||||
|
||||
@@ -58,6 +58,7 @@ function TokensPage() {
|
||||
t: (k) => k,
|
||||
selectedModel: '',
|
||||
prefillKey: '',
|
||||
fetchTokenKey: async () => '',
|
||||
});
|
||||
const [modelOptions, setModelOptions] = useState([]);
|
||||
const [selectedModel, setSelectedModel] = useState('');
|
||||
@@ -74,6 +75,7 @@ function TokensPage() {
|
||||
t: tokensData.t,
|
||||
selectedModel,
|
||||
prefillKey,
|
||||
fetchTokenKey: tokensData.fetchTokenKey,
|
||||
};
|
||||
}, [
|
||||
tokensData.tokens,
|
||||
@@ -81,6 +83,7 @@ function TokensPage() {
|
||||
tokensData.t,
|
||||
selectedModel,
|
||||
prefillKey,
|
||||
tokensData.fetchTokenKey,
|
||||
]);
|
||||
|
||||
const loadModels = async () => {
|
||||
@@ -198,13 +201,14 @@ function TokensPage() {
|
||||
openCCSwitchModalRef.current = openCCSwitchModal;
|
||||
|
||||
// Prefill to Fluent handler
|
||||
const handlePrefillToFluent = () => {
|
||||
const handlePrefillToFluent = async () => {
|
||||
const {
|
||||
tokens,
|
||||
selectedKeys,
|
||||
t,
|
||||
selectedModel: chosenModel,
|
||||
prefillKey: overrideKey,
|
||||
fetchTokenKey,
|
||||
} = latestRef.current;
|
||||
const container = document.getElementById('fluent-new-api-container');
|
||||
if (!container) {
|
||||
@@ -241,7 +245,11 @@ function TokensPage() {
|
||||
Toast.warning(t('没有可用令牌用于填充'));
|
||||
return;
|
||||
}
|
||||
apiKeyToUse = 'sk-' + token.key;
|
||||
try {
|
||||
apiKeyToUse = 'sk-' + (await fetchTokenKey(token));
|
||||
} catch (_) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const payload = {
|
||||
@@ -351,7 +359,6 @@ function TokensPage() {
|
||||
setShowEdit,
|
||||
batchCopyTokens,
|
||||
batchDeleteTokens,
|
||||
copyText,
|
||||
|
||||
// Filters state
|
||||
formInitValues,
|
||||
@@ -401,7 +408,6 @@ function TokensPage() {
|
||||
setShowEdit={setShowEdit}
|
||||
batchCopyTokens={batchCopyTokens}
|
||||
batchDeleteTokens={batchDeleteTokens}
|
||||
copyText={copyText}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
|
||||
@@ -116,8 +116,7 @@ export default function CCSwitchModal({
|
||||
Toast.warning(t('请选择主模型'));
|
||||
return;
|
||||
}
|
||||
const apiKey = 'sk-' + tokenKey;
|
||||
const url = buildCCSwitchURL(app, name, models, apiKey);
|
||||
const url = buildCCSwitchURL(app, name, models, 'sk-' + tokenKey);
|
||||
window.open(url, '_blank');
|
||||
onClose();
|
||||
};
|
||||
|
||||
@@ -20,24 +20,21 @@ For commercial licensing, please contact support@quantumnous.com
|
||||
import React from 'react';
|
||||
import { Modal, Button, Space } from '@douyinfe/semi-ui';
|
||||
|
||||
const CopyTokensModal = ({ visible, onCancel, selectedKeys, copyText, t }) => {
|
||||
const CopyTokensModal = ({
|
||||
visible,
|
||||
onCancel,
|
||||
batchCopyTokens,
|
||||
t,
|
||||
}) => {
|
||||
// Handle copy with name and key format
|
||||
const handleCopyWithName = async () => {
|
||||
let content = '';
|
||||
for (let i = 0; i < selectedKeys.length; i++) {
|
||||
content += selectedKeys[i].name + ' sk-' + selectedKeys[i].key + '\n';
|
||||
}
|
||||
await copyText(content);
|
||||
await batchCopyTokens('name+key');
|
||||
onCancel();
|
||||
};
|
||||
|
||||
// Handle copy with key only format
|
||||
const handleCopyKeyOnly = async () => {
|
||||
let content = '';
|
||||
for (let i = 0; i < selectedKeys.length; i++) {
|
||||
content += 'sk-' + selectedKeys[i].key + '\n';
|
||||
}
|
||||
await copyText(content);
|
||||
await batchCopyTokens('key-only');
|
||||
onCancel();
|
||||
};
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ const ColumnSelectorModal = ({
|
||||
<RadioGroup
|
||||
type='button'
|
||||
value={billingDisplayMode}
|
||||
onChange={(event) => setBillingDisplayMode(event.target.value)}
|
||||
onChange={(value) => setBillingDisplayMode(value)}
|
||||
>
|
||||
<Radio value='price'>
|
||||
{isTokensDisplay ? t('价格模式') : t('价格模式(默认)')}
|
||||
|
||||
Reference in New Issue
Block a user