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