You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/libs/email/smtp.go

137 lines
2.9 KiB
Go

//
// Copyright (C) 2024 veypi <i@veypi.com>
// 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(`
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2>您的验证码</h2>
<p>您的验证码是:</p>
<div style="font-size: 32px; font-weight: bold; color: #333; padding: 20px; background: #f5f5f5; text-align: center; letter-spacing: 5px;">
%s
</div>
<p>此验证码 5 分钟内有效,请勿泄露给他人。</p>
</div>
`, code)
return provider.Send(to, subject, content)
}