Add configurable email notification digests

This commit is contained in:
maxim
2026-07-31 23:01:46 +03:00
parent 58ec47b188
commit a987667233
5 changed files with 288 additions and 2 deletions

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"time"
_ "modernc.org/sqlite"
@@ -59,6 +60,20 @@ type HistoryPoint struct {
type Store struct{ db *sql.DB }
type EmailSettings struct {
Enabled bool `json:"enabled"`
Host string `json:"host"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password,omitempty"`
HasPassword bool `json:"hasPassword"`
From string `json:"from"`
To string `json:"to"`
IntervalMinutes int `json:"intervalMinutes"`
Severities []string `json:"severities"`
Sources []string `json:"sources"`
}
func openStore(path string) (*Store, error) {
if path != ":memory:" {
if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
@@ -114,6 +129,55 @@ func (s *Store) SaveThresholds(value AlertThresholds) error {
return err
}
func defaultEmailSettings() EmailSettings {
return EmailSettings{Port: 587, IntervalMinutes: 60, Severities: []string{"critical", "warning"}, Sources: []string{"CPU", "Память", "Storage", "Диски", "ZFS", "UPS", "VM/LXC", "Dashboard"}}
}
func (s *Store) EmailSettings(includePassword bool) EmailSettings {
settings := defaultEmailSettings()
var value string
if s.db.QueryRow(`SELECT value FROM settings WHERE key='email_notifications'`).Scan(&value) == nil {
_ = json.Unmarshal([]byte(value), &settings)
}
settings.HasPassword = settings.Password != ""
if !includePassword {
settings.Password = ""
}
return settings
}
func (s *Store) SaveEmailSettings(settings EmailSettings) error {
if settings.Port < 1 || settings.Port > 65535 || settings.IntervalMinutes < 5 || settings.IntervalMinutes > 10080 {
return fmt.Errorf("проверьте порт и интервал (от 5 минут до 7 дней)")
}
if settings.Enabled && (settings.Host == "" || settings.From == "" || settings.To == "") {
return fmt.Errorf("для отправки нужны SMTP-сервер, отправитель и получатель")
}
if strings.ContainsAny(settings.Host+settings.From+settings.To+settings.Username, "\r\n") {
return fmt.Errorf("поля почты содержат недопустимые символы")
}
if settings.Password == "" {
settings.Password = s.EmailSettings(true).Password
}
settings.HasPassword = settings.Password != ""
data, _ := json.Marshal(settings)
_, err := s.db.Exec(`INSERT INTO settings(key,value) VALUES('email_notifications',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value`, string(data))
return err
}
func (s *Store) LastEmailAt() time.Time {
var value string
if s.db.QueryRow(`SELECT value FROM settings WHERE key='email_last_sent'`).Scan(&value) != nil {
return time.Time{}
}
result, _ := time.Parse(time.RFC3339, value)
return result
}
func (s *Store) SetLastEmailAt(value time.Time) {
_, _ = s.db.Exec(`INSERT INTO settings(key,value) VALUES('email_last_sent',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value`, value.Format(time.RFC3339))
}
func validateThresholds(t AlertThresholds) error {
percentages := []float64{t.CPUWarning, t.CPUCritical, t.MemoryWarning, t.MemoryCritical, t.SwapWarning, t.StorageWarning, t.StorageCritical, t.ZFSWarning, t.ZFSCritical, t.UPSChargeCritical}
for _, value := range percentages {