Email: add reply-to and direct attachment (#18715)
* Add support for `Reply-To` header * Allow direct attachment Don't have tests yet, but they will follow
This commit is contained in:
@@ -5,15 +5,25 @@ import "errors"
|
|||||||
var ErrInvalidEmailCode = errors.New("Invalid or expired email code")
|
var ErrInvalidEmailCode = errors.New("Invalid or expired email code")
|
||||||
var ErrSmtpNotEnabled = errors.New("SMTP not configured, check your grafana.ini config file's [smtp] section")
|
var ErrSmtpNotEnabled = errors.New("SMTP not configured, check your grafana.ini config file's [smtp] section")
|
||||||
|
|
||||||
type SendEmailCommand struct {
|
// SendEmailAttachFile is a definition of the attached files without path
|
||||||
To []string
|
type SendEmailAttachFile struct {
|
||||||
Template string
|
Name string
|
||||||
Subject string
|
Content []byte
|
||||||
Data map[string]interface{}
|
|
||||||
Info string
|
|
||||||
EmbededFiles []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendEmailCommand is command for sending emails
|
||||||
|
type SendEmailCommand struct {
|
||||||
|
To []string
|
||||||
|
Template string
|
||||||
|
Subject string
|
||||||
|
Data map[string]interface{}
|
||||||
|
Info string
|
||||||
|
ReplyTo []string
|
||||||
|
EmbededFiles []string
|
||||||
|
AttachedFiles []*SendEmailAttachFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendEmailCommandSync is command for sending emails in sync
|
||||||
type SendEmailCommandSync struct {
|
type SendEmailCommandSync struct {
|
||||||
SendEmailCommand
|
SendEmailCommand
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,22 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AttachedFile is struct representating email attached files
|
||||||
|
type AttachedFile struct {
|
||||||
|
Name string
|
||||||
|
Content []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message is representation of the email message
|
||||||
type Message struct {
|
type Message struct {
|
||||||
To []string
|
To []string
|
||||||
From string
|
From string
|
||||||
Subject string
|
Subject string
|
||||||
Body string
|
Body string
|
||||||
Info string
|
Info string
|
||||||
EmbededFiles []string
|
ReplyTo []string
|
||||||
|
EmbededFiles []string
|
||||||
|
AttachedFiles []*AttachedFile
|
||||||
}
|
}
|
||||||
|
|
||||||
func setDefaultTemplateData(data map[string]interface{}, u *m.User) {
|
func setDefaultTemplateData(data map[string]interface{}, u *m.User) {
|
||||||
|
|||||||
@@ -9,13 +9,15 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
gomail "gopkg.in/mail.v2"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
gomail "gopkg.in/mail.v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ns *NotificationService) send(msg *Message) (int, error) {
|
func (ns *NotificationService) send(msg *Message) (int, error) {
|
||||||
@@ -30,8 +32,11 @@ func (ns *NotificationService) send(msg *Message) (int, error) {
|
|||||||
m.SetHeader("From", msg.From)
|
m.SetHeader("From", msg.From)
|
||||||
m.SetHeader("To", address)
|
m.SetHeader("To", address)
|
||||||
m.SetHeader("Subject", msg.Subject)
|
m.SetHeader("Subject", msg.Subject)
|
||||||
for _, file := range msg.EmbededFiles {
|
|
||||||
m.Embed(file)
|
ns.setFiles(m, msg)
|
||||||
|
|
||||||
|
for _, replyTo := range msg.ReplyTo {
|
||||||
|
m.SetAddressHeader("Reply-To", replyTo, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.SetBody("text/html", msg.Body)
|
m.SetBody("text/html", msg.Body)
|
||||||
@@ -48,6 +53,23 @@ func (ns *NotificationService) send(msg *Message) (int, error) {
|
|||||||
return num, err
|
return num, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setFiles attaches files in various forms
|
||||||
|
func (ns *NotificationService) setFiles(
|
||||||
|
m *gomail.Message,
|
||||||
|
msg *Message,
|
||||||
|
) {
|
||||||
|
for _, file := range msg.EmbededFiles {
|
||||||
|
m.Embed(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range msg.AttachedFiles {
|
||||||
|
m.Attach(file.Name, gomail.SetCopyFunc(func(writer io.Writer) error {
|
||||||
|
_, err := writer.Write(file.Content)
|
||||||
|
return err
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (ns *NotificationService) createDialer() (*gomail.Dialer, error) {
|
func (ns *NotificationService) createDialer() (*gomail.Dialer, error) {
|
||||||
host, port, err := net.SplitHostPort(ns.Cfg.Smtp.Host)
|
host, port, err := net.SplitHostPort(ns.Cfg.Smtp.Host)
|
||||||
|
|
||||||
@@ -127,10 +149,27 @@ func (ns *NotificationService) buildEmailMessage(cmd *models.SendEmailCommand) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &Message{
|
return &Message{
|
||||||
To: cmd.To,
|
To: cmd.To,
|
||||||
From: fmt.Sprintf("%s <%s>", ns.Cfg.Smtp.FromName, ns.Cfg.Smtp.FromAddress),
|
From: fmt.Sprintf("%s <%s>", ns.Cfg.Smtp.FromName, ns.Cfg.Smtp.FromAddress),
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
Body: buffer.String(),
|
Body: buffer.String(),
|
||||||
EmbededFiles: cmd.EmbededFiles,
|
EmbededFiles: cmd.EmbededFiles,
|
||||||
|
AttachedFiles: buildAttachedFiles(cmd.AttachedFiles),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildAttachedFiles build attached files
|
||||||
|
func buildAttachedFiles(
|
||||||
|
attached []*models.SendEmailAttachFile,
|
||||||
|
) []*AttachedFile {
|
||||||
|
result := make([]*AttachedFile, 0)
|
||||||
|
|
||||||
|
for _, file := range attached {
|
||||||
|
result = append(result, &AttachedFile{
|
||||||
|
Name: file.Name,
|
||||||
|
Content: file.Content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user