feat: ip access control

This commit is contained in:
jialin
2026-04-29 18:09:55 +08:00
committed by jialin
parent c68f546be9
commit ac99156c90
6 changed files with 85 additions and 32 deletions
+1 -1
View File
@@ -341,7 +341,7 @@ export default (props: any) => {
config={{
apiBaseUrl: GPUSTACK_API_BASE_URL,
theme: userSettings.theme,
iconUrl: '//at.alicdn.com/t/c/font_4613488_mk9rqojjoqk.js',
iconUrl: '//at.alicdn.com/t/c/font_4613488_ueevrwt2v9.js',
isDarkTheme: userSettings.isDarkTheme,
defaultColorPrimary: COLOR_PRIMARY
}}
+50 -21
View File
@@ -1,39 +1,66 @@
// columns.ts
import { tableSorter } from '@/config/settings';
import { AutoTooltip, DropdownButtons, icons } from '@gpustack/core-ui';
import { getGPUStackPlugin } from '@/plugins';
import {
AutoTooltip,
DropdownButtons,
IconFont,
icons
} from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
import { Tag } from 'antd';
import { MenuProps, Tag } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import dayjs from 'dayjs';
import { useMemo } from 'react';
import { ListItem } from '../config/types';
type APIKeyAction = Global.ActionItem<ListItem> & {
onClick?: (record: ListItem) => void;
};
interface ColumnsHookProps {
handleSelect: (val: string, record: ListItem) => void;
handleSelect: (val: string, record: ListItem, item?: APIKeyAction) => void;
sortOrder: string[];
is_admin?: boolean;
onIPConfig?: (record: ListItem) => void;
}
const actionList: Global.ActionItem<string>[] = [
{
label: 'common.button.edit',
key: 'edit',
icon: icons.EditOutlined
},
{
label: 'common.button.delete',
key: 'delete',
icon: icons.DeleteOutlined,
props: { danger: true }
}
];
const useModelsColumns = ({
handleSelect,
sortOrder,
is_admin
is_admin,
onIPConfig
}: ColumnsHookProps): ColumnsType<ListItem> => {
const intl = useIntl();
const actionList = useMemo<APIKeyAction[]>(() => {
const list: APIKeyAction[] = [
{
label: 'common.button.edit',
key: 'edit',
icon: icons.EditOutlined
},
{
label: 'common.button.delete',
key: 'delete',
icon: icons.DeleteOutlined,
props: { danger: true }
}
];
const ipConfigComponent = getGPUStackPlugin()?.APIKeyIPConfig?.form;
if (ipConfigComponent && onIPConfig) {
list.splice(1, 0, {
label: 'apikeys.button.ipConfig',
key: 'ipConfig',
icon: <IconFont type="icon-safe-ip" />,
onClick: (record: ListItem) => onIPConfig(record)
});
}
return list;
}, [onIPConfig]);
return useMemo(() => {
return [
{
@@ -172,13 +199,15 @@ const useModelsColumns = ({
span: 3,
render: (text, record) => (
<DropdownButtons
items={actionList}
onSelect={(val) => handleSelect(val, record)}
items={actionList as MenuProps['items']}
onSelect={(val, item) =>
handleSelect(val, record, item as APIKeyAction)
}
/>
)
}
];
}, [intl, is_admin, handleSelect]);
}, [intl, is_admin, handleSelect, actionList]);
};
export default useModelsColumns;
+32 -7
View File
@@ -3,6 +3,7 @@ import { PaginationKey } from '@/config/settings';
import type { PageActionType } from '@/config/types';
import useTableFetch from '@/hooks/use-table-fetch';
import useQueryUserList from '@/pages/users/services/use-query-user-list';
import { getGPUStackPlugin } from '@/plugins';
import { useModel } from '@@/plugin-model';
import { DeleteModal, FilterBar, IconFont, NoResult } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
@@ -53,6 +54,12 @@ const APIKeys: React.FC = () => {
});
const intl = useIntl();
const apiKeyIPConfig = getGPUStackPlugin()?.APIKeyIPConfig;
const APIKeyIPConfigForm = apiKeyIPConfig?.form;
const { openIPConfigModalStatus, openIPConfigModal, closeIPConfigModal } =
apiKeyIPConfig?.useCreateIPConfig?.() || {};
const [openAddModal, setOpenAddModal] = useState<{
open: boolean;
action: PageActionType;
@@ -115,13 +122,23 @@ const APIKeys: React.FC = () => {
});
};
const onSelect = useMemoizedFn(async (val: string, record: ListItem) => {
if (val === 'delete') {
handleDelete(record);
} else if (val === 'edit') {
handleEditKey(record);
const onSelect = useMemoizedFn(
async (
val: string,
record: ListItem,
item?: { onClick?: (r: ListItem) => void }
) => {
if (item?.onClick) {
item.onClick(record);
return;
}
if (val === 'delete') {
handleDelete(record);
} else if (val === 'edit') {
handleEditKey(record);
}
}
});
);
const handleUserChange = (val: string) => {
handleQueryChange({
@@ -152,7 +169,8 @@ const APIKeys: React.FC = () => {
const columns = useKeysColumns({
handleSelect: onSelect,
sortOrder,
is_admin: currentUser?.is_admin
is_admin: currentUser?.is_admin,
onIPConfig: openIPConfigModal
});
return (
@@ -207,6 +225,13 @@ const APIKeys: React.FC = () => {
onCancel={handleModalCancel}
onOk={handleModalOk}
></AddAPIKeyModal>
{APIKeyIPConfigForm && (
<APIKeyIPConfigForm
open={openIPConfigModalStatus.open}
apiKey={openIPConfigModalStatus.currentData}
onClose={closeIPConfigModal}
/>
)}
<DeleteModal ref={modalRef}></DeleteModal>
</>
);
-1
View File
@@ -22,7 +22,6 @@ ${formatCurlArgs(parameters, isFormdata)}`.trim();
if (edit) {
curlCode = `
curl ${host}${api} \\
-H "Content-Type: multipart/form-data" \\
-H "Authorization: Bearer $\{YOUR_GPUSTACK_API_KEY}" \\${modelProxy ? `\n-H "X-GPUStack-Model: ${parameters.model}" \\` : ''}
-F image="@image.png" \\
-F mask="@mask.png" \\
+1 -1
View File
@@ -2,6 +2,7 @@ import { PageAction, PasswordReg } from '@/config';
import { PageActionType } from '@/config/types';
import {
Input as CInput,
FormDrawer,
IconFont,
Select as SealSelect,
Switch as SealSwitch
@@ -9,7 +10,6 @@ import {
import { useIntl, useModel } from '@umijs/max';
import { Form, Select } from 'antd';
import { useEffect } from 'react';
import FormDrawer from '../../_components/form-drawer';
import { UserRoles, UserRolesOptions } from '../config';
import { FormData, ListItem } from '../config/types';
+1 -1
View File
@@ -124,7 +124,7 @@ export interface AppPlugin {
/**
* components extension
*/
components?: Record<string, ComponentType>;
components?: Record<string, ComponentType<any>>;
/**
* Hooks extension