|
@@ -0,0 +1,220 @@
|
|
|
+package smtp
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "html/template"
|
|
|
+ "strings"
|
|
|
+
|
|
|
+ "github.com/sirupsen/logrus"
|
|
|
+ "github.com/wneessen/go-mail"
|
|
|
+
|
|
|
+ "git.linuxforward.com/byom/byom-golang-lib/pkg/errors"
|
|
|
+)
|
|
|
+
|
|
|
+// SMTPConfig holds the configuration for the SMTP service
|
|
|
+type SMTPConfig struct {
|
|
|
+ Host string
|
|
|
+ Port int
|
|
|
+ Username string
|
|
|
+ Password string
|
|
|
+ From string
|
|
|
+}
|
|
|
+
|
|
|
+// EmailData holds the data for the default email template
|
|
|
+type EmailData struct {
|
|
|
+ Subject string
|
|
|
+ Body string
|
|
|
+}
|
|
|
+
|
|
|
+// VerificationData holds the data for the verification email template
|
|
|
+type VerificationData struct {
|
|
|
+ VerificationURL string
|
|
|
+}
|
|
|
+
|
|
|
+// WelcomeData holds the data for the welcome email template
|
|
|
+type WelcomeData struct {
|
|
|
+ Username string
|
|
|
+ Password string
|
|
|
+ WebAppURL string
|
|
|
+}
|
|
|
+
|
|
|
+// SMTPService handles email sending operations
|
|
|
+type SMTPService struct {
|
|
|
+ client *mail.Client
|
|
|
+ logger *logrus.Logger
|
|
|
+ from string
|
|
|
+}
|
|
|
+
|
|
|
+// NewSMTPService creates a new SMTP service instance
|
|
|
+func NewSMTPService(config SMTPConfig, logger *logrus.Logger) (*SMTPService, error) {
|
|
|
+ if logger == nil {
|
|
|
+ return nil, errors.NewSMTPError("initialization", errors.ErrInvalidInput)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Validate config
|
|
|
+ if config.Host == "" || config.Port == 0 || config.Username == "" || config.Password == "" || config.From == "" {
|
|
|
+ return nil, errors.NewSMTPError("initialization", errors.ErrInvalidInput)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create mail client with proper configuration
|
|
|
+ client, err := mail.NewClient(config.Host,
|
|
|
+ mail.WithPort(config.Port),
|
|
|
+ mail.WithSMTPAuth(mail.SMTPAuthPlain),
|
|
|
+ mail.WithUsername(config.Username),
|
|
|
+ mail.WithPassword(config.Password),
|
|
|
+ mail.WithTLSPolicy(mail.TLSMandatory), // Enforce TLS
|
|
|
+ )
|
|
|
+ if err != nil {
|
|
|
+ logger.WithError(err).Error("Failed to create SMTP client")
|
|
|
+ return nil, errors.NewSMTPError("client creation", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ return &SMTPService{
|
|
|
+ client: client,
|
|
|
+ logger: logger,
|
|
|
+ from: config.From,
|
|
|
+ }, nil
|
|
|
+}
|
|
|
+
|
|
|
+// SendEmail sends a basic email using the default template
|
|
|
+func (s *SMTPService) SendEmail(to string, data EmailData) error {
|
|
|
+ if strings.TrimSpace(to) == "" {
|
|
|
+ return errors.NewSMTPError("send", errors.ErrInvalidInput)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parse and execute template
|
|
|
+ tmpl, err := template.New("email").Parse(defaultTemplate)
|
|
|
+ if err != nil {
|
|
|
+ s.logger.WithError(err).Error("Failed to parse email template")
|
|
|
+ return errors.NewSMTPError("template parsing", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ var body bytes.Buffer
|
|
|
+ if err := tmpl.Execute(&body, data); err != nil {
|
|
|
+ s.logger.WithError(err).Error("Failed to execute email template")
|
|
|
+ return errors.NewSMTPError("template execution", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create and configure message
|
|
|
+ msg := mail.NewMsg()
|
|
|
+ if err := msg.From(s.from); err != nil {
|
|
|
+ return errors.NewSMTPError("message creation", err)
|
|
|
+ }
|
|
|
+ if err := msg.To(to); err != nil {
|
|
|
+ return errors.NewSMTPError("message creation", err)
|
|
|
+ }
|
|
|
+ msg.Subject(data.Subject)
|
|
|
+ msg.SetBodyString(mail.TypeTextHTML, body.String())
|
|
|
+
|
|
|
+ // Send email
|
|
|
+ if err := s.client.DialAndSend(msg); err != nil {
|
|
|
+ s.logger.WithError(err).Error("Failed to send email")
|
|
|
+ return errors.NewSMTPError("sending", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ s.logger.WithFields(logrus.Fields{
|
|
|
+ "to": to,
|
|
|
+ "subject": data.Subject,
|
|
|
+ }).Info("Email sent successfully")
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// SendVerificationEmail sends an email verification message
|
|
|
+func (s *SMTPService) SendVerificationEmail(to string, data VerificationData) error {
|
|
|
+ if strings.TrimSpace(to) == "" || strings.TrimSpace(data.VerificationURL) == "" {
|
|
|
+ return errors.NewSMTPError("send verification", errors.ErrInvalidInput)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parse and execute template
|
|
|
+ tmpl, err := template.New("verification").Parse(verificationTemplate)
|
|
|
+ if err != nil {
|
|
|
+ s.logger.WithError(err).Error("Failed to parse verification template")
|
|
|
+ return errors.NewSMTPError("template parsing", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ var body bytes.Buffer
|
|
|
+ if err := tmpl.Execute(&body, data); err != nil {
|
|
|
+ s.logger.WithError(err).Error("Failed to execute verification template")
|
|
|
+ return errors.NewSMTPError("template execution", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create and configure message
|
|
|
+ msg := mail.NewMsg()
|
|
|
+ if err := msg.From(s.from); err != nil {
|
|
|
+ return errors.NewSMTPError("message creation", err)
|
|
|
+ }
|
|
|
+ if err := msg.To(to); err != nil {
|
|
|
+ return errors.NewSMTPError("message creation", err)
|
|
|
+ }
|
|
|
+ msg.Subject("Verify Your Email Address")
|
|
|
+ msg.SetBodyString(mail.TypeTextHTML, body.String())
|
|
|
+
|
|
|
+ // Send email
|
|
|
+ if err := s.client.DialAndSend(msg); err != nil {
|
|
|
+ s.logger.WithError(err).Error("Failed to send verification email")
|
|
|
+ return errors.NewSMTPError("sending", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ s.logger.WithFields(logrus.Fields{
|
|
|
+ "to": to,
|
|
|
+ }).Info("Verification email sent successfully")
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// SendWelcomeEmail sends a welcome email with credentials
|
|
|
+func (s *SMTPService) SendWelcomeEmail(to string, data WelcomeData) error {
|
|
|
+ if strings.TrimSpace(to) == "" || strings.TrimSpace(data.Username) == "" {
|
|
|
+ return errors.NewSMTPError("send welcome", errors.ErrInvalidInput)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parse and execute template
|
|
|
+ tmpl, err := template.New("welcome").Parse(welcomeTemplate)
|
|
|
+ if err != nil {
|
|
|
+ s.logger.WithError(err).Error("Failed to parse welcome template")
|
|
|
+ return errors.NewSMTPError("template parsing", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ var body bytes.Buffer
|
|
|
+ if err := tmpl.Execute(&body, data); err != nil {
|
|
|
+ s.logger.WithError(err).Error("Failed to execute welcome template")
|
|
|
+ return errors.NewSMTPError("template execution", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create and configure message
|
|
|
+ msg := mail.NewMsg()
|
|
|
+ if err := msg.From(s.from); err != nil {
|
|
|
+ return errors.NewSMTPError("message creation", err)
|
|
|
+ }
|
|
|
+ if err := msg.To(to); err != nil {
|
|
|
+ return errors.NewSMTPError("message creation", err)
|
|
|
+ }
|
|
|
+ msg.Subject("Welcome to Our Platform!")
|
|
|
+ msg.SetBodyString(mail.TypeTextHTML, body.String())
|
|
|
+
|
|
|
+ // Send email
|
|
|
+ if err := s.client.DialAndSend(msg); err != nil {
|
|
|
+ s.logger.WithError(err).Error("Failed to send welcome email")
|
|
|
+ return errors.NewSMTPError("sending", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ s.logger.WithFields(logrus.Fields{
|
|
|
+ "to": to,
|
|
|
+ "username": data.Username,
|
|
|
+ }).Info("Welcome email sent successfully")
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// Close closes the SMTP client connection
|
|
|
+func (s *SMTPService) Close() error {
|
|
|
+ if s.client != nil {
|
|
|
+ if err := s.client.Close(); err != nil {
|
|
|
+ s.logger.WithError(err).Error("Failed to close SMTP client")
|
|
|
+ return errors.NewSMTPError("close", err)
|
|
|
+ }
|
|
|
+ s.logger.Info("SMTP client closed successfully")
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|