package models import ( "log" "time" "rocketgit.ru/rsmon/worker/app/models/concerns" "rocketgit.ru/rsmon/worker/internal/workdays" ) // Notification provides functionality. type Notification struct { concerns.Model Name string `json:"name" gorm:"not null"` AccountID int64 `gorm:"type:bigint REFERENCES accounts(id)" json:"account_id"` Account *Account `json:"-"` Enabled bool `gorm:"not null;default:true" json:"enabled"` ContactIDs []int64 `gorm:"-:all" json:"contact_ids"` Contacts []Contact `gorm:"many2many:notification_contacts;" json:"contacts,omitempty"` GroupIDs []int64 `gorm:"-:all" json:"group_ids"` Groups []Group `gorm:"many2many:notification_groups;" json:"-"` AlertDelay *int64 `json:"alert_delay,omitempty"` // RepeatAlert *int64 `json:"repeat_alert,omitempty"` BeforeExpiration *int64 `json:"before_expiration,omitempty"` NotifyDown bool `gorm:"default:true" json:"notify_down"` NotifyRestore bool `gorm:"default:true" json:"notify_restore"` NotifyWHOIS bool `gorm:"default:true" json:"notify_whois"` NotifySSL bool `gorm:"default:true" json:"notify_ssl"` NotifyDays *int `json:"notify_days"` NotifyDayStart *int `json:"notify_day_start"` NotifyDayEnd *int `json:"notify_day_end"` NotifyHolidays bool `gorm:"default:true" json:"notify_holidays"` Messages []Message `json:"-"` concerns.Timestamped Audited } const notificationDebug = false // EnabledNow checks if the notification is enabled at the given time. func (n *Notification) EnabledNow(tn *time.Time) bool { weekday := int(tn.Weekday()) // делаем из 0-воскр 1-пн 6-сб вариант 0-пн 6-воскр if weekday == 0 { weekday = 7 } weekday-- if notificationDebug { log.Println("notification", n.ID, "check enabled now at", tn, "for day", weekday) } if !n.NotifyHolidays { c := workdays.GetCalendar() if !c.IsWorkday(*tn) { if notificationDebug { log.Println("notification", n.ID, "is not enabled on holiday", tn) } return false } } minusOneDay := false notifyFromDay := true if n.NotifyDayStart != nil && n.NotifyDayEnd != nil { bod := BeginningOfDay(*tn) secondsToday := int(tn.Sub(bod) / time.Second) ds := *n.NotifyDayStart de := *n.NotifyDayEnd // вариант 9 утра - 2 часа ночи if ds == de { //nolint:gocritic // complex condition chain notifyFromDay = true } else if ds > de { // с 0 до DayEnd if secondsToday < de { //nolint:gocritic // complex condition chain minusOneDay = true notifyFromDay = true } else if secondsToday < ds { // с DayEnd до DayStart notifyFromDay = false } else { notifyFromDay = true } } else { if secondsToday < ds { //nolint:gocritic // complex condition chain // с 0 до DayStart notifyFromDay = false } else if secondsToday > de { notifyFromDay = false } else { notifyFromDay = true } } // log.Println(secondsToday) } if !notifyFromDay { if notificationDebug { log.Println("notification", n.ID, *n.NotifyDays, "is NOT enabled as notifyFromDay", tn) } return false } // Если время 0-dayStart считаем что это прошлый день if minusOneDay { weekday-- if weekday < 0 { weekday = 6 } } if n.NotifyDays != nil { if !HasBit(*n.NotifyDays, uint(weekday)) { if notificationDebug { log.Println("notification", n.ID, *n.NotifyDays, "is NOT enabled on weekday", tn.Weekday(), weekday, tn) } return false } if notificationDebug { log.Println("notification", n.ID, *n.NotifyDays, "is enabled on weekday", tn.Weekday(), weekday, tn) } } if notificationDebug { log.Println("notification", n.ID, *n.NotifyDays, "is enabled", tn) } return true } // NotificationLoadIDs provides functionality. func NotificationLoadIDs(notifications *[]Notification) { for i, n := range *notifications { //nolint:gocritic // range copy is acceptable here cids := make([]int64, len(n.Contacts)) for i, c := range n.Contacts { //nolint:gocritic // range copy is acceptable here cids[i] = c.ID } (*notifications)[i].ContactIDs = cids gids := make([]int64, len(n.Groups)) for j, g := range n.Groups { //nolint:gocritic // range copy is acceptable here gids[j] = g.ID } (*notifications)[i].GroupIDs = gids } } // PersistRelations provides functionality. func (n *Notification) PersistRelations() error { cts := make([]Contact, len(n.ContactIDs)) for i, c := range n.ContactIDs { ct := Contact{} ct.ID = c cts[i] = ct } err := DB().Model(&n).Association("Contacts").Replace(cts) if err != nil { return err } grp := make([]Group, len(n.GroupIDs)) for i, g := range n.GroupIDs { gr := Group{} gr.ID = g grp[i] = gr } err = DB().Model(&n).Association("Groups").Replace(grp) if err != nil { return err } return nil } // GetContacts returns the contacts associated with this notification. // Errors are logged and an empty slice is returned instead of panicking so // that a single misconfigured notification cannot kill the scheduler goroutine // that processes expiry alerts (see internal/notifier.RunExp). func (n *Notification) GetContacts() []Contact { contacts := make([]Contact, 0) err := DB().Model(*n).Where("enabled = ?", true).Association("Contacts").Find(&contacts) if err != nil { log.Printf("notification %d: GetContacts failed: %v", n.ID, err) return contacts } return contacts }