Add configurable email notification digests
This commit is contained in:
130
email.go
Normal file
130
email.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/smtp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func filterEmailAlerts(alerts []Alert, settings EmailSettings) []Alert {
|
||||
allowedSeverity, allowedSource := map[string]bool{}, map[string]bool{}
|
||||
for _, value := range settings.Severities {
|
||||
allowedSeverity[value] = true
|
||||
}
|
||||
for _, value := range settings.Sources {
|
||||
allowedSource[value] = true
|
||||
}
|
||||
var result []Alert
|
||||
for _, alert := range alerts {
|
||||
if allowedSeverity[alert.Severity] && allowedSource[alert.Source] {
|
||||
result = append(result, alert)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func sendAlertEmail(settings EmailSettings, alerts []Alert, test bool) error {
|
||||
subject := "Proxmox Dashboard: сводка уведомлений"
|
||||
body := "Система не обнаружила выбранных активных событий."
|
||||
if test {
|
||||
subject = "Proxmox Dashboard: тест почты"
|
||||
body = "Почтовые уведомления настроены правильно."
|
||||
}
|
||||
if len(alerts) > 0 {
|
||||
groups := map[string][]Alert{}
|
||||
for _, alert := range alerts {
|
||||
groups[alert.Severity] = append(groups[alert.Severity], alert)
|
||||
}
|
||||
var lines []string
|
||||
for _, severity := range []string{"critical", "warning", "info"} {
|
||||
items := groups[severity]
|
||||
if len(items) == 0 {
|
||||
continue
|
||||
}
|
||||
title := map[string]string{"critical": "КРИТИЧЕСКИЕ", "warning": "ПРЕДУПРЕЖДЕНИЯ", "info": "ИНФОРМАЦИЯ"}[severity]
|
||||
lines = append(lines, title)
|
||||
sort.Slice(items, func(i, j int) bool { return items[i].Title < items[j].Title })
|
||||
for _, item := range items {
|
||||
lines = append(lines, fmt.Sprintf("• [%s] %s\n %s", item.Source, item.Title, item.Message))
|
||||
}
|
||||
}
|
||||
body = strings.Join(lines, "\n\n")
|
||||
subject = fmt.Sprintf("Proxmox Dashboard: событий — %d", len(alerts))
|
||||
}
|
||||
message := []byte("From: " + settings.From + "\r\nTo: " + settings.To + "\r\nSubject: " + subject + "\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\nContent-Transfer-Encoding: 8bit\r\n\r\n" + body + "\r\n")
|
||||
address := net.JoinHostPort(settings.Host, fmt.Sprint(settings.Port))
|
||||
var client *smtp.Client
|
||||
if settings.Port == 465 {
|
||||
connection, err := tls.Dial("tcp", address, &tls.Config{ServerName: settings.Host, MinVersion: tls.VersionTLS12})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err = smtp.NewClient(connection, settings.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
client, err = smtp.Dial(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok, _ := client.Extension("STARTTLS"); ok {
|
||||
if err = client.StartTLS(&tls.Config{ServerName: settings.Host, MinVersion: tls.VersionTLS12}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
defer client.Close()
|
||||
if settings.Username != "" {
|
||||
if err := client.Auth(smtp.PlainAuth("", settings.Username, settings.Password, settings.Host)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := client.Mail(settings.From); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := client.Rcpt(settings.To); err != nil {
|
||||
return err
|
||||
}
|
||||
writer, err := client.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = writer.Write(message); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = writer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return client.Quit()
|
||||
}
|
||||
|
||||
func runEmailNotifier(collect func() (dashboardMetrics, error), store *Store) {
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
settings := store.EmailSettings(true)
|
||||
if !settings.Enabled || time.Since(store.LastEmailAt()) < time.Duration(settings.IntervalMinutes)*time.Minute {
|
||||
continue
|
||||
}
|
||||
metrics, err := collect()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
alerts := filterEmailAlerts(metrics.Alerts, settings)
|
||||
if len(alerts) == 0 {
|
||||
continue
|
||||
}
|
||||
if err := sendAlertEmail(settings, alerts, false); err != nil {
|
||||
log.Printf("Не удалось отправить почтовую сводку: %v", err)
|
||||
continue
|
||||
}
|
||||
store.SetLastEmailAt(time.Now())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user