package models import ( "gorm.io/datatypes" "rocketgit.ru/rsmon/worker/app/models/concerns" ) // Contact represents a notification contact. // // Ownership / tenancy: // // - A Contact belongs to exactly one Account (tenant). AccountID may be // nil for system-level admin contacts (see SystemContacts). // - A Contact may belong to at most one User. UserID is set when the // contact was created on behalf of a specific user (the typical // case for self-service "My email" / "My Telegram" contacts) and is // nil for account-wide contacts. // // `User` is omitempty because most list payloads don't preload it; the // `/settings/users` page loads it server-side via the AccountUserRow // payload. type Contact struct { concerns.Model AccountID *int64 `json:"account_id" gorm:"type:bigint REFERENCES accounts(id)"` Account *Account `json:"-"` UserID *int64 `gorm:"type:bigint REFERENCES users(id)" json:"user_id"` User *User `json:"user,omitempty"` Name string `json:"name" gorm:"not null"` Kind string `json:"kind" gorm:"not null;index:contact_value"` Value string `json:"value" gorm:"index:contact_value"` Enabled bool `json:"enabled" gorm:"not null;default:true"` Data datatypes.JSON `json:"data"` IsSystem *bool `json:"is_system" gorm:"default:false"` Notifications []Notification `json:"-" gorm:"many2many:notification_contacts;"` NotificationsCount int `gorm:"-:all" json:"notifications_count"` MonitorsCount int `gorm:"-:all" json:"monitors_count"` Messages []Message `json:"-"` concerns.Timestamped concerns.HasToken Audited } // SystemContacts returns all contacts marked as system/admin (is_system=true). // These are the contacts distributed workers notify directly when the main // API is unreachable (see docs/worker-protocol.md "System Selfcheck"). func SystemContacts() ([]Contact, error) { var contacts []Contact err := DB().Where("is_system = ? AND enabled = ?", true, true).Find(&contacts).Error return contacts, err } // ContactsCounts fills NotificationsCount and MonitorsCount for each contact. func ContactsCounts(contacts *[]Contact) { groupIDs := make(map[int64]bool, 0) groupCount := make(map[int64]int, 0) for i, c := range *contacts { //nolint:gocritic // range copy is acceptable here (*contacts)[i].NotificationsCount = len(c.Notifications) for _, n := range c.Notifications { //nolint:gocritic // range copy is acceptable here for _, g := range n.Groups { //nolint:gocritic // range copy is acceptable here groupIDs[g.ID] = true groupCount[g.ID] = 0 } } } gids := make([]int64, 0, len(groupIDs)) for g := range groupIDs { gids = append(gids, g) } CountGroups(gids, &groupCount) for i, c := range *contacts { //nolint:gocritic // range copy is acceptable here cnt := 0 for _, n := range c.Notifications { //nolint:gocritic // range copy is acceptable here for _, g := range n.Groups { //nolint:gocritic // range copy is acceptable here cnt += groupCount[g.ID] } } (*contacts)[i].MonitorsCount = cnt } }