// // Copyright (C) 2024 veypi // 2025-03-04 16:08:06 // Distributed under terms of the MIT license. // package email import ( "crypto/tls" "fmt" "net/smtp" "github.com/veypi/vbase/models" ) // SMTPProvider SMTP 邮件提供商 type SMTPProvider struct { host string port int username string password string from string fromName string } // NewSMTPProvider 创建 SMTP 提供商 func NewSMTPProvider() (*SMTPProvider, error) { host, err := models.GetSetting(models.SettingEmailSMTPHost) if err != nil || host == "" { return nil, fmt.Errorf("smtp host not configured") } port, _ := models.GetSettingInt(models.SettingEmailSMTPPort) if port == 0 { port = 587 } username, _ := models.GetSetting(models.SettingEmailSMTPUser) password, _ := models.GetSetting(models.SettingEmailSMTPPass) from, _ := models.GetSetting(models.SettingEmailFrom) fromName, _ := models.GetSetting(models.SettingEmailFromName) if from == "" { from = username } return &SMTPProvider{ host: host, port: port, username: username, password: password, from: from, fromName: fromName, }, nil } // Send 发送邮件 func (p *SMTPProvider) Send(to, subject, content string) error { from := p.from if p.fromName != "" { from = fmt.Sprintf("%s <%s>", p.fromName, p.from) } // 构造邮件内容 msg := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n%s", from, to, subject, content) addr := fmt.Sprintf("%s:%d", p.host, p.port) // 使用 TLS tlsConfig := &tls.Config{ ServerName: p.host, } conn, err := tls.Dial("tcp", addr, tlsConfig) if err != nil { // 尝试非 TLS return smtp.SendMail(addr, smtp.PlainAuth("", p.username, p.password, p.host), p.from, []string{to}, []byte(msg)) } defer conn.Close() client, err := smtp.NewClient(conn, p.host) if err != nil { return err } defer client.Close() // 认证 auth := smtp.PlainAuth("", p.username, p.password, p.host) if err = client.Auth(auth); err != nil { return err } // 设置发件人 if err = client.Mail(p.from); err != nil { return err } // 设置收件人 if err = client.Rcpt(to); err != nil { return err } // 写入数据 w, err := client.Data() if err != nil { return err } defer w.Close() _, err = w.Write([]byte(msg)) return err } // SendVerificationCode 发送验证码邮件 func SendVerificationCode(to, code string) error { provider, err := NewProvider() if err != nil { return err } subject := "验证码" content := fmt.Sprintf(`

您的验证码

您的验证码是:

%s

此验证码 5 分钟内有效,请勿泄露给他人。

`, code) return provider.Send(to, subject, content) }