diff --git a/api/init.go b/api/init.go index 16db7fa8..0a098ce0 100644 --- a/api/init.go +++ b/api/init.go @@ -49,6 +49,7 @@ func Init(cfg *config.AppConfig) { ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/monitors", alerting.CreateMonitor) ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/monitors/_execute", alerting.ExecuteMonitor) ui.HandleUIMethod(api.DELETE, "/elasticsearch/:id/alerting/monitors/:monitorID", alerting.DeleteMonitor) + ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_monitors/:monitorID/_acknowledge/alerts", alerting.AcknowledgeAlerts) ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/_settings", alerting.GetSettings) ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/destinations", alerting.GetDestinations) ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/destinations", alerting.CreateDestination) @@ -63,6 +64,7 @@ func Init(cfg *config.AppConfig) { ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/destinations/email_groups", alerting.GetEmailGroups) ui.HandleUIMethod(api.DELETE, "/elasticsearch/:id/alerting/email_groups/:emailGroupId", alerting.DeleteEmailGroup) ui.HandleUIMethod(api.PUT, "/elasticsearch/:id/alerting/email_groups/:emailGroupId", alerting.UpdateEmailGroup) + ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/email_groups/:emailGroupId", alerting.GetEmailGroup) diff --git a/service/alerting/alert.go b/service/alerting/alert.go index 966fd58b..8f68057d 100644 --- a/service/alerting/alert.go +++ b/service/alerting/alert.go @@ -39,7 +39,7 @@ func GetAlerts (w http.ResponseWriter, req *http.Request, ps httprouter.Params){ sortField = getQueryParam(req, "sortField", "start_time") severityLevel = getQueryParam(req, "severityLevel", "ALL") alertState = getQueryParam(req, "alertState", "ALL") - //monitorIds = getQueryParam(req, "monitorIds") + monitorIds = req.URL.Query()["monitorIds"] params = map[string]string{ "startIndex": from, "size": size, @@ -68,6 +68,9 @@ func GetAlerts (w http.ResponseWriter, req *http.Request, ps httprouter.Params){ params["sortOrder"] = sortDirection params["missing"] = "_last" } + if len(monitorIds) > 0{ + params["monitorId"] = monitorIds[0] + } if clearSearch := strings.TrimSpace(search); clearSearch != ""{ searches := strings.Split(clearSearch, " ") @@ -93,9 +96,10 @@ func GetAlerts (w http.ResponseWriter, req *http.Request, ps httprouter.Params){ if ds, ok := rawAlerts.([]interface{}); ok { for _, alert := range ds { if alertItem, ok := alert.(map[string]interface{}); ok { - alertItem["version"] = queryValue(alertItem, "alert_version", "") - alertItem["id"] = queryValue(alertItem, "alert_id", "") + if queryValue(alertItem, "id", nil) == nil { + alertItem["id"] = queryValue(alertItem, "alert_id", nil) + } alerts = append(alerts, alertItem) } } diff --git a/service/alerting/destination.go b/service/alerting/destination.go index 815fdc2d..e229ece1 100644 --- a/service/alerting/destination.go +++ b/service/alerting/destination.go @@ -445,7 +445,6 @@ func GetEmailAccounts(w http.ResponseWriter, req *http.Request, ps httprouter.Pa reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_accounts/_search", conf.Endpoint, API_PREFIX) res, err := doRequest(reqUrl, http.MethodPost, nil, reqBody) - //TODO to handle api error in doRequest function if err != nil { writeError(w, err) return @@ -507,7 +506,6 @@ func GetEmailAccount(w http.ResponseWriter, req *http.Request, ps httprouter.Par writeError(w, err) return } - //TODO error handle: check whether resBody has contains field error writeJSON(w, IfaceMap{ "ok": true, @@ -703,8 +701,8 @@ func GetEmailGroups(w http.ResponseWriter, req *http.Request, ps httprouter.Para if ms, ok := source.(map[string]interface{}); ok { assignTo(newItem, ms) } - newItem["ifSeqNo"] = queryValue(emailGroup, "seq_no", 0) - newItem["ifPrimaryTerm"] = queryValue(emailGroup, "primary_term", 0) + newItem["ifSeqNo"] = queryValue(emailGroup, "_seq_no", 0) + newItem["ifPrimaryTerm"] = queryValue(emailGroup, "_primary_term", 0) emailGroups = append(emailGroups, newItem) } } @@ -716,5 +714,44 @@ func GetEmailGroups(w http.ResponseWriter, req *http.Request, ps httprouter.Para }, http.StatusOK) } +func GetEmailGroup(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { + id := ps.ByName("id") + conf := elastic.GetConfig(id) + if conf == nil { + writeError(w, errors.New("cluster not found")) + return + } + emailAccountId := ps.ByName("emailGroupId") + + reqUrl := fmt.Sprintf("%s/%s/_alerting/destinations/email_groups/%s", conf.Endpoint, API_PREFIX, emailAccountId) + res, err := doRequest(reqUrl, http.MethodGet,nil, req.Body) + if err != nil { + writeError(w, err) + return + } + + var resBody = IfaceMap{} + err = decodeJSON(res.Body, &resBody) + res.Body.Close() + if err != nil { + writeError(w, err) + return + } + emailGroup := queryValue(resBody, "email_group", nil) + if emailGroup == nil { + writeJSON(w, IfaceMap{ + "ok": false, + }, http.StatusOK) + return + } + + writeJSON(w, IfaceMap{ + "ok": true, + "resp": emailGroup, + "ifSeqNo": queryValue(resBody, "_seq_no", 0), + "ifPrimaryTerm": queryValue(resBody, "_primary_term", 0), + }, http.StatusOK) + +} diff --git a/service/alerting/monitor.go b/service/alerting/monitor.go index 929c4801..c1039af3 100644 --- a/service/alerting/monitor.go +++ b/service/alerting/monitor.go @@ -443,7 +443,6 @@ func UpdateMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Param writeError(w, err) return } - //TODO error handle: check whether resBody has contains field error writeJSON(w, IfaceMap{ "ok": true, @@ -453,6 +452,43 @@ func UpdateMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Param } +func AcknowledgeAlerts(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { + id := ps.ByName("id") + conf := elastic.GetConfig(id) + if conf == nil { + writeError(w, errors.New("cluster not found")) + return + } + + monitorId := ps.ByName("monitorID") + + reqUrl := fmt.Sprintf("%s/%s/_alerting/monitors/%s/_acknowledge/alerts", conf.Endpoint, API_PREFIX, monitorId) + res, err := doRequest(reqUrl, http.MethodPost,nil, req.Body) + if err != nil { + writeError(w, err) + return + } + + var resBody = IfaceMap{} + err = decodeJSON(res.Body, &resBody) + res.Body.Close() + if err != nil { + writeError(w, err) + return + } + + var isOk = false + if failed, ok := resBody["failed"].([]interface{}); ok && len(failed) == 0 { + isOk = true + } + + writeJSON(w, IfaceMap{ + "ok": isOk, + "resp": resBody, + }, http.StatusOK) + +} + func ExecuteMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { id := ps.ByName("id") conf := elastic.GetConfig(id) diff --git a/web/src/components/kibana/core/public/application/scoped_history.ts b/web/src/components/kibana/core/public/application/scoped_history.ts index d7410123..b4a50d88 100644 --- a/web/src/components/kibana/core/public/application/scoped_history.ts +++ b/web/src/components/kibana/core/public/application/scoped_history.ts @@ -125,6 +125,7 @@ export class ScopedHistory if (typeof pathOrLocation === 'string') { this.parentHistory.push(this.prependBasePath(pathOrLocation), state); } else { + !pathOrLocation.key && (pathOrLocation.key = pathOrLocation.pathname+(pathOrLocation.search||'')) this.parentHistory.push(this.prependBasePath(pathOrLocation)); } }; diff --git a/web/src/locales/zh-CN/alert.js b/web/src/locales/zh-CN/alert.js index 67aa4667..f293a00d 100644 --- a/web/src/locales/zh-CN/alert.js +++ b/web/src/locales/zh-CN/alert.js @@ -14,9 +14,9 @@ export default { 'alert.dashboard.alerts': '告警管理', 'alert.dashboard.severity-options.all': '所有告警级别', 'alert.dashboard.state-options.all': '所有状态', - 'alert.dashboard.state-options.active': '激活的', - 'alert.dashboard.state-options.acknowledged': '已确认', - 'alert.dashboard.state-options.completed': '已完成', + 'alert.dashboard.state-options.active': '告警中', + 'alert.dashboard.state-options.acknowledged': '已知晓', + 'alert.dashboard.state-options.completed': '已恢复', 'alert.dashboard.state-options.error': '错误', 'alert.dashboard.state-options.deleted': '已删除', 'alert.dashboard.create-monitor-text': '暂无监控项。 创建监控项以添加触发器和操作。 一旦触发警报,状态将显示在此表中。', diff --git a/web/src/models/global.js b/web/src/models/global.js index a3e8f8af..61564c7c 100644 --- a/web/src/models/global.js +++ b/web/src/models/global.js @@ -126,15 +126,15 @@ export default { }); }, *rewriteURL({payload}, {select}){ - const {pathname, history} = payload; + const {pathname, history, search} = payload; const global = yield select(state=>state.global); if(pathname && global.selectedClusterID){ const newPart = `/elasticsearch/${global.selectedClusterID}/`; if(!pathname.includes('elasticsearch')){ - history.replace(pathname+newPart) + history.replace(pathname+newPart+search) }else{ const newPath = pathname.replace(/\/elasticsearch\/(\w+)\/?/, newPart); - history.replace(newPath) + history.replace(newPath+search) } } }, @@ -230,14 +230,17 @@ export default { if(pathname.startsWith("/system")){ clusterVisible = false; }else{ - if(!pathname.includes('elasticsearch')){ - dispatch({ - type: 'rewriteURL', - payload: { - pathname, - history, - } - }) + if(!pathname.startsWith("/exception") && pathname != '/alerting'){ + if(!pathname.includes('elasticsearch')){ + dispatch({ + type: 'rewriteURL', + payload: { + pathname, + history, + search, + } + }) + } } } dispatch({ diff --git a/web/src/pages/Alerting/index.js b/web/src/pages/Alerting/index.js index cd975389..67cdebeb 100644 --- a/web/src/pages/Alerting/index.js +++ b/web/src/pages/Alerting/index.js @@ -32,6 +32,8 @@ const notifications = { } } +const AlertingMain = React.memo(Main) + const AlertingUI = (props)=>{ if(!props.selectedCluster.id){ return null; @@ -52,13 +54,14 @@ const AlertingUI = (props)=>{ >
-
+
) } + export default connect(({ global })=>({ diff --git a/web/src/pages/Alerting/pages/CreateTrigger/components/ActionEmptyPrompt/ActionEmptyPrompt.js b/web/src/pages/Alerting/pages/CreateTrigger/components/ActionEmptyPrompt/ActionEmptyPrompt.js index 0e9b4573..ed1e8622 100644 --- a/web/src/pages/Alerting/pages/CreateTrigger/components/ActionEmptyPrompt/ActionEmptyPrompt.js +++ b/web/src/pages/Alerting/pages/CreateTrigger/components/ActionEmptyPrompt/ActionEmptyPrompt.js @@ -22,7 +22,7 @@ const actionEmptyText = 'Add an action to perform when this trigger is triggered const destinationEmptyText = 'There are no existing destinations. Add a destinations to create an action'; const createDestinationButton = ( - + Add destination ); diff --git a/web/src/pages/Alerting/pages/Dashboard/containers/Dashboard.js b/web/src/pages/Alerting/pages/Dashboard/containers/Dashboard.js index 2978feb0..62c83a99 100644 --- a/web/src/pages/Alerting/pages/Dashboard/containers/Dashboard.js +++ b/web/src/pages/Alerting/pages/Dashboard/containers/Dashboard.js @@ -184,7 +184,7 @@ export default class Dashboard extends Component { const queryParamsString = queryString.stringify(params); location.search; const { httpClient, history, notifications } = this.props; - history.replace({ ...this.props.location, search: queryParamsString }); + // history.replace({ ...this.props.location, search: queryParamsString }); httpClient.get('/alerting/alerts', { query: params }).then((resp) => { if (resp.ok) { const { alerts, totalAlerts } = resp; @@ -218,7 +218,7 @@ export default class Dashboard extends Component { const promises = Object.entries(monitorAlerts).map(([monitorId, alerts]) => httpClient - .post(`/alerting/monitors/${monitorId}/_acknowledge/alerts`, { + .post(`/alerting/_monitors/${monitorId}/_acknowledge/alerts`, { body: JSON.stringify({ alerts }), }) .then((resp) => { diff --git a/web/src/pages/Alerting/pages/Dashboard/utils/tableUtils.js b/web/src/pages/Alerting/pages/Dashboard/utils/tableUtils.js index e30a615b..ca9e7f00 100644 --- a/web/src/pages/Alerting/pages/Dashboard/utils/tableUtils.js +++ b/web/src/pages/Alerting/pages/Dashboard/utils/tableUtils.js @@ -28,6 +28,15 @@ const renderTime = (time) => { return DEFAULT_EMPTY_DATA; }; +const stateOptions = { + 'ALL': formatMessage({ id: 'alert.dashboard.state-options.all' }), + [ALERT_STATE.ACTIVE]: formatMessage({ id: 'alert.dashboard.state-options.active' }), + [ALERT_STATE.ACKNOWLEDGED]: formatMessage({ id: 'alert.dashboard.state-options.acknowledged' }), + [ALERT_STATE.COMPLETED]: formatMessage({ id: 'alert.dashboard.state-options.completed' }), + [ALERT_STATE.ERROR]: formatMessage({ id: 'alert.dashboard.state-options.error' }), + [ALERT_STATE.DELETED]: formatMessage({ id: 'alert.dashboard.state-options.deleted' }), +}; + export const columns = [ { field: 'start_time', @@ -76,7 +85,7 @@ export const columns = [ render: (state, alert) => { const stateText = typeof state !== 'string' ? DEFAULT_EMPTY_DATA : _.capitalize(state.toLowerCase()); - return state === ALERT_STATE.ERROR ? `${stateText}: ${alert.error_message}` : stateText; + return state === ALERT_STATE.ERROR ? `${stateText}: ${alert.error_message}` : (stateOptions[state] || stateText); }, }, { diff --git a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/CreateDestination.js b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/CreateDestination.js index 42410832..99e9ee42 100644 --- a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/CreateDestination.js +++ b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/CreateDestination.js @@ -165,11 +165,11 @@ class CreateDestination extends React.Component { handleCancel = () => { const { edit, history } = this.props; - if (edit) { - history.goBack(); - } else { + // if (edit) { + // history.goBack(); + // } else { history.push('/destinations'); - } + // } }; render() { diff --git a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/destinationToFormik.js b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/destinationToFormik.js index f25785d1..3eb38acd 100644 --- a/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/destinationToFormik.js +++ b/web/src/pages/Alerting/pages/Destinations/containers/CreateDestination/utils/destinationToFormik.js @@ -62,7 +62,7 @@ const getSender = async (httpClient, id) => { const getEmailGroup = async (httpClient, id) => { try { - const response = await httpClient.get(`/alerting/destinations/email_groups/${id}`); + const response = await httpClient.get(`/alerting/email_groups/${id}`); if (response.ok) { return response.resp; } diff --git a/web/src/pages/Alerting/pages/Home/Home.js b/web/src/pages/Alerting/pages/Home/Home.js index 7e99af07..498fd4ba 100644 --- a/web/src/pages/Alerting/pages/Home/Home.js +++ b/web/src/pages/Alerting/pages/Home/Home.js @@ -97,7 +97,7 @@ export default class Home extends Component {
( @@ -121,7 +121,15 @@ export default class Home extends Component { /> )} /> - + { + return ( + + ) + }} + />
diff --git a/web/src/pages/Alerting/pages/MonitorDetails/components/MonitorHistory/TriggersTimeSeries/TriggersTimeSeries.js b/web/src/pages/Alerting/pages/MonitorDetails/components/MonitorHistory/TriggersTimeSeries/TriggersTimeSeries.js index 8da39768..d3904601 100644 --- a/web/src/pages/Alerting/pages/MonitorDetails/components/MonitorHistory/TriggersTimeSeries/TriggersTimeSeries.js +++ b/web/src/pages/Alerting/pages/MonitorDetails/components/MonitorHistory/TriggersTimeSeries/TriggersTimeSeries.js @@ -96,7 +96,7 @@ class TriggersTimeSeries extends Component { align={{ vertical: 'top', horizontal: 'auto' }} value={hints[currentTrigger.name]} format={formatTooltip} - style={{ title: { fontWeight: 'bold' } }} + style={{ title: { fontWeight: 'bold' }, backgroundColor: '#fff'}} /> ) : null} diff --git a/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorDetails.js b/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorDetails.js index c4e1c82d..8c80fa7a 100644 --- a/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorDetails.js +++ b/web/src/pages/Alerting/pages/MonitorDetails/containers/MonitorDetails.js @@ -178,7 +178,8 @@ export default class MonitorDetails extends Component { }; onCloseTrigger = () => { - this.props.history.push({ ...this.props.location, search: '' }); + const {pathname} = this.props.location; //important change + this.props.history.push({ pathname, search: '' }); this.setState({ triggerToEdit: null }); }; @@ -301,7 +302,7 @@ export default class MonitorDetails extends Component { - {detector ? ( + {/* {detector ? ( Created from detector:{' '} @@ -310,7 +311,7 @@ export default class MonitorDetails extends Component { - ) : null} + ) : null} */} diff --git a/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.js b/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.js index ae1661c8..c6d9026e 100644 --- a/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.js +++ b/web/src/pages/Alerting/pages/Monitors/containers/Monitors/Monitors.js @@ -249,7 +249,7 @@ export default class Monitors extends Component { const promises = Object.entries(monitorAlerts).map(([monitorId, alerts]) => httpClient - .post(`/alerting/monitors/${monitorId}/_acknowledge/alerts`, { + .post(`/alerting/_monitors/${monitorId}/_acknowledge/alerts`, { body: JSON.stringify({ alerts }), }) .then((resp) => {