fix: apikey model access

This commit is contained in:
jialin
2026-04-16 15:03:59 +08:00
committed by jialin
parent 3ac2720ba8
commit 684c1ab600
7 changed files with 132 additions and 105 deletions
@@ -170,6 +170,7 @@ const Extra = styled.div`
const AddAfter = styled.div`
position: relative;
border-radius: 0 var(--ant-border-radius) var(--ant-border-radius) 0;
color: var(--ant-color-text-tertiary);
font-size: var(--font-size-base);
padding-inline: calc(var(--ant-padding-sm) - 1px);
+13 -2
View File
@@ -37,6 +37,12 @@ interface SelectPanelProps {
selectedKeys: string[];
notFoundContent?: React.ReactNode;
onSelectChange: (selectedKeys: string[]) => void;
styles?: {
container?: React.CSSProperties;
left?: React.CSSProperties;
right?: React.CSSProperties;
header?: React.CSSProperties;
};
}
const SelectPanel: React.FC<SelectPanelProps> = ({
@@ -46,6 +52,7 @@ const SelectPanel: React.FC<SelectPanelProps> = ({
selectedKeys,
searchPlaceholder,
notFoundContent,
styles,
onSelectChange
}) => {
const intl = useIntl();
@@ -150,9 +157,13 @@ const SelectPanel: React.FC<SelectPanelProps> = ({
};
return (
<PanelWrapper $maxHeight={height} $leftWidth={leftWidth}>
<PanelWrapper
$maxHeight={height}
$leftWidth={leftWidth}
style={styles?.container}
>
<Left>
<Header>
<Header style={styles?.header}>
<span
style={{
display: 'flex',
@@ -4,10 +4,11 @@ import useAppUtils from '@/hooks/use-app-utils';
import SelectPanel from '@/pages/_components/select-panel';
import { queryMyModels } from '@/pages/llmodels/apis';
import { useIntl } from '@umijs/max';
import { Divider, Form, Radio } from 'antd';
import { Checkbox, Divider, Form, Radio } from 'antd';
import { useEffect, useState } from 'react';
import styled from 'styled-components';
import { ListItem } from '../../config/types';
import { accessScopeOptions } from '../../config';
import { FormData, ListItem } from '../../config/types';
const Label = styled.div`
font-weight: 500;
@@ -29,6 +30,7 @@ const AllowModelsForm: React.FC<{
form
) as string[];
const allowedType = Form.useWatch('allowed_type', form);
const scope = Form.useWatch('scope', form) as string[];
const [modelList, setModelList] = useState<{ key: string; title: string }[]>(
[]
);
@@ -56,11 +58,10 @@ const AllowModelsForm: React.FC<{
const handleAllowTypeChange = (e: any) => {
const value = e.target.value;
onValuesChange?.({ allowed_type: value }, form.getFieldsValue());
if (value === 'management') {
form.setFieldsValue({ scope: ['management'] });
} else {
form.setFieldsValue({ scope: ['inference'] });
}
};
const handleOnScopeChange = (checkedValues: any) => {
console.log('checkedValues', form.getFieldValue('allowed_type'));
};
useEffect(() => {
@@ -71,61 +72,82 @@ const AllowModelsForm: React.FC<{
<div>
<Divider></Divider>
<Label>{intl.formatMessage({ id: 'apikeys.access.permissions' })}</Label>
<Form.Item
name="allowed_type"
initialValue="all"
style={{ marginBottom: 8 }}
>
<Radio.Group
onChange={handleAllowTypeChange}
options={[
{
label: intl.formatMessage({
id: 'apikeys.accessScope.management'
}),
value: 'management'
},
{
label: intl.formatMessage({ id: 'apikeys.models.all' }),
value: 'all'
},
{
label: intl.formatMessage({ id: 'apikeys.models.selected' }),
value: 'custom'
}
]}
></Radio.Group>
<Form.Item<FormData> name="scope" style={{ marginBottom: 8 }}>
<Checkbox.Group
options={accessScopeOptions.map((item) => ({
label: intl.formatMessage({ id: item.label }),
value: item.value
}))}
onChange={handleOnScopeChange}
></Checkbox.Group>
</Form.Item>
<Form.Item
name="allowed_model_names"
hidden={allowedType === 'all' || allowedType === 'management'}
rules={[
{
required: allowedType === 'custom',
message: getRuleMessage(
'select',
intl.formatMessage({ id: 'models.table.models' })
)
}
]}
>
<SelectPanel
height={300}
searchPlaceholder={intl.formatMessage({ id: 'common.filter.name' })}
options={modelList}
selectedKeys={allowedModelNames || []}
notFoundContent={intl.formatMessage({
id: 'apikeys.models.noModelsFound'
})}
onSelectChange={(selectedKeys) => {
form.setFieldsValue({ allowed_model_names: selectedKeys });
onValuesChange?.(
{ allowed_model_names: selectedKeys },
form.getFieldsValue()
);
{scope?.includes('inference') && (
<div
style={{
padding: '12px',
backgroundColor: 'var(--ant-color-fill-quaternary)',
borderRadius: 4
}}
/>
</Form.Item>
>
<Form.Item name="allowed_type" noStyle>
<Radio.Group
onChange={handleAllowTypeChange}
options={[
{
label: intl.formatMessage({ id: 'apikeys.models.all' }),
value: 'all'
},
{
label: intl.formatMessage({ id: 'apikeys.models.selected' }),
value: 'custom'
}
]}
></Radio.Group>
</Form.Item>
{allowedType === 'custom' && (
<Form.Item
name="allowed_model_names"
style={{ marginBottom: 0, marginTop: 12 }}
rules={[
{
required: allowedType === 'custom',
message: getRuleMessage(
'select',
intl.formatMessage({ id: 'models.table.models' })
)
}
]}
>
<SelectPanel
height={300}
styles={{
container: {
backgroundColor: 'var(--ant-color-bg-container) !important'
},
header: {
backgroundColor: 'var(--ant-color-bg-container) !important'
}
}}
searchPlaceholder={intl.formatMessage({
id: 'common.filter.name'
})}
options={modelList}
selectedKeys={allowedModelNames || []}
notFoundContent={intl.formatMessage({
id: 'apikeys.models.noModelsFound'
})}
onSelectChange={(selectedKeys) => {
form.setFieldsValue({ allowed_model_names: selectedKeys });
onValuesChange?.(
{ allowed_model_names: selectedKeys },
form.getFieldsValue()
);
}}
/>
</Form.Item>
)}
</div>
)}
</div>
);
};
@@ -6,7 +6,7 @@ import { PageActionType } from '@/config/types';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import React from 'react';
import { accessScopeOptions, expirationOptions } from '../../config';
import { expirationOptions } from '../../config';
import { FormData, ListItem } from '../../config/types';
import AllowModelsForm from './allow-models';
@@ -115,19 +115,7 @@ const APIKeyForm: React.FC<{
)}
</>
)}
<Form.Item<FormData>
name="scope"
hidden
normalize={(value) => (value ? [value] : [])}
getValueProps={(value) => ({
value: Array.isArray(value) ? value[0] : value
})}
>
<SealSelect
options={accessScopeOptions}
label={intl.formatMessage({ id: 'models.table.accessScope' })}
></SealSelect>
</Form.Item>
<AllowModelsForm
currentData={currentData}
action={action}
@@ -118,7 +118,7 @@ const AddModal: React.FC<AddModalProps> = ({
..._.omit(formdata, ['allowed_type']),
allowed_model_names:
formdata.allowed_type === 'all' ||
formdata.allowed_type === 'management'
!formdata.scope?.includes('inference')
? []
: formdata.allowed_model_names || []
};
@@ -186,9 +186,7 @@ const AddModal: React.FC<AddModalProps> = ({
scope: currentData.scope,
allowed_type: currentData.allowed_model_names?.length
? 'custom'
: currentData.scope?.includes('management')
? 'management'
: 'all',
: 'all',
expires_in: parseExpireValue(currentData as ListItem),
allowed_model_names: currentData.allowed_model_names || []
});
@@ -272,6 +270,7 @@ const AddModal: React.FC<AddModalProps> = ({
onFinish={handleOnOk}
preserve={false}
initialValues={{
allowed_type: 'all',
scope: ['inference']
}}
>
+2 -2
View File
@@ -27,13 +27,13 @@ export const expirationOptions = [
export const accessScopeOptions = [
{
label: 'management',
label: 'apikeys.accessScope.management',
value: 'management',
description: 'v2/',
locale: true
},
{
label: 'inference',
label: 'apikeys.table.bindModels',
value: 'inference',
description: 'v1/',
locale: true
+29 -23
View File
@@ -8,7 +8,6 @@ import { Tag } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import dayjs from 'dayjs';
import { useMemo } from 'react';
import { accessScopeOptions } from '../config';
import { ListItem } from '../config/types';
interface ColumnsHookProps {
@@ -16,7 +15,7 @@ interface ColumnsHookProps {
sortOrder: string[];
}
const actionList: Global.ActionItem[] = [
const actionList: Global.ActionItem<string>[] = [
{
label: 'common.button.edit',
key: 'edit',
@@ -36,20 +35,6 @@ const useModelsColumns = ({
}: ColumnsHookProps): ColumnsType<ListItem> => {
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 || ''
})}
{option?.description && <span>[{option?.description}]</span>}
</span>
);
};
return useMemo(() => {
return [
{
@@ -112,13 +97,34 @@ const useModelsColumns = ({
showTitle: false
},
render: (text: string[], record: ListItem) => (
<AutoTooltip ghost>
{record.scope?.[0] === 'management'
? intl.formatMessage({ id: 'apikeys.accessScope.management' })
: record.allowed_model_names?.length
? record.allowed_model_names.join(', ')
: intl.formatMessage({ id: 'apikeys.models.all' })}
</AutoTooltip>
<div className="flex-column gap-4">
{record.scope?.includes('management') && (
<AutoTooltip ghost>
{intl.formatMessage({ id: 'apikeys.accessScope.management' })}
</AutoTooltip>
)}
{record.scope?.includes('inference') && (
<div
style={{
border: '1px solid var(--ant-color-split)',
color: 'var(--ant-color-text-tertiary)',
backgroundColor: 'var(--ant-color-fill-quaternary)',
borderRadius: 4,
paddingInline: 8,
flexGrow: 0,
maxWidth: '100%',
width: 'max-content',
display: 'flex'
}}
>
<AutoTooltip ghost>
{record.allowed_model_names?.length
? record.allowed_model_names.join(', ')
: intl.formatMessage({ id: 'apikeys.models.all' })}
</AutoTooltip>
</div>
)}
</div>
)
},
{