diff --git a/api/index_management/common_command.go b/api/index_management/common_command.go
index aa008c6a..ea101d65 100644
--- a/api/index_management/common_command.go
+++ b/api/index_management/common_command.go
@@ -12,15 +12,15 @@ import (
"time"
)
-func (h *APIHandler) HandleSaveCommonCommandAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
+func (h *APIHandler) HandleAddCommonCommandAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
resBody := map[string]interface{}{
}
reqParams := elastic.CommonCommand{}
err := h.DecodeJSON(req, &reqParams)
if err != nil {
- resBody["error"] = err
- h.WriteJSON(w, resBody, http.StatusInternalServerError)
+ resBody["error"] = err.Error()
+ h.WriteJSON(w, resBody, http.StatusOK)
return
}
@@ -28,20 +28,25 @@ func (h *APIHandler) HandleSaveCommonCommandAction(w http.ResponseWriter, req *h
reqParams.ID = util.GetUUID()
esClient := elastic.GetClient(h.Config.Elasticsearch)
- queryDSL :=[]byte(fmt.Sprintf(`{"size":1, "query":{"bool":{"must":{"match":{"title":"%s"}}}}}`, reqParams.Title))
+ queryDSL :=[]byte(fmt.Sprintf(`{"size":1, "query":{"bool":{"must":{"match":{"title.keyword":"%s"}}}}}`, reqParams.Title))
var indexName = orm.GetIndexName(reqParams)
searchRes, err := esClient.SearchWithRawQueryDSL(indexName, queryDSL)
if err != nil {
- resBody["error"] = err
- h.WriteJSON(w, resBody, http.StatusInternalServerError)
+ resBody["error"] = err.Error()
+ h.WriteJSON(w, resBody, http.StatusOK)
return
}
if len(searchRes.Hits.Hits) > 0 {
resBody["error"] = "title already exists"
- h.WriteJSON(w, resBody, http.StatusInternalServerError)
+ h.WriteJSON(w, resBody, http.StatusOK)
return
}
_, err = esClient.Index(indexName,"", reqParams.ID, reqParams)
+ if err != nil {
+ resBody["error"] = err.Error()
+ h.WriteJSON(w, resBody, http.StatusOK)
+ return
+ }
resBody["_id"] = reqParams.ID
resBody["result"] = "created"
@@ -49,20 +54,64 @@ func (h *APIHandler) HandleSaveCommonCommandAction(w http.ResponseWriter, req *h
h.WriteJSON(w, resBody,http.StatusOK)
}
+func (h *APIHandler) HandleSaveCommonCommandAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
+ resBody := map[string]interface{}{
+ }
+
+ reqParams := elastic.CommonCommand{}
+ err := h.DecodeJSON(req, &reqParams)
+ if err != nil {
+ resBody["error"] = err.Error()
+ h.WriteJSON(w, resBody, http.StatusOK)
+ return
+ }
+ reqParams.ID = ps.ByName("cid")
+ esClient := elastic.GetClient(h.Config.Elasticsearch)
+
+ queryDSL :=[]byte(fmt.Sprintf(`{"size":1, "query":{"bool":{"must":{"match":{"title.keyword":"%s"}}}}}`, reqParams.Title))
+ var indexName = orm.GetIndexName(reqParams)
+ searchRes, err := esClient.SearchWithRawQueryDSL(indexName, queryDSL)
+ if err != nil {
+ resBody["error"] = err.Error()
+ h.WriteJSON(w, resBody, http.StatusOK)
+ return
+ }
+ if len(searchRes.Hits.Hits) > 0 && searchRes.Hits.Hits[0].ID.(string) != reqParams.ID {
+ resBody["error"] = "title already exists"
+ h.WriteJSON(w, resBody, http.StatusOK)
+ return
+ }
+ _, err = esClient.Index(indexName,"", reqParams.ID, reqParams)
+ if err != nil {
+ resBody["error"] = err.Error()
+ h.WriteJSON(w, resBody, http.StatusOK)
+ return
+ }
+
+ resBody["_id"] = reqParams.ID
+ resBody["result"] = "updated"
+ resBody["_source"] = reqParams
+
+ h.WriteJSON(w, resBody,http.StatusOK)
+}
func (h *APIHandler) HandleQueryCommonCommandAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
resBody := map[string]interface{}{
}
var (
- title = h.GetParameterOrDefault(req, "title", "")
- queryDSL = `{"query":{"bool":{"filter":[%s]}}, "size": %d, "from": %d}`
+ keyword = h.GetParameterOrDefault(req, "keyword", "")
+ queryDSL = `{"query":{"bool":{"must":[%s]}}, "size": %d, "from": %d}`
strSize = h.GetParameterOrDefault(req, "size", "20")
strFrom = h.GetParameterOrDefault(req, "from", "0")
filterBuilder = &strings.Builder{}
)
- if title != ""{
- filterBuilder.WriteString(fmt.Sprintf(`{"prefix":{"title": "%s"}}`, title))
+ if keyword != ""{
+ filterBuilder.WriteString(fmt.Sprintf(`{"query_string": {
+ "default_field": "*",
+ "query": "%s"
+ }
+ }`, keyword))
}
size, _ := strconv.Atoi(strSize)
if size <= 0 {
diff --git a/api/init.go b/api/init.go
index b86b1ddb..b13468ae 100644
--- a/api/init.go
+++ b/api/init.go
@@ -41,7 +41,8 @@ func Init(cfg *config.AppConfig) {
api.HandleAPIMethod(api.DELETE, path.Join(esPrefix, "index/:index"), handler.HandleDeleteIndexAction)
api.HandleAPIMethod(api.POST, path.Join(esPrefix, "index/:index"), handler.HandleCreateIndexAction)
- api.HandleAPIMethod(api.POST, path.Join(pathPrefix, "elasticsearch/command"), handler.HandleSaveCommonCommandAction)
+ api.HandleAPIMethod(api.POST, path.Join(pathPrefix, "elasticsearch/command"), handler.HandleAddCommonCommandAction)
+ api.HandleAPIMethod(api.PUT, path.Join(pathPrefix, "elasticsearch/command/:cid"), handler.HandleSaveCommonCommandAction)
api.HandleAPIMethod(api.GET, path.Join(pathPrefix, "elasticsearch/command"), handler.HandleQueryCommonCommandAction)
api.HandleAPIMethod(api.DELETE, path.Join(pathPrefix, "elasticsearch/command/:cid"), handler.HandleDeleteCommonCommandAction)
diff --git a/main.go b/main.go
index 9f119a32..4a965f88 100644
--- a/main.go
+++ b/main.go
@@ -91,6 +91,7 @@ func main() {
orm.RegisterSchemaWithIndexName(alerting.Config{}, "alerting-config")
orm.RegisterSchemaWithIndexName(alerting.Alert{}, "alerting-alerts")
orm.RegisterSchemaWithIndexName(alerting.AlertingHistory{}, "alerting-alert-history")
+ orm.RegisterSchema(elastic.CommonCommand{})
alertSrv.GetScheduler().Start()
},nil){
app.Run()
diff --git a/web/config/router.config.js b/web/config/router.config.js
index 0ade204b..d94334b3 100644
--- a/web/config/router.config.js
+++ b/web/config/router.config.js
@@ -1,97 +1,93 @@
export default [
// user
{
- path: '/user',
- component: '../layouts/UserLayout',
+ path: "/user",
+ component: "../layouts/UserLayout",
routes: [
- { path: '/user', redirect: '/user/login' },
- { path: '/user/login', component: './User/Login' },
- { path: '/user/register', component: './User/Register' },
- { path: '/user/register-result', component: './User/RegisterResult' },
+ { path: "/user", redirect: "/user/login" },
+ { path: "/user/login", component: "./User/Login" },
+ { path: "/user/register", component: "./User/Register" },
+ { path: "/user/register-result", component: "./User/RegisterResult" },
],
},
// app
{
- path: '/',
- component: '../layouts/BasicLayout',
- Routes: ['src/pages/Authorized'],
- authority: ['admin', 'user'],
+ path: "/",
+ component: "../layouts/BasicLayout",
+ Routes: ["src/pages/Authorized"],
+ authority: ["admin", "user"],
routes: [
// cluster
- { path: '/', redirect: '/cluster/overview' },
+ { path: "/", redirect: "/cluster/overview" },
{
- path: '/cluster',
- name: 'cluster',
- icon: 'cluster',
+ path: "/cluster",
+ name: "cluster",
+ icon: "cluster",
routes: [
- // { path: '/', redirect: '/platform/gateway' },
- // {
- // path: '/cluster/overview/',
- // name: 'overview',
- // component: './Cluster/Overview',
- // routes:[
- // { path: '/', redirect: '/' },
- // ],
- // },
+ // { path: '/', redirect: '/platform/gateway' },
+ // {
+ // path: '/cluster/overview/',
+ // name: 'overview',
+ // component: './Cluster/Overview',
+ // routes:[
+ // { path: '/', redirect: '/' },
+ // ],
+ // },
{
- path: '/cluster/overview',
- name: 'overview',
- component: './Cluster/NewOverview',
- // hideInMenu: true,
- routes:[
- { path: '/', redirect: '/' },
- ],
- },
- {
- path: '/cluster/monitoring/:cluster_id',
- name: 'cluster',
- component: './Cluster/ClusterMonitor',
- hideInMenu: true,
- routes:[
- { path: '/', redirect: '/' },
- ],
- }, {
- path: '/cluster/metrics/',
- name: 'monitoring',
- component: './Cluster/Metrics',
- routes:[
- { path: '/', redirect: '/' },
- ],
- }, {
- path: '/cluster/metrics/:cluster_id',
- name: 'monitoring',
- component: './Cluster/Metrics',
- hideInMenu: true,
- },
- // {
- // path: '/cluster/logs/',
- // name: 'logging',
- // component: './Cluster/SearchMonitor',
- // routes:[
- // { path: '/', redirect: '/' },
- // ],
- // },{
- // path: '/cluster/settings/',
- // name: 'settings',
- // component: './Cluster/Settings/Base',
- // routes: [
- // {
- // path: '/cluster/settings',
- // redirect: '/cluster/settings/repository',
- // routes:[
- // { path: '/', redirect: '/' },
- // ],
- // },
- // {
- // path: '/cluster/settings/repository',
- // component: './Cluster/Settings/Repository',
- // routes:[
- // { path: '/', redirect: '/' },
- // ],
- // }
- // ]
- // },
- ]
+ path: "/cluster/overview",
+ name: "overview",
+ component: "./Cluster/NewOverview",
+ // hideInMenu: true,
+ routes: [{ path: "/", redirect: "/" }],
+ },
+ {
+ path: "/cluster/monitoring/:cluster_id",
+ name: "cluster",
+ component: "./Cluster/ClusterMonitor",
+ hideInMenu: true,
+ routes: [{ path: "/", redirect: "/" }],
+ },
+ {
+ path: "/cluster/metrics/",
+ name: "monitoring",
+ component: "./Cluster/Metrics",
+ routes: [{ path: "/", redirect: "/" }],
+ },
+ {
+ path: "/cluster/metrics/:cluster_id",
+ name: "monitoring",
+ component: "./Cluster/Metrics",
+ hideInMenu: true,
+ },
+ // {
+ // path: '/cluster/logs/',
+ // name: 'logging',
+ // component: './Cluster/SearchMonitor',
+ // routes:[
+ // { path: '/', redirect: '/' },
+ // ],
+ // },{
+ // path: '/cluster/settings/',
+ // name: 'settings',
+ // component: './Cluster/Settings/Base',
+ // routes: [
+ // {
+ // path: '/cluster/settings',
+ // redirect: '/cluster/settings/repository',
+ // routes:[
+ // { path: '/', redirect: '/' },
+ // ],
+ // },
+ // {
+ // path: '/cluster/settings/repository',
+ // component: './Cluster/Settings/Repository',
+ // routes:[
+ // { path: '/', redirect: '/' },
+ // ],
+ // }
+ // ]
+ // },
+ ],
},
//devtools
// {
@@ -104,40 +100,38 @@ export default [
// component: './DevTool/Console',
// },
- //alerting
- {
- path: '/alerting',
- name: 'alerting',
- icon: 'alert',
- routes: [{
- routes:[
- { path: '/', redirect: '/' },
- ],
- path: '/alerting/overview',
- component: './Alerting/pages/Overview/Overview',
- name: 'overview'
- },{
- routes:[
- { path: '/', redirect: '/' },
- ],
- path: '/alerting/monitor',
- component: './Alerting/index',
- name: 'monitor'
- },{
- routes:[
- { path: '/', redirect: '/' },
- ],
- path: '/alerting/destination',
- component: './Alerting/destination',
- name: 'destination'
- }]
+ //alerting
+ {
+ path: "/alerting",
+ name: "alerting",
+ icon: "alert",
+ routes: [
+ {
+ routes: [{ path: "/", redirect: "/" }],
+ path: "/alerting/overview",
+ component: "./Alerting/pages/Overview/Overview",
+ name: "overview",
+ },
+ {
+ routes: [{ path: "/", redirect: "/" }],
+ path: "/alerting/monitor",
+ component: "./Alerting/index",
+ name: "monitor",
+ },
+ {
+ routes: [{ path: "/", redirect: "/" }],
+ path: "/alerting/destination",
+ component: "./Alerting/destination",
+ name: "destination",
+ },
+ ],
},
//data
{
- path: '/data',
- name: 'data',
- icon: 'database',
+ path: "/data",
+ name: "data",
+ icon: "database",
routes: [
// {
// path: '/data/overview',
@@ -146,14 +140,12 @@ export default [
// routes:[
// { path: '/', redirect: '/' },
// ],
- // },
+ // },
{
- path: '/data/index',
- name: 'index',
- component: './DataManagement/Index',
- routes:[
- { path: '/', redirect: '/' },
- ],
+ path: "/data/index",
+ name: "index",
+ component: "./DataManagement/Index",
+ routes: [{ path: "/", redirect: "/" }],
},
// {
// path: '/data/document',
@@ -162,7 +154,7 @@ export default [
// routes:[
// { path: '/', redirect: '/' },
// ],
- // },
+ // },
// {
// path: '/data/template',
// name: 'template',
@@ -180,31 +172,25 @@ export default [
// ],
// },
{
- routes:[
- { path: '/', redirect: '/' },
- ],
- path: '/data/discover',
- name: 'discover',
- component: './DataManagement/Discover',
+ routes: [{ path: "/", redirect: "/" }],
+ path: "/data/discover",
+ name: "discover",
+ component: "./DataManagement/Discover",
},
{
- routes:[
- { path: '/', redirect: '/' },
- ],
- path: '/data/views/',
- name: 'indexPatterns',
- component: './DataManagement/IndexPatterns',
-
+ routes: [{ path: "/", redirect: "/" }],
+ path: "/data/views/",
+ name: "indexPatterns",
+ component: "./DataManagement/IndexPatterns",
},
- ]
+ ],
},
-
-
+
//search
{
- path: '/search',
- name: 'search',
- icon: 'search',
+ path: "/search",
+ name: "search",
+ icon: "search",
routes: [
// {
// path: '/search/overview',
@@ -245,34 +231,30 @@ export default [
// ],
// },
// ]
- // },
+ // },
{
- path: '/search/alias',
- name: 'alias',
- component: './SearchManage/alias/Alias',
- routes: [
- {
- path: '/search/alias',
- redirect: '/search/alias/index',
- // routes:[
- // { path: '/', redirect: '/' },
- // ],
- },
- {
- path: '/search/alias/index',
- component: './SearchManage/alias/AliasManage',
- routes:[
- { path: '/', redirect: '/' },
- ],
- },
- {
- path: '/search/alias/rule',
- component: './SearchManage/alias/Rule',
- routes:[
- { path: '/', redirect: '/' },
- ],
- }
- ]
+ path: "/search/alias",
+ name: "alias",
+ component: "./SearchManage/alias/Alias",
+ routes: [
+ {
+ path: "/search/alias",
+ redirect: "/search/alias/index",
+ // routes:[
+ // { path: '/', redirect: '/' },
+ // ],
+ },
+ {
+ path: "/search/alias/index",
+ component: "./SearchManage/alias/AliasManage",
+ routes: [{ path: "/", redirect: "/" }],
+ },
+ {
+ path: "/search/alias/rule",
+ component: "./SearchManage/alias/Rule",
+ routes: [{ path: "/", redirect: "/" }],
+ },
+ ],
},
// {
// path: '/search/dict',
@@ -328,33 +310,33 @@ export default [
// ]
// }
//, {
- // path: '/search/nlp',
- // name: 'nlp',
- // component: './SearchManage/nlp/NLP',
- // routes: [
- // {
- // path: '/search/nlp',
- // redirect: '/search/nlp/query',
- // },
- // {
- // path: '/search/nlp/query',
- // component: './SearchManage/nlp/Query',
- // },
- // {
- // path: '/search/nlp/intention',
- // component: './SearchManage/nlp/Intention',
- // },
- // {
- // path: '/search/nlp/knowledge',
- // component: './SearchManage/nlp/Knowledge',
- // },
- // {
- // path: '/search/nlp/text',
- // component: './SearchManage/nlp/Text',
- // }
- //]
- //},
- ]
+ // path: '/search/nlp',
+ // name: 'nlp',
+ // component: './SearchManage/nlp/NLP',
+ // routes: [
+ // {
+ // path: '/search/nlp',
+ // redirect: '/search/nlp/query',
+ // },
+ // {
+ // path: '/search/nlp/query',
+ // component: './SearchManage/nlp/Query',
+ // },
+ // {
+ // path: '/search/nlp/intention',
+ // component: './SearchManage/nlp/Intention',
+ // },
+ // {
+ // path: '/search/nlp/knowledge',
+ // component: './SearchManage/nlp/Knowledge',
+ // },
+ // {
+ // path: '/search/nlp/text',
+ // component: './SearchManage/nlp/Text',
+ // }
+ //]
+ //},
+ ],
},
//
// //sync
@@ -450,32 +432,32 @@ export default [
//settings
{
- path: '/system',
- name: 'system',
- icon: 'setting',
+ path: "/system",
+ name: "system",
+ icon: "setting",
routes: [
{
- path: '/system/cluster',
- name: 'cluster',
- component: './System/Cluster/Index',
+ path: "/system/cluster",
+ name: "cluster",
+ component: "./System/Cluster/Index",
},
{
- path: '/system/cluster/regist',
- name: 'registCluster',
- component: './System/Cluster/Step',
- hideInMenu: true
+ path: "/system/cluster/regist",
+ name: "registCluster",
+ component: "./System/Cluster/Step",
+ hideInMenu: true,
},
{
- path: '/system/cluster/edit',
- name: 'editCluster',
- component: './System/Cluster/Form',
- hideInMenu: true
+ path: "/system/cluster/edit",
+ name: "editCluster",
+ component: "./System/Cluster/Form",
+ hideInMenu: true,
},
{
- path: '/system/command',
- name: 'commonCommand',
- component: './System/Command/Index',
- hideInMenu: true
+ path: "/system/command",
+ name: "commonCommand",
+ component: "./System/Command/Index",
+ // hideInMenu: true
},
// {
// path: '/system/settings',
@@ -549,7 +531,7 @@ export default [
// },
// ]
// },
- ]
+ ],
},
// // forms
@@ -768,7 +750,7 @@ export default [
// ],
// },
{
- component: '404',
+ component: "404",
},
],
},
diff --git a/web/src/components/GlobalHeader/DropdownSelect.js b/web/src/components/GlobalHeader/DropdownSelect.js
index e27e904f..26a2fd4c 100644
--- a/web/src/components/GlobalHeader/DropdownSelect.js
+++ b/web/src/components/GlobalHeader/DropdownSelect.js
@@ -1,59 +1,71 @@
-import { Button, Dropdown, List, Spin, message, Icon, Input } from 'antd';
-import React from 'react';
-import InfiniteScroll from 'react-infinite-scroller';
-import styles from './DropdownSelect.less';
+import { Button, Dropdown, List, Spin, message, Icon, Input } from "antd";
+import React from "react";
+import InfiniteScroll from "react-infinite-scroller";
+import styles from "./DropdownSelect.less";
import _ from "lodash";
-import {DropdownItem} from './DropdownItem';
-import {HealthStatusCircle} from '@/components/infini/health_status_circle'
+import { DropdownItem } from "./DropdownItem";
+import { HealthStatusCircle } from "@/components/infini/health_status_circle";
-class DropdownSelect extends React.Component{
- constructor(props){
- super(props)
- this.state={
+class DropdownSelect extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
value: props.defaultValue,
loading: false,
hasMore: props.data.length > props.size,
overlayVisible: false,
data: (props.data || []).slice(0, props.size),
dataSource: [...props.data],
+ };
+ }
+
+ handleItemClick = (item) => {
+ let preValue = this.props.value || this.state.value;
+ this.setState(
+ {
+ value: item,
+ overlayVisible: false,
+ },
+ () => {
+ let onChange = this.props.onChange;
+ if (preValue != item && onChange && typeof onChange == "function") {
+ onChange(item);
+ }
+ }
+ );
+ };
+
+ componentDidMount() {}
+ componentDidUpdate(preProps, preState) {
+ if (preProps.data.length != preState.dataSource.length) {
+ const newData = [...preProps.data];
+ this.setState({
+ dataSource: newData,
+ data: newData,
+ hasMore: newData.length > this.props.size,
+ selectedIndex: -1,
+ });
}
}
-
- handleItemClick = (item)=>{
- let preValue = this.props.value || this.state.value;
- this.setState({
- value: item,
- overlayVisible: false,
- },()=>{
- let onChange = this.props.onChange;
- if(preValue != item && onChange && typeof onChange == 'function'){
- onChange(item)
- }
- })
- }
-
- componentDidMount(){
- }
-
handleInfiniteOnLoad = (current) => {
- let {size } = this.props;
+ let { size } = this.props;
let targetLength = current * size;
- let {hasMore, dataSource} = this.state;
- if(dataSource.length < targetLength){
+ let { hasMore, dataSource } = this.state;
+ if (dataSource.length < targetLength) {
targetLength = dataSource.length;
- hasMore = false
+ hasMore = false;
}
const newData = this.state.dataSource.slice(0, targetLength);
this.setState({
data: newData,
hasMore: hasMore,
- })
- }
+ });
+ };
- handleInputChange = (e) =>{
+ handleInputChange = (e) => {
const name = e.target.value;
- const newData = this.props.data.filter(item=>{
+ const newData = this.props.data.filter((item) => {
return item.name.includes(name);
});
this.setState({
@@ -61,76 +73,127 @@ class DropdownSelect extends React.Component{
dataSource: newData,
data: newData,
hasMore: newData.length > this.props.size,
- })
- }
+ });
+ };
-
- render(){
+ render() {
let me = this;
- const {labelField, clusterStatus} = this.props;
+ const { labelField, clusterStatus } = this.props;
let value = this.props.value || this.state.value;
let displayVaue = value[labelField];
- const menu = (
-
-
-
-
-
+
+
+
+
+
-
- {(!this.state.data || !this.state.data.length)&&
匹配不到集群(匹配规则为前缀匹配)
}
- {(this.state.data || []).map((item)=>{
- // return
- //
- //
- const cstatus = clusterStatus ? clusterStatus[item.id] : null;
- return
{
- this.handleItemClick(item)
- }}
- />
- })}
+ >
+
+ {(!this.state.data || !this.state.data.length) && (
+
+ 匹配不到集群(匹配规则为前缀匹配)
+
+ )}
+ {(this.state.data || []).map((item) => {
+ // return
+ //
+ //
+ const cstatus = clusterStatus ? clusterStatus[item.id] : null;
+ return (
+
{
+ this.handleItemClick(item);
+ }}
+ />
+ );
+ })}
+
+
-
+ {!this.state.loading && this.state.hasMore && (
+
+ pull load more
+
+ )}
- {!this.state.loading && this.state.hasMore && (
-
- pull load more
-
- )}
- );
+ );
const cstatus = clusterStatus ? clusterStatus[value?.id] : null;
- return(
- this.props.visible ?
- (
{
- this.setState({ overlayVisible: flag });
- }}>
- {/* ) : ""
- )
+
+
+ {cstatus?.available ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ ) : (
+ ""
+ );
}
-
}
-export default DropdownSelect;
\ No newline at end of file
+export default DropdownSelect;
diff --git a/web/src/components/GlobalHeader/RightContent.js b/web/src/components/GlobalHeader/RightContent.js
index b80d1c35..82ac01d8 100644
--- a/web/src/components/GlobalHeader/RightContent.js
+++ b/web/src/components/GlobalHeader/RightContent.js
@@ -1,24 +1,24 @@
-import React, { PureComponent } from 'react';
-import { FormattedMessage, formatMessage } from 'umi/locale';
-import { Spin, Tag, Menu, Icon, Dropdown, Avatar, Tooltip } from 'antd';
-import moment from 'moment';
-import groupBy from 'lodash/groupBy';
-import NoticeIcon from '../NoticeIcon';
-import HeaderSearch from '../HeaderSearch';
-import SelectLang from '../SelectLang';
-import styles from './index.less';
-import {ConsoleUI} from '@/pages/DevTool/Console';
+import React, { PureComponent } from "react";
+import { FormattedMessage, formatMessage } from "umi/locale";
+import { Spin, Tag, Menu, Icon, Dropdown, Avatar, Tooltip } from "antd";
+import moment from "moment";
+import groupBy from "lodash/groupBy";
+import NoticeIcon from "../NoticeIcon";
+import HeaderSearch from "../HeaderSearch";
+import SelectLang from "../SelectLang";
+import styles from "./index.less";
+import { ConsoleUI } from "@/pages/DevTool/Console";
import { Resizable } from "re-resizable";
-import {ResizeBar} from '@/components/infini/resize_bar';
+import { ResizeBar } from "@/components/infini/resize_bar";
export default class GlobalHeaderRight extends PureComponent {
- state={consoleVisible: false}
+ state = { consoleVisible: false };
getNoticeData() {
const { notices = [] } = this.props;
if (notices.length === 0) {
return {};
}
- const newNotices = notices.map(notice => {
+ const newNotices = notices.map((notice) => {
const newNotice = { ...notice };
if (newNotice.datetime) {
newNotice.datetime = moment(notice.datetime).fromNow();
@@ -28,10 +28,10 @@ export default class GlobalHeaderRight extends PureComponent {
}
if (newNotice.extra && newNotice.status) {
const color = {
- todo: '',
- processing: 'blue',
- urgent: 'red',
- doing: 'gold',
+ todo: "",
+ processing: "blue",
+ urgent: "red",
+ doing: "gold",
}[newNotice.status];
newNotice.extra = (
@@ -41,7 +41,7 @@ export default class GlobalHeaderRight extends PureComponent {
}
return newNotice;
});
- return groupBy(newNotices, 'type');
+ return groupBy(newNotices, "type");
}
render() {
@@ -57,15 +57,24 @@ export default class GlobalHeaderRight extends PureComponent {
);
}
@@ -205,4 +226,4 @@ export default class GlobalHeaderRight extends PureComponent {
const TopHandle = () => {
return hello world
;
-};
\ No newline at end of file
+};
diff --git a/web/src/components/kibana/console/components/CommonCommandModal.tsx b/web/src/components/kibana/console/components/CommonCommandModal.tsx
index 0b3b54fe..f74ad35b 100644
--- a/web/src/components/kibana/console/components/CommonCommandModal.tsx
+++ b/web/src/components/kibana/console/components/CommonCommandModal.tsx
@@ -1,16 +1,16 @@
// @ts-ignore
-import React, { useState, useCallback } from 'react';
-import { Modal, Form, Input, Tag } from 'antd';
-import { PlusOutlined } from '@ant-design/icons';
+import React, { useState, useCallback } from "react";
+import { Modal, Form, Input, Tag } from "antd";
+import { PlusOutlined } from "@ant-design/icons";
interface ITagGeneratorProps {
- value?: Array,
+ value?: Array;
onChange?: (val: Array) => void;
}
-const TagGenerator = ({ value = [], onChange }: ITagGeneratorProps) => {
+export const TagGenerator = ({ value = [], onChange }: ITagGeneratorProps) => {
const [inputVisible, setInputVisible] = useState(false);
- const [inputValue, setInputValue] = useState('');
+ const [inputValue, setInputValue] = useState("");
const handleInputChange = (e) => {
setInputValue(e.target.value);
@@ -20,31 +20,52 @@ const TagGenerator = ({ value = [], onChange }: ITagGeneratorProps) => {
setInputVisible(true);
};
- const handleInputConfirm = useCallback((e) => {
- onChange([...(value || []), e.target.value]);
- setInputVisible(false);
- setInputValue('');
- }, [value]);
+ const handleInputConfirm = useCallback(
+ (e) => {
+ onChange([...(value || []), e.target.value]);
+ setInputVisible(false);
+ setInputValue("");
+ },
+ [value]
+ );
- const handleRemove = useCallback((index) => {
- const newValue = [...value];
- newValue.splice(index, 1);
- onChange(newValue);
- }, [value]);
+ const handleRemove = useCallback(
+ (index) => {
+ const newValue = [...value];
+ newValue.splice(index, 1);
+ onChange(newValue);
+ },
+ [value]
+ );
return (
- )
+ );
};
interface ICommonCommandModalProps {
@@ -60,21 +81,27 @@ const CommonCommandModal = Form.create()((props: ICommonCommandModalProps) => {
try {
const values = await form.validateFields();
props.onConfirm(values);
- } catch (e) {
-
- }
+ } catch (e) {}
};
return (
-
+
- {form.getFieldDecorator('title', {
- rules: [{ required: true, message: '请输入标题' }]
+ {form.getFieldDecorator("title", {
+ rules: [{ required: true, message: "请输入标题" }],
})()}
- {form.getFieldDecorator('tag')( )}
+ {form.getFieldDecorator("tag")()}
diff --git a/web/src/components/kibana/console/components/ConsoleInput.tsx b/web/src/components/kibana/console/components/ConsoleInput.tsx
index 3e0837b4..0a472edb 100644
--- a/web/src/components/kibana/console/components/ConsoleInput.tsx
+++ b/web/src/components/kibana/console/components/ConsoleInput.tsx
@@ -1,32 +1,38 @@
// @ts-ignore
-import React, { useRef, useEffect, CSSProperties, useMemo } from 'react';
-import ace from 'brace';
-import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiToolTip, PropertySortType } from '@elastic/eui';
-import { SenseEditor } from '../entities/sense_editor';
-import { LegacyCoreEditor } from '../modules/legacy_core_editor/legacy_core_editor';
-import ConsoleMenu from './ConsoleMenu';
+import React, { useRef, useEffect, CSSProperties, useMemo } from "react";
+import ace from "brace";
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiIcon,
+ EuiToolTip,
+ PropertySortType,
+} from "@elastic/eui";
+import { SenseEditor } from "../entities/sense_editor";
+import { LegacyCoreEditor } from "../modules/legacy_core_editor/legacy_core_editor";
+import ConsoleMenu from "./ConsoleMenu";
// import { RequestContextProvider } from '../contexts/request_context';
-import { getDocumentation, autoIndent } from '../entities/console_menu_actions';
-import './ConsoleInput.scss';
-import { useSendCurrentRequestToES } from '../hooks/use_send_current_request_to_es';
-import { useSetInputEditor } from '../hooks/use_set_input_editor';
-import '@elastic/eui/dist/eui_theme_light.css';
-import { instance as registry } from '../contexts/editor_context/editor_registry';
-import 'antd/dist/antd.css';
-import {retrieveAutoCompleteInfo} from '../modules/mappings/mappings';
-import {useSaveCurrentTextObject} from '../hooks/use_save_current_text_object';
-import {useEditorReadContext} from '../contexts/editor_context/editor_context';
-import {useDataInit} from '../hooks/use_data_init';
-import { useServicesContext } from '../contexts';
-import {applyCurrentSettings} from './apply_editor_settings';
-import { subscribeResizeChecker } from './subscribe_console_resize_checker';
+import { getDocumentation, autoIndent } from "../entities/console_menu_actions";
+import "./ConsoleInput.scss";
+import { useSendCurrentRequestToES } from "../hooks/use_send_current_request_to_es";
+import { useSetInputEditor } from "../hooks/use_set_input_editor";
+import "@elastic/eui/dist/eui_theme_light.css";
+import { instance as registry } from "../contexts/editor_context/editor_registry";
+import "antd/dist/antd.css";
+import { retrieveAutoCompleteInfo } from "../modules/mappings/mappings";
+import { useSaveCurrentTextObject } from "../hooks/use_save_current_text_object";
+import { useEditorReadContext } from "../contexts/editor_context/editor_context";
+import { useDataInit } from "../hooks/use_data_init";
+import { useServicesContext } from "../contexts";
+import { applyCurrentSettings } from "./apply_editor_settings";
+import { subscribeResizeChecker } from "./subscribe_console_resize_checker";
const abs: CSSProperties = {
- position: 'absolute',
- top: '0',
- left: '0',
- bottom: '0',
- right: '0',
+ position: "absolute",
+ top: "0",
+ left: "0",
+ bottom: "0",
+ right: "0",
};
// interface IConsoleInputProps {
@@ -41,19 +47,16 @@ const SendRequestButton = (props: any) => {
const sendCurrentRequestToES = useSendCurrentRequestToES();
const saveCurrentTextObject = useSaveCurrentTextObject();
- const {saveCurrentTextObjectRef} = props;
- useEffect(()=>{
- saveCurrentTextObjectRef.current = saveCurrentTextObject
- }, [saveCurrentTextObjectRef])
-
+ const { saveCurrentTextObjectRef } = props;
+ useEffect(() => {
+ saveCurrentTextObjectRef.current = saveCurrentTextObject;
+ }, [saveCurrentTextObjectRef]);
return (
-
+
@@ -64,11 +67,11 @@ const SendRequestButton = (props: any) => {
};
interface ConsoleInputProps {
- clusterID: string,
- initialText: string | undefined,
- saveEditorContent: (content: string)=>void,
- paneKey: string,
- height?: string,
+ clusterID: string;
+ initialText: string | undefined;
+ saveEditorContent: (content: string) => void;
+ paneKey: string;
+ height?: string;
}
const DEFAULT_INPUT_VALUE = `GET _search
@@ -78,46 +81,59 @@ const DEFAULT_INPUT_VALUE = `GET _search
}
}`;
-
-const ConsoleInputUI = ({clusterID, initialText, saveEditorContent, paneKey, height='100%'}:ConsoleInputProps) => {
+const ConsoleInputUI = ({
+ clusterID,
+ initialText,
+ saveEditorContent,
+ paneKey,
+ height = "100%",
+}: ConsoleInputProps) => {
const editorRef = useRef(null);
const editorActionsRef = useRef(null);
const editorInstanceRef = useRef(null);
-
+
const setInputEditor = useSetInputEditor();
const consoleMenuRef = useRef(null);
const aceEditorRef = useRef(null);
- const sendCurrentRequestToESRef = useRef(()=>{});
- const saveCurrentTextObjectRef = useRef((content:string)=>{});
+ const sendCurrentRequestToESRef = useRef(() => {});
+ const saveCurrentTextObjectRef = useRef((content: string) => {});
sendCurrentRequestToESRef.current = useSendCurrentRequestToES();
-
- const {services:{settings}} = useServicesContext();
+
+ const {
+ services: { settings },
+ } = useServicesContext();
useEffect(() => {
const aceEditor = ace.edit(editorRef.current!);
aceEditorRef.current = aceEditor;
- const legacyCoreEditor = new LegacyCoreEditor(aceEditor, editorActionsRef.current as HTMLElement);
+ const legacyCoreEditor = new LegacyCoreEditor(
+ aceEditor,
+ editorActionsRef.current as HTMLElement
+ );
aceEditor.commands.addCommand({
- name: 'exec_request',
- bindKey: {win: "Ctrl-enter", mac: "Command-enter|Ctrl-enter"},
- exec: ()=>{
+ name: "exec_request",
+ bindKey: { win: "Ctrl-enter", mac: "Command-enter|Ctrl-enter" },
+ exec: () => {
sendCurrentRequestToESRef.current();
- }
- })
+ },
+ });
const senseEditor = new SenseEditor(legacyCoreEditor);
// senseEditor.highlightCurrentRequestsAndUpdateActionBar();
editorInstanceRef.current = senseEditor;
setInputEditor(senseEditor);
senseEditor.paneKey = paneKey;
senseEditor.update(initialText || DEFAULT_INPUT_VALUE);
- applyCurrentSettings(senseEditor!.getCoreEditor(), {fontSize:12, wrapMode: true,});
+ applyCurrentSettings(senseEditor!.getCoreEditor(), {
+ fontSize: 12,
+ wrapMode: true,
+ });
function setupAutosave() {
let timer: number;
const saveDelay = 500;
-
- senseEditor.getCoreEditor().on('change', () => {
+
+ senseEditor.getCoreEditor().on("change", () => {
if (timer) {
clearTimeout(timer);
}
@@ -129,14 +145,17 @@ const ConsoleInputUI = ({clusterID, initialText, saveEditorContent, paneKey, hei
try {
const content = senseEditor.getCoreEditor().getValue();
// saveCurrentTextObjectRef.current(content);
- saveEditorContent(content)
+ saveEditorContent(content);
} catch (e) {
- console.log(e)
+ console.log(e);
// Ignoring saving error
}
}
- const unsubscribeResizer = subscribeResizeChecker(editorRef.current!, senseEditor);
+ const unsubscribeResizer = subscribeResizeChecker(
+ editorRef.current!,
+ senseEditor
+ );
setupAutosave();
return () => {
@@ -144,68 +163,76 @@ const ConsoleInputUI = ({clusterID, initialText, saveEditorContent, paneKey, hei
if (editorInstanceRef.current) {
editorInstanceRef.current.getCoreEditor().destroy();
}
- }
+ };
}, []);
- useEffect(()=>{
+ useEffect(() => {
retrieveAutoCompleteInfo(settings, settings.getAutocomplete(), clusterID);
- aceEditorRef.current && (aceEditorRef.current['clusterID'] = clusterID);
- },[clusterID])
+ aceEditorRef.current && (aceEditorRef.current["clusterID"] = clusterID);
+ }, [clusterID]);
- const handleSaveAsCommonCommand = async () => {
+ const handleSaveAsCommonCommand = async () => {
const editor = editorInstanceRef.current;
- if(editor == null){
- console.warn('editor is null')
- return
+ if (editor == null) {
+ console.warn("editor is null");
+ return;
}
const requests = await editor.getRequestsInRange();
- const formattedRequest = requests.map(request => ({
+ const formattedRequest = requests.map((request) => ({
method: request.method,
path: request.url,
- body: (request.data || []).join('\n'),
+ body: (request.data || []).join("\n"),
}));
return formattedRequest;
};
return (
-
-
-
-
-
-
-
-
-
- {
- return editorInstanceRef.current!.getRequestsAsCURL('');
- }}
- getDocumentation={() => {
- return getDocumentation(editorInstanceRef.current!, '');
- }}
- autoIndent={(event) => {
- autoIndent(editorInstanceRef.current!, event);
- }}
- saveAsCommonCommand={handleSaveAsCommonCommand}
- ref={consoleMenuRef}
- />
-
-
-
{consoleMenuRef.current?.closePopover(); aceEditorRef.current?.focus()}}
+
+
+
+ {
+ return editorInstanceRef.current!.getRequestsAsCURL("");
+ }}
+ getDocumentation={() => {
+ return getDocumentation(editorInstanceRef.current!, "");
+ }}
+ autoIndent={(event) => {
+ autoIndent(editorInstanceRef.current!, event);
+ }}
+ saveAsCommonCommand={handleSaveAsCommonCommand}
+ ref={consoleMenuRef}
+ />
+
+
+
{
+ consoleMenuRef.current?.closePopover();
+ aceEditorRef.current?.focus();
+ }}
+ />
+
+
);
};
@@ -217,6 +244,3 @@ const ConsoleInputUI = ({clusterID, initialText, saveEditorContent, paneKey, hei
// export default ConsoleInput;
export default ConsoleInputUI;
-
-
-
diff --git a/web/src/components/kibana/data/common/index_patterns/index_patterns/index_patterns.ts b/web/src/components/kibana/data/common/index_patterns/index_patterns/index_patterns.ts
index f0e638f2..9dc04462 100644
--- a/web/src/components/kibana/data/common/index_patterns/index_patterns/index_patterns.ts
+++ b/web/src/components/kibana/data/common/index_patterns/index_patterns/index_patterns.ts
@@ -456,7 +456,7 @@ export class IndexPatternsService {
});
const indexPattern = await this.create(spec, true);
- indexPatternCache.set(id, indexPattern);
+ indexPatternCache.set(cacheID, indexPattern);
if (isSaveRequired) {
try {
this.updateSavedObject(indexPattern);
diff --git a/web/src/components/kibana/discover/public/application/components/doc_viewer/doc_viewer.scss b/web/src/components/kibana/discover/public/application/components/doc_viewer/doc_viewer.scss
index 113e037a..cc557245 100644
--- a/web/src/components/kibana/discover/public/application/components/doc_viewer/doc_viewer.scss
+++ b/web/src/components/kibana/discover/public/application/components/doc_viewer/doc_viewer.scss
@@ -1,8 +1,8 @@
-@import '../../../../../core/public/variables.scss';
+@import "../../../../../core/public/variables.scss";
.kbnDocViewerTable {
margin-top: $euiSizeS;
font-size: 12px;
- .kbnDocViewer__buttons{
+ .kbnDocViewer__buttons {
padding-top: 0px !important;
}
}
@@ -20,6 +20,7 @@
}
.kbnDocViewer__field {
padding-top: 8px;
+ max-width: 250px;
}
.dscFieldName {
@@ -73,4 +74,3 @@
.kbnDocViewer__warning {
margin-right: $euiSizeS;
}
-
diff --git a/web/src/components/kibana/discover/public/application/components/table/table.tsx b/web/src/components/kibana/discover/public/application/components/table/table.tsx
index 628045bd..18f2d3bf 100644
--- a/web/src/components/kibana/discover/public/application/components/table/table.tsx
+++ b/web/src/components/kibana/discover/public/application/components/table/table.tsx
@@ -16,11 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React, { useState } from 'react';
-import { escapeRegExp } from 'lodash';
-import { DocViewTableRow } from './table_row';
-import { arrayContainsObjects, trimAngularSpan } from './table_helper';
-import { DocViewRenderProps } from '../../doc_views/doc_views_types';
+import React, { useState } from "react";
+import { escapeRegExp } from "lodash";
+import { DocViewTableRow } from "./table_row";
+import { arrayContainsObjects, trimAngularSpan } from "./table_helper";
+import { DocViewRenderProps } from "../../doc_views/doc_views_types";
const COLLAPSE_LINE_LENGTH = 350;
@@ -34,8 +34,10 @@ export function DocViewTable({
}: DocViewRenderProps) {
const mapping = indexPattern.fields.getByName;
const flattened = indexPattern.flattenHit(hit);
- const formatted = indexPattern.formatHit(hit, 'html');
- const [fieldRowOpen, setFieldRowOpen] = useState({} as Record
);
+ const formatted = indexPattern.formatHit(hit, "html");
+ const [fieldRowOpen, setFieldRowOpen] = useState(
+ {} as Record
+ );
function toggleValueCollapse(field: string) {
fieldRowOpen[field] = fieldRowOpen[field] !== true;
@@ -64,8 +66,10 @@ export function DocViewTable({
}
: undefined;
const isArrayOfObjects =
- Array.isArray(flattened[field]) && arrayContainsObjects(flattened[field]);
- const displayUnderscoreWarning = !mapping(field) && field.indexOf('_') === 0;
+ Array.isArray(flattened[field]) &&
+ arrayContainsObjects(flattened[field]);
+ const displayUnderscoreWarning =
+ !mapping(field) && field.indexOf("_") === 0;
const displayNoMappingWarning =
!mapping(field) && !displayUnderscoreWarning && !isArrayOfObjects;
@@ -107,10 +111,16 @@ export function DocViewTable({
!indexPattern.fields.getByName(field) &&
!!indexPattern.fields.getAll().find((patternField) => {
// We only want to match a full path segment
- const nestedRootRegex = new RegExp(escapeRegExp(field) + '(\\.|$)');
- return nestedRootRegex.test(patternField.subType?.nested?.path ?? '');
+ const nestedRootRegex = new RegExp(
+ escapeRegExp(field) + "(\\.|$)"
+ );
+ return nestedRootRegex.test(
+ patternField.subType?.nested?.path ?? ""
+ );
});
- const fieldType = isNestedField ? 'nested' : indexPattern.fields.getByName(field)?.type;
+ const fieldType = isNestedField
+ ? "nested"
+ : indexPattern.fields.getByName(field)?.type;
return (
toggleValueCollapse(field)}
onToggleColumn={toggleColumn}
diff --git a/web/src/lib/hooks/use_async.js b/web/src/lib/hooks/use_async.js
new file mode 100644
index 00000000..5b053e47
--- /dev/null
+++ b/web/src/lib/hooks/use_async.js
@@ -0,0 +1,23 @@
+import * as React from "react";
+
+export default function useAsync(callback, dependencies = []) {
+ const [loading, setLoading] = React.useState(true);
+ const [error, setError] = React.useState();
+ const [value, setValue] = React.useState();
+
+ const callbackMemoized = React.useCallback(() => {
+ setLoading(true);
+ setError(undefined);
+ setValue(undefined);
+ callback()
+ .then(setValue)
+ .catch(setError)
+ .finally(() => setLoading(false));
+ }, dependencies);
+
+ React.useEffect(() => {
+ callbackMemoized();
+ }, [callbackMemoized]);
+
+ return { loading, error, value };
+}
diff --git a/web/src/lib/hooks/use_fetch.js b/web/src/lib/hooks/use_fetch.js
new file mode 100644
index 00000000..441b3ca0
--- /dev/null
+++ b/web/src/lib/hooks/use_fetch.js
@@ -0,0 +1,21 @@
+import useAsync from "./use_async";
+
+const DEFAULT_OPTIONS = {
+ headers: { "Content-Type": "application/json" },
+};
+
+export default function useFetch(url, options = {}, dependencies = []) {
+ return useAsync(() => {
+ if (options.queryParams) {
+ url +=
+ "?" +
+ Object.entries(options.queryParams)
+ .map((kvs) => kvs.join("="))
+ .join("&");
+ }
+ return fetch(url, { ...DEFAULT_OPTIONS, ...options }).then((res) => {
+ if (res.ok) return res.json();
+ return res.json().then((json) => Promise.reject(json));
+ });
+ }, dependencies);
+}
diff --git a/web/src/pages/Cluster/Metrics.js b/web/src/pages/Cluster/Metrics.js
index 9ba0b411..bcf4e86e 100644
--- a/web/src/pages/Cluster/Metrics.js
+++ b/web/src/pages/Cluster/Metrics.js
@@ -1,559 +1,671 @@
-import React, {PureComponent, useState} from 'react';
-import {connect} from 'dva';
-import {formatMessage} from 'umi/locale';
-import {Button, Card, Col, DatePicker, Dropdown, Icon, Input, InputNumber, Row, Select, Statistic} from 'antd';
-import moment from 'moment';
-import {DateTime} from 'luxon';
+import React, { PureComponent, useState } from "react";
+import { connect } from "dva";
+import { formatMessage } from "umi/locale";
+import {
+ Button,
+ Card,
+ Col,
+ DatePicker,
+ Dropdown,
+ Icon,
+ Input,
+ InputNumber,
+ Row,
+ Select,
+ Statistic,
+} from "antd";
+import moment from "moment";
import router from "umi/router";
-import numeral from 'numeral';
-
-import '@elastic/charts/dist/theme_only_light.css';
+import "@elastic/charts/dist/theme_only_light.css";
import {
- Axis,
- Chart,
- CurveType,
- LineSeries,
- niceTimeFormatByDay,
- Position,
- ScaleType,
- Settings,
- timeFormatter
+ Axis,
+ Chart,
+ CurveType,
+ LineSeries,
+ niceTimeFormatByDay,
+ Position,
+ ScaleType,
+ Settings,
+ timeFormatter,
} from "@elastic/charts";
-import styles from './Metrics.less';
+import styles from "./Metrics.less";
-import { Spin, Alert } from 'antd';
-import {EuiSuperDatePicker} from '@elastic/eui';
-import {calculateBounds} from '../../components/kibana/data/common/query/timefilter';
-
-const {RangePicker} = DatePicker;
+import { Spin, Alert, Tabs } from "antd";
+import { EuiSuperDatePicker } from "@elastic/eui";
+import { calculateBounds } from "../../components/kibana/data/common/query/timefilter";
+import NodeMetric from "./components/node_metric";
+import IndexMetric from "./components/index_metric";
+import { formatter, getFormatter, getNumFormatter } from "./format";
+const { RangePicker } = DatePicker;
let fetchDataCount = 0;
let tv1 = null;
let HealthCircle = (props) => {
- return (
-
- )
-}
-
-const unitArr = Array("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB");
-
-let formatter = {
- bytes: (value) => {
- if (isNaN(value) || null == value || value === ''||value==0) {
- return "0B";
- }
- var index = 0;
- var srcsize = parseFloat(value);
- index = Math.floor(Math.log(srcsize) / Math.log(1024));
- var size = srcsize / Math.pow(1024, index);
- size = size.toFixed(1);
- return size + unitArr[index];
- },
- dates: timeFormatter(niceTimeFormatByDay(1)),
- full_dates: (d) => DateTime.fromMillis(d).toFormat('yyyy-MM-dd HH:mm:ss'),
- utc_full_dates: (d) => DateTime.fromMillis(d).toUTC().toFormat('yyyy-MM-dd HH:mm:ss'),
- ratio: (d) => `${Number(d).toFixed(0)}%`,
- highPrecisionNumber: (d) => numeral(d).format('0.0000'),
- lowPrecisionNumber: (d) => numeral(d).format('0.0'),
- number: (d) => numeral(d).format('0,0'),
-
-}
-
-function getFormatter(type, format, units) {
- switch (type) {
- case "bytes":
- return formatter.bytes
- case "ratio":
- return formatter.ratio
- case "num":
- return getNumFormatter(format, units)
- default:
- return formatter.lowPrecisionNumber
- }
-}
-
-function getNumFormatter(format, units) {
- return (d) => numeral(d).format(format) + (units ? ` ${units}` : '')
-}
+ return (
+
+ );
+};
let timezone = "local";
const theme = {
- legend: {
- margin: 0,
- padding: 0,
- left: 0,
- right: 0,
- top: 0,
- bottom: 0,
+ legend: {
+ margin: 0,
+ padding: 0,
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ },
+ chartMargins: {
+ left: 5,
+ right: 0,
+ top: 1,
+ bottom: 5,
+ },
+ chartPaddings: {
+ left: 5,
+ right: 5,
+ top: 0,
+ bottom: 0,
+ },
+ scales: {
+ barsPadding: 0.24,
+ },
+ axes: {
+ axisTitle: {
+ fill: "#333",
+ fontSize: 12,
+ fontStyle: "bold",
+ fontFamily: "'Open Sans', Helvetica, Arial, sans-serif",
+ padding: 2,
},
- chartMargins: {
- left: 5,
- right: 0,
- top: 1,
- bottom: 5,
+ axisLine: {
+ stroke: "#333",
},
- chartPaddings: {
- left: 5,
- right: 5,
- top: 0,
- bottom: 0,
+ tickLabel: {
+ fill: "#333",
+ fontSize: 10,
+ fontFamily: "'Open Sans', Helvetica, Arial, sans-serif",
+ fontStyle: "normal",
+ padding: 2,
},
- scales: {
- barsPadding: 0.24,
+ tickLine: {
+ visible: true,
+ stroke: "#333",
+ strokeWidth: 1,
+ padding: 0,
},
- axes: {
- axisTitle: {
- fill: '#333',
- fontSize: 12,
- fontStyle: 'bold',
- fontFamily: "'Open Sans', Helvetica, Arial, sans-serif",
- padding: 2,
- },
- axisLine: {
- stroke: '#333',
- },
- tickLabel: {
- fill: '#333',
- fontSize: 10,
- fontFamily: "'Open Sans', Helvetica, Arial, sans-serif",
- fontStyle: 'normal',
- padding: 2,
- },
- tickLine: {
- visible: true,
- stroke: '#333',
- strokeWidth: 1,
- padding: 0,
- },
- },
-}
-
-const vstyle = {
- fontSize: 12,
- wordBreak: "break-all",
- marginRight: "5px"
+ },
};
-const MonitorDatePicker = ({timeRange, commonlyUsedRanges, onChange, isLoading}) => {
- // const [recentlyUsedRanges, setRecentlyUsedRanges] = useState([]);
- const [isPaused, setIsPaused] = useState(true);
- const [refreshInterval, setRefreshInterval] = useState(10000);
-
- const onTimeChange = ({ start, end }) => {
- onChange({
- start,
- end,
- });
+const vstyle = {
+ fontSize: 12,
+ wordBreak: "break-all",
+ marginRight: "5px",
+};
+
+const MonitorDatePicker = ({
+ timeRange,
+ commonlyUsedRanges,
+ onChange,
+ isLoading,
+}) => {
+ // const [recentlyUsedRanges, setRecentlyUsedRanges] = useState([]);
+ const [isPaused, setIsPaused] = useState(false);
+ const [refreshInterval, setRefreshInterval] = useState(10000);
+
+ const onTimeChange = ({ start, end }) => {
+ onChange({
+ start,
+ end,
+ });
+ };
+
+ const onRefresh = ({ start, end, refreshInterval }) => {
+ onChange({ start, end });
+ };
+
+ const onRefreshChange = ({ isPaused, refreshInterval }) => {
+ setIsPaused(isPaused);
+ setRefreshInterval(refreshInterval);
+ };
+
+ return (
+
+ );
+};
+
+@connect(({ clusterMonitor, global }) => ({
+ clusterMonitor,
+ selectedCluster: global.selectedCluster,
+ clusterList: global.clusterList,
+}))
+class ClusterMonitor extends PureComponent {
+ constructor(props) {
+ super(props);
+ //this.timePicker = React.createRef();
+ this.handleChartBrush = this.handleChartBrush.bind(this);
+ this.chartRefs = [];
+ this.handleTimeChange = this.handleTimeChange.bind(this);
+ this.state = {
+ spinning: false,
+ clusterID: null,
+ timeRange: {
+ min: "now-1h", //moment().subtract(1, 'h').toISOString(),
+ max: "now", //moment().toISOString()
+ timeFormatter: formatter.dates(1),
+ },
};
-
- const onRefresh = ({ start, end, refreshInterval }) => {
- onChange({start, end})
+ }
+
+ componentWillReceiveProps(newProps) {}
+
+ fetchData = () => {
+ if (
+ this.state.clusterID === undefined ||
+ this.state.clusterID === "" ||
+ this.state.clusterID === null
+ ) {
+ return;
+ }
+
+ this.setState({
+ spinning: true,
+ });
+
+ fetchDataCount++;
+ const { dispatch } = this.props;
+ const { timeRange } = this.state;
+ const bounds = calculateBounds({
+ from: timeRange.min,
+ to: timeRange.max,
+ });
+ dispatch({
+ type: "clusterMonitor/fetchClusterMetrics",
+ payload: {
+ timeRange: {
+ min: bounds.min.valueOf(),
+ max: bounds.max.valueOf(),
+ },
+ cluster_id: this.state.clusterID,
+ },
+ }).then((res) => {
+ this.chartRefs = Object.keys(res?.metrics || {}).map(() => {
+ return React.createRef();
+ });
+ this.setState({
+ spinning: false,
+ });
+ });
+ };
+
+ componentDidUpdate(prevProps, prevState, snapshot) {
+ // console.log(this.props.selectedCluster)
+ // console.log(this.state.clusterID)
+
+ if (this.props.selectedCluster.id !== this.state.clusterID) {
+ console.log("cluster changed");
+
+ this.setState({ clusterID: this.props.selectedCluster.id }, () => {
+ //TODO 处理 cancel 事件,先把当前还在执行中的请求结束,避免更新完成之后,被晚到的之前的请求给覆盖了。
+ this.fetchData();
+ });
+ }
+ }
+
+ pointerUpdate = (event) => {
+ this.chartRefs.forEach((ref) => {
+ if (ref.current) {
+ ref.current.dispatchExternalPointerEvent(event);
+ }
+ });
+ };
+
+ componentDidMount() {
+ const { match, location } = this.props;
+
+ const queryESID = this.props.match.params.cluster_id;
+
+ if (queryESID !== null && queryESID !== undefined) {
+ this.state.clusterID = queryESID;
+ const { dispatch } = this.props;
+ dispatch({
+ type: "global/changeClusterById",
+ payload: {
+ id: queryESID,
+ },
+ });
+ } else if (
+ this.props.selectedCluster.id !== undefined &&
+ this.props.selectedCluster.id !== null
+ ) {
+ this.setState({ clusterID: this.props.selectedCluster.id }, () => {});
+ } else {
+ //alert("cluster ID is not set");
+ return;
+ }
+
+ let min = location.query.start || this.state.timeRange.min; //'2020-12-10 15:00';
+ let max = location.query.end || this.state.timeRange.max; //'2020-12-10 16:00';
+ this.setState(
+ {
+ timeRange: {
+ ...this.state.timeRange,
+ min: min,
+ max: max,
+ },
+ },
+ () => {
+ this.fetchData();
+ }
+ );
+
+ // tv1 = setInterval(()=>{
+ // this.fetchData();
+ // }, 10000);
+
+ //this.autoRefresh();
+ }
+
+ autoRefresh(durationInSeconds) {
+ !durationInSeconds && (durationInSeconds = 10);
+ clearInterval(tv1);
+ tv1 = setInterval(() => {
+ this.fetchData();
+ }, durationInSeconds * 1000);
+ }
+
+ handleChartBrush({ x }) {
+ if (!x) {
+ return;
+ }
+ const [from, to] = x;
+ let timeRange = {
+ min: moment(from).toISOString(),
+ max: moment(to).toISOString(),
};
-
- const onRefreshChange = ({ isPaused, refreshInterval }) => {
- setIsPaused(isPaused);
- setRefreshInterval(refreshInterval);
- };
-
- return (
-
+ timeRange.day = moment
+ .duration(moment(to).valueOf() - moment(from).valueOf())
+ .asDays();
+ this.setState(
+ {
+ timeRange: timeRange,
+ },
+ () => {
+ this.fetchData();
+ }
+ );
+ }
+
+ handleTimeChange = ({ start, end }) => {
+ const bounds = calculateBounds({
+ from: start,
+ to: end,
+ });
+ const day = moment
+ .duration(bounds.max.valueOf() - bounds.min.valueOf())
+ .asDays();
+ const intDay = parseInt(day) + 1;
+ this.setState(
+ {
+ timeRange: {
+ min: start,
+ max: end,
+ timeFormatter: formatter.dates(intDay),
+ },
+ },
+ () => {
+ this.fetchData();
+ }
);
};
-@connect(({clusterMonitor,global}) => ({
- clusterMonitor,
- selectedCluster: global.selectedCluster,
- clusterList: global.clusterList
-}))
+ render() {
+ const { clusterMonitor } = this.props;
+ let clusterStats = {};
+ let clusterMetrics = {};
-class ClusterMonitor extends PureComponent {
- constructor(props) {
- super(props);
- //this.timePicker = React.createRef();
- this.handleChartBrush = this.handleChartBrush.bind(this);
+ if (clusterMonitor.summary) {
+ let rawStats = clusterMonitor.summary;
+ clusterStats = {
+ cluster_name: rawStats.cluster_name,
+ status: rawStats.status,
+ version: rawStats.version,
+ nodes_count: rawStats.nodes_count,
+ indices_count: formatter.number(rawStats.indices_count),
+ primary_shards: formatter.number(rawStats.primary_shards),
+ unassigned_shards: formatter.number(rawStats.unassigned_shards),
+ total_shards: formatter.number(rawStats.total_shards),
+ used_store_bytes: formatter.bytes(rawStats.used_store_bytes),
+ max_store_bytes: formatter.bytes(rawStats.max_store_bytes),
+ used_jvm_bytes: formatter.bytes(rawStats.used_jvm_bytes),
+ max_jvm_bytes: formatter.bytes(rawStats.max_jvm_bytes),
+ document_count: formatter.number(rawStats.document_count),
+ uptime: moment.duration(rawStats.uptime).humanize(),
+ };
}
- state = {
- spinning: false,
- clusterID:null,
- timeRange: {
- min: 'now-1h', //moment().subtract(1, 'h').toISOString(),
- max: 'now'//moment().toISOString()
- },
+ if (clusterMonitor.metrics) {
+ clusterMetrics = clusterMonitor.metrics;
}
- componentWillReceiveProps(newProps) {
+ const commonlyUsedRanges = [
+ {
+ from: "now-15m",
+ to: "now",
+ display: "最近15分钟",
+ },
+ {
+ from: "now-30m",
+ to: "now",
+ display: "最近30分钟",
+ },
+ {
+ from: "now-1h",
+ to: "now",
+ display: "最近一小时",
+ },
+ {
+ from: "now-24h",
+ to: "now",
+ display: "最近一天",
+ },
+ {
+ from: "now/d",
+ to: "now/d",
+ display: "今天",
+ },
+ {
+ from: "now/w",
+ to: "now/w",
+ display: "这个星期",
+ },
+ {
+ from: "now-7d",
+ to: "now",
+ display: "最近一周",
+ },
+ {
+ from: "now-30d",
+ to: "now",
+ display: "最近一个月",
+ },
+ {
+ from: "now-90d",
+ to: "now",
+ display: "最近三个月",
+ },
+ {
+ from: "now-1y",
+ to: "now",
+ display: "最近一年",
+ },
+ ].map(({ from, to, display }) => {
+ return {
+ start: from,
+ end: to,
+ label: display,
+ };
+ });
- }
+ return (
+
+
+
+
+
- fetchData = () => {
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
- console.log("fetching data ing."+this.state.clusterID)
+
+
+
- if (this.state.clusterID===undefined||this.state.clusterID===""||this.state.clusterID===null){
- return
- }
+
+
+
- this.setState({
- spinning:true,
- })
+
+
+
- fetchDataCount++;
- const {dispatch} = this.props;
- const {timeRange} = this.state;
- const bounds = calculateBounds({
- from: timeRange.min,
- to: timeRange.max,
- });
- dispatch({
- type: 'clusterMonitor/fetchClusterMetrics',
- payload: {
- timeRange: {
- min: bounds.min.valueOf(),
- max: bounds.max.valueOf(),
- },
- cluster_id:this.state.clusterID
- },
- }).then(()=>{
- this.setState({
- spinning:false,
- })
- })
- }
+
+
+
+
+
+
+
+
+
+
+
+ {Object.keys(clusterMetrics).map((e, i) => {
+ let axis = clusterMetrics[e].axis;
+ let lines = clusterMetrics[e].lines;
+ let disableHeaderFormat = false;
+ let headerUnit = "";
+ return (
+
+
+
+ `${formatter.full_dates(value)}${
+ headerUnit ? ` ${headerUnit}` : ""
+ }`,
+ }}
+ debug={false}
+ />
+
+ {axis.map((item) => {
+ return (
+
+ );
+ })}
- componentWillUnmount() {
- clearInterval(tv1);
- }
-
- componentDidUpdate(prevProps, prevState, snapshot) {
-
- // console.log(this.props.selectedCluster)
- // console.log(this.state.clusterID)
-
- if (this.props.selectedCluster.id!==this.state.clusterID){
- console.log("cluster changed")
-
- this.setState({ clusterID:this.props.selectedCluster.id }, () => {
- //TODO 处理 cancel 事件,先把当前还在执行中的请求结束,避免更新完成之后,被晚到的之前的请求给覆盖了。
- this.fetchData();
- });
- }
- }
-
- componentDidMount() {
- const {match, location} = this.props;
-
- const queryESID=this.props.match.params.cluster_id;
-
- if (queryESID !== null&&queryESID !== undefined){
- this.state.clusterID=queryESID
- const {dispatch} = this.props;
- dispatch({
- type: 'global/changeClusterById',
- payload: {
- id: queryESID
- }
- })
- }else if (this.props.selectedCluster.id!==undefined&&this.props.selectedCluster.id!==null){
- this.setState({ clusterID:this.props.selectedCluster.id }, () => {
- });
- }else{
- //alert("cluster ID is not set");
- return
- }
-
- console.log("selectedCluster:"+this.state.clusterID)
-
- let min = location.query.start || this.state.timeRange.min;//'2020-12-10 15:00';
- let max = location.query.end || this.state.timeRange.max;//'2020-12-10 16:00';
- this.setState({
- timeRange: {
- min: min,
- max: max,
- },
- }, () => {
- this.fetchData();
- })
-
-
- // tv1 = setInterval(()=>{
- // this.fetchData();
- // }, 10000);
-
- //this.autoRefresh();
- }
-
- autoRefresh(durationInSeconds) {
- !durationInSeconds && (durationInSeconds = 10);
- clearInterval(tv1);
- tv1 = setInterval(() => {
- this.fetchData();
- }, durationInSeconds * 1000);
- }
-
-
- handleChartBrush({x}) {
- if (!x) {
- return;
- }
- const [from, to] = x;
- let timeRange = {
- min: moment(from).toISOString(),
- max: moment(to).toISOString(),
- }
- this.setState({
- timeRange: timeRange,
- }, () => {
- this.fetchData();
- });
- }
-
- handleTimeChange = ({start, end})=>{
- this.setState({
- timeRange: {
- min: start,
- max: end,
- }
- },()=>{
- this.fetchData();
- })
- }
-
-
- render() {
-
- const {clusterMonitor} = this.props;
- let clusterStats = {};
- let clusterMetrics = {};
-
- if (clusterMonitor.summary) {
- let rawStats = clusterMonitor.summary;
- clusterStats = {
- cluster_name: rawStats.cluster_name,
- status: rawStats.status,
- version: rawStats.version,
- nodes_count: rawStats.nodes_count,
- indices_count: formatter.number(rawStats.indices_count),
- primary_shards: formatter.number(rawStats.primary_shards),
- unassigned_shards: formatter.number(rawStats.unassigned_shards),
- total_shards: formatter.number(rawStats.total_shards),
- used_store_bytes: formatter.bytes(rawStats.used_store_bytes),
- max_store_bytes: formatter.bytes(rawStats.max_store_bytes),
- used_jvm_bytes: formatter.bytes(rawStats.used_jvm_bytes),
- max_jvm_bytes: formatter.bytes(rawStats.max_jvm_bytes),
- document_count: formatter.number(rawStats.document_count),
- uptime: moment.duration(rawStats.uptime).humanize(),
- };
- }
-
- if (clusterMonitor.metrics) {
- clusterMetrics = clusterMonitor.metrics;
- }
-
- const commonlyUsedRanges =
- [
- {
- from: 'now-15m',
- to: 'now',
- display: '最近15分钟'
- },
- {
- from: 'now-30m',
- to: 'now',
- display: '最近30分钟'
- },
- {
- from: 'now-1h',
- to: 'now',
- display: '最近一小时'
- },
- {
- from: 'now-24h',
- to: 'now',
- display: '最近一天',
- },
- {
- from: 'now/d',
- to: 'now/d',
- display:'今天',
- },
- {
- from: 'now/w',
- to: 'now/w',
- display: '这个星期'
- },
- {
- from: 'now-7d',
- to: 'now',
- display: '最近一周',
- },
- {
- from: 'now-30d',
- to: 'now',
- display: '最近一个月',
- },
- {
- from: 'now-90d',
- to: 'now',
- display: '最近三个月',
- },
- {
- from: 'now-1y',
- to: 'now',
- display: '最近一年',
- },
- ].map(({ from, to, display }) => {
- return {
- start: from,
- end: to,
- label: display,
- };
- });
-
- return (
-
-
-
-
+ {lines.map((item) => {
+ return (
+
+ );
+ })}
+
-
-
-
-
-
-
-
-
-
-
-
-
-
- }/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- Object.keys(clusterMetrics).map((e, i) => {
- let axis = clusterMetrics[e].axis
- let lines = clusterMetrics[e].lines
- let disableHeaderFormat = false
- let headerUnit = ""
- return (
-
-
- `${formatter.full_dates(value)}${headerUnit ? ` ${headerUnit}` : ''}`,
- }}
- debug={false}/>
-
- {
- axis.map((item) => {
- return
- })
- }
-
- {
- lines.map((item) => {
- return
- })
- }
-
-
-
- )
- })
- }
-
-
- );
- }
+ );
+ })}
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
}
export default ClusterMonitor;
diff --git a/web/src/pages/Cluster/Metrics.less b/web/src/pages/Cluster/Metrics.less
index d31ee7c0..4ca47bce 100644
--- a/web/src/pages/Cluster/Metrics.less
+++ b/web/src/pages/Cluster/Metrics.less
@@ -1,14 +1,14 @@
-
-.vizChartContainer{
+.vizChartContainer {
padding: 0px;
+ border-bottom: 1px solid rgb(232, 232, 232);
}
-.vizChartItem{
- background: white!important;
+.vizChartItem {
+ background: white !important;
margin-bottom: 5px;
}
-.vizChartTitle{
+.vizChartTitle {
color: red;
position: absolute;
font-size: 14px;
diff --git a/web/src/pages/Cluster/components/index_metric.jsx b/web/src/pages/Cluster/components/index_metric.jsx
new file mode 100644
index 00000000..c991c3d9
--- /dev/null
+++ b/web/src/pages/Cluster/components/index_metric.jsx
@@ -0,0 +1,225 @@
+import * as React from "react";
+import {
+ Axis,
+ Chart,
+ CurveType,
+ LineSeries,
+ niceTimeFormatByDay,
+ Position,
+ ScaleType,
+ Settings,
+ timeFormatter,
+} from "@elastic/charts";
+import useFetch from "@/lib/hooks/use_fetch";
+import { ESPrefix } from "@/services/common";
+import styles from "../Metrics.less";
+import { Spin, Radio, Select, Skeleton } from "antd";
+import { formatter, getFormatter, getNumFormatter } from "../format";
+import "./node_metric.scss";
+import { calculateBounds } from "@/components/kibana/data/common/query/timefilter";
+import moment from "moment";
+
+export default ({ clusterID, timezone, timeRange, handleTimeChange }) => {
+ const [filter, setFilter] = React.useState({
+ top: "5",
+ index_name: undefined,
+ });
+
+ const topChange = (e) => {
+ setFilter({
+ index_name: undefined,
+ top: e.target.value,
+ });
+ };
+
+ const indexValueChange = (value) => {
+ setFilter({
+ top: undefined,
+ index_name: value,
+ });
+ };
+ const queryParams = React.useMemo(() => {
+ const bounds = calculateBounds({
+ from: timeRange.min,
+ to: timeRange.max,
+ });
+ return {
+ ...filter,
+ min: bounds.min.valueOf(),
+ max: bounds.max.valueOf(),
+ };
+ }, [filter, timeRange]);
+ const { loading, error, value } = useFetch(
+ `${ESPrefix}/${clusterID}/index_metrics`,
+ {
+ queryParams: queryParams,
+ },
+ [clusterID, queryParams]
+ );
+
+ const metrics = React.useMemo(() => {
+ return Object.values(value?.metrics || {}).sort(
+ (a, b) => a.order - b.order
+ );
+ }, [value]);
+
+ const chartRefs = React.useRef();
+ React.useEffect(() => {
+ chartRefs.current = metrics.map(() => {
+ return React.createRef();
+ });
+ }, [metrics]);
+
+ const { value: indices } = useFetch(
+ `${ESPrefix}/${clusterID}/_cat/indices`,
+ {},
+ [clusterID]
+ );
+ const indexNames = React.useMemo(() => {
+ return Object.keys(indices || {});
+ }, [indices]);
+
+ const pointerUpdate = (event) => {
+ chartRefs.current.forEach((ref) => {
+ if (ref.current) {
+ ref.current.dispatchExternalPointerEvent(event);
+ }
+ });
+ };
+
+ const handleChartBrush = ({ x }) => {
+ if (!x) {
+ return;
+ }
+ const [from, to] = x;
+ if (typeof handleTimeChange == "function") {
+ handleTimeChange({
+ start: moment(from).toISOString(),
+ end: moment(to).toISOString(),
+ });
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ Top5
+ Top10
+ Top15
+ Top20
+
+
+
+
+
+
+
+
+
+
+ {Object.keys(metrics).map((e, i) => {
+ let axis = metrics[e].axis;
+ let lines = metrics[e].lines;
+ let disableHeaderFormat = false;
+ let headerUnit = "";
+ return (
+
+
+
+ `${formatter.full_dates(value)}${
+ headerUnit ? ` ${headerUnit}` : ""
+ }`,
+ }}
+ debug={false}
+ />
+
+ {axis.map((item) => {
+ return (
+
+ );
+ })}
+
+ {lines.map((item) => {
+ return (
+
+ );
+ })}
+
+
+ );
+ })}
+
+
+
+ );
+};
diff --git a/web/src/pages/Cluster/components/node_metric.jsx b/web/src/pages/Cluster/components/node_metric.jsx
new file mode 100644
index 00000000..e1c0b47b
--- /dev/null
+++ b/web/src/pages/Cluster/components/node_metric.jsx
@@ -0,0 +1,223 @@
+import * as React from "react";
+import {
+ Axis,
+ Chart,
+ CurveType,
+ LineSeries,
+ niceTimeFormatByDay,
+ Position,
+ ScaleType,
+ Settings,
+ timeFormatter,
+} from "@elastic/charts";
+import useFetch from "@/lib/hooks/use_fetch";
+import { ESPrefix } from "@/services/common";
+import styles from "../Metrics.less";
+import { Spin, Radio, Select, Skeleton } from "antd";
+import { formatter, getFormatter, getNumFormatter } from "../format";
+import "./node_metric.scss";
+import { calculateBounds } from "@/components/kibana/data/common/query/timefilter";
+import moment from "moment";
+
+export default ({ clusterID, timezone, timeRange, handleTimeChange }) => {
+ const [filter, setFilter] = React.useState({
+ top: "5",
+ node_name: undefined,
+ });
+
+ const topChange = (e) => {
+ setFilter({
+ node_name: undefined,
+ top: e.target.value,
+ });
+ };
+
+ const nodeValueChange = (value) => {
+ setFilter({
+ top: undefined,
+ node_name: value,
+ });
+ };
+ const queryParams = React.useMemo(() => {
+ const bounds = calculateBounds({
+ from: timeRange.min,
+ to: timeRange.max,
+ });
+ return {
+ ...filter,
+ min: bounds.min.valueOf(),
+ max: bounds.max.valueOf(),
+ };
+ }, [filter, timeRange]);
+ const { loading, error, value } = useFetch(
+ `${ESPrefix}/${clusterID}/node_metrics`,
+ {
+ queryParams: queryParams,
+ },
+ [clusterID, queryParams]
+ );
+
+ const metrics = React.useMemo(() => {
+ return Object.values(value?.metrics || {}).sort(
+ (a, b) => a.order - b.order
+ );
+ }, [value]);
+
+ const chartRefs = React.useRef();
+ React.useEffect(() => {
+ chartRefs.current = metrics.map(() => {
+ return React.createRef();
+ });
+ }, [metrics]);
+
+ const { value: nodes } = useFetch(`${ESPrefix}/${clusterID}/nodes`, {}, [
+ clusterID,
+ ]);
+ const nodeNames = React.useMemo(() => {
+ return Object.keys(nodes || {}).map((k) => nodes[k].name);
+ }, [nodes]);
+
+ const pointerUpdate = (event) => {
+ chartRefs.current.forEach((ref) => {
+ if (ref.current) {
+ ref.current.dispatchExternalPointerEvent(event);
+ }
+ });
+ };
+
+ const handleChartBrush = ({ x }) => {
+ if (!x) {
+ return;
+ }
+ const [from, to] = x;
+ if (typeof handleTimeChange == "function") {
+ handleTimeChange({
+ start: moment(from).toISOString(),
+ end: moment(to).toISOString(),
+ });
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ Top5
+ Top10
+ Top15
+ Top20
+
+
+
+
+
+
+
+
+
+
+ {Object.keys(metrics).map((e, i) => {
+ let axis = metrics[e].axis;
+ let lines = metrics[e].lines;
+ let disableHeaderFormat = false;
+ let headerUnit = "";
+ return (
+
+
+
+ `${formatter.full_dates(value)}${
+ headerUnit ? ` ${headerUnit}` : ""
+ }`,
+ }}
+ debug={false}
+ />
+
+ {axis.map((item) => {
+ return (
+
+ );
+ })}
+
+ {lines.map((item) => {
+ return (
+
+ );
+ })}
+
+
+ );
+ })}
+
+
+
+ );
+};
diff --git a/web/src/pages/Cluster/components/node_metric.scss b/web/src/pages/Cluster/components/node_metric.scss
new file mode 100644
index 00000000..2fd2008c
--- /dev/null
+++ b/web/src/pages/Cluster/components/node_metric.scss
@@ -0,0 +1,13 @@
+.metric-control {
+ display: flex;
+ .selector {
+ margin-left: auto;
+ display: flex;
+ .value-selector {
+ margin-left: 5px;
+ }
+ }
+}
+.px {
+ padding: 0 15px;
+}
diff --git a/web/src/pages/Cluster/format.js b/web/src/pages/Cluster/format.js
new file mode 100644
index 00000000..93f81b29
--- /dev/null
+++ b/web/src/pages/Cluster/format.js
@@ -0,0 +1,46 @@
+import numeral from "numeral";
+import { timeFormatter, niceTimeFormatByDay } from "@elastic/charts";
+import { DateTime } from "luxon";
+
+const unitArr = Array("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB");
+
+export const formatter = {
+ bytes: (value) => {
+ if (isNaN(value) || null == value || value === "" || value == 0) {
+ return "0B";
+ }
+ var index = 0;
+ var srcsize = parseFloat(value);
+ index = Math.floor(Math.log(srcsize) / Math.log(1024));
+ var size = srcsize / Math.pow(1024, index);
+ size = size.toFixed(1);
+ return size + unitArr[index];
+ },
+ dates: (day) => timeFormatter(niceTimeFormatByDay(day)),
+ full_dates: (d) => DateTime.fromMillis(d).toFormat("yyyy-MM-dd HH:mm:ss"),
+ utc_full_dates: (d) =>
+ DateTime.fromMillis(d)
+ .toUTC()
+ .toFormat("yyyy-MM-dd HH:mm:ss"),
+ ratio: (d) => `${Number(d).toFixed(0)}%`,
+ highPrecisionNumber: (d) => numeral(d).format("0.0000"),
+ lowPrecisionNumber: (d) => numeral(d).format("0.0"),
+ number: (d) => numeral(d).format("0,0"),
+};
+
+export function getFormatter(type, format, units) {
+ switch (type) {
+ case "bytes":
+ return formatter.bytes;
+ case "ratio":
+ return formatter.ratio;
+ case "num":
+ return getNumFormatter(format, units);
+ default:
+ return formatter.lowPrecisionNumber;
+ }
+}
+
+export function getNumFormatter(format, units) {
+ return (d) => numeral(d).format(format) + (units ? ` ${units}` : "");
+}
diff --git a/web/src/pages/Cluster/models/cluster.js b/web/src/pages/Cluster/models/cluster.js
index 9f0399c9..36a0d678 100644
--- a/web/src/pages/Cluster/models/cluster.js
+++ b/web/src/pages/Cluster/models/cluster.js
@@ -1,50 +1,56 @@
-import {getClusterOverview, getClusterNodeStats, getClusterList} from "@/services/dashboard";
-import {getClusterMetrics} from "@/services/cluster";
+import {
+ getClusterOverview,
+ getClusterNodeStats,
+ getClusterList,
+} from "@/services/dashboard";
+import { getClusterMetrics } from "@/services/cluster";
export default {
- namespace: 'clusterMonitor',
- state: {
-
+ namespace: "clusterMonitor",
+ state: {},
+ effects: {
+ *fetchClusterOverview({ payload, callback }, { call, put }) {
+ let clusterData = yield call(getClusterOverview, payload);
+ yield put({ type: "saveData", payload: clusterData });
+ if (callback && typeof callback == "function") {
+ callback(clusterData);
+ }
},
- effects:{
- *fetchClusterOverview({payload, callback}, {call, put}){
- let clusterData = yield call(getClusterOverview, payload);
- yield put({type: 'saveData', payload: clusterData})
- if(callback && typeof callback == 'function'){
- callback(clusterData);
- }
- },
- *fetchClusterMetrics({payload, callback}, {call, put}){
- let clusterMetrics = yield call(getClusterMetrics, payload);
- yield put({type: 'saveData', payload: clusterMetrics})
- if(callback && typeof callback == 'function'){
- callback(clusterMetrics);
- console.log("finished call:"+params.cluster_id);
- }
- },
- *fetchClusterNodeStats({callback}, {call, put}){
- let nodesStats = yield call(getClusterNodeStats);
- //yield put({type: 'saveData', payload: nodesStats})
- if(callback && typeof callback == 'function'){
- callback(nodesStats);
- }
- },
- *fetchClusterList({callback}, {call, put}){
- let clusterData = yield call(getClusterList);
- yield put({type: 'saveData', payload: {
- clusterList: clusterData
- }})
- if(callback && typeof callback == 'function'){
- callback(clusterData);
- }
- }
+ *fetchClusterMetrics({ payload, callback }, { call, put }) {
+ let clusterMetrics = yield call(getClusterMetrics, payload);
+ yield put({ type: "saveData", payload: clusterMetrics });
+ if (callback && typeof callback == "function") {
+ callback(clusterMetrics);
+ console.log("finished call:" + params.cluster_id);
+ }
+ return clusterMetrics;
},
- reducers:{
- saveData(state, {payload}){
- return {
- ...state,
- ...payload
- };
+ *fetchClusterNodeStats({ callback }, { call, put }) {
+ let nodesStats = yield call(getClusterNodeStats);
+ //yield put({type: 'saveData', payload: nodesStats})
+ if (callback && typeof callback == "function") {
+ callback(nodesStats);
+ }
+ },
+ *fetchClusterList({ callback }, { call, put }) {
+ let clusterData = yield call(getClusterList);
+ yield put({
+ type: "saveData",
+ payload: {
+ clusterList: clusterData,
},
- }
+ });
+ if (callback && typeof callback == "function") {
+ callback(clusterData);
+ }
+ },
+ },
+ reducers: {
+ saveData(state, { payload }) {
+ return {
+ ...state,
+ ...payload,
+ };
+ },
+ },
};
diff --git a/web/src/pages/DataManagement/Discover.jsx b/web/src/pages/DataManagement/Discover.jsx
index 32ea97a3..c3668959 100644
--- a/web/src/pages/DataManagement/Discover.jsx
+++ b/web/src/pages/DataManagement/Discover.jsx
@@ -392,6 +392,9 @@ const Discover = (props) => {
},
[props.selectedCluster, updateQuery]
);
+ const document = useMemo(() => {
+ saveDocument, deleteDocument;
+ }, [saveDocument, deleteDocument]);
const [settingsVisible, setSettingsVisible] = React.useState(false);
@@ -627,7 +630,7 @@ const Discover = (props) => {
onMoveColumn={onMoveColumn}
onAddColumn={onAddColumn}
onChangeSortOrder={onSort}
- document={{ saveDocument, deleteDocument }}
+ document={document}
hits={rows}
/>
diff --git a/web/src/pages/DevTool/Console.tsx b/web/src/pages/DevTool/Console.tsx
index 72b423c2..b5c12a11 100644
--- a/web/src/pages/DevTool/Console.tsx
+++ b/web/src/pages/DevTool/Console.tsx
@@ -10,7 +10,6 @@ import {
useEffect,
useMemo,
useRef,
- useLayoutEffect,
} from "react";
import { useLocalStorage } from "@/lib/hooks/storage";
import { setClusterID } from "../../components/kibana/console/modules/mappings/mappings";
@@ -195,12 +194,16 @@ export const ConsoleUI = ({
const [tabState, dispatch] = useReducer(consoleTabReducer, localState);
useEffect(() => {
- if (tabState.panes.length == 0) {
+ const panes = tabState.panes.filter((pane: any) => {
+ return typeof clusterMap[pane.cluster_id] != "undefined";
+ });
+
+ if (panes.length == 0) {
removeLocalState();
return;
}
setLocalState(tabState);
- }, [tabState]);
+ }, [tabState, clusterMap]);
const saveEditorContent = useCallback(
(content) => {
diff --git a/web/src/pages/DevTool/NewTabMenu.jsx b/web/src/pages/DevTool/NewTabMenu.jsx
index b017a8dc..ba0f15a4 100644
--- a/web/src/pages/DevTool/NewTabMenu.jsx
+++ b/web/src/pages/DevTool/NewTabMenu.jsx
@@ -1,21 +1,21 @@
-import { Button, Dropdown, List, Spin, message, Icon, Input } from 'antd';
-import * as React from 'react';
-import InfiniteScroll from 'react-infinite-scroller';
-import styles from '@/components/GlobalHeader/DropdownSelect.less';
+import { Button, Dropdown, List, Spin, message, Icon, Input } from "antd";
+import * as React from "react";
+import InfiniteScroll from "react-infinite-scroller";
+import styles from "@/components/GlobalHeader/DropdownSelect.less";
import _ from "lodash";
-import {DropdownItem} from '@/components/GlobalHeader/DropdownItem';
-import {HealthStatusCircle} from '@/components/infini/health_status_circle'
+import { DropdownItem } from "@/components/GlobalHeader/DropdownItem";
+import { HealthStatusCircle } from "@/components/infini/health_status_circle";
-class NewTabMenu extends React.Component{
- handleItemClick = (item)=>{
+class NewTabMenu extends React.Component {
+ handleItemClick = (item) => {
const onItemClick = this.props.onItemClick;
- if(onItemClick && typeof onItemClick == 'function'){
- onItemClick(item)
+ if (onItemClick && typeof onItemClick == "function") {
+ onItemClick(item);
}
- }
- constructor(props){
+ };
+ constructor(props) {
super(props);
- this.state={
+ this.state = {
loading: false,
hasMore: (props.data || []).length > props.size,
data: (props.data || []).slice(0, props.size || 10),
@@ -24,34 +24,41 @@ class NewTabMenu extends React.Component{
dataSourceKey: 1,
selectedIndex: -1,
overlayVisible: false,
+ };
+ }
+
+ componentDidMount() {}
+ componentDidUpdate(preProps, preState) {
+ if (preProps.data.length != preState.dataSource.length) {
+ const newData = [...preProps.data];
+ this.setState({
+ dataSource: newData,
+ data: newData,
+ hasMore: newData.length > this.props.size,
+ selectedIndex: -1,
+ });
}
}
-
- componentDidMount(){
-
- }
-
handleInfiniteOnLoad = (current) => {
- let {size } = this.props;
+ let { size } = this.props;
let targetLength = current * size;
- let {hasMore, dataSource} = this.state;
- if(dataSource.length < targetLength){
+ let { hasMore, dataSource } = this.state;
+ if (dataSource.length < targetLength) {
targetLength = dataSource.length;
- hasMore = false
+ hasMore = false;
}
const newData = this.state.dataSource.slice(0, targetLength);
this.setState({
data: newData,
hasMore: hasMore,
- })
-
- }
+ });
+ };
- handleInputChange = (e) =>{
+ handleInputChange = (e) => {
const name = e.target.value;
- const newData = this.props.data.filter(item=>{
+ const newData = this.props.data.filter((item) => {
return item.name.includes(name);
});
this.setState({
@@ -60,97 +67,129 @@ class NewTabMenu extends React.Component{
data: newData,
hasMore: newData.length > this.props.size,
selectedIndex: -1,
- })
-
- }
+ });
+ };
- selectOffset = (offset)=> {
- let {selectedIndex, data} = this.state;
+ selectOffset = (offset) => {
+ let { selectedIndex, data } = this.state;
const len = data.length;
selectedIndex = (selectedIndex + offset + len) % len;
- // const item = data[selectedIndex];
- this.setState({
- selectedIndex,
- })
- }
+ // const item = data[selectedIndex];
+ this.setState({
+ selectedIndex,
+ });
+ };
onKeyDown = (e) => {
const { which } = e;
// e.preventDefault();
-
+
switch (which) {
case 38:
this.selectOffset(-1);
e.preventDefault();
- e.stopPropagation()
+ e.stopPropagation();
break;
case 40:
this.selectOffset(1);
- e.stopPropagation()
+ e.stopPropagation();
break;
case 13:
- const {data, selectedIndex} = this.state;
- if(selectedIndex > -1){
+ const { data, selectedIndex } = this.state;
+ if (selectedIndex > -1) {
this.handleItemClick(data[selectedIndex]);
- this.setState({ overlayVisible: false })
+ this.setState({ overlayVisible: false });
}
break;
}
- }
+ };
-
- render(){
- const {clusterStatus} = this.props;
- const menu = (
-
{this.searchInputRef.focus()}}
- tabIndex="0" onKeyDown={this.onKeyDown}>
-
- {this.searchInputRef= ref;}}
- onChange={this.handleInputChange} placeholder="输入集群名称查找" value={this.state.displayValue||''} />
-
-
+ {
+ this.searchInputRef.focus();
+ }}
+ tabIndex="0"
+ onKeyDown={this.onKeyDown}
>
-
- {(!this.state.data || !this.state.data.length)&&
匹配不到集群(匹配规则为前缀匹配)
}
- {(this.state.data || []).map((item, idx)=>{
- const cstatus = clusterStatus ? clusterStatus[item.id] : null;
- return
{
- this.handleItemClick(item)
+
+ {
+ this.searchInputRef = ref;
}}
- />
- })}
+ onChange={this.handleInputChange}
+ placeholder="输入集群名称查找"
+ value={this.state.displayValue || ""}
+ />
+
+
+
+ {(!this.state.data || !this.state.data.length) && (
+
+ 匹配不到集群(匹配规则为前缀匹配)
+
+ )}
+ {(this.state.data || []).map((item, idx) => {
+ const cstatus = clusterStatus ? clusterStatus[item.id] : null;
+ return (
+
{
+ this.handleItemClick(item);
+ }}
+ />
+ );
+ })}
+
+
-
+ {!this.state.loading && this.state.hasMore && (
+
+ pull load more
+
+ )}
- {!this.state.loading && this.state.hasMore && (
-
- pull load more
-
- )}
- );
+ );
return (
- {
- this.setState({ overlayVisible: flag });
- }}>
- {this.props.children}
-
-
- )
+
{
+ this.setState({ overlayVisible: flag });
+ }}
+ >
+ {this.props.children}
+
+
+ );
}
-
}
-export default NewTabMenu;
\ No newline at end of file
+export default NewTabMenu;
diff --git a/web/src/pages/System/Cluster/models/cluster.js b/web/src/pages/System/Cluster/models/cluster.js
index f5b170f1..e08af381 100644
--- a/web/src/pages/System/Cluster/models/cluster.js
+++ b/web/src/pages/System/Cluster/models/cluster.js
@@ -1,166 +1,171 @@
-import {createClusterConfig, searchClusterConfig, updateClusterConfig,deleteClusterConfig,
- tryConnect} from "@/services/cluster";
-import {message} from "antd";
-import {formatESSearchResult} from '@/lib/elasticsearch/util';
+import {
+ createClusterConfig,
+ searchClusterConfig,
+ updateClusterConfig,
+ deleteClusterConfig,
+ tryConnect,
+} from "@/services/cluster";
+import { message } from "antd";
+import { formatESSearchResult } from "@/lib/elasticsearch/util";
export default {
- namespace: 'clusterConfig',
+ namespace: "clusterConfig",
state: {
- editMode: '',
+ editMode: "",
editValue: {},
},
- effects:{
- *fetchClusterList({payload}, {call, put, select}){
+ effects: {
+ *fetchClusterList({ payload }, { call, put, select }) {
let res = yield call(searchClusterConfig, payload);
- if(res.error){
- message.error(res.error)
+ if (res.error) {
+ message.error(res.error);
return false;
}
- res = formatESSearchResult(res)
- const {clusterStatus} = yield select(state => state.clusterConfig);
+ res = formatESSearchResult(res);
+ const { clusterStatus } = yield select((state) => state.clusterConfig);
// for(let item of res.data){
// item.status= clusterStatus[item.id]
// }
yield put({
- type: 'saveData',
- payload: res
- })
+ type: "saveData",
+ payload: res,
+ });
},
- *addCluster({payload}, {call, put, select}) {
- let res = yield call(createClusterConfig, payload)
- if(res.error){
- message.error(res.error)
+ *addCluster({ payload }, { call, put, select }) {
+ let res = yield call(createClusterConfig, payload);
+ if (res.error) {
+ message.error(res.error);
return false;
}
- let {data, total} = yield select(state => state.clusterConfig);
- if(!data){
- return
+ let { data, total } = yield select((state) => state.clusterConfig);
+ if (!data) {
+ return;
}
data.unshift({
...res._source,
id: res._id,
});
yield put({
- type: 'saveData',
+ type: "saveData",
payload: {
data,
total: {
...total,
- value: total.value + 1
+ value: total.value + 1,
},
- }
- })
+ },
+ });
yield put({
- type: 'global/addCluster',
+ type: "global/addCluster",
payload: {
+ ...res._source,
id: res._id,
- name: res._source.name,
- }
- })
+ },
+ });
return res;
},
- *updateCluster({payload}, {call, put, select}) {
- let res = yield call(updateClusterConfig, payload)
- if(res.error){
- message.error(res.error)
+ *updateCluster({ payload }, { call, put, select }) {
+ let res = yield call(updateClusterConfig, payload);
+ if (res.error) {
+ message.error(res.error);
return false;
}
- let {data} = yield select(state => state.clusterConfig);
- let idx = data.findIndex((item)=>{
+ let { data } = yield select((state) => state.clusterConfig);
+ let idx = data.findIndex((item) => {
return item.id === res._id;
});
let originalEnabled = data[idx].enabled;
data[idx] = {
...data[idx],
- ...res._source
+ ...res._source,
};
yield put({
- type: 'saveData',
+ type: "saveData",
payload: {
- data
- }
- })
+ data,
+ },
+ });
//handle global cluster logic
- if(originalEnabled !== res._source.enabled){
- if(res._source.enabled === true) {
+ if (originalEnabled !== res._source.enabled) {
+ if (res._source.enabled === true) {
yield put({
- type: 'global/addCluster',
+ type: "global/addCluster",
payload: {
id: res._id,
name: res._source.name,
- }
- })
- }else{
+ },
+ });
+ } else {
yield put({
- type: 'global/removeCluster',
+ type: "global/removeCluster",
payload: {
id: res._id,
- }
- })
+ },
+ });
}
- }else{
+ } else {
yield put({
- type: 'global/updateCluster',
+ type: "global/updateCluster",
payload: {
id: res._id,
name: res._source.name,
- }
- })
+ },
+ });
}
return res;
},
- *deleteCluster({payload}, {call, put, select}) {
- let res = yield call(deleteClusterConfig, payload)
- if(res.error){
- message.error(res.error)
+ *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)=>{
+ let { data, total } = yield select((state) => state.clusterConfig);
+ data = data.filter((item) => {
return item.id !== payload.id;
- })
+ });
yield put({
- type: 'saveData',
+ type: "saveData",
payload: {
data,
total: {
...total,
- value: total.value + 1
- }
- }
- })
+ value: total.value + 1,
+ },
+ },
+ });
yield put({
- type: 'global/removeCluster',
+ type: "global/removeCluster",
payload: {
- id: payload.id
- }
- })
+ id: payload.id,
+ },
+ });
return res;
},
- *doTryConnect({payload}, {call, put, select}) {
- let res = yield call(tryConnect, payload)
- if(res.error){
- message.error(res.error)
+ *doTryConnect({ payload }, { call, put, select }) {
+ let res = yield call(tryConnect, payload);
+ if (res.error) {
+ message.error(res.error);
return false;
}
yield put({
- type: 'saveData',
+ type: "saveData",
payload: {
tempClusterInfo: res,
- }
- })
+ },
+ });
return res;
- }
+ },
},
- reducers:{
- saveData(state, {payload}){
+ reducers: {
+ saveData(state, { payload }) {
return {
...state,
...payload,
- }
- }
- }
-}
+ };
+ },
+ },
+};
diff --git a/web/src/pages/System/Command/Index.js b/web/src/pages/System/Command/Index.js
index 2d3567b9..7daead01 100644
--- a/web/src/pages/System/Command/Index.js
+++ b/web/src/pages/System/Command/Index.js
@@ -1,6 +1,6 @@
-import React, { PureComponent, Fragment } from 'react';
-import { connect } from 'dva';
-import {Link} from 'umi';
+import React, { PureComponent, Fragment } from "react";
+import { connect } from "dva";
+import { Link } from "umi";
import {
Row,
Col,
@@ -17,105 +17,31 @@ import {
Menu,
Table,
Dropdown,
- Icon, Popconfirm,
+ Icon,
+ Popconfirm,
Switch,
-} from 'antd';
-import Editor from '@monaco-editor/react';
+} from "antd";
+// import { loader } from "@monaco-editor/react";
+import Editor from "@monaco-editor/react";
+import { EuiCodeBlock } from "@elastic/eui";
+// loader.config({
+// paths: {
+// vs: "monaco-editor/min/vs",
+// },
+// });
-import styles from '../../List/TableList.less';
-import {transformSettingsForApi} from '@/lib/elasticsearch/edit_settings';
-import PageHeaderWrapper from '@/components/PageHeaderWrapper';
+import styles from "../../List/TableList.less";
+import { transformSettingsForApi } from "@/lib/elasticsearch/edit_settings";
+import PageHeaderWrapper from "@/components/PageHeaderWrapper";
+import { TagGenerator } from "@/components/kibana/console/components/CommonCommandModal";
const FormItem = Form.Item;
const { TextArea } = Input;
-const {TabPane} = Tabs;
-
-class JSONWrapper extends PureComponent {
- state ={
- height: 400,
- }
- componentDidMount(){
-
- let getElementTop = (elem)=>{
- var elemTop=elem.offsetTop;
- elem=elem.offsetParent;
-
- while(elem!=null){
- elemTop+=elem.offsetTop;
- elem=elem.offsetParent;
- }
-
- return elemTop;
-
- }
- // console.log(getElementTop(this.refs.jsonw));
- this.setState({height: window.innerHeight - getElementTop(this.refs.jsonw) -50});
- }
- render(){
- return (
- {console.log(document.getElementById('jsonw').offsetTop)}} style={{overflow:"scroll", height: this.state.height}}> {this.props.children}
- )
- }
-}
-@Form.create()
-class CreateForm extends React.Component {
- okHandle = () => {
- const {handleAdd, form} = this.props;
- const me = this;
-
- form.validateFields((err, fieldsValue) => {
- if (err) return;
- fieldsValue['config'] = me.editor.getValue();
- handleAdd(fieldsValue);
- form.resetFields();
- });
- };
- onEditorDidMount = (editor)=>{
- this.editor = editor;
- }
-
- render() {
- const {modalVisible, form, handleModalVisible} = this.props;
- return (
- handleModalVisible()}
- >
-
- {form.getFieldDecorator('index', {
- rules: [{required: true, message: '请输入至少五个字符的名称!', min: 5}],
- })()}
-
-
-
-
-
-
-
- );
- }
-}
-
+const { TabPane } = Tabs;
/* eslint react/no-multi-comp:0 */
@connect(({ command }) => ({
- command
+ command,
}))
@Form.create()
class Index extends PureComponent {
@@ -125,35 +51,42 @@ class Index extends PureComponent {
expandForm: false,
formValues: {},
drawerVisible: false,
- editingCommand:{},
- indexActiveKey: '1',
+ editingCommand: {},
+ indexActiveKey: "1",
showSystemIndices: false,
};
columns = [
{
- title: '名称',
- dataIndex: 'title',
+ title: "名称",
+ dataIndex: "title",
render: (text, record) => (
- {
- this.setState({
- editingCommand: record,
- drawerVisible: true,
- });
- }}>{text}
- )
+ {
+ this.setState({
+ editingCommand: record,
+ drawerVisible: true,
+ });
+ }}
+ >
+ {text}
+
+ ),
},
{
- title: '标签',
- dataIndex: 'tag',
- // render: (val)=>{
- // return val || 0;
- // }
+ title: "标签",
+ dataIndex: "tag",
+ render: (val) => {
+ return (val || []).join(",");
+ },
},
{
- title: '操作',
+ title: "操作",
render: (text, record) => (
- this.handleDeleteClick(record.id)}>
+ this.handleDeleteClick(record.id)}
+ >
删除
@@ -162,18 +95,18 @@ class Index extends PureComponent {
];
componentDidMount() {
- this.fetchData()
+ this.fetchData();
}
- fetchData = (params={})=>{
+ fetchData = (params = {}) => {
const { dispatch } = this.props;
dispatch({
- type: 'command/fetchCommandList',
+ type: "command/fetchCommandList",
payload: {
- ...params
- }
+ ...params,
+ },
});
- }
+ };
handleFormReset = () => {
const { form, dispatch } = this.props;
@@ -186,65 +119,109 @@ class Index extends PureComponent {
handleDeleteClick = (id) => {
const { dispatch } = this.props;
dispatch({
- type: 'command/removeCommand',
+ type: "command/removeCommand",
payload: {
id: id,
+ },
+ }).then((res) => {
+ if (!res.error) {
+ message.success("删除成功!");
}
});
};
- handleSearch = e => {
+ handleSearch = (e) => {
e.preventDefault();
const { dispatch, form } = this.props;
form.validateFields((err, fieldsValue) => {
if (err) return;
this.fetchData({
- title: fieldsValue.name,
+ keyword: fieldsValue.keyword,
from: 0,
size: 10,
- })
+ });
this.setState({
- searchKey: fieldsValue.name,
+ searchKey: fieldsValue.keyword,
});
});
};
- handleModalVisible = flag => {
+ handleModalVisible = (flag) => {
this.setState({
modalVisible: !!flag,
});
};
- handleIndexTabChanged = (activeKey, indexName) => {
- }
- handleEditorDidMount = (editorName, editor)=>{
+ handleIndexTabChanged = (activeKey, indexName) => {};
+ handleEditorDidMount = (editorName, editor) => {
this[editorName] = editor;
- }
+ };
- handleIndexSettingsSaveClick = (indexName)=>{
- }
- buildRawCommonCommandRequest(cmd){
- const {requests} = cmd;
- if(!requests){
- return '';
+ buildRawCommonCommandRequest(cmd) {
+ const { requests } = cmd;
+ if (!requests) {
+ return "";
}
- const strReqs = requests.map((req)=>{
- const {method, path, body} = req;
+ const strReqs = requests.map((req) => {
+ const { method, path, body } = req;
return `${method} ${path}\n${body}`;
- })
- return strReqs.join('\n');
+ });
+ return strReqs.join("\n");
}
- handleRereshClick=()=>{
- const {searchKey} = this.state;
+ handleRereshClick = () => {
+ const { searchKey } = this.state;
this.fetchData({
title: searchKey,
- })
- }
+ });
+ };
+
+ handleSaveClick = () => {
+ // console.log(this.state.editingCommand);
+ const { dispatch } = this.props;
+ dispatch({
+ type: "command/updateCommand",
+ payload: {
+ ...this.state.editingCommand,
+ },
+ }).then((res) => {
+ if (!res.error) {
+ this.setState({
+ drawerVisible: false,
+ editingCommand: {},
+ });
+ message.success("保存成功!");
+ this.fetchData();
+ }
+ });
+ };
+
+ onEditTitleChange = (e) => {
+ this.setState({
+ editingCommand: {
+ ...this.state.editingCommand,
+ title: e.target.value,
+ },
+ });
+ };
+ onEditTagChange = (val) => {
+ this.setState({
+ editingCommand: {
+ ...this.state.editingCommand,
+ tag: val,
+ },
+ });
+ };
render() {
- const {data, total} = this.props.command;
- const { modalVisible, updateModalVisible, updateFormValues, drawerVisible, editingCommand } = this.state;
+ const { data, total } = this.props.command;
+ const {
+ modalVisible,
+ updateModalVisible,
+ updateFormValues,
+ drawerVisible,
+ editingCommand,
+ } = this.state;
const parentMethods = {
handleAdd: this.handleAdd,
handleModalVisible: this.handleModalVisible,
@@ -253,8 +230,10 @@ class Index extends PureComponent {
handleUpdateModalVisible: this.handleUpdateModalVisible,
handleUpdate: this.handleUpdate,
};
- const {form: { getFieldDecorator }} = this.props;
-
+ const {
+ form: { getFieldDecorator },
+ } = this.props;
+
return (
@@ -263,8 +242,10 @@ class Index extends PureComponent {
-
-
-
- {
+ onClose={() => {
this.setState({
drawerVisible: false,
- indexActiveKey: '1',
+ indexActiveKey: "1",
});
}}
width={720}
>
-
-
this.handleEditorDidMount('indexSettingsEditor', editor)}
- />
+
+
+ 内容:
+
+
+ {this.buildRawCommonCommandRequest(editingCommand)}
+
+ {/*
+ this.handleEditorDidMount("commandEditor", editor)
+ }
+ /> */}
+
+
+
+ 保存
+
diff --git a/web/src/pages/System/Command/models/command.js b/web/src/pages/System/Command/models/command.js
index 0c37d4e9..594a3222 100644
--- a/web/src/pages/System/Command/models/command.js
+++ b/web/src/pages/System/Command/models/command.js
@@ -1,54 +1,63 @@
-import {searchCommand, deleteCommand} from "@/services/command";
-import {message} from "antd";
-import {formatESSearchResult} from '@/lib/elasticsearch/util';
+import { searchCommand, deleteCommand, saveCommand } from "@/services/command";
+import { message } from "antd";
+import { formatESSearchResult } from "@/lib/elasticsearch/util";
export default {
- namespace: 'command',
- state: {
- },
- effects:{
- *fetchCommandList({payload}, {call, put, select}){
- let res = yield call(searchCommand, payload);
- if(res.error){
- message.error(res.error)
- return false;
- }
- res = formatESSearchResult(res)
- yield put({
- type: 'saveData',
- payload: res
- })
- },
+ namespace: "command",
+ state: {},
+ effects: {
+ *fetchCommandList({ payload }, { call, put, select }) {
+ let res = yield call(searchCommand, payload);
+ if (res.error) {
+ message.error(res.error);
+ return false;
+ }
+ res = formatESSearchResult(res);
+ yield put({
+ type: "saveData",
+ payload: res,
+ });
+ },
- *removeCommand({payload}, {call, put, select}) {
- let res = yield call(deleteCommand, payload)
- if(res.error){
- message.error(res.error)
- return false;
- }
- let {data, total} = yield select(state => state.command);
- data = data.filter((item)=>{
- return item.id !== payload.id;
- })
- yield put({
- type: 'saveData',
- payload: {
- data,
- total: {
- ...total,
- value: total.value - 1
- }
- }
- })
- return res;
- },
- },
- reducers:{
- saveData(state, {payload}){
- return {
- ...state,
- ...payload,
- }
- }
- }
-}
+ *removeCommand({ payload }, { call, put, select }) {
+ let res = yield call(deleteCommand, payload);
+ if (res.error) {
+ message.error(res.error);
+ return false;
+ }
+ let { data, total } = yield select((state) => state.command);
+ data = data.filter((item) => {
+ return item.id !== payload.id;
+ });
+ yield put({
+ type: "saveData",
+ payload: {
+ data,
+ total: {
+ ...total,
+ value: total.value - 1,
+ },
+ },
+ });
+ return res;
+ },
+
+ *updateCommand({ payload }, { call, put, select }) {
+ let res = yield call(saveCommand, payload);
+ if (res.error) {
+ message.error(res.error);
+ return false;
+ }
+ // let {data, total} = yield select(state => state.command);
+ return res;
+ },
+ },
+ reducers: {
+ saveData(state, { payload }) {
+ return {
+ ...state,
+ ...payload,
+ };
+ },
+ },
+};
diff --git a/web/src/services/command.js b/web/src/services/command.js
index 54420d2b..5d3f16a5 100644
--- a/web/src/services/command.js
+++ b/web/src/services/command.js
@@ -1,24 +1,32 @@
-import request from '@/utils/request';
-import {buildQueryArgs, pathPrefix} from './common';
+import request from "@/utils/request";
+import { buildQueryArgs, pathPrefix } from "./common";
export async function searchCommand(params) {
- let url = `${pathPrefix}/elasticsearch/command`;
- let args = buildQueryArgs({
- title: params.title,
- from: params.from,
- size: params.size,
- });
- if(args.length > 0){
- url += args;
- }
- return request(url, {
- method: 'GET'
- });
+ let url = `${pathPrefix}/elasticsearch/command`;
+ let args = buildQueryArgs({
+ keyword: params.keyword,
+ from: params.from,
+ size: params.size,
+ });
+ if (args.length > 0) {
+ url += args;
+ }
+ return request(url, {
+ method: "GET",
+ });
}
export async function deleteCommand(params) {
- const url = `${pathPrefix}/elasticsearch/command/${params.id}`;
- return request(url, {
- method: 'DELETE'
- });
-}
\ No newline at end of file
+ const url = `${pathPrefix}/elasticsearch/command/${params.id}`;
+ return request(url, {
+ method: "DELETE",
+ });
+}
+
+export async function saveCommand(params) {
+ const url = `${pathPrefix}/elasticsearch/command/${params.id}`;
+ return request(url, {
+ method: "PUT",
+ body: params,
+ });
+}