mirror of
https://github.com/nezhahq/nezha.git
synced 2025-01-22 20:58:14 -05:00
Compare commits
3 Commits
1cabe975f2
...
163231bbb1
Author | SHA1 | Date | |
---|---|---|---|
|
163231bbb1 | ||
|
2c8ab28efe | ||
|
af7427666a |
@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
@ -82,7 +83,7 @@ func routers(r *gin.Engine, frontendDist fs.FS) {
|
||||
auth.POST("/user", commonHandler(createUser))
|
||||
auth.POST("/batch-delete/user", commonHandler(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,34 +97,34 @@ 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))
|
||||
@ -212,6 +213,29 @@ func commonHandler[T any](handler handlerFunc[T]) func(*gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
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, filter(c, 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 {
|
||||
|
@ -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],
|
||||
})
|
||||
|
@ -61,6 +61,10 @@ func updateServer(c *gin.Context) (any, error) {
|
||||
return nil, singleton.Localizer.ErrorT("server id %d does not exist", id)
|
||||
}
|
||||
|
||||
if !s.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("unauthorized")
|
||||
}
|
||||
|
||||
s.Name = sf.Name
|
||||
s.DisplayIndex = sf.DisplayIndex
|
||||
s.Note = sf.Note
|
||||
@ -99,11 +103,23 @@ func updateServer(c *gin.Context) (any, error) {
|
||||
// @Success 200 {object} model.CommonResponse[any]
|
||||
// @Router /batch-delete/server [post]
|
||||
func batchDeleteServer(c *gin.Context) (any, error) {
|
||||
var servers []uint64
|
||||
if err := c.ShouldBindJSON(&servers); err != nil {
|
||||
var serversRaw []uint64
|
||||
if err := c.ShouldBindJSON(&serversRaw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var servers []uint64
|
||||
singleton.ServerLock.RLock()
|
||||
for _, sid := range serversRaw {
|
||||
if s, ok := singleton.ServerList[sid]; ok {
|
||||
if !s.HasPermission(c) {
|
||||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||
}
|
||||
servers = append(servers, s.ID)
|
||||
}
|
||||
}
|
||||
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
|
||||
|
@ -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],
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -2,6 +2,8 @@ package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -17,6 +19,31 @@ type Common struct {
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at,omitempty"`
|
||||
// Do not use soft deletion
|
||||
// DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"`
|
||||
|
||||
UserID uint64 `json:"user_id,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Common) GetID() uint64 {
|
||||
return c.ID
|
||||
}
|
||||
|
||||
func (c *Common) HasPermission(ctx *gin.Context) bool {
|
||||
auth, ok := ctx.Get(CtxKeyAuthorizedUser)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
user := *auth.(*User)
|
||||
if user.Role == RoleAdmin {
|
||||
return true
|
||||
}
|
||||
|
||||
return user.ID == c.UserID
|
||||
}
|
||||
|
||||
type CommonInterface interface {
|
||||
GetID() uint64
|
||||
HasPermission(*gin.Context) bool
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
|
@ -1,9 +1,15 @@
|
||||
package model
|
||||
|
||||
const (
|
||||
RoleAdmin uint8 = iota
|
||||
RoleMember
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Common
|
||||
Username string `json:"username,omitempty" gorm:"uniqueIndex"`
|
||||
Password string `json:"password,omitempty" gorm:"type:char(72)"`
|
||||
Role uint8 `json:"role,omitempty"`
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
|
Loading…
Reference in New Issue
Block a user