From d50605d668a331208c96884c6313f25ad254e9b0 Mon Sep 17 00:00:00 2001 From: UUBulb <35923940+uubulb@users.noreply.github.com> Date: Tue, 24 Dec 2024 23:23:01 +0800 Subject: [PATCH] feat: support id query for "list" apis (#908) * feat: support id query for "list" apis * gosec --- cmd/dashboard/controller/alertrule.go | 1 + cmd/dashboard/controller/controller.go | 3 +- cmd/dashboard/controller/cron.go | 1 + cmd/dashboard/controller/ddns.go | 1 + cmd/dashboard/controller/nat.go | 1 + cmd/dashboard/controller/notification.go | 1 + cmd/dashboard/controller/server.go | 1 + cmd/dashboard/controller/service.go | 1 + model/common.go | 55 ++++++++++++++++++++++++ model/server.go | 18 ++++++++ 10 files changed, 82 insertions(+), 1 deletion(-) diff --git a/cmd/dashboard/controller/alertrule.go b/cmd/dashboard/controller/alertrule.go index 2b22734..78aa392 100644 --- a/cmd/dashboard/controller/alertrule.go +++ b/cmd/dashboard/controller/alertrule.go @@ -17,6 +17,7 @@ import ( // @Schemes // @Description List Alert rules // @Tags auth required +// @Param id query uint false "Resource ID" // @Produce json // @Success 200 {object} model.CommonResponse[[]model.AlertRule] // @Router /alert-rule [get] diff --git a/cmd/dashboard/controller/controller.go b/cmd/dashboard/controller/controller.go index ab1e62e..6454b28 100644 --- a/cmd/dashboard/controller/controller.go +++ b/cmd/dashboard/controller/controller.go @@ -247,7 +247,8 @@ func listHandler[S ~[]E, E model.CommonInterface](handler handlerFunc[S]) func(* return } - c.JSON(http.StatusOK, model.CommonResponse[S]{Success: true, Data: filter(c, data)}) + filtered := filter(c, data) + c.JSON(http.StatusOK, model.CommonResponse[S]{Success: true, Data: model.SearchByIDCtx(c, filtered)}) } } diff --git a/cmd/dashboard/controller/cron.go b/cmd/dashboard/controller/cron.go index 4c54541..2d019e9 100644 --- a/cmd/dashboard/controller/cron.go +++ b/cmd/dashboard/controller/cron.go @@ -16,6 +16,7 @@ import ( // @Schemes // @Description List schedule tasks // @Tags auth required +// @Param id query uint false "Resource ID" // @Produce json // @Success 200 {object} model.CommonResponse[[]model.Cron] // @Router /cron [get] diff --git a/cmd/dashboard/controller/ddns.go b/cmd/dashboard/controller/ddns.go index c8a168b..556d5ab 100644 --- a/cmd/dashboard/controller/ddns.go +++ b/cmd/dashboard/controller/ddns.go @@ -17,6 +17,7 @@ import ( // @Description List DDNS profiles // @Security BearerAuth // @Tags auth required +// @Param id query uint false "Resource ID" // @Produce json // @Success 200 {object} model.CommonResponse[[]model.DDNSProfile] // @Router /ddns [get] diff --git a/cmd/dashboard/controller/nat.go b/cmd/dashboard/controller/nat.go index 20260ad..8a6156f 100644 --- a/cmd/dashboard/controller/nat.go +++ b/cmd/dashboard/controller/nat.go @@ -16,6 +16,7 @@ import ( // @Description List NAT profiles // @Security BearerAuth // @Tags auth required +// @Param id query uint false "Resource ID" // @Produce json // @Success 200 {object} model.CommonResponse[[]model.NAT] // @Router /nat [get] diff --git a/cmd/dashboard/controller/notification.go b/cmd/dashboard/controller/notification.go index 1180dcb..7fd87a1 100644 --- a/cmd/dashboard/controller/notification.go +++ b/cmd/dashboard/controller/notification.go @@ -16,6 +16,7 @@ import ( // @Schemes // @Description List notification // @Tags auth required +// @Param id query uint false "Resource ID" // @Produce json // @Success 200 {object} model.CommonResponse[[]model.Notification] // @Router /notification [get] diff --git a/cmd/dashboard/controller/server.go b/cmd/dashboard/controller/server.go index 85d9fb7..c28f3c1 100644 --- a/cmd/dashboard/controller/server.go +++ b/cmd/dashboard/controller/server.go @@ -19,6 +19,7 @@ import ( // @Schemes // @Description List server // @Tags auth required +// @Param id query uint false "Resource ID" // @Produce json // @Success 200 {object} model.CommonResponse[[]model.Server] // @Router /server [get] diff --git a/cmd/dashboard/controller/service.go b/cmd/dashboard/controller/service.go index 7395af5..d1ac827 100644 --- a/cmd/dashboard/controller/service.go +++ b/cmd/dashboard/controller/service.go @@ -50,6 +50,7 @@ func showService(c *gin.Context) (*model.ServiceResponse, error) { // @Schemes // @Description List service // @Tags auth required +// @Param id query uint false "Resource ID" // @Produce json // @Success 200 {object} model.CommonResponse[[]model.Service] // @Router /service [get] diff --git a/model/common.go b/model/common.go index 607c1bc..9c7c2db 100644 --- a/model/common.go +++ b/model/common.go @@ -1,9 +1,14 @@ package model import ( + "cmp" + "slices" + "strconv" + "strings" "time" "github.com/gin-gonic/gin" + "github.com/nezhahq/nezha/pkg/utils" ) const ( @@ -61,6 +66,56 @@ func FindByUserID[S ~[]E, E CommonInterface](s S, uid uint64) []uint64 { return list } +func SearchByIDCtx[S ~[]E, E CommonInterface](c *gin.Context, x S) S { + switch any(x).(type) { + case []*Server: + l := searchByIDCtxServer(c, any(x).([]*Server)) + return any(l).(S) + default: + var s S + for _, idStr := range strings.Split(c.Query("id"), ",") { + id, err := strconv.ParseUint(idStr, 10, 64) + if err != nil { + continue + } + + if i, ok := slices.BinarySearchFunc(x, id, func(e E, t uint64) int { + return cmp.Compare(e.GetID(), t) + }); ok { + s = append(s, x[i]) + } + } + return utils.IfOr(len(s) > 0, s, x) + } +} + +func searchByIDCtxServer(c *gin.Context, x []*Server) []*Server { + list1, list2 := SplitList(x) + + var clist1, clist2 []*Server + for _, idStr := range strings.Split(c.Query("id"), ",") { + id, err := strconv.ParseUint(idStr, 10, 64) + if err != nil { + continue + } + + if i, ok := slices.BinarySearchFunc(list1, id, func(e *Server, t uint64) int { + return cmp.Compare(e.ID, t) + }); ok { + clist1 = append(clist1, list1[i]) + } + + if i, ok := slices.BinarySearchFunc(list2, id, func(e *Server, t uint64) int { + return cmp.Compare(e.ID, t) + }); ok { + clist2 = append(clist2, list2[i]) + } + } + + l := slices.Concat(clist1, clist2) + return utils.IfOr(len(l) > 0, l, x) +} + type Response struct { Code int `json:"code,omitempty"` Message string `json:"message,omitempty"` diff --git a/model/server.go b/model/server.go index 240cb9a..e1dd272 100644 --- a/model/server.go +++ b/model/server.go @@ -2,6 +2,7 @@ package model import ( "log" + "slices" "time" "gorm.io/gorm" @@ -54,3 +55,20 @@ func (s *Server) AfterFind(tx *gorm.DB) error { } return nil } + +// Split a sorted server list into two separate lists: +// The first list contains servers with a priority set (DisplayIndex != 0). +// The second list contains servers without a priority set (DisplayIndex == 0). +// The original slice is not modified. If no server without a priority is found, it returns nil. +func SplitList(x []*Server) ([]*Server, []*Server) { + pri := func(s *Server) bool { + return s.DisplayIndex == 0 + } + + i := slices.IndexFunc(x, pri) + if i == -1 { + return nil, x + } + + return x[:i], x[i:] +}