75 lines
1.9 KiB
Go
75 lines
1.9 KiB
Go
package web
|
|
|
|
import (
|
|
"embed"
|
|
"fmt"
|
|
"html/template"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
//go:embed templates
|
|
var templateFS embed.FS
|
|
|
|
var funcMap = template.FuncMap{
|
|
"upper": strings.ToUpper,
|
|
"lower": strings.ToLower,
|
|
"formatDate": func(t time.Time) string { return t.Format("2006-01-02") },
|
|
"formatTime": func(t time.Time) string { return t.Format("2006-01-02 15:04") },
|
|
"ago": func(t time.Time) string {
|
|
d := time.Since(t)
|
|
switch {
|
|
case d < time.Minute:
|
|
return "just now"
|
|
case d < time.Hour:
|
|
return fmt.Sprintf("%dm ago", int(d.Minutes()))
|
|
case d < 24*time.Hour:
|
|
return fmt.Sprintf("%dh ago", int(d.Hours()))
|
|
default:
|
|
return fmt.Sprintf("%dd ago", int(d.Hours()/24))
|
|
}
|
|
},
|
|
"pct": func(f float64) string { return fmt.Sprintf("%.2f%%", f) },
|
|
}
|
|
|
|
// parse returns a template set containing base.html and the named page.
|
|
// Parsing per-request ensures each page's {{define "content"}} is isolated.
|
|
func parse(name string) (*template.Template, error) {
|
|
return template.New("").Funcs(funcMap).ParseFS(templateFS, "templates/base.html", "templates/"+name)
|
|
}
|
|
|
|
type pageData struct {
|
|
Title string
|
|
Username string
|
|
IsAdmin bool
|
|
Flash string
|
|
Path string
|
|
Data any
|
|
}
|
|
|
|
func render(w http.ResponseWriter, r *http.Request, name string, title string, data any) {
|
|
pd := pageData{
|
|
Title: title,
|
|
Path: r.URL.Path,
|
|
Data: data,
|
|
}
|
|
// Inject client info from context if present.
|
|
if c := clientFromCtx(r); c != nil {
|
|
pd.Username = c.DisplayName
|
|
pd.IsAdmin = c.IsAdmin
|
|
}
|
|
// Flash message from query param (redirect-after-post pattern).
|
|
pd.Flash = r.URL.Query().Get("flash")
|
|
|
|
t, err := parse(name)
|
|
if err != nil {
|
|
http.Error(w, "template error: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
if err := t.ExecuteTemplate(w, "base", pd); err != nil {
|
|
http.Error(w, "template error: "+err.Error(), http.StatusInternalServerError)
|
|
}
|
|
}
|