Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 69 additions & 21 deletions internal/mailer/mailer.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mailer

import (
"context"
"fmt"
"net/http"
"net/url"
Expand Down Expand Up @@ -39,34 +40,81 @@ type EmailData struct {

// NewMailer returns a new gotrue mailer
func NewMailer(globalConfig *conf.GlobalConfiguration) Mailer {
from := globalConfig.SMTP.FromAddress()
u, _ := url.ParseRequestURI(globalConfig.API.ExternalURL)
mailClient := NewMailClient(globalConfig)
return &TemplateMailer{
SiteURL: globalConfig.SiteURL,
Config: globalConfig,
MailClient: mailClient,
}
}

type emailValidatorMailClient struct {
ev *EmailValidator
mc MailClient
}

// Mail implements mailer.MailClient interface by calling validate before
// passing the mail request to the next MailClient.
func (o *emailValidatorMailClient) Mail(
ctx context.Context,
to string,
subjectTemplate string,
templateURL string,
defaultTemplate string,
templateData map[string]any,
headers map[string][]string,
typ string,
) error {
if err := o.ev.Validate(ctx, to); err != nil {
return err
}
return o.mc.Mail(
ctx,
to,
subjectTemplate,
templateURL,
defaultTemplate,
templateData,
headers,
typ,
)
}

var mailClient MailClient
// NewMailerWithClient returns a new Mailer that will use the given MailClient.
func NewMailerWithClient(
globalConfig *conf.GlobalConfiguration,
mailClient MailClient,
) Mailer {
ev := newEmailValidator(globalConfig.Mailer)
mr := &emailValidatorMailClient{ev: ev, mc: mailClient}
return &TemplateMailer{
SiteURL: globalConfig.SiteURL,
Config: globalConfig,
MailClient: mr,
}
}

// NewMailClient returns a new MailClient based on the given configuration.
func NewMailClient(globalConfig *conf.GlobalConfiguration) MailClient {
if globalConfig.SMTP.Host == "" {
logrus.Infof("Noop mail client being used for %v", globalConfig.SiteURL)
mailClient = &noopMailClient{
EmailValidator: newEmailValidator(globalConfig.Mailer),
}
} else {
mailClient = &MailmeMailer{
Host: globalConfig.SMTP.Host,
Port: globalConfig.SMTP.Port,
User: globalConfig.SMTP.User,
Pass: globalConfig.SMTP.Pass,
LocalName: u.Hostname(),
From: from,
BaseURL: globalConfig.SiteURL,
Logger: logrus.StandardLogger(),
MailLogging: globalConfig.SMTP.LoggingEnabled,
return &noopMailClient{
EmailValidator: newEmailValidator(globalConfig.Mailer),
}
}

return &TemplateMailer{
SiteURL: globalConfig.SiteURL,
Config: globalConfig,
Mailer: mailClient,
from := globalConfig.SMTP.FromAddress()
u, _ := url.ParseRequestURI(globalConfig.API.ExternalURL)
return &MailmeMailer{
Host: globalConfig.SMTP.Host,
Port: globalConfig.SMTP.Port,
User: globalConfig.SMTP.User,
Pass: globalConfig.SMTP.Pass,
LocalName: u.Hostname(),
From: from,
BaseURL: globalConfig.SiteURL,
Logger: logrus.StandardLogger(),
MailLogging: globalConfig.SMTP.LoggingEnabled,
}
}

Expand Down
29 changes: 11 additions & 18 deletions internal/mailer/mailme.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,17 @@ const TemplateExpiration = 10 * time.Second

// MailmeMailer lets MailMe send templated mails
type MailmeMailer struct {
From string
Host string
Port int
User string
Pass string
BaseURL string
LocalName string
FuncMap template.FuncMap
cache *TemplateCache
Logger logrus.FieldLogger
MailLogging bool
EmailValidator *EmailValidator
From string
Host string
Port int
User string
Pass string
BaseURL string
LocalName string
FuncMap template.FuncMap
cache *TemplateCache
Logger logrus.FieldLogger
MailLogging bool
}

// Mail sends a templated mail. It will try to load the template from a URL, and
Expand All @@ -59,12 +58,6 @@ func (m *MailmeMailer) Mail(
}
}

if m.EmailValidator != nil {
if err := m.EmailValidator.Validate(ctx, to); err != nil {
return err
}
}

tmp, err := template.New("Subject").Funcs(template.FuncMap(m.FuncMap)).Parse(subjectTemplate)
if err != nil {
return err
Expand Down
11 changes: 11 additions & 0 deletions internal/mailer/noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package mailer
import (
"context"
"errors"
"time"
)

type noopMailClient struct {
EmailValidator *EmailValidator
Delay time.Duration
}

func (m *noopMailClient) Mail(
Expand All @@ -19,6 +21,15 @@ func (m *noopMailClient) Mail(
if to == "" {
return errors.New("to field cannot be empty")
}

if m.Delay > 0 {
select {
case <-time.After(m.Delay):
case <-ctx.Done():
return ctx.Err()
}
}

if m.EmailValidator != nil {
if err := m.EmailValidator.Validate(ctx, to); err != nil {
return err
Expand Down
18 changes: 9 additions & 9 deletions internal/mailer/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ type MailClient interface {

// TemplateMailer will send mail and use templates from the site for easy mail styling
type TemplateMailer struct {
SiteURL string
Config *conf.GlobalConfiguration
Mailer MailClient
SiteURL string
Config *conf.GlobalConfiguration
MailClient MailClient
}

func encodeRedirectURL(referrerURL string) string {
Expand Down Expand Up @@ -157,7 +157,7 @@ func (m *TemplateMailer) InviteMail(r *http.Request, user *models.User, otp, ref
"RedirectTo": referrerURL,
}

return m.Mailer.Mail(
return m.MailClient.Mail(
r.Context(),
user.GetEmail(),
withDefault(m.Config.Mailer.Subjects.Invite, "You have been invited"),
Expand Down Expand Up @@ -190,7 +190,7 @@ func (m *TemplateMailer) ConfirmationMail(r *http.Request, user *models.User, ot
"RedirectTo": referrerURL,
}

return m.Mailer.Mail(
return m.MailClient.Mail(
r.Context(),
user.GetEmail(),
withDefault(m.Config.Mailer.Subjects.Confirmation, "Confirm Your Email"),
Expand All @@ -211,7 +211,7 @@ func (m *TemplateMailer) ReauthenticateMail(r *http.Request, user *models.User,
"Data": user.UserMetaData,
}

return m.Mailer.Mail(
return m.MailClient.Mail(
r.Context(),
user.GetEmail(),
withDefault(m.Config.Mailer.Subjects.Reauthentication, "Confirm reauthentication"),
Expand Down Expand Up @@ -281,7 +281,7 @@ func (m *TemplateMailer) EmailChangeMail(r *http.Request, user *models.User, otp
"Data": user.UserMetaData,
"RedirectTo": referrerURL,
}
errors <- m.Mailer.Mail(
errors <- m.MailClient.Mail(
ctx,
address,
withDefault(m.Config.Mailer.Subjects.EmailChange, "Confirm Email Change"),
Expand Down Expand Up @@ -323,7 +323,7 @@ func (m *TemplateMailer) RecoveryMail(r *http.Request, user *models.User, otp, r
"RedirectTo": referrerURL,
}

return m.Mailer.Mail(
return m.MailClient.Mail(
r.Context(),
user.GetEmail(),
withDefault(m.Config.Mailer.Subjects.Recovery, "Reset Your Password"),
Expand Down Expand Up @@ -356,7 +356,7 @@ func (m *TemplateMailer) MagicLinkMail(r *http.Request, user *models.User, otp,
"RedirectTo": referrerURL,
}

return m.Mailer.Mail(
return m.MailClient.Mail(
r.Context(),
user.GetEmail(),
withDefault(m.Config.Mailer.Subjects.MagicLink, "Your Magic Link"),
Expand Down