From 562a090174f91975b576de4cbc782e55c0c4b7e0 Mon Sep 17 00:00:00 2001 From: harryzcy Date: Sun, 28 May 2023 10:30:34 +0800 Subject: [PATCH] Support email received webhook (#271) --- functions/emailReceive/main.go | 16 +++++++++- internal/env/env.go | 2 ++ internal/webhook/webhook.go | 53 ++++++++++++++++++++++++++++++++ internal/webhook/webhook_test.go | 45 +++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 internal/webhook/webhook.go create mode 100644 internal/webhook/webhook_test.go diff --git a/functions/emailReceive/main.go b/functions/emailReceive/main.go index 722b0523..5416574c 100644 --- a/functions/emailReceive/main.go +++ b/functions/emailReceive/main.go @@ -19,6 +19,7 @@ import ( "github.com/harryzcy/mailbox/internal/env" "github.com/harryzcy/mailbox/internal/thread" "github.com/harryzcy/mailbox/internal/util/format" + "github.com/harryzcy/mailbox/internal/webhook" ) func main() { @@ -108,7 +109,20 @@ func receiveEmail(ctx context.Context, ses events.SimpleEmailService) { Timestamp: ses.Mail.Timestamp.UTC().Format(time.RFC3339), }) if err != nil { - log.Fatalf("failed to send email receipt to SQS, %v", err) + log.Printf("failed to send email receipt to SQS, %v\n", err) + } + } + + if webhook.Enabled() { + err = webhook.SendWebhook(ctx, &webhook.Webhook{ + Event: webhook.EventEmail, + Action: webhook.ActionReceived, + Email: webhook.Email{ + ID: ses.Mail.MessageID, + }, + }) + if err != nil { + log.Printf("failed to send webhook, %v\n", err) } } } diff --git a/internal/env/env.go b/internal/env/env.go index 1aa70c2d..471cb6b7 100644 --- a/internal/env/env.go +++ b/internal/env/env.go @@ -11,4 +11,6 @@ var ( GsiIndexName = os.Getenv("DYNAMODB_TIME_INDEX") S3Bucket = os.Getenv("S3_BUCKET") QueueName = os.Getenv("SQS_QUEUE") + + WebhookURL = os.Getenv("WEBHOOK_URL") ) diff --git a/internal/webhook/webhook.go b/internal/webhook/webhook.go new file mode 100644 index 00000000..397ddbb7 --- /dev/null +++ b/internal/webhook/webhook.go @@ -0,0 +1,53 @@ +package webhook + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + "time" + + "github.com/harryzcy/mailbox/internal/env" +) + +const ( + EventEmail = "email" + ActionReceived = "received" +) + +type Webhook struct { + Event string `json:"event"` + Action string `json:"action"` + Email Email +} + +type Email struct { + ID string `json:"id"` // message id +} + +func Enabled() bool { + return env.WebhookURL != "" +} + +func SendWebhook(ctx context.Context, data *Webhook) error { + client := http.Client{ + Timeout: 5 * time.Second, + } + + body, err := json.Marshal(data) + if err != nil { + return err + } + req, err := http.NewRequestWithContext(ctx, "POST", env.WebhookURL, bytes.NewReader(body)) + if err != nil { + return err + } + + res, err := client.Do(req) + if err != nil { + return err + } + defer res.Body.Close() + + return nil +} diff --git a/internal/webhook/webhook_test.go b/internal/webhook/webhook_test.go new file mode 100644 index 00000000..d9d0bd48 --- /dev/null +++ b/internal/webhook/webhook_test.go @@ -0,0 +1,45 @@ +package webhook + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/harryzcy/mailbox/internal/env" + "github.com/stretchr/testify/assert" +) + +func TestSendWebhook(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + var webhook Webhook + err := json.NewDecoder(req.Body).Decode(&webhook) + assert.Nil(t, err) + assert.Equal(t, EventEmail, webhook.Event) + assert.Equal(t, ActionReceived, webhook.Action) + assert.Equal(t, "123", webhook.Email.ID) + + _, err = rw.Write([]byte("OK")) + assert.Nil(t, err) + })) + defer server.Close() + + env.WebhookURL = server.URL + err := SendWebhook(context.Background(), &Webhook{ + Event: EventEmail, + Action: ActionReceived, + Email: Email{ID: "123"}, + }) + assert.Nil(t, err) +} + +func TestSendWebhook_Error(t *testing.T) { + env.WebhookURL = "" + err := SendWebhook(context.Background(), &Webhook{ + Event: EventEmail, + Action: ActionReceived, + Email: Email{ID: "123"}, + }) + assert.NotNil(t, err) +}