diff --git a/docker/initsql/a-n9e.sql b/docker/initsql/a-n9e.sql index f1de7261..ced8e029 100644 --- a/docker/initsql/a-n9e.sql +++ b/docker/initsql/a-n9e.sql @@ -296,14 +296,25 @@ CREATE TABLE `target` ( -- KEY (`group_id`, `type`, `name`) -- ) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4; +CREATE TABLE `metric_view` ( + `id` bigint unsigned not null auto_increment, + `name` varchar(191) not null default '', + `cate` tinyint(1) not null comment '0: preset 1: custom', + `configs` varchar(8192) not null default '', + `create_at` bigint not null default 0, + `create_by` bigint not null default 0 comment 'user id', + `update_at` bigint not null default 0, + PRIMARY KEY (`id`), + KEY (`user_id`) +) 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 '', + `create_by` bigint not null default 0 comment 'user id', `update_at` bigint not null default 0, PRIMARY KEY (`id`), KEY (`user_id`) diff --git a/src/models/alert_aggr_view.go b/src/models/alert_aggr_view.go index 47f6cd73..b5d83794 100644 --- a/src/models/alert_aggr_view.go +++ b/src/models/alert_aggr_view.go @@ -16,9 +16,8 @@ type AlertAggrView struct { 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"` + CreateBy int64 `json:"create_by"` UpdateAt int64 `json:"update_at"` } @@ -49,7 +48,7 @@ func (v *AlertAggrView) Verify() error { "target_note", } - arr := strings.Fields(v.Rule) + arr := strings.Split(v.Rule, "::") for i := 0; i < len(arr); i++ { pair := strings.Split(arr[i], ":") if len(pair) != 2 { @@ -96,17 +95,17 @@ func (v *AlertAggrView) Update(name, rule string) error { } // AlertAggrViewDel: userid for safe delete -func AlertAggrViewDel(ids []int64, userId interface{}) error { +func AlertAggrViewDel(ids []int64, createBy interface{}) error { if len(ids) == 0 { return nil } - return DB().Where("id in ? and user_id = ?", ids, userId).Delete(new(AlertAggrView)).Error + return DB().Where("id in ? and create_by = ?", ids, createBy).Delete(new(AlertAggrView)).Error } -func AlertAggrViewGets(userId interface{}) ([]AlertAggrView, error) { +func AlertAggrViewGets(createBy interface{}) ([]AlertAggrView, error) { var lst []AlertAggrView - err := DB().Where("user_id = ? or cate = 0", userId).Find(&lst).Error + err := DB().Where("create_by = ? or cate = 0", createBy).Find(&lst).Error if err == nil && len(lst) > 0 { sort.Slice(lst, func(i, j int) bool { return lst[i].Name < lst[j].Name diff --git a/src/models/metric_view.go b/src/models/metric_view.go new file mode 100644 index 00000000..91d9d815 --- /dev/null +++ b/src/models/metric_view.go @@ -0,0 +1,95 @@ +package models + +import ( + "errors" + "sort" + "strings" + "time" +) + +// MetricView 在告警聚合视图查看的时候,要存储一些聚合规则 +type MetricView struct { + Id int64 `json:"id" gorm:"primaryKey"` + Name string `json:"name"` + Cate int `json:"cate"` + Configs string `json:"configs"` + CreateAt int64 `json:"create_at"` + CreateBy int64 `json:"create_by"` + UpdateAt int64 `json:"update_at"` +} + +func (v *MetricView) TableName() string { + return "metric_view" +} + +func (v *MetricView) Verify() error { + v.Name = strings.TrimSpace(v.Name) + if v.Name == "" { + return errors.New("name is blank") + } + + v.Configs = strings.TrimSpace(v.Configs) + if v.Configs == "" { + return errors.New("configs is blank") + } + + return nil +} + +func (v *MetricView) 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 *MetricView) Update(name, configs string) error { + if err := v.Verify(); err != nil { + return err + } + + v.UpdateAt = time.Now().Unix() + v.Name = name + v.Configs = configs + + return DB().Model(v).Select("name", "configs", "update_at").Updates(v).Error +} + +// MetricViewDel: userid for safe delete +func MetricViewDel(ids []int64, createBy interface{}) error { + if len(ids) == 0 { + return nil + } + + return DB().Where("id in ? and create_by = ?", ids, createBy).Delete(new(MetricView)).Error +} + +func MetricViewGets(createBy interface{}) ([]MetricView, error) { + var lst []MetricView + err := DB().Where("create_by = ? or cate = 0", createBy).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 MetricViewGet(where string, args ...interface{}) (*MetricView, error) { + var lst []*MetricView + 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 d9d18da5..fd0f196e 100644 --- a/src/webapi/router/router.go +++ b/src/webapi/router/router.go @@ -134,6 +134,11 @@ func configRoute(r *gin.Engine, version string) { pages.PUT("/user/:id/password", jwtAuth(), admin(), userPasswordPut) pages.DELETE("/user/:id", jwtAuth(), admin(), userDel) + pages.GET("/metric-views", jwtAuth(), metricViewGets) + pages.DELETE("/metric-views", jwtAuth(), metricViewDel) + pages.POST("/metric-views", jwtAuth(), metricViewAdd) + pages.PUT("/metric-views", jwtAuth(), metricViewPut) + pages.GET("/user-groups", jwtAuth(), user(), userGroupGets) pages.POST("/user-groups", jwtAuth(), user(), perm("/user-groups/add"), userGroupAdd) pages.GET("/user-group/:id", jwtAuth(), user(), userGroupGet) diff --git a/src/webapi/router/router_alert_aggr_view.go b/src/webapi/router/router_alert_aggr_view.go index 0867df9a..165f566c 100644 --- a/src/webapi/router/router_alert_aggr_view.go +++ b/src/webapi/router/router_alert_aggr_view.go @@ -8,23 +8,24 @@ import ( "github.com/toolkits/pkg/ginx" ) +// no param func alertAggrViewGets(c *gin.Context) { lst, err := models.AlertAggrViewGets(c.MustGet("userid")) ginx.NewRender(c).Data(lst, err) } -// name and rule is necessary +// body: name, rule 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) + f.CreateBy = c.MustGet("userid").(int64) ginx.NewRender(c).Message(f.Add()) } +// body: ids func alertAggrViewDel(c *gin.Context) { var f idsForm ginx.BindJSON(c, &f) @@ -32,7 +33,7 @@ func alertAggrViewDel(c *gin.Context) { ginx.NewRender(c).Message(models.AlertAggrViewDel(f.Ids, c.MustGet("userid"))) } -// id / name / rule is necessary +// body: id, name, rule func alertAggrViewPut(c *gin.Context) { var f models.AlertAggrView ginx.BindJSON(c, &f) @@ -46,7 +47,7 @@ func alertAggrViewPut(c *gin.Context) { } userid := c.MustGet("userid").(int64) - if view.UserId != userid { + if view.CreateBy != userid { ginx.NewRender(c, http.StatusForbidden).Message("forbidden") return } diff --git a/src/webapi/router/router_metric_view.go b/src/webapi/router/router_metric_view.go new file mode 100644 index 00000000..6d7b4e8e --- /dev/null +++ b/src/webapi/router/router_metric_view.go @@ -0,0 +1,56 @@ +package router + +import ( + "net/http" + + "github.com/didi/nightingale/v5/src/models" + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/ginx" +) + +// no param +func metricViewGets(c *gin.Context) { + lst, err := models.MetricViewGets(c.MustGet("userid")) + ginx.NewRender(c).Data(lst, err) +} + +// body: name, configs +func metricViewAdd(c *gin.Context) { + var f models.MetricView + ginx.BindJSON(c, &f) + + f.Id = 0 + f.CreateBy = c.MustGet("userid").(int64) + + ginx.NewRender(c).Message(f.Add()) +} + +// body: ids +func metricViewDel(c *gin.Context) { + var f idsForm + ginx.BindJSON(c, &f) + + ginx.NewRender(c).Message(models.MetricViewDel(f.Ids, c.MustGet("userid"))) +} + +// body: id, name, configs +func metricViewPut(c *gin.Context) { + var f models.MetricView + ginx.BindJSON(c, &f) + + view, err := models.MetricViewGet("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.CreateBy != userid { + ginx.NewRender(c, http.StatusForbidden).Message("forbidden") + return + } + + ginx.NewRender(c).Message(view.Update(f.Name, f.Configs)) +}