Skip to content

Commit

Permalink
feature: use embedded folder to load html templates (#30)
Browse files Browse the repository at this point in the history
* using embedded folder to load html templates and protect available templates with a private variable and a getter to prevent sets
* remove copy assets to final stage of docker image build
  • Loading branch information
lucasmenendez authored Nov 27, 2024
1 parent 9f8e8e0 commit 449d51e
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 46 deletions.
3 changes: 0 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,4 @@ RUN apt-get update && \
WORKDIR /app
COPY --from=builder /src/backend ./

# Include the assets folder
COPY --from=builder /src/assets ./assets

ENTRYPOINT ["/app/backend"]
2 changes: 1 addition & 1 deletion api/users_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func init() {
// create a regex to find the verification code in the email
codeRgx := fmt.Sprintf(`(.{%d})`, VerificationCodeLength*2)
// load the email templates
if err := mailtemplates.Load("../assets"); err != nil {
if err := mailtemplates.Load(); err != nil {
panic(err)
}
// wrap the mail template execution to force plain body and set the regex
Expand Down
17 changes: 5 additions & 12 deletions cmd/service/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"fmt"
"os"
"os/signal"
"syscall"
Expand Down Expand Up @@ -30,7 +29,6 @@ func main() {
flag.StringP("mongoDB", "d", "saasdb", "The name of the MongoDB database")
flag.StringP("privateKey", "k", "", "private key for the Vocdoni account")
flag.BoolP("fullTransparentMode", "a", false, "allow all transactions and do not modify any of them")
flag.String("emailTemplatesPath", "./assets", "path to the email templates")
flag.String("smtpServer", "", "SMTP server")
flag.Int("smtpPort", 587, "SMTP port")
flag.String("smtpUsername", "", "SMTP username")
Expand Down Expand Up @@ -60,7 +58,6 @@ func main() {
mongoURL := viper.GetString("mongoURL")
mongoDB := viper.GetString("mongoDB")
// email vars
emailTemplatesPath := viper.GetString("emailTemplatesPath")
smtpServer := viper.GetString("smtpServer")
smtpPort := viper.GetInt("smtpPort")
smtpUsername := viper.GetString("smtpUsername")
Expand Down Expand Up @@ -136,16 +133,12 @@ func main() {
}); err != nil {
log.Fatalf("could not create the email service: %v", err)
}
// load email templates if the path is set
if emailTemplatesPath != "" {
if err := mailtemplates.Load(emailTemplatesPath); err != nil {
log.Fatalf("could not load email templates: %v", err)
}
log.Infow("email templates loaded",
"path", emailTemplatesPath,
"templates", len(mailtemplates.AvailableTemplates))
// load email templates
if err := mailtemplates.Load(); err != nil {
log.Fatalf("could not load email templates: %v", err)
}
log.Infow("email service created", "from", fmt.Sprintf("%s <%s>", emailFromName, emailFromAddress))
log.Infow("email templates loaded",
"templates", len(mailtemplates.Available()))
}
subscriptions := subscriptions.New(&subscriptions.SubscriptionsConfig{
DB: database,
Expand Down
63 changes: 33 additions & 30 deletions notifications/mailtemplates/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import (
"bytes"
"fmt"
htmltemplate "html/template"
"os"
"path/filepath"
"strings"
texttemplate "text/template"

root "github.com/vocdoni/saas-backend"
"github.com/vocdoni/saas-backend/notifications"
)

// AvailableTemplates is a map that stores the filename and the absolute path
// availableTemplates is a map that stores the filename and the absolute path
// of the email templates. The filename is the key and the path is the value.
var AvailableTemplates map[TemplateFile]string
var availableTemplates map[TemplateFile]string

// TemplateFile represents an email template key. Every email template should
// have a key that identifies it, which is the filename without the extension.
Expand All @@ -31,34 +30,34 @@ type MailTemplate struct {
WebAppURI string
}

// Load function reads the email templates from the specified directory.
// Returns a map with the filename and file absolute path. The filename is
// the key and the path is the value.
func Load(path string) error {
// create a map to store the filename and file content
htmlFiles := make(map[TemplateFile]string)
// Available function returns the available email templates. It returns a map
// with the filename and the absolute path of the email templates. The filename
// is the key and the path is the value.
func Available() map[TemplateFile]string {
return availableTemplates
}

// Load function reads the email templates from embedded assets. It reads the
// html files from the "assets" directory and stores the filename and the file
// path in the availableTemplates map. It returns an error if the directory
// could not be read or if the files could not be read.
func Load() error {
// reset the map to store the filename and file paths
availableTemplates = make(map[TemplateFile]string)
// read files from embedded assets
entries, err := root.Assets.ReadDir("assets")
if err != nil {
return err
}
// walk through the directory and read each file
if err := filepath.Walk(path, func(fPath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
for _, entry := range entries {
// only process regular files and files with a ".html" extension
if !info.IsDir() && strings.HasSuffix(info.Name(), ".html") {
// get the absolute path of the file
absPath, err := filepath.Abs(fPath)
if err != nil {
return err
}
// remove the ".html" extension from the filename
filename := strings.TrimSuffix(info.Name(), ".html")
// store the filename and content in the map
htmlFiles[TemplateFile(filename)] = absPath
if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".html") {
// store the filename and the path in the map
name := strings.TrimSuffix(entry.Name(), ".html")
availableTemplates[TemplateFile(name)] = "assets/" + entry.Name()
}
return nil
}); err != nil {
return err
}
AvailableTemplates = htmlFiles
return nil
}

Expand All @@ -69,7 +68,7 @@ func Load(path string) error {
// data provided. It returns the notification with the body and plain body
// filled with the data provided.
func (mt MailTemplate) ExecTemplate(data any) (*notifications.Notification, error) {
path, ok := AvailableTemplates[mt.File]
path, ok := availableTemplates[mt.File]
if !ok {
return nil, fmt.Errorf("template not found")
}
Expand All @@ -81,7 +80,11 @@ func (mt MailTemplate) ExecTemplate(data any) (*notifications.Notification, erro
// set the mail subject
n.Subject = mt.Placeholder.Subject
// parse the html template file
tmpl, err := htmltemplate.ParseFiles(path)
content, err := root.Assets.ReadFile(path)
if err != nil {
return nil, err
}
tmpl, err := htmltemplate.New(string(mt.File)).Parse(string(content))
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 449d51e

Please sign in to comment.