alerting modify
This commit is contained in:
parent
1efca99090
commit
879f529514
35
api/init.go
35
api/init.go
|
@ -38,21 +38,22 @@ func Init(cfg *config.AppConfig) {
|
|||
ui.HandleUIMethod(api.POST, path.Join(pathPrefix, "index/:index"), handler.HandleCreateIndexAction)
|
||||
|
||||
//new api
|
||||
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/overview", alerting.GetAlertOverview)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/destinations/email_accounts", alerting.CreateEmailAccount)
|
||||
ui.HandleUIMethod(api.PUT, "/elasticsearch/:id/alerting/email_accounts/:emailAccountId", alerting.UpdateEmailAccount)
|
||||
ui.HandleUIMethod(api.DELETE, "/elasticsearch/:id/alerting/email_accounts/:emailAccountId", alerting.DeleteEmailAccount)
|
||||
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/destinations/email_accounts", alerting.GetEmailAccounts)
|
||||
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/email_accounts/:emailAccountId", alerting.GetEmailAccount)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/destinations/email_groups", alerting.CreateEmailGroup)
|
||||
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)
|
||||
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/destinations", alerting.GetDestinations)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/destinations", alerting.CreateDestination)
|
||||
ui.HandleUIMethod(api.PUT, "/elasticsearch/:id/alerting/destinations/:destinationId", alerting.UpdateDestination)
|
||||
ui.HandleUIMethod(api.DELETE, "/elasticsearch/:id/alerting/destinations/:destinationId", alerting.DeleteDestination)
|
||||
ui.HandleUIMethod(api.GET, path.Join(pathPrefix, "alerting/overview"), alerting.GetAlertOverview)
|
||||
ui.HandleUIMethod(api.GET, path.Join(pathPrefix, "alerting/overview/alerts"), alerting.GetAlerts)
|
||||
ui.HandleUIMethod(api.POST, path.Join(pathPrefix,"alerting/destinations/email_accounts"), alerting.CreateEmailAccount)
|
||||
ui.HandleUIMethod(api.PUT, path.Join(pathPrefix, "alerting/email_accounts/:emailAccountId"), alerting.UpdateEmailAccount)
|
||||
ui.HandleUIMethod(api.DELETE, path.Join(pathPrefix,"alerting/email_accounts/:emailAccountId"), alerting.DeleteEmailAccount)
|
||||
ui.HandleUIMethod(api.GET, path.Join(pathPrefix,"alerting/destinations/email_accounts"), alerting.GetEmailAccounts)
|
||||
ui.HandleUIMethod(api.GET, path.Join(pathPrefix,"alerting/email_accounts/:emailAccountId"), alerting.GetEmailAccount)
|
||||
ui.HandleUIMethod(api.POST, path.Join(pathPrefix,"alerting/destinations/email_groups"), alerting.CreateEmailGroup)
|
||||
ui.HandleUIMethod(api.GET, path.Join(pathPrefix,"alerting/destinations/email_groups"), alerting.GetEmailGroups)
|
||||
ui.HandleUIMethod(api.DELETE, path.Join(pathPrefix,"alerting/email_groups/:emailGroupId"), alerting.DeleteEmailGroup)
|
||||
ui.HandleUIMethod(api.PUT, path.Join(pathPrefix,"alerting/email_groups/:emailGroupId"), alerting.UpdateEmailGroup)
|
||||
ui.HandleUIMethod(api.GET, path.Join(pathPrefix,"alerting/email_groups/:emailGroupId"), alerting.GetEmailGroup)
|
||||
ui.HandleUIMethod(api.GET, path.Join(pathPrefix, "alerting/destinations"), alerting.GetDestinations)
|
||||
ui.HandleUIMethod(api.POST, path.Join(pathPrefix,"alerting/destinations"), alerting.CreateDestination)
|
||||
ui.HandleUIMethod(api.PUT, path.Join(pathPrefix,"alerting/destinations/:destinationId"), alerting.UpdateDestination)
|
||||
ui.HandleUIMethod(api.DELETE, path.Join(pathPrefix, "alerting/destinations/:destinationId"), alerting.DeleteDestination)
|
||||
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/monitors/:monitorID", alerting.GetMonitor)
|
||||
ui.HandleUIMethod(api.PUT, "/elasticsearch/:id/alerting/monitors/:monitorID", alerting.UpdateMonitor)
|
||||
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/monitors", alerting.GetMonitors)
|
||||
|
@ -60,11 +61,11 @@ func Init(cfg *config.AppConfig) {
|
|||
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.GET, "/elasticsearch/:id/alerting/_settings", alerting.GetSettings)
|
||||
ui.HandleUIMethod(api.GET, path.Join(pathPrefix,"alerting/_settings"), alerting.GetSettings)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_indices", alerting.GetIndices)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_aliases", alerting.GetAliases)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_mappings", alerting.GetMappings)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/monitors/_search", alerting.Search)
|
||||
ui.HandleUIMethod(api.POST, path.Join(pathPrefix, "alerting/_search"), alerting.Search)
|
||||
ui.HandleUIMethod(api.GET, "/elasticsearch/:id/alerting/alerts", alerting.GetAlerts)
|
||||
ui.HandleUIMethod(api.POST, "/elasticsearch/:id/alerting/_monitors/:monitorID/_acknowledge/alerts", alerting.AcknowledgeAlerts)
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ type EmailAction struct {
|
|||
|
||||
func (act *EmailAction) Execute() ([]byte, error){
|
||||
from := act.Sender.Email
|
||||
//Todo add password input in frontend?
|
||||
act.Sender.Host = strings.TrimSpace(act.Sender.Host)
|
||||
addr := fmt.Sprintf("%s:%d", act.Sender.Host, act.Sender.Port)
|
||||
msg := fmt.Sprintf("To: %s\r\nSubject: %s\r\n%s\r\n", strings.Join(act.Receiver, ","), act.Subject, act.Message)
|
||||
|
@ -41,6 +40,10 @@ func SendEmailOverTLS(addr string, auth smtp.Auth, from string, to []string, msg
|
|||
}
|
||||
|
||||
conn, err := tls.Dial("tcp", addr, tlsConfig)
|
||||
//dialer := &net.Dialer{
|
||||
// Timeout: time.Second * 15,
|
||||
//}
|
||||
//conn, err := tls.DialWithDialer(dialer, "tcp", addr, tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ func GetAlerts (w http.ResponseWriter, req *http.Request, ps httprouter.Params){
|
|||
}
|
||||
must := []IfaceMap{
|
||||
}
|
||||
if id != "_all" {
|
||||
if id != "" {
|
||||
must = append(must, IfaceMap{
|
||||
"match": IfaceMap{
|
||||
"cluster_id": id,
|
||||
|
|
|
@ -9,19 +9,12 @@ import (
|
|||
"infini.sh/framework/core/util"
|
||||
"infini.sh/search-center/model/alerting"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetDestination(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
|
||||
}
|
||||
|
||||
conf := getDefaultConfig()
|
||||
dstID := ps.ByName("destID")
|
||||
reqUrl := fmt.Sprintf("%s/%s/_doc/%s", conf.Endpoint, orm.GetIndexName(alerting.Config{}), dstID)
|
||||
res, err := doRequest(reqUrl, http.MethodGet, nil, nil)
|
||||
|
@ -45,12 +38,6 @@ func GetDestination(w http.ResponseWriter, req *http.Request, ps httprouter.Para
|
|||
}
|
||||
|
||||
func GetDestinations(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
|
||||
}
|
||||
var (
|
||||
from = getQueryParam(req, "from", "0")
|
||||
size = getQueryParam(req, "size", "20")
|
||||
|
@ -153,13 +140,6 @@ func GetDestinations(w http.ResponseWriter, req *http.Request, ps httprouter.Par
|
|||
}
|
||||
|
||||
func CreateDestination(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
|
||||
}
|
||||
|
||||
config := getDefaultConfig()
|
||||
destId := util.GetUUID()
|
||||
reqUrl := fmt.Sprintf("%s/%s/_doc/%s", config.Endpoint, orm.GetIndexName(alerting.Config{}), destId)
|
||||
|
@ -189,7 +169,6 @@ func CreateDestination(w http.ResponseWriter, req *http.Request, ps httprouter.P
|
|||
res, err := doRequest(reqUrl, http.MethodPost, map[string]string{
|
||||
"refresh": "wait_for",
|
||||
}, IfaceMap{
|
||||
"cluster_id": id,
|
||||
DESTINATION_FIELD: toSaveDest,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -217,13 +196,6 @@ func CreateDestination(w http.ResponseWriter, req *http.Request, ps httprouter.P
|
|||
}
|
||||
|
||||
func UpdateDestination(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
|
||||
}
|
||||
|
||||
destinationId := ps.ByName("destinationId")
|
||||
|
||||
config := getDefaultConfig()
|
||||
|
@ -254,7 +226,6 @@ func UpdateDestination(w http.ResponseWriter, req *http.Request, ps httprouter.P
|
|||
res, err := doRequest(reqUrl, http.MethodPut, map[string]string{
|
||||
"refresh": "wait_for",
|
||||
}, IfaceMap{
|
||||
"cluster_id": id,
|
||||
DESTINATION_FIELD: toSaveDest,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -278,13 +249,6 @@ func UpdateDestination(w http.ResponseWriter, req *http.Request, ps httprouter.P
|
|||
}
|
||||
|
||||
func DeleteDestination(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
|
||||
}
|
||||
|
||||
destinationId := ps.ByName("destinationId")
|
||||
|
||||
config := getDefaultConfig()
|
||||
|
@ -337,17 +301,6 @@ func getDefaultConfig() *elastic.ElasticsearchConfig {
|
|||
//}
|
||||
|
||||
func CreateEmailAccount(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
debug.PrintStack()
|
||||
}
|
||||
}()
|
||||
id := ps.ByName("id")
|
||||
conf := elastic.GetConfig(id)
|
||||
if conf == nil {
|
||||
writeError(w, errors.New("cluster not found"))
|
||||
return
|
||||
}
|
||||
config := getDefaultConfig()
|
||||
|
||||
reqUrl := fmt.Sprintf("%s/%s/_doc", config.Endpoint, orm.GetIndexName(alerting.Config{}))
|
||||
|
@ -363,7 +316,6 @@ func CreateEmailAccount(w http.ResponseWriter, req *http.Request, ps httprouter.
|
|||
"refresh": "wait_for",
|
||||
}, IfaceMap{
|
||||
EMAIL_ACCOUNT_FIELD: emailAccount,
|
||||
"cluster_id": id,
|
||||
})
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
|
@ -403,13 +355,6 @@ func CreateEmailAccount(w http.ResponseWriter, req *http.Request, ps httprouter.
|
|||
}
|
||||
|
||||
func UpdateEmailAccount(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("emailAccountId")
|
||||
config := getDefaultConfig()
|
||||
|
||||
|
@ -424,7 +369,6 @@ func UpdateEmailAccount(w http.ResponseWriter, req *http.Request, ps httprouter.
|
|||
res, err := doRequest(reqUrl, http.MethodPut, map[string]string{
|
||||
"refresh": "wait_for",
|
||||
}, IfaceMap{
|
||||
"cluster_id": id,
|
||||
EMAIL_ACCOUNT_FIELD: emailAccount,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -448,13 +392,6 @@ func UpdateEmailAccount(w http.ResponseWriter, req *http.Request, ps httprouter.
|
|||
}
|
||||
|
||||
func DeleteEmailAccount(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("emailAccountId")
|
||||
config := getDefaultConfig()
|
||||
|
||||
|
@ -487,12 +424,6 @@ func DeleteEmailAccount(w http.ResponseWriter, req *http.Request, ps httprouter.
|
|||
}
|
||||
|
||||
func GetEmailAccounts(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
|
||||
}
|
||||
var (
|
||||
from = getQueryParam(req, "from", "0")
|
||||
size = getQueryParam(req, "size", "20")
|
||||
|
@ -577,13 +508,6 @@ func GetEmailAccounts(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
|
|||
}
|
||||
|
||||
func GetEmailAccount(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("emailAccountId")
|
||||
|
||||
config := getDefaultConfig()
|
||||
|
@ -621,13 +545,6 @@ func GetEmailAccount(w http.ResponseWriter, req *http.Request, ps httprouter.Par
|
|||
// --- email group
|
||||
|
||||
func CreateEmailGroup(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
|
||||
}
|
||||
|
||||
config := getDefaultConfig()
|
||||
reqUrl := fmt.Sprintf("%s/%s/_doc", config.Endpoint, orm.GetIndexName(alerting.Config{}))
|
||||
var emailGroup = &alerting.EmailGroup{}
|
||||
|
@ -639,7 +556,6 @@ func CreateEmailGroup(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
|
|||
res, err := doRequest(reqUrl, http.MethodPost, map[string]string{
|
||||
"refresh": "wait_for",
|
||||
}, IfaceMap{
|
||||
"cluster_id": id,
|
||||
EMAIL_GROUP_FIELD: emailGroup,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -670,13 +586,6 @@ func CreateEmailGroup(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
|
|||
}
|
||||
|
||||
func UpdateEmailGroup(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
|
||||
}
|
||||
|
||||
emailGroupId := ps.ByName("emailGroupId")
|
||||
config := getDefaultConfig()
|
||||
reqUrl := fmt.Sprintf("%s/%s/_doc/%s", config.Endpoint, orm.GetIndexName(alerting.Config{}), emailGroupId)
|
||||
|
@ -689,7 +598,6 @@ func UpdateEmailGroup(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
|
|||
res, err := doRequest(reqUrl, http.MethodPut, map[string]string{
|
||||
"refresh": "wait_for",
|
||||
}, IfaceMap{
|
||||
"cluster_id": id,
|
||||
EMAIL_GROUP_FIELD: emailGroup,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -712,13 +620,6 @@ func UpdateEmailGroup(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
|
|||
}
|
||||
|
||||
func DeleteEmailGroup(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
|
||||
}
|
||||
|
||||
emailGroupId := ps.ByName("emailGroupId")
|
||||
config := getDefaultConfig()
|
||||
reqUrl := fmt.Sprintf("%s/%s/_doc/%s", config.Endpoint, orm.GetIndexName(alerting.Config{}), emailGroupId)
|
||||
|
@ -749,12 +650,6 @@ func DeleteEmailGroup(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
|
|||
}
|
||||
|
||||
func GetEmailGroups(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
|
||||
}
|
||||
var (
|
||||
from = getQueryParam(req, "from", "0")
|
||||
size = getQueryParam(req, "size", "20")
|
||||
|
@ -838,13 +733,6 @@ func GetEmailGroups(w http.ResponseWriter, req *http.Request, ps httprouter.Para
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
emailGroupId := ps.ByName("emailGroupId")
|
||||
|
||||
config := getDefaultConfig()
|
||||
|
|
|
@ -17,12 +17,6 @@ type SearchBody struct {
|
|||
}
|
||||
|
||||
func Search(w http.ResponseWriter, req *http.Request, ps httprouter.Params){
|
||||
id := ps.ByName("id")
|
||||
meta := elastic.GetMetadata(id)
|
||||
if meta == nil {
|
||||
writeError(w, errors.New("cluster not found"))
|
||||
return
|
||||
}
|
||||
var body = SearchBody{}
|
||||
err := decodeJSON(req.Body, &body)
|
||||
if err != nil {
|
||||
|
@ -175,15 +169,10 @@ func GetMappings(w http.ResponseWriter, req *http.Request, ps httprouter.Params)
|
|||
}
|
||||
|
||||
func GetSettings(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
||||
id := ps.ByName("id")
|
||||
meta := elastic.GetMetadata(id)
|
||||
if meta == nil {
|
||||
writeError(w, errors.New("cluster not found"))
|
||||
return
|
||||
}
|
||||
|
||||
// /_cluster/settings?include_defaults=true
|
||||
reqUrl := fmt.Sprintf("%s/_cluster/settings", meta.GetActiveEndpoint())
|
||||
config := getDefaultConfig()
|
||||
reqUrl := fmt.Sprintf("%s/_cluster/settings", config.Endpoint)
|
||||
res, err := doRequest(reqUrl, http.MethodGet, map[string]string{
|
||||
"include_defaults": "true",
|
||||
}, nil)
|
||||
|
|
|
@ -695,7 +695,7 @@ func ExecuteMonitor(w http.ResponseWriter, req *http.Request, ps httprouter.Para
|
|||
}
|
||||
}
|
||||
|
||||
period := alertUtil.GetMonitorPeriod(&periodStart, &monitor.Schedule)
|
||||
period := alertUtil.GetMonitorPeriod(periodStart, &monitor.Schedule)
|
||||
writeJSON(w, IfaceMap{
|
||||
"ok": true,
|
||||
"resp": IfaceMap{
|
||||
|
|
|
@ -3,6 +3,8 @@ package alerting
|
|||
import (
|
||||
"fmt"
|
||||
httprouter "infini.sh/framework/core/api/router"
|
||||
"infini.sh/framework/core/elastic"
|
||||
"infini.sh/framework/core/orm"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
@ -127,13 +129,35 @@ func getTopTenAlertCluster()(interface{}, error){
|
|||
}
|
||||
}
|
||||
}
|
||||
//reqBody = IfaceMap{
|
||||
// "query": IfaceMap{
|
||||
// "terms": IfaceMap{
|
||||
// "_id": clusterIDs,
|
||||
// },
|
||||
// },
|
||||
//}
|
||||
reqBody = IfaceMap{
|
||||
"_source": "name",
|
||||
"query": IfaceMap{
|
||||
"terms": IfaceMap{
|
||||
"_id": clusterIDs,
|
||||
},
|
||||
},
|
||||
}
|
||||
config := getDefaultConfig()
|
||||
reqUrl := fmt.Sprintf("%s/%s/_search", config.Endpoint, orm.GetIndexName(elastic.ElasticsearchConfig{}))
|
||||
res, err := doRequest(reqUrl, http.MethodGet, nil, reqBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var resBody = &elastic.SearchResponse{}
|
||||
err = decodeJSON(res.Body, resBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Body.Close()
|
||||
clusterMap := IfaceMap{}
|
||||
for _, hit := range resBody.Hits.Hits {
|
||||
clusterMap[hit.ID.(string)] = hit.Source["name"]
|
||||
}
|
||||
for _, d := range metricData {
|
||||
if name, ok := clusterMap[d["x"].(string)]; ok {
|
||||
d["x"] = name
|
||||
}
|
||||
}
|
||||
return metricData, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"infini.sh/framework/core/orm"
|
||||
"infini.sh/search-center/model/alerting"
|
||||
"infini.sh/search-center/service/alerting/action"
|
||||
"infini.sh/search-center/service/alerting/util"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -148,12 +149,17 @@ type MonitorJob func()
|
|||
func generateMonitorJob(smt *ScheduleMonitor) MonitorJob{
|
||||
sm := *smt
|
||||
return func() {
|
||||
startTime := time.Now()
|
||||
queryResult, err := getQueryResult(sm.ClusterID, &sm.Monitor.Inputs[0])
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
periods := util.GetMonitorPeriod(startTime, &sm.Monitor.Schedule)
|
||||
for _, trigger := range sm.Monitor.Triggers {
|
||||
monitorCtx, err := createMonitorContext(&trigger, queryResult, &sm, IfaceMap{})
|
||||
monitorCtx, err := createMonitorContext(&trigger, queryResult, &sm, IfaceMap{
|
||||
"periodStart": periods.Start,
|
||||
"periodEnd": periods.End,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
|
@ -496,17 +502,17 @@ func resolveMessage(messageTemplate IfaceMap, monitorCtx []byte ) ([]byte, error
|
|||
}
|
||||
|
||||
func createMonitorContext(trigger *alerting.Trigger, result IfaceMap, smt *ScheduleMonitor, extra IfaceMap) ([]byte, error){
|
||||
ctx := IfaceMap{
|
||||
"_ctx": IfaceMap{
|
||||
"results": []interface{}{
|
||||
result,
|
||||
},
|
||||
"trigger": trigger,
|
||||
"monitor": smt.Monitor,
|
||||
"cluster_id": smt.ClusterID,
|
||||
"periodStart": "",
|
||||
"periodEnd":"",
|
||||
params := IfaceMap{
|
||||
"results": []interface{}{
|
||||
result,
|
||||
},
|
||||
"trigger": trigger,
|
||||
"monitor": smt.Monitor,
|
||||
"cluster_id": smt.ClusterID,
|
||||
}
|
||||
assignTo(params, extra)
|
||||
ctx := IfaceMap{
|
||||
"_ctx": params,
|
||||
}
|
||||
return json.Marshal(ctx)
|
||||
}
|
||||
|
|
|
@ -8,11 +8,11 @@ import (
|
|||
)
|
||||
|
||||
type MonitorPeriod struct {
|
||||
Start int64
|
||||
End int64
|
||||
Start time.Time
|
||||
End time.Time
|
||||
}
|
||||
|
||||
func GetMonitorPeriod(currentTime *time.Time, schedule *alerting.Schedule) *MonitorPeriod{
|
||||
func GetMonitorPeriod(currentTime time.Time, schedule *alerting.Schedule) *MonitorPeriod{
|
||||
if schedule.Period != nil {
|
||||
return transformPeriod(currentTime, schedule.Period)
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ func GetMonitorPeriod(currentTime *time.Time, schedule *alerting.Schedule) *Moni
|
|||
}
|
||||
|
||||
|
||||
func transformCron(currentTime *time.Time, cron *alerting.Cron) *MonitorPeriod {
|
||||
func transformCron(currentTime time.Time, cron *alerting.Cron) *MonitorPeriod {
|
||||
timezone := ""
|
||||
if cron.Timezone != "" {
|
||||
timezone = fmt.Sprintf("CRON_TZ=%s ", cron.Timezone)
|
||||
|
@ -39,7 +39,7 @@ func transformCron(currentTime *time.Time, cron *alerting.Cron) *MonitorPeriod {
|
|||
if ssd.Hour == 1 {
|
||||
duration = time.Hour
|
||||
}
|
||||
tempTime := *currentTime
|
||||
tempTime := currentTime
|
||||
nextTime := sd.Next(tempTime)
|
||||
var preTime = tempTime
|
||||
for {
|
||||
|
@ -49,18 +49,18 @@ func transformCron(currentTime *time.Time, cron *alerting.Cron) *MonitorPeriod {
|
|||
}
|
||||
}
|
||||
mp := &MonitorPeriod{
|
||||
Start: preTime.UnixNano()/1e6,
|
||||
End: currentTime.UnixNano()/1e6,
|
||||
Start: preTime,
|
||||
End: currentTime,
|
||||
}
|
||||
return mp
|
||||
}
|
||||
|
||||
func transformPeriod(currentTime *time.Time, period *alerting.Period) *MonitorPeriod {
|
||||
func transformPeriod(currentTime time.Time, period *alerting.Period) *MonitorPeriod {
|
||||
if period == nil {
|
||||
return nil
|
||||
}
|
||||
mp := &MonitorPeriod{
|
||||
End: currentTime.UnixNano()/1e6,
|
||||
End: currentTime,
|
||||
}
|
||||
var duration time.Duration
|
||||
switch period.Unit {
|
||||
|
@ -73,6 +73,6 @@ func transformPeriod(currentTime *time.Time, period *alerting.Period) *MonitorPe
|
|||
default:
|
||||
return nil
|
||||
}
|
||||
mp.Start = currentTime.Add(-duration * time.Duration(period.Interval)).UnixNano()/1e6
|
||||
mp.Start = currentTime.Add(-duration * time.Duration(period.Interval))
|
||||
return mp
|
||||
}
|
|
@ -73,6 +73,11 @@ export default {
|
|||
changeOrigin: true,
|
||||
// pathRewrite: { '^/server': '' },
|
||||
},
|
||||
'/_search-center/': {
|
||||
target: 'http://localhost:9000',
|
||||
changeOrigin: true,
|
||||
// pathRewrite: { '^/server': '' },
|
||||
},
|
||||
},
|
||||
ignoreMomentLocale: true,
|
||||
lessLoaderOptions: {
|
||||
|
|
|
@ -106,13 +106,31 @@ export default [
|
|||
|
||||
//alerting
|
||||
{
|
||||
routes:[
|
||||
{ path: '/', redirect: '/' },
|
||||
],
|
||||
path: '/alerting',
|
||||
name: 'alerting',
|
||||
icon: 'alert',
|
||||
component: './Alerting/index',
|
||||
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
|
||||
|
|
|
@ -68,6 +68,7 @@ export default class GlobalHeader extends PureComponent {
|
|||
payload:{
|
||||
history,
|
||||
pathname: history.location.pathname,
|
||||
isChangedState: true,
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
|
@ -68,6 +68,7 @@ export default {
|
|||
'form.button.add': 'Add',
|
||||
'form.button.edit': 'Edit',
|
||||
'form.button.update': 'Update',
|
||||
'form.button.save': 'Save',
|
||||
'form.button.delete': 'Delete',
|
||||
'form.button.cancel': 'Cancel',
|
||||
'form.button.collapse': 'Collapse',
|
||||
|
@ -91,6 +92,9 @@ export default {
|
|||
'menu.home': 'Home',
|
||||
'menu.devtool': 'CONSOLE',
|
||||
'menu.alerting': 'AERTING',
|
||||
'menu.alerting.overview': 'OVERVIEW',
|
||||
'menu.alerting.monitor': 'MONITORS',
|
||||
'menu.alerting.destination': 'DESTINATIONS',
|
||||
|
||||
'menu.cluster': 'CLUSTER',
|
||||
'menu.cluster.overview': 'OVERVIEW',
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
export default {
|
||||
'alert.overview.metric.active': 'ACTIVE',
|
||||
'alert.overview.metric.acknowledged': 'ACKNOWLEDGED',
|
||||
'alert.overview.metric.error': 'ERROR',
|
||||
'alert.overview.alertlist.title': 'Open Alerts',
|
||||
'alert.overview.alertlist-history.title': 'History Alerts',
|
||||
|
||||
'alert.button.acknowledge': 'Acknowledge',
|
||||
'alert.button.create-monitor': 'Create monitor',
|
||||
'alert.button.update-monitor': 'Update monitor',
|
||||
|
@ -79,6 +85,20 @@ export default {
|
|||
'alert.monitor.create.schedule.when-text': 'When do you want this monitor to run?',
|
||||
'alert.monitor.create.schedule.advise-text': 'Define how often the monitor collects data and determines how often you may receive alerts. We recommend two times of detector interval to avoid missing anomaly results due to any potential delay of processing time.',
|
||||
|
||||
'alert.monitor.overview.title': 'Overview',
|
||||
'alert.monitor.overview.header.state': 'State',
|
||||
'alert.monitor.overview.header.definition_type': 'Monitor definition type',
|
||||
'alert.monitor.overview.header.total_active_alerts': 'Total active alerts',
|
||||
'alert.monitor.overview.header.schedule': 'Schedule',
|
||||
'alert.monitor.overview.header.last_updated': 'Last updated',
|
||||
'alert.monitor.overview.header.monitor_id': 'Monitor ID',
|
||||
'alert.monitor.overview.header.version_number': 'Monitor version number',
|
||||
'alert.monitor.triggers.title': 'Triggers',
|
||||
'alert.monitor.triggers.table.header.name': 'Name',
|
||||
'alert.monitor.triggers.table.header.number_of_actions': 'Number of actions',
|
||||
'alert.monitor.triggers.table.header.severity': 'Severity',
|
||||
'alert.monitor.history.title': 'History',
|
||||
|
||||
'alert.destination': 'Destinations',
|
||||
'alert.destination.self': 'destination',
|
||||
'alert.destination.self-upper': 'Destination',
|
||||
|
@ -107,4 +127,43 @@ export default {
|
|||
|
||||
'alert.trigger': 'Triggers',
|
||||
'alert.trigger.self': 'trigger',
|
||||
'alert.trigger.create.title': 'Create trigger',
|
||||
'alert.trigger.edit.title': 'Edit trigger',
|
||||
'alert.trigger.edit.define.title': 'Define trigger',
|
||||
'alert.trigger.edit.define.field.name': 'Trigger name',
|
||||
'alert.trigger.edit.define.field.severity': 'Severity level',
|
||||
'alert.trigger.edit.define.field.query_response': 'Extraction query response',
|
||||
'alert.trigger.edit.define.field.condition': 'Trigger condition',
|
||||
'alert.trigger.edit.define.field.condition.info.paragraph1': 'You have access to a "_ctx" variable in your yaml scripts',
|
||||
'alert.trigger.edit.define.field.condition.info.paragraph2': `Below shows a quick JSON example of what's available under the "_ctx" variable along withthe actual results (where possible) for you to reference.`,
|
||||
'alert.trigger.edit.define.field.condition_response': 'Trigger condition response',
|
||||
'alert.trigger.edit.define.field.name.help': 'Trigger names must be unique. Names can only contain letters, numbers, and special characters.',
|
||||
'alert.trigger.edit.define.field.severity.help': 'Severity levels help you organize your triggers and actions. A trigger with a high severity level might page a specific individual, whereas a trigger with a low severity level might email a list.',
|
||||
'alert.trigger.edit.define.button.run': 'Run',
|
||||
'alert.trigger.edit.action.title': 'Configure actions',
|
||||
'alert.trigger.edit.action.button.add_action': 'Add action',
|
||||
'alert.trigger.edit.action.field.name': 'Action name',
|
||||
'alert.trigger.edit.action.field.destination': 'Destination',
|
||||
'alert.trigger.edit.action.field.message': 'Message',
|
||||
'alert.trigger.edit.action.field.message_subject': 'Message subject',
|
||||
'alert.trigger.edit.action.field.message_preview': 'Message preview',
|
||||
'alert.trigger.edit.action.send_test_message': 'Send test message',
|
||||
'alert.trigger.edit.action.default_name': 'Notification',
|
||||
'alert.email.manage.title': 'Manage email senders',
|
||||
'alert.email.manage.button.remove': 'Remove sender',
|
||||
'alert.email.manage.button.add': 'Add sender',
|
||||
'alert.email.manage.field.name': 'Sender name',
|
||||
'alert.email.manage.field.name.help': 'A unique and descriptive name that is easy to search. Valid characters are upper and lowercase a-z, 0-9, and _ (underscore).',
|
||||
'alert.email.manage.field.address': 'Email address',
|
||||
'alert.email.manage.field.password': 'Email password',
|
||||
'alert.email.manage.field.host': 'Host',
|
||||
'alert.email.manage.field.port': 'Port',
|
||||
'alert.email.manage.field.method': 'Encryption method',
|
||||
'alert.emailgroup.manage.title': 'Manage email groups',
|
||||
'alert.emailgroup.manage.button.remove': 'Remove email group',
|
||||
'alert.emailgroup.manage.button.add': 'Add email group',
|
||||
'alert.emailgroup.manage.field.name': 'Email group name',
|
||||
'alert.emailgroup.manage.field.name.help': 'A unique and descriptive name that is easy to search. Valid characters are upper and lowercase a-z, 0-9, and _ (underscore).',
|
||||
'alert.emailgroup.manage.field.emails': 'Emails',
|
||||
'alert.emailgroup.manage.field.emails.help': 'Search for previously used email addresses or type in new ones.',
|
||||
};
|
||||
|
|
|
@ -74,6 +74,7 @@ export default {
|
|||
'form.button.add': '添加',
|
||||
'form.button.edit': '编辑',
|
||||
'form.button.update': '更新',
|
||||
'form.button.save': '保存',
|
||||
'form.button.delete': '删除',
|
||||
'form.button.cancel': '取消',
|
||||
'form.button.collapse': '收起',
|
||||
|
@ -98,6 +99,9 @@ export default {
|
|||
'menu.home': '首页',
|
||||
'menu.devtool': '开发工具',
|
||||
'menu.alerting': '告警管理',
|
||||
'menu.alerting.overview': '概览',
|
||||
'menu.alerting.monitor': '监控管理',
|
||||
'menu.alerting.destination': '渠道管理',
|
||||
|
||||
'menu.cluster': '集群管理',
|
||||
'menu.cluster.overview': '概览',
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
export default {
|
||||
'alert.overview.metric.active': '激活告警',
|
||||
'alert.overview.metric.acknowledged': '已响应告警',
|
||||
'alert.overview.metric.error': '错误告警',
|
||||
'alert.overview.alertlist.title': '当前告警',
|
||||
'alert.overview.alertlist-history.title': '历史告警',
|
||||
|
||||
'alert.button.acknowledge': '确认',
|
||||
'alert.button.create-monitor': '创建监控项',
|
||||
'alert.button.update-monitor': '更新监控项',
|
||||
|
@ -79,6 +85,19 @@ export default {
|
|||
'alert.monitor.create.schedule.when-text': '您希望此监视器何时运行?',
|
||||
'alert.monitor.create.schedule.advise-text': '定义监视器收集数据的频率并确定您接收警报的频率。 我们建议两倍检测器间隔,以避免由于处理时间的任何潜在延迟而遗漏异常结果。',
|
||||
|
||||
'alert.monitor.overview.title': '概览',
|
||||
'alert.monitor.overview.header.state': '状态',
|
||||
'alert.monitor.overview.header.definition_type': '监控定义类型',
|
||||
'alert.monitor.overview.header.total_active_alerts': '激活告警数',
|
||||
'alert.monitor.overview.header.schedule': '监控计划',
|
||||
'alert.monitor.overview.header.last_updated': '最近更新时间',
|
||||
'alert.monitor.overview.header.monitor_id': '监控编号',
|
||||
'alert.monitor.overview.header.version_number': '版本',
|
||||
'alert.monitor.triggers.title': '触发器',
|
||||
'alert.monitor.triggers.table.header.name': '名称',
|
||||
'alert.monitor.triggers.table.header.number_of_actions': '通知渠道',
|
||||
'alert.monitor.triggers.table.header.severity': '告警等级',
|
||||
'alert.monitor.history.title': '告警历史',
|
||||
|
||||
'alert.destination': '通知渠道',
|
||||
'alert.destination.self': '通知渠道',
|
||||
|
@ -107,4 +126,43 @@ export default {
|
|||
'alert.destination.create.settings.attributes.label': '通过自定义属性 URL 定义端点',
|
||||
|
||||
'alert.trigger': '触发器',
|
||||
'alert.trigger.create.title': '创建触发器',
|
||||
'alert.trigger.edit.title': '修改触发器',
|
||||
'alert.trigger.edit.define.title': '定义触发器',
|
||||
'alert.trigger.edit.define.field.name': '名称',
|
||||
'alert.trigger.edit.define.field.severity': '告警等级',
|
||||
'alert.trigger.edit.define.field.query_response': '查询响应',
|
||||
'alert.trigger.edit.define.field.condition': '触发条件',
|
||||
'alert.trigger.edit.define.field.condition.info.paragraph1': '您可以在 yaml 脚本中访问 "_ctx" 变量',
|
||||
'alert.trigger.edit.define.field.condition.info.paragraph2': `以下是一个快速 JSON 示例,示例中列举了 "_ctx" 下的可访问变量 。`,
|
||||
'alert.trigger.edit.define.field.condition_response': '触发响应',
|
||||
'alert.trigger.edit.define.field.name.help': '名称必须唯一,只能包含字母,数字和特殊字符。',
|
||||
'alert.trigger.edit.define.field.severity.help':'告警等级帮助您管理触发器',
|
||||
'alert.trigger.edit.define.button.run': '运行',
|
||||
'alert.trigger.edit.action.title': '触发通知配置',
|
||||
'alert.trigger.edit.action.button.add_action': '添加通知',
|
||||
'alert.trigger.edit.action.field.name': '通知名称',
|
||||
'alert.trigger.edit.action.field.destination': '渠道选择',
|
||||
'alert.trigger.edit.action.field.message': '消息配置',
|
||||
'alert.trigger.edit.action.field.message_subject': '消息标题',
|
||||
'alert.trigger.edit.action.field.message_preview': '消息预览',
|
||||
'alert.trigger.edit.action.send_test_message': '发送测试消息',
|
||||
'alert.trigger.edit.action.default_name': '通知',
|
||||
'alert.email.manage.title': '管理邮件发送者',
|
||||
'alert.email.manage.button.remove': '删除',
|
||||
'alert.email.manage.button.add': '添加',
|
||||
'alert.email.manage.field.name': '名称',
|
||||
'alert.email.manage.field.name.help': '唯一的名称利于搜索,名称中只能包含大小写字母、数字和下划线',
|
||||
'alert.email.manage.field.address': '邮件地址',
|
||||
'alert.email.manage.field.password': '邮件密码',
|
||||
'alert.email.manage.field.host': '主机',
|
||||
'alert.email.manage.field.port': '端口',
|
||||
'alert.email.manage.field.method': '加密方式',
|
||||
'alert.emailgroup.manage.title': '管理邮件组',
|
||||
'alert.emailgroup.manage.button.remove': '删除',
|
||||
'alert.emailgroup.manage.button.add': '添加',
|
||||
'alert.emailgroup.manage.field.name': '名称',
|
||||
'alert.emailgroup.manage.field.name.help': '唯一的名称利于搜索,名称中只能包含大小写字母、数字和下划线',
|
||||
'alert.emailgroup.manage.field.emails': '邮件列表',
|
||||
'alert.emailgroup.manage.field.emails.help': '搜索先前用过的邮件或者输入新的邮件地址',
|
||||
};
|
||||
|
|
|
@ -7,7 +7,6 @@ import router from "umi/router";
|
|||
|
||||
const MENU_COLLAPSED_KEY = "search-center:menu:collapsed";
|
||||
|
||||
console.log(localStorage.getItem(MENU_COLLAPSED_KEY))
|
||||
export default {
|
||||
namespace: 'global',
|
||||
|
||||
|
@ -22,7 +21,7 @@ export default {
|
|||
search:{
|
||||
cluster: {
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
effects: {
|
||||
|
@ -130,7 +129,7 @@ export default {
|
|||
});
|
||||
},
|
||||
*rewriteURL({payload}, {select, put}){
|
||||
const {pathname, history, search} = payload;
|
||||
const {pathname, history, search, isChangedState} = payload;
|
||||
const global = yield select(state=>state.global);
|
||||
if(pathname && global.selectedClusterID){
|
||||
const newPart = `/elasticsearch/${global.selectedClusterID}/`;
|
||||
|
@ -139,7 +138,11 @@ export default {
|
|||
}else{
|
||||
const ms = pathname.match(/\/elasticsearch\/(\w+)\/?/);
|
||||
if(ms && ms.length>1 && ms[1] != global.selectedClusterID){
|
||||
console.log(ms[1])
|
||||
if(isChangedState){
|
||||
const newPath = pathname.replace(/\/elasticsearch\/(\w+)\/?/, newPart);
|
||||
history.replace(newPath+(search || ''))
|
||||
return
|
||||
}
|
||||
yield put({
|
||||
type: 'changeClusterById',
|
||||
payload:{
|
||||
|
@ -248,12 +251,21 @@ export default {
|
|||
// Subscribe history(url) change, trigger `load` action if pathname is `/`
|
||||
return history.listen(({ pathname, search }) => {
|
||||
let clusterVisible = true;
|
||||
if(pathname.startsWith("/system")){
|
||||
clusterVisible = false;
|
||||
}else if(pathname.startsWith("/cluster/overview")){
|
||||
const clusterHiddenPath = ["/system", "/cluster/overview", "/alerting/overview", "/alerting/monitor/monitors/", "/alerting/destination"];
|
||||
if(clusterHiddenPath.some(p=>pathname.startsWith(p))){
|
||||
clusterVisible = false;
|
||||
if(pathname.includes("elasticsearch")){
|
||||
dispatch({
|
||||
type: 'rewriteURL',
|
||||
payload: {
|
||||
pathname,
|
||||
history,
|
||||
search,
|
||||
}
|
||||
});
|
||||
}
|
||||
}else{
|
||||
if(!pathname.startsWith("/exception")){
|
||||
if(!pathname.startsWith("/exception") && pathname !="/alerting/monitor"){
|
||||
dispatch({
|
||||
type: 'rewriteURL',
|
||||
payload: {
|
||||
|
|
|
@ -34,16 +34,16 @@ const message = () => ({
|
|||
body: (
|
||||
<EuiText style={{ fontSize: '14px' }}>
|
||||
<p>
|
||||
{`You have access to a "ctx" variable in your painless scripts and action mustache templates.`}
|
||||
{`You have access to a "_ctx" variable in your yaml scripts and action quicktemplate templates.`}
|
||||
</p>
|
||||
<h3>Learn More</h3>
|
||||
{/* <h3>Learn More</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<EuiLink target="_blank" href={URL.MUSTACHE}>
|
||||
HTML Templates with Mustache.js
|
||||
</EuiLink>
|
||||
</li>
|
||||
</ul>
|
||||
</ul> */}
|
||||
</EuiText>
|
||||
),
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { EuiCodeBlock, EuiCodeEditor, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const CONTEXT_VARIABLES = JSON.stringify(
|
||||
{
|
||||
|
@ -40,17 +41,16 @@ const triggerCondition = context => ({
|
|||
header: (
|
||||
<EuiTitle size="m" style={{ fontSize: '25px' }}>
|
||||
<h2>
|
||||
<strong>Trigger condition</strong>
|
||||
<strong>{formatMessage({id:'alert.trigger.edit.define.field.condition'})}</strong>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
),
|
||||
body: (
|
||||
<div>
|
||||
<EuiText style={{ fontSize: '14px' }}>
|
||||
<p>You have access to a "ctx" variable in your painless scripts</p>
|
||||
<p>{formatMessage({id:'alert.trigger.edit.define.field.condition.info.paragraph1'})}</p>
|
||||
<p>
|
||||
Below shows a quick JSON example of what's available under the "ctx" variable along with
|
||||
the actual results (where possible) for you to reference.
|
||||
{formatMessage({id:'alert.trigger.edit.define.field.condition.info.paragraph2'})}
|
||||
</p>
|
||||
</EuiText>
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import React, { Component, useMemo } from 'react';
|
||||
import { Switch, Route, Router } from 'react-router-dom';
|
||||
import CreateDestination from './pages/Destinations/containers/CreateDestination';
|
||||
import DestinationsList from './pages/Destinations/containers/DestinationsList';
|
||||
import {Fetch} from '../../components/kibana/core/public/http/fetch';
|
||||
import {ScopedHistory} from '../../components/kibana/core/public/application/scoped_history';
|
||||
import {notification} from 'antd';
|
||||
|
||||
const Destination = ({httpClient, notifications, history})=> {
|
||||
return (
|
||||
<div style={{ padding: '15px', background:'#fff' }}>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route
|
||||
path='/create-destination'
|
||||
render={(props) => (
|
||||
<CreateDestination
|
||||
httpClient={httpClient}
|
||||
notifications={notifications}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path="/destinations/:destinationId"
|
||||
render={(props) => (
|
||||
<CreateDestination
|
||||
httpClient={httpClient}
|
||||
notifications={notifications}
|
||||
{...props}
|
||||
edit
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
// exact
|
||||
// path="/destinations"
|
||||
render={(props) => {
|
||||
return (
|
||||
<DestinationsList
|
||||
{...props}
|
||||
httpClient={httpClient}
|
||||
notifications={notifications}
|
||||
/>
|
||||
)}}
|
||||
/>
|
||||
</Switch>
|
||||
</Router>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const httpClient = new Fetch({
|
||||
basePath:{
|
||||
get: () => '',
|
||||
prepend: (url) => url,
|
||||
remove: (url) => url,
|
||||
serverBasePath: '',
|
||||
}
|
||||
});
|
||||
const notifications = {
|
||||
toasts: {
|
||||
addDanger: ({title, text, toastLifeTimeMs})=>{
|
||||
notification.warning({
|
||||
message: title,
|
||||
description: text,
|
||||
duration: toastLifeTimeMs/1000,
|
||||
})
|
||||
},
|
||||
addSuccess: (message) => {
|
||||
notification.success({
|
||||
description: message,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default (props)=>{
|
||||
const isDarkMode = false;
|
||||
const history = useMemo(()=>{
|
||||
return new ScopedHistory(props.history, '/alerting/destination');
|
||||
}, [props.history])
|
||||
|
||||
return (
|
||||
<Destination httpClient={httpClient} notifications={notifications} history={history} />
|
||||
)
|
||||
}
|
|
@ -45,7 +45,7 @@ const AlertingUI = (props)=>{
|
|||
}, [props.selectedCluster]);
|
||||
const isDarkMode = false;
|
||||
const history = useMemo(()=>{
|
||||
return new ScopedHistory(props.history, '/alerting');
|
||||
return new ScopedHistory(props.history, '/alerting/monitor');
|
||||
}, [props.history])
|
||||
|
||||
return (
|
||||
|
|
|
@ -21,6 +21,7 @@ import { isInvalid, hasError, validateActionName } from '../../../../utils/valid
|
|||
import { ActionsMap } from './utils/constants';
|
||||
import { validateDestination } from './utils/validate';
|
||||
import { DEFAULT_ACTION_TYPE } from '../../utils/constants';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const Action = ({
|
||||
action,
|
||||
|
@ -44,12 +45,12 @@ const Action = ({
|
|||
className="accordion-action"
|
||||
buttonContent={
|
||||
!_.get(selectedDestination, '0.type', undefined)
|
||||
? 'Notification'
|
||||
? formatMessage({id:'alert.trigger.edit.action.default_name'})
|
||||
: `${actionLabel}: ${name}`
|
||||
}
|
||||
extraAction={
|
||||
<div style={{ paddingRight: '10px' }}>
|
||||
<EuiButton onClick={onDelete}>Delete</EuiButton>
|
||||
<EuiButton onClick={onDelete}>{formatMessage({id:'form.button.delete'})}</EuiButton>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
@ -58,9 +59,9 @@ const Action = ({
|
|||
<FormikFieldText
|
||||
name={`actions.${index}.name`}
|
||||
formRow
|
||||
fieldProps={{ validate: validateActionName(context.ctx.trigger) }}
|
||||
fieldProps={{ validate: validateActionName(context._ctx.trigger) }}
|
||||
rowProps={{
|
||||
label: 'Action name',
|
||||
label: formatMessage({id:'alert.trigger.edit.action.field.name'}),
|
||||
helpText: 'Names can only contain letters, numbers, and special characters',
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
|
@ -72,7 +73,7 @@ const Action = ({
|
|||
formRow
|
||||
fieldProps={{ validate: validateDestination(destinations) }}
|
||||
rowProps={{
|
||||
label: 'Destination',
|
||||
label: formatMessage({id:'alert.trigger.edit.action.field.destination'}),
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
}}
|
||||
|
|
|
@ -40,14 +40,15 @@ import {
|
|||
required,
|
||||
} from '../../../../../utils/validate';
|
||||
import { URL, MAX_THROTTLE_VALUE, WRONG_THROTTLE_WARNING } from '../../../../../utils/constants';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const messageHelpText = (index, sendTestMessage) => (
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
|
||||
<EuiFlexItem>
|
||||
<EuiText size="xs">
|
||||
{/* <EuiText size="xs">
|
||||
Embed variables in your message using Mustache templates.{' '}
|
||||
<a href={URL.MUSTACHE}>Learn more about Mustache</a>
|
||||
</EuiText>
|
||||
</EuiText> */}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="xs">
|
||||
|
@ -56,7 +57,7 @@ const messageHelpText = (index, sendTestMessage) => (
|
|||
sendTestMessage(index);
|
||||
}}
|
||||
>
|
||||
Send test message
|
||||
{formatMessage({id:'alert.trigger.edit.action.send_test_message'})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
|
@ -76,7 +77,7 @@ const Message = ({
|
|||
preview = Mustache.render(action.message_template.source, context);
|
||||
} catch (err) {
|
||||
preview = err.message;
|
||||
console.error('There was an error rendering mustache template', err);
|
||||
console.error('There was an error rendering template', err);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
|
@ -86,7 +87,7 @@ const Message = ({
|
|||
formRow
|
||||
fieldProps={{ validate: required }}
|
||||
rowProps={{
|
||||
label: 'Message subject',
|
||||
label: formatMessage({id:'alert.trigger.edit.action.field.message_subject'}),
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
}}
|
||||
|
@ -100,7 +101,7 @@ const Message = ({
|
|||
rowProps={{
|
||||
label: (
|
||||
<div>
|
||||
<span>Message</span>
|
||||
<span>{formatMessage({id:'alert.trigger.edit.action.field.message'})}</span>
|
||||
<EuiButtonEmpty
|
||||
size="s"
|
||||
onClick={() => {
|
||||
|
@ -117,15 +118,15 @@ const Message = ({
|
|||
error: hasError,
|
||||
}}
|
||||
inputProps={{
|
||||
placeholder: 'Can use mustache templates',
|
||||
placeholder: 'Can use templates',
|
||||
fullWidth: true,
|
||||
isInvalid,
|
||||
}}
|
||||
/>
|
||||
|
||||
<EuiFormRow label="Message preview" style={{ maxWidth: '100%' }}>
|
||||
<EuiFormRow label={formatMessage({id:'alert.trigger.edit.action.field.message_preview'})} style={{ maxWidth: '100%' }}>
|
||||
<EuiTextArea
|
||||
placeholder="Preview of mustache template"
|
||||
placeholder="Preview of template"
|
||||
fullWidth
|
||||
value={preview}
|
||||
readOnly
|
||||
|
@ -133,7 +134,7 @@ const Message = ({
|
|||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
{/* <EuiSpacer size="s" />
|
||||
|
||||
<EuiFormRow
|
||||
label={
|
||||
|
@ -201,7 +202,7 @@ const Message = ({
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
</EuiFormRow> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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 = (
|
||||
<EuiButton fill href={`#/${PLUGIN_NAME}/create-destination`}>
|
||||
<EuiButton fill href={`#/alerting/destination/create-destination`}>
|
||||
Add destination
|
||||
</EuiButton>
|
||||
);
|
||||
|
|
|
@ -18,10 +18,11 @@ import _ from 'lodash';
|
|||
import { EuiButton } from '@elastic/eui';
|
||||
|
||||
import { FORMIK_INITIAL_ACTION_VALUES } from '../../utils/constants';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const AddActionButton = ({ arrayHelpers, type = 'slack' }) => (
|
||||
<EuiButton fill onClick={() => arrayHelpers.unshift(_.cloneDeep(FORMIK_INITIAL_ACTION_VALUES))}>
|
||||
Add action
|
||||
{formatMessage({id:'alert.trigger.edit.action.button.add_action'})}
|
||||
</EuiButton>
|
||||
);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import 'brace/mode/plain_text';
|
|||
import 'brace/snippets/javascript';
|
||||
import 'brace/ext/language_tools';
|
||||
import { formikToTrigger } from '../../containers/CreateTrigger/utils/formikToTrigger';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
export const getExecuteMessage = (response) => {
|
||||
if (!response) return 'No response';
|
||||
|
@ -59,7 +60,7 @@ const TriggerQuery = ({
|
|||
<div style={{ padding: '0px 10px', marginTop: '0px' }}>
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow label="Extraction query response" fullWidth>
|
||||
<EuiFormRow label={formatMessage({id:'alert.trigger.edit.define.field.query_response'})} fullWidth>
|
||||
<EuiCodeEditor
|
||||
mode="json"
|
||||
theme={isDarkMode ? 'sense-dark' : 'github'}
|
||||
|
@ -75,7 +76,7 @@ const TriggerQuery = ({
|
|||
<EuiFormRow
|
||||
label={
|
||||
<div>
|
||||
<span>Trigger condition</span>
|
||||
<span>{formatMessage({id:'alert.trigger.edit.define.field.condition'})}</span>
|
||||
<EuiButtonEmpty
|
||||
size="s"
|
||||
onClick={() => {
|
||||
|
@ -108,7 +109,7 @@ const TriggerQuery = ({
|
|||
</Field>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow label="Trigger condition response:" fullWidth>
|
||||
<EuiFormRow label={formatMessage({id:'alert.trigger.edit.define.field.condition_response'})} fullWidth>
|
||||
<EuiCodeEditor
|
||||
mode="plain_text"
|
||||
theme={isDarkMode ? 'sense-dark' : 'github'}
|
||||
|
@ -125,7 +126,7 @@ const TriggerQuery = ({
|
|||
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton onClick={() => onRun([trigger])}>Run</EuiButton>
|
||||
<EuiButton onClick={() => onRun([trigger])}>{formatMessage({id:'alert.trigger.edit.define.button.run'})}</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</div>
|
||||
|
|
|
@ -24,9 +24,11 @@ import { DESTINATION_OPTIONS } from '../../../Destinations/utils/constants';
|
|||
import { getAllowList } from '../../../Destinations/utils/helpers';
|
||||
import { MAX_QUERY_RESULT_SIZE } from '../../../../utils/constants';
|
||||
import { backendErrorNotification } from '../../../../utils/helpers';
|
||||
import {pathPrefix} from '@/services/common'
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const createActionContext = (context, action) => ({
|
||||
ctx: {
|
||||
_ctx: {
|
||||
...context,
|
||||
action,
|
||||
},
|
||||
|
@ -62,8 +64,9 @@ class ConfigureActions extends React.Component {
|
|||
return destination.type;
|
||||
};
|
||||
try {
|
||||
const response = await httpClient.get('/alerting/destinations', {
|
||||
const response = await httpClient.get(pathPrefix +'/alerting/destinations', {
|
||||
query: { search: searchText, size: MAX_QUERY_RESULT_SIZE },
|
||||
prependBasePath: false,
|
||||
});
|
||||
if (response.ok) {
|
||||
const destinations = response.destinations
|
||||
|
@ -143,7 +146,7 @@ class ConfigureActions extends React.Component {
|
|||
//TODO:: Handle loading Destinations inside the Action which will be more intuitive for customers.
|
||||
return (
|
||||
<ContentPanel
|
||||
title="Configure actions"
|
||||
title={formatMessage({id:'alert.trigger.edit.action.title'})}
|
||||
titleSize="s"
|
||||
panelStyles={{ paddingBottom: '0px' }}
|
||||
bodyStyles={{ padding: '10px' }}
|
||||
|
|
|
@ -42,6 +42,7 @@ import { FORMIK_INITIAL_VALUES } from './utils/constants';
|
|||
import { SEARCH_TYPE } from '../../../../utils/constants';
|
||||
import { SubmitErrorHandler } from '../../../../utils/SubmitErrorHandler';
|
||||
import { backendErrorNotification } from '../../../../utils/helpers';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
export default class CreateTrigger extends Component {
|
||||
constructor(props) {
|
||||
|
@ -201,7 +202,7 @@ export default class CreateTrigger extends Component {
|
|||
{({ values, handleSubmit, isSubmitting, errors, isValid }) => (
|
||||
<Fragment>
|
||||
<EuiTitle size="l">
|
||||
<h1>{edit ? 'Edit' : 'Create'} trigger</h1>
|
||||
<h1>{edit ? formatMessage({id:'alert.trigger.edit.title'}):formatMessage({id:'alert.trigger.create.title'})}</h1>
|
||||
</EuiTitle>
|
||||
<EuiSpacer />
|
||||
<DefineTrigger
|
||||
|
@ -230,11 +231,11 @@ export default class CreateTrigger extends Component {
|
|||
<EuiSpacer />
|
||||
<EuiFlexGroup alignItems="center" justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={onCloseTrigger}>Cancel</EuiButtonEmpty>
|
||||
<EuiButtonEmpty onClick={onCloseTrigger}>{formatMessage({id:'form.button.cancel'})}</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton onClick={handleSubmit} isLoading={isSubmitting} fill>
|
||||
{edit ? 'Update' : 'Create'}
|
||||
{edit ? formatMessage({id:'form.button.update'}) : formatMessage({id:'form.button.create'})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -26,10 +26,11 @@ import { validateTriggerName } from './utils/validation';
|
|||
import { SEARCH_TYPE } from '../../../../utils/constants';
|
||||
import { AnomalyDetectorTrigger } from './AnomalyDetectorTrigger';
|
||||
import { TRIGGER_TYPE } from '../CreateTrigger/utils/constants';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const defaultRowProps = {
|
||||
label: 'Trigger name',
|
||||
helpText: `Trigger names must be unique. Names can only contain letters, numbers, and special characters.`,
|
||||
label: formatMessage({id:"alert.trigger.edit.define.field.name"}),
|
||||
helpText: formatMessage({id:"alert.trigger.edit.define.field.name.help"}),
|
||||
style: { paddingLeft: '10px' },
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
|
@ -38,8 +39,8 @@ const defaultInputProps = { isInvalid };
|
|||
|
||||
const selectFieldProps = { validate: () => {} };
|
||||
const selectRowProps = {
|
||||
label: 'Severity level',
|
||||
helpText: `Severity levels help you organize your triggers and actions. A trigger with a high severity level might page a specific individual, whereas a trigger with a low severity level might email a list.`,
|
||||
label: formatMessage({id:"alert.trigger.edit.define.field.severity"}),
|
||||
helpText: formatMessage({id:'alert.trigger.edit.define.field.severity.help'}),
|
||||
style: { paddingLeft: '10px', marginTop: '0px' },
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
|
@ -119,7 +120,7 @@ const DefineTrigger = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<ContentPanel title="Define trigger" titleSize="s" bodyStyles={{ padding: 'initial' }}>
|
||||
<ContentPanel title={formatMessage({id:"alert.trigger.edit.define.title"})} titleSize="s" bodyStyles={{ padding: 'initial' }}>
|
||||
<FormikFieldText
|
||||
name="name"
|
||||
fieldProps={{ validate: validateTriggerName(triggers, triggerValues) }}
|
||||
|
|
|
@ -14,22 +14,22 @@
|
|||
*/
|
||||
|
||||
const DEFAULT_MESSAGE_SOURCE = `
|
||||
Monitor {{ctx.monitor.name}} just entered alert status. Please investigate the issue.
|
||||
- Trigger: {{ctx.trigger.name}}
|
||||
- Severity: {{ctx.trigger.severity}}
|
||||
- Period start: {{ctx.periodStart}}
|
||||
- Period end: {{ctx.periodEnd}}
|
||||
Monitor {{_ctx.monitor.name}} just entered alert status. Please investigate the issue.
|
||||
- Trigger: {{_ctx.trigger.name}}
|
||||
- Severity: {{_ctx.trigger.severity}}
|
||||
- Period start: {{_ctx.periodStart}}
|
||||
- Period end: {{_ctx.periodEnd}}
|
||||
`.trim();
|
||||
|
||||
export const FORMIK_INITIAL_ACTION_VALUES = {
|
||||
name: '',
|
||||
destination_id: '',
|
||||
subject_template: {
|
||||
lang: 'mustache',
|
||||
lang: 'quicktemplate',
|
||||
source: '',
|
||||
},
|
||||
message_template: {
|
||||
lang: 'mustache',
|
||||
lang: 'quicktemplate',
|
||||
source: DEFAULT_MESSAGE_SOURCE,
|
||||
},
|
||||
throttle_enabled: false,
|
||||
|
@ -39,4 +39,4 @@ export const FORMIK_INITIAL_ACTION_VALUES = {
|
|||
},
|
||||
};
|
||||
|
||||
export const DEFAULT_ACTION_TYPE = 'slack';
|
||||
export const DEFAULT_ACTION_TYPE = 'custom_webhook';
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import {List} from 'antd';
|
||||
import {AlertItem, AlertRecord} from './AlertItem';
|
||||
import './alertlist.scss';
|
||||
import {Legend, LegendItem} from './Legend';
|
||||
|
||||
interface AlertListProps {
|
||||
dataSource: AlertRecord[];
|
||||
pagination?: any;
|
||||
title: string;
|
||||
onItemClick: (item: AlertRecord)=>void
|
||||
onItemClick: (item: AlertRecord)=>void;
|
||||
legendItems?: LegendItem[];
|
||||
}
|
||||
|
||||
export const AlertList = ({
|
||||
|
@ -14,13 +16,22 @@ export const AlertList = ({
|
|||
pagination,
|
||||
title,
|
||||
onItemClick,
|
||||
legendItems
|
||||
}: AlertListProps)=>{
|
||||
return (
|
||||
<div className="alert-list">
|
||||
<div className="title">
|
||||
{title}
|
||||
<span className="total">({pagination?.total})</span>
|
||||
</div>
|
||||
<div className="header">
|
||||
<div className="title">
|
||||
{title}
|
||||
<span className="total">({pagination?.total})</span>
|
||||
</div>
|
||||
{
|
||||
legendItems ? ( <div className="legend">
|
||||
<Legend items={legendItems}/>
|
||||
</div>):null
|
||||
}
|
||||
|
||||
</div>
|
||||
<List
|
||||
itemLayout="vertical"
|
||||
size="large"
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import './legend.scss';
|
||||
export interface LegendItem {
|
||||
color: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
interface LegendProps{
|
||||
items: LegendItem[];
|
||||
}
|
||||
|
||||
export const Legend = ({
|
||||
items
|
||||
}:LegendProps)=>{
|
||||
return (
|
||||
<div className="legend-list">
|
||||
{(items || []).map(item=>{
|
||||
return <div className="legend-item">
|
||||
<span className="shape" style={{backgroundColor:item.color}}></span>
|
||||
<span className="text">{item.title}</span>
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +1,21 @@
|
|||
.alert-list{
|
||||
background: #f0f2f5;
|
||||
padding: 10px 5px;
|
||||
.title{
|
||||
color: #333;
|
||||
font-weight:600;
|
||||
padding-bottom: 6px;
|
||||
.total{
|
||||
color: #999;
|
||||
margin-left: 15px;
|
||||
font-size: 12px;
|
||||
.header{
|
||||
display: flex;
|
||||
.title{
|
||||
color: #333;
|
||||
font-weight:600;
|
||||
padding-bottom: 6px;
|
||||
.total{
|
||||
color: #999;
|
||||
margin-left: 15px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.legend{
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
.legend-list{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
>.legend-item{
|
||||
margin-left: 10px;
|
||||
.shape{
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
.text{
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
margin-left: 3px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import {AlertList} from '../components/AlertList/AlertList';
|
|||
import _ from 'lodash';
|
||||
import {useState, useEffect} from 'react';
|
||||
import './alertoverview.scss';
|
||||
import { formatMessage } from 'umi/locale';
|
||||
|
||||
export const AlertOverview = (props: any)=>{
|
||||
const {httpClient, history} = props;
|
||||
|
@ -10,8 +11,13 @@ export const AlertOverview = (props: any)=>{
|
|||
totalAlerts: 0,
|
||||
});
|
||||
|
||||
const [historyData, setHistoryData] = useState({
|
||||
alerts: [],
|
||||
totalAlerts: 0,
|
||||
});
|
||||
|
||||
const getAlerts = _.debounce(
|
||||
(from, size, search, sortField, sortDirection, severityLevel, alertState, monitorIds) => {
|
||||
(from, size, search, sortField, sortDirection, severityLevel, alertState, monitorIds, type) => {
|
||||
let params = {
|
||||
from,
|
||||
size,
|
||||
|
@ -20,15 +26,21 @@ export const AlertOverview = (props: any)=>{
|
|||
sortDirection,
|
||||
severityLevel,
|
||||
alertState,
|
||||
type,
|
||||
};
|
||||
if(monitorIds){
|
||||
params["monitorIds"]= monitorIds;
|
||||
}
|
||||
// const queryParamsString = queryString.stringify(params);
|
||||
// history.replace({ ...this.props.location, search: queryParamsString });
|
||||
httpClient.get('/alerting/alerts', { query: params }).then((resp:any) => {
|
||||
if (resp.ok) {
|
||||
const { alerts, totalAlerts } = resp;
|
||||
if(type == 'ALERT_HISTORY'){
|
||||
setHistoryData({
|
||||
alerts,
|
||||
totalAlerts,
|
||||
});
|
||||
return;
|
||||
}
|
||||
setData({
|
||||
alerts,
|
||||
totalAlerts,
|
||||
|
@ -44,13 +56,17 @@ export const AlertOverview = (props: any)=>{
|
|||
|
||||
const pageSize = 10;
|
||||
useEffect(()=>{
|
||||
getAlerts(0, pageSize, "", "start_time", "desc", "ALL", "ALL","")
|
||||
getAlerts(0, pageSize, "", "start_time", "desc", "ALL", "ALL","", "ALERT");
|
||||
getAlerts(0, pageSize, "", "start_time", "desc", "ALL", "ALL","", "ALERT_HISTORY")
|
||||
},[])
|
||||
|
||||
const onPageChange = (pageIndex: number)=>{
|
||||
const from = (pageIndex - 1) * pageSize;
|
||||
getAlerts(from, pageSize, "", "start_time", "desc", "ALL", "ALL","")
|
||||
const onPageChangeGen = (type:string) => {
|
||||
return (pageIndex: number)=>{
|
||||
const from = (pageIndex - 1) * pageSize;
|
||||
getAlerts(from, pageSize, "", "start_time", "desc", "ALL", "ALL","", type)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const onItemClick = (item: any)=>{
|
||||
history.push(`/monitors/${item.monitor_id}`)
|
||||
|
@ -60,17 +76,25 @@ export const AlertOverview = (props: any)=>{
|
|||
<div className="alert-overview">
|
||||
<div className="left">
|
||||
<AlertList dataSource={data.alerts}
|
||||
title="Open Alerts"
|
||||
title={formatMessage({id:'alert.overview.alertlist.title'})}
|
||||
onItemClick={onItemClick}
|
||||
pagination={{
|
||||
pageSize,
|
||||
total: data.totalAlerts,
|
||||
onChange: onPageChange,
|
||||
onChange: onPageChangeGen('ALERT'),
|
||||
}}/>
|
||||
<AlertList dataSource={historyData.alerts}
|
||||
title={formatMessage({id:'alert.overview.alertlist-history.title'})}
|
||||
onItemClick={onItemClick}
|
||||
pagination={{
|
||||
pageSize,
|
||||
total: historyData.totalAlerts,
|
||||
onChange: onPageChangeGen('ALERT_HISTORY'),
|
||||
}}/>
|
||||
</div>
|
||||
<div className="right">
|
||||
{/* <div className="right">
|
||||
<div>提示</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -87,7 +87,7 @@ export default class DestinationsActions extends Component {
|
|||
</EuiFlexItem>
|
||||
) : null}
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton fill href={`#/${PLUGIN_NAME}${APP_PATH.CREATE_DESTINATION}`}>
|
||||
<EuiButton fill href={`#/alerting/destination${APP_PATH.CREATE_DESTINATION}`}>
|
||||
{formatMessage({ id: 'alert.button.add-destination' })}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -18,10 +18,11 @@ import _ from 'lodash';
|
|||
import { EuiButton } from '@elastic/eui';
|
||||
|
||||
import { FORMIK_INITIAL_EMAIL_GROUP_VALUES } from '../Email/utils/constants';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const AddEmailGroupButton = ({ arrayHelpers }) => (
|
||||
<EuiButton onClick={() => arrayHelpers.unshift(_.cloneDeep(FORMIK_INITIAL_EMAIL_GROUP_VALUES))}>
|
||||
Add email group
|
||||
{formatMessage({id:'alert.emailgroup.manage.button.add'})}
|
||||
</EuiButton>
|
||||
);
|
||||
|
||||
|
|
|
@ -18,10 +18,11 @@ import _ from 'lodash';
|
|||
import { EuiButton } from '@elastic/eui';
|
||||
|
||||
import { FORMIK_INITIAL_SENDER_VALUES } from '../Email/utils/constants';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const AddSenderButton = ({ arrayHelpers }) => (
|
||||
<EuiButton onClick={() => arrayHelpers.unshift(_.cloneDeep(FORMIK_INITIAL_SENDER_VALUES))}>
|
||||
Add sender
|
||||
{formatMessage({id:'alert.email.manage.button.add'})}
|
||||
</EuiButton>
|
||||
);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import { FormikComboBox, FormikFieldText } from '../../../../../components/FormC
|
|||
import { isInvalid, hasError } from '../../../../../utils/validate';
|
||||
import { validateEmailGroupEmails, validateEmailGroupName } from './utils/validate';
|
||||
import { STATE } from './utils/constants';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const onEmailGroupChange = (index, emailGroup, arrayHelpers) => {
|
||||
// Checking for id here since new email groups should not be marked as updated
|
||||
|
@ -49,7 +50,7 @@ const EmailGroup = ({ emailGroup, emailOptions, arrayHelpers, context, index, on
|
|||
paddingSize="l"
|
||||
extraAction={
|
||||
<EuiButton color="danger" size="s" onClick={onDelete}>
|
||||
Remove email group
|
||||
{formatMessage({id:'alert.emailgroup.manage.button.remove'})}
|
||||
</EuiButton>
|
||||
}
|
||||
>
|
||||
|
@ -58,10 +59,8 @@ const EmailGroup = ({ emailGroup, emailOptions, arrayHelpers, context, index, on
|
|||
formRow
|
||||
fieldProps={{ validate: validateEmailGroupName(context.ctx.emailGroups) }}
|
||||
rowProps={{
|
||||
label: 'Email group name',
|
||||
helpText:
|
||||
'A unique and descriptive name that is easy to search. ' +
|
||||
'Valid characters are upper and lowercase a-z, 0-9, _ (underscore) and - (hyphen).',
|
||||
label: formatMessage({id:'alert.emailgroup.manage.field.name'}),
|
||||
helpText:formatMessage({id:'alert.emailgroup.manage.field.name.help'}),
|
||||
style: { padding: '10px 0px' },
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
|
@ -79,8 +78,8 @@ const EmailGroup = ({ emailGroup, emailOptions, arrayHelpers, context, index, on
|
|||
formRow
|
||||
fieldProps={{ validate: validateEmailGroupEmails }}
|
||||
rowProps={{
|
||||
label: 'Emails',
|
||||
helpText: 'Search for previously used email addresses or type in new ones.',
|
||||
label: formatMessage({id:'alert.emailgroup.manage.field.emails'}),
|
||||
helpText: formatMessage({id:'alert.emailgroup.manage.field.emails.help'}),
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
}}
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
import { isInvalid, hasError } from '../../../../../utils/validate';
|
||||
import { validateEmail, validateHost, validatePort, validateSenderName } from './utils/validate';
|
||||
import { METHOD_TYPE, STATE } from './utils/constants';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const methodOptions = [
|
||||
{ value: METHOD_TYPE.NONE, text: 'None' },
|
||||
|
@ -51,7 +52,7 @@ const Sender = ({ sender, arrayHelpers, context, index, onDelete }) => {
|
|||
paddingSize="l"
|
||||
extraAction={
|
||||
<EuiButton color="danger" size="s" onClick={onDelete}>
|
||||
Remove sender
|
||||
{formatMessage({id:'alert.email.manage.button.remove'})}
|
||||
</EuiButton>
|
||||
}
|
||||
>
|
||||
|
@ -60,10 +61,8 @@ const Sender = ({ sender, arrayHelpers, context, index, onDelete }) => {
|
|||
formRow
|
||||
fieldProps={{ validate: validateSenderName(context.ctx.senders) }}
|
||||
rowProps={{
|
||||
label: 'Sender name',
|
||||
helpText:
|
||||
'A unique and descriptive name that is easy to search. ' +
|
||||
'Valid characters are upper and lowercase a-z, 0-9, and _ (underscore).',
|
||||
label: formatMessage({id:'alert.email.manage.field.name'}),
|
||||
helpText:formatMessage({id:'alert.email.manage.field.name.help'}),
|
||||
style: { padding: '10px 0px' },
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
|
@ -88,7 +87,7 @@ const Sender = ({ sender, arrayHelpers, context, index, onDelete }) => {
|
|||
formRow
|
||||
fieldProps={{ validate: validateEmail }}
|
||||
rowProps={{
|
||||
label: 'Email address',
|
||||
label: formatMessage({id:'alert.email.manage.field.address'}),
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
}}
|
||||
|
@ -108,7 +107,7 @@ const Sender = ({ sender, arrayHelpers, context, index, onDelete }) => {
|
|||
formRow
|
||||
// fieldProps={{ validate: validateEmail }}
|
||||
rowProps={{
|
||||
label: 'Email password',
|
||||
label: formatMessage({id:'alert.email.manage.field.password'}),
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
}}
|
||||
|
@ -130,7 +129,7 @@ const Sender = ({ sender, arrayHelpers, context, index, onDelete }) => {
|
|||
formRow
|
||||
fieldProps={{ validate: validateHost }}
|
||||
rowProps={{
|
||||
label: 'Host',
|
||||
label: formatMessage({id:'alert.email.manage.field.host'}),
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
}}
|
||||
|
@ -150,7 +149,7 @@ const Sender = ({ sender, arrayHelpers, context, index, onDelete }) => {
|
|||
formRow
|
||||
fieldProps={{ validate: validatePort }}
|
||||
rowProps={{
|
||||
label: 'Port',
|
||||
label: formatMessage({id:'alert.email.manage.field.port'}),
|
||||
isInvalid,
|
||||
error: hasError,
|
||||
}}
|
||||
|
@ -169,11 +168,11 @@ const Sender = ({ sender, arrayHelpers, context, index, onDelete }) => {
|
|||
name={`senders.${index}.method`}
|
||||
formRow
|
||||
rowProps={{
|
||||
label: 'Encryption method',
|
||||
helpText: `SSL or TLS is recommended for security.
|
||||
SSL and TLS requires validation by adding the following fields to the Elasticsearch keystore:
|
||||
opendistro.alerting.destination.email.${!name ? '[sender name]' : name}.username
|
||||
opendistro.alerting.destination.email.${!name ? '[sender name]' : name}.password`,
|
||||
label: formatMessage({id:'alert.email.manage.field.method'}),
|
||||
// helpText: `SSL or TLS is recommended for security.
|
||||
// SSL and TLS requires validation by adding the following fields to the Elasticsearch keystore:
|
||||
// opendistro.alerting.destination.email.${!name ? '[sender name]' : name}.username
|
||||
// opendistro.alerting.destination.email.${!name ? '[sender name]' : name}.password`,
|
||||
style: { padding: '10px 0px' },
|
||||
}}
|
||||
inputProps={{
|
||||
|
|
|
@ -39,6 +39,7 @@ import { SubmitErrorHandler } from '../../../../utils/SubmitErrorHandler';
|
|||
import { getAllowList } from '../../utils/helpers';
|
||||
import { backendErrorNotification } from '../../../../utils/helpers';
|
||||
import { formatMessage } from 'umi/locale';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
|
||||
const destinationType = {
|
||||
[DESTINATION_TYPE.SLACK]: (props) => <Webhook {...props} />,
|
||||
|
@ -59,7 +60,7 @@ class CreateDestination extends React.Component {
|
|||
async componentDidMount() {
|
||||
const { httpClient, location, edit, history } = this.props;
|
||||
|
||||
const allowList = await getAllowList(httpClient);
|
||||
const allowList = [DESTINATION_TYPE.EMAIL,DESTINATION_TYPE.CUSTOM_HOOK];//await getAllowList(httpClient);
|
||||
this.setState({ allowList });
|
||||
|
||||
let ifSeqNo, ifPrimaryTerm;
|
||||
|
@ -90,7 +91,7 @@ class CreateDestination extends React.Component {
|
|||
getDestination = async (destinationId) => {
|
||||
const { httpClient, history, notifications } = this.props;
|
||||
try {
|
||||
const resp = await httpClient.get(`/alerting/destinations/${destinationId}`);
|
||||
const resp = await httpClient.get(pathPrefix + `/alerting/destinations/${destinationId}`);
|
||||
if (resp.ok) {
|
||||
const ifSeqNo = _.get(resp, 'ifSeqNo');
|
||||
const ifPrimaryTerm = _.get(resp, 'ifPrimaryTerm');
|
||||
|
@ -101,7 +102,7 @@ class CreateDestination extends React.Component {
|
|||
} else {
|
||||
// Handle error, show message in case of 404
|
||||
backendErrorNotification(notifications, 'get', 'destination', resp.resp);
|
||||
history.push(`/destinations`);
|
||||
history.push(`alerting/destination`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Unable to get the data');
|
||||
|
@ -119,7 +120,7 @@ class CreateDestination extends React.Component {
|
|||
} = this.props;
|
||||
const { ifSeqNo, ifPrimaryTerm } = this.state;
|
||||
try {
|
||||
const resp = await httpClient.put(`/alerting/destinations/${destinationId}`, {
|
||||
const resp = await httpClient.put(pathPrefix+`/alerting/destinations/${destinationId}`, {
|
||||
query: { ifSeqNo, ifPrimaryTerm },
|
||||
body: JSON.stringify(requestData),
|
||||
});
|
||||
|
@ -140,7 +141,7 @@ class CreateDestination extends React.Component {
|
|||
handleCreate = async (requestData, { setSubmitting }) => {
|
||||
const { httpClient, history, notifications } = this.props;
|
||||
try {
|
||||
const resp = await httpClient.post('/alerting/destinations', {
|
||||
const resp = await httpClient.post(pathPrefix+'/alerting/destinations', {
|
||||
body: JSON.stringify(requestData),
|
||||
});
|
||||
setSubmitting(false);
|
||||
|
|
|
@ -44,7 +44,7 @@ export default class EmailRecipients extends React.Component {
|
|||
|
||||
async componentDidMount() {
|
||||
const { httpClient } = this.props;
|
||||
const allowList = await getAllowList(httpClient);
|
||||
const allowList = [DESTINATION_TYPE.EMAIL, DESTINATION_TYPE.CUSTOM_HOOK];//await getAllowList(httpClient);
|
||||
this.setState({ allowList });
|
||||
|
||||
this.loadData();
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
*/
|
||||
|
||||
import { MAX_QUERY_RESULT_SIZE } from '../../../../../../utils/constants';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
|
||||
export default async function getEmailGroups(httpClient, searchText = '') {
|
||||
try {
|
||||
const response = await httpClient.get('/alerting/destinations/email_groups', {
|
||||
const response = await httpClient.get(pathPrefix+'/alerting/destinations/email_groups', {
|
||||
query: { search: searchText, size: MAX_QUERY_RESULT_SIZE },
|
||||
});
|
||||
if (response.ok) {
|
||||
|
|
|
@ -43,7 +43,7 @@ export default class EmailSender extends React.Component {
|
|||
|
||||
async componentDidMount() {
|
||||
const { httpClient } = this.props;
|
||||
const allowList = await getAllowList(httpClient);
|
||||
const allowList = [DESTINATION_TYPE.CUSTOM_HOOK, DESTINATION_TYPE.EMAIL];//await getAllowList(httpClient);
|
||||
this.setState({ allowList });
|
||||
|
||||
this.loadSenders();
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
*/
|
||||
|
||||
import { MAX_QUERY_RESULT_SIZE } from '../../../../../../utils/constants';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
|
||||
export default async function getSenders(httpClient, searchText = '') {
|
||||
try {
|
||||
const response = await httpClient.get('/alerting/destinations/email_accounts', {
|
||||
const response = await httpClient.get(pathPrefix+'/alerting/destinations/email_accounts', {
|
||||
query: { search: searchText, size: MAX_QUERY_RESULT_SIZE },
|
||||
});
|
||||
if (response.ok) {
|
||||
|
|
|
@ -39,6 +39,8 @@ import { emailGroupToFormik } from './utils/helpers';
|
|||
import getEmailGroups from '../EmailRecipients/utils/helpers';
|
||||
import { STATE } from '../../../components/createDestinations/Email/utils/constants';
|
||||
import { ignoreEscape } from '../../../../../utils/helpers';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const createEmailGroupContext = (emailGroups) => ({
|
||||
ctx: {
|
||||
|
@ -119,7 +121,7 @@ export default class ManageEmailGroups extends React.Component {
|
|||
emails: emailGroup.emails.map((email) => ({ email: email.label })),
|
||||
};
|
||||
try {
|
||||
const response = await httpClient.post(`/alerting/destinations/email_groups`, {
|
||||
const response = await httpClient.post(pathPrefix+`/alerting/destinations/email_groups`, {
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!response.ok) {
|
||||
|
@ -147,7 +149,7 @@ export default class ManageEmailGroups extends React.Component {
|
|||
emails: updatedEmailGroup.emails.map((email) => ({ email: email.label })),
|
||||
};
|
||||
try {
|
||||
const response = await httpClient.put(`/alerting/email_groups/${id}`, {
|
||||
const response = await httpClient.put(pathPrefix + `/alerting/email_groups/${id}`, {
|
||||
query: { ifSeqNo, ifPrimaryTerm },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
@ -172,7 +174,7 @@ export default class ManageEmailGroups extends React.Component {
|
|||
const { httpClient, notifications } = this.props;
|
||||
const { id } = emailGroup;
|
||||
try {
|
||||
const response = await httpClient.delete(`/alerting/email_groups/${id}`);
|
||||
const response = await httpClient.delete(pathPrefix + `/alerting/email_groups/${id}`);
|
||||
if (!response.ok) {
|
||||
this.setState({ failedEmailGroups: true });
|
||||
notifications.toasts.addDanger({
|
||||
|
@ -276,7 +278,7 @@ export default class ManageEmailGroups extends React.Component {
|
|||
onClose={ignoreEscape(onClickCancel)}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>Manage email groups</EuiModalHeaderTitle>
|
||||
<EuiModalHeaderTitle> {formatMessage({id:'alert.emailgroup.manage.title'})}</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
|
||||
<EuiHorizontalRule margin="s" />
|
||||
|
@ -301,11 +303,11 @@ export default class ManageEmailGroups extends React.Component {
|
|||
<EuiModalFooter>
|
||||
<EuiFlexGroup alignItems="center" justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={onClickCancel}>Cancel</EuiButtonEmpty>
|
||||
<EuiButtonEmpty onClick={onClickCancel}>{formatMessage({id:'form.button.cancel'})}</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton onClick={handleSubmit} isLoading={isSubmitting} fill>
|
||||
Save
|
||||
{formatMessage({id:'form.button.save'})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -39,6 +39,8 @@ import { senderToFormik } from './utils/helpers';
|
|||
import getSenders from '../EmailSender/utils/helpers';
|
||||
import { STATE } from '../../../components/createDestinations/Email/utils/constants';
|
||||
import { ignoreEscape } from '../../../../../utils/helpers';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const createSenderContext = (senders) => ({
|
||||
ctx: {
|
||||
|
@ -108,7 +110,7 @@ export default class ManageSenders extends React.Component {
|
|||
password: sender.password,
|
||||
};
|
||||
try {
|
||||
const response = await httpClient.post(`/alerting/destinations/email_accounts`, {
|
||||
const response = await httpClient.post(pathPrefix+`/alerting/destinations/email_accounts`, {
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!response.ok) {
|
||||
|
@ -140,7 +142,7 @@ export default class ManageSenders extends React.Component {
|
|||
password: updatedSender.password,
|
||||
};
|
||||
try {
|
||||
const response = await httpClient.put(`/alerting/email_accounts/${id}`, {
|
||||
const response = await httpClient.put(pathPrefix+`/alerting/email_accounts/${id}`, {
|
||||
query: { ifSeqNo, ifPrimaryTerm },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
@ -165,7 +167,7 @@ export default class ManageSenders extends React.Component {
|
|||
const { httpClient, notifications } = this.props;
|
||||
const { id } = sender;
|
||||
try {
|
||||
const response = await httpClient.delete(`/alerting/email_accounts/${id}`);
|
||||
const response = await httpClient.delete(pathPrefix+`/alerting/email_accounts/${id}`);
|
||||
if (!response.ok) {
|
||||
this.setState({ failedSenders: true });
|
||||
notifications.toasts.addDanger({
|
||||
|
@ -266,7 +268,7 @@ export default class ManageSenders extends React.Component {
|
|||
onClose={ignoreEscape(onClickCancel)}
|
||||
>
|
||||
<EuiModalHeader>
|
||||
<EuiModalHeaderTitle>Manage email senders</EuiModalHeaderTitle>
|
||||
<EuiModalHeaderTitle>{formatMessage({id:'alert.email.manage.title'})}</EuiModalHeaderTitle>
|
||||
</EuiModalHeader>
|
||||
|
||||
<EuiHorizontalRule margin="s" />
|
||||
|
@ -291,11 +293,11 @@ export default class ManageSenders extends React.Component {
|
|||
<EuiModalFooter>
|
||||
<EuiFlexGroup alignItems="center" justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={onClickCancel}>Cancel</EuiButtonEmpty>
|
||||
<EuiButtonEmpty onClick={onClickCancel}>{formatMessage({id:'form.button.cancel'})}</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton onClick={handleSubmit} isLoading={isSubmitting} fill>
|
||||
Save
|
||||
{formatMessage({id:'form.button.save'})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -27,7 +27,7 @@ const DEFAULT_CONTENT_VALUE = 'application/json';
|
|||
export const formikInitialValues = {
|
||||
urlType: 'url',
|
||||
name: '',
|
||||
type: 'slack',
|
||||
type: 'custom_webhook',
|
||||
[DESTINATION_TYPE.SLACK]: {
|
||||
url: '',
|
||||
},
|
||||
|
|
|
@ -17,6 +17,7 @@ import _ from 'lodash';
|
|||
import { URL_TYPE, CONTENT_TYPE_KEY } from './constants';
|
||||
import { DESTINATION_TYPE } from '../../../utils/constants';
|
||||
import { RECIPIENT_TYPE } from '../EmailRecipients/utils/constants';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
|
||||
const getAttributeArrayFromValues = (attributes) =>
|
||||
Object.keys(attributes).map((currentKey) => ({
|
||||
|
@ -49,7 +50,7 @@ const customWebhookToFormik = ({
|
|||
|
||||
const getSender = async (httpClient, id) => {
|
||||
try {
|
||||
const response = await httpClient.get(`/alerting/email_accounts/${id}`);
|
||||
const response = await httpClient.get(pathPrefix+`/alerting/email_accounts/${id}`);
|
||||
if (response.ok) {
|
||||
return response.resp;
|
||||
}
|
||||
|
@ -62,7 +63,7 @@ const getSender = async (httpClient, id) => {
|
|||
|
||||
const getEmailGroup = async (httpClient, id) => {
|
||||
try {
|
||||
const response = await httpClient.get(`/alerting/email_groups/${id}`);
|
||||
const response = await httpClient.get(pathPrefix+`/alerting/email_groups/${id}`);
|
||||
if (response.ok) {
|
||||
return response.resp;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import _ from 'lodash';
|
||||
import { INDEX } from '../../../../../utils/constants';
|
||||
import { getAllowList } from '../../../utils/helpers';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
|
||||
export const validateDestinationName = (httpClient, destinationToEdit) => async (value) => {
|
||||
try {
|
||||
|
@ -24,8 +25,9 @@ export const validateDestinationName = (httpClient, destinationToEdit) => async
|
|||
index: INDEX.SCHEDULED_JOBS,
|
||||
query: { query: { term: { 'destination.name.keyword': value } } },
|
||||
};
|
||||
const response = await httpClient.post('/alerting/monitors/_search', {
|
||||
const response = await httpClient.post(pathPrefix+'/alerting/_search', {
|
||||
body: JSON.stringify(options),
|
||||
prependBasePath: false,
|
||||
});
|
||||
if (_.get(response, 'resp.hits.total.value', 0)) {
|
||||
if (!destinationToEdit) return 'Destination name is already used';
|
||||
|
|
|
@ -36,6 +36,7 @@ import { getAllowList } from '../../utils/helpers';
|
|||
import { DESTINATION_TYPE } from '../../utils/constants';
|
||||
import { backendErrorNotification } from '../../../../utils/helpers';
|
||||
import { formatMessage } from 'umi/locale';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
|
||||
class DestinationsList extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -101,7 +102,7 @@ class DestinationsList extends React.Component {
|
|||
|
||||
async componentDidMount() {
|
||||
const { httpClient } = this.props;
|
||||
const allowList = await getAllowList(httpClient);
|
||||
const allowList = [DESTINATION_TYPE.EMAIL, DESTINATION_TYPE.CUSTOM_HOOK];//await getAllowList(httpClient);
|
||||
this.setState({ allowList });
|
||||
|
||||
const { page, queryParams } = this.state;
|
||||
|
@ -120,8 +121,9 @@ class DestinationsList extends React.Component {
|
|||
query: isDeleteAllowedQuery(type, id),
|
||||
index: INDEX.SCHEDULED_JOBS,
|
||||
};
|
||||
const resp = await httpClient.post('/alerting/monitors/_search', {
|
||||
const resp = await httpClient.post(pathPrefix+'/alerting/_search', {
|
||||
body: JSON.stringify(requestBody),
|
||||
prependBasePath: false,
|
||||
});
|
||||
const total = _.get(resp, 'resp.hits.total.value');
|
||||
return total === 0;
|
||||
|
@ -152,7 +154,7 @@ class DestinationsList extends React.Component {
|
|||
const { id: destinationId } = this.state.destinationToDelete;
|
||||
const { httpClient, notifications } = this.props;
|
||||
try {
|
||||
const resp = await httpClient.delete(`/alerting/destinations/${destinationId}`);
|
||||
const resp = await httpClient.delete(pathPrefix+`/alerting/destinations/${destinationId}`);
|
||||
if (resp.ok) {
|
||||
await this.getDestinations();
|
||||
} else {
|
||||
|
@ -215,7 +217,7 @@ class DestinationsList extends React.Component {
|
|||
// search: queryParms,
|
||||
// });
|
||||
try {
|
||||
const resp = await httpClient.get('/alerting/destinations', {
|
||||
const resp = await httpClient.get(pathPrefix+ '/alerting/destinations', {
|
||||
query: { from, ...params },
|
||||
});
|
||||
if (resp.ok) {
|
||||
|
|
|
@ -16,10 +16,13 @@
|
|||
import _ from 'lodash';
|
||||
import { ALLOW_LIST_SETTING_PATH } from './constants';
|
||||
import { backendErrorNotification } from '../../../utils/helpers';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
|
||||
export async function getAllowList(httpClient) {
|
||||
try {
|
||||
const response = await httpClient.get('/alerting/_settings');
|
||||
const response = await httpClient.get(pathPrefix+'/alerting/_settings', {
|
||||
prependBasePath: false,
|
||||
});
|
||||
if (response.ok) {
|
||||
// Attempt to resolve the value of allow_list in the order of 'persistent, 'transient' and 'defaults' settings
|
||||
const { defaults, transient, persistent } = response.resp;
|
||||
|
|
|
@ -49,11 +49,11 @@ export default class Home extends Component {
|
|||
name: formatMessage({ id: 'alert.monitor' }),
|
||||
route: 'monitors',
|
||||
},
|
||||
{
|
||||
id: 'destinations',
|
||||
name: formatMessage({ id: 'alert.destination' }),
|
||||
route: 'destinations',
|
||||
},
|
||||
// {
|
||||
// id: 'destinations',
|
||||
// name: formatMessage({ id: 'alert.destination' }),
|
||||
// route: 'destinations',
|
||||
// },
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -100,10 +100,11 @@ export default class Home extends Component {
|
|||
<Route
|
||||
// exact
|
||||
path="/dashboard"
|
||||
render={(props) => (
|
||||
render={(props) => {
|
||||
return (
|
||||
// <Dashboard {...props} httpClient={httpClient} notifications={notifications} />
|
||||
<AlertOverview {...props} httpClient={httpClient} notifications={notifications} />
|
||||
)}
|
||||
)}}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
|
@ -112,7 +113,7 @@ export default class Home extends Component {
|
|||
<Monitors {...props} httpClient={httpClient} notifications={notifications} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
{/* <Route
|
||||
exact
|
||||
path="/destinations"
|
||||
render={(props) => (
|
||||
|
@ -122,7 +123,7 @@ export default class Home extends Component {
|
|||
notifications={notifications}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
/> */}
|
||||
<Route
|
||||
exact
|
||||
path="/"
|
||||
|
|
|
@ -56,12 +56,6 @@ class Main extends Component {
|
|||
}}
|
||||
/>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/overview"
|
||||
render={(props) => (
|
||||
<Overview {...props} httpClient={core.http} notifications={core.notifications} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={APP_PATH.CREATE_MONITOR}
|
||||
render={(props) => (
|
||||
|
@ -74,7 +68,7 @@ class Main extends Component {
|
|||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
{/* <Route
|
||||
path={APP_PATH.CREATE_DESTINATION}
|
||||
render={(props) => (
|
||||
<CreateDestination
|
||||
|
@ -96,7 +90,7 @@ class Main extends Component {
|
|||
edit
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
/> */}
|
||||
<Route
|
||||
path="/monitors/:monitorId"
|
||||
render={(props) => (
|
||||
|
|
|
@ -19,11 +19,12 @@ import { EuiFlexGrid } from '@elastic/eui';
|
|||
import ContentPanel from '../../../../components/ContentPanel/index';
|
||||
import OverviewStat from '../OverviewStat/index';
|
||||
import getOverviewStats from './utils/getOverviewStats';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const MonitorOverview = ({ monitor, monitorId, monitorVersion, activeCount }) => {
|
||||
const items = getOverviewStats(monitor, monitorId, monitorVersion, activeCount);
|
||||
return (
|
||||
<ContentPanel title="Overview" titleSize="s">
|
||||
<ContentPanel title={formatMessage({id:'alert.monitor.overview.title'})} titleSize="s">
|
||||
<EuiFlexGrid columns={4}>
|
||||
{items.map(props => (
|
||||
<OverviewStat key={props.header} {...props} />
|
||||
|
|
|
@ -18,6 +18,7 @@ import moment from 'moment-timezone';
|
|||
|
||||
import getScheduleFromMonitor from './getScheduleFromMonitor';
|
||||
import { DEFAULT_EMPTY_DATA, SEARCH_TYPE } from '../../../../../utils/constants';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
// TODO: used in multiple places, move into helper
|
||||
function getTime(time) {
|
||||
|
@ -31,42 +32,42 @@ export default function getOverviewStats(monitor, monitorId, monitorVersion, act
|
|||
const searchType = _.get(monitor, 'ui_metadata.search.searchType', 'query');
|
||||
return [
|
||||
{
|
||||
header: 'State',
|
||||
header: formatMessage({id:"alert.monitor.overview.header.state"}),
|
||||
value: monitor.enabled ? 'Enabled' : 'Disabled',
|
||||
},
|
||||
{
|
||||
header: 'Monitor definition type',
|
||||
header: formatMessage({id:"alert.monitor.overview.header.definition_type"}),
|
||||
value: searchType === SEARCH_TYPE.QUERY ? 'Extraction Query' : 'Visual graph',
|
||||
},
|
||||
{
|
||||
header: 'Total active alerts',
|
||||
header: formatMessage({id:"alert.monitor.overview.header.total_active_alerts"}),
|
||||
value: activeCount,
|
||||
},
|
||||
{
|
||||
header: 'Schedule',
|
||||
header: formatMessage({id:"alert.monitor.overview.header.schedule"}),
|
||||
value: getScheduleFromMonitor(monitor),
|
||||
},
|
||||
{
|
||||
header: 'Last updated',
|
||||
header: formatMessage({id:"alert.monitor.overview.header.last_updated"}),
|
||||
value: getTime(monitor.last_update_time),
|
||||
},
|
||||
{
|
||||
header: 'Monitor ID',
|
||||
header: formatMessage({id:"alert.monitor.overview.header.monitor_id"}),
|
||||
value: monitorId,
|
||||
},
|
||||
{
|
||||
header: 'Monitor version number',
|
||||
header: formatMessage({id:"alert.monitor.overview.header.version_number"}),
|
||||
value: monitorVersion,
|
||||
},
|
||||
{
|
||||
/* There are 3 cases:
|
||||
1. Monitors created by older versions and never updated.
|
||||
These monitors won’t have User details in the monitor object. `monitor.user` will be null.
|
||||
2. Monitors are created when security plugin is disabled, these will have empty User object.
|
||||
(`monitor.user.name`, `monitor.user.roles` are empty )
|
||||
3. Monitors are created when security plugin is enabled, these will have an User object. */
|
||||
header: 'Last updated by',
|
||||
value: monitor.user && monitor.user.name ? monitor.user.name : 'N/A',
|
||||
},
|
||||
// {
|
||||
// /* There are 3 cases:
|
||||
// 1. Monitors created by older versions and never updated.
|
||||
// These monitors won’t have User details in the monitor object. `monitor.user` will be null.
|
||||
// 2. Monitors are created when security plugin is disabled, these will have empty User object.
|
||||
// (`monitor.user.name`, `monitor.user.roles` are empty )
|
||||
// 3. Monitors are created when security plugin is enabled, these will have an User object. */
|
||||
// header: 'Last updated by',
|
||||
// value: monitor.user && monitor.user.name ? monitor.user.name : 'N/A',
|
||||
// },
|
||||
];
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import {
|
|||
} from '../../../utils/constants';
|
||||
import { migrateTriggerMetadata } from './utils/helpers';
|
||||
import { backendErrorNotification } from '../../../utils/helpers';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
export default class MonitorDetails extends Component {
|
||||
constructor(props) {
|
||||
|
@ -323,7 +324,7 @@ export default class MonitorDetails extends Component {
|
|||
});
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
{formatMessage({ id: 'form.button.edit' })}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -331,7 +332,7 @@ export default class MonitorDetails extends Component {
|
|||
isLoading={updating}
|
||||
onClick={() => this.updateMonitor({ enabled: !monitor.enabled })}
|
||||
>
|
||||
{monitor.enabled ? 'Disable' : 'Enable'}
|
||||
{monitor.enabled ? formatMessage({id:'alert.monitor.actions.disable'}) : formatMessage({id:'alert.monitor.actions.enable'})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -37,6 +37,8 @@ import {
|
|||
import * as HistoryConstants from './utils/constants';
|
||||
import { INDEX } from '../../../../utils/constants';
|
||||
import { backendErrorNotification } from '../../../../utils/helpers';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
class MonitorHistory extends PureComponent {
|
||||
constructor(props) {
|
||||
|
@ -200,8 +202,9 @@ class MonitorHistory extends PureComponent {
|
|||
),
|
||||
index: INDEX.ALL_ALERTS,
|
||||
};
|
||||
const resp = await httpClient.post('/alerting/monitors/_search', {
|
||||
const resp = await httpClient.post(pathPrefix + '/alerting/_search', {
|
||||
body: JSON.stringify(requestBody),
|
||||
prependBasePath: false,
|
||||
});
|
||||
if (resp.ok) {
|
||||
const poiData = get(resp, 'resp.aggregations.alerts_over_time.buckets', []).map((item) => ({
|
||||
|
@ -309,7 +312,7 @@ class MonitorHistory extends PureComponent {
|
|||
const { triggers, onShowTrigger } = this.props;
|
||||
return (
|
||||
<ContentPanel
|
||||
title="History"
|
||||
title={formatMessage({id:"alert.monitor.history.title"})}
|
||||
titleSize="s"
|
||||
bodyStyles={{ minHeight: 200, padding: 0 }}
|
||||
actions={[
|
||||
|
|
|
@ -19,6 +19,7 @@ import uuid from 'uuid-v4';
|
|||
import { EuiButton, EuiInMemoryTable } from '@elastic/eui';
|
||||
|
||||
import ContentPanel from '../../../../components/ContentPanel';
|
||||
import {formatMessage} from 'umi/locale';
|
||||
|
||||
const MAX_TRIGGERS = 10;
|
||||
|
||||
|
@ -80,20 +81,20 @@ export default class Triggers extends Component {
|
|||
const columns = [
|
||||
{
|
||||
field: 'name',
|
||||
name: 'Name',
|
||||
name: formatMessage({ id: 'alert.monitor.triggers.table.header.name' }),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
name: 'Number of actions',
|
||||
name: formatMessage({ id: 'alert.monitor.triggers.table.header.number_of_actions' }),
|
||||
sortable: true,
|
||||
truncateText: false,
|
||||
render: actions => actions.length,
|
||||
},
|
||||
{
|
||||
field: 'severity',
|
||||
name: 'Severity',
|
||||
name: formatMessage({ id: 'alert.monitor.triggers.table.header.severity' }),
|
||||
sortable: true,
|
||||
truncateText: false,
|
||||
},
|
||||
|
@ -105,22 +106,22 @@ export default class Triggers extends Component {
|
|||
|
||||
return (
|
||||
<ContentPanel
|
||||
title="Triggers"
|
||||
title={formatMessage({ id: 'alert.monitor.triggers.title' })}
|
||||
titleSize="s"
|
||||
bodyStyles={{ padding: 'initial' }}
|
||||
actions={[
|
||||
<EuiButton onClick={this.onEdit} disabled={selectedItems.length !== 1}>
|
||||
Edit
|
||||
{formatMessage({ id: 'form.button.edit' })}
|
||||
</EuiButton>,
|
||||
<EuiButton onClick={this.onDelete} disabled={!selectedItems.length}>
|
||||
Delete
|
||||
{formatMessage({ id: 'form.button.delete' })}
|
||||
</EuiButton>,
|
||||
<EuiButton
|
||||
onClick={onCreateTrigger}
|
||||
disabled={monitor.triggers.length >= MAX_TRIGGERS}
|
||||
fill
|
||||
>
|
||||
Create
|
||||
{formatMessage({ id: 'form.button.create' })}
|
||||
</EuiButton>,
|
||||
]}
|
||||
>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, {useEffect, useState} from "react";
|
||||
import React, {useEffect, useState, useMemo} from "react";
|
||||
import {Spin, Card} from 'antd';
|
||||
import {Fetch} from '../../../../components/kibana/core/public/http/fetch';
|
||||
import './overview.scss';
|
||||
import {
|
||||
Axis,
|
||||
|
@ -13,9 +14,11 @@ import {
|
|||
timeFormatter,
|
||||
BarSeries,
|
||||
} from "@elastic/charts";
|
||||
import {pathPrefix} from '@/services/common';
|
||||
|
||||
import {useAlertData, useAlertHsitoryData} from './hooks/use_alert_data';
|
||||
import {AlertList} from '../Dashboard/components/AlertList/AlertList';
|
||||
import { formatMessage } from 'umi/locale';
|
||||
|
||||
const theme = {
|
||||
legend: {
|
||||
|
@ -70,7 +73,7 @@ const theme = {
|
|||
|
||||
|
||||
export default (props)=>{
|
||||
const {httpClient, history} = props;
|
||||
const {history} = props;
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [data, setData] = useState({
|
||||
metrics: {
|
||||
|
@ -78,8 +81,18 @@ export default (props)=>{
|
|||
top_ten_cluster:{},
|
||||
},
|
||||
});
|
||||
const httpClient = useMemo(()=>{
|
||||
return new Fetch({
|
||||
basePath:{
|
||||
get: () => '',
|
||||
prepend: (url) => url,
|
||||
remove: (url) => url,
|
||||
serverBasePath: '',
|
||||
}
|
||||
});
|
||||
})
|
||||
useEffect(()=>{
|
||||
httpClient.get('/alerting/overview', {}).then((resp) => {
|
||||
httpClient.get(pathPrefix + '/alerting/overview', {}).then((resp) => {
|
||||
if (resp.ok) {
|
||||
const { metrics, state_count } = resp;
|
||||
setData({
|
||||
|
@ -97,9 +110,18 @@ export default (props)=>{
|
|||
const [historyAlerts, onAlertHistoryPageChange] = useAlertHsitoryData(pageSize);
|
||||
|
||||
const onItemClick = (item)=>{
|
||||
history.push(`/monitors/${item.monitor_id}/elasticsearch/${item.cluster_id}`)
|
||||
history.push(`/alerting/monitor/monitors/${item.monitor_id}/elasticsearch/${item.cluster_id}`)
|
||||
}
|
||||
const pickLegendItems = (items)=>{
|
||||
return [{title:'ACKNOWLEDGED',color:'pink'}, {title:'ACTIVE',color:' rgb(208, 2, 27)'},
|
||||
{title:'ERROR', color:'lightgrey'}, {color:'rgb(208, 2, 27)', title:'COMPLETED'}, {title:'DELETED', color:'gray'}]
|
||||
.filter(legend=> items.includes(legend.title)).map(legend=>{
|
||||
return {
|
||||
...legend,
|
||||
title: formatMessage({id: `alert.dashboard.state-options.${legend.title.toLowerCase()}`})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="overview-wrapper">
|
||||
|
@ -107,19 +129,29 @@ export default (props)=>{
|
|||
<div className="layout">
|
||||
<div className="left">
|
||||
<div className="state-count">
|
||||
<Card className="item" title="激活告警">
|
||||
{data.state_count?.ACTIVE || 0}
|
||||
<Card className="item" bodyStyle={{ paddingBottom: 20 }}>
|
||||
<Card.Meta title={formatMessage({id:'alert.overview.metric.active'})} className="title" />
|
||||
<div>
|
||||
<span className="total">{data.state_count?.ACTIVE || 0}</span>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="item" title="已响应告警" >
|
||||
{data.state_count?.ACKNOWLEDGED || 0}
|
||||
<Card className="item" bodyStyle={{ paddingBottom: 20 }}>
|
||||
<Card.Meta title={formatMessage({id:'alert.overview.metric.acknowledged'})} className="title" />
|
||||
<div>
|
||||
<span className="total">{data.state_count?.ACKNOWLEDGED || 0}</span>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="item" title="错误告警">
|
||||
{data.state_count?.ERROR || 0}
|
||||
<Card className="item" bodyStyle={{ paddingBottom: 20 }}>
|
||||
<Card.Meta title={formatMessage({id:'alert.overview.metric.error'})} className="title" />
|
||||
<div>
|
||||
<span className="total">{data.state_count?.ERROR || 0}</span>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<div>
|
||||
<AlertList dataSource={alerts.data}
|
||||
title="Open Alerts"
|
||||
title={formatMessage({id:'alert.overview.alertlist.title'})}
|
||||
legendItems={pickLegendItems(['ACTIVE','ERROR','ACKNOWLEDGED'])}
|
||||
onItemClick={onItemClick}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
|
@ -127,10 +159,11 @@ export default (props)=>{
|
|||
onChange: onAlertPageChange,
|
||||
}}/>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{marginTop:10}}>
|
||||
<AlertList dataSource={historyAlerts.data}
|
||||
title="History Alerts"
|
||||
title={formatMessage({id:'alert.overview.alertlist-history.title'})}
|
||||
onItemClick={onItemClick}
|
||||
legendItems={pickLegendItems(["ACKNOWLEDGED", "ACTIVE", "ERROR", "COMPLETED", "DELETED"])}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
total: historyAlerts.total,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {useState, useEffect} from 'react';
|
||||
import _ from 'lodash';
|
||||
import {pathPrefix} from '@/services/common';
|
||||
|
||||
const getAlerts =
|
||||
async (from, size,type) => {
|
||||
|
@ -15,14 +16,11 @@ const getAlerts =
|
|||
if(qstr){
|
||||
qstr = `?${qstr.slice(1)}`
|
||||
}
|
||||
const resp = await fetch('/elasticsearch/_all/alerting/alerts'+qstr);
|
||||
const resp = await fetch(pathPrefix + '/alerting/overview/alerts'+qstr);
|
||||
return resp.json();
|
||||
// if (resp.ok) {
|
||||
// const { alerts, totalAlerts } = resp;
|
||||
|
||||
}
|
||||
|
||||
export const useAlertData = (pageSize, page)=>{
|
||||
const useData = (pageSize, page, type) => {
|
||||
const [size, setSize] = useState(pageSize || 10);
|
||||
const [pageIndex, setPageIndex] = useState(page || 1);
|
||||
const [alertData, setAlertData] = useState({
|
||||
|
@ -32,7 +30,7 @@ export const useAlertData = (pageSize, page)=>{
|
|||
useEffect(()=>{
|
||||
const from = (pageIndex - 1) * size;
|
||||
const fetchAlerts = async (from, size)=>{
|
||||
const resp = await getAlerts(from, size, 'ALERT');
|
||||
const resp = await getAlerts(from, size, type);
|
||||
if(resp.ok){
|
||||
const { alerts, totalAlerts } = resp;
|
||||
setAlertData({
|
||||
|
@ -43,7 +41,7 @@ export const useAlertData = (pageSize, page)=>{
|
|||
}
|
||||
}
|
||||
fetchAlerts(from,size);
|
||||
}, [pageIndex, size]);
|
||||
}, [pageIndex, size, type]);
|
||||
const changePage = (pageIndex) => {
|
||||
setPageIndex(pageIndex);
|
||||
}
|
||||
|
@ -51,32 +49,10 @@ export const useAlertData = (pageSize, page)=>{
|
|||
return [alertData, changePage];
|
||||
}
|
||||
|
||||
export const useAlertHsitoryData = (pageSize, page)=>{
|
||||
const [size, setSize] = useState(pageSize || 10);
|
||||
const [pageIndex, setPageIndex] = useState(page || 1);
|
||||
const [alertHisotryData, setAlertHisotryData] = useState({
|
||||
data: [],
|
||||
total: 0,
|
||||
});
|
||||
useEffect(()=>{
|
||||
const from = (pageIndex - 1) * size;
|
||||
const fetchHistoryAlerts = async (from, size)=>{
|
||||
const resp = await getAlerts(from, size, 'ALERT_HISTORY');
|
||||
if(resp.ok){
|
||||
const { alerts, totalAlerts } = resp;
|
||||
setAlertHisotryData({
|
||||
...alertHisotryData,
|
||||
data: alerts,
|
||||
total: totalAlerts,
|
||||
})
|
||||
}
|
||||
}
|
||||
fetchHistoryAlerts(from, size);
|
||||
}, [pageIndex, size])
|
||||
export const useAlertData = (pageSize, page)=>{
|
||||
return useData(pageSize, page, 'ALERT');
|
||||
}
|
||||
|
||||
const changePage = (pageIndex) => {
|
||||
setPageIndex(pageIndex);
|
||||
}
|
||||
|
||||
return [alertHisotryData, changePage];
|
||||
export const useAlertHsitoryData = (pageSize, page)=>{
|
||||
return useData(pageSize, page, 'ALERT_HISTORY');
|
||||
}
|
|
@ -6,11 +6,22 @@
|
|||
flex-direction: column;
|
||||
.state-count{
|
||||
display: flex;
|
||||
text-align: center;
|
||||
justify-content: space-between;
|
||||
.item{
|
||||
min-width: 30%;
|
||||
}
|
||||
.title {
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eef1f4;
|
||||
}
|
||||
.total {
|
||||
font-size: 40px;
|
||||
}
|
||||
.unit {
|
||||
color: #767676;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
@ -21,4 +32,5 @@
|
|||
|
||||
.overview-wrapper {
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
}
|
|
@ -68,7 +68,7 @@ export const MAX_QUERY_RESULT_SIZE = 200;
|
|||
|
||||
export const OPEN_DISTRO_PREFIX = 'infini-search-center';
|
||||
|
||||
export const PLUGIN_NAME = `alerting`;
|
||||
export const PLUGIN_NAME = `alerting/monitor`;
|
||||
export const INDEX_PREFIX = `${OPEN_DISTRO_PREFIX}_alerting`;
|
||||
export const INDEX = {
|
||||
SCHEDULED_JOBS: `.${INDEX_PREFIX}-config`,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import { INDEX, MAX_THROTTLE_VALUE, WRONG_THROTTLE_WARNING } from '../utils/constants';
|
||||
import {pathPrefix} from '@/services/common'
|
||||
|
||||
// TODO: Use a validation framework to clean all of this up or create own.
|
||||
|
||||
|
@ -56,8 +57,9 @@ export const validateMonitorName = (httpClient, monitorToEdit) => async (value)
|
|||
index: INDEX.SCHEDULED_JOBS,
|
||||
query: { query: { term: { 'monitor.name.keyword': value } } },
|
||||
};
|
||||
const response = await httpClient.post('/alerting/monitors/_search', {
|
||||
const response = await httpClient.post(pathPrefix + '/alerting/_search', {
|
||||
body: JSON.stringify(options),
|
||||
prependBasePath: false,
|
||||
});
|
||||
if (_.get(response, 'resp.hits.total.value', 0)) {
|
||||
if (!monitorToEdit) return 'Monitor name is already used';
|
||||
|
|
Loading…
Reference in New Issue