├── .gitignore ├── README.md ├── bin └── build ├── main.go ├── sample-env.sh └── tests └── test.html /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mailgun mailing list API 2 | ======================== 3 | 4 | A simple API, written in Go, for subscribing new emails to a Mailgun mailing list. 5 | 6 | Usage 7 | ----- 8 | 9 | Set environment variables to configure the application: 10 | 11 | ```bash 12 | export MAILGUN_DOMAIN=mg.yourdomain.com 13 | export MAILGUN_API_KEY=key-abcdefghijkl 14 | export MAILGUN_MAILING_LIST=newsletter@mg.yourdomain.com 15 | export SUBSCRIBE_HTTP_PORT=8000 16 | export SUBSCRIBE_REDIRECT_URL=http://yourdomain.com/newsletter/success 17 | ``` 18 | 19 | Download and run the application: 20 | 21 | ``` 22 | curl -L https://github.com/amyboyd/go-mailgun-mailing-list-api/releases/download/v0.2.0/mailgun-mailing-list-api -o mailgun-mailing-list-api 23 | chmod +x mailgun-mailing-list-api 24 | ./mailgun-mailing-list-api 25 | ``` 26 | 27 | Add a form to your web page that submits the user's email address to the API: 28 | 29 | ```html 30 |
34 | ``` 35 | 36 | 37 | Monitoring 38 | ---------- 39 | 40 | If you want to set up monitoring, to check the API is running at all times, the API has a URL for that, `/health-check` 41 | -------------------------------------------------------------------------------- /bin/build: -------------------------------------------------------------------------------- 1 | set -o errexit 2 | set -o nounset 3 | 4 | TARGET=build/mailgun-mailing-list-api 5 | 6 | go build -o $TARGET 7 | 8 | echo "Executable has been created: $TARGET" 9 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | mailgun "github.com/mailgun/mailgun-go" 6 | "log" 7 | "net/http" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "unicode/utf8" 12 | ) 13 | 14 | type Config struct { 15 | Domain string 16 | ApiKey string 17 | MailingListAddress string 18 | HttpPort int 19 | RedirectUrlAfterSubscribe string 20 | } 21 | 22 | func main() { 23 | if len(os.Args) > 1 { 24 | command := os.Args[1] 25 | if command == "--help" { 26 | PrintHelp() 27 | } else { 28 | log.Fatal("unknown argument: " + command) 29 | } 30 | } else { 31 | RunApplication() 32 | } 33 | } 34 | 35 | func RunApplication() { 36 | config := CreateConfigFromEnv() 37 | 38 | PrintConfig(config) 39 | 40 | mg := mailgun.NewMailgun(config.Domain, config.ApiKey, "") 41 | 42 | StartHttpServer(mg, config) 43 | } 44 | 45 | func CreateConfigFromEnv() Config { 46 | httpPort, err := strconv.Atoi(GetConfigVarFromEnv("SUBSCRIBE_HTTP_PORT")) 47 | 48 | if err != nil { 49 | log.Fatal("HTTP port must be numeric, given: " + GetConfigVarFromEnv("SUBSCRIBE_HTTP_PORT")) 50 | } 51 | 52 | return Config{ 53 | Domain: GetConfigVarFromEnv("MAILGUN_DOMAIN"), 54 | ApiKey: GetConfigVarFromEnv("MAILGUN_API_KEY"), 55 | MailingListAddress: GetConfigVarFromEnv("MAILGUN_MAILING_LIST"), 56 | HttpPort: httpPort, 57 | RedirectUrlAfterSubscribe: GetConfigVarFromEnv("SUBSCRIBE_REDIRECT_URL"), 58 | } 59 | } 60 | 61 | func GetConfigVarFromEnv(envVar string) string { 62 | value := os.Getenv(envVar) 63 | 64 | if value == "" { 65 | log.Fatal("System environment not set: " + envVar) 66 | } 67 | 68 | return value 69 | } 70 | 71 | func PrintConfig(config Config) { 72 | fmt.Println("Running with configuration:") 73 | 74 | fmt.Println("Mailgun domain (from MAILGUN_DOMAIN):", config.Domain) 75 | 76 | // Redact the API key incase the output is redirected to a log file and, for example, sent 77 | // to a Kibana log server, where the users of the log server shouldn't know the private key. 78 | redactedApiKey := config.ApiKey[0:10] + strings.Repeat("*", utf8.RuneCountInString(config.ApiKey)-10) 79 | fmt.Println("Mailgun API key (from MAILGUN_API_KEY):", redactedApiKey) 80 | 81 | fmt.Println("Mailgun mailing list (from MAILGUN_MAILING_LIST):", config.MailingListAddress) 82 | 83 | fmt.Println("HTTP port (from SUBSCRIBE_HTTP_PORT):", config.HttpPort) 84 | 85 | fmt.Println("Redirect URL after subscribe (from SUBSCRIBE_REDIRECT_URL):", config.RedirectUrlAfterSubscribe) 86 | } 87 | 88 | func StartHttpServer(mg mailgun.Mailgun, config Config) { 89 | http.HandleFunc("/subscribe", func(response http.ResponseWriter, request *http.Request) { 90 | email := request.FormValue("email") 91 | 92 | newMember := mailgun.Member{ 93 | Address: email, 94 | Subscribed: mailgun.Subscribed, 95 | } 96 | 97 | mg.CreateMember(true, config.MailingListAddress, newMember) 98 | 99 | fmt.Println(email + " has been subscribed to the mailing list") 100 | 101 | http.Redirect(response, request, config.RedirectUrlAfterSubscribe, http.StatusTemporaryRedirect) 102 | }) 103 | 104 | http.HandleFunc("/health-check", func(response http.ResponseWriter, request *http.Request) { 105 | fmt.Fprintf(response, "Running") 106 | }) 107 | 108 | http.ListenAndServe(":"+strconv.Itoa(config.HttpPort), nil) 109 | } 110 | 111 | func PrintHelp() { 112 | fmt.Println("You can report issues at: https://github.com/amyboyd/go-mailgun-mailing-list-api/issues") 113 | fmt.Println("You can download the latest binary from: https://github.com/amyboyd/go-mailgun-mailing-list-api/releases") 114 | fmt.Println("The source code is available at: https://github.com/amyboyd/go-mailgun-mailing-list-api") 115 | } 116 | -------------------------------------------------------------------------------- /sample-env.sh: -------------------------------------------------------------------------------- 1 | export MAILGUN_DOMAIN=mg.yourdomain.com 2 | export MAILGUN_API_KEY=key-abcdefghijkl 3 | export MAILGUN_MAILING_LIST=newsletter@mg.yourdomain.com 4 | export SUBSCRIBE_HTTP_PORT=8000 5 | export SUBSCRIBE_REDIRECT_URL=http://yourdomain.com/newsletter/success 6 | -------------------------------------------------------------------------------- /tests/test.html: -------------------------------------------------------------------------------- 1 | 5 | --------------------------------------------------------------------------------