diff --git a/internal/view/middleware/inject_reqctx.go b/internal/view/middleware/inject_reqctx.go index 37ffab7..d4a8f7e 100644 --- a/internal/view/middleware/inject_reqctx.go +++ b/internal/view/middleware/inject_reqctx.go @@ -6,11 +6,16 @@ import ( "github.com/eduardolat/pgbackweb/internal/database/dbgen" "github.com/eduardolat/pgbackweb/internal/logger" "github.com/eduardolat/pgbackweb/internal/view/reqctx" + "github.com/eduardolat/pgbackweb/internal/view/web/htmx" "github.com/labstack/echo/v4" ) func (m *Middleware) InjectReqctx(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { + reqCtx := reqctx.Ctx{ + IsHTMXBoosted: htmx.ServerIsBoosted(c), + } + found, user, err := m.servs.AuthService.GetUserFromSessionCookie(c) if err != nil { logger.Error("failed to get user from session cookie", logger.KV{ @@ -21,21 +26,19 @@ func (m *Middleware) InjectReqctx(next echo.HandlerFunc) echo.HandlerFunc { return c.String(http.StatusInternalServerError, "Internal server error") } - if !found { - return next(c) - } - - reqctx.SetCtx(c, reqctx.Ctx{ - IsAuthed: true, - SessionID: user.SessionID, - User: dbgen.User{ + if found { + reqCtx.IsAuthed = true + reqCtx.SessionID = user.SessionID + reqCtx.User = dbgen.User{ ID: user.ID, Name: user.Name, Email: user.Email, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, - }, - }) + } + } + + reqctx.SetCtx(c, reqCtx) return next(c) } } diff --git a/internal/view/reqctx/ctx.go b/internal/view/reqctx/ctx.go index ec50275..ba76367 100644 --- a/internal/view/reqctx/ctx.go +++ b/internal/view/reqctx/ctx.go @@ -12,9 +12,10 @@ const ( // Ctx represents the values passed through a single request context. type Ctx struct { - IsAuthed bool - SessionID uuid.UUID - User dbgen.User + IsHTMXBoosted bool + IsAuthed bool + SessionID uuid.UUID + User dbgen.User } // SetCtx inserts values into the Echo request context. diff --git a/internal/view/web/dashboard/about/index.go b/internal/view/web/dashboard/about/index.go index bf162f2..db8f769 100644 --- a/internal/view/web/dashboard/about/index.go +++ b/internal/view/web/dashboard/about/index.go @@ -5,6 +5,7 @@ import ( "github.com/eduardolat/pgbackweb/internal/config" "github.com/eduardolat/pgbackweb/internal/util/echoutil" + "github.com/eduardolat/pgbackweb/internal/view/reqctx" "github.com/eduardolat/pgbackweb/internal/view/web/component" "github.com/eduardolat/pgbackweb/internal/view/web/layout" "github.com/labstack/echo/v4" @@ -13,10 +14,11 @@ import ( ) func (h *handlers) indexPageHandler(c echo.Context) error { - return echoutil.RenderGomponent(c, http.StatusOK, indexPage()) + reqCtx := reqctx.GetCtx(c) + return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx)) } -func indexPage() gomponents.Node { +func indexPage(reqCtx reqctx.Ctx) gomponents.Node { content := []gomponents.Node{ component.H1Text("About PG Back Web"), component.H2Text(config.Version), @@ -80,7 +82,7 @@ func indexPage() gomponents.Node { ), } - return layout.Dashboard(layout.DashboardParams{ + return layout.Dashboard(reqCtx, layout.DashboardParams{ Title: "About", Body: content, }) diff --git a/internal/view/web/dashboard/backups/index.go b/internal/view/web/dashboard/backups/index.go index 01c88e2..f2914ef 100644 --- a/internal/view/web/dashboard/backups/index.go +++ b/internal/view/web/dashboard/backups/index.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/eduardolat/pgbackweb/internal/util/echoutil" + "github.com/eduardolat/pgbackweb/internal/view/reqctx" "github.com/eduardolat/pgbackweb/internal/view/web/component" "github.com/eduardolat/pgbackweb/internal/view/web/htmx" "github.com/eduardolat/pgbackweb/internal/view/web/layout" @@ -13,10 +14,11 @@ import ( ) func (h *handlers) indexPageHandler(c echo.Context) error { - return echoutil.RenderGomponent(c, http.StatusOK, indexPage()) + reqCtx := reqctx.GetCtx(c) + return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx)) } -func indexPage() gomponents.Node { +func indexPage(reqCtx reqctx.Ctx) gomponents.Node { content := []gomponents.Node{ html.Div( html.Class("flex justify-between items-start"), @@ -63,7 +65,7 @@ func indexPage() gomponents.Node { }), } - return layout.Dashboard(layout.DashboardParams{ + return layout.Dashboard(reqCtx, layout.DashboardParams{ Title: "Backups", Body: content, }) diff --git a/internal/view/web/dashboard/databases/index.go b/internal/view/web/dashboard/databases/index.go index 0d33413..8dfa80a 100644 --- a/internal/view/web/dashboard/databases/index.go +++ b/internal/view/web/dashboard/databases/index.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/eduardolat/pgbackweb/internal/util/echoutil" + "github.com/eduardolat/pgbackweb/internal/view/reqctx" "github.com/eduardolat/pgbackweb/internal/view/web/component" "github.com/eduardolat/pgbackweb/internal/view/web/htmx" "github.com/eduardolat/pgbackweb/internal/view/web/layout" @@ -13,10 +14,11 @@ import ( ) func (h *handlers) indexPageHandler(c echo.Context) error { - return echoutil.RenderGomponent(c, http.StatusOK, indexPage()) + reqCtx := reqctx.GetCtx(c) + return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx)) } -func indexPage() gomponents.Node { +func indexPage(reqCtx reqctx.Ctx) gomponents.Node { content := []gomponents.Node{ html.Div( html.Class("flex justify-between items-start"), @@ -55,7 +57,7 @@ func indexPage() gomponents.Node { }), } - return layout.Dashboard(layout.DashboardParams{ + return layout.Dashboard(reqCtx, layout.DashboardParams{ Title: "Databases", Body: content, }) diff --git a/internal/view/web/dashboard/destinations/index.go b/internal/view/web/dashboard/destinations/index.go index c6ca68d..dbe062c 100644 --- a/internal/view/web/dashboard/destinations/index.go +++ b/internal/view/web/dashboard/destinations/index.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/eduardolat/pgbackweb/internal/util/echoutil" + "github.com/eduardolat/pgbackweb/internal/view/reqctx" "github.com/eduardolat/pgbackweb/internal/view/web/component" "github.com/eduardolat/pgbackweb/internal/view/web/htmx" "github.com/eduardolat/pgbackweb/internal/view/web/layout" @@ -13,10 +14,11 @@ import ( ) func (h *handlers) indexPageHandler(c echo.Context) error { - return echoutil.RenderGomponent(c, http.StatusOK, indexPage()) + reqCtx := reqctx.GetCtx(c) + return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx)) } -func indexPage() gomponents.Node { +func indexPage(reqCtx reqctx.Ctx) gomponents.Node { content := []gomponents.Node{ html.Div( html.Class("flex justify-between items-start"), @@ -64,7 +66,7 @@ func indexPage() gomponents.Node { }), } - return layout.Dashboard(layout.DashboardParams{ + return layout.Dashboard(reqCtx, layout.DashboardParams{ Title: "S3 Destinations", Body: content, }) diff --git a/internal/view/web/dashboard/executions/index.go b/internal/view/web/dashboard/executions/index.go index 011ca3b..3c9dd3b 100644 --- a/internal/view/web/dashboard/executions/index.go +++ b/internal/view/web/dashboard/executions/index.go @@ -6,6 +6,7 @@ import ( "github.com/eduardolat/pgbackweb/internal/util/echoutil" "github.com/eduardolat/pgbackweb/internal/util/strutil" "github.com/eduardolat/pgbackweb/internal/validate" + "github.com/eduardolat/pgbackweb/internal/view/reqctx" "github.com/eduardolat/pgbackweb/internal/view/web/component" "github.com/eduardolat/pgbackweb/internal/view/web/htmx" "github.com/eduardolat/pgbackweb/internal/view/web/layout" @@ -22,6 +23,8 @@ type execsQueryData struct { } func (h *handlers) indexPageHandler(c echo.Context) error { + reqCtx := reqctx.GetCtx(c) + var queryData execsQueryData if err := c.Bind(&queryData); err != nil { return c.String(http.StatusBadRequest, err.Error()) @@ -30,10 +33,10 @@ func (h *handlers) indexPageHandler(c echo.Context) error { return c.String(http.StatusBadRequest, err.Error()) } - return echoutil.RenderGomponent(c, http.StatusOK, indexPage(queryData)) + return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx, queryData)) } -func indexPage(queryData execsQueryData) gomponents.Node { +func indexPage(reqCtx reqctx.Ctx, queryData execsQueryData) gomponents.Node { content := []gomponents.Node{ component.H1Text("Executions"), component.CardBox(component.CardBoxParams{ @@ -84,7 +87,7 @@ func indexPage(queryData execsQueryData) gomponents.Node { }), } - return layout.Dashboard(layout.DashboardParams{ + return layout.Dashboard(reqCtx, layout.DashboardParams{ Title: "Executions", Body: content, }) diff --git a/internal/view/web/dashboard/profile/index.go b/internal/view/web/dashboard/profile/index.go index 7939d9b..6b92fa5 100644 --- a/internal/view/web/dashboard/profile/index.go +++ b/internal/view/web/dashboard/profile/index.go @@ -25,22 +25,22 @@ func (h *handlers) indexPageHandler(c echo.Context) error { } return echoutil.RenderGomponent( - c, http.StatusOK, indexPage(reqCtx.User, sessions), + c, http.StatusOK, indexPage(reqCtx, sessions), ) } -func indexPage(user dbgen.User, sessions []dbgen.Session) gomponents.Node { +func indexPage(reqCtx reqctx.Ctx, sessions []dbgen.Session) gomponents.Node { content := []gomponents.Node{ component.H1Text("Profile"), html.Div( html.Class("mt-4 grid grid-cols-2 gap-4"), - html.Div(updateUserForm(user)), + html.Div(updateUserForm(reqCtx.User)), html.Div(closeAllSessionsForm(sessions)), ), } - return layout.Dashboard(layout.DashboardParams{ + return layout.Dashboard(reqCtx, layout.DashboardParams{ Title: "Profile", Body: content, }) diff --git a/internal/view/web/dashboard/restorations/index.go b/internal/view/web/dashboard/restorations/index.go index 4738c78..c8df159 100644 --- a/internal/view/web/dashboard/restorations/index.go +++ b/internal/view/web/dashboard/restorations/index.go @@ -6,6 +6,7 @@ import ( "github.com/eduardolat/pgbackweb/internal/util/echoutil" "github.com/eduardolat/pgbackweb/internal/util/strutil" "github.com/eduardolat/pgbackweb/internal/validate" + "github.com/eduardolat/pgbackweb/internal/view/reqctx" "github.com/eduardolat/pgbackweb/internal/view/web/component" "github.com/eduardolat/pgbackweb/internal/view/web/htmx" "github.com/eduardolat/pgbackweb/internal/view/web/layout" @@ -21,6 +22,8 @@ type resQueryData struct { } func (h *handlers) indexPageHandler(c echo.Context) error { + reqCtx := reqctx.GetCtx(c) + var queryData resQueryData if err := c.Bind(&queryData); err != nil { return c.String(http.StatusBadRequest, err.Error()) @@ -29,10 +32,10 @@ func (h *handlers) indexPageHandler(c echo.Context) error { return c.String(http.StatusBadRequest, err.Error()) } - return echoutil.RenderGomponent(c, http.StatusOK, indexPage(queryData)) + return echoutil.RenderGomponent(c, http.StatusOK, indexPage(reqCtx, queryData)) } -func indexPage(queryData resQueryData) gomponents.Node { +func indexPage(reqCtx reqctx.Ctx, queryData resQueryData) gomponents.Node { content := []gomponents.Node{ component.H1Text("Restorations"), component.CardBox(component.CardBoxParams{ @@ -79,7 +82,7 @@ func indexPage(queryData resQueryData) gomponents.Node { }), } - return layout.Dashboard(layout.DashboardParams{ + return layout.Dashboard(reqCtx, layout.DashboardParams{ Title: "Restorations", Body: content, }) diff --git a/internal/view/web/dashboard/summary/index.go b/internal/view/web/dashboard/summary/index.go index 7e2e491..28f86f0 100644 --- a/internal/view/web/dashboard/summary/index.go +++ b/internal/view/web/dashboard/summary/index.go @@ -7,6 +7,7 @@ import ( lucide "github.com/eduardolat/gomponents-lucide" "github.com/eduardolat/pgbackweb/internal/database/dbgen" "github.com/eduardolat/pgbackweb/internal/util/echoutil" + "github.com/eduardolat/pgbackweb/internal/view/reqctx" "github.com/eduardolat/pgbackweb/internal/view/web/alpine" "github.com/eduardolat/pgbackweb/internal/view/web/component" "github.com/eduardolat/pgbackweb/internal/view/web/layout" @@ -18,6 +19,7 @@ import ( func (h *handlers) indexPageHandler(c echo.Context) error { ctx := c.Request().Context() + reqCtx := reqctx.GetCtx(c) databasesQty, err := h.servs.DatabasesService.GetDatabasesQty(ctx) if err != nil { @@ -43,12 +45,14 @@ func (h *handlers) indexPageHandler(c echo.Context) error { return echoutil.RenderGomponent( c, http.StatusOK, indexPage( - databasesQty, destinationsQty, backupsQty, executionsQty, restorationsQty, + reqCtx, databasesQty, destinationsQty, backupsQty, executionsQty, + restorationsQty, ), ) } func indexPage( + reqCtx reqctx.Ctx, databasesQty dbgen.DatabasesServiceGetDatabasesQtyRow, destinationsQty dbgen.DestinationsServiceGetDestinationsQtyRow, backupsQty dbgen.BackupsServiceGetBackupsQtyRow, @@ -300,9 +304,8 @@ func indexPage( ), } - return layout.Dashboard(layout.DashboardParams{ - Title: "Summary", - Body: content, - LoadChartJS: true, + return layout.Dashboard(reqCtx, layout.DashboardParams{ + Title: "Summary", + Body: content, }) } diff --git a/internal/view/web/dashboard/webhooks/index.go b/internal/view/web/dashboard/webhooks/index.go index a6fb72d..402958a 100644 --- a/internal/view/web/dashboard/webhooks/index.go +++ b/internal/view/web/dashboard/webhooks/index.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/eduardolat/pgbackweb/internal/util/echoutil" + "github.com/eduardolat/pgbackweb/internal/view/reqctx" "github.com/eduardolat/pgbackweb/internal/view/web/component" "github.com/eduardolat/pgbackweb/internal/view/web/htmx" "github.com/eduardolat/pgbackweb/internal/view/web/layout" @@ -13,12 +14,13 @@ import ( ) func (h *handlers) indexPageHandler(c echo.Context) error { + reqCtx := reqctx.GetCtx(c) return echoutil.RenderGomponent( - c, http.StatusOK, indexPage(), + c, http.StatusOK, indexPage(reqCtx), ) } -func indexPage() gomponents.Node { +func indexPage(reqCtx reqctx.Ctx) gomponents.Node { content := []gomponents.Node{ html.Div( html.Class("flex justify-between items-start"), @@ -58,7 +60,7 @@ func indexPage() gomponents.Node { }), } - return layout.Dashboard(layout.DashboardParams{ + return layout.Dashboard(reqCtx, layout.DashboardParams{ Title: "Webhooks", Body: content, }) diff --git a/internal/view/web/layout/auth.go b/internal/view/web/layout/auth.go index ff55fd1..4c36936 100644 --- a/internal/view/web/layout/auth.go +++ b/internal/view/web/layout/auth.go @@ -22,7 +22,7 @@ func Auth(params AuthParams) gomponents.Node { Language: "en", Title: title, Head: []gomponents.Node{ - head(headParams{}), + head(), }, Body: []gomponents.Node{ components.Classes{ diff --git a/internal/view/web/layout/common.go b/internal/view/web/layout/common.go index 98d4c3b..e6ab6c7 100644 --- a/internal/view/web/layout/common.go +++ b/internal/view/web/layout/common.go @@ -6,11 +6,7 @@ import ( "github.com/maragudk/gomponents/html" ) -type headParams struct { - LoadChartJS bool -} - -func head(params headParams) gomponents.Node { +func head() gomponents.Node { href := func(path string) gomponents.Node { return html.Href(static.GetVersionedFilePath(path)) } @@ -28,16 +24,12 @@ func head(params headParams) gomponents.Node { html.Script(src("/libs/alpinejs/alpinejs-3.14.1.min.js"), html.Defer()), html.Script(src("/libs/theme-change/theme-change-2.0.2.min.js")), html.Script(src("/libs/sweetalert2/sweetalert2-11.13.1.min.js")), + html.Script(src("/libs/chartjs/chartjs-4.4.3.umd.min.js")), html.Link(html.Rel("stylesheet"), href("/libs/notyf/notyf-3.10.0.min.css")), html.Script(src("/libs/notyf/notyf-3.10.0.min.js")), html.Link(html.Rel("stylesheet"), href("/libs/slim-select/slimselect-2.8.2.css")), html.Script(src("/libs/slim-select/slimselect-2.8.2.min.js")), - - gomponents.If( - params.LoadChartJS, - html.Script(src("/libs/chartjs/chartjs-4.4.3.umd.min.js")), - ), }) } diff --git a/internal/view/web/layout/dashboard.go b/internal/view/web/layout/dashboard.go index aabf35b..84ecf47 100644 --- a/internal/view/web/layout/dashboard.go +++ b/internal/view/web/layout/dashboard.go @@ -1,18 +1,23 @@ package layout import ( + "github.com/eduardolat/pgbackweb/internal/view/reqctx" + "github.com/eduardolat/pgbackweb/internal/view/web/component" "github.com/maragudk/gomponents" "github.com/maragudk/gomponents/components" "github.com/maragudk/gomponents/html" ) type DashboardParams struct { - Title string - Body []gomponents.Node - LoadChartJS bool + Title string + Body []gomponents.Node } -func Dashboard(params DashboardParams) gomponents.Node { +func Dashboard(reqCtx reqctx.Ctx, params DashboardParams) gomponents.Node { + if reqCtx.IsHTMXBoosted { + return component.RenderableGroup(params.Body) + } + title := "PG Back Web" if params.Title != "" { title = params.Title + " - " + title @@ -22,9 +27,7 @@ func Dashboard(params DashboardParams) gomponents.Node { Language: "en", Title: title, Head: []gomponents.Node{ - head(headParams{ - LoadChartJS: params.LoadChartJS, - }), + head(), }, Body: []gomponents.Node{ components.Classes{ @@ -36,6 +39,7 @@ func Dashboard(params DashboardParams) gomponents.Node { html.Class("flex-grow overflow-y-auto"), dashboardHeader(), html.Main( + html.ID("dashboard-main"), html.Class("p-4"), gomponents.Group(params.Body), ), diff --git a/internal/view/web/layout/dashboard_aside.go b/internal/view/web/layout/dashboard_aside.go index 46b8282..9c91fdb 100644 --- a/internal/view/web/layout/dashboard_aside.go +++ b/internal/view/web/layout/dashboard_aside.go @@ -5,6 +5,7 @@ import ( lucide "github.com/eduardolat/gomponents-lucide" "github.com/eduardolat/pgbackweb/internal/view/web/alpine" + "github.com/eduardolat/pgbackweb/internal/view/web/htmx" "github.com/maragudk/gomponents" "github.com/maragudk/gomponents/components" "github.com/maragudk/gomponents/html" @@ -41,6 +42,8 @@ func dashboardAside() gomponents.Node { ), html.Div( + htmx.HxBoost("true"), + htmx.HxTarget("#dashboard-main"), html.Class("mt-6 space-y-4"), dashboardAsideItem(