mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-02-04 12:40:07 +00:00
feat: user roles (#852)
* [WIP] feat: user roles * update * update * admin handler * update * feat: user-specific connection secret * simplify some logics * cleanup * update waf * update user api error handling * update waf api * fix codeql * update waf table * fix several problems * add pagination for waf api * update permission checks * switch to runtime check * 1 * cover? * some changes
This commit is contained in:
@@ -50,6 +50,9 @@ func createAlertRule(c *gin.Context) (uint64, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
uid := getUid(c)
|
||||
|
||||
r.UserID = uid
|
||||
r.Name = arf.Name
|
||||
r.Rules = arf.Rules
|
||||
r.FailTriggerTasks = arf.FailTriggerTasks
|
||||
@@ -59,7 +62,7 @@ func createAlertRule(c *gin.Context) (uint64, error) {
|
||||
r.TriggerMode = arf.TriggerMode
|
||||
r.Enable = &enable
|
||||
|
||||
if err := validateRule(&r); err != nil {
|
||||
if err := validateRule(c, &r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -100,6 +103,10 @@ func updateAlertRule(c *gin.Context) (any, error) {
|
||||
return nil, singleton.Localizer.ErrorT("alert id %d does not exist", id)
|
||||
}
|
||||
|
||||
if !r.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
r.Name = arf.Name
|
||||
r.Rules = arf.Rules
|
||||
r.FailTriggerTasks = arf.FailTriggerTasks
|
||||
@@ -109,7 +116,7 @@ func updateAlertRule(c *gin.Context) (any, error) {
|
||||
r.TriggerMode = arf.TriggerMode
|
||||
r.Enable = &enable
|
||||
|
||||
if err := validateRule(&r); err != nil {
|
||||
if err := validateRule(c, &r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -134,11 +141,21 @@ func updateAlertRule(c *gin.Context) (any, error) {
|
||||
// @Router /batch-delete/alert-rule [post]
|
||||
func batchDeleteAlertRule(c *gin.Context) (any, error) {
|
||||
var ar []uint64
|
||||
|
||||
if err := c.ShouldBindJSON(&ar); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ars []model.AlertRule
|
||||
if err := singleton.DB.Where("id in (?)", ar).Find(&ars).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, a := range ars {
|
||||
if !a.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
|
||||
if err := singleton.DB.Unscoped().Delete(&model.AlertRule{}, "id in (?)", ar).Error; err != nil {
|
||||
return nil, newGormError("%v", err)
|
||||
}
|
||||
@@ -147,9 +164,20 @@ func batchDeleteAlertRule(c *gin.Context) (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func validateRule(r *model.AlertRule) error {
|
||||
func validateRule(c *gin.Context, r *model.AlertRule) error {
|
||||
if len(r.Rules) > 0 {
|
||||
for _, rule := range r.Rules {
|
||||
singleton.ServerLock.RLock()
|
||||
for s := range rule.Ignore {
|
||||
if server, ok := singleton.ServerList[s]; ok {
|
||||
if !server.HasPermission(c) {
|
||||
singleton.ServerLock.RUnlock()
|
||||
return singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.ServerLock.RUnlock()
|
||||
|
||||
if !rule.IsTransferDurationRule() {
|
||||
if rule.Duration < 3 {
|
||||
return singleton.Localizer.ErrorT("duration need to be at least 3")
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
@@ -78,11 +79,11 @@ func routers(r *gin.Engine, frontendDist fs.FS) {
|
||||
|
||||
auth.GET("/profile", commonHandler(getProfile))
|
||||
auth.POST("/profile", commonHandler(updateProfile))
|
||||
auth.GET("/user", commonHandler(listUser))
|
||||
auth.POST("/user", commonHandler(createUser))
|
||||
auth.POST("/batch-delete/user", commonHandler(batchDeleteUser))
|
||||
auth.GET("/user", adminHandler(listUser))
|
||||
auth.POST("/user", adminHandler(createUser))
|
||||
auth.POST("/batch-delete/user", adminHandler(batchDeleteUser))
|
||||
|
||||
auth.GET("/service/list", commonHandler(listService))
|
||||
auth.GET("/service/list", listHandler(listService))
|
||||
auth.POST("/service", commonHandler(createService))
|
||||
auth.PATCH("/service/:id", commonHandler(updateService))
|
||||
auth.POST("/batch-delete/service", commonHandler(batchDeleteService))
|
||||
@@ -96,42 +97,42 @@ func routers(r *gin.Engine, frontendDist fs.FS) {
|
||||
auth.PATCH("/notification-group/:id", commonHandler(updateNotificationGroup))
|
||||
auth.POST("/batch-delete/notification-group", commonHandler(batchDeleteNotificationGroup))
|
||||
|
||||
auth.GET("/server", commonHandler(listServer))
|
||||
auth.GET("/server", listHandler(listServer))
|
||||
auth.PATCH("/server/:id", commonHandler(updateServer))
|
||||
auth.POST("/batch-delete/server", commonHandler(batchDeleteServer))
|
||||
auth.POST("/force-update/server", commonHandler(forceUpdateServer))
|
||||
|
||||
auth.GET("/notification", commonHandler(listNotification))
|
||||
auth.GET("/notification", listHandler(listNotification))
|
||||
auth.POST("/notification", commonHandler(createNotification))
|
||||
auth.PATCH("/notification/:id", commonHandler(updateNotification))
|
||||
auth.POST("/batch-delete/notification", commonHandler(batchDeleteNotification))
|
||||
|
||||
auth.GET("/alert-rule", commonHandler(listAlertRule))
|
||||
auth.GET("/alert-rule", listHandler(listAlertRule))
|
||||
auth.POST("/alert-rule", commonHandler(createAlertRule))
|
||||
auth.PATCH("/alert-rule/:id", commonHandler(updateAlertRule))
|
||||
auth.POST("/batch-delete/alert-rule", commonHandler(batchDeleteAlertRule))
|
||||
|
||||
auth.GET("/cron", commonHandler(listCron))
|
||||
auth.GET("/cron", listHandler(listCron))
|
||||
auth.POST("/cron", commonHandler(createCron))
|
||||
auth.PATCH("/cron/:id", commonHandler(updateCron))
|
||||
auth.GET("/cron/:id/manual", commonHandler(manualTriggerCron))
|
||||
auth.POST("/batch-delete/cron", commonHandler(batchDeleteCron))
|
||||
|
||||
auth.GET("/ddns", commonHandler(listDDNS))
|
||||
auth.GET("/ddns", listHandler(listDDNS))
|
||||
auth.GET("/ddns/providers", commonHandler(listProviders))
|
||||
auth.POST("/ddns", commonHandler(createDDNS))
|
||||
auth.PATCH("/ddns/:id", commonHandler(updateDDNS))
|
||||
auth.POST("/batch-delete/ddns", commonHandler(batchDeleteDDNS))
|
||||
|
||||
auth.GET("/nat", commonHandler(listNAT))
|
||||
auth.GET("/nat", listHandler(listNAT))
|
||||
auth.POST("/nat", commonHandler(createNAT))
|
||||
auth.PATCH("/nat/:id", commonHandler(updateNAT))
|
||||
auth.POST("/batch-delete/nat", commonHandler(batchDeleteNAT))
|
||||
|
||||
auth.GET("/waf", commonHandler(listBlockedAddress))
|
||||
auth.POST("/batch-delete/waf", commonHandler(batchDeleteBlockedAddress))
|
||||
auth.GET("/waf", pCommonHandler(listBlockedAddress))
|
||||
auth.POST("/batch-delete/waf", adminHandler(batchDeleteBlockedAddress))
|
||||
|
||||
auth.PATCH("/setting", commonHandler(updateConfig))
|
||||
auth.PATCH("/setting", adminHandler(updateConfig))
|
||||
|
||||
r.NoRoute(fallbackToFrontend(frontendDist))
|
||||
}
|
||||
@@ -152,6 +153,7 @@ func newErrorResponse(err error) model.CommonResponse[any] {
|
||||
}
|
||||
|
||||
type handlerFunc[T any] func(c *gin.Context) (T, error)
|
||||
type pHandlerFunc[S ~[]E, E any] func(c *gin.Context) (*model.Value[S], error)
|
||||
|
||||
// There are many error types in gorm, so create a custom type to represent all
|
||||
// gorm errors here instead
|
||||
@@ -189,29 +191,86 @@ func (we *wsError) Error() string {
|
||||
|
||||
func commonHandler[T any](handler handlerFunc[T]) func(*gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
data, err := handler(c)
|
||||
if err == nil {
|
||||
c.JSON(http.StatusOK, model.CommonResponse[T]{Success: true, Data: data})
|
||||
handle(c, handler)
|
||||
}
|
||||
}
|
||||
|
||||
func adminHandler[T any](handler handlerFunc[T]) func(*gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
auth, ok := c.Get(model.CtxKeyAuthorizedUser)
|
||||
if !ok {
|
||||
c.JSON(http.StatusOK, newErrorResponse(singleton.Localizer.ErrorT("unauthorized")))
|
||||
return
|
||||
}
|
||||
switch err.(type) {
|
||||
case *gormError:
|
||||
log.Printf("NEZHA>> gorm error: %v", err)
|
||||
c.JSON(http.StatusOK, newErrorResponse(singleton.Localizer.ErrorT("database error")))
|
||||
|
||||
user := *auth.(*model.User)
|
||||
if user.Role != model.RoleAdmin {
|
||||
c.JSON(http.StatusOK, newErrorResponse(singleton.Localizer.ErrorT("permission denied")))
|
||||
return
|
||||
case *wsError:
|
||||
// Connection is upgraded to WebSocket, so c.Writer is no longer usable
|
||||
if msg := err.Error(); msg != "" {
|
||||
log.Printf("NEZHA>> websocket error: %v", err)
|
||||
}
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
handle(c, handler)
|
||||
}
|
||||
}
|
||||
|
||||
func handle[T any](c *gin.Context, handler handlerFunc[T]) {
|
||||
data, err := handler(c)
|
||||
if err == nil {
|
||||
c.JSON(http.StatusOK, model.CommonResponse[T]{Success: true, Data: data})
|
||||
return
|
||||
}
|
||||
switch err.(type) {
|
||||
case *gormError:
|
||||
log.Printf("NEZHA>> gorm error: %v", err)
|
||||
c.JSON(http.StatusOK, newErrorResponse(singleton.Localizer.ErrorT("database error")))
|
||||
return
|
||||
case *wsError:
|
||||
// Connection is upgraded to WebSocket, so c.Writer is no longer usable
|
||||
if msg := err.Error(); msg != "" {
|
||||
log.Printf("NEZHA>> websocket error: %v", err)
|
||||
}
|
||||
return
|
||||
default:
|
||||
c.JSON(http.StatusOK, newErrorResponse(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func listHandler[S ~[]E, E model.CommonInterface](handler handlerFunc[S]) func(*gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
data, err := handler(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, newErrorResponse(err))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.CommonResponse[S]{Success: true, Data: filter(c, data)})
|
||||
}
|
||||
}
|
||||
|
||||
func pCommonHandler[S ~[]E, E any](handler pHandlerFunc[S, E]) func(*gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
data, err := handler(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, newErrorResponse(err))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.PaginatedResponse[S, E]{Success: true, Data: data})
|
||||
}
|
||||
}
|
||||
|
||||
func filter[S ~[]E, E model.CommonInterface](ctx *gin.Context, s S) S {
|
||||
return slices.DeleteFunc(s, func(e E) bool {
|
||||
return !e.HasPermission(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func getUid(c *gin.Context) uint64 {
|
||||
user, _ := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User)
|
||||
return user.ID
|
||||
}
|
||||
|
||||
func fallbackToFrontend(frontendDist fs.FS) func(*gin.Context) {
|
||||
checkLocalFileOrFs := func(c *gin.Context, fs fs.FS, path string) bool {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -50,6 +49,18 @@ func createCron(c *gin.Context) (uint64, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
for _, sid := range cf.Servers {
|
||||
if server, ok := singleton.ServerList[sid]; ok {
|
||||
if !server.HasPermission(c) {
|
||||
singleton.ServerLock.RUnlock()
|
||||
return 0, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.ServerLock.RUnlock()
|
||||
|
||||
cr.UserID = getUid(c)
|
||||
cr.TaskType = cf.TaskType
|
||||
cr.Name = cf.Name
|
||||
cr.Scheduler = cf.Scheduler
|
||||
@@ -104,9 +115,24 @@ func updateCron(c *gin.Context) (any, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
for _, sid := range cf.Servers {
|
||||
if server, ok := singleton.ServerList[sid]; ok {
|
||||
if !server.HasPermission(c) {
|
||||
singleton.ServerLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.ServerLock.RUnlock()
|
||||
|
||||
var cr model.Cron
|
||||
if err := singleton.DB.First(&cr, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("task id %d does not exist", id)
|
||||
return nil, singleton.Localizer.ErrorT("task id %d does not exist", id)
|
||||
}
|
||||
|
||||
if !cr.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
cr.TaskType = cf.TaskType
|
||||
@@ -156,12 +182,19 @@ func manualTriggerCron(c *gin.Context) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cr model.Cron
|
||||
if err := singleton.DB.First(&cr, id).Error; err != nil {
|
||||
singleton.CronLock.RLock()
|
||||
cr, ok := singleton.Crons[id]
|
||||
if !ok {
|
||||
singleton.CronLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("task id %d does not exist", id)
|
||||
}
|
||||
singleton.CronLock.RUnlock()
|
||||
|
||||
singleton.ManualTrigger(&cr)
|
||||
if !cr.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
singleton.ManualTrigger(cr)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -178,11 +211,21 @@ func manualTriggerCron(c *gin.Context) (any, error) {
|
||||
// @Router /batch-delete/cron [post]
|
||||
func batchDeleteCron(c *gin.Context) (any, error) {
|
||||
var cr []uint64
|
||||
|
||||
if err := c.ShouldBindJSON(&cr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
singleton.CronLock.RLock()
|
||||
for _, crID := range cr {
|
||||
if crn, ok := singleton.Crons[crID]; ok {
|
||||
if !crn.HasPermission(c) {
|
||||
singleton.CronLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.CronLock.RUnlock()
|
||||
|
||||
if err := singleton.DB.Unscoped().Delete(&model.Cron{}, "id in (?)", cr).Error; err != nil {
|
||||
return nil, newGormError("%v", err)
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ func createDDNS(c *gin.Context) (uint64, error) {
|
||||
return 0, singleton.Localizer.ErrorT("the retry count must be an integer between 1 and 10")
|
||||
}
|
||||
|
||||
p.UserID = getUid(c)
|
||||
p.Name = df.Name
|
||||
enableIPv4 := df.EnableIPv4
|
||||
enableIPv6 := df.EnableIPv6
|
||||
@@ -125,6 +126,10 @@ func updateDDNS(c *gin.Context) (any, error) {
|
||||
return nil, singleton.Localizer.ErrorT("profile id %d does not exist", id)
|
||||
}
|
||||
|
||||
if !p.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
p.Name = df.Name
|
||||
enableIPv4 := df.EnableIPv4
|
||||
enableIPv6 := df.EnableIPv6
|
||||
@@ -178,6 +183,17 @@ func batchDeleteDDNS(c *gin.Context) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
singleton.DDNSCacheLock.RLock()
|
||||
for _, pid := range ddnsConfigs {
|
||||
if p, ok := singleton.DDNSCache[pid]; ok {
|
||||
if !p.HasPermission(c) {
|
||||
singleton.DDNSCacheLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.DDNSCacheLock.RUnlock()
|
||||
|
||||
if err := singleton.DB.Unscoped().Delete(&model.DDNSProfile{}, "id in (?)", ddnsConfigs).Error; err != nil {
|
||||
return nil, newGormError("%v", err)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
|
||||
"github.com/nezhahq/nezha/model"
|
||||
"github.com/nezhahq/nezha/pkg/utils"
|
||||
"github.com/nezhahq/nezha/pkg/websocketx"
|
||||
@@ -31,13 +32,6 @@ func createFM(c *gin.Context) (*model.CreateFMResponse, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
streamId, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rpc.NezhaHandlerSingleton.CreateStream(streamId)
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
server := singleton.ServerList[id]
|
||||
singleton.ServerLock.RUnlock()
|
||||
@@ -45,6 +39,17 @@ func createFM(c *gin.Context) (*model.CreateFMResponse, error) {
|
||||
return nil, singleton.Localizer.ErrorT("server not found or not connected")
|
||||
}
|
||||
|
||||
if !server.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
streamId, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rpc.NezhaHandlerSingleton.CreateStream(streamId)
|
||||
|
||||
fmData, _ := utils.Json.Marshal(&model.TaskFM{
|
||||
StreamID: streamId,
|
||||
})
|
||||
|
||||
@@ -88,18 +88,21 @@ func authenticator() func(c *gin.Context) (interface{}, error) {
|
||||
}
|
||||
|
||||
var user model.User
|
||||
realip := c.GetString(model.CtxKeyRealIPStr)
|
||||
if err := singleton.DB.Select("id", "password").Where("username = ?", loginVals.Username).First(&user).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
model.BlockIP(singleton.DB, c.GetString(model.CtxKeyRealIPStr), model.WAFBlockReasonTypeLoginFail)
|
||||
model.BlockIP(singleton.DB, realip, model.WAFBlockReasonTypeLoginFail, model.BlockIDUnknownUser)
|
||||
}
|
||||
return nil, jwt.ErrFailedAuthentication
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(loginVals.Password)); err != nil {
|
||||
model.BlockIP(singleton.DB, c.GetString(model.CtxKeyRealIPStr), model.WAFBlockReasonTypeLoginFail)
|
||||
model.BlockIP(singleton.DB, realip, model.WAFBlockReasonTypeLoginFail, int64(user.ID))
|
||||
return nil, jwt.ErrFailedAuthentication
|
||||
}
|
||||
|
||||
model.ClearIP(singleton.DB, realip, model.BlockIDUnknownUser)
|
||||
model.ClearIP(singleton.DB, realip, int64(user.ID))
|
||||
return utils.Itoa(user.ID), nil
|
||||
}
|
||||
}
|
||||
@@ -169,10 +172,10 @@ func optionalAuthMiddleware(mw *jwt.GinJWTMiddleware) func(c *gin.Context) {
|
||||
identity := mw.IdentityHandler(c)
|
||||
|
||||
if identity != nil {
|
||||
model.ClearIP(singleton.DB, c.GetString(model.CtxKeyRealIPStr))
|
||||
model.ClearIP(singleton.DB, c.GetString(model.CtxKeyRealIPStr), model.BlockIDToken)
|
||||
c.Set(mw.IdentityKey, identity)
|
||||
} else {
|
||||
if err := model.BlockIP(singleton.DB, c.GetString(model.CtxKeyRealIPStr), model.WAFBlockReasonTypeBruteForceToken); err != nil {
|
||||
if err := model.BlockIP(singleton.DB, c.GetString(model.CtxKeyRealIPStr), model.WAFBlockReasonTypeBruteForceToken, model.BlockIDToken); err != nil {
|
||||
waf.ShowBlockPage(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -51,6 +51,18 @@ func createNAT(c *gin.Context) (uint64, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
if server, ok := singleton.ServerList[nf.ServerID]; ok {
|
||||
if !server.HasPermission(c) {
|
||||
singleton.ServerLock.RUnlock()
|
||||
return 0, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
singleton.ServerLock.RUnlock()
|
||||
|
||||
uid := getUid(c)
|
||||
|
||||
n.UserID = uid
|
||||
n.Name = nf.Name
|
||||
n.Domain = nf.Domain
|
||||
n.Host = nf.Host
|
||||
@@ -90,11 +102,24 @@ func updateNAT(c *gin.Context) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
if server, ok := singleton.ServerList[nf.ServerID]; ok {
|
||||
if !server.HasPermission(c) {
|
||||
singleton.ServerLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
singleton.ServerLock.RUnlock()
|
||||
|
||||
var n model.NAT
|
||||
if err = singleton.DB.First(&n, id).Error; err != nil {
|
||||
return nil, singleton.Localizer.ErrorT("profile id %d does not exist", id)
|
||||
}
|
||||
|
||||
if !n.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
n.Name = nf.Name
|
||||
n.Domain = nf.Domain
|
||||
n.Host = nf.Host
|
||||
@@ -122,11 +147,21 @@ func updateNAT(c *gin.Context) (any, error) {
|
||||
// @Router /batch-delete/nat [post]
|
||||
func batchDeleteNAT(c *gin.Context) (any, error) {
|
||||
var n []uint64
|
||||
|
||||
if err := c.ShouldBindJSON(&n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
singleton.NATCacheRwLock.RLock()
|
||||
for _, id := range n {
|
||||
if p, ok := singleton.NATCache[singleton.NATIDToDomain[id]]; ok {
|
||||
if !p.HasPermission(c) {
|
||||
singleton.NATCacheRwLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.NATCacheRwLock.RUnlock()
|
||||
|
||||
if err := singleton.DB.Unscoped().Delete(&model.NAT{}, "id in (?)", n).Error; err != nil {
|
||||
return nil, newGormError("%v", err)
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ func createNotification(c *gin.Context) (uint64, error) {
|
||||
}
|
||||
|
||||
var n model.Notification
|
||||
n.UserID = getUid(c)
|
||||
n.Name = nf.Name
|
||||
n.RequestMethod = nf.RequestMethod
|
||||
n.RequestType = nf.RequestType
|
||||
@@ -106,6 +107,10 @@ func updateNotification(c *gin.Context) (any, error) {
|
||||
return nil, singleton.Localizer.ErrorT("notification id %d does not exist", id)
|
||||
}
|
||||
|
||||
if !n.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
n.Name = nf.Name
|
||||
n.RequestMethod = nf.RequestMethod
|
||||
n.RequestType = nf.RequestType
|
||||
@@ -149,11 +154,20 @@ func updateNotification(c *gin.Context) (any, error) {
|
||||
// @Router /batch-delete/notification [post]
|
||||
func batchDeleteNotification(c *gin.Context) (any, error) {
|
||||
var n []uint64
|
||||
|
||||
if err := c.ShouldBindJSON(&n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
singleton.NotificationsLock.RLock()
|
||||
for _, nid := range n {
|
||||
if ns, ok := singleton.NotificationMap[nid]; ok {
|
||||
if !ns.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.NotificationsLock.RUnlock()
|
||||
|
||||
err := singleton.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Unscoped().Delete(&model.Notification{}, "id in (?)", n).Error; err != nil {
|
||||
return err
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
// @Produce json
|
||||
// @Success 200 {object} model.CommonResponse[[]model.NotificationGroupResponseItem]
|
||||
// @Router /notification-group [get]
|
||||
func listNotificationGroup(c *gin.Context) ([]model.NotificationGroupResponseItem, error) {
|
||||
func listNotificationGroup(c *gin.Context) ([]*model.NotificationGroupResponseItem, error) {
|
||||
var ng []model.NotificationGroup
|
||||
if err := singleton.DB.Find(&ng).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -39,9 +39,9 @@ func listNotificationGroup(c *gin.Context) ([]model.NotificationGroupResponseIte
|
||||
groupNotifications[n.NotificationGroupID] = append(groupNotifications[n.NotificationGroupID], n.NotificationID)
|
||||
}
|
||||
|
||||
ngRes := make([]model.NotificationGroupResponseItem, 0, len(ng))
|
||||
ngRes := make([]*model.NotificationGroupResponseItem, 0, len(ng))
|
||||
for _, n := range ng {
|
||||
ngRes = append(ngRes, model.NotificationGroupResponseItem{
|
||||
ngRes = append(ngRes, &model.NotificationGroupResponseItem{
|
||||
Group: n,
|
||||
Notifications: groupNotifications[n.ID],
|
||||
})
|
||||
@@ -68,8 +68,22 @@ func createNotificationGroup(c *gin.Context) (uint64, error) {
|
||||
}
|
||||
ngf.Notifications = slices.Compact(ngf.Notifications)
|
||||
|
||||
singleton.NotificationsLock.RLock()
|
||||
for _, nid := range ngf.Notifications {
|
||||
if n, ok := singleton.NotificationMap[nid]; ok {
|
||||
if !n.HasPermission(c) {
|
||||
singleton.NotificationsLock.RUnlock()
|
||||
return 0, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.NotificationsLock.RUnlock()
|
||||
|
||||
uid := getUid(c)
|
||||
|
||||
var ng model.NotificationGroup
|
||||
ng.Name = ngf.Name
|
||||
ng.UserID = uid
|
||||
|
||||
var count int64
|
||||
if err := singleton.DB.Model(&model.Notification{}).Where("id in (?)", ngf.Notifications).Count(&count).Error; err != nil {
|
||||
@@ -86,6 +100,9 @@ func createNotificationGroup(c *gin.Context) (uint64, error) {
|
||||
}
|
||||
for _, n := range ngf.Notifications {
|
||||
if err := tx.Create(&model.NotificationGroupNotification{
|
||||
Common: model.Common{
|
||||
UserID: uid,
|
||||
},
|
||||
NotificationGroupID: ng.ID,
|
||||
NotificationID: n,
|
||||
}).Error; err != nil {
|
||||
@@ -126,11 +143,27 @@ func updateNotificationGroup(c *gin.Context) (any, error) {
|
||||
if err := c.ShouldBindJSON(&ngf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
singleton.NotificationsLock.RLock()
|
||||
for _, nid := range ngf.Notifications {
|
||||
if n, ok := singleton.NotificationMap[nid]; ok {
|
||||
if !n.HasPermission(c) {
|
||||
singleton.NotificationsLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.NotificationsLock.RUnlock()
|
||||
|
||||
var ngDB model.NotificationGroup
|
||||
if err := singleton.DB.First(&ngDB, id).Error; err != nil {
|
||||
return nil, singleton.Localizer.ErrorT("group id %d does not exist", id)
|
||||
}
|
||||
|
||||
if !ngDB.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
ngDB.Name = ngf.Name
|
||||
ngf.Notifications = slices.Compact(ngf.Notifications)
|
||||
|
||||
@@ -142,6 +175,8 @@ func updateNotificationGroup(c *gin.Context) (any, error) {
|
||||
return nil, singleton.Localizer.ErrorT("have invalid notification id")
|
||||
}
|
||||
|
||||
uid := getUid(c)
|
||||
|
||||
err = singleton.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Save(&ngDB).Error; err != nil {
|
||||
return err
|
||||
@@ -152,6 +187,9 @@ func updateNotificationGroup(c *gin.Context) (any, error) {
|
||||
|
||||
for _, n := range ngf.Notifications {
|
||||
if err := tx.Create(&model.NotificationGroupNotification{
|
||||
Common: model.Common{
|
||||
UserID: uid,
|
||||
},
|
||||
NotificationGroupID: ngDB.ID,
|
||||
NotificationID: n,
|
||||
}).Error; err != nil {
|
||||
@@ -185,6 +223,17 @@ func batchDeleteNotificationGroup(c *gin.Context) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ng []model.NotificationGroup
|
||||
if err := singleton.DB.Where("id in (?)", ngn).Find(&ng).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, n := range ng {
|
||||
if !n.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
|
||||
err := singleton.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Unscoped().Delete(&model.NotificationGroup{}, "id in (?)", ngn).Error; err != nil {
|
||||
return err
|
||||
|
||||
@@ -56,11 +56,26 @@ func updateServer(c *gin.Context) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
singleton.DDNSCacheLock.RLock()
|
||||
for _, pid := range sf.DDNSProfiles {
|
||||
if p, ok := singleton.DDNSCache[pid]; ok {
|
||||
if !p.HasPermission(c) {
|
||||
singleton.DDNSCacheLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.DDNSCacheLock.RUnlock()
|
||||
|
||||
var s model.Server
|
||||
if err := singleton.DB.First(&s, id).Error; err != nil {
|
||||
return nil, singleton.Localizer.ErrorT("server id %d does not exist", id)
|
||||
}
|
||||
|
||||
if !s.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
s.Name = sf.Name
|
||||
s.DisplayIndex = sf.DisplayIndex
|
||||
s.Note = sf.Note
|
||||
@@ -104,6 +119,17 @@ func batchDeleteServer(c *gin.Context) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
for _, sid := range servers {
|
||||
if s, ok := singleton.ServerList[sid]; ok {
|
||||
if !s.HasPermission(c) {
|
||||
singleton.ServerLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.ServerLock.RUnlock()
|
||||
|
||||
err := singleton.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Unscoped().Delete(&model.Server{}, "id in (?)", servers).Error; err != nil {
|
||||
return err
|
||||
@@ -161,6 +187,9 @@ func forceUpdateServer(c *gin.Context) (*model.ForceUpdateResponse, error) {
|
||||
server := singleton.ServerList[sid]
|
||||
singleton.ServerLock.RUnlock()
|
||||
if server != nil && server.TaskStream != nil {
|
||||
if !server.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
if err := server.TaskStream.Send(&pb.Task{
|
||||
Type: model.TaskTypeUpgrade,
|
||||
}); err != nil {
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
// @Produce json
|
||||
// @Success 200 {object} model.CommonResponse[[]model.ServerGroupResponseItem]
|
||||
// @Router /server-group [get]
|
||||
func listServerGroup(c *gin.Context) ([]model.ServerGroupResponseItem, error) {
|
||||
func listServerGroup(c *gin.Context) ([]*model.ServerGroupResponseItem, error) {
|
||||
var sg []model.ServerGroup
|
||||
if err := singleton.DB.Find(&sg).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -38,9 +38,9 @@ func listServerGroup(c *gin.Context) ([]model.ServerGroupResponseItem, error) {
|
||||
groupServers[s.ServerGroupId] = append(groupServers[s.ServerGroupId], s.ServerId)
|
||||
}
|
||||
|
||||
var sgRes []model.ServerGroupResponseItem
|
||||
var sgRes []*model.ServerGroupResponseItem
|
||||
for _, s := range sg {
|
||||
sgRes = append(sgRes, model.ServerGroupResponseItem{
|
||||
sgRes = append(sgRes, &model.ServerGroupResponseItem{
|
||||
Group: s,
|
||||
Servers: groupServers[s.ID],
|
||||
})
|
||||
@@ -67,8 +67,22 @@ func createServerGroup(c *gin.Context) (uint64, error) {
|
||||
}
|
||||
sgf.Servers = slices.Compact(sgf.Servers)
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
for _, sid := range sgf.Servers {
|
||||
if server, ok := singleton.ServerList[sid]; ok {
|
||||
if !server.HasPermission(c) {
|
||||
singleton.ServerLock.RUnlock()
|
||||
return 0, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.ServerLock.RUnlock()
|
||||
|
||||
uid := getUid(c)
|
||||
|
||||
var sg model.ServerGroup
|
||||
sg.Name = sgf.Name
|
||||
sg.UserID = uid
|
||||
|
||||
var count int64
|
||||
if err := singleton.DB.Model(&model.Server{}).Where("id in (?)", sgf.Servers).Count(&count).Error; err != nil {
|
||||
@@ -84,6 +98,9 @@ func createServerGroup(c *gin.Context) (uint64, error) {
|
||||
}
|
||||
for _, s := range sgf.Servers {
|
||||
if err := tx.Create(&model.ServerGroupServer{
|
||||
Common: model.Common{
|
||||
UserID: uid,
|
||||
},
|
||||
ServerGroupId: sg.ID,
|
||||
ServerId: s,
|
||||
}).Error; err != nil {
|
||||
@@ -125,10 +142,26 @@ func updateServerGroup(c *gin.Context) (any, error) {
|
||||
}
|
||||
sg.Servers = slices.Compact(sg.Servers)
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
for _, sid := range sg.Servers {
|
||||
if server, ok := singleton.ServerList[sid]; ok {
|
||||
if !server.HasPermission(c) {
|
||||
singleton.ServerLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.ServerLock.RUnlock()
|
||||
|
||||
var sgDB model.ServerGroup
|
||||
if err := singleton.DB.First(&sgDB, id).Error; err != nil {
|
||||
return nil, singleton.Localizer.ErrorT("group id %d does not exist", id)
|
||||
}
|
||||
|
||||
if !sgDB.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("unauthorized")
|
||||
}
|
||||
|
||||
sgDB.Name = sg.Name
|
||||
|
||||
var count int64
|
||||
@@ -139,6 +172,8 @@ func updateServerGroup(c *gin.Context) (any, error) {
|
||||
return nil, singleton.Localizer.ErrorT("have invalid server id")
|
||||
}
|
||||
|
||||
uid := getUid(c)
|
||||
|
||||
err = singleton.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Save(&sgDB).Error; err != nil {
|
||||
return err
|
||||
@@ -149,6 +184,9 @@ func updateServerGroup(c *gin.Context) (any, error) {
|
||||
|
||||
for _, s := range sg.Servers {
|
||||
if err := tx.Create(&model.ServerGroupServer{
|
||||
Common: model.Common{
|
||||
UserID: uid,
|
||||
},
|
||||
ServerGroupId: sgDB.ID,
|
||||
ServerId: s,
|
||||
}).Error; err != nil {
|
||||
@@ -181,6 +219,17 @@ func batchDeleteServerGroup(c *gin.Context) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sg []model.ServerGroup
|
||||
if err := singleton.DB.Where("id in (?)", sgs).Find(&sg).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, s := range sg {
|
||||
if !s.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
|
||||
err := singleton.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Unscoped().Delete(&model.ServerGroup{}, "id in (?)", sgs).Error; err != nil {
|
||||
return err
|
||||
|
||||
@@ -190,7 +190,10 @@ func createService(c *gin.Context) (uint64, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
uid := getUid(c)
|
||||
|
||||
var m model.Service
|
||||
m.UserID = uid
|
||||
m.Name = mf.Name
|
||||
m.Target = strings.TrimSpace(mf.Target)
|
||||
m.Type = mf.Type
|
||||
@@ -207,6 +210,10 @@ func createService(c *gin.Context) (uint64, error) {
|
||||
m.RecoverTriggerTasks = mf.RecoverTriggerTasks
|
||||
m.FailTriggerTasks = mf.FailTriggerTasks
|
||||
|
||||
if err := validateServers(c, &m); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err := singleton.DB.Create(&m).Error; err != nil {
|
||||
return 0, newGormError("%v", err)
|
||||
}
|
||||
@@ -260,6 +267,11 @@ func updateService(c *gin.Context) (any, error) {
|
||||
if err := singleton.DB.First(&m, id).Error; err != nil {
|
||||
return nil, singleton.Localizer.ErrorT("service id %d does not exist", id)
|
||||
}
|
||||
|
||||
if !m.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
m.Name = mf.Name
|
||||
m.Target = strings.TrimSpace(mf.Target)
|
||||
m.Type = mf.Type
|
||||
@@ -276,6 +288,10 @@ func updateService(c *gin.Context) (any, error) {
|
||||
m.RecoverTriggerTasks = mf.RecoverTriggerTasks
|
||||
m.FailTriggerTasks = mf.FailTriggerTasks
|
||||
|
||||
if err := validateServers(c, &m); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err := singleton.DB.Save(&m).Error; err != nil {
|
||||
return nil, newGormError("%v", err)
|
||||
}
|
||||
@@ -318,6 +334,18 @@ func batchDeleteService(c *gin.Context) (any, error) {
|
||||
if err := c.ShouldBindJSON(&ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
singleton.ServiceSentinelShared.ServicesLock.RLock()
|
||||
for _, id := range ids {
|
||||
if ss, ok := singleton.ServiceSentinelShared.Services[id]; ok {
|
||||
if !ss.HasPermission(c) {
|
||||
singleton.ServiceSentinelShared.ServicesLock.RUnlock()
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
singleton.ServiceSentinelShared.ServicesLock.RUnlock()
|
||||
|
||||
err := singleton.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Unscoped().Delete(&model.Service{}, "id in (?)", ids).Error; err != nil {
|
||||
return err
|
||||
@@ -331,3 +359,18 @@ func batchDeleteService(c *gin.Context) (any, error) {
|
||||
singleton.ServiceSentinelShared.UpdateServiceList()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func validateServers(c *gin.Context, ss *model.Service) error {
|
||||
singleton.ServerLock.RLock()
|
||||
defer singleton.ServerLock.RUnlock()
|
||||
|
||||
for s := range ss.SkipServers {
|
||||
if server, ok := singleton.ServerList[s]; ok {
|
||||
if !server.HasPermission(c) {
|
||||
return singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
|
||||
"github.com/nezhahq/nezha/model"
|
||||
"github.com/nezhahq/nezha/pkg/utils"
|
||||
"github.com/nezhahq/nezha/pkg/websocketx"
|
||||
@@ -29,13 +30,6 @@ func createTerminal(c *gin.Context) (*model.CreateTerminalResponse, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
streamId, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rpc.NezhaHandlerSingleton.CreateStream(streamId)
|
||||
|
||||
singleton.ServerLock.RLock()
|
||||
server := singleton.ServerList[createTerminalReq.ServerID]
|
||||
singleton.ServerLock.RUnlock()
|
||||
@@ -43,6 +37,17 @@ func createTerminal(c *gin.Context) (*model.CreateTerminalResponse, error) {
|
||||
return nil, singleton.Localizer.ErrorT("server not found or not connected")
|
||||
}
|
||||
|
||||
if !server.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
|
||||
streamId, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rpc.NezhaHandlerSingleton.CreateStream(streamId)
|
||||
|
||||
terminalData, _ := utils.Json.Marshal(&model.TerminalTask{
|
||||
StreamID: streamId,
|
||||
})
|
||||
|
||||
@@ -114,6 +114,7 @@ func createUser(c *gin.Context) (uint64, error) {
|
||||
|
||||
var u model.User
|
||||
u.Username = uf.Username
|
||||
u.Role = model.RoleMember
|
||||
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(uf.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
@@ -125,6 +126,7 @@ func createUser(c *gin.Context) (uint64, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
singleton.OnUserUpdate(&u)
|
||||
return u.ID, nil
|
||||
}
|
||||
|
||||
@@ -149,5 +151,6 @@ func batchDeleteUser(c *gin.Context) (any, error) {
|
||||
return nil, singleton.Localizer.ErrorT("can't delete yourself")
|
||||
}
|
||||
|
||||
return nil, singleton.DB.Where("id IN (?)", ids).Delete(&model.User{}).Error
|
||||
err := singleton.OnUserDelete(ids, newGormError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/nezhahq/nezha/model"
|
||||
@@ -13,16 +15,40 @@ import (
|
||||
// @Schemes
|
||||
// @Description List server
|
||||
// @Tags auth required
|
||||
// @Param limit query uint false "Page limit"
|
||||
// @Param offset query uint false "Page offset"
|
||||
// @Produce json
|
||||
// @Success 200 {object} model.CommonResponse[[]model.WAFApiMock]
|
||||
// @Success 200 {object} model.PaginatedResponse[[]model.WAFApiMock, model.WAFApiMock]
|
||||
// @Router /waf [get]
|
||||
func listBlockedAddress(c *gin.Context) ([]*model.WAF, error) {
|
||||
func listBlockedAddress(c *gin.Context) (*model.Value[[]*model.WAF], error) {
|
||||
limit, err := strconv.Atoi(c.Query("limit"))
|
||||
if err != nil || limit < 1 {
|
||||
limit = 25
|
||||
}
|
||||
|
||||
offset, err := strconv.Atoi(c.Query("offset"))
|
||||
if err != nil || offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
var waf []*model.WAF
|
||||
if err := singleton.DB.Find(&waf).Error; err != nil {
|
||||
if err := singleton.DB.Limit(limit).Offset(offset).Find(&waf).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return waf, nil
|
||||
var total int64
|
||||
if err := singleton.DB.Model(&model.WAF{}).Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.Value[[]*model.WAF]{
|
||||
Value: waf,
|
||||
Pagination: model.Pagination{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Total: total,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Batch delete blocked addresses
|
||||
|
||||
@@ -100,12 +100,34 @@ func DispatchTask(serviceSentinelDispatchBus <-chan model.Service) {
|
||||
continue
|
||||
}
|
||||
if task.Cover == model.ServiceCoverIgnoreAll && task.SkipServers[singleton.SortedServerList[workedServerIndex].ID] {
|
||||
singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB())
|
||||
server := singleton.SortedServerList[workedServerIndex]
|
||||
singleton.UserLock.RLock()
|
||||
var role uint8
|
||||
if u, ok := singleton.UserInfoMap[server.UserID]; !ok {
|
||||
role = model.RoleMember
|
||||
} else {
|
||||
role = u.Role
|
||||
}
|
||||
singleton.UserLock.RUnlock()
|
||||
if task.UserID == server.UserID || role == model.RoleAdmin {
|
||||
singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB())
|
||||
}
|
||||
workedServerIndex++
|
||||
continue
|
||||
}
|
||||
if task.Cover == model.ServiceCoverAll && !task.SkipServers[singleton.SortedServerList[workedServerIndex].ID] {
|
||||
singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB())
|
||||
server := singleton.SortedServerList[workedServerIndex]
|
||||
singleton.UserLock.RLock()
|
||||
var role uint8
|
||||
if u, ok := singleton.UserInfoMap[server.UserID]; !ok {
|
||||
role = model.RoleMember
|
||||
} else {
|
||||
role = u.Role
|
||||
}
|
||||
singleton.UserLock.RUnlock()
|
||||
if task.UserID == server.UserID || role == model.RoleAdmin {
|
||||
singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB())
|
||||
}
|
||||
workedServerIndex++
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user