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 }