diff --git a/cmd/tulip/serve.go b/cmd/tulip/serve.go index 204d253..87f7a0f 100644 --- a/cmd/tulip/serve.go +++ b/cmd/tulip/serve.go @@ -9,6 +9,8 @@ import ( "flag" "fmt" "github.com/1f349/tulip/database" + "github.com/1f349/tulip/mail/templates" + "github.com/1f349/tulip/pages" "github.com/1f349/tulip/server" "github.com/1f349/violet/utils" "github.com/MrMelon54/exit-reload" @@ -82,6 +84,13 @@ func normalLoad(startUp startUpConfig, wd string) { log.Fatal("[Tulip] Failed check:", err) } + if err = pages.LoadPages(wd); err != nil { + log.Fatal("[Tulip] Failed to load page templates:", err) + } + if err := templates.LoadMailTemplates(wd); err != nil { + log.Fatal("[Tulip] Failed to load mail templates:", err) + } + srv := server.NewHttpServer(startUp.Listen, startUp.BaseUrl, startUp.OtpIssuer, startUp.ServiceName, startUp.Mail, db, key) log.Printf("[Tulip] Starting HTTP server on '%s'\n", srv.Addr) go utils.RunBackgroundHttp("HTTP", srv) diff --git a/go.mod b/go.mod index fed5174..72a594f 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.21.1 require ( github.com/1f349/cache v0.0.2 + github.com/1f349/overlapfs v0.0.1 github.com/1f349/twofactor v1.0.4 github.com/1f349/violet v0.0.9 github.com/MrMelon54/exit-reload v0.0.1 diff --git a/go.sum b/go.sum index 931572c..1a8b16f 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/1f349/cache v0.0.2 h1:27QD6zPd9xYyvh9V1qqWq+EAt5+N+qvyGWKfnjMrhP8= github.com/1f349/cache v0.0.2/go.mod h1:LibAMy13dF0KO1fQA9aEjZPBCB6Y4b5kKYEQJUqc2rQ= +github.com/1f349/overlapfs v0.0.1 h1:LAxBolrXFAgU0yqZtXg/C/aaPq3eoQSPpBc49BHuTp0= +github.com/1f349/overlapfs v0.0.1/go.mod h1:I6aItQycr7nrzplmfNXp/QF9tTmKRSgY3fXmu/7Ky2o= github.com/1f349/twofactor v1.0.4 h1:kN4EEGFlKRa7fGrxS+FpgwJI+tllES6YzXqCqurk4Uk= github.com/1f349/twofactor v1.0.4/go.mod h1:gnG80vElwqLWNMnLT57yu4o4L1GdXGPP6pcIPlapXZs= github.com/1f349/violet v0.0.9 h1:eQfc5fDMKJXVFUjS2UiAGTkOVVBamppD5dguhmU4GeU= diff --git a/mail/templates/mail-verify.go.html b/mail/templates/mail-verify.go.html new file mode 100644 index 0000000..74e24b4 --- /dev/null +++ b/mail/templates/mail-verify.go.html @@ -0,0 +1,12 @@ + + +Hello, {{.Name}} + +Please open this link to verify your email address: {{.Data.VerifyUrl}} + +This link is valid for 10 minutes. + +If you did not create an account with {{.ServiceName}} then please ignore this email and the account will be deleted within a 48-hour period. + +Regards, +{{.ServiceName}} diff --git a/mail/templates/templates.go b/mail/templates/templates.go index e773946..da418a9 100644 --- a/mail/templates/templates.go +++ b/mail/templates/templates.go @@ -2,26 +2,54 @@ package templates import ( "embed" + "errors" + "github.com/1f349/overlapfs" + htmlTemplate "html/template" "io" + "io/fs" "log" - "text/template" + "os" + "path/filepath" + "sync" + textTemplate "text/template" ) var ( - //go:embed * + //go:embed *.go.html *.go.txt embeddedTemplates embed.FS - - mailTemplate *template.Template + mailHtmlTemplates *htmlTemplate.Template + mailTextTemplates *textTemplate.Template + loadOnce sync.Once ) -func LoadMailTemplates() (err error) { - mailTemplate, err = template.New("mail").ParseFS(embeddedTemplates, "*.go.txt") +func LoadMailTemplates(wd string) (err error) { + loadOnce.Do(func() { + var o fs.FS = embeddedTemplates + if wd != "" { + mailDir := filepath.Join(wd, "mail-templates") + err = os.Mkdir(mailDir, os.ModePerm) + if err != nil && !errors.Is(err, os.ErrExist) { + return + } + wdFs := os.DirFS(mailDir) + o = overlapfs.OverlapFS{A: embeddedTemplates, B: wdFs} + } + mailHtmlTemplates, err = htmlTemplate.New("mail").ParseFS(o, "*.go.html") + if err != nil { + return + } + mailTextTemplates, err = textTemplate.New("mail").ParseFS(o, "*.go.txt") + }) return } -func RenderMailTemplate(wr io.Writer, name string, data any) { - err := mailTemplate.ExecuteTemplate(wr, name+".go.txt", data) +func RenderMailTemplate(wrHtml, wrTxt io.Writer, name string, data any) { + err := mailHtmlTemplates.ExecuteTemplate(wrHtml, name+".go.html", data) if err != nil { - log.Printf("Failed to render mail: %s: %s\n", name, err) + log.Printf("Failed to render mail html: %s: %s\n", name, err) + } + err = mailTextTemplates.ExecuteTemplate(wrTxt, name+".go.txt", data) + if err != nil { + log.Printf("Failed to render mail text: %s: %s\n", name, err) } } diff --git a/pages/pages.go b/pages/pages.go index 963e2cd..03fbd38 100644 --- a/pages/pages.go +++ b/pages/pages.go @@ -3,27 +3,45 @@ package pages import ( "embed" _ "embed" + "errors" + "github.com/1f349/overlapfs" "html/template" "io" + "io/fs" "log" + "os" + "path/filepath" + "sync" ) var ( - //go:embed * - embeddedTemplates embed.FS - - pageTemplate *template.Template + //go:embed *.go.html + wwwPages embed.FS + wwwTemplates *template.Template + loadOnce sync.Once ) -func LoadPageTemplates() (err error) { - pageTemplate, err = template.New("pages").Funcs(template.FuncMap{ - "emailHide": EmailHide, - }).ParseFS(embeddedTemplates, "*.go.html") - return +func LoadPages(wd string) (err error) { + loadOnce.Do(func() { + var o fs.FS = wwwPages + if wd != "" { + wwwDir := filepath.Join(wd, "www") + err = os.Mkdir(wwwDir, os.ModePerm) + if err != nil && !errors.Is(err, os.ErrExist) { + return + } + wdFs := os.DirFS(wwwDir) + o = overlapfs.OverlapFS{A: wwwPages, B: wdFs} + } + wwwTemplates, err = template.New("pages").Funcs(template.FuncMap{ + "emailHide": EmailHide, + }).ParseFS(o, "*.go.html") + }) + return err } func RenderPageTemplate(wr io.Writer, name string, data any) { - err := pageTemplate.ExecuteTemplate(wr, name+".go.html", data) + err := wwwTemplates.ExecuteTemplate(wr, name+".go.html", data) if err != nil { log.Printf("Failed to render page: %s: %s\n", name, err) } diff --git a/server/server.go b/server/server.go index 0286800..7242c60 100644 --- a/server/server.go +++ b/server/server.go @@ -9,9 +9,7 @@ import ( clientStore "github.com/1f349/tulip/client-store" "github.com/1f349/tulip/database" "github.com/1f349/tulip/mail" - "github.com/1f349/tulip/mail/templates" "github.com/1f349/tulip/openid" - "github.com/1f349/tulip/pages" scope2 "github.com/1f349/tulip/scope" "github.com/go-oauth2/oauth2/v4/errors" "github.com/go-oauth2/oauth2/v4/generates" @@ -82,13 +80,6 @@ func NewHttpServer(listen, domain, otpIssuer, serviceName string, mailer mail.Ma log.Fatalln("Failed to generate OpenID configuration:", err) } - if err := pages.LoadPageTemplates(); err != nil { - log.Fatalln("Failed to load page templates:", err) - } - if err := templates.LoadMailTemplates(); err != nil { - log.Fatalln("Failed to load mail templates:", err) - } - oauthManager := manage.NewDefaultManager() oauthSrv := server.NewServer(server.NewConfig(), oauthManager) hs := &HttpServer{