modify cluster config
This commit is contained in:
parent
3aeef79fe0
commit
317fbd4e20
|
@ -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
|
||||
}
|
||||
|
|
2
main.go
2
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")
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -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}"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
},
|
||||
|
|
|
@ -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 (
|
||||
<Card>
|
||||
<Form {...formItemLayout} onSubmit={this.handleSubmit}>
|
||||
<Card title={editMode === 'NEW' ? '注册集群': '修改集群配置'}>
|
||||
<Form {...formItemLayout}>
|
||||
<Form.Item label="集群名称">
|
||||
{getFieldDecorator('name', {
|
||||
initialValue: editValue.name,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input cluster name!',
|
||||
},
|
||||
],
|
||||
})(<Input />)}
|
||||
})(<Input autoComplete='off' />)}
|
||||
</Form.Item>
|
||||
<Form.Item label="集群 URL">
|
||||
{getFieldDecorator('endpoint', {
|
||||
initialValue: editValue.endpoint,
|
||||
rules: [
|
||||
{
|
||||
type: 'url', //https://github.com/yiminghe/async-validator#type
|
||||
|
@ -75,12 +124,14 @@ class ClusterForm extends React.Component{
|
|||
</Form.Item>
|
||||
<Form.Item label="ES 用户名">
|
||||
{getFieldDecorator('username', {
|
||||
initialValue: editValue.username,
|
||||
rules: [
|
||||
],
|
||||
})(<Input />)}
|
||||
})(<Input autoComplete='off' />)}
|
||||
</Form.Item>
|
||||
<Form.Item label="ES 密码" hasFeedback>
|
||||
{getFieldDecorator('password', {
|
||||
initialValue: editValue.password,
|
||||
rules: [
|
||||
{
|
||||
validator: this.validateToNextPassword,
|
||||
|
@ -90,6 +141,7 @@ class ClusterForm extends React.Component{
|
|||
</Form.Item>
|
||||
<Form.Item label="ES 确认密码" hasFeedback>
|
||||
{getFieldDecorator('confirm', {
|
||||
initialValue: editValue.password,
|
||||
rules: [
|
||||
{
|
||||
validator: this.compareToFirstPassword,
|
||||
|
@ -99,25 +151,26 @@ class ClusterForm extends React.Component{
|
|||
</Form.Item>
|
||||
<Form.Item label="排序权重">
|
||||
{getFieldDecorator('order', {
|
||||
initialValue: 0
|
||||
initialValue: editValue.order,
|
||||
})(<InputNumber />)}
|
||||
</Form.Item>
|
||||
<Form.Item label="描述">
|
||||
{getFieldDecorator('order', {
|
||||
{getFieldDecorator('description', {
|
||||
initialValue: editValue.description,
|
||||
})(<Input.TextArea />)}
|
||||
</Form.Item>
|
||||
<Form.Item label="是否启用">
|
||||
{getFieldDecorator('enabled', {
|
||||
valuePropName: 'checked',
|
||||
initialValue: true
|
||||
initialValue: typeof editValue.enabled === 'undefined' ? true: editValue.enabled,
|
||||
})(<Switch
|
||||
checkedChildren={<Icon type="check" />}
|
||||
unCheckedChildren={<Icon type="close" />}
|
||||
/>)}
|
||||
</Form.Item>
|
||||
<Form.Item {...tailFormItemLayout}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Register
|
||||
<Button type="primary" onClick={this.handleSubmit}>
|
||||
{editMode === 'NEW' ? 'Register': 'Save'}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
:global(.ant-form-item){
|
||||
margin-bottom: 16px;
|
||||
}
|
|
@ -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) => (
|
||||
<div>
|
||||
<Link to='/system/cluster/edit' onClick={()=>{this.handleEditClick(record)}}>Edit</Link>
|
||||
<span><Divider type="vertical" />
|
||||
<Popconfirm title="Sure to delete?" onConfirm={() => this.handleDeleteClick(record)}><a key="delete">Delete</a>
|
||||
</Popconfirm>
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
}]
|
||||
|
||||
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 (
|
||||
<Card>
|
||||
<Form>
|
||||
|
@ -52,7 +129,7 @@ class Index extends React.Component {
|
|||
</Col>
|
||||
<Col md={8} sm={8}>
|
||||
<div style={{paddingTop:4}}>
|
||||
<Button type="primary" icon="search" onClick={this.handleSearch}>
|
||||
<Button type="primary" icon="search" onClick={this.handleSearchClick}>
|
||||
Search
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -66,15 +143,16 @@ class Index extends React.Component {
|
|||
<span style={{marginRight:24}}><Switch
|
||||
checkedChildren={<Icon type="check" />}
|
||||
unCheckedChildren={<Icon type="close" />}
|
||||
onChange={this.handleEnabledChange}
|
||||
defaultChecked
|
||||
/>是否启用</span>
|
||||
<Link to='/system/cluster/new'> <Button type="primary" icon="plus">New</Button></Link>
|
||||
<Link to='/system/cluster/edit' onClick={this.handleNewClick}> <Button type="primary" icon="plus">New</Button></Link>
|
||||
</div>}
|
||||
bordered={false}>
|
||||
<Table
|
||||
bordered
|
||||
columns={this.columns}
|
||||
dataSource={[]}
|
||||
dataSource={data}
|
||||
rowKey='id'
|
||||
/>
|
||||
</Card>
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
});
|
||||
}
|
|
@ -1 +1,15 @@
|
|||
export const pathPrefix = '/_search-center';
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue