package emailLogic import ( "bytes" "context" "encoding/json" "strings" "text/template" "time" "github.com/perfect-panel/server/pkg/logger" "github.com/hibiken/asynq" "github.com/perfect-panel/server/internal/model/log" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/pkg/email" "github.com/perfect-panel/server/queue/types" ) type SendEmailLogic struct { svcCtx *svc.ServiceContext } func NewSendEmailLogic(svcCtx *svc.ServiceContext) *SendEmailLogic { return &SendEmailLogic{ svcCtx: svcCtx, } } func (l *SendEmailLogic) ProcessTask(ctx context.Context, task *asynq.Task) error { var payload types.SendEmailPayload if err := json.Unmarshal(task.Payload(), &payload); err != nil { logger.WithContext(ctx).Error("[SendEmailLogic] Unmarshal payload failed", logger.Field("error", err.Error()), logger.Field("payload", task.Payload()), ) return nil } messageLog := log.Message{ Platform: l.svcCtx.Config.Email.Platform, To: payload.Email, Subject: payload.Subject, Content: payload.Content, } sender, err := email.NewSender(l.svcCtx.Config.Email.Platform, l.svcCtx.Config.Email.PlatformConfig, l.svcCtx.Config.Site.SiteName) if err != nil { logger.WithContext(ctx).Error("[SendEmailLogic] NewSender failed", logger.Field("error", err.Error())) return nil } var content string switch payload.Type { case types.EmailTypeVerify: tplStr := l.svcCtx.Config.Email.VerifyEmailTemplate // Use int for better template compatibility if t, ok := payload.Content["Type"].(float64); ok { payload.Content["Type"] = int(t) } else if t, ok := payload.Content["Type"].(int); ok { payload.Content["Type"] = t } typeVal, _ := payload.Content["Type"].(int) // Smart Fallback: If template is empty OR (Type is 4 but template doesn't support it), use default // We check for "Type 4" or "Type eq 4" string in the template as a heuristic needDefault := tplStr == "" if !needDefault && typeVal == 4 && !strings.Contains(tplStr, "Type 4") && !strings.Contains(tplStr, "Type eq 4") { logger.WithContext(ctx).Infow("[SendEmailLogic] Configured template might not support DeleteAccount (Type 4), forcing default template") needDefault = true } if needDefault { tplStr = email.DefaultEmailVerifyTemplate } tpl, _ := template.New("verify").Parse(tplStr) var result bytes.Buffer err = tpl.Execute(&result, payload.Content) if err != nil { logger.WithContext(ctx).Error("[SendEmailLogic] Execute template failed", logger.Field("error", err.Error()), logger.Field("data", payload.Content), ) return nil } content = result.String() case types.EmailTypeMaintenance: tplStr := l.svcCtx.Config.Email.MaintenanceEmailTemplate if tplStr == "" { tplStr = email.DefaultMaintenanceEmailTemplate } tpl, _ := template.New("maintenance").Parse(tplStr) var result bytes.Buffer err = tpl.Execute(&result, payload.Content) if err != nil { logger.WithContext(ctx).Error("[SendEmailLogic] Execute template failed", logger.Field("error", err.Error()), logger.Field("template", l.svcCtx.Config.Email.MaintenanceEmailTemplate), logger.Field("data", payload.Content), ) return nil } content = result.String() case types.EmailTypeExpiration: tplStr := l.svcCtx.Config.Email.ExpirationEmailTemplate if tplStr == "" { tplStr = email.DefaultExpirationEmailTemplate } tpl, _ := template.New("expiration").Parse(tplStr) var result bytes.Buffer err = tpl.Execute(&result, payload.Content) if err != nil { logger.WithContext(ctx).Error("[SendEmailLogic] Execute template failed", logger.Field("error", err.Error()), logger.Field("template", l.svcCtx.Config.Email.ExpirationEmailTemplate), logger.Field("data", payload.Content), ) return nil } content = result.String() case types.EmailTypeTrafficExceed: tplStr := l.svcCtx.Config.Email.TrafficExceedEmailTemplate if tplStr == "" { tplStr = email.DefaultTrafficExceedEmailTemplate } tpl, _ := template.New("traffic_exceed").Parse(tplStr) var result bytes.Buffer err = tpl.Execute(&result, payload.Content) if err != nil { logger.WithContext(ctx).Error("[SendEmailLogic] Execute template failed", logger.Field("error", err.Error()), logger.Field("template", l.svcCtx.Config.Email.TrafficExceedEmailTemplate), logger.Field("data", payload.Content), ) return nil } content = result.String() case types.EmailTypeCustom: if payload.Content == nil { logger.WithContext(ctx).Error("[SendEmailLogic] Custom email content is empty", logger.Field("payload", payload), ) return nil } if tpl, ok := payload.Content["content"].(string); !ok { logger.WithContext(ctx).Error("[SendEmailLogic] Custom email content is not a string", logger.Field("payload", payload), ) return nil } else { content = tpl } default: logger.WithContext(ctx).Error("[SendEmailLogic] Unsupported email type", logger.Field("type", payload.Type), logger.Field("payload", payload), ) return nil } err = sender.Send([]string{payload.Email}, payload.Subject, content) if err != nil { logger.WithContext(ctx).Error("[SendEmailLogic] Send email failed", logger.Field("error", err.Error())) return nil } messageLog.Status = 1 emailLog, err := messageLog.Marshal() if err != nil { logger.WithContext(ctx).Error("[SendEmailLogic] Marshal message log failed", logger.Field("error", err.Error()), logger.Field("messageLog", messageLog), ) return nil } if err = l.svcCtx.LogModel.Insert(ctx, &log.SystemLog{ Type: log.TypeEmailMessage.Uint8(), Date: time.Now().Format("2006-01-02"), ObjectID: 0, Content: string(emailLog), }); err != nil { logger.WithContext(ctx).Error("[SendEmailLogic] Insert email log failed", logger.Field("error", err.Error()), logger.Field("emailLog", string(emailLog)), ) return nil } return nil }