proxyserver
This commit is contained in:
@@ -12,6 +12,7 @@ type Config struct {
|
||||
Auth types.Auth
|
||||
ReplicaId uint64
|
||||
|
||||
HomePage string
|
||||
MainAddress string
|
||||
WebsocketAddr string
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ func init() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
TimeLimit = check.NewTimelimit[string](EmailTaskResendTime)
|
||||
TimeLimit = check.NewTimeLimit[string](EmailTaskResendTime)
|
||||
|
||||
// Initialize the email manager
|
||||
EmailManager = &EmailSender{
|
||||
@@ -53,16 +53,17 @@ func init() {
|
||||
}
|
||||
|
||||
type EmailFormat struct {
|
||||
TemplateName string // 模板名字
|
||||
UniqueKey string // 用于处理唯一的任务,重发都会被利用到
|
||||
TargetEmail string // 发送的目标email
|
||||
CompanyName string // fs公司名
|
||||
ConfirmationLink string // fs确认连接
|
||||
SenderName string // 发送人
|
||||
SenderTitle string // 发送标题
|
||||
Extend map[string]string
|
||||
TemplateName string // 模板名字
|
||||
UniqueKey string // 用于处理唯一的任务,重发都会被利用到
|
||||
TargetEmail string // 发送的目标email
|
||||
CompanyName string // fs公司名
|
||||
ConfirmationLink string // fs确认连接
|
||||
SenderName string // 发送人
|
||||
SenderTitle string // 发送标题
|
||||
Extend map[string]string // 扩展参数
|
||||
}
|
||||
|
||||
// 验证邮件格式是否符合要求
|
||||
func (eformat *EmailFormat) Vaild() error {
|
||||
|
||||
// 1. 检查模板名称
|
||||
@@ -123,39 +124,47 @@ type EmailTask struct {
|
||||
SendTime time.Time // 处理的任务时间
|
||||
}
|
||||
|
||||
// ProcessEmailTasks 是 EmailSender 结构体的方法,用于处理邮件任务。
|
||||
func (m *EmailSender) ProcessEmailTasks() {
|
||||
for {
|
||||
// 从 EmailTasks 通道中接收邮件任务
|
||||
emailformat, ok := <-m.EmailTasks
|
||||
if !ok {
|
||||
log.Println("Email task channel closed")
|
||||
break
|
||||
}
|
||||
|
||||
// 验证邮件格式是否合法
|
||||
err := emailformat.Vaild()
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 加锁,避免并发修改 emailSending 字典
|
||||
m.lock.Lock()
|
||||
|
||||
// 检查 emailSending 字典中是否已存在相同的任务
|
||||
_, isSending := m.emailSending[emailformat.UniqueKey]
|
||||
if isSending {
|
||||
m.lock.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
// 将邮件任务添加到 emailSending 字典中
|
||||
m.emailSending[emailformat.UniqueKey] = &EmailTask{
|
||||
Email: emailformat,
|
||||
SendTime: time.Now().UTC(),
|
||||
}
|
||||
m.lock.Unlock()
|
||||
|
||||
// Acquire a token
|
||||
// 获取一个信号量,限制同时发送的邮件任务数量
|
||||
m.semaphore <- struct{}{}
|
||||
|
||||
go func() {
|
||||
defer func() { <-m.semaphore }() // Release a token
|
||||
defer func() { <-m.semaphore }() // 释放一个信号量
|
||||
|
||||
// 渲染邮件模板内容
|
||||
content := RenderEmailTemplate(
|
||||
emailformat.TemplateName,
|
||||
emailformat.CompanyName,
|
||||
@@ -164,34 +173,41 @@ func (m *EmailSender) ProcessEmailTasks() {
|
||||
emailformat.SenderTitle,
|
||||
emailformat.Extend,
|
||||
)
|
||||
|
||||
// 发送邮件
|
||||
err := smtp.SendMail("smtp.gmail.com:587", m.Auth, m.FromEmail, []string{emailformat.TargetEmail}, content)
|
||||
if err != nil {
|
||||
log.Printf("Failed to send email to %s: %v\n", emailformat, err)
|
||||
// 重新发送邮件
|
||||
m.Resend(emailformat.UniqueKey, content)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Resend 重发邮件
|
||||
// Resend 是 EmailSender 结构体的方法,用于重发邮件。
|
||||
func (m *EmailSender) Resend(uniqueKey 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[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)
|
||||
if err != nil {
|
||||
log.Printf("Failed to resend email to %s: %v\n", task.Email.TargetEmail, err)
|
||||
} else {
|
||||
// 从 emailSending 字典中删除已成功发送的邮件任务
|
||||
delete(m.emailSending, uniqueKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ClearExpiredTasks 清除过期的邮件任务
|
||||
// ClearExpiredTasks 是 EmailSender 结构体的方法,用于清除过期的邮件任务。
|
||||
func (m *EmailSender) ClearExpiredTasks() {
|
||||
// 每分钟触发一次清除操作
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
@@ -199,6 +215,7 @@ func (m *EmailSender) ClearExpiredTasks() {
|
||||
<-ticker.C
|
||||
|
||||
m.lock.Lock()
|
||||
// 遍历 emailSending 字典,删除发送时间超过一定限制的过期任务
|
||||
for email, task := range m.emailSending {
|
||||
if task.SendTime.Add(m.ResendTimeLimit).Before(time.Now().UTC()) {
|
||||
delete(m.emailSending, email)
|
||||
@@ -208,8 +225,19 @@ func (m *EmailSender) ClearExpiredTasks() {
|
||||
}
|
||||
}
|
||||
|
||||
func RenderEmailTemplate(templateName, companyName, confirmationLink, senderName, senderTitle string, extend map[string]string) []byte {
|
||||
// RenderEmailTemplate 是一个渲染邮件模板的函数,根据给定的参数生成邮件内容。
|
||||
// 参数:
|
||||
// - templateName: 邮件模板的名称
|
||||
// - companyName: 公司名称
|
||||
// - confirmationLink: 确认链接
|
||||
// - senderName: 发件人姓名
|
||||
// - senderTitle: 发件人职务
|
||||
// - extend: 扩展字段,包含其他自定义键值对的映射
|
||||
// 返回值:
|
||||
// - []byte: 渲染后的邮件内容(以字节切片形式返回)
|
||||
|
||||
func RenderEmailTemplate(templateName, companyName, confirmationLink, senderName, senderTitle string, extend map[string]string) []byte {
|
||||
// 创建一个包含邮件模板所需数据的映射
|
||||
data := map[string]string{
|
||||
"CompanyName": companyName,
|
||||
"ConfirmationLink": confirmationLink,
|
||||
@@ -217,16 +245,20 @@ func RenderEmailTemplate(templateName, companyName, confirmationLink, senderName
|
||||
"SenderTitle": senderTitle,
|
||||
}
|
||||
|
||||
// 将扩展字段中的键值对添加到数据映射中
|
||||
for k, v := range extend {
|
||||
data[k] = v
|
||||
}
|
||||
|
||||
// 创建一个字节缓冲区,用于存储渲染后的邮件内容
|
||||
var result bytes.Buffer
|
||||
// result.Write([]byte("MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"))
|
||||
|
||||
// 使用给定的数据映射执行邮件模板渲染
|
||||
err := tpls.ExecuteTemplate(&result, templateName, data)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// 将渲染后的邮件内容转换为字节切片并返回
|
||||
return result.Bytes()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"fusenapi/server/auth/internal/svc"
|
||||
"fusenapi/server/auth/internal/types"
|
||||
|
||||
"github.com/474420502/requests"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"gorm.io/gorm"
|
||||
@@ -57,7 +56,7 @@ func FinishRegister(svcCtx *svc.ServiceContext, user *gmodel.FsUser, token *auth
|
||||
event.Data = wevent.DataEmailRegister{
|
||||
JwtToken: jwtToken,
|
||||
}
|
||||
err = CommonNotify(svcCtx.Config.WebsocketAddr, token.Wid, event)
|
||||
err = wevent.CommonNotify(svcCtx.Config.WebsocketAddr, token.Wid, event)
|
||||
if err != nil {
|
||||
// logx.Error(err, token.TraceId)
|
||||
return err
|
||||
@@ -66,34 +65,6 @@ func FinishRegister(svcCtx *svc.ServiceContext, user *gmodel.FsUser, token *auth
|
||||
return nil
|
||||
}
|
||||
|
||||
func CommonNotify(WebsocketAddr, wid string, event *wevent.WebsocketEvent) error {
|
||||
|
||||
reqWebsocketAddr := fmt.Sprintf("%s/api/websocket/common_notify", WebsocketAddr)
|
||||
tp := requests.Post(reqWebsocketAddr)
|
||||
tp.SetBodyJson(requests.M{
|
||||
"wid": wid,
|
||||
"data": event,
|
||||
})
|
||||
|
||||
wresp, err := tp.Execute()
|
||||
if err != nil {
|
||||
// logx.Error(err, token.TraceId)
|
||||
return err
|
||||
}
|
||||
|
||||
result := wresp.Json()
|
||||
|
||||
if !result.Get("code").Exists() {
|
||||
return fmt.Errorf("send %s is error", reqWebsocketAddr)
|
||||
}
|
||||
|
||||
if result.Get("code").Int() != 200 {
|
||||
return fmt.Errorf("%s", result.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *UserEmailConfirmationLogic) UserEmailConfirmation(req *types.RequestEmailConfirmation, userinfo *auth.UserInfo) (resp *basic.Response) {
|
||||
// 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data)
|
||||
// userinfo 传入值时, 一定不为null
|
||||
@@ -176,7 +147,7 @@ func (l *UserEmailConfirmationLogic) UserEmailConfirmation(req *types.RequestEma
|
||||
}
|
||||
|
||||
event := wevent.NewWebsocketEventSuccess(wevent.UserResetToken, rt.TraceId)
|
||||
err = CommonNotify(l.svcCtx.Config.MainAddress, rt.Wid, event)
|
||||
err = wevent.CommonNotify(l.svcCtx.Config.MainAddress, rt.Wid, event)
|
||||
if err != nil {
|
||||
logx.Error(err, rt.TraceId)
|
||||
return resp.SetStatus(basic.CodeResetPasswordErr, err.Error())
|
||||
|
||||
@@ -50,7 +50,7 @@ func (l *UserResetPasswordHtmlLogic) UserResetPasswordHtml(req *types.RequestUse
|
||||
func (l *UserResetPasswordHtmlLogic) AfterLogic(w http.ResponseWriter, r *http.Request, resp *basic.Response) {
|
||||
|
||||
err := tpls.ExecuteTemplate(w, "reset_confirm.tpl", map[string]string{
|
||||
"HomePage": "http://www.fusen.3718.cn",
|
||||
"HomePage": l.svcCtx.Config.HomePage,
|
||||
"ResetToken": l.ResetToken,
|
||||
"ResetPasswordLink": l.svcCtx.Config.MainAddress + "/api/auth/reset/password",
|
||||
})
|
||||
|
||||
@@ -51,7 +51,7 @@ func (l *UserResetPasswordLogic) UserResetPassword(req *types.RequestUserResetPa
|
||||
}
|
||||
|
||||
if time.Since(rt.CreateAt) > 30*time.Minute {
|
||||
return resp.SetStatusWithMessage(basic.CodeOAuthConfirmationTimeoutErr, "Verification links expire after 30 minute.")
|
||||
return resp.SetStatusWithMessage(basic.CodeOAuthConfirmationTimeoutErr, "verification links expire after 30 minute.")
|
||||
}
|
||||
|
||||
err = l.svcCtx.AllModels.FsUser.Transaction(l.ctx, func(tx *gorm.DB) error {
|
||||
@@ -67,7 +67,7 @@ func (l *UserResetPasswordLogic) UserResetPassword(req *types.RequestUserResetPa
|
||||
}
|
||||
|
||||
if *user.PasswordHash == req.NewPassword {
|
||||
return fmt.Errorf("the password is the same as the old one. It needs to be changed")
|
||||
return fmt.Errorf("the password is the same as the old one. it needs to be changed")
|
||||
}
|
||||
|
||||
return tx.Where("id = ?", rt.UserId).Update("PasswordHash", req.NewPassword).Error
|
||||
@@ -79,7 +79,7 @@ func (l *UserResetPasswordLogic) UserResetPassword(req *types.RequestUserResetPa
|
||||
}
|
||||
|
||||
event := wevent.NewWebsocketEventSuccess(wevent.UserResetToken, rt.TraceId)
|
||||
err = CommonNotify(l.svcCtx.Config.WebsocketAddr, rt.Wid, event)
|
||||
err = wevent.CommonNotify(l.svcCtx.Config.WebsocketAddr, rt.Wid, event)
|
||||
if err != nil {
|
||||
logx.Error(err, rt.TraceId)
|
||||
return resp.SetStatusWithMessage(basic.CodeResetPasswordErr, err.Error())
|
||||
|
||||
Reference in New Issue
Block a user