Files
worker/app/models/notification.go
Gleb Tv 2c7a0236da feat: publish standalone worker
Separate worker packaging and service lifecycle from the control plane.
2026-07-13 17:55:14 +03:00

194 строки
5.3 KiB
Go

package models
import (
"log"
"time"
"rsgit.ru/rsmon/rsmon/app/models/concerns"
"rsgit.ru/rsmon/rsmon/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
}