68 lines
1.5 KiB
Go
68 lines
1.5 KiB
Go
package email
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/smtp"
|
|
|
|
"github.com/tim/cairnquire/apps/server/internal/config"
|
|
)
|
|
|
|
type Sender interface {
|
|
Send(ctx context.Context, to []string, subject, body string) error
|
|
}
|
|
|
|
type SMTPConfig struct {
|
|
Host string
|
|
Port int
|
|
From string
|
|
}
|
|
|
|
type SMTPSender struct {
|
|
cfg SMTPConfig
|
|
logger *slog.Logger
|
|
}
|
|
|
|
func NewSMTPSender(cfg config.EmailConfig, logger *slog.Logger) *SMTPSender {
|
|
return &SMTPSender{
|
|
cfg: SMTPConfig{
|
|
Host: cfg.SMTPHost,
|
|
Port: cfg.SMTPPort,
|
|
From: cfg.From,
|
|
},
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (s *SMTPSender) Send(ctx context.Context, to []string, subject, body string) error {
|
|
if s.cfg.Host == "" {
|
|
return fmt.Errorf("email host not configured")
|
|
}
|
|
addr := fmt.Sprintf("%s:%d", s.cfg.Host, s.cfg.Port)
|
|
msg := []byte(fmt.Sprintf("To: %s\r\nSubject: %s\r\n\r\n%s\r\n", to[0], subject, body))
|
|
|
|
s.logger.Info("sending email", "to", to, "subject", subject, "addr", addr)
|
|
|
|
// Use SMTP without auth for local testing (Mailpit/MailHog)
|
|
var auth smtp.Auth
|
|
err := smtp.SendMail(addr, auth, s.cfg.From, to, msg)
|
|
if err != nil {
|
|
return fmt.Errorf("send email: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type NoOpSender struct {
|
|
logger *slog.Logger
|
|
}
|
|
|
|
func NewNoOpSender(logger *slog.Logger) *NoOpSender {
|
|
return &NoOpSender{logger: logger}
|
|
}
|
|
|
|
func (n *NoOpSender) Send(ctx context.Context, to []string, subject, body string) error {
|
|
n.logger.Info("email noop", "to", to, "subject", subject, "body_len", len(body))
|
|
return nil
|
|
}
|