- Add urlEncode template function to properly encode prefix URLs - Move prefix_length.html to embedded templates with function map - Prevents broken links for prefixes containing slashes
140 lines
3.2 KiB
Go
140 lines
3.2 KiB
Go
// 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
|
|
}
|