Add acknowledged alert history and HTML email digests
This commit is contained in:
33
email.go
33
email.go
@@ -3,7 +3,9 @@ package main
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"html"
|
||||
"log"
|
||||
"mime"
|
||||
"net"
|
||||
"net/smtp"
|
||||
"sort"
|
||||
@@ -30,33 +32,36 @@ func filterEmailAlerts(alerts []Alert, settings EmailSettings) []Alert {
|
||||
|
||||
func sendAlertEmail(settings EmailSettings, alerts []Alert, test bool) error {
|
||||
subject := "Proxmox Dashboard: сводка уведомлений"
|
||||
body := "Система не обнаружила выбранных активных событий."
|
||||
body := `<div style="padding:32px;background:#0b1117;color:#edf3f7;font-family:Arial,sans-serif"><div style="max-width:680px;margin:auto"><div style="color:#55d58a;font-size:12px;letter-spacing:2px">PROXMOX DASHBOARD</div><h1 style="margin:10px 0 24px">Сводка событий</h1>`
|
||||
if test {
|
||||
subject = "Proxmox Dashboard: тест почты"
|
||||
body = "Почтовые уведомления настроены правильно."
|
||||
body += `<div style="padding:22px;border:1px solid #2b3944;border-radius:14px;background:#111a22"><b style="font-size:18px">Почта настроена</b><p style="color:#9aabb7;margin-bottom:0">Тестовое сообщение успешно отправлено вашим сервером.</p></div>`
|
||||
}
|
||||
if len(alerts) > 0 {
|
||||
groups := map[string][]Alert{}
|
||||
for _, alert := range alerts {
|
||||
groups[alert.Severity] = append(groups[alert.Severity], alert)
|
||||
}
|
||||
var lines []string
|
||||
var sections []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)
|
||||
title := map[string]string{"critical": "Критические", "warning": "Предупреждения", "info": "Информация"}[severity]
|
||||
color := map[string]string{"critical": "#ff665c", "warning": "#ffb35c", "info": "#6ba8ff"}[severity]
|
||||
sort.Slice(items, func(i, j int) bool { return items[i].Title < items[j].Title })
|
||||
cards := ""
|
||||
for _, item := range items {
|
||||
lines = append(lines, fmt.Sprintf("• [%s] %s\n %s", item.Source, item.Title, item.Message))
|
||||
cards += fmt.Sprintf(`<div style="margin:10px 0;padding:16px 18px;border:1px solid #2b3944;border-left:4px solid %s;border-radius:10px;background:#111a22"><div style="color:#8fa0ac;font-size:11px;text-transform:uppercase">%s</div><div style="font-weight:bold;font-size:16px;margin:5px 0">%s</div><div style="color:#aab7c0;font-size:14px">%s</div></div>`, color, html.EscapeString(item.Source), html.EscapeString(item.Title), html.EscapeString(item.Message))
|
||||
}
|
||||
sections = append(sections, fmt.Sprintf(`<h2 style="font-size:14px;color:%s;margin:24px 0 8px">%s · %d</h2>%s`, color, title, len(items), cards))
|
||||
}
|
||||
body = strings.Join(lines, "\n\n")
|
||||
body += strings.Join(sections, "")
|
||||
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")
|
||||
body += `<div style="color:#687985;font-size:12px;margin-top:28px">События собраны напрямую с Proxmox-хоста. Подтверждённые события повторно не отправляются.</div></div></div>`
|
||||
message := []byte("From: " + settings.From + "\r\nTo: " + settings.To + "\r\nSubject: " + mime.QEncoding.Encode("UTF-8", subject) + "\r\nMIME-Version: 1.0\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Transfer-Encoding: 8bit\r\n\r\n" + body + "\r\n")
|
||||
address := net.JoinHostPort(settings.Host, fmt.Sprint(settings.Port))
|
||||
tlsConfig := &tls.Config{ServerName: settings.Host, MinVersion: tls.VersionTLS12, InsecureSkipVerify: settings.SkipTLSVerify} //nolint:gosec -- explicit private-network option
|
||||
var client *smtp.Client
|
||||
@@ -114,11 +119,15 @@ func runEmailNotifier(collect func() (dashboardMetrics, error), store *Store) {
|
||||
if !settings.Enabled || time.Since(store.LastEmailAt()) < time.Duration(settings.IntervalMinutes)*time.Minute {
|
||||
continue
|
||||
}
|
||||
metrics, err := collect()
|
||||
_, err := collect()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
alerts := filterEmailAlerts(metrics.Alerts, settings)
|
||||
pending, err := store.PendingEmailAlerts()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
alerts := filterEmailAlerts(pending, settings)
|
||||
if len(alerts) == 0 {
|
||||
continue
|
||||
}
|
||||
@@ -126,6 +135,10 @@ func runEmailNotifier(collect func() (dashboardMetrics, error), store *Store) {
|
||||
log.Printf("Не удалось отправить почтовую сводку: %v", err)
|
||||
continue
|
||||
}
|
||||
if err := store.MarkAlertsEmailed(alerts); err != nil {
|
||||
log.Printf("Не удалось отметить отправленные события: %v", err)
|
||||
continue
|
||||
}
|
||||
store.SetLastEmailAt(time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user