mirror of
https://github.com/didi/KnowStreaming.git
synced 2026-01-06 05:22:16 +08:00
初始化3.0.0版本
This commit is contained in:
@@ -0,0 +1,420 @@
|
||||
import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
||||
import { Button, Form, Input, Select, message, Drawer, Space, Divider, Utils, Radio, AutoComplete, Alert } from 'knowdesign';
|
||||
import api from '@src/api';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { UsersProps } from '../SecurityUsers';
|
||||
|
||||
// 字段对应后端存储值的枚举类型
|
||||
export enum ACL_OPERATION {
|
||||
Unknown,
|
||||
Any,
|
||||
All,
|
||||
Read,
|
||||
Write,
|
||||
Create,
|
||||
Delete,
|
||||
Alter,
|
||||
Describe,
|
||||
ClusterAction,
|
||||
DescribeConfigs,
|
||||
AlterConfigs,
|
||||
IdempotentWrite,
|
||||
}
|
||||
export enum ACL_PERMISSION_TYPE {
|
||||
Unknown,
|
||||
Any,
|
||||
Deny,
|
||||
Allow,
|
||||
}
|
||||
export enum ACL_PATTERN_TYPE {
|
||||
Unknown,
|
||||
Any,
|
||||
Match,
|
||||
Literal,
|
||||
Prefixed,
|
||||
}
|
||||
export enum ACL_RESOURCE_TYPE {
|
||||
Unknown,
|
||||
Any,
|
||||
Topic,
|
||||
Group,
|
||||
Cluster,
|
||||
TransactionalId,
|
||||
DelegationToken,
|
||||
}
|
||||
|
||||
export type RESOURCE_MAP_KEYS = Exclude<keyof typeof ACL_RESOURCE_TYPE, 'Unknown' | 'Any' | 'DelegationToken'>;
|
||||
|
||||
// 资源类型和操作映射表
|
||||
export const RESOURCE_TO_OPERATIONS_MAP: {
|
||||
[P in RESOURCE_MAP_KEYS]: string[];
|
||||
} = {
|
||||
Cluster: ['Alter', 'AlterConfigs', 'ClusterAction', 'Create', 'Describe', 'DescribeConfigs', 'IdempotentWrite'],
|
||||
Topic: ['Alter', 'AlterConfigs', 'Create', 'Delete', 'Describe', 'DescribeConfigs', 'Read', 'Write'],
|
||||
Group: ['Delete', 'Describe', 'Read'],
|
||||
TransactionalId: ['Write', 'Describe'],
|
||||
};
|
||||
|
||||
// ACL 配置类型
|
||||
const CONFIG_TYPE = [
|
||||
{
|
||||
label: '配置生产权限',
|
||||
value: 'produce',
|
||||
},
|
||||
{
|
||||
label: '配置消费权限',
|
||||
value: 'consume',
|
||||
},
|
||||
{
|
||||
label: '配置自定义权限',
|
||||
value: 'custom',
|
||||
},
|
||||
];
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
const AddDrawer = forwardRef((_, ref) => {
|
||||
const { clusterId } = useParams<{
|
||||
clusterId: string;
|
||||
}>();
|
||||
const [form] = Form.useForm();
|
||||
const [visible, setVisible] = useState<boolean>(false);
|
||||
const [kafkaUserOptions, setKafkaUserOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [confirmLoading, setConfirmLoading] = useState<boolean>(false);
|
||||
const callback = useRef(() => {
|
||||
return;
|
||||
});
|
||||
const [topicMetaData, setTopicMetaData] = React.useState([]);
|
||||
|
||||
// 获取 Topic 元信息
|
||||
const getTopicMetaData = (newValue: any) => {
|
||||
Utils.request(api.getTopicMetaData(+clusterId), {
|
||||
method: 'GET',
|
||||
params: { searchKeyword: newValue },
|
||||
}).then((res: UsersProps[]) => {
|
||||
const topics = (res || []).map((item: any) => {
|
||||
return {
|
||||
label: item.topicName,
|
||||
value: item.topicName,
|
||||
};
|
||||
});
|
||||
setTopicMetaData(topics);
|
||||
});
|
||||
};
|
||||
|
||||
// 获取 kafkaUser 列表
|
||||
const getKafkaUserList = () => {
|
||||
Utils.request(api.getKafkaUsers(clusterId), {
|
||||
method: 'GET',
|
||||
}).then((res: UsersProps[]) => {
|
||||
setKafkaUserOptions(res.map(({ name }) => ({ label: name, value: name })));
|
||||
});
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const onSubmit = () => {
|
||||
form.validateFields().then((formData) => {
|
||||
const submitData = [];
|
||||
const { configType, principle, kafkaUser } = formData;
|
||||
|
||||
if (configType === 'custom') {
|
||||
// 1. 自定义权限
|
||||
const { resourceType, resourcePatternType, aclPermissionType, aclOperation, aclClientHost } = formData;
|
||||
submitData.push({
|
||||
clusterId,
|
||||
kafkaUser: principle === 'all' ? '*' : kafkaUser,
|
||||
resourceType,
|
||||
resourcePatternType,
|
||||
resourceName: '*',
|
||||
aclPermissionType,
|
||||
aclOperation,
|
||||
aclClientHost,
|
||||
});
|
||||
} else {
|
||||
// 2. 生产或者消费权限
|
||||
// 1). 配置生产权限将赋予 User 对应 Topic 的 Create、Write 权限
|
||||
// 2). 配置消费权限将赋予 User 对应 Topic的 Read 权限和 Group 的 Read 权限
|
||||
const { topicPatternType, topicPrinciple, topicName } = formData;
|
||||
submitData.push({
|
||||
clusterId,
|
||||
kafkaUser: principle === 'all' ? '*' : kafkaUser,
|
||||
resourceType: ACL_RESOURCE_TYPE.Topic,
|
||||
resourcePatternType: topicPatternType,
|
||||
resourceName: topicPrinciple === 'all' ? '*' : topicName,
|
||||
aclPermissionType: ACL_PERMISSION_TYPE.Allow,
|
||||
aclOperation: configType === 'consume' ? ACL_OPERATION.Read : ACL_OPERATION.Create,
|
||||
aclClientHost: '*',
|
||||
});
|
||||
// 消费权限
|
||||
if (configType === 'consume') {
|
||||
const { groupPatternType, groupPrinciple, groupName } = formData;
|
||||
submitData.push({
|
||||
clusterId,
|
||||
kafkaUser: principle === 'all' ? '*' : kafkaUser,
|
||||
resourceType: ACL_RESOURCE_TYPE.Group,
|
||||
resourcePatternType: groupPatternType,
|
||||
resourceName: groupPrinciple === 'all' ? '*' : groupName,
|
||||
aclPermissionType: ACL_PERMISSION_TYPE.Allow,
|
||||
aclOperation: ACL_OPERATION.Read,
|
||||
aclClientHost: '*',
|
||||
});
|
||||
} else {
|
||||
submitData.push({
|
||||
clusterId,
|
||||
kafkaUser: principle === 'all' ? '*' : kafkaUser,
|
||||
resourceType: ACL_RESOURCE_TYPE.Topic,
|
||||
resourcePatternType: topicPatternType,
|
||||
resourceName: topicPrinciple === 'all' ? '*' : topicName,
|
||||
aclPermissionType: ACL_PERMISSION_TYPE.Allow,
|
||||
aclOperation: ACL_OPERATION.Write,
|
||||
aclClientHost: '*',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setConfirmLoading(true);
|
||||
Utils.request(api.addACL, {
|
||||
method: 'POST',
|
||||
data: submitData,
|
||||
}).then(
|
||||
() => {
|
||||
// 执行回调,刷新列表数据
|
||||
callback.current();
|
||||
|
||||
onClose();
|
||||
message.success('成功新增 ACL');
|
||||
},
|
||||
() => setConfirmLoading(false)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// 展开抽屉
|
||||
const onOpen = (status: boolean, cbk: () => void) => {
|
||||
setVisible(status);
|
||||
callback.current = cbk;
|
||||
};
|
||||
|
||||
// 关闭抽屉
|
||||
const onClose = () => {
|
||||
setVisible(false);
|
||||
setConfirmLoading(false);
|
||||
form.resetFields();
|
||||
};
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
onOpen,
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
getKafkaUserList();
|
||||
getTopicMetaData('');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
className="acls-edit-drawer"
|
||||
title="新增ACL"
|
||||
width={480}
|
||||
visible={visible}
|
||||
maskClosable={false}
|
||||
onClose={onClose}
|
||||
extra={
|
||||
<Space>
|
||||
<Button size="small" onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="primary" size="small" loading={confirmLoading} onClick={onSubmit}>
|
||||
确定
|
||||
</Button>
|
||||
<Divider type="vertical" />
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Alert
|
||||
className="drawer-alert-full-screen"
|
||||
message="新增 ACL 必须在集群已开启 ACL 功能时才会生效"
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginBottom: 20 }}
|
||||
/>
|
||||
<Form form={form} layout="vertical">
|
||||
<Form.Item
|
||||
label="ACL用途"
|
||||
name="configType"
|
||||
rules={[{ required: true, message: 'ACL用途不能为空' }]}
|
||||
initialValue={CONFIG_TYPE[0].value}
|
||||
>
|
||||
<Select options={CONFIG_TYPE} />
|
||||
</Form.Item>
|
||||
<Form.Item label="Principle" name="principle" rules={[{ required: true, message: 'Principle 不能为空' }]} initialValue="all">
|
||||
<Radio.Group>
|
||||
<Radio value="all">ALL</Radio>
|
||||
<Radio value="special">Special</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item dependencies={['principle']} style={{ marginBottom: 0 }}>
|
||||
{({ getFieldValue }) =>
|
||||
getFieldValue('principle') === 'special' ? (
|
||||
<Form.Item name="kafkaUser" rules={[{ required: true, message: 'Kafka User 不能为空' }]}>
|
||||
<Select placeholder="请选择 Kafka User" options={kafkaUserOptions} />
|
||||
</Form.Item>
|
||||
) : null
|
||||
}
|
||||
</Form.Item>
|
||||
<Form.Item dependencies={['configType']} style={{ marginBottom: 0 }}>
|
||||
{({ getFieldValue }) => {
|
||||
const PatternTypeFormItems = (props: { type: string }) => {
|
||||
const { type } = props;
|
||||
const UpperCaseType = type[0].toUpperCase() + type.slice(1);
|
||||
return (
|
||||
<div className="form-item-group">
|
||||
<Form.Item
|
||||
label={`${UpperCaseType} Pattern Type`}
|
||||
name={`${type}PatternType`}
|
||||
rules={[{ required: true, message: `${UpperCaseType} Pattern Type 不能为空` }]}
|
||||
initialValue={ACL_PATTERN_TYPE['Literal']}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={ACL_PATTERN_TYPE['Literal']}>Literal</Radio>
|
||||
<Radio value={ACL_PATTERN_TYPE['Prefixed']}>Prefixed</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={UpperCaseType}
|
||||
name={`${type}Principle`}
|
||||
rules={[{ required: true, message: `${UpperCaseType} 不能为空` }]}
|
||||
initialValue="all"
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value="all">ALL</Radio>
|
||||
<Radio value="special">Special</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item dependencies={[`${type}Principle`]} style={{ marginBottom: 0 }}>
|
||||
{({ getFieldValue }) =>
|
||||
getFieldValue(`${type}Principle`) === 'special' ? (
|
||||
<Form.Item
|
||||
name={`${type}Name`}
|
||||
dependencies={[`${type}PatternType`]}
|
||||
validateTrigger="onBlur"
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator: (rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject(`${UpperCaseType}Name 不能为空`);
|
||||
}
|
||||
if (type === 'topic' && getFieldValue(`${type}PatternType`) === ACL_PATTERN_TYPE['Literal']) {
|
||||
return Utils.request(api.getTopicMetadata(clusterId as any, value)).then((res: any) => {
|
||||
return res?.exist ? Promise.resolve() : Promise.reject('该 Topic 不存在');
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<AutoComplete
|
||||
filterOption={(value, option) => {
|
||||
if (option?.value.includes(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
options={topicMetaData}
|
||||
placeholder={`请输入 ${type}Name`}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : null
|
||||
}
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CustomFormItems = () => {
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label="Permission Type"
|
||||
name="aclPermissionType"
|
||||
rules={[{ required: true, message: 'Permission Type 不能为空' }]}
|
||||
initialValue={ACL_PERMISSION_TYPE['Allow']}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={ACL_PERMISSION_TYPE['Allow']}>Allow</Radio>
|
||||
<Radio value={ACL_PERMISSION_TYPE['Deny']}>Deny</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Pattern Type"
|
||||
name="resourcePatternType"
|
||||
rules={[{ required: true, message: 'Pattern Type 不能为空' }]}
|
||||
initialValue={ACL_PATTERN_TYPE['Literal']}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={ACL_PATTERN_TYPE['Literal']}>Literal</Radio>
|
||||
<Radio value={ACL_PATTERN_TYPE['Prefixed']}>Prefixed</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="Resource Type"
|
||||
name="resourceType"
|
||||
rules={[{ required: true, message: 'Resource Type 不能为空' }]}
|
||||
initialValue={ACL_RESOURCE_TYPE['Cluster']}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择 Resource Type"
|
||||
options={Object.keys(RESOURCE_TO_OPERATIONS_MAP).map((type: RESOURCE_MAP_KEYS) => ({
|
||||
label: type,
|
||||
value: ACL_RESOURCE_TYPE[type],
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item dependencies={['resourceType']} style={{ marginBottom: 0 }}>
|
||||
{({ getFieldValue }) => {
|
||||
form.resetFields(['aclOperation']);
|
||||
return (
|
||||
<Form.Item label="Operation" name="aclOperation" rules={[{ required: true, message: 'Operation 不能为空' }]}>
|
||||
<Select
|
||||
placeholder="请选择 Resource Type"
|
||||
options={RESOURCE_TO_OPERATIONS_MAP[ACL_RESOURCE_TYPE[getFieldValue('resourceType')] as RESOURCE_MAP_KEYS].map(
|
||||
(type) => ({
|
||||
label: type,
|
||||
value: ACL_OPERATION[type as keyof typeof ACL_OPERATION],
|
||||
})
|
||||
)}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item label="Host" name="aclClientHost" initialValue="*">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const type = getFieldValue('configType');
|
||||
if (type === 'produce') {
|
||||
return <PatternTypeFormItems type="topic" />;
|
||||
} else if (type === 'consume') {
|
||||
return (
|
||||
<>
|
||||
<PatternTypeFormItems type="topic" />
|
||||
<PatternTypeFormItems type="group" />
|
||||
</>
|
||||
);
|
||||
} else if (type === 'custom') {
|
||||
return <CustomFormItems />;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Drawer>
|
||||
);
|
||||
});
|
||||
|
||||
export default AddDrawer;
|
||||
@@ -0,0 +1,30 @@
|
||||
.security-acls-page {
|
||||
.card-bar {
|
||||
margin: 12px 0;
|
||||
}
|
||||
&-list {
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
padding: 16px 24px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.01), 0 3px 6px 3px rgba(0, 0, 0, 0.01), 0 2px 6px 0 rgba(0, 0, 0, 0.03);
|
||||
// border-radius: 12px;
|
||||
}
|
||||
.operate-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.acls-edit-drawer {
|
||||
.form-item-group {
|
||||
padding: 16px 20px 0 20px;
|
||||
margin-bottom: 16px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.dcloud-form-item-control-input {
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Button, Form, Input, Select, Modal, message, ProTable, AppContainer, DKSBreadcrumb, Utils } from 'knowdesign';
|
||||
import ACLsCardBar from '@src/components/CardBar/ACLsCardBar';
|
||||
import api from '@src/api';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import AddACLDrawer, {
|
||||
ACL_OPERATION,
|
||||
ACL_PERMISSION_TYPE,
|
||||
ACL_PATTERN_TYPE,
|
||||
ACL_RESOURCE_TYPE,
|
||||
RESOURCE_TO_OPERATIONS_MAP,
|
||||
RESOURCE_MAP_KEYS,
|
||||
} from './EditDrawer';
|
||||
import './index.less';
|
||||
|
||||
const { confirm } = Modal;
|
||||
|
||||
export type ACLsProps = {
|
||||
kafkaUser: string;
|
||||
resourceType: ACL_RESOURCE_TYPE;
|
||||
resourceName: string;
|
||||
resourcePatternType: ACL_PATTERN_TYPE;
|
||||
aclPermissionType: ACL_PATTERN_TYPE;
|
||||
aclOperation: ACL_OPERATION;
|
||||
aclClientHost: string;
|
||||
};
|
||||
|
||||
export const defaultPagination = {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
position: 'bottomRight',
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
};
|
||||
|
||||
const SecurityACLs = (): JSX.Element => {
|
||||
const [global] = AppContainer.useGlobalValue();
|
||||
const { clusterId } = useParams<{
|
||||
clusterId: string;
|
||||
}>();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [data, setData] = useState<ACLsProps[]>([]);
|
||||
const [pagination, setPagination] = useState<any>(defaultPagination);
|
||||
const [form] = Form.useForm();
|
||||
const editDrawerRef = useRef(null);
|
||||
|
||||
const getACLs = (query = {}) => {
|
||||
const formData = form.getFieldsValue();
|
||||
const queryData = {
|
||||
// 模糊查询
|
||||
fuzzySearchDTOList: [] as { fieldName: string; fieldValue: string }[],
|
||||
// 精确查询
|
||||
preciseFilterDTOList: [] as { fieldName: string; fieldValueList: (string | number)[] }[],
|
||||
};
|
||||
Object.entries(formData)
|
||||
.filter((i) => i[1])
|
||||
.forEach(([fieldName, fieldValue]: [string, any]) => {
|
||||
if (fieldName === 'resourceType') {
|
||||
queryData.preciseFilterDTOList.push({
|
||||
fieldName,
|
||||
fieldValueList: fieldValue.map((type: string) => ACL_RESOURCE_TYPE[type as RESOURCE_MAP_KEYS]),
|
||||
});
|
||||
} else {
|
||||
queryData.fuzzySearchDTOList.push({
|
||||
fieldName,
|
||||
fieldValue,
|
||||
});
|
||||
}
|
||||
});
|
||||
const queryParams = {
|
||||
pageNo: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
...queryData,
|
||||
...query,
|
||||
};
|
||||
|
||||
setLoading(true);
|
||||
Utils.request(api.getACLs(clusterId), {
|
||||
method: 'POST',
|
||||
data: queryParams,
|
||||
}).then(
|
||||
(res: any) => {
|
||||
const { pageNo, pageSize, total } = res.pagination;
|
||||
const pages = Math.ceil(total / pageSize);
|
||||
if (pageNo > pages && pages !== 0) {
|
||||
getACLs({ pageNo: pages });
|
||||
return false;
|
||||
}
|
||||
|
||||
setPagination({
|
||||
...pagination,
|
||||
current: pageNo,
|
||||
pageSize,
|
||||
total,
|
||||
});
|
||||
setData(res.bizData);
|
||||
setLoading(false);
|
||||
return true;
|
||||
},
|
||||
() => setLoading(false)
|
||||
);
|
||||
};
|
||||
|
||||
const columns = () => {
|
||||
const baseColumns = [
|
||||
{
|
||||
title: 'Principal',
|
||||
dataIndex: 'kafkaUser',
|
||||
},
|
||||
{
|
||||
title: 'Permission',
|
||||
dataIndex: 'aclPermissionType',
|
||||
render(type: number) {
|
||||
return ACL_PERMISSION_TYPE[type];
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Pattern Type',
|
||||
dataIndex: 'resourcePatternType',
|
||||
width: 180,
|
||||
render(type: number) {
|
||||
return ACL_PATTERN_TYPE[type];
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Operations',
|
||||
dataIndex: 'aclOperation',
|
||||
render(type: number) {
|
||||
return ACL_OPERATION[type];
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Resource',
|
||||
dataIndex: 'resourceType',
|
||||
render(type: number, record: ACLsProps) {
|
||||
return `${ACL_RESOURCE_TYPE[type]} ${record.resourceName}`;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Host',
|
||||
dataIndex: 'aclClientHost',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: '',
|
||||
width: 120,
|
||||
render(record: ACLsProps) {
|
||||
return (
|
||||
<>
|
||||
<Button type="link" size="small" style={{ paddingLeft: 0 }} onClick={() => onDelete(record)}>
|
||||
删除
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return baseColumns;
|
||||
};
|
||||
|
||||
const onDelete = (record: ACLsProps) => {
|
||||
confirm({
|
||||
title: '确定删除 ACL 吗?',
|
||||
okText: '删除',
|
||||
okType: 'primary',
|
||||
centered: true,
|
||||
okButtonProps: {
|
||||
size: 'small',
|
||||
danger: true,
|
||||
},
|
||||
cancelButtonProps: {
|
||||
size: 'small',
|
||||
},
|
||||
onOk() {
|
||||
return Utils.request(api.delACLs, {
|
||||
method: 'DELETE',
|
||||
data: { ...record, clusterId: Number(clusterId) },
|
||||
}).then((_) => {
|
||||
message.success('删除成功');
|
||||
getACLs();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onTableChange = (curPagination: any) => {
|
||||
getACLs({ pageNo: curPagination.current, pageSize: curPagination.pageSize });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getACLs();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="security-acls-page">
|
||||
<DKSBreadcrumb
|
||||
breadcrumbs={[
|
||||
{ label: '多集群管理', aHref: '/' },
|
||||
{ label: global?.clusterInfo?.name, aHref: `/cluster/${global?.clusterInfo?.id}` },
|
||||
{ label: 'ACLs', aHref: `` },
|
||||
]}
|
||||
/>
|
||||
<div className="card-bar">
|
||||
<ACLsCardBar />
|
||||
</div>
|
||||
<div className="security-acls-page-list clustom-table-content">
|
||||
<div className="operate-bar">
|
||||
<Form form={form} layout="inline" onFinish={() => getACLs({ page: 1 })}>
|
||||
<Form.Item name="kafkaUser">
|
||||
<Input placeholder="请输入 Principal" />
|
||||
</Form.Item>
|
||||
<Form.Item name="resourceType">
|
||||
<Select
|
||||
placeholder="选择 ResourceType"
|
||||
options={Object.keys(RESOURCE_TO_OPERATIONS_MAP).map((key) => ({ label: key, value: key }))}
|
||||
mode="multiple"
|
||||
maxTagCount="responsive"
|
||||
allowClear
|
||||
style={{ width: 200 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="resourceName">
|
||||
<Input placeholder="请输入 Resource" />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" ghost htmlType="submit">
|
||||
查询
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<Button
|
||||
type="primary"
|
||||
// icon={<PlusOutlined />}
|
||||
onClick={() => editDrawerRef.current.onOpen(true, getACLs)}
|
||||
>
|
||||
新增ACL
|
||||
</Button>
|
||||
</div>
|
||||
<ProTable
|
||||
tableProps={{
|
||||
showHeader: false,
|
||||
loading,
|
||||
rowKey: 'id',
|
||||
dataSource: data,
|
||||
paginationProps: pagination,
|
||||
columns: columns() as any,
|
||||
lineFillColor: true,
|
||||
attrs: {
|
||||
onChange: onTableChange,
|
||||
scroll: { y: 'calc(100vh - 400px)' },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 新增 ACL 抽屉 */}
|
||||
<AddACLDrawer ref={editDrawerRef} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SecurityACLs;
|
||||
Reference in New Issue
Block a user