diff --git a/internal/handlers/index.go b/internal/handlers/index.go index 5a97d56..e739406 100644 --- a/internal/handlers/index.go +++ b/internal/handlers/index.go @@ -43,7 +43,7 @@ func getDailyHistory(history []models.HealthcheckHistory) *History { numDays := 90 historyDailyMap := map[string]string{} numOfSuccess := 0 - numTotal := 1 + numTotal := 0 for i := 0; i < numDays; i++ { day := getDay(time.Now().AddDate(0, 0, -i).Truncate(time.Hour * 24)) @@ -77,9 +77,14 @@ func getDailyHistory(history []models.HealthcheckHistory) *History { historyDaily[i] = historyDailyMap[day] } + uptime := 0 + if numTotal > 0 { + uptime = 100 * numOfSuccess / numTotal + } + return &History{ History: historyDaily, - Uptime: 100 * numOfSuccess / numTotal, + Uptime: uptime, } } @@ -87,7 +92,7 @@ func getHourlyHistory(history []models.HealthcheckHistory) *History { numHours := 48 historyHourlyMap := map[string]string{} numOfSuccess := 0 - numTotal := 1 + numTotal := 0 for i := 0; i < numHours; i++ { hour := getHour(time.Now().Add(time.Hour * time.Duration(-i)).Truncate(time.Hour)) @@ -121,15 +126,20 @@ func getHourlyHistory(history []models.HealthcheckHistory) *History { historyHourly[i] = historyHourlyMap[hour] } + uptime := 0 + if numTotal > 0 { + uptime = 100 * numOfSuccess / numTotal + } + return &History{ History: historyHourly, - Uptime: 100 * numOfSuccess / numTotal, + Uptime: uptime, } } func (h *BaseHandler) Index(c echo.Context) error { ctx := context.Background() - healthchecks, err := services.GetHealthchecksWithHistory(ctx, h.query) + healthchecks, err := services.GetHealthchecks(ctx, h.query) if err != nil { return err } diff --git a/internal/services/healthcheck.go b/internal/services/healthcheck.go index 30d8ef2..1eb2f51 100644 --- a/internal/services/healthcheck.go +++ b/internal/services/healthcheck.go @@ -21,10 +21,12 @@ func GetHealthcheck(ctx context.Context, q *query.Query, slug string) (*models.H return q.Healthcheck.WithContext(ctx).Where( q.Healthcheck.Slug.Eq(slug), q.Healthcheck.DeletedAt.IsNull(), + ).Preload( + q.Healthcheck.History, ).First() } -func GetHealthchecksWithHistory(ctx context.Context, q *query.Query) ([]*models.Healthcheck, error) { +func GetHealthchecks(ctx context.Context, q *query.Query) ([]*models.Healthcheck, error) { return q.Healthcheck.WithContext(ctx).Preload( q.Healthcheck.History, ).Where( diff --git a/web/static/css/tailwind.css b/web/static/css/tailwind.css index b9ae389..ed621e6 100644 --- a/web/static/css/tailwind.css +++ b/web/static/css/tailwind.css @@ -716,6 +716,10 @@ video { width: 100%; } +.min-w-full { + min-width: 100%; +} + .max-w-screen-md { max-width: 768px; } @@ -826,6 +830,10 @@ video { border-width: 1px; } +.border-b-2 { + border-bottom-width: 2px; +} + .border-gray-300 { --tw-border-opacity: 1; border-color: rgb(209 213 219 / var(--tw-border-opacity)); @@ -851,6 +859,11 @@ video { background-color: rgb(249 250 251 / var(--tw-bg-opacity)); } +.bg-green-100 { + --tw-bg-opacity: 1; + background-color: rgb(220 252 231 / var(--tw-bg-opacity)); +} + .bg-green-300 { --tw-bg-opacity: 1; background-color: rgb(134 239 172 / var(--tw-bg-opacity)); @@ -861,6 +874,11 @@ video { background-color: rgb(74 222 128 / var(--tw-bg-opacity)); } +.bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); +} + .bg-red-300 { --tw-bg-opacity: 1; background-color: rgb(252 165 165 / var(--tw-bg-opacity)); @@ -1004,6 +1022,10 @@ video { text-transform: uppercase; } +.leading-5 { + line-height: 1.25rem; +} + .leading-none { line-height: 1; } @@ -1012,6 +1034,10 @@ video { letter-spacing: -0.025em; } +.tracking-wider { + letter-spacing: 0.05em; +} + .text-blue-600 { --tw-text-opacity: 1; color: rgb(37 99 235 / var(--tw-text-opacity)); @@ -1027,6 +1053,11 @@ video { color: rgb(107 114 128 / var(--tw-text-opacity)); } +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + .text-gray-700 { --tw-text-opacity: 1; color: rgb(55 65 81 / var(--tw-text-opacity)); @@ -1037,11 +1068,21 @@ video { color: rgb(17 24 39 / var(--tw-text-opacity)); } +.text-green-800 { + --tw-text-opacity: 1; + color: rgb(22 101 52 / var(--tw-text-opacity)); +} + .text-red-600 { --tw-text-opacity: 1; color: rgb(220 38 38 / var(--tw-text-opacity)); } +.text-red-800 { + --tw-text-opacity: 1; + color: rgb(153 27 27 / var(--tw-text-opacity)); +} + .text-slate-500 { --tw-text-opacity: 1; color: rgb(100 116 139 / var(--tw-text-opacity)); @@ -1358,6 +1399,10 @@ video { } @media (min-width: 768px) { + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .md\:text-3xl { font-size: 1.875rem; line-height: 2.25rem; diff --git a/web/templates/pages/settings_healthchecks_create.tmpl b/web/templates/pages/settings_healthchecks_create.tmpl index 7fab5da..5c710f3 100644 --- a/web/templates/pages/settings_healthchecks_create.tmpl +++ b/web/templates/pages/settings_healthchecks_create.tmpl @@ -39,7 +39,7 @@ export const options = { thresholds: { // http errors should be less than 1% http_req_failed: ['rate<0.01'], - }, + }, vus: 10, duration: '5s', }; diff --git a/web/templates/pages/settings_healthchecks_describe.tmpl b/web/templates/pages/settings_healthchecks_describe.tmpl index 62cbc67..bd8bd0b 100644 --- a/web/templates/pages/settings_healthchecks_describe.tmpl +++ b/web/templates/pages/settings_healthchecks_describe.tmpl @@ -1,14 +1,85 @@ {{define "settings"}}
-

- {{ .Healthcheck.Name }} -

- {{ .Healthcheck.ID }} - {{ .Healthcheck.Slug }} - {{ .Healthcheck.Schedule }} - {{ .Healthcheck.WorkerGroups }} -
-  {{ .Healthcheck.Script }}
-  
+
+
+

+ {{ .Healthcheck.Name }} +

+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+

History

+ + + + + + + + + + + {{range .Healthcheck.History}} + + + + + + + {{end}} + +
StatusCreated AtDurationNote
+ + {{ .Status }} + + + {{ .CreatedAt.Format "2006-01-02 15:04:05" }} + + { .Duration } + + {{ .Note }} +
+
+ + + {{end}} diff --git a/web/templates/tempaltes.go b/web/templates/tempaltes.go index c80ec0d..c4854a3 100644 --- a/web/templates/tempaltes.go +++ b/web/templates/tempaltes.go @@ -4,6 +4,7 @@ import ( "embed" "io" "log" + "strings" "text/template" "github.com/labstack/echo/v4" @@ -20,7 +21,13 @@ type Templates struct { func load(files ...string) *template.Template { files = append(files, base) - return template.Must(template.ParseFS(templates, files...)) + + t := template.New("default").Funcs( + template.FuncMap{ + "StringsJoin": strings.Join, + }) + + return template.Must(t.ParseFS(templates, files...)) } func loadSettings(files ...string) *template.Template {