169 lines
3.9 KiB
Go
169 lines
3.9 KiB
Go
package logic
|
|
|
|
import (
|
|
"bytes"
|
|
"log"
|
|
"net/smtp"
|
|
"sync"
|
|
"text/template"
|
|
"time"
|
|
)
|
|
|
|
var EmailManager *EmailSender
|
|
|
|
// EmailSender
|
|
type EmailSender struct {
|
|
lock sync.Mutex
|
|
EmailTasks chan string
|
|
Auth smtp.Auth
|
|
FromEmail string
|
|
emailSending map[string]*EmailTask
|
|
ResendTimeLimit time.Duration
|
|
semaphore chan struct{}
|
|
}
|
|
|
|
// EmailTask
|
|
type EmailTask struct {
|
|
Email string // email
|
|
SendTime time.Time // 处理的任务时间
|
|
}
|
|
|
|
func (m *EmailSender) ProcessEmailTasks() {
|
|
for {
|
|
emailTarget, ok := <-m.EmailTasks
|
|
if !ok {
|
|
log.Println("Email task channel closed")
|
|
break
|
|
}
|
|
|
|
m.lock.Lock()
|
|
_, isSending := m.emailSending[emailTarget]
|
|
if isSending {
|
|
m.lock.Unlock()
|
|
continue
|
|
}
|
|
|
|
m.emailSending[emailTarget] = &EmailTask{
|
|
Email: emailTarget,
|
|
SendTime: time.Now(),
|
|
}
|
|
m.lock.Unlock()
|
|
|
|
// Acquire a token
|
|
m.semaphore <- struct{}{}
|
|
|
|
go func() {
|
|
defer func() { <-m.semaphore }() // Release a token
|
|
|
|
content := RenderEmailTemplate("fusen", "http://www.baidu.com", "fusen@gmail.com", "mail-valid")
|
|
err := smtp.SendMail("smtp.gmail.com:587", m.Auth, m.FromEmail, []string{emailTarget}, content)
|
|
if err != nil {
|
|
log.Printf("Failed to send email to %s: %v\n", emailTarget, err)
|
|
m.Resend(emailTarget, content)
|
|
}
|
|
}()
|
|
}
|
|
}
|
|
|
|
// Resend 重发邮件
|
|
func (m *EmailSender) Resend(emailTarget string, content []byte) {
|
|
time.Sleep(m.ResendTimeLimit)
|
|
|
|
m.lock.Lock()
|
|
defer m.lock.Unlock()
|
|
|
|
// Check if the email task still exists and has not been sent successfully
|
|
if task, ok := m.emailSending[emailTarget]; ok && task.SendTime.Add(m.ResendTimeLimit).After(time.Now()) {
|
|
err := smtp.SendMail(emailTarget, m.Auth, m.FromEmail, []string{emailTarget}, content)
|
|
if err != nil {
|
|
log.Printf("Failed to resend email to %s: %v\n", emailTarget, err)
|
|
} else {
|
|
delete(m.emailSending, emailTarget)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
if task.SendTime.Add(m.ResendTimeLimit).Before(time.Now()) {
|
|
delete(m.emailSending, email)
|
|
}
|
|
}
|
|
m.lock.Unlock()
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
|
|
// Initialize the email manager
|
|
EmailManager = &EmailSender{
|
|
EmailTasks: make(chan string, 10),
|
|
Auth: smtp.PlainAuth(
|
|
"",
|
|
"user@example.com",
|
|
"password",
|
|
"smtp.gmail.com",
|
|
),
|
|
FromEmail: "user@example.com",
|
|
emailSending: make(map[string]*EmailTask, 10),
|
|
ResendTimeLimit: time.Minute * 1,
|
|
semaphore: make(chan struct{}, 10), // Initialize semaphore with a capacity of 10
|
|
}
|
|
|
|
// Start processing email tasks
|
|
go EmailManager.ProcessEmailTasks()
|
|
|
|
// Start clearing expired tasks
|
|
go EmailManager.ClearExpiredTasks()
|
|
}
|
|
|
|
const emailTemplate = `Subject: Your {{.CompanyName}} Account Confirmation
|
|
|
|
Dear
|
|
|
|
Thank you for creating an account with {{.CompanyName}}. We're excited to have you on board!
|
|
|
|
Before we get started, we just need to confirm that this is the right email address. Please confirm your email address by clicking on the link below:
|
|
|
|
{{.ConfirmationLink}}
|
|
|
|
Once you've confirmed, you can get started with {{.CompanyName}}. If you have any questions, feel free to reply to this email. We're here to help!
|
|
|
|
If you did not create an account with us, please ignore this email.
|
|
|
|
Thanks,
|
|
{{.SenderName}}
|
|
{{.SenderTitle}}
|
|
{{.CompanyName}}
|
|
`
|
|
|
|
func RenderEmailTemplate(companyName, confirmationLink, senderName, senderTitle string) []byte {
|
|
tmpl, err := template.New("email").Parse(emailTemplate)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
data := map[string]string{
|
|
"CompanyName": companyName,
|
|
"ConfirmationLink": confirmationLink,
|
|
"SenderName": senderName,
|
|
"SenderTitle": senderTitle,
|
|
}
|
|
|
|
var result bytes.Buffer
|
|
err = tmpl.Execute(&result, data)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
return result.Bytes()
|
|
}
|