Implement AS and prefix detail pages
- Implement handleASDetail() and handlePrefixDetail() HTML handlers - Create AS detail HTML template with prefix listings - Create prefix detail HTML template with route information - Add timeSince template function for human-readable durations - Update templates.go to include new templates - Server-side rendered pages as requested (no client-side API calls)
This commit is contained in:
@@ -5,14 +5,23 @@ import (
|
||||
_ "embed"
|
||||
"html/template"
|
||||
"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
|
||||
|
||||
// Templates contains all parsed templates
|
||||
type Templates struct {
|
||||
Status *template.Template
|
||||
Status *template.Template
|
||||
ASDetail *template.Template
|
||||
PrefixDetail *template.Template
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -22,17 +31,72 @@ var (
|
||||
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,
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the singleton Templates instance
|
||||
@@ -46,3 +110,13 @@ func Get() *Templates {
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user