package web import ( "bytes" "fmt" "html/template" "net/http" "strings" "time" ) // pageNames are the templates with a full page each; "layout" wraps them. Fragments (for htmx) are defined inside // page files and rendered on their own. var pageNames = []string{"home", "signin", "signup", "account", "device", "online", "console", "console_people", "console_person", "error"} var funcs = template.FuncMap{ "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("%d min ago", int(d.Minutes())) case d < 48*time.Hour: return fmt.Sprintf("%d h ago", int(d.Hours())) } return t.Format("2 Jan 2006") }, "date": func(t time.Time) string { return t.Format("2 Jan 2006 15:04") }, "upper": strings.ToUpper, } func (s *Server) parseTemplates() error { s.pages = map[string]*template.Template{} for _, name := range pageNames { t, err := template.New("layout.html").Funcs(funcs).ParseFS(templateFiles, "templates/layout.html", "templates/"+name+".html") if err != nil { return err } s.pages[name] = t } return nil } // render shows a page in the layout. data is the page's own values; the layout gets the person and CSRF token. func (s *Server) render(q *request, name string, data map[string]any) { if data == nil { data = map[string]any{} } data["Person"] = q.person data["CSRF"] = q.csrf data["Flash"] = q.flash data["Page"] = name var buf bytes.Buffer if err := s.pages[name].ExecuteTemplate(&buf, "layout.html", data); err != nil { s.Log.Printf("rendering %s: %v", name, err) http.Error(q.w, "Something went wrong on our side.", http.StatusInternalServerError) return } q.w.Header().Set("Content-Type", "text/html; charset=utf-8") q.w.Header().Set("Cache-Control", "no-store") buf.WriteTo(q.w) } // fragment shows one template from a page's file, without the layout (htmx swaps it in). func (s *Server) fragment(q *request, page, name string, data map[string]any) { if data == nil { data = map[string]any{} } data["Person"] = q.person data["CSRF"] = q.csrf var buf bytes.Buffer if err := s.pages[page].ExecuteTemplate(&buf, name, data); err != nil { s.Log.Printf("rendering %s/%s: %v", page, name, err) http.Error(q.w, "Something went wrong on our side.", http.StatusInternalServerError) return } q.w.Header().Set("Content-Type", "text/html; charset=utf-8") q.w.Header().Set("Cache-Control", "no-store") buf.WriteTo(q.w) }