internal/web/web.go

4f3bd4893bc9923a02b86ae2cbae1fb17e019d2c
gitbay/internal/web/web.go history · blame · raw

69 lines · 1676 bytes

 1// Package web holds the server-rendered templates and static assets for the
 2// read-only UI. No JavaScript, no build step.
 3package web
 4
 5import (
 6	"embed"
 7	"html/template"
 8	"io"
 9	"runtime/debug"
10	"sync"
11	"time"
12)
13
14//go:embed templates/*.html
15var templateFS embed.FS
16
17//go:embed static/style.css
18var StyleCSS []byte
19
20//go:embed static/favicon.svg
21var FaviconSVG []byte
22
23// version returns the short VCS revision baked into the binary, or "" when
24// built outside a checkout. Used by the layout footer.
25var version = sync.OnceValue(func() string {
26	info, ok := debug.ReadBuildInfo()
27	if !ok {
28		return ""
29	}
30	for _, s := range info.Settings {
31		if s.Key == "vcs.revision" && len(s.Value) >= 10 {
32			return s.Value[:10]
33		}
34	}
35	return ""
36})
37
38var funcs = template.FuncMap{
39	"gitbayVersion": func() string { return version() },
40	// short abbreviates a commit SHA for display.
41	"short": func(s string) string {
42		if len(s) > 10 {
43			return s[:10]
44		}
45		return s
46	},
47	"add": func(a, b int) int { return a + b },
48	"sub": func(a, b int) int { return a - b },
49	// when formats a stored RFC3339 timestamp for display; unparseable
50	// values pass through unchanged.
51	"when": func(s string) string {
52		t, err := time.Parse(time.RFC3339Nano, s)
53		if err != nil {
54			return s
55		}
56		return t.UTC().Format("2006-01-02 15:04")
57	},
58}
59
60// Render executes the named page template with the shared layout.
61func Render(w io.Writer, page string, data any) error {
62	t, err := template.Must(
63		template.New("layout.html").Funcs(funcs).ParseFS(templateFS, "templates/layout.html"),
64	).ParseFS(templateFS, "templates/"+page)
65	if err != nil {
66		return err
67	}
68	return t.ExecuteTemplate(w, "layout", data)
69}