package mail import ( "crypto/tls" "fmt" "net/smtp" "strings" "github.com/imc-vibe/backend/internal/config" ) type EmailService struct { host string port string username string password string from string baseURL string } func NewEmailService(cfg *config.Config) *EmailService { return &EmailService{ host: cfg.SMTPHost, port: cfg.SMTPPort, username: cfg.SMTPUser, password: cfg.SMTPPassword, from: cfg.SMTPFrom, baseURL: cfg.BaseURL, } } func (s *EmailService) SendPasswordReset(to, token string) error { resetURL := fmt.Sprintf("%s/auth/reset-password?token=%s", s.baseURL, token) subject := "Password Reset Request" body := fmt.Sprintf(`Hi, You requested a password reset for your account. Click the link below to reset your password: %s This link will expire in 1 hour. If you didn't request this, please ignore this email. Best regards, IMC Vibe Mail Server Admin `, resetURL) return s.send(to, subject, body) } func (s *EmailService) send(to, subject, body string) error { if s.host == "" || s.username == "" { return fmt.Errorf("SMTP not configured") } addr := fmt.Sprintf("%s:%s", s.host, s.port) msg := fmt.Sprintf("From: %s\r\n"+ "To: %s\r\n"+ "Subject: %s\r\n"+ "MIME-Version: 1.0\r\n"+ "Content-Type: text/plain; charset=\"UTF-8\"\r\n"+ "\r\n"+ "%s", s.from, to, subject, body) var auth smtp.Auth if s.port == "465" { err := s.sendWithTLS(addr, to, msg) return err } else { auth = smtp.PlainAuth("", s.username, s.password, s.host) err := smtp.SendMail(addr, auth, s.from, []string{to}, []byte(msg)) return err } } func (s *EmailService) sendWithTLS(addr, to, msg string) error { tlsConfig := &tls.Config{ ServerName: s.host, } conn, err := tls.Dial("tcp", addr, tlsConfig) if err != nil { return err } defer conn.Close() client, err := smtp.NewClient(conn, s.host) if err != nil { return err } defer client.Close() auth := smtp.PlainAuth("", s.username, s.password, s.host) if err := client.Auth(auth); err != nil { return err } if err := client.Mail(s.from); err != nil { return err } if err := client.Rcpt(to); err != nil { return err } w, err := client.Data() if err != nil { return err } _, err = w.Write([]byte(msg)) if err != nil { return err } err = w.Close() if err != nil { return err } return client.Quit() } func GenerateToken() string { b := make([]byte, 32) for i := range b { b[i] = tokenChars[i%len(tokenChars)] } return fmt.Sprintf("%x", b) } const tokenChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" func ValidateToken(token string) bool { if len(token) != 64 { return false } for _, c := range token { if !strings.ContainsRune(tokenChars, c) { return false } } return true }