diff --git a/config/config.go b/config/config.go index a107b28..465a9a9 100644 --- a/config/config.go +++ b/config/config.go @@ -23,7 +23,8 @@ type Configuration struct { } Template struct { - BaseName string `json:"BaseTemplateName"` + BaseName string `json:"BaseTemplateName"` + ContentPath string `json:"ContentPath"` } } diff --git a/controllers/get.go b/controllers/get.go index deeef98..b054546 100644 --- a/controllers/get.go +++ b/controllers/get.go @@ -22,7 +22,7 @@ func (g *Get) ShowHome(w http.ResponseWriter, _ *http.Request) { Test: "Hello World!", } - templating.RenderTemplate(g.App, w, "templates/pages/home.html", data) + templating.RenderTemplate(w, "templates/pages/home.html", data) } func (g *Get) ShowRegister(w http.ResponseWriter, r *http.Request) { @@ -39,7 +39,7 @@ func (g *Get) ShowRegister(w http.ResponseWriter, r *http.Request) { CsrfToken: CsrfToken, } - templating.RenderTemplate(g.App, w, "templates/pages/register.html", data) + templating.RenderTemplate(w, "templates/pages/register.html", data) } func (g *Get) ShowLogin(w http.ResponseWriter, r *http.Request) { @@ -56,7 +56,7 @@ func (g *Get) ShowLogin(w http.ResponseWriter, r *http.Request) { CsrfToken: CsrfToken, } - templating.RenderTemplate(g.App, w, "templates/pages/login.html", data) + templating.RenderTemplate(w, "templates/pages/login.html", data) } func (g *Get) Logout(w http.ResponseWriter, r *http.Request) { diff --git a/env_example.json b/env_example.json index 0708fdc..53754ff 100644 --- a/env_example.json +++ b/env_example.json @@ -12,6 +12,7 @@ "HttpPort": "8090" }, "Template": { - "BaseTemplateName": "templates/base.html" + "BaseTemplateName": "templates/base.html", + "ContentPath": "templates" } } \ No newline at end of file diff --git a/main.go b/main.go index c546878..7251d8c 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "GoWeb/database" "GoWeb/models" "GoWeb/routes" + "GoWeb/templating" "context" "embed" "errors" @@ -67,6 +68,13 @@ func main() { routes.Get(&appLoaded) routes.Post(&appLoaded) + // Prepare templates + err = templating.BuildPages(&appLoaded) + if err != nil { + slog.Error("error building templates: " + err.Error()) + os.Exit(1) + } + // Start server server := &http.Server{Addr: appLoaded.Config.Listen.Ip + ":" + appLoaded.Config.Listen.Port} go func() { diff --git a/templating/templateHelper.go b/templating/templateHelper.go index dd8695f..73def88 100644 --- a/templating/templateHelper.go +++ b/templating/templateHelper.go @@ -2,45 +2,82 @@ package templating import ( "GoWeb/app" + "fmt" "html/template" + "io/fs" "log/slog" "net/http" ) -// RenderTemplate renders and serves a template from the embedded filesystem optionally with given data -func RenderTemplate(app *app.App, w http.ResponseWriter, contentPath string, data any) { - templatePath := app.Config.Template.BaseName +var templates = make(map[string]*template.Template) // This is only used here, does not need to be in app.App - templateContent, err := app.Res.ReadFile(templatePath) +func BuildPages(app *app.App) error { + basePath := app.Config.Template.BaseName + + baseContent, err := app.Res.ReadFile(basePath) if err != nil { + return fmt.Errorf("error reading base file: %w", err) + } + + base, err := template.New(basePath).Parse(string(baseContent)) // Sets filepath as name and parses content + if err != nil { + return fmt.Errorf("error parsing base file: %w", err) + } + + readFilesRecursively := func(fsys fs.FS, root string) ([]string, error) { + var files []string + err := fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return fmt.Errorf("error walking the path %q: %w", path, err) + } + if !d.IsDir() { + files = append(files, path) + } + return nil + }) + return files, err + } + + // Get all file paths in the directory tree + filePaths, err := readFilesRecursively(app.Res, app.Config.Template.ContentPath) + if err != nil { + return fmt.Errorf("error reading files recursively: %w", err) + } + + for _, contentPath := range filePaths { // Create a new template base + content for each page + content, err := app.Res.ReadFile(contentPath) + if err != nil { + return fmt.Errorf("error reading content file %s: %w", contentPath, err) + } + + t, err := base.Clone() + if err != nil { + return fmt.Errorf("error cloning base template: %w", err) + } + + _, err = t.Parse(string(content)) + if err != nil { + return fmt.Errorf("error parsing content: %w", err) + } + + templates[contentPath] = t + } + + return nil +} + +func RenderTemplate(w http.ResponseWriter, contentPath string, data any) { + t, ok := templates[contentPath] + if !ok { + err := fmt.Errorf("template not found for path: %s", contentPath) slog.Error(err.Error()) - http.Error(w, err.Error(), 500) + http.Error(w, "Template not found", 404) return } - t, err := template.New(templatePath).Parse(string(templateContent)) - if err != nil { - slog.Error(err.Error()) - http.Error(w, err.Error(), 500) - return - } - - content, err := app.Res.ReadFile(contentPath) - if err != nil { - slog.Error(err.Error()) - http.Error(w, err.Error(), 500) - return - } - - t, err = t.Parse(string(content)) - if err != nil { - slog.Error(err.Error()) - http.Error(w, err.Error(), 500) - return - } - - err = t.Execute(w, data) + err := t.Execute(w, data) // Execute prebuilt template with dynamic data if err != nil { + err = fmt.Errorf("error executing template: %w", err) slog.Error(err.Error()) http.Error(w, err.Error(), 500) return