diff --git a/bun.lockb b/bun.lockb index 997b758..cbfeff8 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/internal/handler/response.go b/internal/handler/response.go index 4a2ae4c..86a2754 100644 --- a/internal/handler/response.go +++ b/internal/handler/response.go @@ -147,6 +147,48 @@ func (s *fiberServer) remoteNodesHandler(c *fiber.Ctx) error { return handler(c) } +// Returns a single node information based on `id` query param. +// For now, only process from HTMX request. +func (s *fiberServer) nodeHandler(c *fiber.Ctx) error { + switch c.Get("HX-Target") { + case "modal-section": + nodeID, err := c.ParamsInt("id", 0) + if err != nil { + return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{ + "status": "error", + "message": err.Error(), + "data": nil, + }) + } + if nodeID == 0 { + return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{ + "status": "error", + "message": "Invalid node id", + "data": nil, + }) + } + moneroRepo := monero.New() + node, err := moneroRepo.Node(nodeID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "status": "error", + "message": err.Error(), + "data": nil, + }) + } + cmp := views.ModalLayout(fmt.Sprintf("Node #%d", nodeID), views.Node(node)) + handler := adaptor.HTTPHandler(templ.Handler(cmp)) + return handler(c) + } + + // for now, just return 400 + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "status": "error", + "message": "Bad Request, invalid HTMX request", + "data": nil, + }) +} + // Returns a list of nodes (API) func Nodes(c *fiber.Ctx) error { moneroRepo := monero.New() diff --git a/internal/handler/routes.go b/internal/handler/routes.go index fc66792..9f8f7c8 100644 --- a/internal/handler/routes.go +++ b/internal/handler/routes.go @@ -3,6 +3,7 @@ package handler func (s *fiberServer) Routes() { s.App.Get("/", s.homeHandler) s.App.Get("/remote-nodes", s.remoteNodesHandler) + s.App.Get("/remote-nodes/id/:id", s.nodeHandler) s.App.Get("/add-node", s.addNodeHandler) // V1 API routes diff --git a/internal/handler/views/layout.templ b/internal/handler/views/layout.templ index 74f80b7..5feb898 100644 --- a/internal/handler/views/layout.templ +++ b/internal/handler/views/layout.templ @@ -53,6 +53,7 @@ templ base(m Meta) { + } @@ -65,3 +66,26 @@ templ BaseLayout(m Meta, cmp templ.Component) { templ BlankLayout(cmp templ.Component) { @cmp } + +templ ModalLayout(title string, cmp templ.Component) { +
+ +
+} diff --git a/internal/handler/views/layout_templ.go b/internal/handler/views/layout_templ.go index 09acf1e..bd2f70c 100644 --- a/internal/handler/views/layout_templ.go +++ b/internal/handler/views/layout_templ.go @@ -231,7 +231,7 @@ func base(m Meta) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(", source code licensed under BSD-3-Clause license.

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(", source code licensed under BSD-3-Clause license.

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -315,4 +315,54 @@ func BlankLayout(cmp templ.Component) templ.Component { }) } +func ModalLayout(title string, cmp templ.Component) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var18 := templ.GetChildren(ctx) + if templ_7745c5c3_Var18 == nil { + templ_7745c5c3_Var18 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var19 string + templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(title) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 74, Col: 33} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = cmp.Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + var _ = templruntime.GeneratedTemplate diff --git a/internal/handler/views/remote_nodes.templ b/internal/handler/views/remote_nodes.templ index 58bf516..4d52f67 100644 --- a/internal/handler/views/remote_nodes.templ +++ b/internal/handler/views/remote_nodes.templ @@ -194,7 +194,7 @@ templ TableNodes(data monero.Nodes, countries []monero.Countries, q monero.Query for _, row := range data.Items { - @cellHostPort(row.IPAddresses, row.Hostname, row.Port, row.IsTor, row.IPv6Only) + @cellHostPort(row.ID, row.Port, row.Hostname, row.IPAddresses, row.IsTor, row.IPv6Only) @cellNettype(row.Nettype, row.Height) @@ -225,10 +225,22 @@ templ TableNodes(data monero.Nodes, countries []monero.Countries, q monero.Query } -templ cellHostPort(ips, hostname string, port uint, isTor, ipv6Only bool) { +templ Node(data monero.Node) { +

{ fmt.Sprintf("%s:%d", data.Hostname, data.Port) }

+} + +templ cellHostPort(id, port uint, hostname, ips string, isTor, ipv6Only bool) { if isTor { - -
diff --git a/internal/handler/views/remote_nodes_templ.go b/internal/handler/views/remote_nodes_templ.go index 3f1bf5d..bed4525 100644 --- a/internal/handler/views/remote_nodes_templ.go +++ b/internal/handler/views/remote_nodes_templ.go @@ -433,7 +433,7 @@ func TableNodes(data monero.Nodes, countries []monero.Countries, q monero.QueryN if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = cellHostPort(row.IPAddresses, row.Hostname, row.Port, row.IsTor, row.IPv6Only).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = cellHostPort(row.ID, row.Port, row.Hostname, row.IPAddresses, row.IsTor, row.IPv6Only).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -541,7 +541,7 @@ func TableNodes(data monero.Nodes, countries []monero.Countries, q monero.QueryN }) } -func cellHostPort(ips, hostname string, port uint, isTor, ipv6Only bool) templ.Component { +func Node(data monero.Node) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -562,17 +562,72 @@ func cellHostPort(ips, hostname string, port uint, isTor, ipv6Only bool) templ.C templ_7745c5c3_Var22 = templ.NopComponent } ctx = templ.ClearChildren(ctx) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var23 string + templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s:%d", data.Hostname, data.Port)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 229, Col: 52} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return templ_7745c5c3_Err + }) +} + +func cellHostPort(id, port uint, hostname, ips string, isTor, ipv6Only bool) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var24 := templ.GetChildren(ctx) + if templ_7745c5c3_Var24 == nil { + templ_7745c5c3_Var24 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) if isTor { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("