package web import ( "html/template" "time" ) var funcMap = template.FuncMap{ "fmtTime": func(t time.Time) string { if t.IsZero() { return "—" } return t.UTC().Format("2006-01-02 15:04:05") }, "uptimeClass": func(pct float64) string { switch { case pct >= 99: return "good" case pct >= 95: return "warn" default: return "bad" } }, } var ( indexTmpl = template.Must(template.New("index").Funcs(funcMap).Parse(indexHTML)) historyTmpl = template.Must(template.New("history").Funcs(funcMap).Parse(historyHTML)) incidentsTmpl = template.Must(template.New("incidents").Funcs(funcMap).Parse(incidentsHTML)) statusTmpl = template.Must(template.New("status").Funcs(funcMap).Parse(statusHTML)) ) const sharedCSS = ` *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Courier New', monospace; background: #0f0f0f; color: #d4d4d4; padding: 2rem; } h1 { color: #e2e2e2; margin-bottom: 0.25rem; font-size: 1.4rem; letter-spacing: 0.05em; } .subtitle { color: #666; font-size: 0.8rem; margin-bottom: 1.5rem; } nav { margin-bottom: 1.5rem; } nav a { color: #6b9edb; text-decoration: none; margin-right: 1.5rem; font-size: 0.85rem; } nav a:hover { color: #90bfff; } table { border-collapse: collapse; width: 100%; font-size: 0.875rem; } th { text-align: left; padding: 0.5rem 1rem; color: #888; font-weight: normal; border-bottom: 1px solid #2a2a2a; } td { padding: 0.5rem 1rem; border-bottom: 1px solid #1e1e1e; } tr:hover td { background: #161616; } .badge { display: inline-block; padding: 2px 8px; border-radius: 3px; font-size: 0.75rem; font-weight: bold; letter-spacing: 0.05em; } .badge-up { background: #1a3a1a; color: #4caf50; border: 1px solid #2d5a2d; } .badge-down { background: #3a1a1a; color: #f44336; border: 1px solid #5a2d2d; } .good { color: #4caf50; } .warn { color: #ff9800; } .bad { color: #f44336; } .dim { color: #555; font-size: 0.8rem; }` const sharedNav = `` const indexHTML = ` arcline-uptime

arcline-uptime

Auto-refreshes every 30s — {{fmtTime .Now}} UTC

` + sharedNav + ` {{range .Monitors}} {{end}}
Monitor Status Response 24h 7d 30d Last Check Info
{{.Name}} {{if .Up}}UP{{else}}DOWN{{end}} {{if .ResponseMS}}{{.ResponseMS}}ms{{else}}{{end}} {{printf "%.2f" .Uptime24h}}% {{printf "%.2f" .Uptime7d}}% {{printf "%.2f" .Uptime30d}}% {{fmtTime .CheckedAt}} {{if .Error}}{{.Error}}{{else}}—{{end}}
` const historyHTML = ` arcline-uptime — History ` + sharedNav + `

Response Time History (last 100 checks per monitor)

{{range .}}

{{.Name}}

{{if .Points}}
{{range .DownDotXs}} {{end}}

max: {{.MaxMS}}ms   avg: {{printf "%.0f" .AvgMS}}ms   checks: {{.Count}}

{{else}}

No data yet.

{{end}} {{end}} ` const incidentsHTML = ` arcline-uptime — Incidents ` + sharedNav + `

Incident Log

All outage periods, newest first.

{{if .}} {{range .}} {{end}}
Monitor Started Resolved Duration Status
{{.MonitorName}} {{fmtTime .StartedAt}} {{if .Ongoing}}—{{else}}{{fmtTime .ResolvedAt}}{{end}} {{.Duration}} {{if .Ongoing}}ONGOING{{else}}resolved{{end}}
{{else}}

No incidents recorded.

{{end}} ` const statusHTML = ` Service Status

Service Status

{{if .AllUp}} {{else}} {{end}} {{range .Monitors}} {{end}}
ServiceStatusUptime (24h)
{{.Name}} {{if .Up}}Operational{{else}}Disrupted{{end}} {{printf "%.2f" .Uptime24h}}%
`