package main import ( "bytes" "embed" "html/template" "log" "net/http" "time" "github.com/yuin/goldmark" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/parser" ) var ( //go:embed templates/*.html templateFS embed.FS //go:embed static staticFS embed.FS //go:embed about.md aboutMarkdown []byte ) // newMarkdown returns a CommonMark renderer with GitHub-style extensions. // Raw HTML in posts is omitted (goldmark's default). func newMarkdown() goldmark.Markdown { return goldmark.New( goldmark.WithExtensions(extension.GFM), goldmark.WithParserOptions(parser.WithAutoHeadingID()), ) } // pageData is passed to every template. type pageData struct { Site string Config siteConfig Posts []*Post Post *Post Newer, Older *Post Status int Message string // About page only. Version, Commit string About template.HTML } // loadTemplates parses each page template together with the shared layout. func loadTemplates() (map[string]*template.Template, error) { funcs := template.FuncMap{ "date": func(t time.Time) string { return t.Format("2 January 2006") }, "isodate": func(t time.Time) string { return t.Format(time.DateOnly) }, } base, err := template.New("layout.html").Funcs(funcs).ParseFS(templateFS, "templates/layout.html") if err != nil { return nil, err } pages := map[string]*template.Template{} for _, name := range []string{"index", "post", "about", "error"} { t, err := base.Clone() if err != nil { return nil, err } if pages[name], err = t.ParseFS(templateFS, "templates/"+name+".html"); err != nil { return nil, err } } return pages, nil } // render executes a page into a buffer first so a template error becomes a // clean 500 instead of a half-written page. func (a *app) render(w http.ResponseWriter, status int, page string, data pageData) { data.Site = a.siteName data.Config = a.config.Get() var buf bytes.Buffer if err := a.pages[page].ExecuteTemplate(&buf, "layout.html", data); err != nil { log.Printf("render %s: %v", page, err) http.Error(w, "internal server error", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(status) buf.WriteTo(w) }