diff --git a/api/system/cluster_config.go b/api/system/cluster_config.go index 4c000470..3370d006 100644 --- a/api/system/cluster_config.go +++ b/api/system/cluster_config.go @@ -10,6 +10,8 @@ import ( "infini.sh/search-center/model" "infini.sh/framework/core/orm" "net/http" + "strings" + "time" ) type APIHandler struct { @@ -30,13 +32,18 @@ func (h *APIHandler) HandleCreateClusterAction(w http.ResponseWriter, req *http. // TODO validate data format esClient := elastic.GetClient(h.Config.Elasticsearch) id := util.GetUUID() + conf.Created = time.Now() + conf.Updated = conf.Created + conf.ID = id ir, err := esClient.Index(orm.GetIndexName(model.ClusterConfig{}), "", id, conf) if err != nil { resBody["error"] = err h.WriteJSON(w, resBody, http.StatusOK) return } - resBody["payload"] = ir + conf.ID = ir.ID + resBody["payload"] = conf + resBody["acknowledged"] = true h.WriteJSON(w, resBody, http.StatusOK) } @@ -44,9 +51,9 @@ func (h *APIHandler) HandleUpdateClusterAction(w http.ResponseWriter, req *http. var conf = map[string]interface{}{} resBody := map[string] interface{}{ } - err := h.DecodeJSON(req, conf) + err := h.DecodeJSON(req, &conf) if err != nil { - resBody["error"] = err + resBody["error"] = err.Error() h.WriteJSON(w, resBody, http.StatusOK) return } @@ -55,7 +62,7 @@ func (h *APIHandler) HandleUpdateClusterAction(w http.ResponseWriter, req *http. indexName := orm.GetIndexName(model.ClusterConfig{}) originConf, err := esClient.Get(indexName, "", id) if err != nil { - resBody["error"] = err + resBody["error"] = err.Error() h.WriteJSON(w, resBody, http.StatusOK) return } @@ -66,14 +73,15 @@ func (h *APIHandler) HandleUpdateClusterAction(w http.ResponseWriter, req *http. } source[k] = v } - ir, err := esClient.Index(indexName, "", id, source) + conf["updated"] = time.Now() + _, err = esClient.Index(indexName, "", id, source) if err != nil { - resBody["error"] = err + resBody["error"] = err.Error() h.WriteJSON(w, resBody, http.StatusOK) return } resBody["acknowledged"] = true - resBody["payload"] = ir + resBody["payload"] = conf h.WriteJSON(w, resBody, http.StatusOK) } @@ -84,7 +92,7 @@ func (h *APIHandler) HandleDeleteClusterAction(w http.ResponseWriter, req *http. esClient := elastic.GetClient(h.Config.Elasticsearch) _, err := esClient.Delete(orm.GetIndexName(model.ClusterConfig{}), "", id) if err != nil { - resBody["error"] = err + resBody["error"] = err.Error() h.WriteJSON(w, resBody, http.StatusOK) return } @@ -98,23 +106,28 @@ func (h *APIHandler) HandleSearchClusterAction(w http.ResponseWriter, req *http. } var ( name = h.GetParameterOrDefault(req, "name", "") - enable = h.GetParameterOrDefault(req, "enable", "") - queryDSL = `{"query":{"bool":{"must":[%s, %s]}}}` + enabled = h.GetParameterOrDefault(req, "enabled", "") + queryDSL = `{"query":{"bool":{"must":[%s]}}}` + mustBuilder = &strings.Builder{} ) if name != ""{ - name = fmt.Sprintf(`{"match":{"name": "%s""}}`, name) + mustBuilder.WriteString(fmt.Sprintf(`{"match":{"name": "%s"}}`, name)) } - if enable != "" { - if enable != "true" { - enable = "false" + if enabled != "" { + if enabled != "true" { + enabled = "false" } - enable = fmt.Sprintf(`{"match":{"enable": "%s""}}`, enable) + if mustBuilder.Len() > 0 { + mustBuilder.WriteString(",") + } + mustBuilder.WriteString(fmt.Sprintf(`{"match":{"enabled": %s}}`, enabled)) } - queryDSL = fmt.Sprintf(queryDSL, name, enable) + + queryDSL = fmt.Sprintf(queryDSL, mustBuilder.String()) esClient := elastic.GetClient(h.Config.Elasticsearch) res, err := esClient.SearchWithRawQueryDSL(orm.GetIndexName(model.ClusterConfig{}), []byte(queryDSL)) if err != nil { - resBody["error"] = err + resBody["error"] = err.Error() h.WriteJSON(w, resBody, http.StatusOK) return } diff --git a/main.go b/main.go index 7999069e..00c453ca 100644 --- a/main.go +++ b/main.go @@ -71,7 +71,7 @@ func main() { }, func() { orm.RegisterSchemaWithIndexName(model.Dict{}, "infini-dict") orm.RegisterSchemaWithIndexName(model.Reindex{}, "infini-reindex") - orm.RegisterSchemaWithIndexName(model.Reindex{}, "infini-cluster") + orm.RegisterSchemaWithIndexName(model.ClusterConfig{}, "infini-cluster") }) } diff --git a/model/cluster_config.go b/model/cluster_config.go index 0c4944ba..93f91a90 100644 --- a/model/cluster_config.go +++ b/model/cluster_config.go @@ -1,11 +1,16 @@ package model +import "time" + type ClusterConfig struct { ID string `json:"id" elastic_meta:"_id"` Name string `json:"name" elastic_mapping:"name:{type:text}"` - Endpoint string `json:"endpoint" elastic_mapping:"name:{type:text}"` - User string `json:"user" elastic_mapping:"name:{type:keyword}"` - Password string `json:"password"elastic_mapping:"name:{type:keyword}" ` - Description string `json:"desc" elastic_mapping:"name:{type:text}"` - Enable bool `json:"enable" elastic_mapping:"name:{type:boolean}"` + Endpoint string `json:"endpoint" elastic_mapping:"endpoint:{type:text}"` + UserName string `json:"username" elastic_mapping:"username:{type:keyword}"` + Password string `json:"password" elastic_mapping:"password:{type:keyword}" ` + Order int `json:"order" elastic_mapping:"order:{type:integer}"` + Description string `json:"description" elastic_mapping:"description:{type:text}"` + Enabled bool `json:"enabled" elastic_mapping:"enabled:{type:boolean}"` + Created time.Time `json:"created" elastic_mapping:"created:{type:date}"` + Updated time.Time `json:"updated" elastic_mapping:"updated:{type:date}"` } diff --git a/web/config/router.config.js b/web/config/router.config.js index 00872531..8a68b37b 100644 --- a/web/config/router.config.js +++ b/web/config/router.config.js @@ -303,8 +303,8 @@ export default [ component: './System/Cluster/Index', }, { - path: '/system/cluster/new', - name: 'new-cluster', + path: '/system/cluster/edit', + name: 'edit-cluster', component: './System/Cluster/Form', hideInMenu: true }, diff --git a/web/src/pages/System/Cluster/Form.js b/web/src/pages/System/Cluster/Form.js index 6e25429d..5605e697 100644 --- a/web/src/pages/System/Cluster/Form.js +++ b/web/src/pages/System/Cluster/Form.js @@ -1,11 +1,22 @@ import React from 'react'; import {Card, Form, Icon, Input, InputNumber, Button, Switch} from 'antd'; +import router from 'umi/router'; + +import styles from './Form.less'; +import {connect} from "_dva@2.4.1@dva"; @Form.create() +@connect(({clusterConfig}) =>({ + clusterConfig +})) class ClusterForm extends React.Component{ state = { confirmDirty: false, } + componentDidMount() { + //console.log(this.props.clusterConfig.editMode) + } + compareToFirstPassword = (rule, value, callback) => { const { form } = this.props; if (value && value !== form.getFieldValue('password')) { @@ -22,6 +33,41 @@ class ClusterForm extends React.Component{ } callback(); }; + + handleSubmit = () =>{ + const {form, dispatch, clusterConfig} = this.props; + form.validateFields((errors, values) => { + if(errors){ + return + } + //console.log(values); + let newVals = { + ...values + } + delete(newVals['confirm']); + if(clusterConfig.editMode === 'NEW') { + dispatch({ + type: 'clusterConfig/addCluster', + payload: newVals, + }).then(function (rel){ + if(rel){ + router.push('/system/cluster'); + } + }); + }else{ + newVals.id = clusterConfig.editValue.id; + dispatch({ + type: 'clusterConfig/updateCluster', + payload: newVals, + }).then(function (rel){ + if(rel){ + router.push('/system/cluster'); + } + }); + } + }) + } + render() { const {getFieldDecorator} = this.props.form; const formItemLayout = { @@ -46,21 +92,24 @@ class ClusterForm extends React.Component{ }, }, }; + const {editValue, editMode} = this.props.clusterConfig; return ( - -
+ + {getFieldDecorator('name', { + initialValue: editValue.name, rules: [ { required: true, message: 'Please input cluster name!', }, ], - })()} + })()} {getFieldDecorator('endpoint', { + initialValue: editValue.endpoint, rules: [ { type: 'url', //https://github.com/yiminghe/async-validator#type @@ -75,12 +124,14 @@ class ClusterForm extends React.Component{ {getFieldDecorator('username', { + initialValue: editValue.username, rules: [ ], - })()} + })()} {getFieldDecorator('password', { + initialValue: editValue.password, rules: [ { validator: this.validateToNextPassword, @@ -90,6 +141,7 @@ class ClusterForm extends React.Component{ {getFieldDecorator('confirm', { + initialValue: editValue.password, rules: [ { validator: this.compareToFirstPassword, @@ -99,25 +151,26 @@ class ClusterForm extends React.Component{ {getFieldDecorator('order', { - initialValue: 0 + initialValue: editValue.order, })()} - {getFieldDecorator('order', { + {getFieldDecorator('description', { + initialValue: editValue.description, })()} {getFieldDecorator('enabled', { valuePropName: 'checked', - initialValue: true + initialValue: typeof editValue.enabled === 'undefined' ? true: editValue.enabled, })(} unCheckedChildren={} />)} - diff --git a/web/src/pages/System/Cluster/Form.less b/web/src/pages/System/Cluster/Form.less new file mode 100644 index 00000000..7fe15810 --- /dev/null +++ b/web/src/pages/System/Cluster/Form.less @@ -0,0 +1,3 @@ +:global(.ant-form-item){ + margin-bottom: 16px; +} \ No newline at end of file diff --git a/web/src/pages/System/Cluster/Index.js b/web/src/pages/System/Cluster/Index.js index 38f64304..3624243b 100644 --- a/web/src/pages/System/Cluster/Index.js +++ b/web/src/pages/System/Cluster/Index.js @@ -1,8 +1,12 @@ import React from 'react'; -import {Button, Card, Col, Divider, Form, Input, Row, Table,Switch, Icon} from "antd"; +import {Button, Card, Col, Divider, Form, Input, Row, Table, Switch, Icon, Popconfirm} from "antd"; import Link from "_umi@2.13.16@umi/link"; +import {connect} from "dva"; @Form.create() +@connect(({clusterConfig}) =>({ + clusterConfig +})) class Index extends React.Component { columns = [{ title: '集群名称', @@ -32,8 +36,80 @@ class Index extends React.Component { title: '是否启用', dataIndex: 'enabled', key: 'enabled', + render: (val) =>{ + return val === true ? '是': '否'; + } + },{ + title: 'Operation', + render: (text, record) => ( +
+ {this.handleEditClick(record)}}>Edit + + this.handleDeleteClick(record)}>Delete + + +
+ ), }] + fetchData = (params)=>{ + const {dispatch} = this.props; + dispatch({ + type: 'clusterConfig/fetchClusterList', + payload: params, + }) + } + componentDidMount() { + this.fetchData({}) + } + + handleSearchClick = ()=>{ + const {form} = this.props; + this.fetchData({ + name: form.getFieldValue('name'), + }) + } + + handleDeleteClick = (record)=>{ + const {dispatch} = this.props; + return dispatch({ + type:'clusterConfig/deleteCluster', + payload: { + id: record.id + } + }); + } + + saveData = (payload)=>{ + const {dispatch} = this.props; + return dispatch({ + type:'clusterConfig/saveData', + payload: { + ...payload + } + }); + } + handleNewClick = () => { + this.saveData({ + editMode: 'NEW', + editValue: {}, + }) + } + handleEditClick = (record)=>{ + this.saveData({ + editMode : 'UPDATE', + editValue: record, + }) + } + + handleEnabledChange = (enabled) => { + const {form} = this.props; + this.fetchData({ + name: form.getFieldValue('name'), + enabled: enabled, + }) + } + render() { const {getFieldDecorator} = this.props.form; const formItemLayout = { @@ -41,6 +117,7 @@ class Index extends React.Component { wrapperCol: { span: 14 }, style: {marginBottom: 0} }; + const {data} = this.props.clusterConfig; return (
@@ -52,7 +129,7 @@ class Index extends React.Component {
-
@@ -66,15 +143,16 @@ class Index extends React.Component { } unCheckedChildren={} + onChange={this.handleEnabledChange} defaultChecked />是否启用 - + } bordered={false}> diff --git a/web/src/pages/System/Cluster/models/cluster.js b/web/src/pages/System/Cluster/models/cluster.js new file mode 100644 index 00000000..e9b8b798 --- /dev/null +++ b/web/src/pages/System/Cluster/models/cluster.js @@ -0,0 +1,68 @@ +import {createClusterConfig,searchClusterConfig, updateClusterConfig,deleteClusterConfig} from "@/services/clusterConfig"; +import {message} from "antd"; +import {formatESSearchResult} from '@/lib/elasticsearch/util'; + +export default { + namespace: 'clusterConfig', + state: { + editMode: '', + editValue: {}, + }, + effects:{ + *fetchClusterList({payload}, {call, put}){ + let res = yield call(searchClusterConfig, payload); + if(res.error){ + message.error(res.error) + return false; + } + res = formatESSearchResult(res) + yield put({ + type: 'saveData', + payload: res + }) + }, + *addCluster({payload}, {call, put, select}) { + let res = yield call(createClusterConfig, payload) + if(res.error){ + message.error(res.error) + return false; + } + return res; + }, + *updateCluster({payload}, {call, put, select}) { + let res = yield call(updateClusterConfig, payload) + if(res.error){ + message.error(res.error) + return false; + } + return res; + }, + *deleteCluster({payload}, {call, put, select}) { + let res = yield call(deleteClusterConfig, payload) + if(res.error){ + message.error(res.error) + return false; + } + let {data, total} = yield select(state => state.clusterConfig); + data = data.filter((item)=>{ + return item.id !== payload.id; + }) + yield put({ + type: 'saveData', + payload: { + data, + total: total -1, + } + }) + return res; + } + }, + reducers:{ + saveData(state, {payload}){ + return { + ...state, + ...payload, + } + } + } +} \ No newline at end of file diff --git a/web/src/services/clusterConfig.js b/web/src/services/clusterConfig.js new file mode 100644 index 00000000..21637846 --- /dev/null +++ b/web/src/services/clusterConfig.js @@ -0,0 +1,37 @@ +import {pathPrefix, buildQueryArgs} from "./common"; +import request from '@/utils/request'; + +export async function createClusterConfig(params) { + return request(`${pathPrefix}/system/cluster`, { + method: 'POST', + body: params, + }); +} + +export async function updateClusterConfig(params) { + return request(`${pathPrefix}/system/cluster/${params.id}`, { + method: 'PUT', + body: params, + }); +} + +export async function deleteClusterConfig(params) { + return request(`${pathPrefix}/system/cluster/${params.id}`, { + method: 'DELETE', + body: params, + }); +} + +export async function searchClusterConfig(params) { + let url = `${pathPrefix}/system/cluster`; + let args = buildQueryArgs({ + name: params.name, + enabled: params.enabled + }); + if(args.length > 0){ + url += args; + } + return request(url, { + method: 'GET', + }); +} diff --git a/web/src/services/common.js b/web/src/services/common.js index 2a65fdab..b173886b 100644 --- a/web/src/services/common.js +++ b/web/src/services/common.js @@ -1 +1,15 @@ -export const pathPrefix = '/_search-center'; \ No newline at end of file +export const pathPrefix = '/_search-center'; + +export function buildQueryArgs(params){ + let argsStr = ''; + for(let key in params){ + if(typeof params[key] !== 'undefined') { + argsStr += `${key}=${params[key]}&` + } + } + if(argsStr.length > 0){ + argsStr = '?' + argsStr + argsStr = argsStr.slice(0, argsStr.length -1) + } + return argsStr; +} \ No newline at end of file