mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-02-04 12:40:07 +00:00
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:
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
193
cmd/dashboard/controller/cron.go
Normal file
193
cmd/dashboard/controller/cron.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
138
cmd/dashboard/controller/nat.go
Normal file
138
cmd/dashboard/controller/nat.go
Normal 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
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user