add cron, nat api & refactor alert rule (#459)

* add cron api & refactor alert rule

* add nat api

* fix swagger

* remove unnecessary steps
This commit is contained in:
UUBulb
2024-10-26 23:57:47 +08:00
committed by GitHub
parent ebc4fad9bc
commit 68d7e16773
24 changed files with 573 additions and 144 deletions

View File

@@ -64,13 +64,12 @@ func createAlertRule(c *gin.Context) (uint64, error) {
enable := arf.Enable
r.TriggerMode = arf.TriggerMode
r.Enable = &enable
r.ID = arf.ID
if err := singleton.DB.Create(&r).Error; err != nil {
return 0, newGormError("%v", err)
}
singleton.OnRefreshOrAddAlert(r)
singleton.OnRefreshOrAddAlert(&r)
return r.ID, nil
}
@@ -115,13 +114,12 @@ func updateAlertRule(c *gin.Context) (any, error) {
enable := arf.Enable
r.TriggerMode = arf.TriggerMode
r.Enable = &enable
r.ID = arf.ID
if err := singleton.DB.Save(&r).Error; err != nil {
return 0, newGormError("%v", err)
}
singleton.OnRefreshOrAddAlert(r)
singleton.OnRefreshOrAddAlert(&r)
return r.ID, nil
}
@@ -143,7 +141,7 @@ func batchDeleteAlertRule(c *gin.Context) (any, error) {
return nil, err
}
if err := singleton.DB.Unscoped().Delete(&model.DDNSProfile{}, "id in (?)", ar).Error; err != nil {
if err := singleton.DB.Unscoped().Delete(&model.AlertRule{}, "id in (?)", ar).Error; err != nil {
return nil, newGormError("%v", err)
}

View File

@@ -97,12 +97,23 @@ func routers(r *gin.Engine) {
auth.PATCH("/alert-rule/:id", commonHandler(updateAlertRule))
auth.POST("/batch-delete/alert-rule", commonHandler(batchDeleteAlertRule))
auth.GET("/cron", commonHandler(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/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.POST("/nat", commonHandler(createNAT))
auth.PATCH("/nat/:id", commonHandler(updateNAT))
auth.POST("/batch-delete/nat", commonHandler(batchDeleteNAT))
r.NoRoute(fallbackToFrontend)
}

View File

@@ -0,0 +1,193 @@
package controller
import (
"errors"
"fmt"
"strconv"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/naiba/nezha/model"
"github.com/naiba/nezha/service/singleton"
)
// List schedule tasks
// @Summary List schedule tasks
// @Security BearerAuth
// @Schemes
// @Description List schedule tasks
// @Tags auth required
// @Produce json
// @Success 200 {object} model.CommonResponse[[]model.Cron]
// @Router /cron [get]
func listCron(c *gin.Context) ([]*model.Cron, error) {
singleton.CronLock.RLock()
defer singleton.CronLock.RUnlock()
var cr []*model.Cron
if err := copier.Copy(&cr, &singleton.CronList); err != nil {
return nil, err
}
return cr, nil
}
// Create new schedule task
// @Summary Create new schedule task
// @Security BearerAuth
// @Schemes
// @Description Create new schedule task
// @Tags auth required
// @Accept json
// @param request body model.CronForm true "CronForm"
// @Produce json
// @Success 200 {object} model.CommonResponse[uint64]
// @Router /cron [post]
func createCron(c *gin.Context) (uint64, error) {
var cf model.CronForm
var cr model.Cron
if err := c.ShouldBindJSON(&cf); err != nil {
return 0, err
}
cr.TaskType = cf.TaskType
cr.Name = cf.Name
cr.Scheduler = cf.Scheduler
cr.Command = cf.Command
cr.Servers = cf.Servers
cr.PushSuccessful = cf.PushSuccessful
cr.NotificationGroupID = cf.NotificationGroupID
cr.Cover = cf.Cover
if cr.TaskType == model.CronTypeCronTask && cr.Cover == model.CronCoverAlertTrigger {
return 0, errors.New("计划任务类型不得使用触发服务器执行方式")
}
// 对于计划任务类型需要更新CronJob
var err error
if cf.TaskType == model.CronTypeCronTask {
if cr.CronJobID, err = singleton.Cron.AddFunc(cr.Scheduler, singleton.CronTrigger(&cr)); err != nil {
return 0, err
}
}
if err = singleton.DB.Create(&cr).Error; err != nil {
return 0, newGormError("%v", err)
}
singleton.OnRefreshOrAddCron(&cr)
singleton.UpdateCronList()
return cr.ID, nil
}
// Update schedule task
// @Summary Update schedule task
// @Security BearerAuth
// @Schemes
// @Description Update schedule task
// @Tags auth required
// @Accept json
// @param id path uint true "Task ID"
// @param request body model.CronForm true "CronForm"
// @Produce json
// @Success 200 {object} model.CommonResponse[any]
// @Router /cron/{id} [patch]
func updateCron(c *gin.Context) (any, error) {
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
return nil, err
}
var cf model.CronForm
if err := c.ShouldBindJSON(&cf); err != nil {
return 0, err
}
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)
}
cr.TaskType = cf.TaskType
cr.Name = cf.Name
cr.Scheduler = cf.Scheduler
cr.Command = cf.Command
cr.Servers = cf.Servers
cr.PushSuccessful = cf.PushSuccessful
cr.NotificationGroupID = cf.NotificationGroupID
cr.Cover = cf.Cover
if cr.TaskType == model.CronTypeCronTask && cr.Cover == model.CronCoverAlertTrigger {
return nil, errors.New("计划任务类型不得使用触发服务器执行方式")
}
// 对于计划任务类型需要更新CronJob
if cf.TaskType == model.CronTypeCronTask {
if cr.CronJobID, err = singleton.Cron.AddFunc(cr.Scheduler, singleton.CronTrigger(&cr)); err != nil {
return nil, err
}
}
if err = singleton.DB.Save(&cr).Error; err != nil {
return nil, newGormError("%v", err)
}
singleton.OnRefreshOrAddCron(&cr)
singleton.UpdateCronList()
return nil, nil
}
// Trigger schedule task
// @Summary Trigger schedule task
// @Security BearerAuth
// @Schemes
// @Description Trigger schedule task
// @Tags auth required
// @Accept json
// @param id path uint true "Task ID"
// @Produce json
// @Success 200 {object} model.CommonResponse[any]
// @Router /cron/{id}/manual [get]
func manualTriggerCron(c *gin.Context) (any, error) {
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
return nil, err
}
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)
}
singleton.ManualTrigger(&cr)
return nil, nil
}
// Batch delete schedule tasks
// @Summary Batch delete schedule tasks
// @Security BearerAuth
// @Schemes
// @Description Batch delete schedule tasks
// @Tags auth required
// @Accept json
// @param request body []uint64 true "id list"
// @Produce json
// @Success 200 {object} model.CommonResponse[any]
// @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
}
if err := singleton.DB.Unscoped().Delete(&model.Cron{}, "id in (?)", cr).Error; err != nil {
return nil, newGormError("%v", err)
}
singleton.OnDeleteCron(cr)
return nil, nil
}

