mirror of
https://github.com/nezhahq/nezha.git
synced 2025-01-23 13:18:13 -05:00
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…
Reference in New Issue
Block a user