diff --git a/search-center.yml b/search-center.yml index be668c58..c68e3e6b 100644 --- a/search-center.yml +++ b/search-center.yml @@ -1,7 +1,7 @@ elasticsearch: - name: default enabled: true - endpoint: https://192.168.3.98:9200 + endpoint: http://127.0.0.1:9200 basic_auth: username: elastic password: ZBdkVQUUdF1Sir4X4BGB @@ -29,6 +29,7 @@ elastic: init_template: true template_name: ".infini-search-center" index_prefix: ".infini-search-center_" + # load_remote_elasticsearch_configs: true search-center: ui_path: .public diff --git a/web/config/router.config.js b/web/config/router.config.js index 7a79baf3..c3de5b79 100644 --- a/web/config/router.config.js +++ b/web/config/router.config.js @@ -442,6 +442,12 @@ export default [ name: 'cluster', component: './System/Cluster/Index', }, + { + path: '/system/cluster/regist', + name: 'registCluster', + component: './System/Cluster/Step', + hideInMenu: true + }, { path: '/system/cluster/edit', name: 'editCluster', diff --git a/web/src/components/kibana/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.tsx b/web/src/components/kibana/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.tsx index 053122aa..801bf90a 100644 --- a/web/src/components/kibana/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.tsx +++ b/web/src/components/kibana/index_pattern_management/public/components/index_pattern_table/empty_index_pattern_prompt/empty_index_pattern_prompt.tsx @@ -29,6 +29,8 @@ import { IndexPatternCreationOption } from '../../types'; import { CreateButton } from '../../create_button'; import { Illustration } from './assets/index_pattern_illustration'; import { ManagementAppMountParams } from '../../../../../management/public'; +import Link from 'umi/link'; +import Exception from '@/components/Exception'; interface Props { canSave: boolean; @@ -53,9 +55,9 @@ export const EmptyIndexPatternPrompt = ({ horizontalPosition="center" > - + {/* - + */}

