udemy-go-microservices/mail-service/cmd/api/mailer.go
2024-09-03 21:09:34 +02:00

169 lines
3.2 KiB
Go

package main
import (
"bytes"
"embed"
"html/template"
"log"
"time"
"github.com/vanng822/go-premailer/premailer"
mail "github.com/xhit/go-simple-mail/v2"
)
//go:embed templates
var emailTemplateFS embed.FS
type Mail struct {
Domain string
Host string
Port int
Username string
Password string
Encryption string
FromAddr string
FromName string
}
type Message struct {
From string
FromName string
To string
Subject string
Attachments []string
Data any
DataMap map[string]any
}
func (m *Mail) SendSMTPMessage(msg Message) error {
if msg.From == "" {
msg.From = m.FromAddr
}
if msg.FromName == "" {
msg.FromName = m.FromName
}
data := map[string]any{
"message": msg.Data,
}
msg.DataMap = data
formattedMessage, err := m.buildHTMLMessage(msg)
if err != nil {
return err
}
plainMessage, err := m.buildPlainTextMessage(msg)
if err != nil {
return err
}
server := mail.NewSMTPClient()
server.Host = m.Host
server.Port = m.Port
server.Username = m.Username
server.Password = m.Password
server.Encryption = m.getEncryption(m.Encryption)
server.KeepAlive = false
server.ConnectTimeout = 10 * time.Second
server.SendTimeout = 10 * time.Second
log.Println(server.Host, server.Port)
smtpClient, err := server.Connect()
if err != nil {
return err
}
email := mail.NewMSG()
email.SetFrom(msg.From).AddTo(msg.To).SetSubject(msg.Subject)
email.SetBody(mail.TextPlain, plainMessage)
email.AddAlternative(mail.TextHTML, formattedMessage)
if len(msg.Attachments) > 0 {
for _, x := range msg.Attachments {
email.AddAttachment(x)
}
}
err = email.Send(smtpClient)
if err != nil {
return err
}
return nil
}
func (m *Mail) buildPlainTextMessage(msg Message) (string, error) {
templateToRender := "templates/mail.plain.gohtml"
t, err := template.New("email-plain").ParseFS(emailTemplateFS, templateToRender)
if err != nil {
return "", err
}
var tpl bytes.Buffer
if err = t.ExecuteTemplate(&tpl, "body", msg.DataMap); err != nil {
return "", err
}
formattedMessage := tpl.String()
return formattedMessage, nil
}
func (m *Mail) buildHTMLMessage(msg Message) (string, error) {
templateToRender := "templates/mail.html.gohtml"
t, err := template.New("email-html").ParseFS(emailTemplateFS, templateToRender)
if err != nil {
return "", err
}
var tpl bytes.Buffer
if err = t.ExecuteTemplate(&tpl, "body", msg.DataMap); err != nil {
return "", err
}
formattedMessage := tpl.String()
formattedMessage, err = m.inlineCSS(formattedMessage)
if err != nil {
return "", err
}
return formattedMessage, nil
}
func (m *Mail) inlineCSS(s string) (string, error) {
options := premailer.Options{
RemoveClasses: false,
CssToAttributes: false,
KeepBangImportant: true,
}
prem, err := premailer.NewPremailerFromString(s, &options)
if err != nil {
return "", err
}
html, err := prem.Transform()
if err != nil {
return "", err
}
return html, nil
}
func (m *Mail) getEncryption(s string) mail.Encryption {
switch s {
case "tls":
return mail.EncryptionSTARTTLS
case "ssl":
return mail.EncryptionSSLTLS
case "none":
return mail.EncryptionNone
default:
return mail.EncryptionNone
}
}