diff --git a/routes/routes.go b/routes/routes.go
index b9bdeef..cd3d3c6 100644
--- a/routes/routes.go
+++ b/routes/routes.go
@@ -2,27 +2,24 @@ package routes
import (
"fmt"
+ "github.com/muety/wakapi/views"
"html/template"
- "io/fs"
- "io/ioutil"
"net/http"
- "path"
"strings"
"github.com/muety/wakapi/config"
"github.com/muety/wakapi/models"
"github.com/muety/wakapi/utils"
- "github.com/muety/wakapi/views"
)
-func Init() {
- loadTemplates()
-}
-
type action func(w http.ResponseWriter, r *http.Request) (int, string, string)
var templates map[string]*template.Template
+func Init() {
+ loadTemplates()
+}
+
func DefaultTemplateFuncs() template.FuncMap {
return template.FuncMap{
"json": utils.Json,
@@ -58,44 +55,6 @@ func DefaultTemplateFuncs() template.FuncMap {
}
}
-func loadTemplates() {
- tpls := template.New("").Funcs(DefaultTemplateFuncs())
- templates = make(map[string]*template.Template)
-
- // Use local file system when in 'dev' environment, go embed file system otherwise
- templateFs := config.ChooseFS("views", views.TemplateFiles)
-
- files, err := fs.ReadDir(templateFs, ".")
- if err != nil {
- panic(err)
- }
-
- for _, file := range files {
- tplName := file.Name()
- if file.IsDir() || path.Ext(tplName) != ".html" {
- continue
- }
-
- templateFile, err := templateFs.Open(tplName)
- if err != nil {
- panic(err)
- }
- templateData, err := ioutil.ReadAll(templateFile)
- if err != nil {
- panic(err)
- }
-
- templateFile.Close()
-
- tpl, err := tpls.New(tplName).Parse(string(templateData))
- if err != nil {
- panic(err)
- }
-
- templates[tplName] = tpl
- }
-}
-
func typeName(t uint8) string {
if t == models.SummaryProject {
return "project"
@@ -118,6 +77,16 @@ func typeName(t uint8) string {
return "unknown"
}
+func loadTemplates() {
+ // Use local file system when in 'dev' environment, go embed file system otherwise
+ templateFs := config.ChooseFS("views", views.TemplateFiles)
+ if tpls, err := utils.LoadTemplates(templateFs, DefaultTemplateFuncs()); err == nil {
+ templates = tpls
+ } else {
+ panic(err)
+ }
+}
+
func defaultErrorRedirectTarget() string {
return fmt.Sprintf("%s/?error=unauthorized", config.Get().Server.BasePath)
}
diff --git a/services/mail/mail.go b/services/mail/mail.go
index 1dc3163..6f1c638 100644
--- a/services/mail/mail.go
+++ b/services/mail/mail.go
@@ -7,12 +7,10 @@ import (
"github.com/muety/wakapi/routes"
"github.com/muety/wakapi/services"
"github.com/muety/wakapi/utils"
- "html/template"
- "io/ioutil"
+ "github.com/muety/wakapi/views/mail"
"time"
conf "github.com/muety/wakapi/config"
- "github.com/muety/wakapi/views"
)
const (
@@ -33,6 +31,7 @@ type SendingService interface {
type MailService struct {
config *conf.Config
sendingService SendingService
+ templates utils.TemplateMap
}
func NewMailService() services.IMailService {
@@ -49,11 +48,18 @@ func NewMailService() services.IMailService {
}
}
- return &MailService{sendingService: sendingService, config: config}
+ // Use local file system when in 'dev' environment, go embed file system otherwise
+ templateFs := conf.ChooseFS("views/mail", mail.TemplateFiles)
+ templates, err := utils.LoadTemplates(templateFs, routes.DefaultTemplateFuncs())
+ if err != nil {
+ panic(err)
+ }
+
+ return &MailService{sendingService: sendingService, config: config, templates: templates}
}
func (m *MailService) SendPasswordReset(recipient *models.User, resetLink string) error {
- tpl, err := getPasswordResetTemplate(PasswordResetTplData{ResetLink: resetLink})
+ tpl, err := m.getPasswordResetTemplate(PasswordResetTplData{ResetLink: resetLink})
if err != nil {
return err
}
@@ -67,7 +73,7 @@ func (m *MailService) SendPasswordReset(recipient *models.User, resetLink string
}
func (m *MailService) SendWakatimeFailureNotification(recipient *models.User, numFailures int) error {
- tpl, err := getWakatimeFailureNotificationTemplate(WakatimeFailureNotificationNotificationTplData{
+ tpl, err := m.getWakatimeFailureNotificationTemplate(WakatimeFailureNotificationNotificationTplData{
PublicUrl: m.config.Server.PublicUrl,
NumFailures: numFailures,
})
@@ -84,7 +90,7 @@ func (m *MailService) SendWakatimeFailureNotification(recipient *models.User, nu
}
func (m *MailService) SendImportNotification(recipient *models.User, duration time.Duration, numHeartbeats int) error {
- tpl, err := getImportNotificationTemplate(ImportNotificationTplData{
+ tpl, err := m.getImportNotificationTemplate(ImportNotificationTplData{
PublicUrl: m.config.Server.PublicUrl,
Duration: fmt.Sprintf("%.0f seconds", duration.Seconds()),
NumHeartbeats: numHeartbeats,
@@ -102,7 +108,7 @@ func (m *MailService) SendImportNotification(recipient *models.User, duration ti
}
func (m *MailService) SendReport(recipient *models.User, report *models.Report) error {
- tpl, err := getReportTemplate(ReportTplData{report})
+ tpl, err := m.getReportTemplate(ReportTplData{report})
if err != nil {
return err
}
@@ -115,68 +121,38 @@ func (m *MailService) SendReport(recipient *models.User, report *models.Report)
return m.sendingService.Send(mail)
}
-func getPasswordResetTemplate(data PasswordResetTplData) (*bytes.Buffer, error) {
- tpl, err := loadTemplate(tplNamePasswordReset)
- if err != nil {
- return nil, err
- }
+func (m *MailService) getPasswordResetTemplate(data PasswordResetTplData) (*bytes.Buffer, error) {
var rendered bytes.Buffer
- if err := tpl.Execute(&rendered, data); err != nil {
+ if err := m.templates[m.fmtName(tplNamePasswordReset)].Execute(&rendered, data); err != nil {
return nil, err
}
return &rendered, nil
}
-func getWakatimeFailureNotificationTemplate(data WakatimeFailureNotificationNotificationTplData) (*bytes.Buffer, error) {
- tpl, err := loadTemplate(tplNameWakatimeFailureNotification)
- if err != nil {
- return nil, err
- }
+func (m *MailService) getWakatimeFailureNotificationTemplate(data WakatimeFailureNotificationNotificationTplData) (*bytes.Buffer, error) {
var rendered bytes.Buffer
- if err := tpl.Execute(&rendered, data); err != nil {
+ if err := m.templates[m.fmtName(tplNameWakatimeFailureNotification)].Execute(&rendered, data); err != nil {
return nil, err
}
return &rendered, nil
}
-func getImportNotificationTemplate(data ImportNotificationTplData) (*bytes.Buffer, error) {
- tpl, err := loadTemplate(tplNameImportNotification)
- if err != nil {
- return nil, err
- }
+func (m *MailService) getImportNotificationTemplate(data ImportNotificationTplData) (*bytes.Buffer, error) {
var rendered bytes.Buffer
- if err := tpl.Execute(&rendered, data); err != nil {
+ if err := m.templates[m.fmtName(tplNameImportNotification)].Execute(&rendered, data); err != nil {
return nil, err
}
return &rendered, nil
}
-func getReportTemplate(data ReportTplData) (*bytes.Buffer, error) {
- tpl, err := loadTemplate(tplNameReport)
- if err != nil {
- return nil, err
- }
+func (m *MailService) getReportTemplate(data ReportTplData) (*bytes.Buffer, error) {
var rendered bytes.Buffer
- if err := tpl.Execute(&rendered, data); err != nil {
+ if err := m.templates[m.fmtName(tplNameReport)].Execute(&rendered, data); err != nil {
return nil, err
}
return &rendered, nil
}
-func loadTemplate(tplName string) (*template.Template, error) {
- tplFile, err := views.TemplateFiles.Open(fmt.Sprintf("mail/%s.tpl.html", tplName))
- if err != nil {
- return nil, err
- }
- defer tplFile.Close()
-
- tplData, err := ioutil.ReadAll(tplFile)
- if err != nil {
- return nil, err
- }
-
- return template.
- New(tplName).
- Funcs(routes.DefaultTemplateFuncs()).
- Parse(string(tplData))
+func (m *MailService) fmtName(name string) string {
+ return fmt.Sprintf("%s.tpl.html", name)
}
diff --git a/utils/template.go b/utils/template.go
index 1abeb5b..9282f4b 100644
--- a/utils/template.go
+++ b/utils/template.go
@@ -3,8 +3,13 @@ package utils
import (
"encoding/json"
"html/template"
+ "io/fs"
+ "io/ioutil"
+ "path"
)
+type TemplateMap map[string]*template.Template
+
func Json(data interface{}) template.JS {
d, err := json.Marshal(data)
if err != nil {
@@ -19,3 +24,40 @@ func ToRunes(s string) (r []string) {
}
return r
}
+
+func LoadTemplates(templateFs fs.FS, funcs template.FuncMap) (TemplateMap, error) {
+ tpls := template.New("").Funcs(funcs)
+ templates := make(map[string]*template.Template)
+
+ files, err := fs.ReadDir(templateFs, ".")
+ if err != nil {
+ return nil, err
+ }
+
+ for _, file := range files {
+ tplName := file.Name()
+ if file.IsDir() || path.Ext(tplName) != ".html" {
+ continue
+ }
+
+ templateFile, err := templateFs.Open(tplName)
+ if err != nil {
+ return nil, err
+ }
+ templateData, err := ioutil.ReadAll(templateFile)
+ if err != nil {
+ return nil, err
+ }
+
+ templateFile.Close()
+
+ tpl, err := tpls.New(tplName).Parse(string(templateData))
+ if err != nil {
+ return nil, err
+ }
+
+ templates[tplName] = tpl
+ }
+
+ return templates, nil
+}
diff --git a/views/mail/head.tpl.html b/views/mail/head.tpl.html
new file mode 100644
index 0000000..b76f453
--- /dev/null
+++ b/views/mail/head.tpl.html
@@ -0,0 +1,88 @@
+
+
+
+Wakapi
+
+
\ No newline at end of file
diff --git a/views/mail/import_finished.tpl.html b/views/mail/import_finished.tpl.html
index f332890..a8ddda1 100644
--- a/views/mail/import_finished.tpl.html
+++ b/views/mail/import_finished.tpl.html
@@ -1,107 +1,14 @@
-
-
-
- Wakapi – Data Import Finished
-
-
+{{ template "head.tpl.html" . }}
+
|
-
+ {{ template "theader.tpl.html" . }}
-
+ {{ template "tfooter.tpl.html" . }}
|
|
diff --git a/views/mail/mail.go b/views/mail/mail.go
new file mode 100644
index 0000000..2a27047
--- /dev/null
+++ b/views/mail/mail.go
@@ -0,0 +1,6 @@
+package mail
+
+import "embed"
+
+//go:embed *.html
+var TemplateFiles embed.FS
diff --git a/views/mail/report.tpl.html b/views/mail/report.tpl.html
index 60b4dac..ab8ee8a 100644
--- a/views/mail/report.tpl.html
+++ b/views/mail/report.tpl.html
@@ -1,107 +1,14 @@
-
-
-
- Wakapi – Report
-
-
+{{ template "head.tpl.html" . }}
+
|
-
+ {{ template "theader.tpl.html" . }}
-
+
+ {{ template "tfooter.tpl.html" . }}
|
|
diff --git a/views/mail/reset_password.tpl.html b/views/mail/reset_password.tpl.html
index d267fb9..953bb59 100644
--- a/views/mail/reset_password.tpl.html
+++ b/views/mail/reset_password.tpl.html
@@ -1,107 +1,14 @@
-
-
-
- Wakapi – Reset Password
-
-
+{{ template "head.tpl.html" . }}
+
|
-
+ {{ template "theader.tpl.html" . }}
-
+ {{ template "tfooter.tpl.html" . }}
|
|
diff --git a/views/mail/tfooter.tpl.html b/views/mail/tfooter.tpl.html
new file mode 100644
index 0000000..d2801e9
--- /dev/null
+++ b/views/mail/tfooter.tpl.html
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/views/mail/theader.tpl.html b/views/mail/theader.tpl.html
new file mode 100644
index 0000000..2f6745c
--- /dev/null
+++ b/views/mail/theader.tpl.html
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/views/mail/wakatime_connection_failure.tpl.html b/views/mail/wakatime_connection_failure.tpl.html
index 2ef4ca4..e80be3d 100644
--- a/views/mail/wakatime_connection_failure.tpl.html
+++ b/views/mail/wakatime_connection_failure.tpl.html
@@ -1,107 +1,14 @@
-
-
-
- Wakapi – WakaTime Connection Failure
-
-
+{{ template "head.tpl.html" . }}
+
|
-
+ {{ template "theader.tpl.html" . }}
-
+ {{ template "tfooter.tpl.html" . }}
|
|
diff --git a/views/views.go b/views/views.go
index 8eb83d9..51849c2 100644
--- a/views/views.go
+++ b/views/views.go
@@ -2,5 +2,5 @@ package views
import "embed"
-//go:embed *.html mail/*.html
+//go:embed *.html
var TemplateFiles embed.FS