fusenapi/server/auth/internal/logic/email_manager.go

233 lines
5.3 KiB
Go
Raw Normal View History

2023-07-24 09:22:06 +00:00
package logic
import (
"bytes"
2023-09-04 08:23:11 +00:00
"fmt"
2023-08-31 03:12:49 +00:00
"fusenapi/utils/check"
2023-09-04 04:30:24 +00:00
"fusenapi/utils/fstpl"
2023-09-05 10:24:20 +00:00
"html/template"
2023-07-24 09:22:06 +00:00
"log"
"net/smtp"
"sync"
"time"
2023-08-31 04:20:05 +00:00
"github.com/zeromicro/go-zero/core/logx"
2023-07-24 09:22:06 +00:00
)
2023-08-31 04:20:05 +00:00
var EmailTaskResendTime = time.Second * 30
2023-08-31 03:12:49 +00:00
var TimeLimit *check.TimeLimit[string]
2023-07-24 09:22:06 +00:00
var EmailManager *EmailSender
2023-09-04 03:32:17 +00:00
var tpls *template.Template
2023-09-04 02:59:17 +00:00
2023-08-31 03:12:49 +00:00
func init() {
2023-09-04 03:32:17 +00:00
var err error
2023-08-31 03:12:49 +00:00
2023-09-04 04:30:24 +00:00
tpls = fstpl.AutoParseTplFiles()
2023-09-04 02:59:17 +00:00
if err != nil {
log.Fatal(err)
}
2023-08-31 04:20:05 +00:00
TimeLimit = check.NewTimelimit[string](EmailTaskResendTime)
2023-08-31 03:12:49 +00:00
// Initialize the email manager
EmailManager = &EmailSender{
EmailTasks: make(chan *EmailFormat, 10),
Auth: smtp.PlainAuth(
2023-09-05 07:53:04 +00:00
"fusen support",
2023-08-31 03:12:49 +00:00
"support@fusenpack.com",
"wfbjpdgvaozjvwah",
"smtp.gmail.com",
),
FromEmail: "support@fusenpack.com",
emailSending: make(map[string]*EmailTask, 10),
2023-08-31 04:20:05 +00:00
ResendTimeLimit: EmailTaskResendTime,
semaphore: make(chan struct{}, 100), // Initialize semaphore with a capacity of 10
2023-08-31 03:12:49 +00:00
}
// Start processing email tasks
go EmailManager.ProcessEmailTasks()
// Start clearing expired tasks
go EmailManager.ClearExpiredTasks()
}
2023-07-27 08:48:43 +00:00
type EmailFormat struct {
2023-09-04 08:23:11 +00:00
TemplateName string // 模板名字
2023-08-31 04:20:05 +00:00
UniqueKey string // 用于处理唯一的任务,重发都会被利用到
2023-07-27 08:48:43 +00:00
TargetEmail string // 发送的目标email
CompanyName string // fs公司名
ConfirmationLink string // fs确认连接
SenderName string // 发送人
SenderTitle string // 发送标题
2023-09-04 05:29:36 +00:00
Extend map[string]string
2023-07-27 08:48:43 +00:00
}
2023-09-04 08:23:11 +00:00
func (eformat *EmailFormat) Vaild() error {
// 1. 检查模板名称
if tpls.Lookup(eformat.TemplateName) == nil {
return fmt.Errorf("%s template name is not found", eformat.TemplateName)
}
// 2. 检查公司名称
if eformat.CompanyName == "" {
return fmt.Errorf("company name cannot be empty")
}
// 3. 检查确认链接
if eformat.ConfirmationLink == "" {
return fmt.Errorf("confirmation link cannot be empty")
}
// 4. 检查发送人名称
if eformat.SenderName == "" {
return fmt.Errorf("sender name cannot be empty")
}
// 5. 检查发送人头衔
if eformat.SenderTitle == "" {
return fmt.Errorf("sender title cannot be empty")
}
// 6. 检查目标邮箱
if eformat.TargetEmail == "" {
return fmt.Errorf("target email cannot be empty")
}
// 7. 检查唯一键
if eformat.UniqueKey == "" {
return fmt.Errorf("unique key cannot be empty")
}
// 所有校验通过
return nil
}
2023-07-24 09:22:06 +00:00
// EmailSender
type EmailSender struct {
lock sync.Mutex
2023-07-27 08:48:43 +00:00
EmailTasks chan *EmailFormat
2023-07-24 11:43:56 +00:00
Auth smtp.Auth
FromEmail string
ResendTimeLimit time.Duration
2023-07-27 08:48:43 +00:00
emailSending map[string]*EmailTask
semaphore chan struct{}
2023-07-24 09:22:06 +00:00
}
// EmailTask
type EmailTask struct {
2023-07-27 08:48:43 +00:00
Email *EmailFormat // email
SendTime time.Time // 处理的任务时间
2023-07-24 09:22:06 +00:00
}
func (m *EmailSender) ProcessEmailTasks() {
for {
2023-07-27 08:48:43 +00:00
emailformat, ok := <-m.EmailTasks
2023-07-24 09:22:06 +00:00
if !ok {
log.Println("Email task channel closed")
break
}
2023-09-04 08:23:11 +00:00
err := emailformat.Vaild()
if err != nil {
logx.Error(err)
2023-08-31 04:20:05 +00:00
continue
}
2023-07-24 09:22:06 +00:00
m.lock.Lock()
2023-08-31 04:20:05 +00:00
_, isSending := m.emailSending[emailformat.UniqueKey]
2023-07-24 09:22:06 +00:00
if isSending {
m.lock.Unlock()
continue
}
2023-08-31 04:20:05 +00:00
m.emailSending[emailformat.UniqueKey] = &EmailTask{
2023-07-27 08:48:43 +00:00
Email: emailformat,
2023-08-24 10:28:01 +00:00
SendTime: time.Now().UTC(),
2023-07-24 09:22:06 +00:00
}
m.lock.Unlock()
2023-07-24 11:43:56 +00:00
// Acquire a token
m.semaphore <- struct{}{}
go func() {
defer func() { <-m.semaphore }() // Release a token
2023-09-04 08:23:11 +00:00
content := RenderEmailTemplate(
emailformat.TemplateName,
emailformat.CompanyName,
2023-09-04 05:29:36 +00:00
emailformat.ConfirmationLink,
emailformat.SenderName,
emailformat.SenderTitle,
emailformat.Extend,
)
2023-07-27 08:48:43 +00:00
err := smtp.SendMail("smtp.gmail.com:587", m.Auth, m.FromEmail, []string{emailformat.TargetEmail}, content)
2023-07-24 11:43:56 +00:00
if err != nil {
2023-07-27 08:48:43 +00:00
log.Printf("Failed to send email to %s: %v\n", emailformat, err)
2023-08-31 04:20:05 +00:00
m.Resend(emailformat.UniqueKey, content)
2023-07-24 11:43:56 +00:00
}
}()
2023-07-24 09:22:06 +00:00
}
}
// Resend 重发邮件
2023-08-31 04:20:05 +00:00
func (m *EmailSender) Resend(uniqueKey string, content []byte) {
2023-07-24 09:22:06 +00:00
time.Sleep(m.ResendTimeLimit)
m.lock.Lock()
defer m.lock.Unlock()
// Check if the email task still exists and has not been sent successfully
2023-08-31 04:20:05 +00:00
if task, ok := m.emailSending[uniqueKey]; ok && task.SendTime.Add(m.ResendTimeLimit).After(time.Now().UTC()) {
err := smtp.SendMail(task.Email.TargetEmail, m.Auth, m.FromEmail, []string{task.Email.TargetEmail}, content)
2023-07-24 09:22:06 +00:00
if err != nil {
2023-08-31 04:20:05 +00:00
log.Printf("Failed to resend email to %s: %v\n", task.Email.TargetEmail, err)
2023-07-24 09:22:06 +00:00
} else {
2023-08-31 04:20:05 +00:00
delete(m.emailSending, uniqueKey)
2023-07-24 09:22:06 +00:00
}
}
}
// ClearExpiredTasks 清除过期的邮件任务
func (m *EmailSender) ClearExpiredTasks() {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for {
<-ticker.C
m.lock.Lock()
for email, task := range m.emailSending {
2023-08-24 10:28:01 +00:00
if task.SendTime.Add(m.ResendTimeLimit).Before(time.Now().UTC()) {
2023-07-24 09:22:06 +00:00
delete(m.emailSending, email)
}
}
m.lock.Unlock()
}
}
2023-09-04 08:23:11 +00:00
func RenderEmailTemplate(templateName, companyName, confirmationLink, senderName, senderTitle string, extend map[string]string) []byte {
2023-09-04 02:50:21 +00:00
2023-07-24 09:22:06 +00:00
data := map[string]string{
"CompanyName": companyName,
"ConfirmationLink": confirmationLink,
"SenderName": senderName,
"SenderTitle": senderTitle,
}
2023-09-04 05:29:36 +00:00
for k, v := range extend {
data[k] = v
}
2023-07-24 09:22:06 +00:00
var result bytes.Buffer
2023-09-05 16:08:51 +00:00
// result.Write([]byte("MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"))
2023-09-04 08:23:11 +00:00
err := tpls.ExecuteTemplate(&result, templateName, data)
2023-07-24 09:22:06 +00:00
if err != nil {
log.Fatal(err)
}
2023-07-24 11:43:56 +00:00
return result.Bytes()
2023-07-24 09:22:06 +00:00
}