From cc0a318040cb63dfcbf4aa594a2acfb78ac67f14 Mon Sep 17 00:00:00 2001 From: Radoslav Dimitrov Date: Fri, 28 Jun 2024 17:21:37 +0300 Subject: [PATCH] Use email template Signed-off-by: Radoslav Dimitrov --- internal/email/email.go | 67 +++++++-- internal/email/template.go | 291 +++++++++++++++++++++++++++++++++++++ 2 files changed, 349 insertions(+), 9 deletions(-) create mode 100644 internal/email/template.go diff --git a/internal/email/email.go b/internal/email/email.go index 0a64486f88..848abfbbb2 100644 --- a/internal/email/email.go +++ b/internal/email/email.go @@ -19,8 +19,10 @@ import ( "context" "encoding/json" "fmt" + "strings" "github.com/ThreeDotsLabs/watermill/message" + "github.com/docker/cli/templates" "github.com/google/uuid" "github.com/stacklok/minder/internal/events" @@ -64,24 +66,18 @@ func (m *MailEventHandler) Register(reg events.Registrar) { // handlerInviteEmail handles the invite email event func (m *MailEventHandler) handlerInviteEmail(msg *message.Message) error { - var event MailEventPayload + var e MailEventPayload // Get the message context msgCtx := msg.Context() // Unmarshal the message payload - if err := json.Unmarshal(msg.Payload, &event); err != nil { + if err := json.Unmarshal(msg.Payload, &e); err != nil { return fmt.Errorf("error unmarshalling invite email event: %w", err) } - // Create the email subject and body - // TODO: Use templates here - subject := fmt.Sprintf("You have been invited to join %s", event.ProjectDisplay) - bodyText := fmt.Sprintf("You have been invited to join %s as a %s by %s. Use code %s to accept the invitation.", - event.ProjectDisplay, event.Role, event.SponsorDisplay, event.Code) - bodyHtml := fmt.Sprintf("

%s

", bodyText) // Send the email - return m.client.SendEmail(msgCtx, event.Email, subject, bodyHtml, bodyText) + return m.client.SendEmail(msgCtx, e.Email, e.getEmailSubject(), e.getEmailBodyHTML(), e.getEmailBodyText()) } // NewMessage creates a new message for sending an invitation email @@ -105,3 +101,56 @@ func NewMessage(inviteeEmail, code, role, projectDisplay, sponsorDisplay string) // Create the message return message.NewMessage(id.String(), payload), nil } + +// getBodyHTML returns the HTML body for the email based on the message payload +func (e *MailEventPayload) getEmailBodyHTML() string { + data := struct { + AdminName string + OrganizationName string + InvitationURL string + RecipientEmail string + MinderURL string + TermsURL string + PrivacyURL string + SignInURL string + RoleName string + }{ + AdminName: e.SponsorDisplay, + OrganizationName: e.ProjectDisplay, + // TODO: Determine the correct environment for the invite URL and the rest of the URLs + InvitationURL: fmt.Sprintf("https://cloud.minder.com/join/%s", e.Code), + RecipientEmail: e.Email, + MinderURL: "https://cloud.minder.com", + TermsURL: "https://cloud.minder.com/terms", + PrivacyURL: "https://cloud.minder.com/privacy", + SignInURL: "https://cloud.minder.com", + RoleName: e.Role, + } + + // TODO: Load the email template from elsewhere + + // Parse the template + tmpl, err := templates.Parse(bodyHTML) + if err != nil { + // TODO: Log the error + // Default to the text body + return e.getEmailBodyText() + } + // Execute the template + var b strings.Builder + if err := tmpl.Execute(&b, data); err != nil { + return "" + } + return b.String() +} + +// getEmailBodyText returns the text body for the email based on the message payload +func (e *MailEventPayload) getEmailBodyText() string { + return fmt.Sprintf("You have been invited to join %s as a %s by %s. Use code %s to accept the invitation.", + e.ProjectDisplay, e.Role, e.SponsorDisplay, e.Code) +} + +// getEmailSubject returns the subject for the email based on the message payload +func (e *MailEventPayload) getEmailSubject() string { + return fmt.Sprintf("You have been invited to join the %s organisation in Minder", e.ProjectDisplay) +} diff --git a/internal/email/template.go b/internal/email/template.go new file mode 100644 index 0000000000..b29c5a34b9 --- /dev/null +++ b/internal/email/template.go @@ -0,0 +1,291 @@ +// Copyright 2024 Stacklok, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package email + +//nolint:lll +const ( + // bodyHTML is the HTML body of the email + bodyHTML = ` +
+ + + + + + +
+
+ Minder by Stacklok +
+
+
+

+ {{.AdminName}} has invited you to become {{.RoleName}} in + the {{.OrganizationName}} organization in Minder. +

+
+
+ + + + +
+
+ + View Invitation + +
+
+
+
+ Once you accept, you’ll be able to view the {{.OrganizationName}} + organization, and its security posture, in Minder. +
+
+

+ This invitation was sent to + {{.RecipientEmail}}. If you were not + expecting it, you can ignore this email. +

+
+
+
+
+
+

+ Minder + by Stacklok is an open source platform that helps development + teams and open source communities build more secure software, + and prove to others that what they’ve built is secure. +

+
+
+ + + + + + + + +
+ + +
+

+ Privacy +

+
+
+ +
+
+
+
+ Stacklok +
+
+
+` +)