diff --git a/.env.dist b/.env.dist index 984f04a..2e2711d 100644 --- a/.env.dist +++ b/.env.dist @@ -1,6 +1,6 @@ SERVER_HOST= SERVER_PORT=8080 SERVER_SHUTDOWN_TIMEOUT=5 -HTTP_TRACE_HEADER=X-Amzn-Trace-Id +TRACEPARENT_HEADER=traceparent SMTP_ADDR=smtp:1025 LOG_LEVEL=debug diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d41a940 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.gif filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 4c49bd7..87a669b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .env +coverage.txt diff --git a/README.md b/README.md index 9b6e664..c499328 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,46 @@ -# HTTP to SMTP +# 📩 HTTP to SMTP -[![docker image](https://img.shields.io/docker/v/eexit/http2smtp?label=docker-image&sort=date)](https://hub.docker.com/repository/docker/eexit/http2smtp) [![ci](https://github.com/eexit/http2smtp/workflows/build/badge.svg)](https://github.com/eexit/http2smtp/actions) [![codecov](https://codecov.io/gh/eexit/http2smtp/branch/master/graph/badge.svg?token=XH18EYLDLZ)](https://codecov.io/gh/eexit/http2smtp) +[![version](https://img.shields.io/github/v/tag/eexit/http2smtp?label=version&logo=github&sort=semver)](https://github.com/eexit/http2smtp/releases) [![docker pull](https://img.shields.io/docker/pulls/eexit/http2smtp)](https://hub.docker.com/repository/docker/eexit/http2smtp) [![ci](https://github.com/eexit/http2smtp/workflows/build/badge.svg)](https://github.com/eexit/http2smtp/actions) [![codecov](https://codecov.io/gh/eexit/http2smtp/branch/master/graph/badge.svg?token=XH18EYLDLZ)](https://codecov.io/gh/eexit/http2smtp) [![license](https://img.shields.io/github/license/eexit/http2smtp)](https://github.com/eexit/http2smtp/blob/master/LICENSE) -This small app allows to connect any HTTP-based vendor mailer to a SMTP server. Developped because of the lack of capability to test email sending thru APIs. +An API proxies HTTP-backed vendor mailer calls to SMTP. -### Supported vendors - -- [SparkPost RFC 822 transmission](https://developers.sparkpost.com/api/transmissions/#transmissions-post-send-rfc822-content) +Plug a MailHog or MailCatcher to API email sending vendors such as SparkPost, MailGun or Twilio SendGrid for testing purposes. ## Usage +See [examples](examples). + +:zap: ProTip: for tracing purposes, this app kinda supports [W3C Trace Context recommendation](https://www.w3.org/TR/trace-context/). Configure the env var `TRACEPARENT_HEADER` and inject any trace into this header value. All log entries will be contextualized with the given value. + ### Docker image 1. Checkout this repo or only copy the `.env.dist` and `docker-compose.yml` files 1. Rename `.env.dist` into `.env` -2. Update the values accordingly - -```bash -# Pull the images -docker-compose pull -# Up the stack -docker-compose up http2smtp -Creating http2smtp_smtp_1 ... done -Creating http2smtp_http2smtp_1 ... done -Attaching to http2smtp_http2smtp_1 -http2smtp_1 | {"level":"info","version":"v0.1.0+dev","time":"2021-01-03T22:32:08Z","message":"app is starting"} -http2smtp_1 | {"level":"info","version":"v0.1.0+dev","smtp":{"addr":"smtp:1025","id":"go:net/smtp"},"time":"2021-01-03T22:32:08Z","message":"dialing to smtp server"} -http2smtp_1 | {"level":"info","version":"v0.1.0+dev","time":"2021-01-03T22:32:08Z","message":"listening on http:8080"} -``` +1. Update the values accordingly +1. Pull images and run `docker-compose up http2smtp` ## Vendor endpoints -### SparkPost +### [SparkPost](https://developers.sparkpost.com/api/) #### Inline transmission -_Not supported yet._ +API documentation: https://developers.sparkpost.com/api/transmissions/#transmissions-post-send-inline-content + +_:warning: Not supported yet._ #### RFC 822 transmission -SparkPost documentation: https://developers.sparkpost.com/api/transmissions/#transmissions-post-send-rfc822-content +API documentation: https://developers.sparkpost.com/api/transmissions/#transmissions-post-send-rfc822-content POST /sparkpost/api/v1/transmissions -Basic validation is enforced, only the recipients list and the RFC 822 content are used. +Basic validation is enforced, only the recipients list email and the RFC 822 content are used and mandatory. + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Contributors +![contributors](https://contrib.rocks/image?repo=eexit/http2smtp) diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..0bab292 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,10 @@ +# Examples + +## Healthcheck + +![healthcheck.gif](healthcheck.gif) + +## SparkPost + +- [RFC 822 transmissions](sparkpost_rfc822.md) + diff --git a/examples/healthcheck.gif b/examples/healthcheck.gif new file mode 100644 index 0000000..cc2906a --- /dev/null +++ b/examples/healthcheck.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ce91b13cf25076ea83987b9bd18cfa36d384dc4298be811afb601c2869564164 +size 8374316 diff --git a/examples/sparkpost_rfc822.gif b/examples/sparkpost_rfc822.gif new file mode 100644 index 0000000..a234a85 --- /dev/null +++ b/examples/sparkpost_rfc822.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b58e063bf2e4a75df5170aca658189c20d7f530e4876ee84ed8a0d058dab8cc +size 35524868 diff --git a/examples/sparkpost_rfc822.json b/examples/sparkpost_rfc822.json new file mode 100644 index 0000000..e8ddb02 --- /dev/null +++ b/examples/sparkpost_rfc822.json @@ -0,0 +1,10 @@ +{ + "recipients":[ + { + "address":{"email":"bob@example.com"} + } + ], + "content":{ + "email_rfc822":"From: Test \nTo: Bob \nSubject: Hello world!\n\nHello world!" + } +} diff --git a/examples/sparkpost_rfc822.md b/examples/sparkpost_rfc822.md new file mode 100644 index 0000000..0a6caf1 --- /dev/null +++ b/examples/sparkpost_rfc822.md @@ -0,0 +1,37 @@ +# SparkPost RFC 822 + +API documentation: https://developers.sparkpost.com/api/transmissions/#transmissions-post-send-rfc822-content + +![sparkpost_rfc822.gif](sparkpost_rfc822.gif) + +Launch the API: + +```bash +docker-compose up http2smtp +Creating http2smtp_smtp_1 ... done +Creating http2smtp_http2smtp_1 ... done +Attaching to http2smtp_http2smtp_1 +http2smtp_1 | {"level":"info","version":"v0.1.0+dev","time":"2021-01-03T22:32:08Z","message":"app is starting"} +http2smtp_1 | {"level":"info","version":"v0.1.0+dev","smtp":{"addr":"smtp:1025","id":"go:net/smtp"},"time":"2021-01-03T22:32:08Z","message":"dialing to smtp server"} +http2smtp_1 | {"level":"info","version":"v0.1.0+dev","time":"2021-01-03T22:32:08Z","message":"listening on http:8080"} +``` + +Send the example request: + +```bash +http POST :8080/sparkpost/api/v1/transmissions traceparent:$(openssl rand -hex 16) < sparkpost_rfc822.json +``` + +Logs: + +```bash +http2smtp_1 | {"level":"info","version":"v0.1.0+dev","smtp":{"addr":"smtp:1025","id":"go:net/smtp"},"trace_id":"304dfb8a7fbcfbdb1db373da9e39354a","time":"2021-01-04T00:24:27Z","message":"sending message"} +http2smtp_1 | {"level":"debug","version":"v0.1.0+dev","smtp":{"addr":"smtp:1025","id":"go:net/smtp"},"trace_id":"304dfb8a7fbcfbdb1db373da9e39354a","tos":["bob@example.com"],"time":"2021-01-04T00:24:27Z","message":"executing transaction"} +http2smtp_1 | {"level":"debug","version":"v0.1.0+dev","smtp":{"addr":"smtp:1025","id":"go:net/smtp"},"trace_id":"304dfb8a7fbcfbdb1db373da9e39354a","from":"Test ","time":"2021-01-04T00:24:27Z","message":"sending MAIL FROM cmd"} +http2smtp_1 | {"level":"debug","version":"v0.1.0+dev","smtp":{"addr":"smtp:1025","id":"go:net/smtp"},"trace_id":"304dfb8a7fbcfbdb1db373da9e39354a","to":"bob@example.com","time":"2021-01-04T00:24:27Z","message":"sending RCPT cmd"} +http2smtp_1 | {"level":"debug","version":"v0.1.0+dev","smtp":{"addr":"smtp:1025","id":"go:net/smtp"},"trace_id":"304dfb8a7fbcfbdb1db373da9e39354a","time":"2021-01-04T00:24:27Z","message":"sending DATA cmd"} +http2smtp_1 | {"level":"debug","version":"v0.1.0+dev","smtp":{"addr":"smtp:1025","id":"go:net/smtp"},"trace_id":"304dfb8a7fbcfbdb1db373da9e39354a","data":"From: Test \nTo: Bob \nSubject: Hello world!\n\nHello world!","time":"2021-01-04T00:24:27Z","message":"writing data"} +http2smtp_1 | {"level":"debug","version":"v0.1.0+dev","smtp":{"addr":"smtp:1025","id":"go:net/smtp"},"trace_id":"304dfb8a7fbcfbdb1db373da9e39354a","tos":["bob@example.com"],"time":"2021-01-04T00:24:27Z","message":"transaction executed"} +http2smtp_1 | {"level":"info","version":"v0.1.0+dev","smtp":{"addr":"smtp:1025","id":"go:net/smtp"},"trace_id":"304dfb8a7fbcfbdb1db373da9e39354a","accepted":1,"time":"2021-01-04T00:24:27Z","message":"message sent"} +http2smtp_1 | {"level":"info","version":"v0.1.0+dev","trace_id":"304dfb8a7fbcfbdb1db373da9e39354a","verb":"POST","ip":"172.24.0.1","user_agent":"HTTPie/2.3.0","url":"/sparkpost/api/v1/transmissions","code":201,"size":97,"duration":3.273861,"time":"2021-01-04T00:24:27Z","message":"served request"} +``` diff --git a/internal/env/env.go b/internal/env/env.go index bdd06d2..4d1a72c 100644 --- a/internal/env/env.go +++ b/internal/env/env.go @@ -12,7 +12,7 @@ type Bag struct { // HTTPTraceHeader is a header that provides a unique per-request ID that will be // injected into all logs entries related to the same request. This is used to track // the a request trace within the underlying services - HTTPTraceHeader string `envconfig:"HTTP_TRACE_HEADER"` + HTTPTraceHeader string `envconfig:"TRACEPARENT_HEADER" default:"traceparent"` // SMTPAddr is the hostname:port config of the SMTP server the app forwards emails to SMTPAddr string `envconfig:"SMTP_ADDR" required:"true"` // LogLevel is the level of log generated by the app