diff --git a/docker/initsql/a-n9e.sql b/docker/initsql/a-n9e.sql index 10b1080e..c17b71d0 100644 --- a/docker/initsql/a-n9e.sql +++ b/docker/initsql/a-n9e.sql @@ -296,6 +296,22 @@ CREATE TABLE `target` ( -- KEY (`group_id`, `type`, `name`) -- ) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4; +CREATE TABLE `alert_aggr_view` ( + `id` bigint unsigned not null auto_increment, + `name` varchar(191) not null default '', + `rule` varchar(2048) not null default '', + `cate` tinyint(1) not null comment '0: preset 1: custom', + `user_id` bigint not null default 0, + `create_at` bigint not null default 0, + `create_by` varchar(64) not null default '', + `update_at` bigint not null default 0, + PRIMARY KEY (`id`), + KEY (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4; + +insert into alert_aggr_view(name, rule, cate) values('GroupBy BusiGroup, Severity', 'field:group_name field:severity', 0); +insert into alert_aggr_view(name, rule, cate) values('GroupBy Metric', 'tagkey:__name__', 0); + CREATE TABLE `alert_cur_event` ( `id` bigint unsigned not null comment 'use alert_his_event.id', `cluster` varchar(128) not null, diff --git a/src/models/alert_aggr_view.go b/src/models/alert_aggr_view.go new file mode 100644 index 00000000..3d566db2 --- /dev/null +++ b/src/models/alert_aggr_view.go @@ -0,0 +1,96 @@ +package models + +import ( + "errors" + "sort" + "strings" + "time" +) + +// AlertAggrView 在告警聚合视图查看的时候,要存储一些聚合规则 +type AlertAggrView struct { + Id int64 `json:"id" gorm:"primaryKey"` + Name string `json:"name"` + Rule string `json:"rule"` + Cate int `json:"cate"` + UserId int64 `json:"user_id"` + CreateAt int64 `json:"create_at"` + CreateBy string `json:"create_by"` + UpdateAt int64 `json:"update_at"` +} + +func (v *AlertAggrView) TableName() string { + return "alert_aggr_view" +} + +func (v *AlertAggrView) Verify() error { + v.Name = strings.TrimSpace(v.Name) + if v.Name == "" { + return errors.New("name is blank") + } + + v.Rule = strings.TrimSpace(v.Rule) + if v.Rule == "" { + return errors.New("rule is blank") + } + + return nil +} + +func (v *AlertAggrView) Add() error { + if err := v.Verify(); err != nil { + return err + } + + now := time.Now().Unix() + v.CreateAt = now + v.UpdateAt = now + v.Cate = 1 + return Insert(v) +} + +func (v *AlertAggrView) Update(name, rule string) error { + if err := v.Verify(); err != nil { + return err + } + + v.UpdateAt = time.Now().Unix() + v.Name = name + v.Rule = rule + + return DB().Model(v).Select("name", "rule", "update_at").Updates(v).Error +} + +// AlertAggrViewDel: userid for safe delete +func AlertAggrViewDel(ids []int64, userId interface{}) error { + if len(ids) == 0 { + return nil + } + + return DB().Where("id in ? and user_id = ?", ids, userId).Delete(new(AlertAggrView)).Error +} + +func AlertAggrViewGets(userId interface{}) ([]AlertAggrView, error) { + var lst []AlertAggrView + err := DB().Where("user_id = ? or cate = 0", userId).Find(&lst).Error + if err == nil && len(lst) > 0 { + sort.Slice(lst, func(i, j int) bool { + return lst[i].Name < lst[j].Name + }) + } + return lst, err +} + +func AlertAggrViewGet(where string, args ...interface{}) (*AlertAggrView, error) { + var lst []*AlertAggrView + err := DB().Where(where, args...).Find(&lst).Error + if err != nil { + return nil, err + } + + if len(lst) == 0 { + return nil, nil + } + + return lst[0], nil +} diff --git a/src/webapi/router/router.go b/src/webapi/router/router.go index 251f5b1a..5e49f323 100644 --- a/src/webapi/router/router.go +++ b/src/webapi/router/router.go @@ -217,6 +217,11 @@ func configRoute(r *gin.Engine, version string) { pages.GET("/alert-his-event/:eid", jwtAuth(), alertHisEventGet) } + pages.GET("/alert-aggr-views", jwtAuth(), alertAggrViewGets) + pages.DELETE("/alert-aggr-views", jwtAuth(), alertAggrViewDel) + pages.POST("/alert-aggr-views", jwtAuth(), alertAggrViewAdd) + pages.PUT("/alert-aggr-views", jwtAuth(), alertAggrViewPut) + pages.GET("/busi-group/:id/task-tpls", jwtAuth(), user(), perm("/job-tpls"), bgro(), taskTplGets) pages.POST("/busi-group/:id/task-tpls", jwtAuth(), user(), perm("/job-tpls/add"), bgrw(), taskTplAdd) pages.DELETE("/busi-group/:id/task-tpl/:tid", jwtAuth(), user(), perm("/job-tpls/del"), bgrw(), taskTplDel) diff --git a/src/webapi/router/router_alert_aggr_view.go b/src/webapi/router/router_alert_aggr_view.go new file mode 100644 index 00000000..0867df9a --- /dev/null +++ b/src/webapi/router/router_alert_aggr_view.go @@ -0,0 +1,55 @@ +package router + +import ( + "net/http" + + "github.com/didi/nightingale/v5/src/models" + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/ginx" +) + +func alertAggrViewGets(c *gin.Context) { + lst, err := models.AlertAggrViewGets(c.MustGet("userid")) + ginx.NewRender(c).Data(lst, err) +} + +// name and rule is necessary +func alertAggrViewAdd(c *gin.Context) { + var f models.AlertAggrView + ginx.BindJSON(c, &f) + + f.Id = 0 + f.CreateBy = c.MustGet("username").(string) + f.UserId = c.MustGet("userid").(int64) + + ginx.NewRender(c).Message(f.Add()) +} + +func alertAggrViewDel(c *gin.Context) { + var f idsForm + ginx.BindJSON(c, &f) + + ginx.NewRender(c).Message(models.AlertAggrViewDel(f.Ids, c.MustGet("userid"))) +} + +// id / name / rule is necessary +func alertAggrViewPut(c *gin.Context) { + var f models.AlertAggrView + ginx.BindJSON(c, &f) + + view, err := models.AlertAggrViewGet("id = ?", f.Id) + ginx.Dangerous(err) + + if view == nil { + ginx.NewRender(c).Message("no such item(id: %d)", f.Id) + return + } + + userid := c.MustGet("userid").(int64) + if view.UserId != userid { + ginx.NewRender(c, http.StatusForbidden).Message("forbidden") + return + } + + ginx.NewRender(c).Message(view.Update(f.Name, f.Rule)) +} diff --git a/src/webapi/router/router_collect_rule.go b/src/webapi/router/router_collect_rule.go index 7912612f..fb485f79 100644 --- a/src/webapi/router/router_collect_rule.go +++ b/src/webapi/router/router_collect_rule.go @@ -1,79 +1,79 @@ package router -import ( - "net/http" - - "github.com/gin-gonic/gin" - "github.com/toolkits/pkg/ginx" - - "github.com/didi/nightingale/v5/src/models" -) - -func collectRuleGets(c *gin.Context) { - busiGroupId := ginx.UrlParamInt64(c, "id") - crs, err := models.CollectRuleGets(busiGroupId, ginx.QueryStr(c, "type", "")) - ginx.NewRender(c).Data(crs, err) -} - -func collectRuleAdd(c *gin.Context) { - var lst []models.CollectRule - ginx.BindJSON(c, &lst) - - count := len(lst) - if count == 0 { - ginx.Bomb(http.StatusBadRequest, "input json is empty") - } - - username := c.MustGet("username").(string) - bgid := ginx.UrlParamInt64(c, "id") - - // collect rule name -> error string - reterr := make(map[string]string) - for i := 0; i < count; i++ { - lst[i].Id = 0 - lst[i].GroupId = bgid - lst[i].CreateBy = username - lst[i].UpdateBy = username - lst[i].FE2DB() - - if err := lst[i].Add(); err != nil { - reterr[lst[i].Name] = err.Error() - } else { - reterr[lst[i].Name] = "" - } - } - - ginx.NewRender(c).Data(reterr, nil) -} - -func collectRuleDel(c *gin.Context) { - var f idsForm - ginx.BindJSON(c, &f) - f.Verify() - - // param(busiGroupId) for protect - ginx.NewRender(c).Message(models.CollectRuleDels(f.Ids, ginx.UrlParamInt64(c, "id"))) -} - -func collectRuleGet(c *gin.Context) { - crid := ginx.UrlParamInt64(c, "crid") - cr, err := models.CollectRuleGetById(crid) - ginx.NewRender(c).Data(cr, err) -} - -func collectRulePut(c *gin.Context) { - var f models.CollectRule - ginx.BindJSON(c, &f) - - crid := ginx.UrlParamInt64(c, "crid") - cr, err := models.CollectRuleGetById(crid) - ginx.Dangerous(err) - - if cr == nil { - ginx.NewRender(c, http.StatusNotFound).Message("No such CollectRule") - return - } - - f.UpdateBy = c.MustGet("username").(string) - ginx.NewRender(c).Message(cr.Update(f)) -} +// import ( +// "net/http" + +// "github.com/gin-gonic/gin" +// "github.com/toolkits/pkg/ginx" + +// "github.com/didi/nightingale/v5/src/models" +// ) + +// func collectRuleGets(c *gin.Context) { +// busiGroupId := ginx.UrlParamInt64(c, "id") +// crs, err := models.CollectRuleGets(busiGroupId, ginx.QueryStr(c, "type", "")) +// ginx.NewRender(c).Data(crs, err) +// } + +// func collectRuleAdd(c *gin.Context) { +// var lst []models.CollectRule +// ginx.BindJSON(c, &lst) + +// count := len(lst) +// if count == 0 { +// ginx.Bomb(http.StatusBadRequest, "input json is empty") +// } + +// username := c.MustGet("username").(string) +// bgid := ginx.UrlParamInt64(c, "id") + +// // collect rule name -> error string +// reterr := make(map[string]string) +// for i := 0; i < count; i++ { +// lst[i].Id = 0 +// lst[i].GroupId = bgid +// lst[i].CreateBy = username +// lst[i].UpdateBy = username +// lst[i].FE2DB() + +// if err := lst[i].Add(); err != nil { +// reterr[lst[i].Name] = err.Error() +// } else { +// reterr[lst[i].Name] = "" +// } +// } + +// ginx.NewRender(c).Data(reterr, nil) +// } + +// func collectRuleDel(c *gin.Context) { +// var f idsForm +// ginx.BindJSON(c, &f) +// f.Verify() + +// // param(busiGroupId) for protect +// ginx.NewRender(c).Message(models.CollectRuleDels(f.Ids, ginx.UrlParamInt64(c, "id"))) +// } + +// func collectRuleGet(c *gin.Context) { +// crid := ginx.UrlParamInt64(c, "crid") +// cr, err := models.CollectRuleGetById(crid) +// ginx.NewRender(c).Data(cr, err) +// } + +// func collectRulePut(c *gin.Context) { +// var f models.CollectRule +// ginx.BindJSON(c, &f) + +// crid := ginx.UrlParamInt64(c, "crid") +// cr, err := models.CollectRuleGetById(crid) +// ginx.Dangerous(err) + +// if cr == nil { +// ginx.NewRender(c, http.StatusNotFound).Message("No such CollectRule") +// return +// } + +// f.UpdateBy = c.MustGet("username").(string) +// ginx.NewRender(c).Message(cr.Update(f)) +// } diff --git a/src/webapi/router/router_dashboard.go b/src/webapi/router/router_dashboard.go index 46fdbb91..1ac9bb44 100644 --- a/src/webapi/router/router_dashboard.go +++ b/src/webapi/router/router_dashboard.go @@ -223,10 +223,6 @@ func dashboardImport(c *gin.Context) { ginx.NewRender(c).Data(ret, nil) } -type idForm struct { - Id int64 `json:"id" binding:"required"` -} - func dashboardClone(c *gin.Context) { dash := Dashboard(ginx.UrlParamInt64(c, "did")) user := c.MustGet("user").(*models.User) diff --git a/src/webapi/router/router_task_tpl.go b/src/webapi/router/router_task_tpl.go index 0569789f..60b9e306 100644 --- a/src/webapi/router/router_task_tpl.go +++ b/src/webapi/router/router_task_tpl.go @@ -45,7 +45,7 @@ func taskTplGet(c *gin.Context) { ginx.NewRender(c).Data(gin.H{ "tpl": tpl, "hosts": hosts, - }, nil) + }, err) } type taskTplForm struct { @@ -97,7 +97,8 @@ func taskTplPut(c *gin.Context) { ginx.Dangerous(err) if tpl == nil { - ginx.Dangerous("no such task template") + ginx.NewRender(c).Message("no such task template") + return } user := c.MustGet("user").(*models.User)