Replace AWS SES with Resend for email delivery
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
276fddc467
commit
4841986e0b
4 changed files with 33 additions and 43 deletions
|
|
@ -49,8 +49,9 @@ func main() {
|
|||
BaseDomain: getEnv("BASE_DOMAIN", "crimata.com"),
|
||||
})
|
||||
|
||||
notifyClient := notify.NewClient(awsCfg, notify.Config{
|
||||
FromEmail: getEnv("SES_FROM_EMAIL", "noreply@crimata.com"),
|
||||
notifyClient := notify.NewClient(notify.Config{
|
||||
ApiKey: mustEnv("RESEND_API_KEY"),
|
||||
FromEmail: getEnv("MAILER_FROM", "hello@crimata.com"),
|
||||
BaseDomain: getEnv("BASE_DOMAIN", "crimata.com"),
|
||||
})
|
||||
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -8,10 +8,10 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.151.0
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.40.0
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0
|
||||
github.com/aws/aws-sdk-go-v2/service/ses v1.22.0
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.49.0
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/resend/resend-go/v2 v2.28.0
|
||||
golang.org/x/crypto v0.48.0
|
||||
)
|
||||
|
||||
|
|
|
|||
11
go.sum
11
go.sum
|
|
@ -30,8 +30,6 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.40.0 h1:MRriK+ntpKpUc8RwcYJbc5W/
|
|||
github.com/aws/aws-sdk-go-v2/service/route53 v1.40.0/go.mod h1:n6oZO1BbhPw2X46ObAjn8ol00kujRT+Y+Q9AnbrRUe0=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0 h1:r3o2YsgW9zRcIP3Q0WCmttFVhTuugeKIvT5z9xDspc0=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.0/go.mod h1:w2E4f8PUfNtyjfL6Iu+mWI96FGttE03z3UdNcUEC4tA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ses v1.22.0 h1:1HJMg3WpV9L003TOhH8eEQgkPnegOwCE3/j0Iqlmt0U=
|
||||
github.com/aws/aws-sdk-go-v2/service/ses v1.22.0/go.mod h1:9MbVy83ucnbP+4D9lky3IeUBxjECR2bHq63yzkl+WKU=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.49.0 h1:EtNvvxv0m6aP4cbTyo43vBRXeTpyt8juyNPmgKSTyYs=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.49.0/go.mod h1:wzPAvA+afHPFlAMkCf80sg7bm7GbCuFX1INetlm9DAk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.19.0 h1:u6OkVDxtBPnxPkZ9/63ynEe+8kHbtS5IfaC4PzVxzWM=
|
||||
|
|
@ -42,8 +40,9 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.27.0 h1:cjTRjh700H36MQ8M0LnDn33W3Jmw
|
|||
github.com/aws/aws-sdk-go-v2/service/sts v1.27.0/go.mod h1:nXfOBMWPokIbOY+Gi7a1psWMSvskUCemZzI+SMB7Akc=
|
||||
github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
|
||||
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
|
|
@ -56,7 +55,11 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
|||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/resend/resend-go/v2 v2.28.0 h1:ttM1/VZR4fApBv3xI1TneSKi1pbfFsVrq7fXFlHKtj4=
|
||||
github.com/resend/resend-go/v2 v2.28.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
|
|
@ -66,3 +69,5 @@ golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
|
|
@ -4,63 +4,47 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ses"
|
||||
sestypes "github.com/aws/aws-sdk-go-v2/service/ses/types"
|
||||
resend "github.com/resend/resend-go/v2"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ApiKey string
|
||||
FromEmail string
|
||||
BaseDomain string
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
ses *ses.Client
|
||||
resend *resend.Client
|
||||
cfg Config
|
||||
}
|
||||
|
||||
func NewClient(awsCfg aws.Config, cfg Config) *Client {
|
||||
func NewClient(cfg Config) *Client {
|
||||
return &Client{
|
||||
ses: ses.NewFromConfig(awsCfg),
|
||||
resend: resend.NewClient(cfg.ApiKey),
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) SendWelcome(ctx context.Context, email, slug, password string) error {
|
||||
func (c *Client) SendWelcome(_ context.Context, email, slug, password string) error {
|
||||
url := fmt.Sprintf("https://%s.%s", slug, c.cfg.BaseDomain)
|
||||
subject := "Your Crimata hub is ready"
|
||||
text := fmt.Sprintf("Your hub is live at %s\n\nPassword: %s\n\nChange your password in Settings after logging in.\n\n— Crimata", url, password)
|
||||
html := fmt.Sprintf(`<p>Your hub is live at <a href="%s">%s</a></p><p><strong>Password:</strong> <code>%s</code></p><p style="color:#555">Change your password in Settings after logging in.</p><p>— Crimata</p>`, url, url, password)
|
||||
|
||||
_, err := c.ses.SendEmail(ctx, &ses.SendEmailInput{
|
||||
Source: aws.String(c.cfg.FromEmail),
|
||||
Destination: &sestypes.Destination{ToAddresses: []string{email}},
|
||||
Message: &sestypes.Message{
|
||||
Subject: &sestypes.Content{Data: aws.String(subject)},
|
||||
Body: &sestypes.Body{
|
||||
Text: &sestypes.Content{Data: aws.String(text)},
|
||||
Html: &sestypes.Content{Data: aws.String(html)},
|
||||
},
|
||||
},
|
||||
_, err := c.resend.Emails.Send(&resend.SendEmailRequest{
|
||||
From: c.cfg.FromEmail,
|
||||
To: []string{email},
|
||||
Subject: "Your Crimata hub is ready",
|
||||
Text: fmt.Sprintf("Your hub is live at %s\n\nUsername: %s\nPassword: %s\n\nChange your password in Settings after logging in.\n\n— Crimata", url, slug, password),
|
||||
Html: fmt.Sprintf(`<p>Your hub is live at <a href="%s">%s</a></p><p><strong>Username:</strong> <code>%s</code><br><strong>Password:</strong> <code>%s</code></p><p style="color:#555">Change your password in Settings after logging in.</p><p>— Crimata</p>`, url, url, slug, password),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) SendDataExport(ctx context.Context, email, downloadURL string) error {
|
||||
subject := "Your Crimata data export is ready"
|
||||
text := fmt.Sprintf("Your data export is ready for download:\n\n%s\n\nThis link expires in 24 hours.\n\n— Crimata", downloadURL)
|
||||
html := fmt.Sprintf(`<p>Your data export is ready: <a href="%s">Download</a></p><p style="color:#555">This link expires in 24 hours.</p><p>— Crimata</p>`, downloadURL)
|
||||
|
||||
_, err := c.ses.SendEmail(ctx, &ses.SendEmailInput{
|
||||
Source: aws.String(c.cfg.FromEmail),
|
||||
Destination: &sestypes.Destination{ToAddresses: []string{email}},
|
||||
Message: &sestypes.Message{
|
||||
Subject: &sestypes.Content{Data: aws.String(subject)},
|
||||
Body: &sestypes.Body{
|
||||
Text: &sestypes.Content{Data: aws.String(text)},
|
||||
Html: &sestypes.Content{Data: aws.String(html)},
|
||||
},
|
||||
},
|
||||
func (c *Client) SendDataExport(_ context.Context, email, downloadURL string) error {
|
||||
_, err := c.resend.Emails.Send(&resend.SendEmailRequest{
|
||||
From: c.cfg.FromEmail,
|
||||
To: []string{email},
|
||||
Subject: "Your Crimata data export is ready",
|
||||
Text: fmt.Sprintf("Your data export is ready for download:\n\n%s\n\nThis link expires in 24 hours.\n\n— Crimata", downloadURL),
|
||||
Html: fmt.Sprintf(`<p>Your data export is ready: <a href="%s">Download</a></p><p style="color:#555">This link expires in 24 hours.</p><p>— Crimata</p>`, downloadURL),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue