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)
}