mirror of
https://github.com/h44z/wg-portal.git
synced 2025-12-14 10:36:18 +00:00
191 lines
5.6 KiB
Go
191 lines
5.6 KiB
Go
package mail
|
|
|
|
import (
|
|
"bytes"
|
|
"embed"
|
|
"fmt"
|
|
htmlTemplate "html/template"
|
|
"io"
|
|
"io/fs"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"text/template"
|
|
|
|
"github.com/h44z/wg-portal/internal/domain"
|
|
)
|
|
|
|
//go:embed tpl_files/*
|
|
var TemplateFiles embed.FS
|
|
|
|
// TemplateHandler is a struct that holds the html and text templates.
|
|
type TemplateHandler struct {
|
|
portalUrl string
|
|
portalName string
|
|
htmlTemplates *htmlTemplate.Template
|
|
textTemplates *template.Template
|
|
}
|
|
|
|
func newTemplateHandler(portalUrl, portalName string, basePath string) (*TemplateHandler, error) {
|
|
// Always parse embedded defaults first
|
|
htmlTemplateCache, err := htmlTemplate.New("Html").ParseFS(TemplateFiles, "tpl_files/*.gohtml")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse embedded html template files: %w", err)
|
|
}
|
|
|
|
txtTemplateCache, err := template.New("Txt").ParseFS(TemplateFiles, "tpl_files/*.gotpl")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse embedded text template files: %w", err)
|
|
}
|
|
|
|
// If a basePath is provided, ensure existence, populate if empty, then parse to override
|
|
if basePath != "" {
|
|
if err := os.MkdirAll(basePath, 0755); err != nil {
|
|
return nil, fmt.Errorf("failed to create templates base directory %s: %w", basePath, err)
|
|
}
|
|
|
|
hasTemplates, err := dirHasTemplates(basePath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to inspect templates directory: %w", err)
|
|
}
|
|
|
|
// If no templates present, copy embedded defaults to directory
|
|
if !hasTemplates {
|
|
if err := copyEmbeddedTemplates(basePath); err != nil {
|
|
return nil, fmt.Errorf("failed to populate templates directory: %w", err)
|
|
}
|
|
}
|
|
|
|
// Parse files from basePath to override embedded ones.
|
|
// Only parse when matches exist to allow partial overrides without errors.
|
|
if matches, _ := filepath.Glob(filepath.Join(basePath, "*.gohtml")); len(matches) > 0 {
|
|
slog.Debug("parsing html email templates from base path", "base-path", basePath, "files", matches)
|
|
if htmlTemplateCache, err = htmlTemplateCache.ParseFiles(matches...); err != nil {
|
|
return nil, fmt.Errorf("failed to parse html templates from base path: %w", err)
|
|
}
|
|
}
|
|
if matches, _ := filepath.Glob(filepath.Join(basePath, "*.gotpl")); len(matches) > 0 {
|
|
slog.Debug("parsing text email templates from base path", "base-path", basePath, "files", matches)
|
|
if txtTemplateCache, err = txtTemplateCache.ParseFiles(matches...); err != nil {
|
|
return nil, fmt.Errorf("failed to parse text templates from base path: %w", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
handler := &TemplateHandler{
|
|
portalUrl: portalUrl,
|
|
portalName: portalName,
|
|
htmlTemplates: htmlTemplateCache,
|
|
textTemplates: txtTemplateCache,
|
|
}
|
|
|
|
return handler, nil
|
|
}
|
|
|
|
// dirHasTemplates checks whether directory contains any .gohtml or .gotpl files.
|
|
func dirHasTemplates(basePath string) (bool, error) {
|
|
entries, err := os.ReadDir(basePath)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
for _, e := range entries {
|
|
if e.IsDir() {
|
|
continue
|
|
}
|
|
ext := filepath.Ext(e.Name())
|
|
if ext == ".gohtml" || ext == ".gotpl" {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
// copyEmbeddedTemplates writes embedded templates into basePath.
|
|
func copyEmbeddedTemplates(basePath string) error {
|
|
list, err := fs.ReadDir(TemplateFiles, "tpl_files")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, entry := range list {
|
|
if entry.IsDir() {
|
|
continue
|
|
}
|
|
name := entry.Name()
|
|
// Only copy known template extensions
|
|
if ext := filepath.Ext(name); ext != ".gohtml" && ext != ".gotpl" {
|
|
continue
|
|
}
|
|
data, err := TemplateFiles.ReadFile(filepath.Join("tpl_files", name))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out := filepath.Join(basePath, name)
|
|
if err := os.WriteFile(out, data, 0644); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetConfigMail returns the text and html template for the mail with a link.
|
|
func (c TemplateHandler) GetConfigMail(user *domain.User, link string) (io.Reader, io.Reader, error) {
|
|
var tplBuff bytes.Buffer
|
|
var htmlTplBuff bytes.Buffer
|
|
|
|
err := c.textTemplates.ExecuteTemplate(&tplBuff, "mail_with_link.gotpl", map[string]any{
|
|
"User": user,
|
|
"Link": link,
|
|
"PortalUrl": c.portalUrl,
|
|
"PortalName": c.portalName,
|
|
})
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to execute template mail_with_link.gotpl: %w", err)
|
|
}
|
|
|
|
err = c.htmlTemplates.ExecuteTemplate(&htmlTplBuff, "mail_with_link.gohtml", map[string]any{
|
|
"User": user,
|
|
"Link": link,
|
|
"PortalUrl": c.portalUrl,
|
|
"PortalName": c.portalName,
|
|
})
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to execute template mail_with_link.gohtml: %w", err)
|
|
}
|
|
|
|
return &tplBuff, &htmlTplBuff, nil
|
|
}
|
|
|
|
// GetConfigMailWithAttachment returns the text and html template for the mail with an attachment.
|
|
func (c TemplateHandler) GetConfigMailWithAttachment(user *domain.User, cfgName, qrName string) (
|
|
io.Reader,
|
|
io.Reader,
|
|
error,
|
|
) {
|
|
var tplBuff bytes.Buffer
|
|
var htmlTplBuff bytes.Buffer
|
|
|
|
err := c.textTemplates.ExecuteTemplate(&tplBuff, "mail_with_attachment.gotpl", map[string]any{
|
|
"User": user,
|
|
"ConfigFileName": cfgName,
|
|
"QrcodePngName": qrName,
|
|
"PortalUrl": c.portalUrl,
|
|
"PortalName": c.portalName,
|
|
})
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to execute template mail_with_attachment.gotpl: %w", err)
|
|
}
|
|
|
|
err = c.htmlTemplates.ExecuteTemplate(&htmlTplBuff, "mail_with_attachment.gohtml", map[string]any{
|
|
"User": user,
|
|
"ConfigFileName": cfgName,
|
|
"QrcodePngName": qrName,
|
|
"PortalUrl": c.portalUrl,
|
|
"PortalName": c.portalName,
|
|
})
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to execute template mail_with_attachment.gohtml: %w", err)
|
|
}
|
|
|
|
return &tplBuff, &htmlTplBuff, nil
|
|
}
|