Compare commits

...

2 Commits

Author SHA1 Message Date
naiba
5128bfff61 feat: show online user id
Some checks are pending
CodeQL / Analyze (go) (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
Contributors / contributors (push) Waiting to run
Sync / sync-to-jihulab (push) Waiting to run
Run Tests / tests (macos) (push) Waiting to run
Run Tests / tests (ubuntu) (push) Waiting to run
Run Tests / tests (windows) (push) Waiting to run
2024-12-24 23:28:37 +08:00
UUBulb
d50605d668
feat: support id query for "list" apis (#908)
* feat: support id query for "list" apis

* gosec
2024-12-24 23:23:01 +08:00
13 changed files with 93 additions and 7 deletions

View File

@ -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]

View File

@ -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)})
}
}

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -33,7 +33,7 @@ func listBlockedAddress(c *gin.Context) (*model.Value[[]*model.WAF], error) {
}
var waf []*model.WAF
if err := singleton.DB.Limit(limit).Offset(offset).Find(&waf).Error; err != nil {
if err := singleton.DB.Order("block_timestamp DESC").Limit(limit).Offset(offset).Find(&waf).Error; err != nil {
return nil, err
}

View File

@ -119,7 +119,14 @@ func serverStream(c *gin.Context) (any, error) {
userIp = c.RemoteIP()
}
u, isMember := c.Get(model.CtxKeyAuthorizedUser)
var userId uint64
if isMember {
userId = u.(*model.User).ID
}
singleton.AddOnlineUser(connId, &model.OnlineUser{
UserID: userId,
IP: userIp,
ConnectedAt: time.Now(),
Conn: conn,
@ -128,7 +135,7 @@ func serverStream(c *gin.Context) (any, error) {
count := 0
for {
stat, err := getServerStat(c, count == 0)
stat, err := getServerStat(count == 0, isMember)
if err != nil {
continue
}
@ -149,9 +156,7 @@ func serverStream(c *gin.Context) (any, error) {
var requestGroup singleflight.Group
func getServerStat(c *gin.Context, withPublicNote bool) ([]byte, error) {
_, isMember := c.Get(model.CtxKeyAuthorizedUser)
authorized := isMember // TODO || isViewPasswordVerfied
func getServerStat(withPublicNote, authorized bool) ([]byte, error) {
v, err, _ := requestGroup.Do(fmt.Sprintf("serverStats::%t", authorized), func() (interface{}, error) {
singleton.SortedServerLock.RLock()
defer singleton.SortedServerLock.RUnlock()

View File

@ -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"`

View File

@ -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:]
}

View File

@ -9,7 +9,7 @@
name: "Official"
repository: "https://github.com/hamster1963/nezha-dash-v1"
author: "hamster1963"
version: "v1.8.0"
version: "v1.8.1"
isofficial: true
- path: "nazhua-dist"
name: "Nazhua"