package customerauth import ( "context" "fmt" "log" "net/mail" "strings" "time" "github.com/mailersend/mailersend-go" ) // MailerSendNotifier delivers transactional auth messages (email verification // and password reset) through the MailerSend API. It implements the Notifier // interface and is a drop-in replacement for LogNotifier in production. type MailerSendNotifier struct { client *mailersend.Mailersend from mail.Address } // NewMailerSendNotifier creates a new MailerSend-backed notifier. The apiKey // is the MailerSend API token (MAILERSEND_API_KEY env var). The from address // must include at least an email; the name portion is optional. func NewMailerSendNotifier(apiKey string, from mail.Address) (*MailerSendNotifier, error) { if strings.TrimSpace(apiKey) == "" { return nil, fmt.Errorf("mailersend notifier: missing API key") } if strings.TrimSpace(from.Address) == "" { return nil, fmt.Errorf("mailersend notifier: missing from address") } return &MailerSendNotifier{ client: mailersend.NewMailersend(apiKey), from: from, }, nil } // SendEmailVerification sends the verification link to the customer's email // address. Errors are logged but not returned (best-effort delivery matches the // signature of the Notifier interface). func (n *MailerSendNotifier) SendEmailVerification(email, link string) { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() if err := n.send(ctx, email, "Verify your email address", fmt.Sprintf("Click the link to verify your email address:\n\n%s", link), fmt.Sprintf(`
Click here to verify your email address.
`, link), ); err != nil { log.Printf("customerauth: send verification to %s: %v", email, err) } } // SendPasswordReset sends the password-reset link to the customer's email. // Errors are logged but not returned. func (n *MailerSendNotifier) SendPasswordReset(email, link string) { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() if err := n.send(ctx, email, "Reset your password", fmt.Sprintf("Click the link to reset your password:\n\n%s\n\nIf you didn't request this, ignore this email.", link), fmt.Sprintf(`Click here to reset your password.
If you didn't request this, ignore this email.
`, link), ); err != nil { log.Printf("customerauth: send password reset to %s: %v", email, err) } } func (n *MailerSendNotifier) send(ctx context.Context, to, subject, textBody, htmlBody string) error { message := n.client.Email.NewMessage() message.SetFrom(mailersend.From{ Name: n.from.Name, Email: n.from.Address, }) message.SetRecipients([]mailersend.Recipient{ {Email: to}, }) message.SetSubject(subject) message.SetText(textBody) if htmlBody != "" { message.SetHTML(htmlBody) } _, err := n.client.Email.Send(ctx, message) if err != nil { return fmt.Errorf("mailersend: %w", err) } return nil }