cluster and command pagination and new metrics

This commit is contained in:
liugq 2021-11-26 16:23:45 +08:00
parent 2af1c743d4
commit 94dfeb9217
18 changed files with 544 additions and 346 deletions

View File

@ -1,9 +1,7 @@
package api package api
import ( import (
log "github.com/cihub/seelog"
"infini.sh/framework/core/api" "infini.sh/framework/core/api"
"infini.sh/framework/core/task"
"infini.sh/search-center/api/index_management" "infini.sh/search-center/api/index_management"
"infini.sh/search-center/config" "infini.sh/search-center/config"
"infini.sh/search-center/service/alerting" "infini.sh/search-center/service/alerting"
@ -79,14 +77,14 @@ func Init(cfg *config.AppConfig) {
api.HandleAPIMethod(api.POST, "/elasticsearch/:id/alerting/_monitors/:monitorID/_acknowledge/alerts", alerting.AcknowledgeAlerts) api.HandleAPIMethod(api.POST, "/elasticsearch/:id/alerting/_monitors/:monitorID/_acknowledge/alerts", alerting.AcknowledgeAlerts)
task.RegisterScheduleTask(task.ScheduleTask{ //task.RegisterScheduleTask(task.ScheduleTask{
Description: "sync reindex task result", // Description: "sync reindex task result",
Task: func() { // Task: func() {
err := index_management.SyncRebuildResult(cfg.Elasticsearch) // err := index_management.SyncRebuildResult(cfg.Elasticsearch)
if err != nil { // if err != nil {
log.Error(err) // log.Error(err)
} // }
}, // },
}) //})
} }

View File

@ -3,10 +3,14 @@ elasticsearch:
- name: default - name: default
enabled: true enabled: true
monitored: false monitored: false
endpoint: http://192.168.3.188:9299 # endpoint: https://k8es.client.bindiego.com
# basic_auth:
# username: infini
# password: BTxSrT5m33rfpF4
endpoint: http://localhost:9200
basic_auth: basic_auth:
username: elastic username: elastic
password: ZBdkVQUUdF1Sir4X4BGB password: infinilabs
#前端 UI HTTP 配置 #前端 UI HTTP 配置
web: web:

View File

@ -1,21 +1,35 @@
// @ts-ignore // @ts-ignore
import React, { useRef, useMemo,useEffect, useLayoutEffect, useState } from 'react'; import React, {
import ConsoleInput from './ConsoleInput'; useRef,
import ConsoleOutput from './ConsoleOutput'; useMemo,
import { Panel } from './Panel'; useEffect,
import PanelsContainer from './PanelContainer'; useLayoutEffect,
import { PanelContextProvider } from '../contexts/panel_context'; useState,
import { PanelRegistry } from '../contexts/panel_context/registry'; } from "react";
import './Console.scss'; import ConsoleInput from "./ConsoleInput";
import { RequestContextProvider, useRequestReadContext } from '../contexts/request_context'; import ConsoleOutput from "./ConsoleOutput";
import {EditorContextProvider} from '../contexts/editor_context/editor_context'; import { Panel } from "./Panel";
import { ServicesContextProvider } from '../contexts/services_context'; import PanelsContainer from "./PanelContainer";
import { createHistory, History, createStorage, createSettings } from '../services'; import { PanelContextProvider } from "../contexts/panel_context";
import { create } from '../storage/local_storage_object_client'; import { PanelRegistry } from "../contexts/panel_context/registry";
import { EuiFlexGroup, EuiFlexItem,EuiCodeBlock } from '@elastic/eui'; import "./Console.scss";
import {RequestStatusBar} from './request_status_bar'; import {
import useEventListener from '@/lib/hooks/use_event_listener'; RequestContextProvider,
import {Tabs} from 'antd'; useRequestReadContext,
} from "../contexts/request_context";
import { EditorContextProvider } from "../contexts/editor_context/editor_context";
import { ServicesContextProvider } from "../contexts/services_context";
import {
createHistory,
History,
createStorage,
createSettings,
} from "../services";
import { create } from "../storage/local_storage_object_client";
import { EuiFlexGroup, EuiFlexItem, EuiCodeBlock } from "@elastic/eui";
import { RequestStatusBar } from "./request_status_bar";
import useEventListener from "@/lib/hooks/use_event_listener";
import { Tabs } from "antd";
interface props { interface props {
selectedCluster: any; selectedCluster: any;
@ -26,7 +40,7 @@ interface props {
} }
const INITIAL_PANEL_WIDTH = 50; const INITIAL_PANEL_WIDTH = 50;
const PANEL_MIN_WIDTH = '300px'; const PANEL_MIN_WIDTH = "300px";
const ConsoleWrapper = ({ const ConsoleWrapper = ({
selectedCluster, selectedCluster,
@ -42,7 +56,7 @@ const ConsoleWrapper = ({
const lastDatum = requestData?.[requestData.length - 1] ?? requestError; const lastDatum = requestData?.[requestData.length - 1] ?? requestError;
const calcHeight = height > 0 ? (height)+'px' : '100%'; const calcHeight = height > 0 ? height + "px" : "100%";
// const leftBarRef = useRef(null) // const leftBarRef = useRef(null)
// const rightBarRef = useRef(null) // const rightBarRef = useRef(null)
// const [widths, setWidths] = useState(['calc(50% - 7px)', 'calc(50% - 7px)']) // const [widths, setWidths] = useState(['calc(50% - 7px)', 'calc(50% - 7px)'])
@ -53,11 +67,36 @@ const ConsoleWrapper = ({
return ( return (
<div style={{ height: calcHeight }}> <div style={{ height: calcHeight }}>
<div className="Console" style={{height:'100%'}}> <div className="Console" style={{ height: "100%" }}>
<PanelsContainer resizerClassName="resizer"> <PanelsContainer resizerClassName="resizer">
<Panel style={{ height: '100%', position: 'relative', minWidth: PANEL_MIN_WIDTH, paddingBottom:26 }} initialWidth={INITIAL_PANEL_WIDTH}> <Panel
<ConsoleInput height={height-26+'px'} clusterID={selectedCluster.id} saveEditorContent={saveEditorContent} initialText={initialText} paneKey={paneKey} /> style={{
<div style={{background:'#fff', position:'absolute', left:0, bottom:0, width: '100%', height:26, zIndex:997, borderTop: '1px solid #eee'}}> height: "100%",
position: "relative",
minWidth: PANEL_MIN_WIDTH,
paddingBottom: 26,
}}
initialWidth={INITIAL_PANEL_WIDTH}
>
<ConsoleInput
height={height - 26 + "px"}
clusterID={selectedCluster.id}
saveEditorContent={saveEditorContent}
initialText={initialText}
paneKey={paneKey}
/>
<div
style={{
background: "#fff",
position: "absolute",
left: 0,
bottom: 0,
width: "100%",
height: 26,
zIndex: 997,
borderTop: "1px solid #eee",
}}
>
<RequestStatusBar <RequestStatusBar
requestInProgress={requestInProgress} requestInProgress={requestInProgress}
selectedCluster={selectedCluster} selectedCluster={selectedCluster}
@ -78,13 +117,24 @@ const ConsoleWrapper = ({
/> />
</div> </div>
</Panel> </Panel>
<Panel style={{ height: '100%', position: 'relative', minWidth: PANEL_MIN_WIDTH, paddingBottom:26 }} initialWidth={INITIAL_PANEL_WIDTH}> <Panel
<Tabs tabPosition='right' style={{height:'100%', width:'100%'}} size="small"> style={{
height: "100%",
position: "relative",
minWidth: PANEL_MIN_WIDTH,
paddingBottom: 26,
}}
initialWidth={INITIAL_PANEL_WIDTH}
>
<Tabs
tabPosition="right"
style={{ height: "100%", width: "100%" }}
size="small"
>
<Tabs.TabPane tab="Result" key="result"> <Tabs.TabPane tab="Result" key="result">
<div style={{ height: height - 26 }}> <div style={{ height: height - 26 }}>
<ConsoleOutput clusterID={selectedCluster.id} /> <ConsoleOutput clusterID={selectedCluster.id} />
</div> </div>
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab="Headers" key="headers"> <Tabs.TabPane tab="Headers" key="headers">
<Tabs animated={false}> <Tabs animated={false}>
@ -101,7 +151,18 @@ const ConsoleWrapper = ({
</Tabs> </Tabs>
</Tabs.TabPane> </Tabs.TabPane>
</Tabs> </Tabs>
<div style={{background:'#fff', position:'absolute', right:0, bottom:0, width: '100%', height:26, zIndex:997, borderTop: '1px solid #eee'}}> <div
style={{
background: "#fff",
position: "absolute",
right: 0,
bottom: 0,
width: "100%",
height: 26,
zIndex: 997,
borderTop: "1px solid #eee",
}}
>
<RequestStatusBar <RequestStatusBar
requestInProgress={requestInProgress} requestInProgress={requestInProgress}
selectedCluster={selectedCluster} selectedCluster={selectedCluster}
@ -150,7 +211,6 @@ const ConsoleWrapper = ({
}; };
const Console = (params: props) => { const Console = (params: props) => {
const registryRef = useRef(new PanelRegistry()); const registryRef = useRef(new PanelRegistry());
// const [consoleInputKey] = useMemo(()=>{ // const [consoleInputKey] = useMemo(()=>{
// return [selectedCluster.id + '-console-input']; // return [selectedCluster.id + '-console-input'];
@ -159,24 +219,28 @@ const Console = (params:props) => {
const { storage, history, objectStorageClient, settings } = useMemo(() => { const { storage, history, objectStorageClient, settings } = useMemo(() => {
const storage = createStorage({ const storage = createStorage({
engine: window.localStorage, engine: window.localStorage,
prefix: 'sense:', prefix: "sense:",
}); });
const history: History = createHistory({ storage }); const history: History = createHistory({ storage });
const objectStorageClient = create(storage); const objectStorageClient = create(storage);
const settings = createSettings({ storage }); const settings = createSettings({ storage });
return { storage, history, objectStorageClient, settings }; return { storage, history, objectStorageClient, settings };
}, []) }, []);
return ( <PanelContextProvider registry={registryRef.current}> return (
<PanelContextProvider registry={registryRef.current}>
<RequestContextProvider> <RequestContextProvider>
<EditorContextProvider> <EditorContextProvider>
<ServicesContextProvider value={{ services: { history, storage, objectStorageClient, settings}, clusterID: params.selectedCluster.id }}> <ServicesContextProvider
value={{
services: { history, storage, objectStorageClient, settings },
clusterID: params.selectedCluster.id,
}}
>
<ConsoleWrapper {...params} /> <ConsoleWrapper {...params} />
</ServicesContextProvider> </ServicesContextProvider>
</EditorContextProvider> </EditorContextProvider>
</RequestContextProvider> </RequestContextProvider>
</PanelContextProvider> </PanelContextProvider>
); );
};
}
export default Console; export default Console;

View File

@ -166,8 +166,10 @@ const ConsoleInputUI = ({
}; };
}, []); }, []);
useEffect(() => { useEffect(() => {
if (clusterID) {
retrieveAutoCompleteInfo(settings, settings.getAutocomplete(), clusterID); retrieveAutoCompleteInfo(settings, settings.getAutocomplete(), clusterID);
aceEditorRef.current && (aceEditorRef.current["clusterID"] = clusterID); aceEditorRef.current && (aceEditorRef.current["clusterID"] = clusterID);
}
}, [clusterID]); }, [clusterID]);
const handleSaveAsCommonCommand = async () => { const handleSaveAsCommonCommand = async () => {

View File

@ -30,44 +30,50 @@
* GitHub history for details. * GitHub history for details.
*/ */
import { useCallback } from 'react'; import { useCallback } from "react";
import { sendRequestToES } from './send_request_to_es'; import { sendRequestToES } from "./send_request_to_es";
import { instance as registry } from '../../contexts/editor_context/editor_registry'; import { instance as registry } from "../../contexts/editor_context/editor_registry";
import { useRequestActionContext } from '../../contexts/request_context'; import { useRequestActionContext } from "../../contexts/request_context";
import { useServicesContext } from '../../contexts/services_context'; import { useServicesContext } from "../../contexts/services_context";
import {getCommand} from '../../modules/mappings/mappings'; import { getCommand } from "../../modules/mappings/mappings";
import {useEditorReadContext} from '../../contexts/editor_context'; import { useEditorReadContext } from "../../contexts/editor_context";
function buildRawCommonCommandRequest(cmd: any) { function buildRawCommonCommandRequest(cmd: any) {
const { requests } = cmd._source; const { requests } = cmd._source;
const strReqs = requests.map((req: any) => { const strReqs = requests.map((req: any) => {
const { method, path, body } = req; const { method, path, body } = req;
return `${method} ${path}\n${body}`; return `${method} ${path}\n${body}`;
}) });
return strReqs.join('\n'); return strReqs.join("\n");
} }
export const useSendCurrentRequestToES = () => { export const useSendCurrentRequestToES = () => {
const dispatch = useRequestActionContext(); const dispatch = useRequestActionContext();
const { services: { history }, clusterID } = useServicesContext(); const {
services: { history },
clusterID,
} = useServicesContext();
const { sensorEditor: editor } = useEditorReadContext(); const { sensorEditor: editor } = useEditorReadContext();
return useCallback(async () => { return useCallback(async () => {
try { try {
// const editor = registry.getInputEditor(); // const editor = registry.getInputEditor();
if(!editor) return if (!editor) return;
const requests = await editor.getRequestsInRange(); const requests = await editor.getRequestsInRange();
if (!requests.length) { if (!requests.length) {
console.log('No request selected. Select a request by placing the cursor inside it.'); console.log(
"No request selected. Select a request by placing the cursor inside it."
);
return; return;
} }
const { url, method, data } = requests[0]; const { url, method, data } = requests[0];
if(method === 'LOAD'){ if (method === "LOAD") {
const rawUrl = data[0] ? data[0].slice(4).trim() : url; const rawUrl = data[0] ? data[0].slice(4).trim() : url;
const cmd = getCommand(rawUrl); const cmd = getCommand(rawUrl);
// const curPostion = editor.currentReqRange //(editor.getCoreEditor().getCurrentPosition()); // const curPostion = editor.currentReqRange //(editor.getCoreEditor().getCurrentPosition());
const lineNumber = editor.getCoreEditor().getCurrentPosition().lineNumber; const lineNumber = editor.getCoreEditor().getCurrentPosition()
let crange = await editor.getRequestRange(lineNumber) .lineNumber;
const rawRequest = buildRawCommonCommandRequest(cmd) let crange = await editor.getRequestRange(lineNumber);
const rawRequest = buildRawCommonCommandRequest(cmd);
await editor.getCoreEditor().replaceRange(crange as any, rawRequest); await editor.getCoreEditor().replaceRange(crange as any, rawRequest);
// await editor.autoIndent(); // await editor.autoIndent();
// editor.getCoreEditor().getContainer().focus(); // editor.getCoreEditor().getContainer().focus();
@ -81,7 +87,7 @@ export const useSendCurrentRequestToES = () => {
return; return;
} }
dispatch({ type: 'sendRequest', payload: undefined }); dispatch({ type: "sendRequest", payload: undefined });
// @ts-ignore // @ts-ignore
const results = await sendRequestToES({ requests, clusterID }); const results = await sendRequestToES({ requests, clusterID });
@ -104,20 +110,20 @@ export const useSendCurrentRequestToES = () => {
// } // }
// //
dispatch({ dispatch({
type: 'requestSuccess', type: "requestSuccess",
payload: { payload: {
data: results, data: results,
}, },
}); });
} catch (e) { } catch (e) {
if (e?.response) { if (e) {
dispatch({ dispatch({
type: 'requestFail', type: "requestSuccess",
payload: e, payload: { data: [e] },
}); });
} else { } else {
dispatch({ dispatch({
type: 'requestFail', type: "requestFail",
payload: undefined, payload: undefined,
}); });
} }

View File

@ -31,10 +31,10 @@
*/ */
// @ts-ignore // @ts-ignore
import $ from 'jquery'; import $ from "jquery";
// @ts-ignore // @ts-ignore
import { stringify } from 'query-string'; import { stringify } from "query-string";
import {pathPrefix, ESPrefix} from '@/services/common'; import { pathPrefix, ESPrefix } from "@/services/common";
interface SendOptions { interface SendOptions {
asSystemRequest?: boolean; asSystemRequest?: boolean;
@ -48,14 +48,14 @@ export function getVersion() {
} }
export function getContentType(body: unknown) { export function getContentType(body: unknown) {
if (!body) return 'text/plain'; if (!body) return "text/plain";
return 'application/json'; return "application/json";
} }
export function extractClusterIDFromURL() { export function extractClusterIDFromURL() {
const matchs = location.hash.match(/\/elasticsearch\/(\w+)\/?/); const matchs = location.hash.match(/\/elasticsearch\/(\w+)\/?/);
if (!matchs) { if (!matchs) {
return '' return "";
} }
return matchs[1]; return matchs[1];
} }
@ -84,16 +84,20 @@ export function send(
data, data,
contentType: getContentType(data), contentType: getContentType(data),
cache: false, cache: false,
crossDomain: true, // crossDomain: true,
type: 'POST', type: "POST",
dataType: 'json', // disable automatic guessing dataType: "json", // disable automatic guessing
}; };
$.ajax(options).then( $.ajax(options).then(
(responseData: any, textStatus: string, jqXHR: unknown) => { (responseData: any, textStatus: string, jqXHR: unknown) => {
wrappedDfd.resolveWith({}, [responseData, textStatus, jqXHR]); wrappedDfd.resolveWith({}, [responseData, textStatus, jqXHR]);
}, },
((jqXHR: { status: number; responseText: string }, textStatus: string, errorThrown: Error) => { ((
jqXHR: { status: number; responseText: string },
textStatus: string,
errorThrown: Error
) => {
if (jqXHR.status === 0) { if (jqXHR.status === 0) {
jqXHR.responseText = jqXHR.responseText =
"\n\nFailed to connect to Console's backend.\nPlease check the server is up and running"; "\n\nFailed to connect to Console's backend.\nPlease check the server is up and running";
@ -107,25 +111,25 @@ export function send(
export function queryCommonCommands(title?: string) { export function queryCommonCommands(title?: string) {
let url = `${pathPrefix}/elasticsearch/command`; let url = `${pathPrefix}/elasticsearch/command`;
if (title) { if (title) {
url +=`?title=${title}` url += `?title=${title}`;
} }
return fetch(url, { return fetch(url, {
method: 'GET', method: "GET",
}) });
} }
export function constructESUrl(baseUri: string, path: string) { export function constructESUrl(baseUri: string, path: string) {
baseUri = baseUri.replace(/\/+$/, ''); baseUri = baseUri.replace(/\/+$/, "");
path = path.replace(/^\/+/, ''); path = path.replace(/^\/+/, "");
return baseUri + '/' + path; return baseUri + "/" + path;
} }
export function saveCommonCommand(params: any) { export function saveCommonCommand(params: any) {
return fetch(`${pathPrefix}/elasticsearch/command`, { return fetch(`${pathPrefix}/elasticsearch/command`, {
method: 'POST', method: "POST",
body: JSON.stringify(params), body: JSON.stringify(params),
headers: { headers: {
'content-type': 'application/json' "content-type": "application/json",
} },
}) });
} }

View File

@ -207,8 +207,12 @@ export default {
"Storage Usage", "Storage Usage",
"dashboard.charts.title.cluster_storage.axis.available_storage": "dashboard.charts.title.cluster_storage.axis.available_storage":
"Storage Available", "Storage Available",
"dashboard.charts.title.cluster_documents.axis.documents": "Documents Count", "dashboard.charts.title.cluster_documents.axis.count": "Documents Count",
"dashboard.charts.title.cluster_documents.axis.deleted": "Docmuents Deleted",
"dashboard.charts.title.cluster_indices.axis.count": "Indices Count",
"dashboard.charts.title.cluster_documents.axis.counts": "Shards Count", "dashboard.charts.title.cluster_documents.axis.counts": "Shards Count",
"dashboard.charts.title.node_count.axis.count": "Nodes Count",
"dashboard.charts.title.cluster_health.axis.percent": "Health Percent",
"app.login.message-invalid-credentials": "app.login.message-invalid-credentials":
"Invalid username or passwordadmin/888888", "Invalid username or passwordadmin/888888",

View File

@ -76,6 +76,13 @@ export default {
"cluster.metrics.node.axis.docs_count.title": "Document Count", "cluster.metrics.node.axis.docs_count.title": "Document Count",
"cluster.metrics.node.axis.index_storage.title": "Indices Storage", "cluster.metrics.node.axis.index_storage.title": "Indices Storage",
"cluster.metrics.node.axis.jvm_heap_used_percent.title": "JVM Heap Usage", "cluster.metrics.node.axis.jvm_heap_used_percent.title": "JVM Heap Usage",
"cluster.metrics.node.axis.os_cpu.title": "OS CPU Percent",
"cluster.metrics.node.axis.os_used_mem.title": "OS Mem Usage",
"cluster.metrics.node.axis.indexing_pressure_memory.title":
"Indexing Pressure",
"cluster.metrics.node.axis.jvm_used_heap.title": "JVM Used Heap",
"cluster.metrics.node.axis.jvm_young_gc_rate.title": "Young GC Rate",
"cluster.metrics.node.axis.jvm_young_gc_latency.title": "Young GC Latency",
"cluster.metrics.index.axis.index_storage.title": "Index Storage", "cluster.metrics.index.axis.index_storage.title": "Index Storage",
"cluster.metrics.index.axis.doc_count.title": "Document count", "cluster.metrics.index.axis.doc_count.title": "Document count",
@ -105,4 +112,5 @@ export default {
"cluster.metrics.group.http": "Http Traffic", "cluster.metrics.group.http": "Http Traffic",
"cluster.metrics.group.memory": "Memory", "cluster.metrics.group.memory": "Memory",
"cluster.metrics.group.cache": "Cache", "cluster.metrics.group.cache": "Cache",
"cluster.metrics.group.JVM": "JVM",
}; };

View File

@ -6,7 +6,8 @@ export default {
"navBar.lang": "语言", "navBar.lang": "语言",
"layout.user.appname": "极限数据管理后台", "layout.user.appname": "极限数据管理后台",
"layout.user.appslogon": "极限科技的数据管理平台是东半球最好用的实时数据管理平台", "layout.user.appslogon":
"极限科技的数据管理平台是东半球最好用的实时数据管理平台",
"app.setting.appname": "极限数据中心", "app.setting.appname": "极限数据中心",
"layout.user.link.help": "帮助", "layout.user.link.help": "帮助",
@ -209,8 +210,12 @@ export default {
"dashboard.charts.title.cluster_storage.axis.indices_storage": "索引存储", "dashboard.charts.title.cluster_storage.axis.indices_storage": "索引存储",
"dashboard.charts.title.cluster_storage.axis.available_storage": "剩余存储", "dashboard.charts.title.cluster_storage.axis.available_storage": "剩余存储",
"dashboard.charts.title.node_count.axis.count": "节点数",
"dashboard.charts.title.cluster_health.axis.percent": "健康状态百分比",
"dashboard.charts.title.cluster_documents.axis.documents": "文档总数", "dashboard.charts.title.cluster_documents.axis.count": "文档总数",
"dashboard.charts.title.cluster_documents.axis.deleted": "文档删除数",
"dashboard.charts.title.cluster_indices.axis.count": "索引总数",
"dashboard.charts.title.cluster_documents.axis.counts": "分片总数", "dashboard.charts.title.cluster_documents.axis.counts": "分片总数",
"app.login.message-invalid-credentials": "账户或密码错误admin/888888", "app.login.message-invalid-credentials": "账户或密码错误admin/888888",

View File

@ -77,6 +77,16 @@ export default {
"cluster.metrics.node.axis.docs_count.title": "Document Count", "cluster.metrics.node.axis.docs_count.title": "Document Count",
"cluster.metrics.node.axis.index_storage.title": "Indices Storage", "cluster.metrics.node.axis.index_storage.title": "Indices Storage",
"cluster.metrics.node.axis.jvm_heap_used_percent.title": "JVM Heap Usage", "cluster.metrics.node.axis.jvm_heap_used_percent.title": "JVM Heap Usage",
"cluster.metrics.node.axis.os_cpu.title": "OS CPU Percent",
"cluster.metrics.node.axis.os_used_mem.title": "OS Mem Usage",
"cluster.metrics.node.axis.indexing_pressure_memory.title":
"Indexing Pressure",
"cluster.metrics.node.axis.jvm_used_heap.title": "JVM Used Heap",
"cluster.metrics.node.axis.jvm_young_gc_rate.title": "Young GC Rate",
"cluster.metrics.node.axis.jvm_young_gc_latency.title": "Young GC Latency",
"cluster.metrics.node.axis.jvm_mem_young_used.title": "Pools Young Used",
"cluster.metrics.node.axis.jvm_mem_young_peak_used.title":
"Pools Young Peak Used",
"cluster.metrics.index.axis.index_storage.title": "Index Storage", "cluster.metrics.index.axis.index_storage.title": "Index Storage",
"cluster.metrics.index.axis.doc_count.title": "Document count", "cluster.metrics.index.axis.doc_count.title": "Document count",
@ -106,4 +116,5 @@ export default {
"cluster.metrics.group.http": "Http Traffic", "cluster.metrics.group.http": "Http Traffic",
"cluster.metrics.group.memory": "Memory", "cluster.metrics.group.memory": "Memory",
"cluster.metrics.group.cache": "Cache", "cluster.metrics.group.cache": "Cache",
"cluster.metrics.group.JVM": "JVM",
}; };

View File

@ -453,7 +453,7 @@ class ClusterMonitor extends PureComponent {
}); });
let clusterAvailable = true; let clusterAvailable = true;
const { clusterStatus: cstatus, selectedCluster } = this.props; const { clusterStatus: cstatus, selectedCluster } = this.props;
if (cstatus && selectedCluster) { if (cstatus && selectedCluster && cstatus[selectedCluster.id]) {
clusterAvailable = cstatus[selectedCluster.id].available; clusterAvailable = cstatus[selectedCluster.id].available;
} }
@ -674,6 +674,7 @@ class ClusterMonitor extends PureComponent {
id={item.metric.label} id={item.metric.label}
groupId={item.metric.group} groupId={item.metric.group}
timeZone={timezone} timeZone={timezone}
color={item.color}
xScaleType={ScaleType.Time} xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear} yScaleType={ScaleType.Linear}
xAccessor={0} xAccessor={0}

View File

@ -28,6 +28,7 @@ const gorupOrder = [
"latency", "latency",
"storage", "storage",
"http", "http",
"JVM",
"memory", "memory",
"cache", "cache",
]; ];

View File

@ -10,6 +10,7 @@ import {
useEffect, useEffect,
useMemo, useMemo,
useRef, useRef,
useLayoutEffect,
} from "react"; } from "react";
import { useLocalStorage } from "@/lib/hooks/storage"; import { useLocalStorage } from "@/lib/hooks/storage";
import { setClusterID } from "../../components/kibana/console/modules/mappings/mappings"; import { setClusterID } from "../../components/kibana/console/modules/mappings/mappings";
@ -116,6 +117,9 @@ const consoleTabReducer = (state: any, action: any) => {
...state, ...state,
order: action.payload.order, order: action.payload.order,
}; };
break;
case "init":
newState = action.payload;
default: default:
} }
// setLocalState(newState); // setLocalState(newState);
@ -199,11 +203,16 @@ export const ConsoleUI = ({
}); });
if (panes.length == 0) { if (panes.length == 0) {
removeLocalState(); //reset tabState
dispatch({
type: "init",
payload: initialDefaultState(),
});
//removeLocalState();
return; return;
} }
setLocalState(tabState); setLocalState(tabState);
}, [tabState, clusterMap]); }, [tabState, clusterMap, dispatch]);
const saveEditorContent = useCallback( const saveEditorContent = useCallback(
(content) => { (content) => {

View File

@ -1,29 +1,43 @@
import React from 'react'; import React from "react";
import {Card, Form, Icon, Input, InputNumber, Button, Switch, message, Spin} from 'antd'; import {
import router from 'umi/router'; Card,
Form,
Icon,
Input,
InputNumber,
Button,
Switch,
message,
Spin,
} from "antd";
import router from "umi/router";
import styles from './Form.less'; import styles from "./Form.less";
import { connect } from "dva"; import { connect } from "dva";
import NewCluster from './Step'; import NewCluster from "./Step";
import PageHeaderWrapper from '@/components/PageHeaderWrapper'; import PageHeaderWrapper from "@/components/PageHeaderWrapper";
@Form.create() @Form.create()
@connect(({ clusterConfig }) => ({ @connect(({ clusterConfig }) => ({
clusterConfig clusterConfig,
})) }))
class ClusterForm extends React.Component { class ClusterForm extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
let editValue = this.props.clusterConfig.editValue; let editValue = this.props.clusterConfig.editValue;
let needAuth = false; let needAuth = false;
if(editValue.basic_auth && typeof editValue.basic_auth.username !== 'undefined' && editValue.basic_auth.username !== ''){ if (
editValue.basic_auth &&
typeof editValue.basic_auth.username !== "undefined" &&
editValue.basic_auth.username !== ""
) {
needAuth = true; needAuth = true;
} }
this.state = { this.state = {
confirmDirty: false, confirmDirty: false,
needAuth: needAuth, needAuth: needAuth,
isLoading: false, isLoading: false,
} };
} }
componentDidMount() { componentDidMount() {
//console.log(this.props.clusterConfig.editMode) //console.log(this.props.clusterConfig.editMode)
@ -31,8 +45,8 @@ class ClusterForm extends React.Component{
compareToFirstPassword = (rule, value, callback) => { compareToFirstPassword = (rule, value, callback) => {
const { form } = this.props; const { form } = this.props;
if (value && value !== form.getFieldValue('password')) { if (value && value !== form.getFieldValue("password")) {
callback('Two passwords that you enter is inconsistent!'); callback("Two passwords that you enter is inconsistent!");
} else { } else {
callback(); callback();
} }
@ -41,7 +55,7 @@ class ClusterForm extends React.Component{
validateToNextPassword = (rule, value, callback) => { validateToNextPassword = (rule, value, callback) => {
const { form } = this.props; const { form } = this.props;
if (value && this.state.confirmDirty) { if (value && this.state.confirmDirty) {
form.validateFields(['confirm'], { force: true }); form.validateFields(["confirm"], { force: true });
} }
callback(); callback();
}; };
@ -50,7 +64,7 @@ class ClusterForm extends React.Component{
const { form, dispatch, clusterConfig } = this.props; const { form, dispatch, clusterConfig } = this.props;
form.validateFields((errors, values) => { form.validateFields((errors, values) => {
if (errors) { if (errors) {
return return;
} }
//console.log(values); //console.log(values);
let newVals = { let newVals = {
@ -64,39 +78,39 @@ class ClusterForm extends React.Component{
enabled: values.enabled, enabled: values.enabled,
monitored: values.monitored, monitored: values.monitored,
version: values.version, version: values.version,
schema: values.isTLS === true ? 'https': 'http', schema: values.isTLS === true ? "https" : "http",
// order: values.order, // order: values.order,
} };
if(clusterConfig.editMode === 'NEW') { if (clusterConfig.editMode === "NEW") {
dispatch({ dispatch({
type: 'clusterConfig/addCluster', type: "clusterConfig/addCluster",
payload: newVals, payload: newVals,
}).then(function(rel) { }).then(function(rel) {
if (rel) { if (rel) {
message.success("添加成功") message.success("添加成功");
router.push('/system/cluster'); router.push("/system/cluster");
} }
}); });
} else { } else {
newVals.id = clusterConfig.editValue.id; newVals.id = clusterConfig.editValue.id;
dispatch({ dispatch({
type: 'clusterConfig/updateCluster', type: "clusterConfig/updateCluster",
payload: newVals, payload: newVals,
}).then(function(rel) { }).then(function(rel) {
if (rel) { if (rel) {
message.success("修改成功") message.success("修改成功");
router.push('/system/cluster'); router.push("/system/cluster");
} }
}); });
} }
}) });
} };
handleAuthChange = (val) => { handleAuthChange = (val) => {
this.setState({ this.setState({
needAuth: val, needAuth: val,
}) });
} };
tryConnect = async () => { tryConnect = async () => {
const { dispatch, form } = this.props; const { dispatch, form } = this.props;
@ -104,6 +118,13 @@ class ClusterForm extends React.Component{
if (errors) { if (errors) {
return false; return false;
} }
return values;
});
if (!values) {
return;
}
let newVals = { let newVals = {
name: values.name, name: values.name,
host: values.host, host: values.host,
@ -111,26 +132,21 @@ class ClusterForm extends React.Component{
username: values.username, username: values.username,
password: values.password, password: values.password,
}, },
schema: values.isTLS === true ? 'https': 'http', schema: values.isTLS === true ? "https" : "http",
} };
return values; this.setState({ isLoading: true });
});
if(!values){
return
}
this.setState({isLoading: true})
const res = await dispatch({ const res = await dispatch({
type: 'clusterConfig/doTryConnect', type: "clusterConfig/doTryConnect",
payload: values payload: newVals,
}); });
if (res) { if (res) {
message.success('连接成功!') message.success("连接成功!");
form.setFieldsValue({ form.setFieldsValue({
version: res.version version: res.version,
}) });
}
this.setState({isLoading: false})
} }
this.setState({ isLoading: false });
};
render() { render() {
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
@ -159,56 +175,64 @@ class ClusterForm extends React.Component{
const { editValue, editMode } = this.props.clusterConfig; const { editValue, editMode } = this.props.clusterConfig;
return ( return (
<PageHeaderWrapper> <PageHeaderWrapper>
<Card title={editMode === 'NEW' ? '注册集群': '修改集群配置'} <Card
extra={[<Button type="primary" onClick={()=>{ title={editMode === "NEW" ? "注册集群" : "修改集群配置"}
router.push('/system/cluster'); extra={[
}}>返回</Button>]} <Button
type="primary"
onClick={() => {
router.push("/system/cluster");
}}
>
返回
</Button>,
]}
> >
<Spin spinning={this.state.isLoading}> <Spin spinning={this.state.isLoading}>
<Form {...formItemLayout}> <Form {...formItemLayout}>
<Form.Item label="集群名称"> <Form.Item label="集群名称">
{getFieldDecorator('name', { {getFieldDecorator("name", {
initialValue: editValue.name, initialValue: editValue.name,
rules: [ rules: [
{ {
required: true, required: true,
message: 'Please input cluster name!', message: "Please input cluster name!",
}, },
], ],
})(<Input autoComplete='off' placeholder="cluster-name" />)} })(<Input autoComplete="off" placeholder="cluster-name" />)}
</Form.Item> </Form.Item>
<Form.Item label="集群地址"> <Form.Item label="集群地址">
{getFieldDecorator('host', { {getFieldDecorator("host", {
initialValue: editValue.host, initialValue: editValue.host,
rules: [ rules: [
{ {
type: 'string', type: "string",
pattern: /^[\w\.]+\:\d+$/, //(https?:\/\/)? pattern: /^[\w\.]+\:\d+$/, //(https?:\/\/)?
message: '请输入域名或 IP 地址和端口号', message: "请输入域名或 IP 地址和端口号",
}, },
{ {
required: true, required: true,
message: '请输入域名或 IP 地址和端口号!', message: "请输入域名或 IP 地址和端口号!",
}, },
], ],
})(<Input placeholder="127.0.0.1:9200" />)} })(<Input placeholder="127.0.0.1:9200" />)}
</Form.Item> </Form.Item>
<Form.Item style={{ marginBottom: 0 }}> <Form.Item style={{ marginBottom: 0 }}>
{getFieldDecorator('version', { {getFieldDecorator("version", {
initialValue: editValue.version, initialValue: editValue.version,
rules: [ rules: [],
],
})(<Input type="hidden" />)} })(<Input type="hidden" />)}
</Form.Item> </Form.Item>
<Form.Item label="TLS"> <Form.Item label="TLS">
{getFieldDecorator('isTLS', { {getFieldDecorator("isTLS", {
initialValue: editValue?.schema === "https", initialValue: editValue?.schema === "https",
})( })(
<Switch <Switch
defaultChecked={editValue?.schema === "https"} defaultChecked={editValue?.schema === "https"}
checkedChildren={<Icon type="check" />} checkedChildren={<Icon type="check" />}
unCheckedChildren={<Icon type="close" />} unCheckedChildren={<Icon type="close" />}
/>)} />
)}
</Form.Item> </Form.Item>
<Form.Item label="是否需要身份验证"> <Form.Item label="是否需要身份验证">
<Switch <Switch
@ -218,29 +242,31 @@ class ClusterForm extends React.Component{
unCheckedChildren={<Icon type="close" />} unCheckedChildren={<Icon type="close" />}
/> />
</Form.Item> </Form.Item>
{this.state.needAuth === true ? (<div> {this.state.needAuth === true ? (
<div>
<Form.Item label="用户名"> <Form.Item label="用户名">
{getFieldDecorator('username', { {getFieldDecorator("username", {
initialValue: editValue.basic_auth?.username, initialValue: editValue.basic_auth?.username,
rules: [ rules: [],
], })(<Input autoComplete="off" />)}
})(<Input autoComplete='off' />)}
</Form.Item> </Form.Item>
<Form.Item label="密码" hasFeedback> <Form.Item label="密码" hasFeedback>
{getFieldDecorator('password', { {getFieldDecorator("password", {
initialValue: editValue.basic_auth?.password, initialValue: editValue.basic_auth?.password,
rules: [ rules: [],
],
})(<Input.Password />)} })(<Input.Password />)}
</Form.Item> </Form.Item>
</div>):''} </div>
) : (
""
)}
{/* <Form.Item label=""> {/* <Form.Item label="">
{getFieldDecorator('order', { {getFieldDecorator('order', {
initialValue: editValue.order || 0, initialValue: editValue.order || 0,
})(<InputNumber />)} })(<InputNumber />)}
</Form.Item> */} </Form.Item> */}
<Form.Item label="描述"> <Form.Item label="描述">
{getFieldDecorator('description', { {getFieldDecorator("description", {
initialValue: editValue.description, initialValue: editValue.description,
})(<Input.TextArea placeholder="集群应用描述" />)} })(<Input.TextArea placeholder="集群应用描述" />)}
</Form.Item> </Form.Item>
@ -254,17 +280,22 @@ class ClusterForm extends React.Component{
/>)} />)}
</Form.Item> */} </Form.Item> */}
<Form.Item label="启用监控"> <Form.Item label="启用监控">
{getFieldDecorator('monitored', { {getFieldDecorator("monitored", {
valuePropName: 'checked', valuePropName: "checked",
initialValue: typeof editValue.monitored === 'undefined' ? true: editValue.monitored, initialValue:
})(<Switch typeof editValue.monitored === "undefined"
? true
: editValue.monitored,
})(
<Switch
checkedChildren={<Icon type="check" />} checkedChildren={<Icon type="check" />}
unCheckedChildren={<Icon type="close" />} unCheckedChildren={<Icon type="close" />}
/>)} />
)}
</Form.Item> </Form.Item>
<Form.Item {...tailFormItemLayout}> <Form.Item {...tailFormItemLayout}>
<Button type="primary" onClick={this.handleSubmit}> <Button type="primary" onClick={this.handleSubmit}>
{editMode === 'NEW' ? '注册': '保存'} {editMode === "NEW" ? "注册" : "保存"}
</Button> </Button>
<Button style={{ marginLeft: 15 }} onClick={this.tryConnect}> <Button style={{ marginLeft: 15 }} onClick={this.tryConnect}>
测试连接 测试连接
@ -274,7 +305,7 @@ class ClusterForm extends React.Component{
</Spin> </Spin>
</Card> </Card>
</PageHeaderWrapper> </PageHeaderWrapper>
) );
} }
} }

View File

@ -214,13 +214,17 @@ class Index extends React.Component {
}); });
}; };
componentDidMount() { componentDidMount() {
this.fetchData({}); const { pageSize } = this.props.clusterConfig;
this.fetchData({
size: pageSize,
});
} }
handleSearchClick = () => { handleSearchClick = () => {
const { form } = this.props; const { form } = this.props;
this.fetchData({ this.fetchData({
name: form.getFieldValue("name"), name: form.getFieldValue("name"),
current: 1,
}); });
}; };
@ -268,6 +272,17 @@ class Index extends React.Component {
}); });
}; };
handleTableChange = (pagination, filters, sorter, extra) => {
const { form } = this.props;
const { pageSize, current } = pagination;
this.fetchData({
from: (current - 1) * pageSize,
size: pageSize,
name: form.getFieldValue("name"),
current,
});
};
render() { render() {
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
const formItemLayout = { const formItemLayout = {
@ -275,7 +290,7 @@ class Index extends React.Component {
wrapperCol: { span: 14 }, wrapperCol: { span: 14 },
style: { marginBottom: 0 }, style: { marginBottom: 0 },
}; };
const { data } = this.props.clusterConfig; const { total, data, pageSize, current } = this.props.clusterConfig;
return ( return (
<PageHeaderWrapper <PageHeaderWrapper
title={formatMessage({ id: "cluster.manage.title" })} title={formatMessage({ id: "cluster.manage.title" })}
@ -342,7 +357,13 @@ class Index extends React.Component {
bordered bordered
columns={this.columns} columns={this.columns}
dataSource={data} dataSource={data}
onChange={this.handleTableChange}
rowKey="id" rowKey="id"
pagination={{
pageSize: pageSize,
total: total?.value || total,
current: current,
}}
/> />
</Card> </Card>
</PageHeaderWrapper> </PageHeaderWrapper>

View File

@ -13,6 +13,8 @@ export default {
state: { state: {
editMode: "", editMode: "",
editValue: {}, editValue: {},
pageSize: 20,
current: 1,
}, },
effects: { effects: {
*fetchClusterList({ payload }, { call, put, select }) { *fetchClusterList({ payload }, { call, put, select }) {
@ -28,7 +30,10 @@ export default {
// } // }
yield put({ yield put({
type: "saveData", type: "saveData",
payload: res, payload: {
...res,
current: payload.current,
},
}); });
}, },
*addCluster({ payload }, { call, put, select }) { *addCluster({ payload }, { call, put, select }) {

View File

@ -113,7 +113,9 @@ class Index extends PureComponent {
]; ];
componentDidMount() { componentDidMount() {
this.fetchData(); this.fetchData({
current: 1,
});
} }
fetchData = (params = {}) => { fetchData = (params = {}) => {
@ -159,6 +161,7 @@ class Index extends PureComponent {
keyword: fieldsValue.keyword, keyword: fieldsValue.keyword,
from: 0, from: 0,
size: 10, size: 10,
current: 1,
}); });
this.setState({ this.setState({
searchKey: fieldsValue.keyword, searchKey: fieldsValue.keyword,
@ -231,9 +234,19 @@ class Index extends PureComponent {
}, },
}); });
}; };
handleTableChange = (pagination, filters, sorter, extra) => {
const { form } = this.props;
const { pageSize, current } = pagination;
this.fetchData({
from: (current - 1) * pageSize,
size: pageSize,
keyword: form.getFieldValue("keyword"),
current,
});
};
render() { render() {
const { data, total } = this.props.command; const { data, total, pageSize, current } = this.props.command;
const { const {
modalVisible, modalVisible,
updateModalVisible, updateModalVisible,
@ -301,7 +314,12 @@ class Index extends PureComponent {
bordered bordered
dataSource={data} dataSource={data}
rowKey="id" rowKey="id"
pagination={{ pageSize: 10 }} pagination={{
pageSize: pageSize,
current: current,
total: total?.value || total,
}}
onChange={this.handleTableChange}
columns={this.columns} columns={this.columns}
/> />
</div> </div>

View File

@ -4,7 +4,10 @@ import { formatESSearchResult } from "@/lib/elasticsearch/util";
export default { export default {
namespace: "command", namespace: "command",
state: {}, state: {
pageSize: 20,
current: 1,
},
effects: { effects: {
*fetchCommandList({ payload }, { call, put, select }) { *fetchCommandList({ payload }, { call, put, select }) {
let res = yield call(searchCommand, payload); let res = yield call(searchCommand, payload);
@ -15,7 +18,10 @@ export default {
res = formatESSearchResult(res); res = formatESSearchResult(res);
yield put({ yield put({
type: "saveData", type: "saveData",
payload: res, payload: {
...res,
current: payload.current,
},
}); });
}, },