mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-19 08:01:16 +00:00
143 lines
3.6 KiB
Go
143 lines
3.6 KiB
Go
package tasks
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/getsentry/sentry-go"
|
|
"github.com/google/uuid"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
"github.com/warmbly/warmbly/internal/tasks/proto"
|
|
)
|
|
|
|
func (s *tasksService) HandleUserEmailTask(task *proto.ProcessTask) *errx.Error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
|
defer cancel()
|
|
|
|
// STEP 1: Parse task ID
|
|
taskID, err := uuid.Parse(task.TaskId)
|
|
if err != nil {
|
|
sentry.CaptureException(err)
|
|
return errx.New(errx.BadRequest, "invalid task ID")
|
|
}
|
|
|
|
executionKey := "user-email:" + taskID.String()
|
|
executionStatus := "failed"
|
|
if s.advanced != nil {
|
|
duplicate, xerr := s.advanced.StartTaskExecution(ctx, taskID, executionKey, map[string]interface{}{
|
|
"task_type": "user_email",
|
|
})
|
|
if xerr != nil {
|
|
return xerr
|
|
}
|
|
if duplicate {
|
|
return nil
|
|
}
|
|
defer func() {
|
|
_ = s.advanced.CompleteTaskExecution(ctx, taskID, executionKey, executionStatus, map[string]interface{}{
|
|
"task_type": "user_email",
|
|
})
|
|
}()
|
|
}
|
|
|
|
// STEP 2: Load task record
|
|
taskRecord, err := s.taskRepo.GetTask(ctx, taskID)
|
|
if err != nil {
|
|
sentry.CaptureException(err)
|
|
return errx.InternalError()
|
|
}
|
|
|
|
if taskRecord == nil {
|
|
return errx.ErrNotFound
|
|
}
|
|
|
|
// STEP 3: Load email_task record (with new columns)
|
|
emailTask, err := s.taskRepo.GetEmailTask(ctx, taskID)
|
|
if err != nil {
|
|
sentry.CaptureException(err)
|
|
return errx.InternalError()
|
|
}
|
|
|
|
if emailTask == nil {
|
|
return errx.ErrNotFound
|
|
}
|
|
|
|
// STEP 4: Load email account
|
|
account, xerr := s.emailRepo.GetByID(ctx, taskRecord.EmailAccountID)
|
|
if xerr != nil {
|
|
return xerr
|
|
}
|
|
|
|
// STEP 5: Mark task as active (with advisory lock)
|
|
if err := s.taskRepo.UpdateTaskStatusWithLock(ctx, taskID, "active"); err != nil {
|
|
sentry.CaptureException(err)
|
|
return errx.InternalError()
|
|
}
|
|
|
|
// STEP 6: Add signature if SignatureSync
|
|
bodyHTML := emailTask.BodyHTML
|
|
bodyPlain := emailTask.BodyPlain
|
|
|
|
if account.SignatureSync {
|
|
if bodyHTML != "" {
|
|
bodyHTML = AddSignature(bodyHTML, account.SignatureHTML, true)
|
|
}
|
|
if bodyPlain != "" {
|
|
bodyPlain = AddSignature(bodyPlain, account.SignaturePlain, false)
|
|
}
|
|
}
|
|
|
|
// STEP 7: Generate Message-ID
|
|
messageID := generateMessageID(account.Email)
|
|
|
|
// STEP 8: Build InReplyTo string
|
|
var inReplyTo string
|
|
if len(emailTask.InReplyTo) > 0 {
|
|
inReplyTo = emailTask.InReplyTo[0]
|
|
}
|
|
|
|
// STEP 9: Build EmailMessage and send via worker
|
|
emailMsg := EmailMessage{
|
|
From: account.Email,
|
|
To: emailTask.To,
|
|
CC: emailTask.CC,
|
|
BCC: emailTask.BCC,
|
|
Subject: emailTask.Subject,
|
|
BodyHTML: bodyHTML,
|
|
BodyPlain: bodyPlain,
|
|
InReplyTo: inReplyTo,
|
|
MessageID: messageID,
|
|
IsWarmup: false,
|
|
Tracking: nil,
|
|
}
|
|
|
|
if err := s.emailSender.Send(ctx, taskID, emailMsg, *account); err != nil {
|
|
s.taskRepo.RecordTaskFailure(ctx, taskID, "Send failed", err.Error())
|
|
if s.advanced != nil {
|
|
_ = s.advanced.CaptureTaskDeadLetter(ctx, taskID, "user_email", map[string]interface{}{
|
|
"account_id": account.ID.String(),
|
|
"to": emailTask.To,
|
|
}, err.Error(), 1)
|
|
_ = s.taskRepo.UpdateTaskStatus(ctx, taskID, "dead_lettered")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// STEP 10: Update task record
|
|
taskRecord.MessageID = messageID
|
|
taskRecord.Status = "completed"
|
|
|
|
// STEP 11: Mark task completed (with advisory lock)
|
|
if err := s.taskRepo.UpdateTaskStatusWithLock(ctx, taskID, "completed"); err != nil {
|
|
sentry.CaptureException(err)
|
|
return errx.InternalError()
|
|
}
|
|
|
|
// STEP 12: Publish events
|
|
log.Info().Str("task_id", task.TaskId).Str("account_id", account.ID.String()).Msg("user email sent")
|
|
|
|
executionStatus = "completed"
|
|
return nil
|
|
}
|