package notifier import ( "log" "time" "rocketgit.ru/rsmon/worker/app/models" ) // RunExp provides functionality. // // A panic in any single check's notification must not kill the scheduler // goroutine. The defensive recover() keeps the 2h tick alive even if // RunExpCheck trips over a bad row or a stale schema reference. func RunExp() { defer func() { if r := recover(); r != nil { log.Printf("notifier: RunExp recovered from panic: %v", r) } }() _ = models.LogCheck("exp") checks := make([]models.Check, 0) err := models.ExpScope(models.DB()).Find(&checks).Error if err != nil { log.Println(err) return } for i := range checks { RunExpCheck(&checks[i]) } } // RunExpCheck provides functionality. // // A panic in any per-row work (notifier, contact lookup, message write) is // contained here so one bad row cannot take down the whole RunExp scheduler. // The panic is logged with the check id and the loop continues. func RunExpCheck(c *models.Check) { defer func() { if r := recover(); r != nil { log.Printf("notifier: RunExpCheck recovered from panic on check %d: %v", c.ID, r) } }() if c.Monitor == nil { log.Println("!BUG! check", c.ID, "has no Monitor (or not preloaded). Monitor ID: ", c.MonitorID, " Not running.") log.Println() return } if !c.Monitor.Enabled { return } if c.Monitor.Group == nil { log.Println("!BUG! check", c.ID, "has no Monitor (or not preloaded). Monitor ID: ", c.Monitor.ID, ", group id:", c.Monitor.GroupID, " Not running.") //nolint:lll return } if c.Monitor.Group.Notifications == nil { log.Println("!BUG! check", c.ID, "has no .Monitor.Group.Notifications (or not preloaded). Not running.") return } for i := range c.Monitor.Group.Notifications { n := &c.Monitor.Group.Notifications[i] if n.BeforeExpiration == nil { continue } if c.Expires == nil { // should not happen continue } if c.Kind == "whois" && !n.NotifyWHOIS { continue } if c.Kind == "ssl" && !n.NotifySSL { continue } // notify delay not reached notifyOn := time.Now().Add(time.Second * time.Duration(*n.BeforeExpiration)) if c.Expires.After(notifyOn) { continue } createExpMessage(c, n) } } func createExpMessage(c *models.Check, n *models.Notification) { for _, contact := range n.GetContacts() { //nolint:gocritic // range copy is acceptable here message := models.Message{ CheckID: &c.ID, NotificationID: n.ID, ContactID: contact.ID, Kind: "exp", } models.DB(). Where(message). Where("created_at > ?", time.Now().Add(-time.Hour*24*14)). Find(&message) if message.ID > 0 { continue } message.State = models.TaskStateQueued err := models.DB().Save(&message).Error if err != nil { log.Println(err) return // panic(err) } } }