diff --git a/web/src/locales/en-US.js b/web/src/locales/en-US.js index bcc75ab5..c6ee1b75 100644 --- a/web/src/locales/en-US.js +++ b/web/src/locales/en-US.js @@ -123,6 +123,7 @@ export default { 'menu.system': 'SYSTEM', 'menu.system.cluster': 'CLUSTERS', + 'menu.system.registCluster': 'REGIST CLUSTER', 'menu.system.editCluster': 'EDIT CLUSTER', 'menu.system.settings': 'SETTINGS', 'menu.system.settings.global': 'GLOBAL', diff --git a/web/src/locales/zh-CN.js b/web/src/locales/zh-CN.js index 07272774..312b7b59 100644 --- a/web/src/locales/zh-CN.js +++ b/web/src/locales/zh-CN.js @@ -130,7 +130,8 @@ export default { 'menu.system': '系统管理', 'menu.system.cluster': '集群管理', - 'menu.system.editCluster': '集群编辑', + 'menu.system.registCluster': '集群注册', + 'menu.system.editCluster': '集群修改', 'menu.system.settings': '系统设置', 'menu.system.settings.global': '全局设置', 'menu.system.settings.gateway': '网关设置', diff --git a/web/src/pages/Cluster/Metrics.js b/web/src/pages/Cluster/Metrics.js index 4c282c48..1c925d89 100644 --- a/web/src/pages/Cluster/Metrics.js +++ b/web/src/pages/Cluster/Metrics.js @@ -146,7 +146,7 @@ const vstyle = { const MonitorDatePicker = ({timeRange, commonlyUsedRanges, onChange, isLoading}) => { // const [recentlyUsedRanges, setRecentlyUsedRanges] = useState([]); const [isPaused, setIsPaused] = useState(true); - const [refreshInterval, setRefreshInterval] = useState(); + const [refreshInterval, setRefreshInterval] = useState(10000); const onTimeChange = ({ start, end }) => { onChange({ diff --git a/web/src/pages/DataManagement/context.js b/web/src/pages/DataManagement/context.js index d3090f05..d1999f45 100644 --- a/web/src/pages/DataManagement/context.js +++ b/web/src/pages/DataManagement/context.js @@ -164,8 +164,8 @@ const filterManager = new FilterManager(); const storage = new Storage(localStorage); const queryStringManager = new QueryStringManager(storage); const timefilterConfig = { - timeDefaults: { from: 'now-15m', to: 'now' }, - refreshIntervalDefaults: { pause: false, value: 0 }, + timeDefaults: { from: 'now-1y', to: 'now' }, + refreshIntervalDefaults: { pause: true, value: 10000 }, }; const timeHistory = new TimeHistory(storage); const timefilter = new Timefilter(timefilterConfig, timeHistory); diff --git a/web/src/pages/System/Cluster/Form.js b/web/src/pages/System/Cluster/Form.js index 727635ba..dff6afda 100644 --- a/web/src/pages/System/Cluster/Form.js +++ b/web/src/pages/System/Cluster/Form.js @@ -53,7 +53,7 @@ class ClusterForm extends React.Component{ //console.log(values); let newVals = { name: values.name, - endpoint: values.endpoint, + host: values.host, basic_auth: { username: values.username, password: values.password, @@ -61,7 +61,8 @@ class ClusterForm extends React.Component{ description: values.description, enabled: values.enabled, monitored: values.monitored, - order: values.order, + schema: values.isTLS === true ? 'https': 'http', + // order: values.order, } if(clusterConfig.editMode === 'NEW') { dispatch({ @@ -125,7 +126,6 @@ class ClusterForm extends React.Component{ router.push('/system/cluster'); }}>返回]} > - {/* */}
{getFieldDecorator('name', { @@ -138,20 +138,31 @@ class ClusterForm extends React.Component{ ], })()} - - {getFieldDecorator('endpoint', { - initialValue: editValue.endpoint, + + {getFieldDecorator('host', { + initialValue: editValue.host, rules: [ { - type: 'url', //https://github.com/yiminghe/async-validator#type - message: 'The input is not valid url!', + type: 'string', + pattern: /^[\w\.]+\:\d+$/, //(https?:\/\/)? + message: '请输入域名或 IP 地址和端口号', }, { required: true, - message: 'Please input cluster name!', + message: '请输入域名或 IP 地址和端口号!', }, ], - })()} + })()} + + + {getFieldDecorator('isTLS', { + initialValue: editValue?.schema === "https", + })( + } + unCheckedChildren={} + />)} )} ):''} - {/* - {getFieldDecorator('version', { - initialValue: editValue.version || '', - })()} - */} - + {/* {getFieldDecorator('order', { initialValue: editValue.order || 0, })()} - + */} {getFieldDecorator('description', { initialValue: editValue.description, })()} - + {/* {getFieldDecorator('enabled', { valuePropName: 'checked', initialValue: typeof editValue.enabled === 'undefined' ? true: editValue.enabled, @@ -200,7 +206,7 @@ class ClusterForm extends React.Component{ checkedChildren={} unCheckedChildren={} />)} - + */} {getFieldDecorator('monitored', { valuePropName: 'checked', @@ -214,9 +220,6 @@ class ClusterForm extends React.Component{ - {/* */} diff --git a/web/src/pages/System/Cluster/Index.js b/web/src/pages/System/Cluster/Index.js index 6ebbbcef..93dba2d3 100644 --- a/web/src/pages/System/Cluster/Index.js +++ b/web/src/pages/System/Cluster/Index.js @@ -40,14 +40,14 @@ class Index extends React.Component { } }, { title: '部署环境', - dataIndex: 'business_department', - key: 'business_department', + dataIndex: 'deploy_env', + key: 'deploy_env', render: ()=>{ return 'PROD' } },{ title: '程序版本', - dataIndex: 'status.version', + dataIndex: 'version', key: 'elasticsearch_version', // render: (data)=>{ // return @@ -212,7 +212,7 @@ class Index extends React.Component { onChange={this.handleEnabledChange} defaultChecked />是否启用 - + { - const initalFormRef = useRef(); + const formRef = useRef(); + const [clusterConfig, setClusterConfig] = useState({}) + const [isLoading, setIsLoading] = useState(false); + // const [clusterInfo, setClusterInfo] = useState({}); + + const handleConnect = async ()=>{ + const result = await formRef.current.validateFields((errors, values) => { + if(errors){ + return false; + } + return values; + + }).catch((err)=>{ + return false; + }); + + if(!result){ + return false + } + setIsLoading(true) + const res = await dispatch({ + type: 'clusterConfig/doTryConnect', + payload: { + basic_auth:{ + username: result.username, + password: result.password, + }, + host: result.host, + schema: result.isTLS === true ? 'https': 'http', + }, + }); + if(res && !res.error){ + setClusterConfig({ + ...result, + ...res, + }); + return true; + }else{ + setIsLoading(false) + return false; + } + } + + const handleCommit = async ()=>{ + const result = await formRef.current.validateFields((errors, values) => { + if(errors){ + return false; + } + // console.log(values); + return values + }); + if(!result){ + return fasle; + } + const newVals = { + name: result.name, + version: clusterConfig.version, + host: clusterConfig.host, + basic_auth: { + username: clusterConfig.username || '', + password: clusterConfig.password || '', + }, + description: result.description, + enabled: true, + monitored: result.monitored, + schema: clusterConfig.isTLS ? 'https': 'http' + } + setIsLoading(true); + const res = await dispatch({ + type: 'clusterConfig/addCluster', + payload: newVals, + }); + if(res && !res.error){ + return true; + }else{ + setIsLoading(false) + return false; + } + } const next = async () => { let result if(current === 0){ - result = await initalFormRef.current.validateFields((errors, values) => { - if(errors){ - return false; - } - console.log(values) - }).catch((err)=>{ - return false; - }) + result = await handleConnect(); + }else if(current === 1){ + result = await handleCommit(); } if(!result){ return } + setIsLoading(false) changeStep(current + 1) }; @@ -47,48 +125,97 @@ const ClusterStep = ({ changeStep(current - 1) }; + const oneMoreClick = ()=>{ + setClusterConfig({}); + changeStep(0); + } + + const goToClusterList = ()=>{ + history.push('/system/cluster'); + } + const renderContent = (current)=>{ if(current===0){ - return + return }else if(current === 1){ - return <> - }else{ - return null + return + }else if(current === 2){ + return } } - return ( - <> - - {steps.map(item => ( - - ))} - -
{renderContent(current)}
-
- {current < steps.length - 1 && ( - - )} - {current === steps.length - 1 && ( - - )} - {current > 0 && ( - - )} + + const content = ( +
+

+ 输入集群地址和身份验证信息分步创建集群。 +

+ - +
+ ); + + const extraContent = ( +
+ 这是一个标题 +
+ ); + + + return ( + + + +
+ + {steps.map(item => ( + + ))} + +
{renderContent(current)}
+
+ {current === 1 && ( + + )} + {current < steps.length - 1 && ( + + )} + +
+
+
+
+
); }; -const NewCluster = ()=>{ +const NewCluster = (props)=>{ + const {dispatch, history} = props; const [current, setCurrent] = useState(0); - return + return } export default connect(({ diff --git a/web/src/pages/System/Cluster/models/cluster.js b/web/src/pages/System/Cluster/models/cluster.js index 7c15efa8..b9123f17 100644 --- a/web/src/pages/System/Cluster/models/cluster.js +++ b/web/src/pages/System/Cluster/models/cluster.js @@ -1,4 +1,5 @@ -import {createClusterConfig, searchClusterConfig, updateClusterConfig,deleteClusterConfig, getClusterStatus} from "@/services/cluster"; +import {createClusterConfig, searchClusterConfig, updateClusterConfig,deleteClusterConfig, + getClusterStatus, tryConnect} from "@/services/cluster"; import {message} from "antd"; import {formatESSearchResult} from '@/lib/elasticsearch/util'; @@ -45,6 +46,9 @@ export default { return false; } let {data, total} = yield select(state => state.clusterConfig); + if(!data){ + return + } data.unshift({ ...res._source, id: res._id, @@ -147,6 +151,21 @@ export default { } }) return res; + }, + *doTryConnect({payload}, {call, put, select}) { + let res = yield call(tryConnect, payload) + if(res.error){ + message.error(res.error) + return false; + } + + yield put({ + type: 'saveData', + payload: { + tempClusterInfo: res, + } + }) + return res; } }, reducers:{ diff --git a/web/src/pages/System/Cluster/step.less b/web/src/pages/System/Cluster/step.less new file mode 100644 index 00000000..a9f85a50 --- /dev/null +++ b/web/src/pages/System/Cluster/step.less @@ -0,0 +1,26 @@ +.extraImg { + margin-top: -60px; + text-align: center; + width: 195px; + img { + width: 100%; + } +} + +.pageHeaderContent { + position: relative; +} + +.contentLink { + margin-top: 16px; + a { + margin-right: 32px; + img { + width: 24px; + } + } + img { + vertical-align: middle; + margin-right: 8px; + } +} \ No newline at end of file diff --git a/web/src/pages/System/Cluster/steps/extra_step.js b/web/src/pages/System/Cluster/steps/extra_step.js new file mode 100644 index 00000000..d5a17ef3 --- /dev/null +++ b/web/src/pages/System/Cluster/steps/extra_step.js @@ -0,0 +1,88 @@ +import {Form, Input, Switch, Icon, InputNumber, Divider, Descriptions} from 'antd'; + +@Form.create() +export class ExtraStep extends React.Component { + state = { + } + render(){ + const {form:{getFieldDecorator}, initialValue} = this.props; + const formItemLayout = { + labelCol: { + xs: { span: 24 }, + sm: { span: 6 }, + }, + wrapperCol: { + xs: { span: 24 }, + sm: { span: 16 }, + }, + }; + return ( + <> + + + {initialValue?.version} + + + {initialValue?.status} + + + {initialValue?.number_of_nodes} + + + {initialValue?.number_of_data_nodes} + + + {initialValue?.active_shards} + + + +
+ + {getFieldDecorator('name', { + initialValue: initialValue?.cluster_name || '', + rules: [ + { + required: true, + message: 'Please input cluster name!', + }, + ], + })()} + + {/* + {getFieldDecorator('version', { + initialValue: initialValue?.version || '', + })()} + */} + {/* + {getFieldDecorator('order', { + initialValue: 0, + })()} + */} + + {getFieldDecorator('description', { + initialValue: '', + })()} + + {/* + {getFieldDecorator('enabled', { + valuePropName: 'checked', + initialValue: true, + })(} + unCheckedChildren={} + />)} + */} + + {getFieldDecorator('monitored', { + valuePropName: 'checked', + initialValue: true, + })(} + unCheckedChildren={} + />)} + + + + ) + } +} diff --git a/web/src/pages/System/Cluster/steps/index.js b/web/src/pages/System/Cluster/steps/index.js new file mode 100644 index 00000000..9f57cd38 --- /dev/null +++ b/web/src/pages/System/Cluster/steps/index.js @@ -0,0 +1,3 @@ +export * from './initial_step'; +export * from './extra_step'; +export * from './result_step'; \ No newline at end of file diff --git a/web/src/pages/System/Cluster/steps/initial_step.js b/web/src/pages/System/Cluster/steps/initial_step.js index cd00f684..ea818b09 100644 --- a/web/src/pages/System/Cluster/steps/initial_step.js +++ b/web/src/pages/System/Cluster/steps/initial_step.js @@ -1,10 +1,12 @@ import {Form, Input, Switch, Icon} from 'antd'; -import {useState} from 'react'; @Form.create() -class InitialStep extends React.Component { - state = { - needAuth: false, +export class InitialStep extends React.Component { + constructor(props){ + super(props); + this.state = { + needAuth: props.initialValue?.username !== undefined, + } } handleAuthChange = (val) => { this.setState({ @@ -12,7 +14,7 @@ class InitialStep extends React.Component { }) } render(){ - const {form:{getFieldDecorator}} = this.props; + const {form:{getFieldDecorator}, initialValue} = this.props; const formItemLayout = { labelCol: { xs: { span: 24 }, @@ -24,34 +26,33 @@ class InitialStep extends React.Component { }, }; return ( -
- - {getFieldDecorator('name', { - initialValue: '', + + + {getFieldDecorator('host', { + initialValue: initialValue?.host || '', rules: [ { - required: true, - message: 'Please input cluster name!', - }, - ], - })()} - - - {getFieldDecorator('endpoint', { - initialValue: '', - rules: [ - { - type: 'url', - message: 'The input is not valid url!', + type: 'string', + pattern: /^[\w\.]+\:\d+$/, //(https?:\/\/)? + message: '请输入域名或 IP 地址和端口号', }, { required: true, - message: 'Please input cluster endpoint!', + message: '请输入集群地址!', }, ], - })()} + })()} - + + {getFieldDecorator('isTLS', { + initialValue: initialValue?.isTLS || false, + })( + } + unCheckedChildren={} + />)} + + {getFieldDecorator('username', { - initialValue: '', + initialValue: initialValue?.username || '', rules: [ { required: true, @@ -73,7 +74,7 @@ class InitialStep extends React.Component { {getFieldDecorator('password', { - initialValue: '', + initialValue: initialValue?.password || '', rules: [ { required: true, @@ -86,6 +87,4 @@ class InitialStep extends React.Component { ) } -} - -export default InitialStep; \ No newline at end of file +} \ No newline at end of file diff --git a/web/src/pages/System/Cluster/steps/result_step.js b/web/src/pages/System/Cluster/steps/result_step.js new file mode 100644 index 00000000..4b0f9fe7 --- /dev/null +++ b/web/src/pages/System/Cluster/steps/result_step.js @@ -0,0 +1,63 @@ +import Result from '@/components/Result'; +import React, { Fragment } from 'react'; +import { Button, Row, Col } from 'antd'; +import styles from './styles.less'; + +export const ResultStep = (props)=>{ + const {clusterConfig, oneMoreClick, goToClusterList} = props; + const information = ( +
+ +
+ 集群名称: + + + {clusterConfig?.cluster_name} + + + + + 集群版本: + + + {clusterConfig?.version} + + + + + 集群地址: + + + {clusterConfig?.host} + + + + + TLS: + + + {clusterConfig?.isTLS ? '是': '否'} + + + + ); + const actions = ( + + + + + ); + + return ( + + ); +} \ No newline at end of file diff --git a/web/src/pages/System/Cluster/steps/styles.less b/web/src/pages/System/Cluster/steps/styles.less new file mode 100644 index 00000000..ae478f33 --- /dev/null +++ b/web/src/pages/System/Cluster/steps/styles.less @@ -0,0 +1,62 @@ +@import '~antd/lib/style/themes/default.less'; +.result { + margin: 0 auto; + max-width: 560px; + padding: 24px 0 8px; +} + +.desc { + padding: 0 56px; + // color: @text-color-secondary; + h3 { + font-size: 16px; + margin: 0 0 12px 0; + // color: @text-color-secondary; + line-height: 32px; + } + h4 { + margin: 0 0 4px 0; + // color: @text-color-secondary; + font-size: 14px; + line-height: 22px; + } + p { + margin-top: 0; + margin-bottom: 12px; + line-height: 22px; + } +} + +@media screen and (max-width: @screen-md) { + .desc { + padding: 0; + } +} + +.information { + line-height: 22px; + :global { + .ant-row:not(:last-child) { + margin-bottom: 24px; + } + } + .label { + // color: @heading-color; + text-align: right; + padding-right: 8px; + @media screen and (max-width: @screen-sm) { + text-align: left; + } + } +} + +.money { + font-family: 'Helvetica Neue', sans-serif; + font-weight: 500; + font-size: 20px; + line-height: 14px; +} + +.uppercase { + font-size: 12px; +} \ No newline at end of file diff --git a/web/src/services/cluster.js b/web/src/services/cluster.js index cfe84a70..711b6401 100644 --- a/web/src/services/cluster.js +++ b/web/src/services/cluster.js @@ -16,7 +16,7 @@ export async function getClusterMetrics(params) { } export async function createClusterConfig(params) { - return request(`/elasticsearch`, { + return request(`/elasticsearch/`, { method: 'POST', body: params, }); @@ -57,4 +57,12 @@ export async function getClusterStatus(params) { return request(url, { method: 'GET', }); +} + +export async function tryConnect(params) { + let url = `/elasticsearch/try_connect`; + return request(url, { + method: 'POST', + body: params, + }); } \ No newline at end of file