import { useEffect, useState, useRef } from 'react'; import { useModel } from 'umi'; import { Space, Button, Tooltip, Select, Popconfirm, message } from 'antd'; import { QuestionCircleOutlined, DeleteOutlined } from '@ant-design/icons'; import { ProCard, ProForm, ProFormText, ProFormSelect, EditableProTable, } from '@ant-design/pro-components'; import type { ProColumns, EditableFormInstance, } from '@ant-design/pro-components'; import { getObdInfo } from '@/services/ob-deploy-web/Info'; import useRequest from '@/utils/useRequest'; import { handleQuit } from '@/utils'; import { commonStyle, pathRule } from '../constants'; import ServerTags from './ServerTags'; import styles from './index.less'; interface FormValues extends API.Components { auth?: { user?: string; password?: string; }; home_path?: string; } export default function NodeConfig() { const { setCurrentStep, configData, setConfigData, currentType, lowVersion, handleQuitProgress, nameIndex, setNameIndex, } = useModel('global'); const { components = {}, auth, home_path } = configData || {}; const { oceanbase = {}, ocpexpress = {}, obproxy = {} } = components; const [form] = ProForm.useForm(); const [editableForm] = ProForm.useForm(); const tableFormRef = useRef>(); const initDBConfigData = oceanbase?.topology?.length ? oceanbase?.topology?.map((item: API.Zone, index: number) => ({ id: (Date.now() + index).toString(), ...item, servers: item?.servers?.map((server) => server?.ip), })) : [ { id: (Date.now() + 1).toString(), name: 'zone1', servers: [], rootservice: undefined, }, { id: (Date.now() + 2).toString(), name: 'zone2', servers: [], rootservice: undefined, }, { id: (Date.now() + 3).toString(), name: 'zone3', servers: [], rootservice: undefined, }, ]; const homePathSuffix = `/${oceanbase.appname}`; const initHomePath = home_path ? home_path.substring(0, home_path.length - homePathSuffix.length) : undefined; const [dbConfigData, setDBConfigData] = useState(initDBConfigData); const [editableKeys, setEditableRowKeys] = useState(() => dbConfigData.map((item) => item.id), ); // all servers const [allOBServer, setAllOBServer] = useState([]); // all zone servers const [allZoneOBServer, setAllZoneOBServer] = useState({}); const [lastDeleteServer, setLastDeleteServer] = useState(''); const serverReg = /^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])?$/; const { run: getUserInfo } = useRequest(getObdInfo, { onSuccess: ({ success, data }: API.OBResponseServiceInfo_) => { if (success) { form.setFieldsValue({ auth: { user: data?.user || undefined, }, home_path: data?.user === 'root' ? '/root' : `/home/${data?.user}`, }); } }, }); const handleDelete = (id: string) => { setDBConfigData(dbConfigData.filter((item) => item.id !== id)); }; const setData = (dataSource: FormValues) => { let newComponents: API.Components = {}; if (currentType === 'all') { newComponents.obproxy = { ...(components.obproxy || {}), ...dataSource.obproxy, }; if (!lowVersion) { newComponents.ocpexpress = { ...(components.ocpexpress || {}), ...dataSource?.ocpexpress, }; } newComponents.obagent = { ...(components.obagent || {}), servers: allOBServer, }; } newComponents.oceanbase = { ...(components.oceanbase || {}), topology: dbConfigData?.map((item) => ({ ...item, servers: item?.servers?.map((server) => ({ ip: server })), })), }; setConfigData({ ...configData, components: newComponents, auth: dataSource.auth, home_path: `${ dataSource.home_path ? `${dataSource.home_path}${homePathSuffix}` : undefined }`, }); }; const prevStep = () => { const formValues = form.getFieldsValue(true); setData(formValues); setCurrentStep(1); }; const nextStep = () => { const tableFormRefValidate = () => { return tableFormRef?.current?.validateFields().then((values) => { return values; }); }; const formValidate = () => { return form.validateFields().then((values) => { return values; }); }; Promise.all([tableFormRefValidate(), formValidate()]).then((result) => { const formValues = result?.[1]; setData(formValues); setCurrentStep(3); }); }; const formatOptions = (data: string[]) => data?.map((item) => ({ label: item, value: item })); const getAllServers = (dataSource: API.DBConfig[]) => { const allServersList = dataSource.map((item) => item.servers); let newAllOBServer: string[] = []; allServersList.forEach((item) => { if (item && item.length) { newAllOBServer = [...newAllOBServer, ...item]; } }); return newAllOBServer; }; const onValuesChange = (values: FormValues) => { if (values?.auth?.user) { form.setFieldsValue({ home_path: values?.auth?.user === 'root' ? '/root' : `/home/${values?.auth?.user}`, }); } }; useEffect(() => { const allServers = getAllServers(dbConfigData); const allZoneServers: any = {}; dbConfigData.forEach((item) => { allZoneServers[`${item.id}`] = item.servers; }); const obproxyServers = form.getFieldValue(['obproxy', 'servers']); const ocpexpressServers = form.getFieldValue(['ocpexpress', 'servers']); const customOBproxyServers = obproxyServers?.filter( (item: string) => !(allServers?.includes(item) || item === lastDeleteServer), ); const customOcpexpressServers = ocpexpressServers?.filter( (item: string) => !(allServers?.includes(item) || item === lastDeleteServer), ); let obproxyServersValue; let ocpexpressServersValue; if (allServers?.length) { const checkPass = serverReg.test(allServers[0]); if (!obproxyServers?.length) { obproxyServersValue = [allServers[0]]; } else { const newOBproxyServers: string[] = []; obproxyServers?.forEach((item: string) => { if (allServers?.includes(item)) { newOBproxyServers.push(item); } }); if (newOBproxyServers?.length) { obproxyServersValue = [...customOBproxyServers, ...newOBproxyServers]; } else if (customOBproxyServers?.length) { obproxyServersValue = customOBproxyServers; } else { obproxyServersValue = [allServers[0]]; if (!checkPass) { form.setFields([ { name: ['obproxy', 'servers'], errors: ['请选择正确的 OBProxy 节点'], }, ]); } } } if (!ocpexpressServers?.length) { ocpexpressServersValue = [allServers[0]]; } else { const newOcpexpressServers: string[] = []; ocpexpressServers?.forEach((item: string) => { if (allServers?.includes(item)) { newOcpexpressServers.push(item); } }); if (newOcpexpressServers?.length) { ocpexpressServersValue = [ ...customOcpexpressServers, ...newOcpexpressServers, ]; } else if (customOcpexpressServers?.length) { ocpexpressServersValue = customOcpexpressServers; } else { ocpexpressServersValue = [allServers[0]]; if (!checkPass) { form.setFields([ { name: ['ocpexpress', 'servers'], errors: ['请选择正确的 OCPExpress 节点'], }, ]); } } } } else { if (!customOBproxyServers?.length) { obproxyServersValue = undefined; } else { obproxyServersValue = customOBproxyServers; } if (!customOcpexpressServers?.length) { ocpexpressServersValue = undefined; } else { ocpexpressServersValue = customOcpexpressServers; } } form.setFieldsValue({ obproxy: { servers: obproxyServersValue, }, ocpexpress: { servers: ocpexpressServersValue, }, }); setAllOBServer(allServers); setAllZoneOBServer(allZoneServers); }, [dbConfigData, lastDeleteServer]); useEffect(() => { if (!auth?.user) { getUserInfo(); } }, []); const nameValidator = ({ field }: any, value: string) => { const currentId = field.split('.')[0]; let validtor = true; const reg = /^[a-zA-Z]([a-zA-Z0-9_]{0,30})[a-zA-Z0-9]$/; if (value) { if (reg.test(value)) { dbConfigData.some((item) => { if (currentId !== item.id && item.name === value) { validtor = false; return true; } return false; }); } else { return Promise.reject( new Error( '以英文字母开头,英文或数字结尾,可包含英文数字和下划线且长度在 2-32 个字符之间', ), ); } } if (validtor) { return Promise.resolve(); } return Promise.reject(new Error('Zone 名称已被占用')); }; const ocpServersValidator = (_: any, value: string[]) => { let validtor = true; if (value?.length > 1) { return Promise.reject(new Error('仅可选择或输入一个节点')); } if (value && value.length) { value.some((item) => { validtor = serverReg.test(item.trim()); return !serverReg.test(item.trim()); }); } if (validtor) { return Promise.resolve(); } return Promise.reject(new Error('请选择正确的 OCPExpress 节点')); }; const serversValidator = (_: any, value: string[], type: string) => { let validtor = true; if (value && value.length) { value.some((item) => { validtor = serverReg.test(item.trim()); return !serverReg.test(item.trim()); }); } if (validtor) { return Promise.resolve(); } if (type === 'OBServer') { return Promise.reject(new Error('请输入正确的 IP 地址')); } else { return Promise.reject(new Error('请选择正确的 OBProxy 节点')); } }; const columns: ProColumns[] = [ { title: ( <> Zone 名称 ), dataIndex: 'name', width: 224, formItemProps: { rules: [ { required: true, whitespace: false, message: '此项是必填项' }, { validator: nameValidator }, ], }, }, { title: ( <> OBServer 节点 ), dataIndex: 'servers', formItemProps: { rules: [ { required: true, message: '此项是必填项' }, { validator: (_: any, value: string[]) => serversValidator(_, value, 'OBServer'), }, ], }, renderFormItem: (_: any, { isEditable, record }: any) => { return isEditable ? ( ) : null; }, }, { title: ( <> RootServer 节点 ), dataIndex: 'rootservice', formItemProps: { rules: [ { required: true, message: '此项是必选项' }, { pattern: serverReg, message: '请选择正确的 RootServer 节点' }, ], }, width: 224, renderFormItem: (_: any, { isEditable, record }: any) => { // rootservice options are items entered by the OBServer const options = record?.servers ? formatOptions(record?.servers) : []; return isEditable ? (