View File

@@ -128,7 +128,6 @@ func updateDDNS(c *gin.Context) (any, error) {
}
p.Name = df.Name
p.ID = id
enableIPv4 := df.EnableIPv4
enableIPv6 := df.EnableIPv6
p.EnableIPv4 = &enableIPv4

View File

@@ -58,12 +58,6 @@ func (ma *memberAPI) delete(c *gin.Context) {
var err error
switch c.Param("model") {
case "nat":
err = singleton.DB.Unscoped().Delete(&model.NAT{}, "id = ?", id).Error
if err == nil {
singleton.OnNATUpdate()
}
case "cron":
err = singleton.DB.Unscoped().Delete(&model.Cron{}, "id = ?", id).Error
if err == nil {
@@ -139,12 +133,6 @@ func (ma *memberAPI) addOrEditCron(c *gin.Context) {
err = tx.Save(&cr).Error
}
}
if err == nil {
// 对于计划任务类型需要更新CronJob
if cf.TaskType == model.CronTypeCronTask {
cr.CronJobID, err = singleton.Cron.AddFunc(cr.Scheduler, singleton.CronTrigger(cr))
}
}
if err == nil {
err = tx.Commit().Error
} else {
@@ -183,7 +171,7 @@ func (ma *memberAPI) manualTrigger(c *gin.Context) {
return
}
singleton.ManualTrigger(cr)
//singleton.ManualTrigger(cr)
c.JSON(http.StatusOK, model.Response{
Code: http.StatusOK,
@@ -452,7 +440,7 @@ func (ma *memberAPI) addOrEditNAT(c *gin.Context) {
})
return
}
singleton.OnNATUpdate()
//singleton.OnNATUpdate()
c.JSON(http.StatusOK, model.Response{
Code: http.StatusOK,
})
@@ -538,7 +526,7 @@ func (ma *memberAPI) addOrEditAlertRule(c *gin.Context) {
})
return
}
singleton.OnRefreshOrAddAlert(r)
//singleton.OnRefreshOrAddAlert(r)
c.JSON(http.StatusOK, model.Response{
Code: http.StatusOK,
})

View File

@@ -0,0 +1,138 @@
package controller
import (
"fmt"
"strconv"
"github.com/gin-gonic/gin"
"github.com/jinzhu/copier"
"github.com/naiba/nezha/model"
"github.com/naiba/nezha/service/singleton"
)
// List NAT Profiles
// @Summary List NAT profiles
// @Schemes
// @Description List NAT profiles
// @Security BearerAuth
// @Tags auth required
// @Produce json
// @Success 200 {object} model.CommonResponse[[]model.NAT]
// @Router /nat [get]
func listNAT(c *gin.Context) ([]*model.NAT, error) {
var n []*model.NAT
singleton.NATCacheRwLock.RLock()
defer singleton.NATCacheRwLock.RUnlock()
if err := copier.Copy(&n, &singleton.NATList); err != nil {
return nil, err
}
return n, nil
}
// Add NAT profile
// @Summary Add NAT profile
// @Security BearerAuth
// @Schemes
// @Description Add NAT profile
// @Tags auth required
// @Accept json
// @param request body model.NATForm true "NAT Request"
// @Produce json
// @Success 200 {object} model.CommonResponse[uint64]
// @Router /nat [post]
func createNAT(c *gin.Context) (uint64, error) {
var nf model.NATForm
var n model.NAT
if err := c.ShouldBindJSON(&nf); err != nil {
return 0, err
}
n.Name = nf.Name
n.Domain = nf.Domain
n.Host = nf.Host
n.ServerID = nf.ServerID
if err := singleton.DB.Create(&n).Error; err != nil {
return 0, newGormError("%v", err)
}
singleton.OnNATUpdate(&n)
singleton.UpdateNATList()
return n.ID, nil
}
// Edit NAT profile
// @Summary Edit NAT profile
// @Security BearerAuth
// @Schemes
// @Description Edit NAT profile
// @Tags auth required
// @Accept json
// @param id path uint true "Profile ID"
// @param request body model.NATForm true "NAT Request"
// @Produce json
// @Success 200 {object} model.CommonResponse[any]
// @Router /nat/{id} [patch]
func updateNAT(c *gin.Context) (any, error) {
idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
return nil, err
}
var nf model.NATForm
if err := c.ShouldBindJSON(&nf); err != nil {
return nil, err
}
var n model.NAT
if err = singleton.DB.First(&n, id).Error; err != nil {
return nil, fmt.Errorf("profile id %d does not exist", id)
}
n.Name = nf.Name
n.Domain = nf.Domain
n.Host = nf.Host
n.ServerID = nf.ServerID
if err := singleton.DB.Save(&n).Error; err != nil {
return 0, newGormError("%v", err)
}
singleton.OnNATUpdate(&n)
singleton.UpdateNATList()
return nil, nil
}
// Batch delete NAT configurations
// @Summary Batch delete NAT configurations
// @Security BearerAuth
// @Schemes
// @Description Batch delete NAT configurations
// @Tags auth required
// @Accept json
// @param request body []uint64 true "id list"
// @Produce json
// @Success 200 {object} model.CommonResponse[any]
// @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
}
if err := singleton.DB.Unscoped().Delete(&model.NAT{}, "id in (?)", n).Error; err != nil {
return nil, newGormError("%v", err)
}
singleton.OnNATDelete(n)
singleton.UpdateNATList()
return nil, nil
}

View File

@@ -57,7 +57,7 @@ func createNotification(c *gin.Context) (uint64, error) {
n.URL = nf.URL
verifySSL := nf.VerifySSL
n.VerifySSL = &verifySSL
n.ID = nf.ID
ns := model.NotificationServerBundle{
Notification: &n,
Server: nil,
@@ -115,7 +115,7 @@ func updateNotification(c *gin.Context) (any, error) {
n.URL = nf.URL
verifySSL := nf.VerifySSL
n.VerifySSL = &verifySSL
n.ID = nf.ID
ns := model.NotificationServerBundle{
Notification: &n,
Server: nil,

View File

@@ -17,7 +17,7 @@ import (
// @Schemes
// @Description List notification group
// @Security BearerAuth
// @Tags common
// @Tags auth required
// @Produce json
// @Success 200 {object} model.CommonResponse[[]model.NotificationGroupResponseItem]
// @Router /notification-group [get]

View File

@@ -62,7 +62,6 @@ func updateServer(c *gin.Context) (any, error) {
s.Name = sf.Name
s.DisplayIndex = sf.DisplayIndex
s.ID = id
s.Note = sf.Note
s.PublicNote = sf.PublicNote
s.HideForGuest = sf.HideForGuest