mirror of
https://github.com/nezhahq/nezha.git
synced 2025-01-22 12:48:14 -05:00
d50605d668
* feat: support id query for "list" apis * gosec
238 lines
6.0 KiB
Go
238 lines
6.0 KiB
Go
package controller
|
||
|
||
import (
|
||
"strconv"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/jinzhu/copier"
|
||
|
||
"github.com/nezhahq/nezha/model"
|
||
"github.com/nezhahq/nezha/service/singleton"
|
||
)
|
||
|
||
// List schedule tasks
|
||
// @Summary List schedule tasks
|
||
// @Security BearerAuth
|
||
// @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]
|
||
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
|
||
}
|
||
|
||
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
|
||
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, singleton.Localizer.ErrorT("scheduled tasks cannot be triggered by alarms")
|
||
}
|
||
|
||
// 对于计划任务类型,需要更新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
|
||
}
|
||
|
||
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, 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
|
||
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, singleton.Localizer.ErrorT("scheduled tasks cannot be triggered by alarms")
|
||
}
|
||
|
||
// 对于计划任务类型,需要更新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
|
||
}
|
||
|
||
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()
|
||
|
||
if !cr.HasPermission(c) {
|
||
return nil, singleton.Localizer.ErrorT("permission denied")
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
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)
|
||
}
|
||
|
||
singleton.OnDeleteCron(cr)
|
||
singleton.UpdateCronList()
|
||
return nil, nil
|
||
}
|