[#1069] added default Message-ID and more options to customize the mail message

This commit is contained in:
Gani Georgiev
2022-11-21 14:53:05 +02:00
parent c4a660d2d2
commit 3e1a19685b
11 changed files with 186 additions and 157 deletions
+15 -8
View File
@@ -5,14 +5,21 @@ import (
"net/mail"
)
// Message defines a generic email message struct.
type Message struct {
From mail.Address
To mail.Address
Bcc []string
Cc []string
Subject string
HTML string
Text string
Headers map[string]string
Attachments map[string]io.Reader
}
// Mailer defines a base mail client interface.
type Mailer interface {
// Send sends an email with HTML body to the specified recipient.
Send(
fromEmail mail.Address,
toEmail mail.Address,
subject string,
htmlContent string,
attachments map[string]io.Reader,
) error
// Send sends an email with the provided Message.
Send(message *Message) error
}
+13 -17
View File
@@ -3,10 +3,8 @@ package mailer
import (
"bytes"
"errors"
"io"
"mime"
"net/http"
"net/mail"
"os/exec"
)
@@ -20,19 +18,11 @@ type Sendmail struct {
}
// Send implements `mailer.Mailer` interface.
//
// Attachments are currently not supported.
func (m *Sendmail) Send(
fromEmail mail.Address,
toEmail mail.Address,
subject string,
htmlContent string,
attachments map[string]io.Reader,
) error {
func (c *Sendmail) Send(m *Message) error {
headers := make(http.Header)
headers.Set("Subject", mime.QEncoding.Encode("utf-8", subject))
headers.Set("From", fromEmail.String())
headers.Set("To", toEmail.String())
headers.Set("Subject", mime.QEncoding.Encode("utf-8", m.Subject))
headers.Set("From", m.From.String())
headers.Set("To", m.To.String())
headers.Set("Content-Type", "text/html; charset=UTF-8")
cmdPath, err := findSendmailPath()
@@ -50,12 +40,18 @@ func (m *Sendmail) Send(
if _, err := buffer.Write([]byte("\r\n")); err != nil {
return err
}
if _, err := buffer.Write([]byte(htmlContent)); err != nil {
return err
if m.HTML != "" {
if _, err := buffer.Write([]byte(m.HTML)); err != nil {
return err
}
} else {
if _, err := buffer.Write([]byte(m.Text)); err != nil {
return err
}
}
// ---
sendmail := exec.Command(cmdPath, toEmail.Address)
sendmail := exec.Command(cmdPath, m.To.Address)
sendmail.Stdin = &buffer
return sendmail.Run()
+50 -24
View File
@@ -2,11 +2,11 @@ package mailer
import (
"fmt"
"io"
"net/mail"
"net/smtp"
"strings"
"github.com/domodwyer/mailyak/v3"
"github.com/pocketbase/pocketbase/tools/security"
)
var _ Mailer = (*SmtpClient)(nil)
@@ -39,46 +39,72 @@ type SmtpClient struct {
}
// Send implements `mailer.Mailer` interface.
func (m *SmtpClient) Send(
fromEmail mail.Address,
toEmail mail.Address,
subject string,
htmlContent string,
attachments map[string]io.Reader,
) error {
func (c *SmtpClient) Send(m *Message) error {
var smtpAuth smtp.Auth
if m.username != "" || m.password != "" {
smtpAuth = smtp.PlainAuth("", m.username, m.password, m.host)
if c.username != "" || c.password != "" {
smtpAuth = smtp.PlainAuth("", c.username, c.password, c.host)
}
// create mail instance
var yak *mailyak.MailYak
if m.tls {
if c.tls {
var tlsErr error
yak, tlsErr = mailyak.NewWithTLS(fmt.Sprintf("%s:%d", m.host, m.port), smtpAuth, nil)
yak, tlsErr = mailyak.NewWithTLS(fmt.Sprintf("%s:%d", c.host, c.port), smtpAuth, nil)
if tlsErr != nil {
return tlsErr
}
} else {
yak = mailyak.New(fmt.Sprintf("%s:%d", m.host, m.port), smtpAuth)
yak = mailyak.New(fmt.Sprintf("%s:%d", c.host, c.port), smtpAuth)
}
if fromEmail.Name != "" {
yak.FromName(fromEmail.Name)
if m.From.Name != "" {
yak.FromName(m.From.Name)
}
yak.From(fromEmail.Address)
yak.To(toEmail.Address)
yak.Subject(subject)
yak.HTML().Set(htmlContent)
yak.From(m.From.Address)
yak.To(m.To.Address)
yak.Subject(m.Subject)
yak.HTML().Set(m.HTML)
// try to generate a plain text version of the HTML
if plain, err := html2Text(htmlContent); err == nil {
yak.Plain().Set(plain)
if m.Text == "" {
// try to generate a plain text version of the HTML
if plain, err := html2Text(m.HTML); err == nil {
yak.Plain().Set(plain)
}
} else {
yak.Plain().Set(m.Text)
}
for name, data := range attachments {
if len(m.Bcc) > 0 {
yak.Bcc(m.Bcc...)
}
if len(m.Cc) > 0 {
yak.Cc(m.Cc...)
}
// add attachements (if any)
for name, data := range m.Attachments {
yak.Attach(name, data)
}
// add custom headers (if any)
var hasMessageId bool
for k, v := range m.Headers {
if strings.EqualFold(k, "Message-ID") {
hasMessageId = true
}
yak.AddHeader(k, v)
}
if !hasMessageId {
// add a default message id if missing
fromParts := strings.Split(m.From.Address, "@")
if len(fromParts) == 2 {
yak.AddHeader("Message-ID", fmt.Sprintf("<%s@%s>",
security.PseudorandomString(15),
fromParts[1],
))
}
}
return yak.Send()
}