mirror of
https://github.com/didi/KnowStreaming.git
synced 2025-12-24 03:42:07 +08:00
refactor: 接入/编辑集群优化
This commit is contained in:
@@ -6,9 +6,8 @@ import { regClusterName, regUsername } from '@src/constants/reg';
|
||||
import { bootstrapServersErrCodes, jmxErrCodes, zkErrCodes } from './config';
|
||||
import CodeMirrorFormItem from '@src/components/CodeMirrorFormItem';
|
||||
|
||||
const rows = 4;
|
||||
const lowKafkaVersion = '2.8.0';
|
||||
const clientPropertiesPlaceholder = `用于创建Kafka客户端进行信息获取的相关配置,
|
||||
const LOW_KAFKA_VERSION = '2.8.0';
|
||||
const CLIENT_PROPERTIES_PLACEHOLDER = `用于创建Kafka客户端进行信息获取的相关配置,
|
||||
例如开启SCRAM-SHA-256安全管控模式的集群需输入如下配置,
|
||||
未开启安全管控可不进行任何输入:
|
||||
{
|
||||
@@ -26,37 +25,25 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
const intl = useIntl();
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [confirmLoading, setConfirmLoading] = React.useState(false);
|
||||
const [curClusterInfo, setCurClusterInfo] = React.useState<any>({});
|
||||
const [security, setSecurity] = React.useState(curClusterInfo?.security || 'None');
|
||||
const [extra, setExtra] = React.useState({
|
||||
versionExtra: '',
|
||||
zooKeeperExtra: '',
|
||||
bootstrapExtra: '',
|
||||
jmxExtra: '',
|
||||
});
|
||||
const [isLowVersion, setIsLowVersion] = React.useState<boolean>(false);
|
||||
const [zookeeperErrorStatus, setZookeeperErrorStatus] = React.useState<boolean>(false);
|
||||
|
||||
const lastFormItemValue = React.useRef({
|
||||
bootstrap: curClusterInfo?.bootstrapServers || '',
|
||||
bootstrapServers: curClusterInfo?.bootstrapServers || '',
|
||||
zookeeper: curClusterInfo?.zookeeper || '',
|
||||
clientProperties: curClusterInfo?.clientProperties || {},
|
||||
});
|
||||
|
||||
const onHandleValuesChange = (value: any, allValues: any) => {
|
||||
Object.keys(value).forEach((key) => {
|
||||
const onHandleValuesChange = (changedValue: string[]) => {
|
||||
Object.keys(changedValue).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'security':
|
||||
setSecurity(value.security);
|
||||
break;
|
||||
case 'zookeeper':
|
||||
setExtra({
|
||||
...extra,
|
||||
zooKeeperExtra: '',
|
||||
bootstrapExtra: '',
|
||||
jmxExtra: '',
|
||||
});
|
||||
break;
|
||||
case 'bootstrapServers':
|
||||
setExtra({
|
||||
...extra,
|
||||
@@ -78,21 +65,19 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
const onCancel = () => {
|
||||
form.resetFields();
|
||||
setLoading(false);
|
||||
setZookeeperErrorStatus(false);
|
||||
setIsLowVersion(false);
|
||||
setSecurity('None');
|
||||
setExtra({
|
||||
versionExtra: '',
|
||||
zooKeeperExtra: '',
|
||||
bootstrapExtra: '',
|
||||
jmxExtra: '',
|
||||
});
|
||||
lastFormItemValue.current = { bootstrap: '', zookeeper: '', clientProperties: {} };
|
||||
lastFormItemValue.current = { bootstrapServers: '', zookeeper: '', clientProperties: {} };
|
||||
props.setVisible && props.setVisible(false);
|
||||
};
|
||||
|
||||
const onSubmit = () => {
|
||||
form.validateFields().then((res) => {
|
||||
setConfirmLoading(true);
|
||||
let clientProperties = null;
|
||||
try {
|
||||
clientProperties = res.clientProperties && JSON.parse(res.clientProperties);
|
||||
@@ -107,7 +92,7 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
jmxProperties: {
|
||||
jmxPort: res.jmxPort,
|
||||
maxConn: res.maxConn,
|
||||
openSSL: res.security === 'Password',
|
||||
openSSL: res.openSSL || false,
|
||||
token: res.token,
|
||||
username: res.username,
|
||||
},
|
||||
@@ -115,7 +100,7 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
name: res.name,
|
||||
zookeeper: res.zookeeper || '',
|
||||
};
|
||||
setLoading(true);
|
||||
|
||||
if (!isNaN(curClusterInfo?.id)) {
|
||||
Utils.put(api.phyCluster, {
|
||||
...params,
|
||||
@@ -127,7 +112,7 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
onCancel();
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
setConfirmLoading(false);
|
||||
});
|
||||
} else {
|
||||
Utils.post(api.phyCluster, params)
|
||||
@@ -137,7 +122,7 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
onCancel();
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
setConfirmLoading(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -154,125 +139,224 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setIsLowVersion(false);
|
||||
setZookeeperErrorStatus(false);
|
||||
|
||||
return Utils.post(api.kafkaValidator, {
|
||||
bootstrapServers: bootstrapServers || '',
|
||||
zookeeper: zookeeper || '',
|
||||
clientProperties,
|
||||
})
|
||||
.then((res: any) => {
|
||||
form.setFieldsValue({
|
||||
jmxPort: res.jmxPort,
|
||||
});
|
||||
.then(
|
||||
(res: {
|
||||
errList: { code: number; message: string; data: any }[];
|
||||
jmxPort: number | null;
|
||||
kafkaVersion: string | null;
|
||||
zookeeper: string | null;
|
||||
}) => {
|
||||
const changedValue: { jmxPort?: number; kafkaVersion?: string; zookeeper: string } = {
|
||||
zookeeper: zookeeper || res.zookeeper,
|
||||
};
|
||||
if (res.kafkaVersion && props.kafkaVersion.includes(res.kafkaVersion)) {
|
||||
changedValue.kafkaVersion = res.kafkaVersion;
|
||||
}
|
||||
if (res.jmxPort) {
|
||||
changedValue.jmxPort = res.jmxPort;
|
||||
}
|
||||
form.setFieldsValue(changedValue);
|
||||
|
||||
if (props.kafkaVersion.indexOf(res.kafkaVersion) > -1) {
|
||||
form.setFieldsValue({
|
||||
kafkaVersion: res.kafkaVersion,
|
||||
});
|
||||
} else {
|
||||
form.setFieldsValue({
|
||||
kafkaVersion: undefined,
|
||||
const extraMsg = {
|
||||
...extra,
|
||||
// 重置默认信息为连接成功
|
||||
bootstrapExtra: bootstrapServers ? '连接成功' : '',
|
||||
zooKeeperExtra: zookeeper ? '连接成功' : '',
|
||||
};
|
||||
|
||||
const errList = res.errList || [];
|
||||
// 处理错误信息
|
||||
errList.forEach((item: any) => {
|
||||
const { code, message } = item;
|
||||
let modifyKey: 'bootstrapExtra' | 'zooKeeperExtra' | 'jmxExtra' | undefined;
|
||||
if (bootstrapServersErrCodes.includes(code)) {
|
||||
modifyKey = 'bootstrapExtra';
|
||||
} else if (zkErrCodes.includes(code)) {
|
||||
modifyKey = 'zooKeeperExtra';
|
||||
} else if (jmxErrCodes.includes(code)) {
|
||||
modifyKey = 'jmxExtra';
|
||||
}
|
||||
|
||||
if (modifyKey) {
|
||||
extraMsg[modifyKey] = message;
|
||||
}
|
||||
});
|
||||
|
||||
setExtra(extraMsg);
|
||||
return res;
|
||||
}
|
||||
|
||||
form.setFieldsValue({
|
||||
zookeeper: zookeeper || res.zookeeper,
|
||||
});
|
||||
|
||||
const errList = res.errList || [];
|
||||
|
||||
const extraMsg = extra;
|
||||
|
||||
// 初始化信息为连接成功
|
||||
extraMsg.bootstrapExtra = bootstrapServers ? '连接成功' : '';
|
||||
extraMsg.zooKeeperExtra = zookeeper ? '连接成功' : '';
|
||||
|
||||
// 处理错误信息
|
||||
errList.forEach((item: any) => {
|
||||
const { code, message } = item;
|
||||
let modifyKey: 'bootstrapExtra' | 'zooKeeperExtra' | 'jmxExtra' | undefined;
|
||||
if (bootstrapServersErrCodes.includes(code)) {
|
||||
modifyKey = 'bootstrapExtra';
|
||||
} else if (zkErrCodes.includes(code)) {
|
||||
modifyKey = 'zooKeeperExtra';
|
||||
} else if (jmxErrCodes.includes(code)) {
|
||||
modifyKey = 'jmxExtra';
|
||||
}
|
||||
|
||||
if (modifyKey) {
|
||||
extraMsg[modifyKey] = message;
|
||||
}
|
||||
});
|
||||
|
||||
// 如果kafkaVersion小于最低版本则提示
|
||||
const showLowVersion = !(
|
||||
curClusterInfo?.zookeeper ||
|
||||
!curClusterInfo?.kafkaVersion ||
|
||||
curClusterInfo?.kafkaVersion >= lowKafkaVersion
|
||||
);
|
||||
setIsLowVersion(showLowVersion);
|
||||
setExtra({
|
||||
...extraMsg,
|
||||
versionExtra: showLowVersion ? intl.formatMessage({ id: 'access.cluster.low.version.tip' }) : '',
|
||||
});
|
||||
return res;
|
||||
})
|
||||
)
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
// 更新表单状态
|
||||
React.useEffect(() => {
|
||||
const showLowVersion = !(curClusterInfo?.zookeeper || !curClusterInfo?.kafkaVersion || curClusterInfo?.kafkaVersion >= lowKafkaVersion);
|
||||
lastFormItemValue.current = {
|
||||
bootstrap: curClusterInfo?.bootstrapServers || '',
|
||||
bootstrapServers: curClusterInfo?.bootstrapServers || '',
|
||||
zookeeper: curClusterInfo?.zookeeper || '',
|
||||
clientProperties: curClusterInfo?.clientProperties || {},
|
||||
};
|
||||
setIsLowVersion(showLowVersion);
|
||||
setExtra({
|
||||
...extra,
|
||||
versionExtra: showLowVersion ? intl.formatMessage({ id: 'access.cluster.low.version.tip' }) : '',
|
||||
});
|
||||
form.setFieldsValue({ ...curClusterInfo });
|
||||
if (curClusterInfo?.kafkaVersion) {
|
||||
form.validateFields(['kafkaVersion']);
|
||||
}
|
||||
}, [curClusterInfo]);
|
||||
|
||||
// 获取集群详情数据
|
||||
React.useEffect(() => {
|
||||
if (visible) {
|
||||
if (clusterInfo?.id) {
|
||||
setLoading(true);
|
||||
|
||||
const resolveJmxProperties = (obj: any) => {
|
||||
const res = { ...obj };
|
||||
try {
|
||||
const originValue = obj?.jmxProperties;
|
||||
if (originValue) {
|
||||
const jmxProperties = JSON.parse(originValue);
|
||||
typeof jmxProperties === 'object' && jmxProperties !== null && Object.assign(res, jmxProperties);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('jmxProperties not JSON: ', err);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
Utils.request(api.getPhyClusterBasic(clusterInfo.id))
|
||||
.then((res: any) => {
|
||||
let jmxProperties = null;
|
||||
try {
|
||||
jmxProperties = JSON.parse(res?.jmxProperties);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
// 转化值对应成表单值
|
||||
if (jmxProperties?.openSSL) {
|
||||
jmxProperties.security = 'Password';
|
||||
}
|
||||
|
||||
if (jmxProperties) {
|
||||
res = Object.assign({}, res || {}, jmxProperties);
|
||||
}
|
||||
setCurClusterInfo(res);
|
||||
setLoading(false);
|
||||
setCurClusterInfo(resolveJmxProperties(res));
|
||||
})
|
||||
.catch((err) => {
|
||||
setCurClusterInfo(clusterInfo);
|
||||
setCurClusterInfo(resolveJmxProperties(clusterInfo));
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
setCurClusterInfo(clusterInfo);
|
||||
setCurClusterInfo({});
|
||||
}
|
||||
}
|
||||
}, [visible, clusterInfo]);
|
||||
|
||||
const validators = {
|
||||
name: async (_: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('集群名称不能为空');
|
||||
}
|
||||
if (value === curClusterInfo?.name) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (value?.length > 128) {
|
||||
return Promise.reject('集群名称长度限制在1~128字符');
|
||||
}
|
||||
if (!new RegExp(regClusterName).test(value)) {
|
||||
return Promise.reject('集群名称支持中英文、数字、特殊字符 ! " # $ % & \' ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~');
|
||||
}
|
||||
return Utils.request(api.getClusterBasicExit(value))
|
||||
.then((res: any) => {
|
||||
const data = res || {};
|
||||
return data?.exist ? Promise.reject('集群名称重复') : Promise.resolve();
|
||||
})
|
||||
.catch(() => Promise.reject('连接超时! 请重试或检查服务'));
|
||||
},
|
||||
bootstrapServers: async (_: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('Bootstrap Servers不能为空');
|
||||
}
|
||||
if (value.length > 2000) {
|
||||
return Promise.reject('Bootstrap Servers长度限制在2000字符');
|
||||
}
|
||||
if (value && value !== lastFormItemValue.current.bootstrapServers) {
|
||||
lastFormItemValue.current.bootstrapServers = value;
|
||||
return connectTest().catch(() => (lastFormItemValue.current.bootstrapServers = ''));
|
||||
}
|
||||
return Promise.resolve('');
|
||||
},
|
||||
zookeeper: async (_: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
|
||||
if (value.length > 2000) {
|
||||
return Promise.reject('Zookeeper长度限制在2000字符');
|
||||
}
|
||||
|
||||
if (value && value !== lastFormItemValue.current.zookeeper) {
|
||||
lastFormItemValue.current.zookeeper = value;
|
||||
return connectTest().catch(() => (lastFormItemValue.current.zookeeper = ''));
|
||||
}
|
||||
return Promise.resolve('');
|
||||
},
|
||||
securityUserName: async (_: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('用户名不能为空');
|
||||
}
|
||||
if (!new RegExp(regUsername).test(value)) {
|
||||
return Promise.reject('仅支持大小写、下划线、短划线(-)');
|
||||
}
|
||||
if (value.length > 128) {
|
||||
return Promise.reject('用户名长度限制在1~128字符');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
securityToken: async (_: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('密码不能为空');
|
||||
}
|
||||
if (!new RegExp(regUsername).test(value)) {
|
||||
return Promise.reject('密码只能由大小写、下划线、短划线(-)组成');
|
||||
}
|
||||
if (value.length < 6 || value.length > 32) {
|
||||
return Promise.reject('密码长度限制在6~32字符');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
kafkaVersion: async (_: any, value: any) => {
|
||||
if (!value) {
|
||||
return Promise.reject('版本号不能为空');
|
||||
}
|
||||
// 检测版本号小于2.8.0,如果没有填zookeeper信息,才会提示
|
||||
const zookeeper = form.getFieldValue('zookeeper');
|
||||
let versionExtra = '';
|
||||
if (value < LOW_KAFKA_VERSION && !zookeeper) {
|
||||
versionExtra = intl.formatMessage({ id: 'access.cluster.low.version.tip' });
|
||||
}
|
||||
setExtra({
|
||||
...extra,
|
||||
versionExtra,
|
||||
});
|
||||
return Promise.resolve();
|
||||
},
|
||||
clientProperties: async (_: any, value: string) => {
|
||||
try {
|
||||
if (value) {
|
||||
JSON.parse(value);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error('输入内容必须为 JSON'));
|
||||
}
|
||||
},
|
||||
description: async (_: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
if (value && value.length > 200) {
|
||||
return Promise.reject('集群描述长度限制在200字符');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Drawer
|
||||
@@ -285,14 +369,14 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
<Button size="small" onClick={onCancel}>
|
||||
取消
|
||||
</Button>
|
||||
<Button size="small" type="primary" onClick={onSubmit}>
|
||||
<Button size="small" type="primary" loading={confirmLoading} onClick={onSubmit}>
|
||||
确定
|
||||
</Button>
|
||||
<Divider type="vertical" />
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
title={intl.formatMessage({ id: props.title || 'access.cluster' })}
|
||||
title={intl.formatMessage({ id: props.title || clusterInfo?.id ? 'edit.cluster' : 'access.cluster' })}
|
||||
visible={props.visible}
|
||||
placement="right"
|
||||
width={480}
|
||||
@@ -306,30 +390,7 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
validator: async (rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('集群名称不能为空');
|
||||
}
|
||||
if (value === curClusterInfo?.name) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (value?.length > 128) {
|
||||
return Promise.reject('集群名称长度限制在1~128字符');
|
||||
}
|
||||
if (!new RegExp(regClusterName).test(value)) {
|
||||
return Promise.reject(
|
||||
'集群名称支持中英文、数字、特殊字符 ! " # $ % & \' ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~'
|
||||
);
|
||||
}
|
||||
return Utils.request(api.getClusterBasicExit(value)).then((res: any) => {
|
||||
const data = res || {};
|
||||
if (data?.exist) {
|
||||
return Promise.reject('集群名称重复');
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
},
|
||||
validator: validators.name,
|
||||
},
|
||||
]}
|
||||
>
|
||||
@@ -343,26 +404,7 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
validator: async (rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('Bootstrap Servers不能为空');
|
||||
}
|
||||
if (value.length > 2000) {
|
||||
return Promise.reject('Bootstrap Servers长度限制在2000字符');
|
||||
}
|
||||
if (value && value !== lastFormItemValue.current.bootstrap) {
|
||||
return connectTest()
|
||||
.then((res: any) => {
|
||||
lastFormItemValue.current.bootstrap = value;
|
||||
|
||||
return Promise.resolve('');
|
||||
})
|
||||
.catch((err) => {
|
||||
return Promise.reject('连接失败');
|
||||
});
|
||||
}
|
||||
return Promise.resolve('');
|
||||
},
|
||||
validator: validators.bootstrapServers,
|
||||
},
|
||||
]}
|
||||
>
|
||||
@@ -375,35 +417,10 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
name="zookeeper"
|
||||
label="Zookeeper"
|
||||
extra={<span className={!extra.zooKeeperExtra.includes('连接成功') ? 'error-extra-info' : ''}>{extra.zooKeeperExtra}</span>}
|
||||
validateStatus={zookeeperErrorStatus ? 'error' : 'success'}
|
||||
validateTrigger={'onBlur'}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
validator: async (rule: any, value: string) => {
|
||||
if (!value) {
|
||||
setZookeeperErrorStatus(false);
|
||||
return Promise.resolve('');
|
||||
}
|
||||
|
||||
if (value.length > 2000) {
|
||||
return Promise.reject('Zookeeper长度限制在2000字符');
|
||||
}
|
||||
|
||||
if (value && value !== lastFormItemValue.current.zookeeper) {
|
||||
return connectTest()
|
||||
.then((res: any) => {
|
||||
lastFormItemValue.current.zookeeper = value;
|
||||
setZookeeperErrorStatus(false);
|
||||
return Promise.resolve('');
|
||||
})
|
||||
.catch((err) => {
|
||||
setZookeeperErrorStatus(true);
|
||||
return Promise.reject('连接失败');
|
||||
});
|
||||
}
|
||||
return Promise.resolve('');
|
||||
},
|
||||
validator: validators.zookeeper,
|
||||
},
|
||||
]}
|
||||
>
|
||||
@@ -412,142 +429,65 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
placeholder="请输入Zookeeper地址,例如:192.168.0.1:2181,192.168.0.2:2181,192.168.0.2:2181/ks-kafka"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
className="no-item-control"
|
||||
name="Metrics"
|
||||
label="Metrics"
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: '',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<></>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="jmxPort"
|
||||
label="JMX Port"
|
||||
className="inline-item adjust-height-style"
|
||||
extra={extra.jmxExtra}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: '',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber style={{ width: 134 }} min={0} max={99999} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="maxConn"
|
||||
label="MaxConn"
|
||||
className="inline-item adjust-height-style"
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: '',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber style={{ width: 134 }} min={0} max={99999} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="security"
|
||||
label="Security"
|
||||
className="inline-item adjust-height-style"
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: '',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value="None">None</Radio>
|
||||
<Radio value="Password">Password Authentication</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{security === 'Password' ? (
|
||||
<>
|
||||
<Form.Item
|
||||
className="inline-item max-width-66"
|
||||
name="username"
|
||||
label="User Info"
|
||||
style={{ width: '58%' }}
|
||||
rules={[
|
||||
{
|
||||
required: security === 'Password' || curClusterInfo?.security === 'Password',
|
||||
validator: async (rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('用户名不能为空');
|
||||
}
|
||||
if (!new RegExp(regUsername).test(value)) {
|
||||
return Promise.reject('仅支持大小写、下划线、短划线(-)');
|
||||
}
|
||||
if (value.length > 128) {
|
||||
return Promise.reject('用户名长度限制在1~128字符');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入用户名" />
|
||||
<Form.Item className="metrics-form-item" label="Metrics">
|
||||
<div className="horizontal-form-container">
|
||||
<div className="inline-items">
|
||||
<Form.Item name="jmxPort" label="JMX Port :" extra={extra.jmxExtra}>
|
||||
<InputNumber min={0} max={99999} style={{ width: 129 }} />
|
||||
</Form.Item>
|
||||
<Form.Item name="maxConn" label="Max Conn :">
|
||||
<InputNumber addonAfter="个" min={0} max={99999} style={{ width: 124 }} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item name="openSSL" label="Security :">
|
||||
<Radio.Group>
|
||||
<Radio value={false}>None</Radio>
|
||||
<Radio value={true}>Password Authentication</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
className="inline-item"
|
||||
name="token"
|
||||
label=""
|
||||
style={{ width: '38%', marginRight: 0 }}
|
||||
rules={[
|
||||
{
|
||||
required: security === 'Password' || curClusterInfo?.security === 'Password',
|
||||
validator: async (rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject('密码不能为空');
|
||||
}
|
||||
if (!new RegExp(regUsername).test(value)) {
|
||||
return Promise.reject('密码只能由大小写、下划线、短划线(-)组成');
|
||||
}
|
||||
if (value.length < 6 || value.length > 32) {
|
||||
return Promise.reject('密码长度限制在6~32字符');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入密码" />
|
||||
<Form.Item dependencies={['openSSL']} noStyle>
|
||||
{({ getFieldValue }) => {
|
||||
return getFieldValue('openSSL') ? (
|
||||
<div className="user-info-form-items">
|
||||
<Form.Item className="user-info-label" label="User Info :" required />
|
||||
<div className="inline-items">
|
||||
<Form.Item
|
||||
name="username"
|
||||
rules={[
|
||||
{
|
||||
validator: validators.securityUserName,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入用户名" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
className="token-form-item"
|
||||
name="token"
|
||||
rules={[
|
||||
{
|
||||
validator: validators.securityToken,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入密码" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
}}
|
||||
</Form.Item>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="kafkaVersion"
|
||||
label="Version"
|
||||
dependencies={['zookeeper']}
|
||||
extra={<span className="error-extra-info">{extra.versionExtra}</span>}
|
||||
validateStatus={isLowVersion ? 'error' : 'success'}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
validator: async (rule: any, value: any) => {
|
||||
if (!value) {
|
||||
setIsLowVersion(true);
|
||||
return Promise.reject('版本号不能为空');
|
||||
}
|
||||
// 检测版本号小于2.8.0,如果没有填zookeeper信息,才会提示
|
||||
const zookeeper = form.getFieldValue('zookeeper');
|
||||
if (value < lowKafkaVersion && !zookeeper) {
|
||||
setIsLowVersion(true);
|
||||
setExtra({
|
||||
...extra,
|
||||
versionExtra: intl.formatMessage({ id: 'access.cluster.low.version.tip' }),
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
setIsLowVersion(false);
|
||||
return Promise.resolve();
|
||||
},
|
||||
validator: validators.kafkaVersion,
|
||||
},
|
||||
]}
|
||||
>
|
||||
@@ -565,29 +505,15 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
label="集群配置"
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: '请输入集群配置',
|
||||
validator: validators.clientProperties,
|
||||
},
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
try {
|
||||
if (value) {
|
||||
JSON.parse(value);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
return Promise.reject(new Error('输入内容必须为 JSON'));
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<div>
|
||||
<CodeMirrorFormItem
|
||||
resize
|
||||
defaultInput={form.getFieldValue('clientProperties')}
|
||||
placeholder={clientPropertiesPlaceholder}
|
||||
placeholder={CLIENT_PROPERTIES_PLACEHOLDER}
|
||||
onBeforeChange={(clientProperties: string) => {
|
||||
form.setFieldsValue({ clientProperties });
|
||||
form.validateFields(['clientProperties']);
|
||||
@@ -621,20 +547,11 @@ const AccessClusters = (props: any): JSX.Element => {
|
||||
label="集群描述"
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
validator: async (rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
if (value && value.length > 200) {
|
||||
return Promise.reject('集群描述长度限制在200字符');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
validator: validators.description,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input.TextArea rows={rows} />
|
||||
<Input.TextArea rows={4} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
|
||||
@@ -656,43 +656,37 @@
|
||||
color: @error-color;
|
||||
}
|
||||
}
|
||||
.inline-item.dcloud-form-item {
|
||||
display: -webkit-inline-box;
|
||||
margin-right: 16px;
|
||||
|
||||
&.adjust-height-style {
|
||||
.dcloud-form-item-label {
|
||||
padding: 0;
|
||||
label {
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
.dcloud-form-item-control {
|
||||
&-input {
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
.horizontal-form-container {
|
||||
padding-left: 16px;
|
||||
.inline-items {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&.max-width-66 {
|
||||
.dcloud-form-item-control {
|
||||
max-width: 66%;
|
||||
}
|
||||
}
|
||||
|
||||
.dcloud-form-item-label {
|
||||
margin-right: 12px;
|
||||
|
||||
label {
|
||||
.dcloud-form-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
&-label {
|
||||
padding: 0 12px 0 0;
|
||||
font-size: 13px;
|
||||
font-family: @font-family;
|
||||
color: #74788d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-item-control {
|
||||
margin-bottom: 8px !important;
|
||||
.dcloud-form-item-control {
|
||||
display: none;
|
||||
.metrics-form-item {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.user-info-form-items {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
.user-info-label {
|
||||
padding-top: 4px;
|
||||
}
|
||||
.inline-items {
|
||||
flex: 0 0 80%;
|
||||
.token-form-item {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user