1package channels
2
3import (
4	"context"
5	"net/url"
6	"path"
7
8	"github.com/prometheus/alertmanager/template"
9	"github.com/prometheus/alertmanager/types"
10
11	"github.com/grafana/grafana/pkg/bus"
12	"github.com/grafana/grafana/pkg/infra/log"
13	"github.com/grafana/grafana/pkg/models"
14	"github.com/grafana/grafana/pkg/util"
15)
16
17// EmailNotifier is responsible for sending
18// alert notifications over email.
19type EmailNotifier struct {
20	*Base
21	Addresses   []string
22	SingleEmail bool
23	Message     string
24	log         log.Logger
25	tmpl        *template.Template
26}
27
28// NewEmailNotifier is the constructor function
29// for the EmailNotifier.
30func NewEmailNotifier(model *NotificationChannelConfig, t *template.Template) (*EmailNotifier, error) {
31	if model.Settings == nil {
32		return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
33	}
34
35	addressesString := model.Settings.Get("addresses").MustString()
36	singleEmail := model.Settings.Get("singleEmail").MustBool(false)
37
38	if addressesString == "" {
39		return nil, receiverInitError{Reason: "could not find addresses in settings", Cfg: *model}
40	}
41
42	// split addresses with a few different ways
43	addresses := util.SplitEmails(addressesString)
44
45	return &EmailNotifier{
46		Base: NewBase(&models.AlertNotification{
47			Uid:                   model.UID,
48			Name:                  model.Name,
49			Type:                  model.Type,
50			DisableResolveMessage: model.DisableResolveMessage,
51			Settings:              model.Settings,
52		}),
53		Addresses:   addresses,
54		SingleEmail: singleEmail,
55		Message:     model.Settings.Get("message").MustString(),
56		log:         log.New("alerting.notifier.email"),
57		tmpl:        t,
58	}, nil
59}
60
61// Notify sends the alert notification.
62func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
63	var tmplErr error
64	tmpl, data := TmplText(ctx, en.tmpl, as, en.log, &tmplErr)
65
66	title := tmpl(DefaultMessageTitleEmbed)
67
68	alertPageURL := en.tmpl.ExternalURL.String()
69	ruleURL := en.tmpl.ExternalURL.String()
70	u, err := url.Parse(en.tmpl.ExternalURL.String())
71	if err == nil {
72		basePath := u.Path
73		u.Path = path.Join(basePath, "/alerting/list")
74		ruleURL = u.String()
75		u.RawQuery = "alertState=firing&view=state"
76		alertPageURL = u.String()
77	} else {
78		en.log.Debug("failed to parse external URL", "url", en.tmpl.ExternalURL.String(), "err", err.Error())
79	}
80
81	cmd := &models.SendEmailCommandSync{
82		SendEmailCommand: models.SendEmailCommand{
83			Subject: title,
84			Data: map[string]interface{}{
85				"Title":             title,
86				"Message":           tmpl(en.Message),
87				"Status":            data.Status,
88				"Alerts":            data.Alerts,
89				"GroupLabels":       data.GroupLabels,
90				"CommonLabels":      data.CommonLabels,
91				"CommonAnnotations": data.CommonAnnotations,
92				"ExternalURL":       data.ExternalURL,
93				"RuleUrl":           ruleURL,
94				"AlertPageUrl":      alertPageURL,
95			},
96			To:          en.Addresses,
97			SingleEmail: en.SingleEmail,
98			Template:    "ng_alert_notification",
99		},
100	}
101
102	if tmplErr != nil {
103		en.log.Warn("failed to template email message", "err", tmplErr.Error())
104	}
105
106	if err := bus.DispatchCtx(ctx, cmd); err != nil {
107		return false, err
108	}
109
110	return true, nil
111}
112
113func (en *EmailNotifier) SendResolved() bool {
114	return !en.GetDisableResolveMessage()
115}
116