Merge pull request #241 from AkkiaS7/trigger-task
feat: 报警规则触发任务执行 Co-authored-by: AkkiaS7 <68485070+AkkiaS7@users.noreply.github.com>
This commit is contained in:
		
						commit
						278127165e
					
				@ -33,6 +33,7 @@ func (ma *memberAPI) serve() {
 | 
				
			|||||||
	}))
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mr.GET("/search-server", ma.searchServer)
 | 
						mr.GET("/search-server", ma.searchServer)
 | 
				
			||||||
 | 
						mr.GET("/search-tasks", ma.searchTask)
 | 
				
			||||||
	mr.POST("/server", ma.addOrEditServer)
 | 
						mr.POST("/server", ma.addOrEditServer)
 | 
				
			||||||
	mr.POST("/monitor", ma.addOrEditMonitor)
 | 
						mr.POST("/monitor", ma.addOrEditMonitor)
 | 
				
			||||||
	mr.POST("/cron", ma.addOrEditCron)
 | 
						mr.POST("/cron", ma.addOrEditCron)
 | 
				
			||||||
@ -275,6 +276,27 @@ func (ma *memberAPI) searchServer(c *gin.Context) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ma *memberAPI) searchTask(c *gin.Context) {
 | 
				
			||||||
 | 
						var tasks []model.Cron
 | 
				
			||||||
 | 
						likeWord := "%" + c.Query("word") + "%"
 | 
				
			||||||
 | 
						singleton.DB.Select("id,name").Where("id = ? OR name LIKE ?",
 | 
				
			||||||
 | 
							c.Query("word"), likeWord).Find(&tasks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var resp []searchResult
 | 
				
			||||||
 | 
						for i := 0; i < len(tasks); i++ {
 | 
				
			||||||
 | 
							resp = append(resp, searchResult{
 | 
				
			||||||
 | 
								Value: tasks[i].ID,
 | 
				
			||||||
 | 
								Name:  tasks[i].Name,
 | 
				
			||||||
 | 
								Text:  tasks[i].Name,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.JSON(http.StatusOK, map[string]interface{}{
 | 
				
			||||||
 | 
							"success": true,
 | 
				
			||||||
 | 
							"results": resp,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type serverForm struct {
 | 
					type serverForm struct {
 | 
				
			||||||
	ID           uint64
 | 
						ID           uint64
 | 
				
			||||||
	Name         string `binding:"required"`
 | 
						Name         string `binding:"required"`
 | 
				
			||||||
@ -415,6 +437,7 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type cronForm struct {
 | 
					type cronForm struct {
 | 
				
			||||||
	ID              uint64
 | 
						ID              uint64
 | 
				
			||||||
 | 
						TaskType        uint8 // 0:计划任务 1:触发任务
 | 
				
			||||||
	Name            string
 | 
						Name            string
 | 
				
			||||||
	Scheduler       string
 | 
						Scheduler       string
 | 
				
			||||||
	Command         string
 | 
						Command         string
 | 
				
			||||||
@ -429,6 +452,7 @@ func (ma *memberAPI) addOrEditCron(c *gin.Context) {
 | 
				
			|||||||
	var cr model.Cron
 | 
						var cr model.Cron
 | 
				
			||||||
	err := c.ShouldBindJSON(&cf)
 | 
						err := c.ShouldBindJSON(&cf)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
 | 
							cr.TaskType = cf.TaskType
 | 
				
			||||||
		cr.Name = cf.Name
 | 
							cr.Name = cf.Name
 | 
				
			||||||
		cr.Scheduler = cf.Scheduler
 | 
							cr.Scheduler = cf.Scheduler
 | 
				
			||||||
		cr.Command = cf.Command
 | 
							cr.Command = cf.Command
 | 
				
			||||||
@ -439,6 +463,17 @@ func (ma *memberAPI) addOrEditCron(c *gin.Context) {
 | 
				
			|||||||
		cr.Cover = cf.Cover
 | 
							cr.Cover = cf.Cover
 | 
				
			||||||
		err = utils.Json.Unmarshal([]byte(cf.ServersRaw), &cr.Servers)
 | 
							err = utils.Json.Unmarshal([]byte(cf.ServersRaw), &cr.Servers)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 计划任务类型不得使用触发服务器执行方式
 | 
				
			||||||
 | 
						if cr.TaskType == model.CronTypeCronTask && cr.Cover == model.CronCoverAlertTrigger {
 | 
				
			||||||
 | 
							err = errors.New("计划任务类型不得使用触发服务器执行方式")
 | 
				
			||||||
 | 
							c.JSON(http.StatusOK, model.Response{
 | 
				
			||||||
 | 
								Code:    http.StatusBadRequest,
 | 
				
			||||||
 | 
								Message: fmt.Sprintf("请求错误:%s", err),
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tx := singleton.DB.Begin()
 | 
						tx := singleton.DB.Begin()
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		// 保证NotificationTag不为空
 | 
							// 保证NotificationTag不为空
 | 
				
			||||||
@ -452,8 +487,11 @@ func (ma *memberAPI) addOrEditCron(c *gin.Context) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
 | 
							// 对于计划任务类型,需要更新CronJob
 | 
				
			||||||
 | 
							if cf.TaskType == model.CronTypeCronTask {
 | 
				
			||||||
			cr.CronJobID, err = singleton.Cron.AddFunc(cr.Scheduler, singleton.CronTrigger(cr))
 | 
								cr.CronJobID, err = singleton.Cron.AddFunc(cr.Scheduler, singleton.CronTrigger(cr))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		err = tx.Commit().Error
 | 
							err = tx.Commit().Error
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
@ -599,7 +637,10 @@ type alertRuleForm struct {
 | 
				
			|||||||
	ID                     uint64
 | 
						ID                     uint64
 | 
				
			||||||
	Name                   string
 | 
						Name                   string
 | 
				
			||||||
	RulesRaw               string
 | 
						RulesRaw               string
 | 
				
			||||||
 | 
						FailTriggerTasksRaw    string // 失败时触发的任务id
 | 
				
			||||||
 | 
						RecoverTriggerTasksRaw string // 恢复时触发的任务id
 | 
				
			||||||
	NotificationTag        string
 | 
						NotificationTag        string
 | 
				
			||||||
 | 
						TriggerMode            int
 | 
				
			||||||
	Enable                 string
 | 
						Enable                 string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -640,11 +681,22 @@ func (ma *memberAPI) addOrEditAlertRule(c *gin.Context) {
 | 
				
			|||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		r.Name = arf.Name
 | 
							r.Name = arf.Name
 | 
				
			||||||
		r.RulesRaw = arf.RulesRaw
 | 
							r.RulesRaw = arf.RulesRaw
 | 
				
			||||||
 | 
							r.FailTriggerTasksRaw = arf.FailTriggerTasksRaw
 | 
				
			||||||
 | 
							r.RecoverTriggerTasksRaw = arf.RecoverTriggerTasksRaw
 | 
				
			||||||
		r.NotificationTag = arf.NotificationTag
 | 
							r.NotificationTag = arf.NotificationTag
 | 
				
			||||||
		enable := arf.Enable == "on"
 | 
							enable := arf.Enable == "on"
 | 
				
			||||||
 | 
							r.TriggerMode = arf.TriggerMode
 | 
				
			||||||
		r.Enable = &enable
 | 
							r.Enable = &enable
 | 
				
			||||||
		r.ID = arf.ID
 | 
							r.ID = arf.ID
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							err = utils.Json.Unmarshal([]byte(arf.FailTriggerTasksRaw), &r.FailTriggerTasks)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							err = utils.Json.Unmarshal([]byte(arf.RecoverTriggerTasksRaw), &r.RecoverTriggerTasks)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	//保证NotificationTag不为空
 | 
						//保证NotificationTag不为空
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
		if r.NotificationTag == "" {
 | 
							if r.NotificationTag == "" {
 | 
				
			||||||
			r.NotificationTag = "default"
 | 
								r.NotificationTag = "default"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										22
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								go.mod
									
									
									
									
									
								
							@ -4,7 +4,7 @@ go 1.19
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5
 | 
						code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5
 | 
				
			||||||
	github.com/AlecAivazis/survey/v2 v2.3.5
 | 
						github.com/AlecAivazis/survey/v2 v2.3.6
 | 
				
			||||||
	github.com/BurntSushi/toml v1.2.0
 | 
						github.com/BurntSushi/toml v1.2.0
 | 
				
			||||||
	github.com/Erope/goss v0.0.0-20211230093305-df3c03fd1ed4
 | 
						github.com/Erope/goss v0.0.0-20211230093305-df3c03fd1ed4
 | 
				
			||||||
	github.com/artdarek/go-unzip v1.0.0
 | 
						github.com/artdarek/go-unzip v1.0.0
 | 
				
			||||||
@ -25,16 +25,16 @@ require (
 | 
				
			|||||||
	github.com/ory/graceful v0.1.3
 | 
						github.com/ory/graceful v0.1.3
 | 
				
			||||||
	github.com/patrickmn/go-cache v2.1.0+incompatible
 | 
						github.com/patrickmn/go-cache v2.1.0+incompatible
 | 
				
			||||||
	github.com/robfig/cron/v3 v3.0.1
 | 
						github.com/robfig/cron/v3 v3.0.1
 | 
				
			||||||
	github.com/shirou/gopsutil/v3 v3.22.7
 | 
						github.com/shirou/gopsutil/v3 v3.22.8
 | 
				
			||||||
	github.com/spf13/pflag v1.0.5
 | 
						github.com/spf13/pflag v1.0.5
 | 
				
			||||||
	github.com/spf13/viper v1.12.0
 | 
						github.com/spf13/viper v1.13.0
 | 
				
			||||||
	github.com/stretchr/testify v1.8.0
 | 
						github.com/stretchr/testify v1.8.0
 | 
				
			||||||
	github.com/xanzy/go-gitlab v0.72.0
 | 
						github.com/xanzy/go-gitlab v0.73.1
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
 | 
						golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
 | 
				
			||||||
	golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7
 | 
						golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
 | 
				
			||||||
	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
 | 
						golang.org/x/sync v0.0.0-20220907140024-f12130a52804
 | 
				
			||||||
	golang.org/x/text v0.3.7
 | 
						golang.org/x/text v0.3.7
 | 
				
			||||||
	google.golang.org/grpc v1.48.0
 | 
						google.golang.org/grpc v1.49.0
 | 
				
			||||||
	google.golang.org/protobuf v1.28.1
 | 
						google.golang.org/protobuf v1.28.1
 | 
				
			||||||
	gopkg.in/yaml.v2 v2.4.0
 | 
						gopkg.in/yaml.v2 v2.4.0
 | 
				
			||||||
	gorm.io/driver/sqlite v1.3.6
 | 
						gorm.io/driver/sqlite v1.3.6
 | 
				
			||||||
@ -70,14 +70,14 @@ require (
 | 
				
			|||||||
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 | 
						github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 | 
				
			||||||
	github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
						github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
				
			||||||
	github.com/pelletier/go-toml v1.9.5 // indirect
 | 
						github.com/pelletier/go-toml v1.9.5 // indirect
 | 
				
			||||||
	github.com/pelletier/go-toml/v2 v2.0.1 // indirect
 | 
						github.com/pelletier/go-toml/v2 v2.0.5 // indirect
 | 
				
			||||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
						github.com/pkg/errors v0.9.1 // indirect
 | 
				
			||||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
						github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
				
			||||||
	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
 | 
						github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
 | 
				
			||||||
	github.com/spf13/afero v1.8.2 // indirect
 | 
						github.com/spf13/afero v1.8.2 // indirect
 | 
				
			||||||
	github.com/spf13/cast v1.5.0 // indirect
 | 
						github.com/spf13/cast v1.5.0 // indirect
 | 
				
			||||||
	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 | 
						github.com/spf13/jwalterweatherman v1.1.0 // indirect
 | 
				
			||||||
	github.com/subosito/gotenv v1.3.0 // indirect
 | 
						github.com/subosito/gotenv v1.4.1 // indirect
 | 
				
			||||||
	github.com/tcnksm/go-gitconfig v0.1.2 // indirect
 | 
						github.com/tcnksm/go-gitconfig v0.1.2 // indirect
 | 
				
			||||||
	github.com/tklauser/go-sysconf v0.3.10 // indirect
 | 
						github.com/tklauser/go-sysconf v0.3.10 // indirect
 | 
				
			||||||
	github.com/tklauser/numcpus v0.4.0 // indirect
 | 
						github.com/tklauser/numcpus v0.4.0 // indirect
 | 
				
			||||||
@ -90,6 +90,6 @@ require (
 | 
				
			|||||||
	golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
 | 
						golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
 | 
				
			||||||
	google.golang.org/appengine v1.6.7 // indirect
 | 
						google.golang.org/appengine v1.6.7 // indirect
 | 
				
			||||||
	google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect
 | 
						google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect
 | 
				
			||||||
	gopkg.in/ini.v1 v1.66.4 // indirect
 | 
						gopkg.in/ini.v1 v1.67.0 // indirect
 | 
				
			||||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
						gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										43
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								go.sum
									
									
									
									
									
								
							@ -38,8 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
 | 
				
			|||||||
code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5 h1:tM5+dn2C9xZw1RzgI6WTQW1rGqdUimKB3RFbyu4h6Hc=
 | 
					code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5 h1:tM5+dn2C9xZw1RzgI6WTQW1rGqdUimKB3RFbyu4h6Hc=
 | 
				
			||||||
code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5/go.mod h1:v4VVB6oBMz/c9fRY6vZrwr5xKRWOH5NPDjQZlPk0Gbs=
 | 
					code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5/go.mod h1:v4VVB6oBMz/c9fRY6vZrwr5xKRWOH5NPDjQZlPk0Gbs=
 | 
				
			||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
					dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 | 
				
			||||||
github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ=
 | 
					github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
 | 
				
			||||||
github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
 | 
					github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
 | 
				
			||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
					github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
				
			||||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 | 
					github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 | 
				
			||||||
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
 | 
					github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
 | 
				
			||||||
@ -273,8 +273,9 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
 | 
				
			|||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 | 
					github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 | 
				
			||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
 | 
					github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
 | 
				
			||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 | 
					github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 | 
				
			||||||
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
 | 
					 | 
				
			||||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
 | 
					github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
 | 
				
			||||||
 | 
					github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
 | 
				
			||||||
 | 
					github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
 | 
				
			||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 | 
					github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 | 
				
			||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
					github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
				
			||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
@ -291,8 +292,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
 | 
				
			|||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 | 
					github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
 | 
					github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 | 
					github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 | 
				
			||||||
github.com/shirou/gopsutil/v3 v3.22.7 h1:flKnuCMfUUrO+oAvwAd6GKZgnPzr098VA/UJ14nhJd4=
 | 
					github.com/shirou/gopsutil/v3 v3.22.8 h1:a4s3hXogo5mE2PfdfJIonDbstO/P+9JszdfhAHSzD9Y=
 | 
				
			||||||
github.com/shirou/gopsutil/v3 v3.22.7/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI=
 | 
					github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI=
 | 
				
			||||||
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
 | 
					github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
 | 
				
			||||||
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
 | 
					github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
 | 
				
			||||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
 | 
					github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
 | 
				
			||||||
@ -301,8 +302,8 @@ github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmq
 | 
				
			|||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 | 
					github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 | 
				
			||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 | 
					github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 | 
				
			||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 | 
					github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 | 
				
			||||||
github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
 | 
					github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
 | 
				
			||||||
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
 | 
					github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
 | 
				
			||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 | 
					github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 | 
				
			||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
					github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
				
			||||||
@ -314,8 +315,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 | 
				
			|||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 | 
					github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 | 
				
			||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 | 
					github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 | 
				
			||||||
github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
 | 
					github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
 | 
				
			||||||
github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
 | 
					github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
 | 
				
			||||||
github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw=
 | 
					github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw=
 | 
				
			||||||
github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE=
 | 
					github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE=
 | 
				
			||||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
 | 
					github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
 | 
				
			||||||
@ -327,8 +328,8 @@ github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0
 | 
				
			|||||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
 | 
					github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
 | 
				
			||||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
 | 
					github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
 | 
				
			||||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 | 
					github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
 | 
				
			||||||
github.com/xanzy/go-gitlab v0.72.0 h1:/9BQTftUE7GRK/RO1eeWxG1cOE+tjwBrvRdpkeSOq6w=
 | 
					github.com/xanzy/go-gitlab v0.73.1 h1:UMagqUZLJdjss1SovIC+kJCH4k2AZWXl58gJd38Y/hI=
 | 
				
			||||||
github.com/xanzy/go-gitlab v0.72.0/go.mod h1:d/a0vswScO7Agg1CZNz15Ic6SSvBG9vfw8egL99t4kA=
 | 
					github.com/xanzy/go-gitlab v0.73.1/go.mod h1:d/a0vswScO7Agg1CZNz15Ic6SSvBG9vfw8egL99t4kA=
 | 
				
			||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
					github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
@ -350,8 +351,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
 | 
				
			|||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 | 
					golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
					golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
					golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
 | 
					golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
					golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
					golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
				
			||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
					golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
				
			||||||
@ -432,8 +433,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
 | 
				
			|||||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
					golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
					golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
					golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 h1:dtndE8FcEta75/4kHF3AbpuWzV6f1LjnLrM4pe2SZrw=
 | 
					golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 | 
					golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
@ -445,8 +446,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 | 
				
			|||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
 | 
					golang.org/x/sync v0.0.0-20220907140024-f12130a52804 h1:0SH2R3f1b1VmIMG7BXbEZCBUu2dKmHschSmjqGUrW8A=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20220907140024-f12130a52804/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
					golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
				
			||||||
@ -658,8 +659,8 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
 | 
				
			|||||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 | 
					google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 | 
				
			||||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 | 
					google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 | 
				
			||||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 | 
					google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 | 
				
			||||||
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
 | 
					google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
 | 
				
			||||||
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
 | 
					google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 | 
				
			||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 | 
					google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 | 
				
			||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 | 
					google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 | 
				
			||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 | 
					google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
 | 
				
			||||||
@ -682,8 +683,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
 | 
				
			|||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
					gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
				
			||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
					gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 | 
				
			||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
					gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
				
			||||||
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
 | 
					gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 | 
				
			||||||
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
					gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 | 
				
			||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 | 
					gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 | 
				
			||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
					gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
				
			||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,11 @@ import (
 | 
				
			|||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ModeAlwaysTrigger  = 0
 | 
				
			||||||
 | 
						ModeOnetimeTrigger = 1
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CycleTransferStats struct {
 | 
					type CycleTransferStats struct {
 | 
				
			||||||
	Name       string
 | 
						Name       string
 | 
				
			||||||
	From       time.Time
 | 
						From       time.Time
 | 
				
			||||||
@ -23,21 +28,46 @@ type AlertRule struct {
 | 
				
			|||||||
	Name                   string
 | 
						Name                   string
 | 
				
			||||||
	RulesRaw               string
 | 
						RulesRaw               string
 | 
				
			||||||
	Enable                 *bool
 | 
						Enable                 *bool
 | 
				
			||||||
 | 
						TriggerMode            int      `gorm:"default:0"` // 触发模式: 0-始终触发(默认) 1-单次触发
 | 
				
			||||||
	NotificationTag        string   // 该报警规则所在的通知组
 | 
						NotificationTag        string   // 该报警规则所在的通知组
 | 
				
			||||||
 | 
						FailTriggerTasksRaw    string   `gorm:"default:'[]'"`
 | 
				
			||||||
 | 
						RecoverTriggerTasksRaw string   `gorm:"default:'[]'"`
 | 
				
			||||||
	Rules                  []Rule   `gorm:"-" json:"-"`
 | 
						Rules                  []Rule   `gorm:"-" json:"-"`
 | 
				
			||||||
 | 
						FailTriggerTasks       []uint64 `gorm:"-" json:"-"` // 失败时执行的触发任务id
 | 
				
			||||||
 | 
						RecoverTriggerTasks    []uint64 `gorm:"-" json:"-"` // 恢复时执行的触发任务id
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *AlertRule) BeforeSave(tx *gorm.DB) error {
 | 
					func (r *AlertRule) BeforeSave(tx *gorm.DB) error {
 | 
				
			||||||
	data, err := utils.Json.Marshal(r.Rules)
 | 
						if data, err := utils.Json.Marshal(r.Rules); err != nil {
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						} else {
 | 
				
			||||||
		r.RulesRaw = string(data)
 | 
							r.RulesRaw = string(data)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if data, err := utils.Json.Marshal(r.FailTriggerTasks); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							r.FailTriggerTasksRaw = string(data)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if data, err := utils.Json.Marshal(r.RecoverTriggerTasks); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							r.RecoverTriggerTasksRaw = string(data)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *AlertRule) AfterFind(tx *gorm.DB) error {
 | 
					func (r *AlertRule) AfterFind(tx *gorm.DB) error {
 | 
				
			||||||
	return utils.Json.Unmarshal([]byte(r.RulesRaw), &r.Rules)
 | 
						var err error
 | 
				
			||||||
 | 
						if err = utils.Json.Unmarshal([]byte(r.RulesRaw), &r.Rules); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = utils.Json.Unmarshal([]byte(r.FailTriggerTasksRaw), &r.FailTriggerTasks); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err = utils.Json.Unmarshal([]byte(r.RecoverTriggerTasksRaw), &r.RecoverTriggerTasks); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *AlertRule) Enabled() bool {
 | 
					func (r *AlertRule) Enabled() bool {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
package model
 | 
					package model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
@ -67,7 +66,7 @@ func (c *AgentConfig) Save() error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ioutil.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm)
 | 
						return os.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Config 站点配置
 | 
					// Config 站点配置
 | 
				
			||||||
@ -159,5 +158,5 @@ func (c *Config) Save() error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ioutil.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm)
 | 
						return os.WriteFile(c.v.ConfigFileUsed(), data, os.ModePerm)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,11 +11,15 @@ import (
 | 
				
			|||||||
const (
 | 
					const (
 | 
				
			||||||
	CronCoverIgnoreAll = iota
 | 
						CronCoverIgnoreAll = iota
 | 
				
			||||||
	CronCoverAll
 | 
						CronCoverAll
 | 
				
			||||||
 | 
						CronCoverAlertTrigger
 | 
				
			||||||
 | 
						CronTypeCronTask    = 0
 | 
				
			||||||
 | 
						CronTypeTriggerTask = 1
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Cron struct {
 | 
					type Cron struct {
 | 
				
			||||||
	Common
 | 
						Common
 | 
				
			||||||
	Name            string
 | 
						Name            string
 | 
				
			||||||
 | 
						TaskType        uint8  `gorm:"default:0"` // 0:计划任务 1:触发任务
 | 
				
			||||||
	Scheduler       string //分钟 小时 天 月 星期
 | 
						Scheduler       string //分钟 小时 天 月 星期
 | 
				
			||||||
	Command         string
 | 
						Command         string
 | 
				
			||||||
	Servers         []uint64  `gorm:"-"`
 | 
						Servers         []uint64  `gorm:"-"`
 | 
				
			||||||
@ -23,7 +27,7 @@ type Cron struct {
 | 
				
			|||||||
	NotificationTag string    // 指定通知方式的分组
 | 
						NotificationTag string    // 指定通知方式的分组
 | 
				
			||||||
	LastExecutedAt  time.Time // 最后一次执行时间
 | 
						LastExecutedAt  time.Time // 最后一次执行时间
 | 
				
			||||||
	LastResult      bool      // 最后一次执行结果
 | 
						LastResult      bool      // 最后一次执行结果
 | 
				
			||||||
	Cover           uint8     // 计划任务覆盖范围 (0:仅覆盖特定服务器 1:仅忽略特定服务器)
 | 
						Cover           uint8     // 计划任务覆盖范围 (0:仅覆盖特定服务器 1:仅忽略特定服务器 2:由触发该计划任务的服务器执行)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CronJobID  cron.EntryID `gorm:"-"`
 | 
						CronJobID  cron.EntryID `gorm:"-"`
 | 
				
			||||||
	ServersRaw string
 | 
						ServersRaw string
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import (
 | 
				
			|||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
@ -150,7 +150,7 @@ func (ns *NotificationServerBundle) Send(message string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
 | 
						if resp.StatusCode < 200 || resp.StatusCode > 299 {
 | 
				
			||||||
		defer resp.Body.Close()
 | 
							defer resp.Body.Close()
 | 
				
			||||||
		body, _ := ioutil.ReadAll(resp.Body)
 | 
							body, _ := io.ReadAll(resp.Body)
 | 
				
			||||||
		return fmt.Errorf("%d@%s %s", resp.StatusCode, resp.Status, string(body))
 | 
							return fmt.Errorf("%d@%s %s", resp.StatusCode, resp.Status, string(body))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										30
									
								
								resource/l10n/zh-CN.toml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								resource/l10n/zh-CN.toml
									
									
									
									
										vendored
									
									
								
							@ -70,6 +70,9 @@ other = "忽略所有,仅通过特定服务器执行"
 | 
				
			|||||||
[AllIncludedOnlySpecificServersAreNotExecuted]
 | 
					[AllIncludedOnlySpecificServersAreNotExecuted]
 | 
				
			||||||
other = "覆盖所有,仅特定服务器不执行"
 | 
					other = "覆盖所有,仅特定服务器不执行"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ExecuteByTriggerServer]
 | 
				
			||||||
 | 
					other = "由触发的服务器执行"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[SpecificServers]
 | 
					[SpecificServers]
 | 
				
			||||||
other = "特定服务器"
 | 
					other = "特定服务器"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -82,6 +85,15 @@ other = "通知方式组"
 | 
				
			|||||||
[PushSuccessMessages]
 | 
					[PushSuccessMessages]
 | 
				
			||||||
other = "推送成功的消息"
 | 
					other = "推送成功的消息"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[TaskType]
 | 
				
			||||||
 | 
					other = "任务类型"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[CronTask]
 | 
				
			||||||
 | 
					other = "计划任务"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[TriggerTask]
 | 
				
			||||||
 | 
					other = "触发任务"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[TheFormaOfTheScheduleIs]
 | 
					[TheFormaOfTheScheduleIs]
 | 
				
			||||||
other = "计划的格式为:"
 | 
					other = "计划的格式为:"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -148,6 +160,21 @@ other = "添加报警规则"
 | 
				
			|||||||
[Rules]
 | 
					[Rules]
 | 
				
			||||||
other = "规则"
 | 
					other = "规则"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[NotificationTriggerMode]
 | 
				
			||||||
 | 
					other = "通知触发模式"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ModeAlwaysTrigger]
 | 
				
			||||||
 | 
					other = "始终触发"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ModeOnetimeTrigger]
 | 
				
			||||||
 | 
					other = "单次触发"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[FailTriggerTasks]
 | 
				
			||||||
 | 
					other = "故障时触发任务"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[RecoverTriggerTasks]
 | 
				
			||||||
 | 
					other = "恢复时触发任务"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Enable]
 | 
					[Enable]
 | 
				
			||||||
other = "启用"
 | 
					other = "启用"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -196,6 +223,9 @@ other = "覆盖所有"
 | 
				
			|||||||
[IgnoreAll]
 | 
					[IgnoreAll]
 | 
				
			||||||
other = "忽略所有"
 | 
					other = "忽略所有"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ByTrigger]
 | 
				
			||||||
 | 
					other = "触发执行"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[DeleteScheduledTask]
 | 
					[DeleteScheduledTask]
 | 
				
			||||||
other = "删除计划任务"
 | 
					other = "删除计划任务"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -69,6 +69,8 @@ function showFormModal(modelSelector, formID, URL, getData) {
 | 
				
			|||||||
                item.name === "ID" ||
 | 
					                item.name === "ID" ||
 | 
				
			||||||
                item.name === "RequestType" ||
 | 
					                item.name === "RequestType" ||
 | 
				
			||||||
                item.name === "RequestMethod" ||
 | 
					                item.name === "RequestMethod" ||
 | 
				
			||||||
 | 
					                item.name === "TriggerMode" ||
 | 
				
			||||||
 | 
					                item.name === "TaskType" ||
 | 
				
			||||||
                item.name === "DisplayIndex" ||
 | 
					                item.name === "DisplayIndex" ||
 | 
				
			||||||
                item.name === "Type" ||
 | 
					                item.name === "Type" ||
 | 
				
			||||||
                item.name === "Cover" ||
 | 
					                item.name === "Cover" ||
 | 
				
			||||||
@ -89,6 +91,16 @@ function showFormModal(modelSelector, formID, URL, getData) {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              if (item.name.endsWith("TasksRaw")) {
 | 
				
			||||||
 | 
					                if (item.value.length > 2) {
 | 
				
			||||||
 | 
					                  obj[item.name] = JSON.stringify(
 | 
				
			||||||
 | 
					                      [...item.value.matchAll(/\d+/gm)].map((k) =>
 | 
				
			||||||
 | 
					                          parseInt(k[0])
 | 
				
			||||||
 | 
					                      )
 | 
				
			||||||
 | 
					                  );
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              return obj;
 | 
					              return obj;
 | 
				
			||||||
            }, {});
 | 
					            }, {});
 | 
				
			||||||
        $.post(URL, JSON.stringify(data))
 | 
					        $.post(URL, JSON.stringify(data))
 | 
				
			||||||
@ -130,12 +142,51 @@ function addOrEditAlertRule(rule) {
 | 
				
			|||||||
  modal.find("input[name=ID]").val(rule ? rule.ID : null);
 | 
					  modal.find("input[name=ID]").val(rule ? rule.ID : null);
 | 
				
			||||||
  modal.find("input[name=Name]").val(rule ? rule.Name : null);
 | 
					  modal.find("input[name=Name]").val(rule ? rule.Name : null);
 | 
				
			||||||
  modal.find("textarea[name=RulesRaw]").val(rule ? rule.RulesRaw : null);
 | 
					  modal.find("textarea[name=RulesRaw]").val(rule ? rule.RulesRaw : null);
 | 
				
			||||||
 | 
					  modal.find("select[name=TriggerMode]").val(rule ? rule.TriggerMode : 0);
 | 
				
			||||||
  modal.find("input[name=NotificationTag]").val(rule ? rule.NotificationTag : null);
 | 
					  modal.find("input[name=NotificationTag]").val(rule ? rule.NotificationTag : null);
 | 
				
			||||||
  if (rule && rule.Enable) {
 | 
					  if (rule && rule.Enable) {
 | 
				
			||||||
    modal.find(".ui.rule-enable.checkbox").checkbox("set checked");
 | 
					    modal.find(".ui.rule-enable.checkbox").checkbox("set checked");
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    modal.find(".ui.rule-enable.checkbox").checkbox("set unchecked");
 | 
					    modal.find(".ui.rule-enable.checkbox").checkbox("set unchecked");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  modal.find("a.ui.label.visible").each((i, el) => {
 | 
				
			||||||
 | 
					    el.remove();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  var failTriggerTasks;
 | 
				
			||||||
 | 
					  var recoverTriggerTasks;
 | 
				
			||||||
 | 
					  if (rule) {
 | 
				
			||||||
 | 
					    failTriggerTasks = rule.FailTriggerTasksRaw;
 | 
				
			||||||
 | 
					    recoverTriggerTasks = rule.RecoverTriggerTasksRaw;
 | 
				
			||||||
 | 
					    const failTriggerTasksList = JSON.parse(failTriggerTasks || "[]");
 | 
				
			||||||
 | 
					    const recoverTriggerTasksList = JSON.parse(recoverTriggerTasks || "[]");
 | 
				
			||||||
 | 
					    const node1 = modal.find("i.dropdown.icon.1");
 | 
				
			||||||
 | 
					    const node2 = modal.find("i.dropdown.icon.2");
 | 
				
			||||||
 | 
					    for (let i = 0; i < failTriggerTasksList.length; i++) {
 | 
				
			||||||
 | 
					      node1.after(
 | 
				
			||||||
 | 
					          '<a class="ui label transition visible" data-value="' +
 | 
				
			||||||
 | 
					          failTriggerTasksList[i] +
 | 
				
			||||||
 | 
					          '" style="display: inline-block !important;">ID:' +
 | 
				
			||||||
 | 
					          failTriggerTasksList[i] +
 | 
				
			||||||
 | 
					          '<i class="delete icon"></i></a>'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (let i = 0; i < recoverTriggerTasksList.length; i++) {
 | 
				
			||||||
 | 
					      node2.after(
 | 
				
			||||||
 | 
					          '<a class="ui label transition visible" data-value="' +
 | 
				
			||||||
 | 
					          recoverTriggerTasksList[i] +
 | 
				
			||||||
 | 
					          '" style="display: inline-block !important;">ID:' +
 | 
				
			||||||
 | 
					          recoverTriggerTasksList[i] +
 | 
				
			||||||
 | 
					          '<i class="delete icon"></i></a>'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  modal
 | 
				
			||||||
 | 
					      .find("input[name=FailTriggerTasksRaw]")
 | 
				
			||||||
 | 
					      .val(rule ? "[]," + failTriggerTasks.substr(1, failTriggerTasks.length - 2) : "[]");
 | 
				
			||||||
 | 
					  modal
 | 
				
			||||||
 | 
					      .find("input[name=RecoverTriggerTasksRaw]")
 | 
				
			||||||
 | 
					      .val(rule ? "[]," + recoverTriggerTasks.substr(1, recoverTriggerTasks.length - 2) : "[]");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  showFormModal(".rule.modal", "#ruleForm", "/api/alert-rule");
 | 
					  showFormModal(".rule.modal", "#ruleForm", "/api/alert-rule");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -262,6 +313,9 @@ function addOrEditMonitor(monitor) {
 | 
				
			|||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    modal.find(".ui.nb-notify.checkbox").checkbox("set unchecked");
 | 
					    modal.find(".ui.nb-notify.checkbox").checkbox("set unchecked");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  modal.find("a.ui.label.visible").each((i, el) => {
 | 
				
			||||||
 | 
					    el.remove();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
  var servers;
 | 
					  var servers;
 | 
				
			||||||
  if (monitor) {
 | 
					  if (monitor) {
 | 
				
			||||||
    servers = monitor.SkipServersRaw;
 | 
					    servers = monitor.SkipServersRaw;
 | 
				
			||||||
@ -293,6 +347,8 @@ function addOrEditCron(cron) {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  modal.find("input[name=ID]").val(cron ? cron.ID : null);
 | 
					  modal.find("input[name=ID]").val(cron ? cron.ID : null);
 | 
				
			||||||
  modal.find("input[name=Name]").val(cron ? cron.Name : null);
 | 
					  modal.find("input[name=Name]").val(cron ? cron.Name : null);
 | 
				
			||||||
 | 
					  modal.find("select[name=TaskType]").val(cron ? cron.TaskType : 0);
 | 
				
			||||||
 | 
					  modal.find("select[name=Cover]").val(cron ? cron.Cover : 0);
 | 
				
			||||||
  modal.find("input[name=NotificationTag]").val(cron ? cron.NotificationTag : null);
 | 
					  modal.find("input[name=NotificationTag]").val(cron ? cron.NotificationTag : null);
 | 
				
			||||||
  modal.find("input[name=Scheduler]").val(cron ? cron.Scheduler : null);
 | 
					  modal.find("input[name=Scheduler]").val(cron ? cron.Scheduler : null);
 | 
				
			||||||
  modal.find("a.ui.label.visible").each((i, el) => {
 | 
					  modal.find("a.ui.label.visible").each((i, el) => {
 | 
				
			||||||
@ -430,3 +486,15 @@ $(document).ready(() => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  } catch (error) { }
 | 
					  } catch (error) { }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$(document).ready(() => {
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    $(".ui.tasks.search.dropdown").dropdown({
 | 
				
			||||||
 | 
					      clearable: true,
 | 
				
			||||||
 | 
					      apiSettings: {
 | 
				
			||||||
 | 
					        url: "/api/search-tasks?word={query}",
 | 
				
			||||||
 | 
					        cache: false,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  } catch (error) { }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								resource/template/component/cron.html
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								resource/template/component/cron.html
									
									
									
									
										vendored
									
									
								
							@ -8,6 +8,13 @@
 | 
				
			|||||||
                <label>{{tr "Name"}}</label>
 | 
					                <label>{{tr "Name"}}</label>
 | 
				
			||||||
                <input type="text" name="Name" placeholder="{{tr "BackUp"}}">
 | 
					                <input type="text" name="Name" placeholder="{{tr "BackUp"}}">
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="field">
 | 
				
			||||||
 | 
					                <label>{{tr "TaskType"}}</label>
 | 
				
			||||||
 | 
					                <select name="TaskType" class="ui fluid dropdown">
 | 
				
			||||||
 | 
					                    <option value="0">{{tr "CronTask"}}</option>
 | 
				
			||||||
 | 
					                    <option value="1">{{tr "TriggerTask"}}</option>
 | 
				
			||||||
 | 
					                </select>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
            <div class="field">
 | 
					            <div class="field">
 | 
				
			||||||
                <label>{{tr "Scheduler"}}</label>
 | 
					                <label>{{tr "Scheduler"}}</label>
 | 
				
			||||||
                <input type="text" name="Scheduler" placeholder="0 0 3 * * *{{tr "3amDaily"}}">
 | 
					                <input type="text" name="Scheduler" placeholder="0 0 3 * * *{{tr "3amDaily"}}">
 | 
				
			||||||
@ -21,6 +28,7 @@
 | 
				
			|||||||
                <select name="Cover" class="ui fluid dropdown">
 | 
					                <select name="Cover" class="ui fluid dropdown">
 | 
				
			||||||
                    <option value="0">{{tr "IgnoreAllAndExecuteOnlyThroughSpecificServers"}}</option>
 | 
					                    <option value="0">{{tr "IgnoreAllAndExecuteOnlyThroughSpecificServers"}}</option>
 | 
				
			||||||
                    <option value="1">{{tr "AllIncludedOnlySpecificServersAreNotExecuted"}}</option>
 | 
					                    <option value="1">{{tr "AllIncludedOnlySpecificServersAreNotExecuted"}}</option>
 | 
				
			||||||
 | 
					                    <option value="2">{{tr "ExecuteByTriggerServer"}}</option>
 | 
				
			||||||
                </select>
 | 
					                </select>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div class="field">
 | 
					            <div class="field">
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										26
									
								
								resource/template/component/rule.html
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								resource/template/component/rule.html
									
									
									
									
										vendored
									
									
								
							@ -16,6 +16,32 @@
 | 
				
			|||||||
                <label>{{tr "NotificationMethodGroup"}}</label>
 | 
					                <label>{{tr "NotificationMethodGroup"}}</label>
 | 
				
			||||||
                <input type="text" name="NotificationTag" placeholder="default">
 | 
					                <input type="text" name="NotificationTag" placeholder="default">
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="field">
 | 
				
			||||||
 | 
					                <label>{{tr "NotificationTriggerMode"}}</label>
 | 
				
			||||||
 | 
					                <select name="TriggerMode" class="ui fluid dropdown">
 | 
				
			||||||
 | 
					                    <option value="0">{{tr "ModeAlwaysTrigger"}}</option>
 | 
				
			||||||
 | 
					                    <option value="1">{{tr "ModeOnetimeTrigger"}}</option>
 | 
				
			||||||
 | 
					                </select>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="field">
 | 
				
			||||||
 | 
					                <label>{{tr "FailTriggerTasks"}}</label>
 | 
				
			||||||
 | 
					                <div class="ui fluid multiple tasks search selection dropdown">
 | 
				
			||||||
 | 
					                    <input type="hidden" name="FailTriggerTasksRaw">
 | 
				
			||||||
 | 
					                    <i class="dropdown icon 1"></i>
 | 
				
			||||||
 | 
					                    <div class="default text">{{tr "EnterIdAndNameToSearch"}}</div>
 | 
				
			||||||
 | 
					                    <div class="menu"></div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="field">
 | 
				
			||||||
 | 
					                <label>{{tr "RecoverTriggerTasks"}}</label>
 | 
				
			||||||
 | 
					                <div class="ui fluid multiple tasks search selection dropdown">
 | 
				
			||||||
 | 
					                    <input type="hidden" name="RecoverTriggerTasksRaw">
 | 
				
			||||||
 | 
					                    <i class="dropdown icon 2"></i>
 | 
				
			||||||
 | 
					                    <div class="default text">{{tr "EnterIdAndNameToSearch"}}</div>
 | 
				
			||||||
 | 
					                    <div class="menu"></div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <div class="field">
 | 
					            <div class="field">
 | 
				
			||||||
                <div class="ui rule-enable checkbox">
 | 
					                <div class="ui rule-enable checkbox">
 | 
				
			||||||
                    <input name="Enable" type="checkbox" tabindex="0" class="hidden">
 | 
					                    <input name="Enable" type="checkbox" tabindex="0" class="hidden">
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@
 | 
				
			|||||||
                <tr>
 | 
					                <tr>
 | 
				
			||||||
                    <th>ID</th>
 | 
					                    <th>ID</th>
 | 
				
			||||||
                    <th>{{tr "Name"}}</th>
 | 
					                    <th>{{tr "Name"}}</th>
 | 
				
			||||||
 | 
					                    <th>{{tr "TaskType"}}</th>
 | 
				
			||||||
                    <th>{{tr "Scheduler"}}</th>
 | 
					                    <th>{{tr "Scheduler"}}</th>
 | 
				
			||||||
                    <th>{{tr "Command"}}</th>
 | 
					                    <th>{{tr "Command"}}</th>
 | 
				
			||||||
                    <th>{{tr "NotificationMethodGroup"}}</th>
 | 
					                    <th>{{tr "NotificationMethodGroup"}}</th>
 | 
				
			||||||
@ -31,11 +32,12 @@
 | 
				
			|||||||
                <tr>
 | 
					                <tr>
 | 
				
			||||||
                    <td>{{$cron.ID}}</td>
 | 
					                    <td>{{$cron.ID}}</td>
 | 
				
			||||||
                    <td>{{$cron.Name}}</td>
 | 
					                    <td>{{$cron.Name}}</td>
 | 
				
			||||||
 | 
					                    <td>{{if eq $cron.TaskType 0}}{{tr "CronTask"}}{{else}}{{tr "TriggerTask"}}{{end}}</td>
 | 
				
			||||||
                    <td>{{$cron.Scheduler}}</td>
 | 
					                    <td>{{$cron.Scheduler}}</td>
 | 
				
			||||||
                    <td>{{$cron.Command}}</td>
 | 
					                    <td>{{$cron.Command}}</td>
 | 
				
			||||||
                    <td>{{$cron.NotificationTag}}</td>
 | 
					                    <td>{{$cron.NotificationTag}}</td>
 | 
				
			||||||
                    <td>{{$cron.PushSuccessful}}</td>
 | 
					                    <td>{{$cron.PushSuccessful}}</td>
 | 
				
			||||||
                    <td>{{if eq $cron.Cover 0}}{{tr "IgnoreAll"}}{{else}}{{tr "CoverAll"}}{{end}}</td>
 | 
					                    <td>{{if eq $cron.Cover 0}}{{tr "IgnoreAll"}}{{else if eq $cron.Cover 1}}{{tr "CoverAll"}}{{else}}{{tr "ByTrigger"}}{{end}}</td>
 | 
				
			||||||
                    <td>{{$cron.ServersRaw}}</td>
 | 
					                    <td>{{$cron.ServersRaw}}</td>
 | 
				
			||||||
                    <td>{{$cron.LastExecutedAt|tf}}</td>
 | 
					                    <td>{{$cron.LastExecutedAt|tf}}</td>
 | 
				
			||||||
                    <td>{{$cron.LastResult}}</td>
 | 
					                    <td>{{$cron.LastResult}}</td>
 | 
				
			||||||
 | 
				
			|||||||
@ -58,7 +58,10 @@
 | 
				
			|||||||
                    <th>ID</th>
 | 
					                    <th>ID</th>
 | 
				
			||||||
                    <th>{{tr "Name"}}</th>
 | 
					                    <th>{{tr "Name"}}</th>
 | 
				
			||||||
                    <th>{{tr "NotificationMethodGroup"}}</th>
 | 
					                    <th>{{tr "NotificationMethodGroup"}}</th>
 | 
				
			||||||
 | 
					                    <th>{{tr "NotificationTriggerMode"}}</th>
 | 
				
			||||||
                    <th>{{tr "Rules"}}</th>
 | 
					                    <th>{{tr "Rules"}}</th>
 | 
				
			||||||
 | 
					                    <th>{{tr "FailTriggerTasks"}}</th>
 | 
				
			||||||
 | 
					                    <th>{{tr "RecoverTriggerTasks"}}</th>
 | 
				
			||||||
                    <th>{{tr "Enable"}}</th>
 | 
					                    <th>{{tr "Enable"}}</th>
 | 
				
			||||||
                    <th>{{tr "Administration"}}</th>
 | 
					                    <th>{{tr "Administration"}}</th>
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
@ -69,7 +72,10 @@
 | 
				
			|||||||
                    <td>{{$rule.ID}}</td>
 | 
					                    <td>{{$rule.ID}}</td>
 | 
				
			||||||
                    <td>{{$rule.Name}}</td>
 | 
					                    <td>{{$rule.Name}}</td>
 | 
				
			||||||
                    <td>{{$rule.NotificationTag}}</td>
 | 
					                    <td>{{$rule.NotificationTag}}</td>
 | 
				
			||||||
 | 
					                    <td>{{if eq $rule.TriggerMode  0}}{{tr "ModeAlwaysTrigger"}}{{else}}{{tr "ModeOnetimeTrigger"}}{{end}}
 | 
				
			||||||
                    <td>{{$rule.RulesRaw}}</td>
 | 
					                    <td>{{$rule.RulesRaw}}</td>
 | 
				
			||||||
 | 
					                    <td>{{$rule.FailTriggerTasksRaw}}</td>
 | 
				
			||||||
 | 
					                    <td>{{$rule.RecoverTriggerTasksRaw}}</td>
 | 
				
			||||||
                    <td>{{$rule.Enable}}</td>
 | 
					                    <td>{{$rule.Enable}}</td>
 | 
				
			||||||
                    <td>
 | 
					                    <td>
 | 
				
			||||||
                        <div class="ui mini icon buttons">
 | 
					                        <div class="ui mini icon buttons">
 | 
				
			||||||
 | 
				
			|||||||
@ -6,11 +6,11 @@
 | 
				
			|||||||
    <meta charset="UTF-8">
 | 
					    <meta charset="UTF-8">
 | 
				
			||||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
					    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
				
			||||||
    <title>Redireting..</title>
 | 
					    <title>Redirecting..</title>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
    <p>Please click <a href="{{.URL}}">here</a> if you are not redirected.</p>
 | 
					    <p>If you are not redirected, please click <a href="{{.URL}}">here</a>.</p>
 | 
				
			||||||
    <script>window.location.href = "{{.URL}}"</script>
 | 
					    <script>window.location.href = "{{.URL}}"</script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -153,17 +153,25 @@ func checkStatus() {
 | 
				
			|||||||
			// 保存当前服务器状态信息
 | 
								// 保存当前服务器状态信息
 | 
				
			||||||
			curServer := model.Server{}
 | 
								curServer := model.Server{}
 | 
				
			||||||
			copier.Copy(&curServer, server)
 | 
								copier.Copy(&curServer, server)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 本次未通过检查
 | 
				
			||||||
			if !passed {
 | 
								if !passed {
 | 
				
			||||||
 | 
									// 始终触发模式或上次检查不为失败时触发报警(跳过单次触发+上次失败的情况)
 | 
				
			||||||
 | 
									if alert.TriggerMode == model.ModeAlwaysTrigger || alertsPrevState[alert.ID][server.ID] != _RuleCheckFail {
 | 
				
			||||||
					alertsPrevState[alert.ID][server.ID] = _RuleCheckFail
 | 
										alertsPrevState[alert.ID][server.ID] = _RuleCheckFail
 | 
				
			||||||
					message := fmt.Sprintf("[%s] %s(%s) %s", Localizer.MustLocalize(&i18n.LocalizeConfig{
 | 
										message := fmt.Sprintf("[%s] %s(%s) %s", Localizer.MustLocalize(&i18n.LocalizeConfig{
 | 
				
			||||||
						MessageID: "Incident",
 | 
											MessageID: "Incident",
 | 
				
			||||||
					}), server.Name, IPDesensitize(server.Host.IP), alert.Name)
 | 
										}), server.Name, IPDesensitize(server.Host.IP), alert.Name)
 | 
				
			||||||
 | 
										go SendTriggerTasks(alert.FailTriggerTasks, curServer.ID)
 | 
				
			||||||
					go SendNotification(alert.NotificationTag, message, true, &curServer)
 | 
										go SendNotification(alert.NotificationTag, message, true, &curServer)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
 | 
									// 本次通过检查但上一次的状态为失败,则发送恢复通知
 | 
				
			||||||
				if alertsPrevState[alert.ID][server.ID] == _RuleCheckFail {
 | 
									if alertsPrevState[alert.ID][server.ID] == _RuleCheckFail {
 | 
				
			||||||
					message := fmt.Sprintf("[%s] %s(%s) %s", Localizer.MustLocalize(&i18n.LocalizeConfig{
 | 
										message := fmt.Sprintf("[%s] %s(%s) %s", Localizer.MustLocalize(&i18n.LocalizeConfig{
 | 
				
			||||||
						MessageID: "Resolved",
 | 
											MessageID: "Resolved",
 | 
				
			||||||
					}), server.Name, IPDesensitize(server.Host.IP), alert.Name)
 | 
										}), server.Name, IPDesensitize(server.Host.IP), alert.Name)
 | 
				
			||||||
 | 
										go SendTriggerTasks(alert.RecoverTriggerTasks, curServer.ID)
 | 
				
			||||||
					go SendNotification(alert.NotificationTag, message, true, &curServer)
 | 
										go SendNotification(alert.NotificationTag, message, true, &curServer)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				alertsPrevState[alert.ID][server.ID] = _RuleCheckPass
 | 
									alertsPrevState[alert.ID][server.ID] = _RuleCheckPass
 | 
				
			||||||
 | 
				
			|||||||
@ -3,9 +3,10 @@ package singleton
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/jinzhu/copier"
 | 
					 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/jinzhu/copier"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/robfig/cron/v3"
 | 
						"github.com/robfig/cron/v3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/naiba/nezha/model"
 | 
						"github.com/naiba/nezha/model"
 | 
				
			||||||
@ -32,6 +33,11 @@ func LoadCronTasks() {
 | 
				
			|||||||
	var notificationTagList []string
 | 
						var notificationTagList []string
 | 
				
			||||||
	notificationMsgMap := make(map[string]*bytes.Buffer)
 | 
						notificationMsgMap := make(map[string]*bytes.Buffer)
 | 
				
			||||||
	for i := 0; i < len(crons); i++ {
 | 
						for i := 0; i < len(crons); i++ {
 | 
				
			||||||
 | 
							// 触发任务类型无需注册
 | 
				
			||||||
 | 
							if crons[i].TaskType == model.CronTypeTriggerTask {
 | 
				
			||||||
 | 
								Crons[crons[i].ID] = &crons[i]
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		// 旧版本计划任务可能不存在通知组 为其添加默认通知组
 | 
							// 旧版本计划任务可能不存在通知组 为其添加默认通知组
 | 
				
			||||||
		if crons[i].NotificationTag == "" {
 | 
							if crons[i].NotificationTag == "" {
 | 
				
			||||||
			crons[i].NotificationTag = "default"
 | 
								crons[i].NotificationTag = "default"
 | 
				
			||||||
@ -63,12 +69,51 @@ func ManualTrigger(c model.Cron) {
 | 
				
			|||||||
	CronTrigger(c)()
 | 
						CronTrigger(c)()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func CronTrigger(cr model.Cron) func() {
 | 
					func SendTriggerTasks(taskIDs []uint64, triggerServer uint64) {
 | 
				
			||||||
 | 
						CronLock.RLock()
 | 
				
			||||||
 | 
						var cronLists []*model.Cron
 | 
				
			||||||
 | 
						for _, taskID := range taskIDs {
 | 
				
			||||||
 | 
							if c, ok := Crons[taskID]; ok {
 | 
				
			||||||
 | 
								cronLists = append(cronLists, c)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						CronLock.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 依次调用CronTrigger发送任务
 | 
				
			||||||
 | 
						for _, c := range cronLists {
 | 
				
			||||||
 | 
							go CronTrigger(*c, triggerServer)()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func CronTrigger(cr model.Cron, triggerServer ...uint64) func() {
 | 
				
			||||||
	crIgnoreMap := make(map[uint64]bool)
 | 
						crIgnoreMap := make(map[uint64]bool)
 | 
				
			||||||
	for j := 0; j < len(cr.Servers); j++ {
 | 
						for j := 0; j < len(cr.Servers); j++ {
 | 
				
			||||||
		crIgnoreMap[cr.Servers[j]] = true
 | 
							crIgnoreMap[cr.Servers[j]] = true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return func() {
 | 
						return func() {
 | 
				
			||||||
 | 
							if cr.Cover == model.CronCoverAlertTrigger {
 | 
				
			||||||
 | 
								if len(triggerServer) == 0 {
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ServerLock.RLock()
 | 
				
			||||||
 | 
								defer ServerLock.RUnlock()
 | 
				
			||||||
 | 
								if s, ok := ServerList[triggerServer[0]]; ok {
 | 
				
			||||||
 | 
									if s.TaskStream != nil {
 | 
				
			||||||
 | 
										s.TaskStream.Send(&pb.Task{
 | 
				
			||||||
 | 
											Id:   cr.ID,
 | 
				
			||||||
 | 
											Data: cr.Command,
 | 
				
			||||||
 | 
											Type: model.TaskTypeCommand,
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										// 保存当前服务器状态信息
 | 
				
			||||||
 | 
										curServer := model.Server{}
 | 
				
			||||||
 | 
										copier.Copy(&curServer, s)
 | 
				
			||||||
 | 
										SendNotification(cr.NotificationTag, fmt.Sprintf("[任务失败] %s,服务器 %s 离线,无法执行。", cr.Name, s.Name), false, &curServer)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ServerLock.RLock()
 | 
							ServerLock.RLock()
 | 
				
			||||||
		defer ServerLock.RUnlock()
 | 
							defer ServerLock.RUnlock()
 | 
				
			||||||
		for _, s := range ServerList {
 | 
							for _, s := range ServerList {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user