ultisuite-backend/internal/api/mail/outbox_scheduling.go
R3D347HR4Y 65fc9e517a Implement outbox management features with scheduling and attachment support
- Added new API endpoints for sending, rescheduling, and canceling scheduled outbox messages.
- Implemented outbox processing logic to handle attachments and manage message statuses.
- Introduced a dead-letter strategy for failed outbox messages, enhancing reliability.
- Updated database schema to support new outbox statuses and dead-letter entries.
- Enhanced unit tests for outbox functionalities, ensuring robust error handling and validation.
- Improved attachment handling in the outbox processor to support inline and regular attachments.
2026-05-22 17:46:30 +02:00

74 lines
2.0 KiB
Go

package mail
import (
"context"
"errors"
"time"
"github.com/jackc/pgx/v5"
)
var ErrInvalidOutboxStatus = errors.New("invalid outbox status")
func (s *Service) SendOutboxNow(ctx context.Context, userID, outboxID string) (string, error) {
var status string
err := s.db.QueryRow(ctx, `
UPDATE outbox SET status = 'queued', scheduled_at = NULL, updated_at = NOW()
WHERE id = $1 AND user_id = $2 AND status = 'scheduled'
RETURNING status
`, outboxID, userID).Scan(&status)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return "", s.outboxActionError(ctx, userID, outboxID)
}
return "", err
}
return status, nil
}
func (s *Service) RescheduleOutbox(ctx context.Context, userID, outboxID string, scheduledAt time.Time) (string, error) {
var status string
err := s.db.QueryRow(ctx, `
UPDATE outbox SET status = 'scheduled', scheduled_at = $3, updated_at = NOW()
WHERE id = $1 AND user_id = $2 AND status IN ('scheduled', 'queued')
RETURNING status
`, outboxID, userID, scheduledAt).Scan(&status)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return "", s.outboxActionError(ctx, userID, outboxID)
}
return "", err
}
return status, nil
}
func (s *Service) CancelScheduledOutbox(ctx context.Context, userID, outboxID string) (string, error) {
var status string
err := s.db.QueryRow(ctx, `
UPDATE outbox SET status = 'cancelled', scheduled_at = NULL, updated_at = NOW()
WHERE id = $1 AND user_id = $2 AND status = 'scheduled'
RETURNING status
`, outboxID, userID).Scan(&status)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return "", s.outboxActionError(ctx, userID, outboxID)
}
return "", err
}
return status, nil
}
func (s *Service) outboxActionError(ctx context.Context, userID, outboxID string) error {
var status string
err := s.db.QueryRow(ctx, `
SELECT status FROM outbox WHERE id = $1 AND user_id = $2
`, outboxID, userID).Scan(&status)
if errors.Is(err, pgx.ErrNoRows) {
return ErrNotFound
}
if err != nil {
return err
}
return ErrInvalidOutboxStatus
}