// Package templates provides embedded HTML templates for the RouteWatch application package templates import ( _ "embed" "html/template" "net/url" "strings" "sync" "git.eeqj.de/sneak/routewatch/internal/version" "github.com/dustin/go-humanize" ) //go:embed index.html var indexHTML string //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 { // Index is the template for the home page Index *template.Template // Status is the template for the main status page Status *template.Template // ASDetail is the template for displaying AS (Autonomous System) details ASDetail *template.Template // PrefixDetail is the template for displaying prefix details PrefixDetail *template.Template // PrefixLength is the template for displaying prefixes by length PrefixLength *template.Template } var ( //nolint:gochecknoglobals // Singleton pattern for templates defaultTemplates *Templates //nolint:gochecknoglobals // Singleton pattern for templates once sync.Once ) const cidrPartCount = 2 // A CIDR has two parts: prefix and length // prefixURL generates a URL path for a prefix in CIDR notation. // Takes a prefix like "192.168.1.0/24" and returns "/prefix/192.168.1.0/24" // with the prefix part URL-encoded to handle IPv6 colons. func prefixURL(cidr string) string { // Split CIDR into prefix and length parts := strings.SplitN(cidr, "/", cidrPartCount) if len(parts) != cidrPartCount { // Fallback if no slash found return "/prefix/" + url.PathEscape(cidr) + "/0" } return "/prefix/" + url.PathEscape(parts[0]) + "/" + parts[1] } // initTemplates parses all embedded templates func initTemplates() { var err error defaultTemplates = &Templates{} // Create common template functions funcs := template.FuncMap{ "timeSince": humanize.Time, "urlEncode": url.QueryEscape, "prefixURL": prefixURL, "appName": func() string { return version.Name }, "appAuthor": func() string { return version.Author }, "appAuthorURL": func() string { return version.AuthorURL }, "appLicense": func() string { return version.License }, "appRepoURL": func() string { return version.RepoURL }, "appGitRevision": func() string { return version.GitRevisionShort }, "appGitCommitURL": version.CommitURL, } // Parse index template defaultTemplates.Index, err = template.New("index").Funcs(funcs).Parse(indexHTML) if err != nil { panic("failed to parse index template: " + err.Error()) } // Parse status template defaultTemplates.Status, err = template.New("status").Funcs(funcs).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 } // IndexTemplate returns the parsed index template func IndexTemplate() *template.Template { return Get().Index } // 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 }