// Package handler contains all HTTP request handlers for the paste service. package handler import ( "html/template" "log" "net/http" "os" "path/filepath" "time" "ridgwaysystems.org/paste/internal/paste" ) // Handler holds shared dependencies for all HTTP handlers. type Handler struct { store *paste.Store siteURL string devMode bool templates map[string]*template.Template } // New creates a Handler. func New(store *paste.Store) *Handler { h := &Handler{ store: store, siteURL: getenv("SITE_URL", "https://paste.ridgwaysystems.org"), devMode: os.Getenv("DEV") == "1", } if !h.devMode { h.templates = mustLoadTemplates() } return h } func getenv(key, def string) string { if v := os.Getenv(key); v != "" { return v } return def } func (h *Handler) tmpl(name string) *template.Template { if h.devMode { return mustLoadTemplates()[name] } return h.templates[name] } func mustLoadTemplates() map[string]*template.Template { m := make(map[string]*template.Template) funcMap := template.FuncMap{ "formatDate": func(t time.Time) string { if t.IsZero() { return "" } return t.Format("2 January 2006") }, "formatDateTime": func(t time.Time) string { if t.IsZero() { return "" } return t.Format("2 Jan 2006 15:04 UTC") }, "expiryStr": func(p *paste.Paste) string { if p.ExpiresAt == nil { return "never" } if p.Expired() { return "expired" } return p.ExpiresAt.Format("2 Jan 2006 15:04 UTC") }, "isAdmin": func(r *http.Request) bool { return isAuthenticated(r) }, } base := "templates/base.html" pages := []struct { name string file string }{ {"list", "templates/list.html"}, {"paste", "templates/paste.html"}, {"new", "templates/new.html"}, {"admin-login", "templates/admin/login.html"}, {"admin-dashboard", "templates/admin/dashboard.html"}, } for _, p := range pages { t, err := template.New(filepath.Base(p.file)).Funcs(funcMap).ParseFiles(base, p.file) if err != nil { log.Fatalf("template %s: %v", p.name, err) } m[p.name] = t } return m } // envelope wraps page-specific data for the base template. type envelope struct { Inner any IsAdmin bool } func (h *Handler) render(w http.ResponseWriter, r *http.Request, name string, data any) { t := h.tmpl(name) if t == nil { http.Error(w, "template not found: "+name, http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") env := envelope{Inner: data, IsAdmin: isAuthenticated(r)} if err := t.ExecuteTemplate(w, "base", env); err != nil { log.Printf("render %s: %v", name, err) } } func (h *Handler) renderErr(w http.ResponseWriter, code int, msg string) { http.Error(w, msg, code) }