mirror of
https://github.com/eduardolat/pgbackweb.git
synced 2026-01-26 06:29:03 -06:00
Implement health check button in dashboard for displaying system health status
This commit is contained in:
@@ -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,
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
)
|
||||
}
|
||||
103
internal/view/web/dashboard/health_button.go
Normal file
103
internal/view/web/dashboard/health_button.go
Normal 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),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user