package mail import ( "crypto/tls" "fmt" "net" "net/mail" "net/smtp" "strings" ) type SMTPConfig struct { Host string Port string User string Password string From string FromName string } type Sender struct { cfg SMTPConfig } func NewSender(cfg SMTPConfig) Sender { if strings.TrimSpace(cfg.Port) == "" { cfg.Port = "587" } return Sender{cfg: cfg} } func (s Sender) IsConfigured() bool { return strings.TrimSpace(s.cfg.Host) != "" && strings.TrimSpace(s.cfg.Port) != "" && strings.TrimSpace(s.cfg.From) != "" } func (s Sender) SendInvitation(to string, invitationURL string) error { subject := "Convite para acessar o Mira" textBody := "Voce foi convidado para acessar o Mira.\n\nCrie sua senha pelo link abaixo:\n" + invitationURL + "\n\nEste convite expira em 7 dias." htmlBody := `

Voce foi convidado para acessar o Mira.

Criar minha senha

Este convite expira em 7 dias.

` return s.send(to, subject, textBody, htmlBody) } func (s Sender) send(to string, subject string, textBody string, htmlBody string) error { if !s.IsConfigured() { return nil } from := mail.Address{Name: strings.TrimSpace(s.cfg.FromName), Address: strings.TrimSpace(s.cfg.From)} recipient := mail.Address{Address: strings.TrimSpace(to)} boundary := "mira-mail-boundary" message := strings.Join([]string{ "From: " + from.String(), "To: " + recipient.String(), "Subject: " + subject, "MIME-Version: 1.0", "Content-Type: multipart/alternative; boundary=\"" + boundary + "\"", "", "--" + boundary, "Content-Type: text/plain; charset=UTF-8", "", textBody, "--" + boundary, "Content-Type: text/html; charset=UTF-8", "", htmlBody, "--" + boundary + "--", "", }, "\r\n") host := strings.TrimSpace(s.cfg.Host) port := strings.TrimSpace(s.cfg.Port) addr := net.JoinHostPort(host, port) var auth smtp.Auth if strings.TrimSpace(s.cfg.User) != "" || strings.TrimSpace(s.cfg.Password) != "" { auth = smtp.PlainAuth("", strings.TrimSpace(s.cfg.User), strings.TrimSpace(s.cfg.Password), host) } if port == "465" { if err := sendMailTLS(addr, host, auth, from.Address, []string{recipient.Address}, []byte(message)); err != nil { return fmt.Errorf("send smtp email: %w", err) } return nil } if err := sendMailStartTLS(addr, host, auth, from.Address, []string{recipient.Address}, []byte(message)); err != nil { return fmt.Errorf("send smtp email: %w", err) } return nil } func sendMailStartTLS(addr string, host string, auth smtp.Auth, from string, to []string, message []byte) error { client, err := smtp.Dial(addr) if err != nil { return err } defer client.Quit() if ok, _ := client.Extension("STARTTLS"); ok { if err := client.StartTLS(&tls.Config{ServerName: host, MinVersion: tls.VersionTLS12}); err != nil { return err } } return sendMailWithClient(client, auth, from, to, message) } func sendMailTLS(addr string, host string, auth smtp.Auth, from string, to []string, message []byte) error { conn, err := tls.Dial("tcp", addr, &tls.Config{ServerName: host, MinVersion: tls.VersionTLS12}) if err != nil { return err } client, err := smtp.NewClient(conn, host) if err != nil { _ = conn.Close() return err } defer client.Quit() return sendMailWithClient(client, auth, from, to, message) } func sendMailWithClient(client *smtp.Client, auth smtp.Auth, from string, to []string, message []byte) error { if auth != nil { if err := client.Auth(auth); err != nil { return err } } if err := client.Mail(from); err != nil { return err } for _, recipient := range to { if err := client.Rcpt(recipient); err != nil { return err } } writer, err := client.Data() if err != nil { return err } if _, err := writer.Write(message); err != nil { _ = writer.Close() return err } return writer.Close() }