feat: custom api key
This commit is contained in:
@@ -368,3 +368,15 @@ textarea:hover {
|
|||||||
.line-6 {
|
.line-6 {
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import styled from 'styled-components';
|
|||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const HeaderWrapper = styled.div<{ $height?: number }>`
|
const HeaderWrapper = styled.div<{ $height?: number }>`
|
||||||
height: ${(props) => (props.$height ? `${props.$height}px` : 'auto')};
|
|
||||||
display: flex;
|
display: flex;
|
||||||
padding-block: 0;
|
padding-block: 0;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -12,7 +11,7 @@ const HeaderWrapper = styled.div<{ $height?: number }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
border-radius: var(--border-radius-mini);
|
border-radius: var(--border-radius);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&.bordered {
|
&.bordered {
|
||||||
@@ -57,7 +56,15 @@ const EditorWrap: React.FC<EditorwrapProps> = ({
|
|||||||
borderless: variant === 'borderless'
|
borderless: variant === 'borderless'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{header && <HeaderWrapper $height={headerHeight}>{header}</HeaderWrapper>}
|
{header && (
|
||||||
|
<HeaderWrapper
|
||||||
|
style={{
|
||||||
|
height: headerHeight || 'auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{header}
|
||||||
|
</HeaderWrapper>
|
||||||
|
)}
|
||||||
<div>{children}</div>
|
<div>{children}</div>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,5 +17,9 @@ export default {
|
|||||||
'apikeys.table.bindModels': 'Model Access',
|
'apikeys.table.bindModels': 'Model Access',
|
||||||
'apikeys.models.all': 'All models',
|
'apikeys.models.all': 'All models',
|
||||||
'apikeys.models.selected': 'Allowed models',
|
'apikeys.models.selected': 'Allowed models',
|
||||||
'apikeys.models.noModelsFound': 'No models found'
|
'apikeys.models.noModelsFound': 'No models found',
|
||||||
|
'apikeys.accessScope.all': 'All',
|
||||||
|
'apikeys.accessScope.management': 'Management APIs',
|
||||||
|
'apikeys.accessScope.inference': 'Inference APIs',
|
||||||
|
'apikeys.access.permissions': 'Access Permissions'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,12 +17,17 @@ export default {
|
|||||||
'apikeys.table.bindModels': 'Model Access',
|
'apikeys.table.bindModels': 'Model Access',
|
||||||
'apikeys.models.all': 'All models',
|
'apikeys.models.all': 'All models',
|
||||||
'apikeys.models.selected': 'Allowed models',
|
'apikeys.models.selected': 'Allowed models',
|
||||||
'apikeys.models.noModelsFound': 'No models found'
|
'apikeys.models.noModelsFound': 'No models found',
|
||||||
|
'apikeys.accessScope.all': 'All',
|
||||||
|
'apikeys.accessScope.management': 'Management APIs',
|
||||||
|
'apikeys.accessScope.inference': 'Inference APIs',
|
||||||
|
'apikeys.access.permissions': 'Access Permissions'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
// 1. 'apikeys.table.bindModels': 'Model Access',
|
// 1. 'apikeys.table.bindModels': 'Model Access',
|
||||||
// 2. 'apikeys.models.all': 'All models',
|
// 2. 'apikeys.models.all': 'All models',
|
||||||
// 3. 'apikeys.models.selected': 'Allowed models',
|
// 3. 'apikeys.models.selected': 'Allowed models',
|
||||||
// 4. 'apikeys.models.noModelsFound': 'No models found'
|
// 4. 'apikeys.models.noModelsFound': 'No models found',
|
||||||
|
// 5. 'apikeys.access.permissions': 'Access Permissions'
|
||||||
// ========== End of To-Do List ==========
|
// ========== End of To-Do List ==========
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ export default {
|
|||||||
'apikeys.table.bindModels': 'Доступ к моделям',
|
'apikeys.table.bindModels': 'Доступ к моделям',
|
||||||
'apikeys.models.all': 'Все модели',
|
'apikeys.models.all': 'Все модели',
|
||||||
'apikeys.models.selected': 'Разрешенные модели',
|
'apikeys.models.selected': 'Разрешенные модели',
|
||||||
'apikeys.models.noModelsFound': 'Модели не найдены'
|
'apikeys.models.noModelsFound': 'Модели не найдены',
|
||||||
|
'apikeys.accessScope.all': 'All',
|
||||||
|
'apikeys.accessScope.management': 'Management APIs',
|
||||||
|
'apikeys.accessScope.inference': 'Inference APIs',
|
||||||
|
'apikeys.access.permissions': 'Access Permissions'
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
// ========== To-Do: Translate Keys (Remove After Translation) ==========
|
||||||
|
|||||||
@@ -17,5 +17,9 @@ export default {
|
|||||||
'apikeys.table.bindModels': 'Model Erişimi',
|
'apikeys.table.bindModels': 'Model Erişimi',
|
||||||
'apikeys.models.all': 'Tüm modeller',
|
'apikeys.models.all': 'Tüm modeller',
|
||||||
'apikeys.models.selected': 'İzin verilen modeller',
|
'apikeys.models.selected': 'İzin verilen modeller',
|
||||||
'apikeys.models.noModelsFound': 'Model bulunamadı'
|
'apikeys.models.noModelsFound': 'Model bulunamadı',
|
||||||
|
'apikeys.accessScope.all': 'All',
|
||||||
|
'apikeys.accessScope.management': 'Management APIs',
|
||||||
|
'apikeys.accessScope.inference': 'Inference APIs',
|
||||||
|
'apikeys.access.permissions': 'Access Permissions'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,5 +16,9 @@ export default {
|
|||||||
'apikeys.table.bindModels': '模型访问',
|
'apikeys.table.bindModels': '模型访问',
|
||||||
'apikeys.models.all': '所有模型',
|
'apikeys.models.all': '所有模型',
|
||||||
'apikeys.models.selected': '指定模型',
|
'apikeys.models.selected': '指定模型',
|
||||||
'apikeys.models.noModelsFound': '未找到模型'
|
'apikeys.models.noModelsFound': '未找到模型',
|
||||||
|
'apikeys.accessScope.all': '全部',
|
||||||
|
'apikeys.accessScope.management': '管理接口',
|
||||||
|
'apikeys.accessScope.inference': '推理接口',
|
||||||
|
'apikeys.access.permissions': '访问权限'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const Container = styled.div`
|
|||||||
position: relative;
|
position: relative;
|
||||||
border: 1px solid var(--ant-color-border);
|
border: 1px solid var(--ant-color-border);
|
||||||
border-radius: var(--ant-border-radius);
|
border-radius: var(--ant-border-radius);
|
||||||
|
overflow: hidden;
|
||||||
.monaco-editor .scroll-decoration {
|
.monaco-editor .scroll-decoration {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Password from '@/components/seal-form/password';
|
||||||
import SealInput from '@/components/seal-form/seal-input';
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
import SealSelect from '@/components/seal-form/seal-select';
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
@@ -5,7 +6,7 @@ import { PageActionType } from '@/config/types';
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Form } from 'antd';
|
import { Form } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { expirationOptions } from '../../config';
|
import { accessScopeOptions, expirationOptions } from '../../config';
|
||||||
import { FormData, ListItem } from '../../config/types';
|
import { FormData, ListItem } from '../../config/types';
|
||||||
import AllowModelsForm from './allow-models';
|
import AllowModelsForm from './allow-models';
|
||||||
|
|
||||||
@@ -16,6 +17,17 @@ const APIKeyForm: React.FC<{
|
|||||||
}> = ({ action, currentData, onValuesChange }) => {
|
}> = ({ action, currentData, onValuesChange }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const optionRender = (option: any) => {
|
||||||
|
return (
|
||||||
|
<span className="flex-center gap-4">
|
||||||
|
{option.label}
|
||||||
|
{option.data.description && (
|
||||||
|
<span className="text-tertiary">[{option.data.description}]</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
@@ -39,6 +51,28 @@ const APIKeyForm: React.FC<{
|
|||||||
required
|
required
|
||||||
></SealInput.Input>
|
></SealInput.Input>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
{action === PageAction.CREATE && (
|
||||||
|
<Form.Item<FormData> name="custom">
|
||||||
|
<Password
|
||||||
|
trim
|
||||||
|
autoComplete="new-password"
|
||||||
|
label={intl.formatMessage({ id: 'playground.params.custom' })}
|
||||||
|
></Password>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name="scope"
|
||||||
|
normalize={(value) => (value ? [value] : [])}
|
||||||
|
getValueProps={(value) => ({
|
||||||
|
value: Array.isArray(value) ? value[0] : value
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<SealSelect
|
||||||
|
optionRender={optionRender}
|
||||||
|
options={accessScopeOptions}
|
||||||
|
label={intl.formatMessage({ id: 'models.table.accessScope' })}
|
||||||
|
></SealSelect>
|
||||||
|
</Form.Item>
|
||||||
<Form.Item<FormData>
|
<Form.Item<FormData>
|
||||||
name="expires_in"
|
name="expires_in"
|
||||||
rules={[
|
rules={[
|
||||||
|
|||||||
@@ -96,6 +96,12 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
expires_in: getExpireValue(data.expires_in)
|
expires_in: getExpireValue(data.expires_in)
|
||||||
};
|
};
|
||||||
const res = await createApisKey({ data: params });
|
const res = await createApisKey({ data: params });
|
||||||
|
|
||||||
|
// if custom value
|
||||||
|
if (data.custom) {
|
||||||
|
return onOk();
|
||||||
|
}
|
||||||
|
|
||||||
setAPIKeyValue(res.value);
|
setAPIKeyValue(res.value);
|
||||||
setShowKey(true);
|
setShowKey(true);
|
||||||
};
|
};
|
||||||
@@ -107,6 +113,7 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
|
|
||||||
const handleOnOk = async (formdata: FormData) => {
|
const handleOnOk = async (formdata: FormData) => {
|
||||||
try {
|
try {
|
||||||
|
console.log('formdata=========', formdata);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const data = {
|
const data = {
|
||||||
..._.omit(formdata, ['allowed_type']),
|
..._.omit(formdata, ['allowed_type']),
|
||||||
@@ -261,6 +268,9 @@ const AddModal: React.FC<AddModalProps> = ({
|
|||||||
form={form}
|
form={form}
|
||||||
onFinish={handleOnOk}
|
onFinish={handleOnOk}
|
||||||
preserve={false}
|
preserve={false}
|
||||||
|
initialValues={{
|
||||||
|
scope: ['*']
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{!showKey && (
|
{!showKey && (
|
||||||
<APIKeyForm
|
<APIKeyForm
|
||||||
|
|||||||
@@ -24,3 +24,23 @@ export const expirationOptions = [
|
|||||||
locale: true
|
locale: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const accessScopeOptions = [
|
||||||
|
{
|
||||||
|
label: 'apikeys.accessScope.all',
|
||||||
|
value: '*',
|
||||||
|
locale: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'apikeys.accessScope.management',
|
||||||
|
value: 'management',
|
||||||
|
description: 'v2/',
|
||||||
|
locale: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'apikeys.accessScope.inference',
|
||||||
|
value: 'inference',
|
||||||
|
description: 'v1/',
|
||||||
|
locale: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export interface ListItem {
|
|||||||
updated_at: string;
|
updated_at: string;
|
||||||
expires_at: string;
|
expires_at: string;
|
||||||
allowed_model_names: string[];
|
allowed_model_names: string[];
|
||||||
|
custom?: string;
|
||||||
|
scope?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormData {
|
export interface FormData {
|
||||||
@@ -15,4 +17,6 @@ export interface FormData {
|
|||||||
description: string;
|
description: string;
|
||||||
allowed_model_names: string[];
|
allowed_model_names: string[];
|
||||||
expires_in: number | null;
|
expires_in: number | null;
|
||||||
|
custom?: string;
|
||||||
|
scope?: string[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import DropdownButtons from '@/components/drop-down-buttons';
|
|||||||
import icons from '@/components/icon-font/icons';
|
import icons from '@/components/icon-font/icons';
|
||||||
import { tableSorter } from '@/config/settings';
|
import { tableSorter } from '@/config/settings';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Tag } from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { accessScopeOptions } from '../config';
|
||||||
import { ListItem } from '../config/types';
|
import { ListItem } from '../config/types';
|
||||||
|
|
||||||
interface ColumnsHookProps {
|
interface ColumnsHookProps {
|
||||||
@@ -34,6 +36,20 @@ const useModelsColumns = ({
|
|||||||
}: ColumnsHookProps): ColumnsType<ListItem> => {
|
}: ColumnsHookProps): ColumnsType<ListItem> => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const renderAPIs = (record: ListItem) => {
|
||||||
|
const option = accessScopeOptions.find(
|
||||||
|
(item) => item.value === record.scope?.[0]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<span className="flex-center gap-4">
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: option?.label || ''
|
||||||
|
})}
|
||||||
|
<span>[{option?.description}]</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -42,9 +58,23 @@ const useModelsColumns = ({
|
|||||||
key: 'name',
|
key: 'name',
|
||||||
sorter: tableSorter(1),
|
sorter: tableSorter(1),
|
||||||
render: (text: string, record: ListItem) => (
|
render: (text: string, record: ListItem) => (
|
||||||
<AutoTooltip ghost style={{ maxWidth: 400 }}>
|
<span className="flex items-center">
|
||||||
{text}
|
<AutoTooltip ghost style={{ maxWidth: 400 }}>
|
||||||
</AutoTooltip>
|
<span className="text-primary">{text}</span>
|
||||||
|
</AutoTooltip>
|
||||||
|
<Tag
|
||||||
|
style={{
|
||||||
|
marginLeft: 8,
|
||||||
|
borderRadius: 12,
|
||||||
|
color: 'var(--ant-color-text-tertiary)',
|
||||||
|
borderColor: 'var(--ant-color-split)',
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}
|
||||||
|
variant="outlined"
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'playground.params.custom' })}
|
||||||
|
</Tag>
|
||||||
|
</span>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -73,18 +103,34 @@ const useModelsColumns = ({
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: intl.formatMessage({ id: 'apikeys.table.bindModels' }),
|
title: intl.formatMessage({ id: 'apikeys.access.permissions' }),
|
||||||
dataIndex: 'allowed_model_names',
|
dataIndex: 'allowed_model_names',
|
||||||
key: 'allowed_model_names',
|
key: 'allowed_model_names',
|
||||||
ellipsis: {
|
ellipsis: {
|
||||||
showTitle: false
|
showTitle: false
|
||||||
},
|
},
|
||||||
render: (text: string[], record: ListItem) => (
|
render: (text: string[], record: ListItem) => (
|
||||||
<AutoTooltip ghost>
|
<div
|
||||||
{text?.length
|
style={{
|
||||||
? text?.join(', ')
|
display: 'grid',
|
||||||
: intl.formatMessage({ id: 'common.select.option' })}
|
gridTemplateColumns: 'max-content 1fr',
|
||||||
</AutoTooltip>
|
gap: 8,
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="text-primary">APIs:</span>
|
||||||
|
<AutoTooltip ghost>{renderAPIs(record)}</AutoTooltip>
|
||||||
|
<span className="text-primary">
|
||||||
|
{intl.formatMessage({ id: 'models.title' })}:
|
||||||
|
</span>
|
||||||
|
<AutoTooltip ghost>
|
||||||
|
<span>
|
||||||
|
{text?.length
|
||||||
|
? text?.join(', ')
|
||||||
|
: intl.formatMessage({ id: 'common.select.option' })}
|
||||||
|
</span>
|
||||||
|
</AutoTooltip>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user