zdravko/internal/handlers/index.go

172 lines
4 KiB
Go

package handlers
import (
"context"
"net/http"
"time"
"code.tjo.space/mentos1386/zdravko/internal/models"
"code.tjo.space/mentos1386/zdravko/internal/services"
"code.tjo.space/mentos1386/zdravko/web/templates/components"
"github.com/labstack/echo/v4"
)
type IndexData struct {
*components.Base
HealthChecks []*HealthCheck
HealthchecksLength int
TimeRange string
Status string
}
type HealthCheck struct {
Name string
Status string
HistoryDaily *History
HistoryHourly *History
}
type History struct {
History []string
Uptime int
}
func getDay(date time.Time) string {
return date.Format("2006-01-02")
}
func getHour(date time.Time) string {
return date.Format("2006-01-02T15:04")
}
func getDailyHistory(history []models.HealthcheckHistory) *History {
numDays := 90
historyDailyMap := map[string]string{}
numOfSuccess := 0
numTotal := 1
for i := 0; i < numDays; i++ {
day := getDay(time.Now().AddDate(0, 0, -i).Truncate(time.Hour * 24))
historyDailyMap[day] = models.HealthcheckUnknown
}
for _, _history := range history {
day := getDay(_history.CreatedAt.Truncate(time.Hour * 24))
// skip if day is not in the last 90 days
if _, ok := historyDailyMap[day]; !ok {
continue
}
numTotal++
if _history.Status == models.HealthcheckSuccess {
numOfSuccess++
}
// skip if day is already set to failure
if historyDailyMap[day] == models.HealthcheckFailure {
continue
}
historyDailyMap[day] = _history.Status
}
historyDaily := make([]string, numDays)
for i := 0; i < numDays; i++ {
day := getDay(time.Now().AddDate(0, 0, -numDays+i+1).Truncate(time.Hour * 24))
historyDaily[i] = historyDailyMap[day]
}
return &History{
History: historyDaily,
Uptime: 100 * numOfSuccess / numTotal,
}
}
func getHourlyHistory(history []models.HealthcheckHistory) *History {
numHours := 48
historyHourlyMap := map[string]string{}
numOfSuccess := 0
numTotal := 1
for i := 0; i < numHours; i++ {
hour := getHour(time.Now().Add(time.Hour * time.Duration(-i)).Truncate(time.Hour))
historyHourlyMap[hour] = models.HealthcheckUnknown
}
for _, _history := range history {
hour := getHour(_history.CreatedAt.Truncate(time.Hour))
// skip if day is not in the last 90 days
if _, ok := historyHourlyMap[hour]; !ok {
continue
}
numTotal++
if _history.Status == models.HealthcheckSuccess {
numOfSuccess++
}
// skip if day is already set to failure
if historyHourlyMap[hour] == models.HealthcheckFailure {
continue
}
historyHourlyMap[hour] = _history.Status
}
historyHourly := make([]string, numHours)
for i := 0; i < numHours; i++ {
hour := getHour(time.Now().Add(time.Hour * time.Duration(-numHours+i+1)).Truncate(time.Hour))
historyHourly[i] = historyHourlyMap[hour]
}
return &History{
History: historyHourly,
Uptime: 100 * numOfSuccess / numTotal,
}
}
func (h *BaseHandler) Index(c echo.Context) error {
ctx := context.Background()
healthchecks, err := services.GetHealthchecksWithHistory(ctx, h.query)
if err != nil {
return err
}
timeRange := c.QueryParam("time-range")
if timeRange != "48hours" && timeRange != "90days" {
timeRange = "90days"
}
overallStatus := "SUCCESS"
healthchecksWithHistory := make([]*HealthCheck, len(healthchecks))
for i, healthcheck := range healthchecks {
historyDaily := getDailyHistory(healthcheck.History)
historyHourly := getHourlyHistory(healthcheck.History)
status := historyDaily.History[89]
if status != models.HealthcheckSuccess {
overallStatus = status
}
healthchecksWithHistory[i] = &HealthCheck{
Name: healthcheck.Name,
Status: status,
HistoryDaily: historyDaily,
HistoryHourly: historyHourly,
}
}
return c.Render(http.StatusOK, "index.tmpl", &IndexData{
Base: &components.Base{
NavbarActive: GetPageByTitle(Pages, "Status"),
Navbar: Pages,
},
HealthChecks: healthchecksWithHistory,
HealthchecksLength: len(healthchecks),
TimeRange: timeRange,
Status: overallStatus,
})
}