repos
/ orchard main

orchard

mirror

Every site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.

blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics

1.6 KB · 55 lines · Go Raw History
 1package web
 2
 3import (
 4	"bytes"
 5	"fmt"
 6	"html/template"
 7	"io/fs"
 8	"net/http"
 9)
10
11// Renderer parses a base layout plus one template per page, and renders into a
12// buffer before writing. Buffering is what makes a mid-render template error
13// recoverable: writing straight to the ResponseWriter would send the 200 and
14// half a page before anything failed.
15type Renderer struct {
16	pages map[string]*template.Template
17}
18
19// NewRenderer parses every page template against the given base layouts.
20// Layouts go into each page's own set, so a page can both {{define}} blocks and
21// {{template}} the shared chrome.
22func NewRenderer(files fs.FS, funcs template.FuncMap, layouts []string, pages []string) (*Renderer, error) {
23	r := &Renderer{pages: make(map[string]*template.Template, len(pages))}
24
25	for _, page := range pages {
26		patterns := append(append([]string{}, layouts...), page)
27		t, err := template.New("base.html").Funcs(funcs).ParseFS(files, patterns...)
28		if err != nil {
29			return nil, fmt.Errorf("parse %s: %w", page, err)
30		}
31		r.pages[page] = t
32	}
33
34	return r, nil
35}
36
37// Render writes a page, or a 500 rather than a half-written body.
38func (r *Renderer) Render(w http.ResponseWriter, status int, page string, data any) {
39	t, ok := r.pages[page]
40	if !ok {
41		http.Error(w, "internal server error", http.StatusInternalServerError)
42		panic("render: no such template " + page)
43	}
44
45	var buf bytes.Buffer
46	if err := t.Execute(&buf, data); err != nil {
47		http.Error(w, "internal server error", http.StatusInternalServerError)
48		panic(fmt.Sprintf("render %s: %v", page, err))
49	}
50
51	w.Header().Set("Content-Type", "text/html; charset=utf-8")
52	w.WriteHeader(status)
53	_, _ = buf.WriteTo(w)
54}