2022-04-12 01:16:33 -04:00
package singleton
import (
2024-12-08 07:21:35 -05:00
"cmp"
2022-04-12 01:16:33 -04:00
"fmt"
2024-10-26 11:57:47 -04:00
"slices"
2024-11-20 08:36:21 -05:00
"strings"
2022-04-14 04:41:34 -04:00
2022-09-14 10:14:47 -04:00
"github.com/jinzhu/copier"
2022-04-14 04:41:34 -04:00
"github.com/robfig/cron/v3"
2024-11-28 06:38:54 -05:00
"github.com/nezhahq/nezha/model"
2024-12-21 11:05:41 -05:00
"github.com/nezhahq/nezha/pkg/utils"
2024-11-28 06:38:54 -05:00
pb "github.com/nezhahq/nezha/proto"
2022-04-12 01:16:33 -04:00
)
2025-02-21 10:08:12 -05:00
type CronClass struct {
class [ uint64 , * model . Cron ]
* cron . Cron
}
2024-10-26 11:57:47 -04:00
2025-02-21 10:08:12 -05:00
func NewCronClass ( ) * CronClass {
cronx := cron . New ( cron . WithSeconds ( ) , cron . WithLocation ( Loc ) )
list := make ( map [ uint64 ] * model . Cron )
2022-04-12 01:16:33 -04:00
2025-02-21 10:08:12 -05:00
var sortedList [ ] * model . Cron
DB . Find ( & sortedList )
2022-04-12 01:16:33 -04:00
var err error
2024-10-23 09:55:12 -04:00
var notificationGroupList [ ] uint64
2024-11-20 08:36:21 -05:00
notificationMsgMap := make ( map [ uint64 ] * strings . Builder )
2025-02-21 10:08:12 -05:00
for _ , cron := range sortedList {
2022-09-12 18:14:47 -04:00
// 触发任务类型无需注册
2024-10-31 17:07:04 -04:00
if cron . TaskType == model . CronTypeTriggerTask {
2025-02-21 10:08:12 -05:00
list [ cron . ID ] = cron
2022-09-12 18:14:47 -04:00
continue
}
2022-04-12 01:16:33 -04:00
// 注册计划任务
2025-02-21 10:08:12 -05:00
cron . CronJobID , err = cronx . AddFunc ( cron . Scheduler , CronTrigger ( cron ) )
2022-04-12 01:16:33 -04:00
if err == nil {
2025-02-21 10:08:12 -05:00
list [ cron . ID ] = cron
2022-04-12 01:16:33 -04:00
} else {
2022-04-14 22:55:21 -04:00
// 当前通知组首次出现 将其加入通知组列表并初始化通知组消息缓存
2024-10-31 17:07:04 -04:00
if _ , ok := notificationMsgMap [ cron . NotificationGroupID ] ; ! ok {
notificationGroupList = append ( notificationGroupList , cron . NotificationGroupID )
2024-11-20 08:36:21 -05:00
notificationMsgMap [ cron . NotificationGroupID ] = new ( strings . Builder )
2024-10-31 17:07:04 -04:00
notificationMsgMap [ cron . NotificationGroupID ] . WriteString ( Localizer . T ( "Tasks failed to register: [" ) )
2022-04-12 01:16:33 -04:00
}
2024-10-31 17:07:04 -04:00
notificationMsgMap [ cron . NotificationGroupID ] . WriteString ( fmt . Sprintf ( "%d," , cron . ID ) )
2022-04-12 01:16:33 -04:00
}
}
2025-02-21 10:08:12 -05:00
2022-04-14 22:55:21 -04:00
// 向注册错误的计划任务所在通知组发送通知
2024-10-23 09:55:12 -04:00
for _ , gid := range notificationGroupList {
2024-10-31 17:07:04 -04:00
notificationMsgMap [ gid ] . WriteString ( Localizer . T ( "] These tasks will not execute properly. Fix them in the admin dashboard." ) )
2025-02-21 10:08:12 -05:00
NotificationShared . SendNotification ( gid , notificationMsgMap [ gid ] . String ( ) , nil )
}
cronx . Start ( )
return & CronClass {
class : class [ uint64 , * model . Cron ] {
list : list ,
sortedList : sortedList ,
} ,
Cron : cronx ,
2022-04-12 01:16:33 -04:00
}
}
2025-02-21 10:08:12 -05:00
func ( c * CronClass ) Update ( cr * model . Cron ) {
c . listMu . Lock ( )
crOld := c . list [ cr . ID ]
2024-10-26 11:57:47 -04:00
if crOld != nil && crOld . CronJobID != 0 {
2025-02-21 10:08:12 -05:00
c . Cron . Remove ( crOld . CronJobID )
2024-10-26 11:57:47 -04:00
}
2025-02-21 10:08:12 -05:00
delete ( c . list , cr . ID )
c . list [ cr . ID ] = cr
c . listMu . Unlock ( )
2024-10-26 11:57:47 -04:00
2025-02-21 10:08:12 -05:00
c . sortList ( )
2024-10-26 11:57:47 -04:00
}
2025-02-21 10:08:12 -05:00
func ( c * CronClass ) Delete ( idList [ ] uint64 ) {
c . listMu . Lock ( )
for _ , id := range idList {
cr := c . list [ id ]
2024-10-26 11:57:47 -04:00
if cr != nil && cr . CronJobID != 0 {
2025-02-21 10:08:12 -05:00
c . Cron . Remove ( cr . CronJobID )
2024-10-26 11:57:47 -04:00
}
2025-02-21 10:08:12 -05:00
delete ( c . list , id )
2024-10-26 11:57:47 -04:00
}
2025-02-21 10:08:12 -05:00
c . listMu . Unlock ( )
c . sortList ( )
2024-10-26 11:57:47 -04:00
}
2025-02-21 10:08:12 -05:00
func ( c * CronClass ) sortList ( ) {
c . listMu . RLock ( )
defer c . listMu . RUnlock ( )
sortedList := utils . MapValuesToSlice ( c . list )
slices . SortFunc ( sortedList , func ( a , b * model . Cron ) int {
return cmp . Compare ( a . ID , b . ID )
} )
c . sortedListMu . Lock ( )
defer c . sortedListMu . Unlock ( )
c . sortedList = sortedList
2022-04-12 01:16:33 -04:00
}
2025-02-21 10:08:12 -05:00
func ( c * CronClass ) SendTriggerTasks ( taskIDs [ ] uint64 , triggerServer uint64 ) {
c . listMu . RLock ( )
2022-09-13 23:14:23 -04:00
var cronLists [ ] * model . Cron
for _ , taskID := range taskIDs {
2025-02-21 10:08:12 -05:00
if c , ok := c . list [ taskID ] ; ok {
2022-09-13 23:14:23 -04:00
cronLists = append ( cronLists , c )
}
}
2025-02-21 10:08:12 -05:00
c . listMu . RUnlock ( )
2022-09-13 23:14:23 -04:00
// 依次调用CronTrigger发送任务
for _ , c := range cronLists {
2024-10-26 11:57:47 -04:00
go CronTrigger ( c , triggerServer ) ( )
2022-09-13 23:14:23 -04:00
}
}
2025-02-21 10:08:12 -05:00
func ManualTrigger ( cr * model . Cron ) {
CronTrigger ( cr ) ( )
}
2024-10-26 11:57:47 -04:00
func CronTrigger ( cr * model . Cron , triggerServer ... uint64 ) func ( ) {
2022-04-12 01:16:33 -04:00
crIgnoreMap := make ( map [ uint64 ] bool )
for j := 0 ; j < len ( cr . Servers ) ; j ++ {
crIgnoreMap [ cr . Servers [ j ] ] = true
}
return func ( ) {
2022-09-14 10:14:47 -04:00
if cr . Cover == model . CronCoverAlertTrigger {
2022-09-12 18:14:47 -04:00
if len ( triggerServer ) == 0 {
return
}
2025-02-21 10:08:12 -05:00
if s , ok := ServerShared . Get ( triggerServer [ 0 ] ) ; ok {
2022-09-12 18:14:47 -04:00
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 )
2025-02-21 10:08:12 -05:00
NotificationShared . SendNotification ( cr . NotificationGroupID , Localizer . Tf ( "[Task failed] %s: server %s is offline and cannot execute the task" , cr . Name , s . Name ) , nil , & curServer )
2022-09-12 18:14:47 -04:00
}
}
return
}
2025-02-21 10:08:12 -05:00
for _ , s := range ServerShared . Range {
2022-04-12 01:16:33 -04:00
if cr . Cover == model . CronCoverAll && crIgnoreMap [ s . ID ] {
continue
}
if cr . Cover == model . CronCoverIgnoreAll && ! crIgnoreMap [ s . ID ] {
continue
}
if s . TaskStream != nil {
s . TaskStream . Send ( & pb . Task {
Id : cr . ID ,
Data : cr . Command ,
Type : model . TaskTypeCommand ,
} )
} else {
2022-04-18 07:59:42 -04:00
// 保存当前服务器状态信息
curServer := model . Server { }
copier . Copy ( & curServer , s )
2025-02-21 10:08:12 -05:00
NotificationShared . SendNotification ( cr . NotificationGroupID , Localizer . Tf ( "[Task failed] %s: server %s is offline and cannot execute the task" , cr . Name , s . Name ) , nil , & curServer )
2022-04-12 01:16:33 -04:00
}
}
}
}