template-monorepo/internal/model/notification/provider/email/ses_sender.go

103 lines
2.4 KiB
Go
Raw Permalink Normal View History

package email
import (
"context"
"fmt"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/ses"
"github.com/aws/aws-sdk-go-v2/service/ses/types"
)
// SESSettings configures AWS SES (ported from app-cloudep-notification-service).
type SESSettings struct {
Sort int
Region string
AccessKey string
SecretKey string
SessionToken string
SendTimeout time.Duration
}
// SESSender delivers email via AWS SES.
type SESSender struct {
name string
sort int
client *ses.Client
timeout time.Duration
}
// NewSESSender builds an SES provider.
func NewSESSender(cfg SESSettings) (*SESSender, error) {
if cfg.Region == "" {
return nil, fmt.Errorf("email ses: region is required")
}
if cfg.AccessKey == "" || cfg.SecretKey == "" {
return nil, fmt.Errorf("email ses: access key and secret key are required")
}
timeout := cfg.SendTimeout
if timeout <= 0 {
timeout = 90 * time.Second
}
awsCfg := aws.Config{
Region: cfg.Region,
Credentials: credentials.NewStaticCredentialsProvider(
cfg.AccessKey,
cfg.SecretKey,
cfg.SessionToken,
),
}
return &SESSender{
name: "ses",
sort: cfg.Sort,
client: ses.NewFromConfig(awsCfg),
timeout: timeout,
}, nil
}
func (s *SESSender) Name() string { return s.name }
func (s *SESSender) Sort() int { return s.sort }
func (s *SESSender) Send(ctx context.Context, msg *Message) (string, error) {
if msg == nil || len(msg.To) == 0 {
return "", fmt.Errorf("email ses: message or recipients missing")
}
from := msg.From
if from == "" {
return "", fmt.Errorf("email ses: from address is required")
}
sendCtx, cancel := context.WithTimeout(ctx, s.timeout)
defer cancel()
out, err := s.client.SendEmail(sendCtx, &ses.SendEmailInput{
Source: aws.String(from),
Destination: &types.Destination{
ToAddresses: append([]string(nil), msg.To...),
},
Message: &types.Message{
Subject: &types.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(msg.Subject),
},
Body: &types.Body{
Html: &types.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(msg.Body),
},
},
},
})
if err != nil {
return "", fmt.Errorf("email ses: %w", err)
}
if out.MessageId == nil || *out.MessageId == "" {
return "", fmt.Errorf("email ses: empty message id")
}
return *out.MessageId, nil
}