service.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. package smtp
  2. import (
  3. "bytes"
  4. "html/template"
  5. "strings"
  6. "github.com/sirupsen/logrus"
  7. "github.com/wneessen/go-mail"
  8. "git.linuxforward.com/byom/byom-golang-lib/pkg/errors"
  9. )
  10. // SMTPConfig holds the configuration for the SMTP service
  11. type SMTPConfig struct {
  12. Host string
  13. Port int
  14. Username string
  15. Password string
  16. From string
  17. }
  18. // EmailData holds the data for the default email template
  19. type EmailData struct {
  20. Subject string
  21. Body string
  22. }
  23. // VerificationData holds the data for the verification email template
  24. type VerificationData struct {
  25. VerificationURL string
  26. }
  27. // WelcomeData holds the data for the welcome email template
  28. type WelcomeData struct {
  29. Username string
  30. Password string
  31. WebAppURL string
  32. }
  33. // SMTPService handles email sending operations
  34. type SMTPService struct {
  35. client *mail.Client
  36. logger *logrus.Logger
  37. from string
  38. }
  39. // NewSMTPService creates a new SMTP service instance
  40. func NewSMTPService(config SMTPConfig, logger *logrus.Logger) (*SMTPService, error) {
  41. if logger == nil {
  42. return nil, errors.NewSMTPError("initialization", errors.ErrInvalidInput)
  43. }
  44. // Validate config
  45. if config.Host == "" || config.Port == 0 || config.Username == "" || config.Password == "" || config.From == "" {
  46. return nil, errors.NewSMTPError("initialization", errors.ErrInvalidInput)
  47. }
  48. // Create mail client with proper configuration
  49. client, err := mail.NewClient(config.Host,
  50. mail.WithPort(config.Port),
  51. mail.WithSMTPAuth(mail.SMTPAuthPlain),
  52. mail.WithUsername(config.Username),
  53. mail.WithPassword(config.Password),
  54. mail.WithTLSPolicy(mail.TLSMandatory), // Enforce TLS
  55. )
  56. if err != nil {
  57. logger.WithError(err).Error("Failed to create SMTP client")
  58. return nil, errors.NewSMTPError("client creation", err)
  59. }
  60. return &SMTPService{
  61. client: client,
  62. logger: logger,
  63. from: config.From,
  64. }, nil
  65. }
  66. // SendEmail sends a basic email using the default template
  67. func (s *SMTPService) SendEmail(to string, data EmailData) error {
  68. if strings.TrimSpace(to) == "" {
  69. return errors.NewSMTPError("send", errors.ErrInvalidInput)
  70. }
  71. // Parse and execute template
  72. tmpl, err := template.New("email").Parse(defaultTemplate)
  73. if err != nil {
  74. s.logger.WithError(err).Error("Failed to parse email template")
  75. return errors.NewSMTPError("template parsing", err)
  76. }
  77. var body bytes.Buffer
  78. if err := tmpl.Execute(&body, data); err != nil {
  79. s.logger.WithError(err).Error("Failed to execute email template")
  80. return errors.NewSMTPError("template execution", err)
  81. }
  82. // Create and configure message
  83. msg := mail.NewMsg()
  84. if err := msg.From(s.from); err != nil {
  85. return errors.NewSMTPError("message creation", err)
  86. }
  87. if err := msg.To(to); err != nil {
  88. return errors.NewSMTPError("message creation", err)
  89. }
  90. msg.Subject(data.Subject)
  91. msg.SetBodyString(mail.TypeTextHTML, body.String())
  92. // Send email
  93. if err := s.client.DialAndSend(msg); err != nil {
  94. s.logger.WithError(err).Error("Failed to send email")
  95. return errors.NewSMTPError("sending", err)
  96. }
  97. s.logger.WithFields(logrus.Fields{
  98. "to": to,
  99. "subject": data.Subject,
  100. }).Info("Email sent successfully")
  101. return nil
  102. }
  103. // SendVerificationEmail sends an email verification message
  104. func (s *SMTPService) SendVerificationEmail(to string, data VerificationData) error {
  105. if strings.TrimSpace(to) == "" || strings.TrimSpace(data.VerificationURL) == "" {
  106. return errors.NewSMTPError("send verification", errors.ErrInvalidInput)
  107. }
  108. // Parse and execute template
  109. tmpl, err := template.New("verification").Parse(verificationTemplate)
  110. if err != nil {
  111. s.logger.WithError(err).Error("Failed to parse verification template")
  112. return errors.NewSMTPError("template parsing", err)
  113. }
  114. var body bytes.Buffer
  115. if err := tmpl.Execute(&body, data); err != nil {
  116. s.logger.WithError(err).Error("Failed to execute verification template")
  117. return errors.NewSMTPError("template execution", err)
  118. }
  119. // Create and configure message
  120. msg := mail.NewMsg()
  121. if err := msg.From(s.from); err != nil {
  122. return errors.NewSMTPError("message creation", err)
  123. }
  124. if err := msg.To(to); err != nil {
  125. return errors.NewSMTPError("message creation", err)
  126. }
  127. msg.Subject("Verify Your Email Address")
  128. msg.SetBodyString(mail.TypeTextHTML, body.String())
  129. // Send email
  130. if err := s.client.DialAndSend(msg); err != nil {
  131. s.logger.WithError(err).Error("Failed to send verification email")
  132. return errors.NewSMTPError("sending", err)
  133. }
  134. s.logger.WithFields(logrus.Fields{
  135. "to": to,
  136. }).Info("Verification email sent successfully")
  137. return nil
  138. }
  139. // SendWelcomeEmail sends a welcome email with credentials
  140. func (s *SMTPService) SendWelcomeEmail(to string, data WelcomeData) error {
  141. if strings.TrimSpace(to) == "" || strings.TrimSpace(data.Username) == "" {
  142. return errors.NewSMTPError("send welcome", errors.ErrInvalidInput)
  143. }
  144. // Parse and execute template
  145. tmpl, err := template.New("welcome").Parse(welcomeTemplate)
  146. if err != nil {
  147. s.logger.WithError(err).Error("Failed to parse welcome template")
  148. return errors.NewSMTPError("template parsing", err)
  149. }
  150. var body bytes.Buffer
  151. if err := tmpl.Execute(&body, data); err != nil {
  152. s.logger.WithError(err).Error("Failed to execute welcome template")
  153. return errors.NewSMTPError("template execution", err)
  154. }
  155. // Create and configure message
  156. msg := mail.NewMsg()
  157. if err := msg.From(s.from); err != nil {
  158. return errors.NewSMTPError("message creation", err)
  159. }
  160. if err := msg.To(to); err != nil {
  161. return errors.NewSMTPError("message creation", err)
  162. }
  163. msg.Subject("Welcome to Our Platform!")
  164. msg.SetBodyString(mail.TypeTextHTML, body.String())
  165. // Send email
  166. if err := s.client.DialAndSend(msg); err != nil {
  167. s.logger.WithError(err).Error("Failed to send welcome email")
  168. return errors.NewSMTPError("sending", err)
  169. }
  170. s.logger.WithFields(logrus.Fields{
  171. "to": to,
  172. "username": data.Username,
  173. }).Info("Welcome email sent successfully")
  174. return nil
  175. }
  176. // Close closes the SMTP client connection
  177. func (s *SMTPService) Close() error {
  178. if s.client != nil {
  179. if err := s.client.Close(); err != nil {
  180. s.logger.WithError(err).Error("Failed to close SMTP client")
  181. return errors.NewSMTPError("close", err)
  182. }
  183. s.logger.Info("SMTP client closed successfully")
  184. }
  185. return nil
  186. }