From 3dbd5a86e2ac4dffda03696dad4a30c1263f7f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lilian=20J=C3=B3nsd=C3=B3ttir?= Date: Wed, 24 Jan 2024 00:20:07 -0800 Subject: [PATCH] major refactor of render - split many parts of RenderTemplate into their own functions so they can be used individually - move web apps into their own page, add projects page, both generated by executing links.tmpl with their own yaml data - handle / specially, generating LinkMap from Handlers --- README.md | 5 +- cmd/web/routes.go | 3 + internal/handlers/handlers.go | 58 ++++++++++++++++-- internal/models/templatecache.go | 18 ++++++ internal/render/render.go | 101 ++++++++++++++++++++----------- internal/td/td.go | 15 +++-- static/css/style.css | 8 +++ templates/about.page.tmpl | 15 +---- templates/data/about.page.yaml | 10 --- templates/data/apps.yaml | 11 ++++ templates/data/projects.yaml | 25 ++++++++ templates/home.page.tmpl | 5 ++ templates/links.tmpl | 25 ++++++++ 13 files changed, 232 insertions(+), 67 deletions(-) create mode 100644 templates/data/apps.yaml create mode 100644 templates/data/projects.yaml create mode 100644 templates/links.tmpl diff --git a/README.md b/README.md index fe60969..c71ddad 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,14 @@ Code for the [burning.moe](https://burning.moe) homepage ## technologies used -- [go](https://go.dev) +- written in [go](https://go.dev) using: - [chi](https://github.com/go-chi/chi) - [cleanenv](https://github.com/ilyakaznacheev/cleanenv) - [log](https://github.com/charmbracelet/log) - [mage](https://github.com/magefile/mage) +- written using [helix](https://helix-editor.com/) +- tested on [void linux](https://voidlinux.org/) +- hosted on [debian linux](https://www.debian.org/) ## why? diff --git a/cmd/web/routes.go b/cmd/web/routes.go index bee268f..9e8f111 100644 --- a/cmd/web/routes.go +++ b/cmd/web/routes.go @@ -27,5 +27,8 @@ func routes(app *config.AppConfig) http.Handler { mux.Get(handler.Handles, handler.Handler) } + // Setup home handler + mux.Get("/", handlers.HomeHandler) + return mux } diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 8617a17..9c7e12c 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -1,10 +1,16 @@ package handlers import ( + "fmt" + "html/template" "net/http" + "strings" + "time" "git.burning.moe/celediel/burning.moe/internal/config" + "git.burning.moe/celediel/burning.moe/internal/models" "git.burning.moe/celediel/burning.moe/internal/render" + "git.burning.moe/celediel/burning.moe/internal/td" ) // Handler holds data required for handlers. @@ -17,15 +23,17 @@ var app *config.AppConfig // The actual handlers var Handlers = []Handler{ - // /about { Handles: "/about", Handler: makeBasicHandler("about"), }, - // / comes last { - Handles: "/", - Handler: makeBasicHandler("home"), + Handles: "/projects", + Handler: makeLinksHandler("projects"), + }, + { + Handles: "/apps", + Handler: makeLinksHandler("apps"), }, } @@ -34,6 +42,26 @@ func Initialise(a *config.AppConfig) { app = a } +// HomeHandler handles /, generating data from Handlers +func HomeHandler(w http.ResponseWriter, r *http.Request) { + app.Logger.Info("Got request for homepage") + d := td.MakeBasicTemplateData(time.Now()) + var pages []models.Link = []models.Link{} + + for _, handler := range Handlers { + href := strings.TrimPrefix(handler.Handles, "/") + pages = append(pages, models.Link{ + Href: template.URL(href), + Text: href, + }) + } + + d.LinkMap = make(map[string][]models.Link) + d.LinkMap["Pages"] = pages + app.Logger.Debug("handling home with some data", "data", &d) + render.RenderTemplateWithData(w, "home.page.tmpl", &d) +} + // makeBasicHandler returns a simple handler that renders a template from `name`.page.tmpl func makeBasicHandler(name string) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { @@ -42,3 +70,25 @@ func makeBasicHandler(name string) func(w http.ResponseWriter, r *http.Request) render.RenderTemplate(w, pageName) } } + +// makeLinksHandler returns a handler for links.tmpl with template data from `name` +func makeLinksHandler(name string) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + page := "links.tmpl" + template, err := render.GetTemplateFromCache(page) + if err != nil { + app.Logger.Error(fmt.Sprintf("couldn't get %s from cache", page), "err", err) + } + + app.Logger.Infof("Got request for %s links page", name) + data, err := td.LoadTemplateData(name) + if err != nil { + app.Logger.Fatal("couldn't load template data for "+name, "err", err) + } else { + data.StringMap["GeneratedAt"] = template.GeneratedAt.Format(time.UnixDate) + } + + app.Logger.Debug("handling a links page", "data", &data) + render.RenderTemplateWithData(w, page, &data) + } +} diff --git a/internal/models/templatecache.go b/internal/models/templatecache.go index 89163a9..4da6c13 100644 --- a/internal/models/templatecache.go +++ b/internal/models/templatecache.go @@ -1,7 +1,9 @@ package models import ( + "bytes" "html/template" + "net/http" "time" ) @@ -16,3 +18,19 @@ type TemplateCacheItem struct { Template *template.Template GeneratedAt time.Time } + +// Execute writes the template to the supplied writer using the supplied data. +func (self *TemplateCacheItem) Execute(d *TemplateData, w http.ResponseWriter) error { + buf := new(bytes.Buffer) + err := self.Template.Execute(buf, d) + if err != nil { + return err + } + + _, err = buf.WriteTo(w) + if err != nil { + return err + } + + return nil +} diff --git a/internal/render/render.go b/internal/render/render.go index 66aec35..f7f2871 100644 --- a/internal/render/render.go +++ b/internal/render/render.go @@ -1,7 +1,7 @@ package render import ( - "bytes" + "errors" "fmt" "html/template" "net/http" @@ -16,20 +16,20 @@ import ( const ( templatesDir string = "./templates/" layoutGlob string = "*.layout.tmpl" - pageGlob string = "*.page.tmpl" + pageGlob string = "*.tmpl" ) var app *config.AppConfig // Initialise the render package. func Initialise(a *config.AppConfig) { - var err error app = a if app.UseCache { + var err error app.TemplateCache, err = GenerateNewTemplateCache() - } - if err != nil { - app.Logger.Fatal("Error generating template cache, bailing out!") + if err != nil { + app.Logger.Fatal("Error generating template cache, bailing out!") + } } } @@ -83,30 +83,73 @@ func GenerateNewTemplateCache() (models.TemplateCache, error) { // RenderTemplate renders requested template (t), pulling from cache. func RenderTemplate(w http.ResponseWriter, filename string) { + // TODO: implement this better if !app.UseCache { - c, err := GenerateNewTemplateCache() - if err != nil { - app.Logger.Fatal("Error generating template cache, bailing out!") - } - app.TemplateCache = c + RegenerateTemplateCache() } - // Get templates from cache - template, ok := app.TemplateCache.Cache[filename] - if !ok { - app.Logger.Errorf("Couldn't get %s from template cache, dunno what happened, but we're gonna generate a new one", filename) - c, err := GenerateNewTemplateCache() - if err != nil { - app.Logger.Fatal("Error generating template cache, bailing out!") - } - app.TemplateCache = c - template = app.TemplateCache.Cache[filename] + template, err := GetTemplateFromCache(filename) + if err != nil { + app.Logger.Fatalf("Tried loading %s from the cache, but %s!", filename, err) + } + + data, err := GetOrGenerateTemplateData(filename) + if err != nil { + app.Logger.Error(err) + } + + app.Logger.Debug(fmt.Sprintf("Executing template %s", filename), "data", &data) + err = template.Execute(data, w) + if err != nil { + app.Logger.Fatalf("Failed to execute template %s: %s", filename, err) + } +} + +func RenderTemplateWithData(w http.ResponseWriter, filename string, data *models.TemplateData) { + if !app.UseCache { + RegenerateTemplateCache() + } + + template, err := GetTemplateFromCache(filename) + if err != nil { + app.Logger.Fatalf("Tried loading %s from the cache, but %s!", filename, err) + } + + app.Logger.Debug(fmt.Sprintf("Executing template %s", filename), "data", &data) + err = template.Execute(data, w) + if err != nil { + app.Logger.Fatalf("Failed to execute template %s: %s", filename, err) + } +} + +func RegenerateTemplateCache() { + c, err := GenerateNewTemplateCache() + if err != nil { + app.Logger.Fatal("Error generating template cache, bailing out!") + } + app.TemplateCache = c + +} + +// GetTemplateFromCache gets templates from cache +func GetTemplateFromCache(filename string) (*models.TemplateCacheItem, error) { + if template, ok := app.TemplateCache.Cache[filename]; ok { + return &template, nil + } else { + return &models.TemplateCacheItem{}, errors.New("Couldn't load template from cache") + } +} + +// GetOrGenerateTemplateData gets template data from file, or generate simple +func GetOrGenerateTemplateData(filename string) (*models.TemplateData, error) { + template, err := GetTemplateFromCache(filename) + if err != nil { + return &models.TemplateData{}, err } - // Get template data from file, or generate simple data, err := td.LoadTemplateData(filename) if err == nil { - app.Logger.Debug(fmt.Sprintf("Loaded data for template %s.", filename), "data", data) + app.Logger.Debug(fmt.Sprintf("Loaded data for template %s.", filename), "data", &data) if _, ok := data.StringMap["GeneratedAt"]; !ok { data.StringMap["GeneratedAt"] = template.GeneratedAt.Format(time.UnixDate) } @@ -115,15 +158,5 @@ func RenderTemplate(w http.ResponseWriter, filename string) { data = td.MakeBasicTemplateData(template.GeneratedAt) } - // Execute templates in a new buffer - buf := new(bytes.Buffer) - err = template.Template.Execute(buf, data) - if err != nil { - app.Logger.Fatal(fmt.Sprintf("Error executing template %s! Goodbye!", filename), "err", err) - } - - _, err = buf.WriteTo(w) - if err != nil { - app.Logger.Error(fmt.Sprintf("Error writing template %s!", filename), "err", err) - } + return &data, nil } diff --git a/internal/td/td.go b/internal/td/td.go index 3cef8bf..b9bde45 100644 --- a/internal/td/td.go +++ b/internal/td/td.go @@ -16,8 +16,8 @@ const dataDir string = "./templates/data/" // in order of precedence var dataExtensions = [4]string{"yml", "yaml", "toml", "json"} -// makeBasicTemplateData creates a blank TemplateData containing only the -// time the related template was generated +// makeBasicTemplateData creates a blank TemplateData +// containing only the time the template was generated func MakeBasicTemplateData(when time.Time) models.TemplateData { strMap := map[string]string{ "GeneratedAt": when.Format(time.UnixDate), @@ -33,13 +33,16 @@ func MakeBasicTemplateData(when time.Time) models.TemplateData { // fails, it returns an empty TemplateData and an error func LoadTemplateData(page string) (models.TemplateData, error) { var data models.TemplateData - output := dataDir + strings.ReplaceAll(page, "tmpl", "") + output := dataDir + strings.ReplaceAll(page, ".tmpl", "") for _, extension := range dataExtensions { - if info, err := os.Stat(output + extension); err == nil && !info.IsDir() { - err = cleanenv.ReadConfig(output+extension, &data) + if info, err := os.Stat(output + "." + extension); err == nil && !info.IsDir() { + err = cleanenv.ReadConfig(output+"."+extension, &data) if err == nil { - // don't try anymore files + // don't try anymore files, but do setup an empty StringMap + if len(data.StringMap) == 0 { + data.StringMap = make(map[string]string) + } return data, nil } } diff --git a/static/css/style.css b/static/css/style.css index 9f8499b..f201df7 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1,4 +1,12 @@ @import url('https://fonts.googleapis.com/css2?family=Gloria+Hallelujah&display=swap'); +/* + TODO: update this to be a bit more modern + maybe use SASS or something + + the real TODO here is finding an image that doesn't require a white background + + also TODO: proper mobile layout +*/ body { background: #FFF; diff --git a/templates/about.page.tmpl b/templates/about.page.tmpl index be9a983..2fb3414 100644 --- a/templates/about.page.tmpl +++ b/templates/about.page.tmpl @@ -9,16 +9,7 @@

links

- {{ range (index .LinkMap "Personal") }} - - - {{ .Text }} -
- {{ end }} - - -

hosted apps

- {{ range (index .LinkMap "HostedApps") }} + {{- range (index .LinkMap "Personal") }} {{ .Text }} @@ -26,8 +17,8 @@ {{ end }}
- - back + + back {{ end -}} diff --git a/templates/data/about.page.yaml b/templates/data/about.page.yaml index 49b709d..60f5472 100644 --- a/templates/data/about.page.yaml +++ b/templates/data/about.page.yaml @@ -14,13 +14,3 @@ LinkMap: - href: https://github.com/celediel text: github icon: bi:github - HostedApps: - - href: https://git.burning.moe/ - text: self-hosted git - icon: mdi:git - - href: https://bin.burning.moe/ - text: wastebin - icon: fluent:bin-recycle-20-filled - - href: https://gist.burning.moe/ - text: gist - icon: carbon:paste diff --git a/templates/data/apps.yaml b/templates/data/apps.yaml new file mode 100644 index 0000000..cdbc5c0 --- /dev/null +++ b/templates/data/apps.yaml @@ -0,0 +1,11 @@ +LinkMap: + hosted apps: + - href: https://git.burning.moe/ + text: self-hosted git + icon: mdi:git + - href: https://bin.burning.moe/ + text: wastebin + icon: fluent:bin-recycle-20-filled + - href: https://gist.burning.moe/ + text: gist + icon: carbon:paste diff --git a/templates/data/projects.yaml b/templates/data/projects.yaml new file mode 100644 index 0000000..2f581bc --- /dev/null +++ b/templates/data/projects.yaml @@ -0,0 +1,25 @@ +LinkMap: + morrowind lua mods: + - href: https://git.burning.moe/celediel/morrowind-a-sinking-feeling + text: a sinking feeling - armour causes sinking in water + icon: simple-icons:lua + - href: https://git.burning.moe/celediel/morrowind-no-more-friendly-fire + text: no more friendly fire - does what it says on the tin + icon: simple-icons:lua + - href: https://git.burning.moe/celediel/morrowind-door-randomizer + text: door randomizer - randomizes destinations of cell change doors + icon: simple-icons:lua + - href: https://git.burning.moe/celediel/morrowind-more-attentive-guards + text: more attentive guards - guards follow sneaking characters and help out when attacked unjustly + icon: simple-icons:lua + other modding: + - href: https://git.burning.moe/celediel/qud-mods + text: Assorted mods for Caves of Qud (C#) + icon: teenyicons:c-sharp-solid + misc: + - href: https://git.burning.moe/celediel/poiekolon.nvim + text: poiekolon - neovim plugin to add or toggle a char at the end of a line + icon: simple-icons:lua + - href: https://git.burning.moe/celediel/burning.moe + text: burning.moe - this website + icon: file-icons:go-old diff --git a/templates/home.page.tmpl b/templates/home.page.tmpl index 6ecbe41..05cc642 100644 --- a/templates/home.page.tmpl +++ b/templates/home.page.tmpl @@ -11,6 +11,11 @@
+ + {{ range (index .LinkMap "Pages")}} + {{ .Text }}
+ {{ end }}
{{ end -}} diff --git a/templates/links.tmpl b/templates/links.tmpl new file mode 100644 index 0000000..706446d --- /dev/null +++ b/templates/links.tmpl @@ -0,0 +1,25 @@ +{{- template "base" . -}} +{{- define "content" }} + + {{- range $key, $value := .LinkMap }} +

{{ $key }}

+ {{- range $value }} + + {{- if (ne .Icon "") }} + + {{ end }} + {{- .Text }} +
+ {{- end }} + {{ end -}} +
+ + + back + +
+{{ end -}} + +{{- define "js" }} + +{{ end -}}