package email import ( "bytes" "crypto/tls" "fmt" "mime" "net/smtp" "strings" "time" "sproutgate-backend/internal/storage" ) func SendVerificationEmail(cfg storage.EmailConfig, to string, code string, expiresIn time.Duration) error { if strings.TrimSpace(to) == "" { return fmt.Errorf("email is required") } fromName := strings.TrimSpace(cfg.FromName) if fromName == "" { fromName = "萌芽账户认证中心" } fromAddress := strings.TrimSpace(cfg.FromAddress) if fromAddress == "" { return fmt.Errorf("from address is required") } username := strings.TrimSpace(cfg.Username) if username == "" { username = fromAddress } subject := "萌芽账户认证中心 - 邮箱验证" encodedName := mime.QEncoding.Encode("UTF-8", fromName) fromHeader := fmt.Sprintf("%s <%s>", encodedName, fromAddress) body := fmt.Sprintf("您的验证码是:%s\n有效期:%d 分钟\n\n如非本人操作,请忽略此邮件。", code, int(expiresIn.Minutes())) var msg bytes.Buffer msg.WriteString("From: " + fromHeader + "\r\n") msg.WriteString("To: " + to + "\r\n") msg.WriteString("Subject: " + mime.QEncoding.Encode("UTF-8", subject) + "\r\n") msg.WriteString("MIME-Version: 1.0\r\n") msg.WriteString("Content-Type: text/plain; charset=\"UTF-8\"\r\n") msg.WriteString("Content-Transfer-Encoding: 8bit\r\n") msg.WriteString("\r\n") msg.WriteString(body) addr := fmt.Sprintf("%s:%d", cfg.SMTPHost, cfg.SMTPPort) auth := smtp.PlainAuth("", username, cfg.Password, cfg.SMTPHost) encryption := strings.ToUpper(strings.TrimSpace(cfg.Encryption)) if cfg.SMTPPort == 465 || encryption == "SSL" { tlsConfig := &tls.Config{ ServerName: cfg.SMTPHost, MinVersion: tls.VersionTLS12, InsecureSkipVerify: false, } conn, err := tls.Dial("tcp", addr, tlsConfig) if err != nil { return err } client, err := smtp.NewClient(conn, cfg.SMTPHost) if err != nil { return err } defer client.Close() if err := client.Auth(auth); err != nil { return err } if err := client.Mail(fromAddress); err != nil { return err } if err := client.Rcpt(to); err != nil { return err } writer, err := client.Data() if err != nil { return err } if _, err := writer.Write(msg.Bytes()); err != nil { return err } if err := writer.Close(); err != nil { return err } return client.Quit() } return smtp.SendMail(addr, auth, fromAddress, []string{to}, msg.Bytes()) } func SendResetPasswordEmail(cfg storage.EmailConfig, to string, code string, expiresIn time.Duration) error { if strings.TrimSpace(to) == "" { return fmt.Errorf("email is required") } fromName := strings.TrimSpace(cfg.FromName) if fromName == "" { fromName = "萌芽账户认证中心" } fromAddress := strings.TrimSpace(cfg.FromAddress) if fromAddress == "" { return fmt.Errorf("from address is required") } username := strings.TrimSpace(cfg.Username) if username == "" { username = fromAddress } subject := "萌芽账户认证中心 - 重置密码" encodedName := mime.QEncoding.Encode("UTF-8", fromName) fromHeader := fmt.Sprintf("%s <%s>", encodedName, fromAddress) body := fmt.Sprintf("您的重置密码验证码是:%s\n有效期:%d 分钟\n\n如非本人操作,请忽略此邮件。", code, int(expiresIn.Minutes())) var msg bytes.Buffer msg.WriteString("From: " + fromHeader + "\r\n") msg.WriteString("To: " + to + "\r\n") msg.WriteString("Subject: " + mime.QEncoding.Encode("UTF-8", subject) + "\r\n") msg.WriteString("MIME-Version: 1.0\r\n") msg.WriteString("Content-Type: text/plain; charset=\"UTF-8\"\r\n") msg.WriteString("Content-Transfer-Encoding: 8bit\r\n") msg.WriteString("\r\n") msg.WriteString(body) addr := fmt.Sprintf("%s:%d", cfg.SMTPHost, cfg.SMTPPort) auth := smtp.PlainAuth("", username, cfg.Password, cfg.SMTPHost) encryption := strings.ToUpper(strings.TrimSpace(cfg.Encryption)) if cfg.SMTPPort == 465 || encryption == "SSL" { tlsConfig := &tls.Config{ ServerName: cfg.SMTPHost, MinVersion: tls.VersionTLS12, InsecureSkipVerify: false, } conn, err := tls.Dial("tcp", addr, tlsConfig) if err != nil { return err } client, err := smtp.NewClient(conn, cfg.SMTPHost) if err != nil { return err } defer client.Close() if err := client.Auth(auth); err != nil { return err } if err := client.Mail(fromAddress); err != nil { return err } if err := client.Rcpt(to); err != nil { return err } writer, err := client.Data() if err != nil { return err } if _, err := writer.Write(msg.Bytes()); err != nil { return err } if err := writer.Close(); err != nil { return err } return client.Quit() } return smtp.SendMail(addr, auth, fromAddress, []string{to}, msg.Bytes()) }