// Package templates provides embedded HTML templates for the RouteWatch application package templates import ( _ "embed" "html/template" "net/url" "sync" "time" ) //go:embed status.html var statusHTML string //go:embed as_detail.html var asDetailHTML string //go:embed prefix_detail.html var prefixDetailHTML string //go:embed prefix_length.html var prefixLengthHTML string // Templates contains all parsed templates type Templates struct { Status *template.Template ASDetail *template.Template PrefixDetail *template.Template PrefixLength *template.Template } var ( //nolint:gochecknoglobals // Singleton pattern for templates defaultTemplates *Templates //nolint:gochecknoglobals // Singleton pattern for templates once sync.Once ) const ( hoursPerDay = 24 daysPerMonth = 30 ) // timeSince returns a human-readable duration since the given time func timeSince(t time.Time) string { duration := time.Since(t) if duration < time.Minute { return "just now" } if duration < time.Hour { minutes := int(duration.Minutes()) if minutes == 1 { return "1 minute ago" } return duration.Truncate(time.Minute).String() + " ago" } if duration < hoursPerDay*time.Hour { hours := int(duration.Hours()) if hours == 1 { return "1 hour ago" } return duration.Truncate(time.Hour).String() + " ago" } days := int(duration.Hours() / hoursPerDay) if days == 1 { return "1 day ago" } if days < daysPerMonth { return duration.Truncate(hoursPerDay*time.Hour).String() + " ago" } return t.Format("2006-01-02") } // initTemplates parses all embedded templates func initTemplates() { var err error defaultTemplates = &Templates{} // Create common template functions funcs := template.FuncMap{ "timeSince": timeSince, "urlEncode": url.QueryEscape, } // Parse status template defaultTemplates.Status, err = template.New("status").Parse(statusHTML) if err != nil { panic("failed to parse status template: " + err.Error()) } // Parse AS detail template defaultTemplates.ASDetail, err = template.New("asDetail").Funcs(funcs).Parse(asDetailHTML) if err != nil { panic("failed to parse AS detail template: " + err.Error()) } // Parse prefix detail template defaultTemplates.PrefixDetail, err = template.New("prefixDetail").Funcs(funcs).Parse(prefixDetailHTML) if err != nil { panic("failed to parse prefix detail template: " + err.Error()) } // Parse prefix length template defaultTemplates.PrefixLength, err = template.New("prefixLength").Funcs(funcs).Parse(prefixLengthHTML) if err != nil { panic("failed to parse prefix length template: " + err.Error()) } } // Get returns the singleton Templates instance func Get() *Templates { once.Do(initTemplates) return defaultTemplates } // StatusTemplate returns the parsed status template func StatusTemplate() *template.Template { return Get().Status } // ASDetailTemplate returns the parsed AS detail template func ASDetailTemplate() *template.Template { return Get().ASDetail } // PrefixDetailTemplate returns the parsed prefix detail template func PrefixDetailTemplate() *template.Template { return Get().PrefixDetail } // PrefixLengthTemplate returns the parsed prefix length template func PrefixLengthTemplate() *template.Template { return Get().PrefixLength }