2021-12-19 13:22:55 -08:00
|
|
|
package services
|
|
|
|
|
|
|
|
import (
|
2022-01-19 06:14:18 -08:00
|
|
|
"errors"
|
2021-12-19 17:37:51 -08:00
|
|
|
"fmt"
|
|
|
|
|
2021-12-19 13:22:55 -08:00
|
|
|
"github.com/labstack/echo/v4"
|
2024-07-09 17:57:05 -07:00
|
|
|
|
|
|
|
"git.grosinger.net/tgrosinger/saasitone/config"
|
|
|
|
"git.grosinger.net/tgrosinger/saasitone/pkg/log"
|
2021-12-19 13:22:55 -08:00
|
|
|
)
|
|
|
|
|
2022-01-14 12:42:32 -08:00
|
|
|
type (
|
|
|
|
// MailClient provides a client for sending email
|
|
|
|
// This is purposely not completed because there are many different methods and services
|
|
|
|
// for sending email, many of which are very different. Choose what works best for you
|
|
|
|
// and populate the methods below
|
|
|
|
MailClient struct {
|
|
|
|
// config stores application configuration
|
|
|
|
config *config.Config
|
|
|
|
|
|
|
|
// templates stores the template renderer
|
|
|
|
templates *TemplateRenderer
|
|
|
|
}
|
|
|
|
|
2022-01-19 06:14:18 -08:00
|
|
|
// mail represents an email to be sent
|
2022-01-14 12:42:32 -08:00
|
|
|
mail struct {
|
|
|
|
client *MailClient
|
|
|
|
from string
|
|
|
|
to string
|
|
|
|
subject string
|
|
|
|
body string
|
|
|
|
template string
|
2023-12-16 08:07:20 -08:00
|
|
|
templateData any
|
2022-01-14 12:42:32 -08:00
|
|
|
}
|
|
|
|
)
|
2021-12-19 13:22:55 -08:00
|
|
|
|
|
|
|
// NewMailClient creates a new MailClient
|
2021-12-19 17:37:51 -08:00
|
|
|
func NewMailClient(cfg *config.Config, templates *TemplateRenderer) (*MailClient, error) {
|
2021-12-19 13:22:55 -08:00
|
|
|
return &MailClient{
|
2021-12-19 17:37:51 -08:00
|
|
|
config: cfg,
|
|
|
|
templates: templates,
|
2021-12-19 13:22:55 -08:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2022-01-14 12:42:32 -08:00
|
|
|
// Compose creates a new email
|
|
|
|
func (m *MailClient) Compose() *mail {
|
|
|
|
return &mail{
|
|
|
|
client: m,
|
|
|
|
from: m.config.Mail.FromAddress,
|
2021-12-19 13:22:55 -08:00
|
|
|
}
|
2022-01-14 12:42:32 -08:00
|
|
|
}
|
2021-12-19 13:22:55 -08:00
|
|
|
|
2022-01-14 12:42:32 -08:00
|
|
|
// skipSend determines if mail sending should be skipped
|
|
|
|
func (m *MailClient) skipSend() bool {
|
|
|
|
return m.config.App.Environment != config.EnvProduction
|
|
|
|
}
|
|
|
|
|
2022-01-19 06:14:18 -08:00
|
|
|
// send attempts to send the email
|
|
|
|
func (m *MailClient) send(email *mail, ctx echo.Context) error {
|
|
|
|
switch {
|
|
|
|
case email.to == "":
|
|
|
|
return errors.New("email cannot be sent without a to address")
|
|
|
|
case email.body == "" && email.template == "":
|
|
|
|
return errors.New("email cannot be sent without a body or template")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if a template was supplied
|
|
|
|
if email.template != "" {
|
|
|
|
// Parse and execute template
|
|
|
|
buf, err := m.templates.
|
|
|
|
Parse().
|
|
|
|
Group("mail").
|
|
|
|
Key(email.template).
|
|
|
|
Base(email.template).
|
|
|
|
Files(fmt.Sprintf("emails/%s", email.template)).
|
|
|
|
Execute(email.templateData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
email.body = buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if mail sending should be skipped
|
|
|
|
if m.skipSend() {
|
2024-06-14 18:01:48 -07:00
|
|
|
log.Ctx(ctx).Debug("skipping email delivery",
|
|
|
|
"to", email.to,
|
|
|
|
)
|
2022-01-19 06:14:18 -08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Finish based on your mail sender of choice!
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-14 12:42:32 -08:00
|
|
|
// From sets the email from address
|
|
|
|
func (m *mail) From(from string) *mail {
|
|
|
|
m.from = from
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// To sets the email address this email will be sent to
|
|
|
|
func (m *mail) To(to string) *mail {
|
|
|
|
m.to = to
|
|
|
|
return m
|
2021-12-19 13:22:55 -08:00
|
|
|
}
|
|
|
|
|
2022-01-14 12:42:32 -08:00
|
|
|
// Subject sets the subject line of the email
|
|
|
|
func (m *mail) Subject(subject string) *mail {
|
|
|
|
m.subject = subject
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// Body sets the body of the email
|
|
|
|
// This is not required and will be ignored if a template via Template()
|
|
|
|
func (m *mail) Body(body string) *mail {
|
|
|
|
m.body = body
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// Template sets the template to be used to produce the body of the email
|
2021-12-19 13:22:55 -08:00
|
|
|
// The template name should only include the filename without the extension or directory.
|
2022-01-14 12:42:32 -08:00
|
|
|
// The template must reside within the emails sub-directory.
|
|
|
|
// The funcmap will be automatically added to the template.
|
|
|
|
// Use TemplateData() to supply the data that will be passed in to the template.
|
|
|
|
func (m *mail) Template(template string) *mail {
|
|
|
|
m.template = template
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// TemplateData sets the data that will be passed to the template specified when calling Template()
|
2023-12-16 08:07:20 -08:00
|
|
|
func (m *mail) TemplateData(data any) *mail {
|
2022-01-14 12:42:32 -08:00
|
|
|
m.templateData = data
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send attempts to send the email
|
|
|
|
func (m *mail) Send(ctx echo.Context) error {
|
2022-01-19 06:14:18 -08:00
|
|
|
return m.client.send(m, ctx)
|
2021-12-19 13:22:55 -08:00
|
|
|
}
|