diff --git a/exporter/html.go b/exporter/html.go index e8537e22..137d31d4 100644 --- a/exporter/html.go +++ b/exporter/html.go @@ -12,22 +12,7 @@ import ( var htmlTemplate string func HTMLExporter(s *store.Store) http.HandlerFunc { - tmpl := template.Must(template.New("status").Funcs(template.FuncMap{ - "each_runes": func(s string) []string { - r := make([]string, len(s)) - for i, c := range []rune(s) { - r[i] = string(c) - } - return r - }, - "invert_incidents": func(xs []frozenIncident) []frozenIncident { - rs := make([]frozenIncident, len(xs)) - for i, x := range xs { - rs[len(xs)-i-1] = x - } - return rs - }, - }).Parse(htmlTemplate)) + tmpl := template.Must(template.New("status.html").Funcs(templateFuncs).Parse(htmlTemplate)) return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=UTF-8") diff --git a/exporter/templates.go b/exporter/templates.go new file mode 100644 index 00000000..b78c769e --- /dev/null +++ b/exporter/templates.go @@ -0,0 +1,41 @@ +package exporter + +import ( + "strings" +) + +var ( + templateFuncs = map[string]interface{}{ + "each_runes": func(s string) []string { + r := make([]string, len(s)) + for i, c := range []rune(s) { + r[i] = string(c) + } + return r + }, + "invert_incidents": func(xs []frozenIncident) []frozenIncident { + rs := make([]frozenIncident, len(xs)) + for i, x := range xs { + rs[len(xs)-i-1] = x + } + return rs + }, + "break_text": func(s string, width int) []string { + r := []string{} + for start := 0; start < len(s); start += width { + end := start + width + if end >= len(s) { + end = len(s) + } + r = append(r, s[start:end]) + } + return r + }, + "align_center": func(s string, width int) string { + if len(s) > width { + return s + } + return strings.Repeat(" ", (width-len(s))/2) + s + }, + } +) diff --git a/exporter/templates/status.txt b/exporter/templates/status.txt new file mode 100644 index 00000000..fed759ed --- /dev/null +++ b/exporter/templates/status.txt @@ -0,0 +1,44 @@ +───────────────────────────────┤ Current Status ├─────────────────────────────── +{{ range .CurrentStatus }} +┌─ {{ .Target }} +└{{ range .History | each_runes }}{{ + if eq . "-" }}─{{ end }}{{ + if eq . "?" }}━{{ end }}{{ + if eq . "F" }}!{{ end }}{{ + if eq . "O" }}✓{{ end }}{{ end }}┤ updated: {{ .Updated }} +{{ end }} + +──────────────────────────────┤ Current Incident ├────────────────────────────── +{{ range .CurrentIncidents | invert_incidents }} +┳━ {{ + if eq .Status "FAIL" + }}!FAILURE!{{ + else + }}━UNKNOWN━{{ + end + }} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳ +┃{{ printf "%-78s" .Target }}┃ +┃ {{ printf "%-77s" (printf "%s - continue" .CausedAt) }}┃ +┃ ┃{{ range (break_text .Message 78) }} +┃{{ printf "%-78s" . }}┃{{ end }} +┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻ +{{ end }} + +──────────────────────────────┤ Incident History ├────────────────────────────── +{{ range .IncidentHistory | invert_incidents }} +┌─ {{ + if eq .Status "FAIL" + }}!FAILURE!{{ + else + }}━UNKNOWN━{{ + end + }} ──────────────────────────────────────────────────────────────────┐ +│{{ printf "%-78s" .Target }}│ +│ {{ printf "%-77s" (printf "%s - %s" .CausedAt .ResolvedAt) }}│ +│ │{{ range (break_text .Message 78) }} +│{{ printf "%-78s" . }}│{{ end }} +└──────────────────────────────────────────────────────────────────────────────┘ +{{ end }} + + ────────────────────────────── +{{ align_center (printf "Reported by Ayd? (%s)" .ReportedAt) 80 }} diff --git a/exporter/text.go b/exporter/text.go index 85fec6a9..15d3aca9 100644 --- a/exporter/text.go +++ b/exporter/text.go @@ -1,100 +1,22 @@ package exporter import ( - "fmt" + _ "embed" "net/http" - "strings" - "time" + "text/template" "github.com/macrat/ayd/store" ) -func TextExporter(s *store.Store) http.HandlerFunc { - showIncidentBox := func(w http.ResponseWriter, i *store.Incident, bold bool) { - banner := "━UNKNOWN━" - if i.Status == store.STATUS_FAIL { - banner = "!FAILURE!" - } - - vert := "" - if bold { - fmt.Fprintf(w, "┳━ %s ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳\n", banner) - vert = "┃" - } else { - fmt.Fprintf(w, "┌─ %s ──────────────────────────────────────────────────────────────────┐\n", banner) - vert = "│" - } - - fmt.Fprintf(w, "%s%-78s%s\n", vert, i.Target, vert) - - period := fmt.Sprintf("%s - continue", i.CausedAt.Format(time.RFC3339)) - if !i.ResolvedAt.IsZero() { - period = fmt.Sprintf("%s - %s", i.CausedAt.Format(time.RFC3339), i.ResolvedAt.Format(time.RFC3339)) - } - fmt.Fprintf(w, "%s %-77s%s\n", vert, period, vert) - - fmt.Fprint(w, vert, strings.Repeat(" ", 78), vert, "\n") +//go:embed templates/status.txt +var textTemplate string - for offset := 0; offset < len(i.Message); offset += 78 { - end := offset + 78 - if end >= len(i.Message) { - end = len(i.Message) - 1 - } - fmt.Fprintf(w, "%s%-78s%s\n", vert, i.Message[offset:end], vert) - } - - if bold { - fmt.Fprintln(w, "┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻") - } else { - fmt.Fprintln(w, "└──────────────────────────────────────────────────────────────────────────────┘") - } - } +func TextExporter(s *store.Store) http.HandlerFunc { + tmpl := template.Must(template.New("status.txt").Funcs(templateFuncs).Parse(textTemplate)) return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=UTF-8") - fmt.Fprintln(w, "───────────────────────────────┤ Current Status ├───────────────────────────────") - fmt.Fprintln(w) - - for _, history := range s.ProbeHistory.AsSortedArray() { - fmt.Fprint(w, " ┌─ ", history.Target, "\n") - fmt.Fprint(w, " └", strings.Repeat("─", store.PROBE_HISTORY_LEN-len(history.Results))) - - for _, r := range history.Results { - switch r.Status { - case store.STATUS_OK: - fmt.Fprintf(w, "✓") - case store.STATUS_FAIL: - fmt.Fprintf(w, "!") - default: - fmt.Fprintf(w, "━") - } - } - - fmt.Fprint(w, "┤ updated: ", history.Results[len(history.Results)-1].CheckedAt.Format(time.RFC3339)) - - fmt.Fprintln(w) - } - - fmt.Fprintln(w) - fmt.Fprintln(w) - fmt.Fprintln(w, "──────────────────────────────┤ Incident History ├──────────────────────────────") - fmt.Fprintln(w) - - for i := range s.CurrentIncidents { - showIncidentBox(w, s.CurrentIncidents[len(s.CurrentIncidents)-1-i], true) - } - - for i := range s.IncidentHistory { - showIncidentBox(w, s.IncidentHistory[len(s.IncidentHistory)-1-i], false) - } - - fmt.Fprintln(w) - fmt.Fprintln(w) - - footer := "Reported by Ayd? (" + time.Now().Format(time.RFC3339) + ")" - pad := strings.Repeat(" ", (80-len(footer))/2) - fmt.Fprint(w, pad, strings.Repeat("─", len(footer)), "\n") - fmt.Fprint(w, pad, footer, "\n") + tmpl.Execute(w, freezeStatus(s)) } }