check / check (push) Canceled after 0s
The home page gets cards for the newest and oldest kept route, the newest and oldest updated AS, the AS with the most prefixes, the ASes with a single prefix and fifteen well-known networks. Two more cards count the bogon announcements and the prefixes with several origin ASes. They link to the new pages /bogons and /multiple-origins, which list up to 1,000 entries each. The new package internal/bogon tells reserved prefixes and AS numbers, from the IANA special-purpose and address space registries. Table.Overview walks the table and lets the read lock go every 10,000 prefixes. The server works the cards out once a minute and serves that copy. A new index on asns(last_seen) finds the newest and oldest updated AS. Model: opus-5-5
186 lines
5.3 KiB
Go
186 lines
5.3 KiB
Go
// 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
|
|
|
|
//go:embed bogons.html
|
|
var bogonsHTML string
|
|
|
|
//go:embed multiple_origins.html
|
|
var multipleOriginsHTML 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
|
|
// Bogons is the template of the page that lists the bogon announcements
|
|
Bogons *template.Template
|
|
// MultipleOrigins is the template of the page that lists the prefixes
|
|
// announced by more than one origin AS
|
|
MultipleOrigins *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())
|
|
}
|
|
|
|
defaultTemplates.Bogons, err = template.New("bogons").
|
|
Funcs(funcs).Parse(bogonsHTML)
|
|
if err != nil {
|
|
panic("failed to parse bogons template: " + err.Error())
|
|
}
|
|
|
|
defaultTemplates.MultipleOrigins, err = template.New("multipleOrigins").
|
|
Funcs(funcs).Parse(multipleOriginsHTML)
|
|
if err != nil {
|
|
panic("failed to parse multiple origins 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
|
|
}
|
|
|
|
// BogonsTemplate returns the parsed template of the bogon announcements page
|
|
func BogonsTemplate() *template.Template {
|
|
return Get().Bogons
|
|
}
|
|
|
|
// MultipleOriginsTemplate returns the parsed template of the page of prefixes
|
|
// announced by more than one origin AS
|
|
func MultipleOriginsTemplate() *template.Template {
|
|
return Get().MultipleOrigins
|
|
}
|