package email import ( "context" "fmt" "github.com/google/uuid" "gopkg.in/gomail.v2" ) // SMTPSettings configures an SMTP Sender (ported from app-cloudep-notification-service). type SMTPSettings struct { Sort int Host string Port int Username string Password string } // SMTPSender delivers email via SMTP (gomail). type SMTPSender struct { name string sort int dialer *gomail.Dialer } // NewSMTPSender builds an SMTP provider. Host and Port are required. func NewSMTPSender(cfg SMTPSettings) (*SMTPSender, error) { if cfg.Host == "" { return nil, fmt.Errorf("email smtp: host is required") } if cfg.Port <= 0 { return nil, fmt.Errorf("email smtp: port must be positive") } return &SMTPSender{ name: "smtp", sort: cfg.Sort, dialer: gomail.NewDialer(cfg.Host, cfg.Port, cfg.Username, cfg.Password), }, nil } func (s *SMTPSender) Name() string { return s.name } func (s *SMTPSender) Sort() int { return s.sort } func (s *SMTPSender) Send(ctx context.Context, msg *Message) (string, error) { if err := ctx.Err(); err != nil { return "", err } if msg == nil || len(msg.To) == 0 { return "", fmt.Errorf("email smtp: message or recipients missing") } from := msg.From if from == "" { return "", fmt.Errorf("email smtp: from address is required") } m := gomail.NewMessage() m.SetHeader("From", from) m.SetHeader("To", msg.To...) m.SetHeader("Subject", msg.Subject) m.SetBody("text/html", msg.Body) // gomail does not accept context; honor cancellation before blocking I/O. if err := ctx.Err(); err != nil { return "", err } if err := s.dialer.DialAndSend(m); err != nil { return "", fmt.Errorf("email smtp: %w", err) } return uuid.NewString(), nil }