package service import ( "bytes" "embed" "encoding/base64" "fmt" "html/template" "log" "math/rand" "time" "gopkg.in/gomail.v2" ) type MailService interface { SendEmail(to, subject, body string) error SendTemplateEmail(to, subject string, templateName string, data interface{}) error SendConfirmationEmail(to, token string) error SendPasswordResetEmail(to, token string) error SendRecoveryEmail(value string, str string, address string, agent string) interface{} SendSessionConfirmation(email string, token string, address string, agent string) error } type mailService struct { from string smtpHost string smtpPort int smtpUser string smtpPass string templates *template.Template appURL string } func (s *mailService) SendSessionConfirmation(email string, token string, address string, agent string) error { //TODO implement me panic("implement me") } func (s *mailService) SendRecoveryEmail(value string, str string, address string, agent string) interface{} { //TODO implement me panic("implement me") } //go:embed templates/* var templateFS embed.FS func NewMailService(from, smtpHost string, smtpPort int, smtpUser, smtpPass, appURL string) (MailService, error) { // Загружаем шаблоны писем templates, err := template.ParseFS(templateFS, "templates/*.html") if err != nil { return nil, fmt.Errorf("failed to parse email templates: %w", err) } return &mailService{ from: from, smtpHost: smtpHost, smtpPort: smtpPort, smtpUser: smtpUser, smtpPass: smtpPass, templates: templates, appURL: appURL, }, nil } func (s *mailService) SendEmail(to, subject, body string) error { mail := gomail.NewMessage() mail.SetHeader("From", s.from) mail.SetHeader("To", to) mail.SetHeader("Subject", encodeSubject(subject)) mail.SetHeader("Message-ID", fmt.Sprintf(generateMessageID())) mail.SetBody("text/html; charset=UTF-8", body) mail.SetHeader("Contens-Transfer-Encoding", "base64") dialer := gomail.NewDialer(s.smtpHost, s.smtpPort, s.smtpUser, s.smtpPass) log.Printf("SendEmail dialer: %v", dialer) return dialer.DialAndSend(mail) } func encodeSubject(subject string) string { return fmt.Sprintf("=?UTF-8?B?%s?=", base64.StdEncoding.EncodeToString([]byte(subject))) } func generateMessageID() string { b := make([]byte, 16) rand.Read(b) return fmt.Sprintf("<%x.%x@tailly.ru>", time.Now().UnixNano(), b) } func (s *mailService) SendTemplateEmail(to, subject string, templateName string, data interface{}) error { var body bytes.Buffer if err := s.templates.ExecuteTemplate(&body, templateName+".html", data); err != nil { return fmt.Errorf("failed to execute template: %w", err) } log.Printf("SendTemplateEmail : %v", to, subject, body.String()) return s.SendEmail(to, subject, body.String()) } func (s *mailService) SendConfirmationEmail(to, token string) error { data := struct { Email string Token string Year int AppURL string }{ Email: to, Token: token, Year: time.Now().Year(), AppURL: s.appURL, } log.Printf("SendConfirmationEmail data: %v", data) return s.SendTemplateEmail(to, "Подтверждение email", "confirmation", data) } func (s *mailService) SendPasswordResetEmail(to, token string) error { data := struct { Email string Token string Year int }{ Email: to, Token: token, Year: time.Now().Year(), } return s.SendTemplateEmail(to, "Сброс пароля", "password_reset", data) }