From ed872ddfa3941e5602a6dcd91bb1952e75025a1b Mon Sep 17 00:00:00 2001 From: Jack Punter Date: Tue, 23 Apr 2024 20:48:46 +0100 Subject: [PATCH] Add buttons to actually send emails either the test address or mailing list --- main.go | 107 +++++++++++++++++++++++++++++++------------ postmark.go | 60 ++++++++++++++++++++++++ static/css/site.css | 5 ++ templates/index.html | 19 ++++++-- 4 files changed, 159 insertions(+), 32 deletions(-) diff --git a/main.go b/main.go index d8b756f..73dfbd7 100644 --- a/main.go +++ b/main.go @@ -45,11 +45,6 @@ type ApiConfig struct { Postmark PostmarkConfig } -// ------------- HMC Api Responses ------------- -type EmailsResonse struct { - Emails []string `json:"emails"` -} - // Load the HMCsend-email app.ini and store the values in a structure, some of the keys in here // we probably don't actualy care about for this usage. func loadConfig() ApiConfig { @@ -89,6 +84,47 @@ func loadConfig() ApiConfig { } } +func ParseArguments(rawArgs string) map[string]string { + pairs := strings.Split(rawArgs, "&") + result := make(map[string]string) + for _, pair := range pairs { + key_val := strings.Split(pair, "=") + if len(key_val) != 2 { + log.Fatalf("Cannot parse argument %v\n", pair) + } + result[key_val[0]] = key_val[1] + } + return result +} + +func GetMailingList(cfg ApiConfig) []string { + type EmailsResonse struct { + Emails []string `json:"emails"` + } + body := []byte(fmt.Sprintf("{\"city\": \"%s\"}", cfg.Hmc.City)) + req, err := http.NewRequest("POST", cfg.Hmc.ApiUrl, bytes.NewBuffer(body)) + if err != nil { + panic(err) + } + + req.Header.Add("Content-Type", "application/json") + req.Header.Add("Authorization", cfg.Hmc.ApiSecret) + + client := &http.Client{} + res, err := client.Do(req) + if err != nil { + panic(err) + } + defer res.Body.Close() + + response := &EmailsResonse{} + decode_error := json.NewDecoder(res.Body).Decode(&response) + if decode_error != nil { + panic(decode_error) + } + return response.Emails +} + func main() { apiConfig := loadConfig() postmarkTemplate := getPostmarkTemplate(apiConfig.Postmark) @@ -102,25 +138,25 @@ func main() { // The base endpoint for the web-app simply renders the index tempalte mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - log.Printf("Hello '%v %v' handler", r.Method, r.URL) + log.Printf("%v %v", r.Method, r.URL) templates.ExecuteTemplate(w, "index", nil) }) // Endpoint called when editing the text area and posts to the postmark api to generate the email preview mux.HandleFunc("/mail-content", func(w http.ResponseWriter, r *http.Request) { - log.Printf("Hello '%v %v' handler", r.Method, r.URL) + log.Printf("%v %v", r.Method, r.URL) body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer r.Body.Close() - textarea_content := string(body) - decoded_textarea, err := url.QueryUnescape(textarea_content) + arguments := ParseArguments(string(body)) + // textarea_content := string(body) + email_content, err := url.QueryUnescape(arguments["email_input"]) if err != nil { panic(err) } - email_content, _ := strings.CutPrefix(decoded_textarea, "email_input=") // Return a response to HTMX model := PostmarkTemplateModel{ @@ -135,17 +171,16 @@ func main() { }) mux.HandleFunc("/subject_preview", func(w http.ResponseWriter, r *http.Request) { - log.Printf("Hello '%v %v' handler", r.Method, r.URL) + log.Printf("%v %v", r.Method, r.URL) body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer r.Body.Close() + arguments := ParseArguments(string(body)) - text_field_body := string(body) - text_field_body, _ = strings.CutPrefix(text_field_body, "subject=") - decoded_subject, err := url.QueryUnescape(text_field_body) + decoded_subject, err := url.QueryUnescape(arguments["subject"]) if err != nil { panic(err) } @@ -162,30 +197,44 @@ func main() { // Endpoint called on load or button press by htmx to retrieve and populate the mailing list mux.HandleFunc("/mailing_list", func(w http.ResponseWriter, r *http.Request) { - body := []byte(fmt.Sprintf("{\"city\": \"%s\"}", apiConfig.Hmc.City)) + log.Printf("%v %v", r.Method, r.URL) + mailing_list := GetMailingList(apiConfig) + templates.ExecuteTemplate(w, "mailing_list", mailing_list) + }) - req, err := http.NewRequest("POST", apiConfig.Hmc.ApiUrl, bytes.NewBuffer(body)) + mux.HandleFunc("/send_email", func(w http.ResponseWriter, r *http.Request) { + log.Printf("%v %v", r.Method, r.URL) + + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer r.Body.Close() + + body_string := string(body) + arguments := ParseArguments(body_string) + fmt.Printf("%v\n", arguments) + var recipients []string + if arguments["destination"] == "test" { + recipients = []string{apiConfig.Default.TestEmail} + } else if arguments["destination"] == "mailing_list" { + recipients = GetMailingList(apiConfig) + } else { + panic("unknown destination in arguments") + } + + decoded_email, err := url.QueryUnescape(arguments["email_input"]) if err != nil { panic(err) } - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", apiConfig.Hmc.ApiSecret) - - client := &http.Client{} - res, err := client.Do(req) + decoded_subject, err := url.QueryUnescape(arguments["subject"]) if err != nil { panic(err) } - defer res.Body.Close() - response := &EmailsResonse{} - decode_error := json.NewDecoder(res.Body).Decode(&response) - if decode_error != nil { - panic(decode_error) - } - - templates.ExecuteTemplate(w, "mailing_list", response) + sendBatchWithTemplate(apiConfig, decoded_email, decoded_subject, recipients) }) srv := &http.Server{ diff --git a/postmark.go b/postmark.go index 83f423c..fe1e2f7 100644 --- a/postmark.go +++ b/postmark.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "log" "net/http" ) @@ -141,3 +142,62 @@ func renderPostmarkTemplate(cfg PostmarkConfig, template PostmarkTemplate, model Subject: response.Subject.RenderedContent, } } + +type PostmarkBatchArguments struct { + From string + To string + TemplateId int + TemplateModel PostmarkTemplateModel + MessageStream string +} + +func sendBatchWithTemplate(cfg ApiConfig, email, subject string, recipients []string) { + type BatchAPIArgs struct { + Messages []PostmarkBatchArguments + } + bodyObj := &BatchAPIArgs{} + + for _, recipient := range recipients { + args := PostmarkBatchArguments{ + From: cfg.Postmark.SenderEmail, + To: recipient, + TemplateId: cfg.Postmark.TemplateId, + TemplateModel: PostmarkTemplateModel{ + Name: cfg.Default.SenderName, + Email: cfg.Postmark.SenderEmail, + Body: email, + Subject: subject, + }, + MessageStream: cfg.Postmark.MessageStream, + } + bodyObj.Messages = append(bodyObj.Messages, args) + } + + body, err := json.Marshal(bodyObj) + if err != nil { + panic(err) + } + + req, err := http.NewRequest("POST", "https://api.postmarkapp.com/email/batchWithTemplates", bytes.NewBuffer(body)) + if err != nil { + panic(err) + } + + req.Header.Add("Accept", "application/json") + req.Header.Add("Content-Type", "application/json") + req.Header.Add("X-Postmark-Server-Token", cfg.Postmark.ServerToken) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + rawBodyContent, err := io.ReadAll(resp.Body) + if err != nil { + panic(err) + } + + log.Printf("batchWithTemplates responded with: %s\n", string(rawBodyContent)) +} diff --git a/static/css/site.css b/static/css/site.css index 0ad57ce..621ecd3 100644 --- a/static/css/site.css +++ b/static/css/site.css @@ -87,9 +87,14 @@ ul { .row { display: flex; + flex-direction: row; + flex-wrap: nowrap; } .column { padding: 0.5em; border-color: #E8B; + display: flex; + flex-direction: column; + flex-wrap: nowrap; } \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 5ec0f3c..9c8732b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -19,15 +19,28 @@

This is an input

-
+
Subject:

- +
+
+ + +
@@ -54,7 +67,7 @@ {{ block "mailing_list" . }}
    - {{ range .Emails }} + {{ range . }}
  • {{ . }}
  • {{end}}