Implement health check button in dashboard for displaying system health status

This commit is contained in:
Luis Eduardo Jeréz Girón
2024-09-06 22:30:13 -06:00
parent 73369fb160
commit 8544bf20f1
5 changed files with 172 additions and 50 deletions

View File

@@ -1,33 +0,0 @@
package component
import (
"github.com/maragudk/gomponents"
"github.com/maragudk/gomponents/components"
"github.com/maragudk/gomponents/html"
)
func IsActivePing(isActive bool) gomponents.Node {
return html.Div(
html.Class("tooltip tooltip-right"),
gomponents.If(isActive, html.Data("tip", "Active")),
gomponents.If(!isActive, html.Data("tip", "Inactive")),
html.Span(
html.Class("relative flex h-3 w-3"),
html.Span(
components.Classes{
"absolute inline-flex h-full w-full": true,
"animate-ping rounded-full opacity-75": true,
"bg-success": isActive,
"bg-error": !isActive,
},
),
html.Span(
components.Classes{
"relative inline-flex rounded-full h-3 w-3": true,
"bg-success": isActive,
"bg-error": !isActive,
},
),
),
)
}

View File

@@ -9,15 +9,72 @@ import (
"github.com/maragudk/gomponents/html"
)
func Ping(color color) gomponents.Node {
if color.Value == "" {
color = ColorNeutral
}
bgClass := ""
switch color {
case ColorPrimary:
bgClass = "bg-primary"
case ColorSecondary:
bgClass = "bg-secondary"
case ColorAccent:
bgClass = "bg-accent"
case ColorNeutral:
bgClass = "bg-neutral"
case ColorInfo:
bgClass = "bg-info"
case ColorSuccess:
bgClass = "bg-success"
case ColorWarning:
bgClass = "bg-warning"
case ColorError:
bgClass = "bg-error"
}
return html.Span(
html.Class("relative flex h-3 w-3"),
html.Span(
components.Classes{
"absolute inline-flex h-full w-full": true,
"animate-ping rounded-full opacity-75": true,
bgClass: true,
},
),
html.Span(
components.Classes{
"relative inline-flex rounded-full h-3 w-3": true,
bgClass: true,
},
),
)
}
func IsActivePing(isActive bool) gomponents.Node {
pingColor := ColorSuccess
if !isActive {
pingColor = ColorError
}
return html.Div(
html.Class("tooltip tooltip-right"),
gomponents.If(isActive, html.Data("tip", "Active")),
gomponents.If(!isActive, html.Data("tip", "Inactive")),
Ping(pingColor),
)
}
func HealthStatusPing(
testOk sql.NullBool, testError sql.NullString, lastTestAt sql.NullTime,
) gomponents.Node {
bgClass := "bg-warning"
pingColor := ColorWarning
if testOk.Valid {
if testOk.Bool {
bgClass = "bg-success"
pingColor = ColorSuccess
} else {
bgClass = "bg-error"
pingColor = ColorError
}
}
@@ -95,20 +152,8 @@ func HealthStatusPing(
moHTML,
html.Span(
moOpenerAttr,
html.Class("relative flex h-3 w-3 cursor-pointer"),
html.Span(
components.Classes{
"absolute inline-flex h-full w-full": true,
"animate-ping rounded-full opacity-75": true,
bgClass: true,
},
),
html.Span(
components.Classes{
"relative inline-flex rounded-full h-3 w-3": true,
bgClass: true,
},
),
html.Class("cursor-pointer"),
Ping(pingColor),
),
)
}

View File

@@ -0,0 +1,103 @@
package dashboard
import (
"fmt"
"net/http"
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
"github.com/eduardolat/pgbackweb/internal/service"
"github.com/eduardolat/pgbackweb/internal/util/echoutil"
"github.com/eduardolat/pgbackweb/internal/view/web/component"
"github.com/eduardolat/pgbackweb/internal/view/web/htmx"
"github.com/labstack/echo/v4"
"github.com/maragudk/gomponents"
"github.com/maragudk/gomponents/html"
)
func healthButtonHandler(servs *service.Service) echo.HandlerFunc {
return func(c echo.Context) error {
ctx := c.Request().Context()
databasesQty, err := servs.DatabasesService.GetDatabasesQty(ctx)
if err != nil {
return htmx.RespondToastError(c, err.Error())
}
destinationsQty, err := servs.DestinationsService.GetDestinationsQty(ctx)
if err != nil {
return htmx.RespondToastError(c, err.Error())
}
return echoutil.RenderGomponent(c, http.StatusOK, healthButton(
databasesQty, destinationsQty,
))
}
}
func healthButton(
databasesQty dbgen.DatabasesServiceGetDatabasesQtyRow,
destinationsQty dbgen.DestinationsServiceGetDestinationsQtyRow,
) gomponents.Node {
isHealthy := true
if databasesQty.Unhealthy > 0 {
isHealthy = false
}
if destinationsQty.Unhealthy > 0 {
isHealthy = false
}
pingColor := component.ColorSuccess
if !isHealthy {
pingColor = component.ColorError
}
mo := component.Modal(component.ModalParams{
Size: component.SizeMd,
Title: "Health status",
Content: []gomponents.Node{
component.PText(`
The health check for both databases and destinations runs automatically
every 10 minutes, when PG Back Web starts, and when you click the
"Test connection" button on each resource. You can see additional
information and error messages by clicking the health check button
for each resource.
`),
html.Table(
html.Class("table mt-2"),
html.THead(
html.Tr(
html.Th(component.SpanText("Resource")),
html.Th(component.SpanText("Total")),
html.Th(component.SpanText("Healthy")),
html.Th(component.SpanText("Unhealthy")),
),
),
html.TBody(
html.Tr(
html.Td(component.SpanText("Databases")),
html.Td(component.SpanText(fmt.Sprintf("%d", databasesQty.All))),
html.Td(component.SpanText(fmt.Sprintf("%d", databasesQty.Healthy))),
html.Td(component.SpanText(fmt.Sprintf("%d", databasesQty.Unhealthy))),
),
html.Tr(
html.Td(component.SpanText("Destinations")),
html.Td(component.SpanText(fmt.Sprintf("%d", destinationsQty.All))),
html.Td(component.SpanText(fmt.Sprintf("%d", destinationsQty.Healthy))),
html.Td(component.SpanText(fmt.Sprintf("%d", destinationsQty.Unhealthy))),
),
),
),
},
})
return html.Div(
html.Class("inline-block"),
mo.HTML,
html.Button(
mo.OpenerAttr,
html.Class("btn btn-ghost btn-neutral"),
component.SpanText("Health status"),
component.Ping(pingColor),
),
)
}

View File

@@ -18,6 +18,8 @@ import (
func MountRouter(
parent *echo.Group, mids *middleware.Middleware, servs *service.Service,
) {
parent.GET("/health-button", healthButtonHandler(servs))
summary.MountRouter(parent.Group(""), mids, servs)
databases.MountRouter(parent.Group("/databases"), mids, servs)
destinations.MountRouter(parent.Group("/destinations"), mids, servs)

View File

@@ -33,6 +33,11 @@ func dashboardHeader() gomponents.Node {
),
html.Div(
html.Class("flex justify-end items-center space-x-2"),
html.Div(
htmx.HxGet("/dashboard/health-button"),
htmx.HxSwap("outerHTML"),
htmx.HxTrigger("load once"),
),
html.A(
html.Href("https://discord.gg/BmAwq29UZ8"),
html.Target("_blank"),