mirror of
https://github.com/ditatompel/xmr-remote-nodes.git
synced 2025-01-08 05:52:10 +07:00
Compare commits
No commits in common. "b23b0ae31a57506bfc87e9c45a9dca4486c1bb69" and "93fb22f29b0d89a3a5c98f55a38972868019ceb7" have entirely different histories.
b23b0ae31a
...
93fb22f29b
16 changed files with 93 additions and 1114 deletions
|
@ -16,7 +16,6 @@ import (
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
"github.com/gofiber/fiber/v2/middleware/favicon"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -75,10 +74,6 @@ func serve() {
|
||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
app.Use(favicon.New(favicon.Config{
|
|
||||||
File: "internal/handler/views/assets/favicon.ico",
|
|
||||||
URL: "/favicon.ico",
|
|
||||||
}))
|
|
||||||
app.Use("/assets", views.EmbedAssets())
|
app.Use("/assets", views.EmbedAssets())
|
||||||
app.Routes()
|
app.Routes()
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
1
go.mod
1
go.mod
|
@ -6,7 +6,6 @@ require (
|
||||||
github.com/a-h/templ v0.2.778
|
github.com/a-h/templ v0.2.778
|
||||||
github.com/go-sql-driver/mysql v1.8.1
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
github.com/gofiber/fiber/v2 v2.52.5
|
github.com/gofiber/fiber/v2 v2.52.5
|
||||||
github.com/google/go-querystring v1.1.0
|
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/jmoiron/sqlx v1.4.0
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -11,11 +11,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
@ -64,7 +61,6 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/a-h/templ"
|
"github.com/a-h/templ"
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/handler/views"
|
"github.com/ditatompel/xmr-remote-nodes/internal/handler/views"
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
|
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/paging"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/adaptor"
|
"github.com/gofiber/fiber/v2/middleware/adaptor"
|
||||||
|
@ -31,6 +30,24 @@ func (s *fiberServer) homeHandler(c *fiber.Ctx) error {
|
||||||
return handler(c)
|
return handler(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render Remote Nodes Page
|
||||||
|
func (s *fiberServer) remoteNodesHandler(c *fiber.Ctx) error {
|
||||||
|
p := views.Meta{
|
||||||
|
Title: "Public Monero Remote Nodes List",
|
||||||
|
Description: "Although it's possible to use these existing public Monero nodes, you're MUST RUN AND USE YOUR OWN NODE!",
|
||||||
|
Keywords: "monero remote nodes,public monero nodes,monero public nodes,monero wallet,tor monero node,monero cors rpc",
|
||||||
|
Robots: "INDEX,FOLLOW",
|
||||||
|
Permalink: "https://xmr.ditatompel.com/remote-nodes",
|
||||||
|
Identifier: "/remote-nodes",
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, p.Permalink))
|
||||||
|
home := views.BaseLayout(p, views.RemoteNodes())
|
||||||
|
handler := adaptor.HTTPHandler(templ.Handler(home))
|
||||||
|
|
||||||
|
return handler(c)
|
||||||
|
}
|
||||||
|
|
||||||
// Render Add Node Page
|
// Render Add Node Page
|
||||||
func (s *fiberServer) addNodeHandler(c *fiber.Ctx) error {
|
func (s *fiberServer) addNodeHandler(c *fiber.Ctx) error {
|
||||||
p := views.Meta{
|
p := views.Meta{
|
||||||
|
@ -85,78 +102,20 @@ func Node(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render Remote Nodes Page
|
// Returns a list of nodes
|
||||||
func (s *fiberServer) remoteNodesHandler(c *fiber.Ctx) error {
|
|
||||||
p := views.Meta{
|
|
||||||
Title: "Public Monero Remote Nodes List",
|
|
||||||
Description: "Although it's possible to use these existing public Monero nodes, you're MUST RUN AND USE YOUR OWN NODE!",
|
|
||||||
Keywords: "monero remote nodes,public monero nodes,monero public nodes,monero wallet,tor monero node,monero cors rpc",
|
|
||||||
Robots: "INDEX,FOLLOW",
|
|
||||||
Permalink: "https://xmr.ditatompel.com/remote-nodes",
|
|
||||||
Identifier: "/remote-nodes",
|
|
||||||
}
|
|
||||||
|
|
||||||
moneroRepo := monero.New()
|
|
||||||
query := monero.QueryNodes{
|
|
||||||
Paging: paging.Paging{
|
|
||||||
Limit: c.QueryInt("limit", 10), // rows per page
|
|
||||||
Page: c.QueryInt("page", 1),
|
|
||||||
SortBy: c.Query("sort_by", "id"),
|
|
||||||
SortDir: c.Query("sort_dir", "desc"),
|
|
||||||
SortDirection: c.Query("sort_direction", "desc"), // deprecated
|
|
||||||
Refresh: c.QueryInt("refresh", 0),
|
|
||||||
},
|
|
||||||
Host: c.Query("host"),
|
|
||||||
Nettype: c.Query("nettype", "any"),
|
|
||||||
Protocol: c.Query("protocol", "any"),
|
|
||||||
CC: c.Query("cc", "any"),
|
|
||||||
Status: c.QueryInt("status", -1),
|
|
||||||
CORS: c.QueryInt("cors", -1),
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes, err := moneroRepo.Nodes(query)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
||||||
"status": "error",
|
|
||||||
"message": err.Error(),
|
|
||||||
"data": nil,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pagination := paging.NewPagination(query.Page, nodes.TotalPages)
|
|
||||||
|
|
||||||
// handle request from HTMX
|
|
||||||
if c.Get("HX-Target") == "tbl_nodes" {
|
|
||||||
cmp := views.BlankLayout(views.TableNodes(nodes, query, pagination))
|
|
||||||
handler := adaptor.HTTPHandler(templ.Handler(cmp))
|
|
||||||
return handler(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, p.Permalink))
|
|
||||||
home := views.BaseLayout(p, views.RemoteNodes(nodes, query, pagination))
|
|
||||||
handler := adaptor.HTTPHandler(templ.Handler(home))
|
|
||||||
|
|
||||||
return handler(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a list of nodes (API)
|
|
||||||
func Nodes(c *fiber.Ctx) error {
|
func Nodes(c *fiber.Ctx) error {
|
||||||
moneroRepo := monero.New()
|
moneroRepo := monero.New()
|
||||||
query := monero.QueryNodes{
|
query := monero.QueryNodes{
|
||||||
Paging: paging.Paging{
|
RowsPerPage: c.QueryInt("limit", 10),
|
||||||
Limit: c.QueryInt("limit", 10), // rows per page
|
Page: c.QueryInt("page", 1),
|
||||||
Page: c.QueryInt("page", 1),
|
SortBy: c.Query("sort_by", "id"),
|
||||||
SortBy: c.Query("sort_by", "id"),
|
SortDirection: c.Query("sort_direction", "desc"),
|
||||||
SortDir: c.Query("sort_dir", "desc"),
|
Host: c.Query("host"),
|
||||||
SortDirection: c.Query("sort_direction", "desc"), // deprecated
|
Nettype: c.Query("nettype", "any"),
|
||||||
Refresh: c.QueryInt("refresh", 0),
|
Protocol: c.Query("protocol", "any"),
|
||||||
},
|
CC: c.Query("cc", "any"),
|
||||||
Host: c.Query("host"),
|
Status: c.QueryInt("status", -1),
|
||||||
Nettype: c.Query("nettype", "any"),
|
CORS: c.QueryInt("cors", -1),
|
||||||
Protocol: c.Query("protocol", "any"),
|
|
||||||
CC: c.Query("cc", "any"),
|
|
||||||
Status: c.QueryInt("status", -1),
|
|
||||||
CORS: c.QueryInt("cors", -1),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes, err := moneroRepo.Nodes(query)
|
nodes, err := moneroRepo.Nodes(query)
|
||||||
|
|
|
@ -49,7 +49,7 @@ templ base(m Meta) {
|
||||||
</main>
|
</main>
|
||||||
<footer class="mt-auto py-3 bg-neutral-800 text-center">
|
<footer class="mt-auto py-3 bg-neutral-800 text-center">
|
||||||
<div class="max-w-[85rem] mx-auto px-4 sm:px-6 lg:px-8">
|
<div class="max-w-[85rem] mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<p class="text-sm">XMR Nodes { config.Version }, <a href="https://github.com/ditatompel/xmr-remote-nodes" target="_blank" rel="noopener" class="external">source code</a> licensed under <strong>BSD-3-Clause</strong> license.</p>
|
<p class="text-sm">XMR Nodes { config.Version }, <a href="https://github.com/ditatompel/xmr-remote-nodes">source code</a> licensed under <strong>BSD-3-Clause</strong> license.</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -231,7 +231,7 @@ func base(m Meta) templ.Component {
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(", <a href=\"https://github.com/ditatompel/xmr-remote-nodes\" target=\"_blank\" rel=\"noopener\" class=\"external\">source code</a> licensed under <strong>BSD-3-Clause</strong> license.</p></div></footer></body></html>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(", <a href=\"https://github.com/ditatompel/xmr-remote-nodes\">source code</a> licensed under <strong>BSD-3-Clause</strong> license.</p></div></footer></body></html>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
package views
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/paging"
|
|
||||||
)
|
|
||||||
|
|
||||||
var availablePages = []int{5, 10, 20, 50, 100}
|
|
||||||
|
|
||||||
templ DtRowPerPage(url, hxTarget string, rowsPerPage int, q interface{}) {
|
|
||||||
<div class="max-w-sm space-y-3">
|
|
||||||
<select
|
|
||||||
name="limit"
|
|
||||||
id="dt_limit"
|
|
||||||
class="py-2 px-3 pe-9 block bg-neutral-900 border-neutral-700 rounded-lg text-sm focus:border-orange-400 focus:ring-orange-400"
|
|
||||||
hx-get={ fmt.Sprintf("%s?%s", url, paging.EncodedQuery(q, []string{"limit"})) }
|
|
||||||
hx-trigger="change"
|
|
||||||
hx-push-url="true"
|
|
||||||
hx-target={ hxTarget }
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>
|
|
||||||
<option disabled>CHOOSE</option>
|
|
||||||
for _, page := range availablePages {
|
|
||||||
<option
|
|
||||||
value={ fmt.Sprintf("%d", page) }
|
|
||||||
selected?={ page == rowsPerPage }
|
|
||||||
>{ fmt.Sprintf("%d", page) }</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
templ DtRowCount(currentPage, rowsPerPage, totalRows int) {
|
|
||||||
<div>
|
|
||||||
<p class="text-sm">
|
|
||||||
if totalRows <= 0 {
|
|
||||||
No entries found
|
|
||||||
} else {
|
|
||||||
<b>{ fmt.Sprintf("%d", (rowsPerPage * currentPage) - rowsPerPage + 1) }</b>
|
|
||||||
if rowsPerPage * currentPage > totalRows {
|
|
||||||
- <b>{ fmt.Sprintf("%d", totalRows) }</b>
|
|
||||||
} else {
|
|
||||||
- <b>{ fmt.Sprintf("%d", rowsPerPage * currentPage) }</b>
|
|
||||||
}
|
|
||||||
<b>/ { fmt.Sprintf("%d", totalRows) }</b>
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
templ DtPagination(url, hxTarget string, q interface{}, p paging.Pagination) {
|
|
||||||
<div>
|
|
||||||
<nav class="pagination inline-flex gap-x-2">
|
|
||||||
for _, page := range p.Pages {
|
|
||||||
if page == -1 {
|
|
||||||
<button class="cursor-not-allowed" disabled>...</button>
|
|
||||||
} else if page == p.CurrentPage {
|
|
||||||
<button class="active" disabled>{ fmt.Sprintf("%d", page) }</button>
|
|
||||||
} else {
|
|
||||||
<button
|
|
||||||
hx-get={ fmt.Sprintf("%s?%s&page=%d", url, paging.EncodedQuery(q, []string{"page"}), page) }
|
|
||||||
hx-push-url="true"
|
|
||||||
hx-target={ hxTarget }
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>{ fmt.Sprintf("%d", page) }</button>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
}
|
|
|
@ -1,333 +0,0 @@
|
||||||
// Code generated by templ - DO NOT EDIT.
|
|
||||||
|
|
||||||
// templ: version: v0.2.778
|
|
||||||
package views
|
|
||||||
|
|
||||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
||||||
|
|
||||||
import "github.com/a-h/templ"
|
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/paging"
|
|
||||||
)
|
|
||||||
|
|
||||||
var availablePages = []int{5, 10, 20, 50, 100}
|
|
||||||
|
|
||||||
func DtRowPerPage(url, hxTarget string, rowsPerPage int, q interface{}) 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_Var1 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var1 == nil {
|
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"max-w-sm space-y-3\"><select name=\"limit\" id=\"dt_limit\" class=\"py-2 px-3 pe-9 block bg-neutral-900 border-neutral-700 rounded-lg text-sm focus:border-orange-400 focus:ring-orange-400\" hx-get=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var2 string
|
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s?%s", url, paging.EncodedQuery(q, []string{"limit"})))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 16, Col: 80}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-trigger=\"change\" hx-push-url=\"true\" hx-target=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var3 string
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(hxTarget)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 19, Col: 23}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"outerHTML\"><option disabled>CHOOSE</option> ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
for _, page := range availablePages {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var4 string
|
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", page))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 25, Col: 36}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
|
||||||
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
|
|
||||||
}
|
|
||||||
if page == rowsPerPage {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" selected")
|
|
||||||
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
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var5 string
|
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", page))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 27, Col: 30}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</select></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func DtRowCount(currentPage, rowsPerPage, totalRows int) 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_Var6 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var6 == nil {
|
|
||||||
templ_7745c5c3_Var6 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><p class=\"text-sm\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if totalRows <= 0 {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("No entries found")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<b>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var7 string
|
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", (rowsPerPage*currentPage)-rowsPerPage+1))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 39, Col: 73}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</b> ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if rowsPerPage*currentPage > totalRows {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("- <b>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var8 string
|
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", totalRows))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 41, Col: 40}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</b>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("- <b>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var9 string
|
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", rowsPerPage*currentPage))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 43, Col: 56}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</b>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <b>/ ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var10 string
|
|
||||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", totalRows))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 45, Col: 39}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</b>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func DtPagination(url, hxTarget string, q interface{}, p paging.Pagination) 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_Var11 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var11 == nil {
|
|
||||||
templ_7745c5c3_Var11 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><nav class=\"pagination inline-flex gap-x-2\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
for _, page := range p.Pages {
|
|
||||||
if page == -1 {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button class=\"cursor-not-allowed\" disabled>...</button>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else if page == p.CurrentPage {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button class=\"active\" disabled>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var12 string
|
|
||||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", page))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 58, Col: 62}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button hx-get=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var13 string
|
|
||||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s?%s&page=%d", url, paging.EncodedQuery(q, []string{"page"}), page))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 61, Col: 96}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-push-url=\"true\" hx-target=\"")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var14 string
|
|
||||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(hxTarget)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 63, Col: 26}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"outerHTML\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var15 string
|
|
||||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", page))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/partial_datatable.templ`, Line: 65, Col: 31}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</nav></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ = templruntime.GeneratedTemplate
|
|
|
@ -1,15 +1,6 @@
|
||||||
package views
|
package views
|
||||||
|
|
||||||
import (
|
templ RemoteNodes() {
|
||||||
"fmt"
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/ip"
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/paging"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
templ RemoteNodes(data monero.Nodes, q monero.QueryNodes, p paging.Pagination) {
|
|
||||||
<!-- Hero -->
|
<!-- Hero -->
|
||||||
<section class="relative overflow-hidden pt-6">
|
<section class="relative overflow-hidden pt-6">
|
||||||
<!-- Gradients -->
|
<!-- Gradients -->
|
||||||
|
@ -39,87 +30,4 @@ templ RemoteNodes(data monero.Nodes, q monero.QueryNodes, p paging.Pagination) {
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<!-- End Hero -->
|
<!-- End Hero -->
|
||||||
<div class="flex flex-col max-w-6xl mx-auto mb-10">
|
|
||||||
<div class="min-w-full inline-block align-middle">
|
|
||||||
@TableNodes(data, q, p)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
templ TableNodes(data monero.Nodes, q monero.QueryNodes, p paging.Pagination) {
|
|
||||||
<div id="tbl_nodes" class="bg-neutral-800 border border-neutral-700 rounded-xl shadow-sm overflow-hidden">
|
|
||||||
<div class="px-6 py-4 grid gap-3 md:flex md:justify-between md:items-center border-b border-neutral-700">
|
|
||||||
@DtRowPerPage("/remote-nodes", "#tbl_nodes", q.Limit, q)
|
|
||||||
</div>
|
|
||||||
<div class="overflow-x-auto">
|
|
||||||
<table class="dt">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">Host:Port</th>
|
|
||||||
<th scope="col">Nettype</th>
|
|
||||||
<th scope="col">Protocol</th>
|
|
||||||
<th scope="col">Country</th>
|
|
||||||
<th scope="col">Status</th>
|
|
||||||
<th scope="col">Estimate Fee</th>
|
|
||||||
<th scope="col">Uptime</th>
|
|
||||||
<th scope="col">Check</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
for _, row := range data.Items {
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
@cellHostPort(row.IPAddresses, row.Hostname, row.Port, row.IsTor, row.IPv6Only)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@cellNettype(row.Nettype, row.Height)
|
|
||||||
</td>
|
|
||||||
<td>{ row.Protocol }</td>
|
|
||||||
<td>{ row.CountryCode }</td>
|
|
||||||
<td>{ fmt.Sprintf("%d", row.EstimateFee) }</td>
|
|
||||||
<td>{ time.Unix(row.LastChecked, 0).Format("2006-01-02 15:04") }</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="px-6 py-4 grid gap-3 md:flex md:justify-between md:items-center border-t border-neutral-700">
|
|
||||||
@DtRowCount(p.CurrentPage, data.RowsPerPage, data.TotalRows)
|
|
||||||
@DtPagination("/remote-nodes", "#tbl_nodes", q, p)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
templ cellHostPort(ips, hostname string, port uint, isTor, ipv6Only bool) {
|
|
||||||
if isTor {
|
|
||||||
<!-- TODO: Add modal -->
|
|
||||||
<button class="max-w-40 truncate text-orange-400">
|
|
||||||
👁 { hostname }
|
|
||||||
</button>
|
|
||||||
<br/>
|
|
||||||
.onion:<span class="text-indigo-400">{ fmt.Sprintf("%d", port) }</span>
|
|
||||||
<span class="text-neutral-400">(TOR)</span>
|
|
||||||
} else {
|
|
||||||
{ ip.FormatHostname(hostname) }:<span class="text-indigo-400">{ fmt.Sprintf("%d", port) }</span>
|
|
||||||
<br/>
|
|
||||||
<div class="max-w-40 text-ellipsis overflow-x-auto md:overflow-hidden hover:overflow-visible">
|
|
||||||
<span class="whitespace-break-spaces text-gray-400">{ strings.ReplaceAll(ips, ",", " ") }</span>
|
|
||||||
if ipv6Only {
|
|
||||||
<span class="text-rose-400">(IPv6 only)</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
templ cellNettype(nettype string, height uint) {
|
|
||||||
switch nettype {
|
|
||||||
case "stagenet":
|
|
||||||
<span class="font-semibold uppercase text-sky-500">{ nettype }</span>
|
|
||||||
case "testnet":
|
|
||||||
<span class="font-semibold uppercase text-rose-500">{ nettype }</span>
|
|
||||||
default:
|
|
||||||
<span class="font-semibold uppercase text-green-500">{ nettype }</span>
|
|
||||||
}
|
|
||||||
<br/>
|
|
||||||
{ fmt.Sprintf("%d", height) }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,7 @@ package views
|
||||||
import "github.com/a-h/templ"
|
import "github.com/a-h/templ"
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
import (
|
func RemoteNodes() templ.Component {
|
||||||
"fmt"
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/ip"
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/monero"
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/paging"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RemoteNodes(data monero.Nodes, q monero.QueryNodes, p paging.Pagination) templ.Component {
|
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
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
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
@ -38,343 +29,7 @@ func RemoteNodes(data monero.Nodes, q monero.QueryNodes, p paging.Pagination) te
|
||||||
templ_7745c5c3_Var1 = templ.NopComponent
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!-- Hero --><section class=\"relative overflow-hidden pt-6\"><!-- Gradients --><div aria-hidden=\"true\" class=\"flex absolute -top-96 start-1/2 transform -translate-x-1/2\"><div class=\"bg-gradient-to-r blur-3xl w-[25rem] h-[44rem] rotate-[-60deg] transform -translate-x-[10rem] from-amber-800/30 to-orange-800/40\"></div><div class=\"bg-gradient-to-tl blur-3xl w-[90rem] h-[50rem] rounded-fulls origin-top-left -rotate-12 -translate-x-[15rem] from-orange-900/60 via-orange-900/40 to-amber-900/80\"></div></div><!-- End Gradients --><div class=\"relative z-10\"><div class=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-10 lg:py-16\"><div class=\"text-center\"><!-- Title --><div class=\"mt-5\"><h1 class=\"block font-extrabold text-4xl md:text-5xl lg:text-6xl text-neutral-200\">Public Monero Remote Nodes List</h1></div><!-- End Title --><div class=\"mt-5\"><p class=\"text-lg text-neutral-300\"><strong>Monero remote node</strong> is a device on the internet running the Monero software with full copy of the Monero blockchain that doesn't run on the same local machine where the Monero wallet is located.</p></div><hr class=\"mt-6\"></div><div class=\"max-w-3xl text-center mx-auto mt-8 prose prose-invert\"><p>Remote node can be used by people who, for their own reasons (usually because of hardware requirements, disk space, or technical abilities), cannot/don't want to run their own node and prefer to relay on one publicly available on the Monero network.</p><p>Using an open node will allow to make a transaction instantaneously, without the need to download the blockchain and sync to the Monero network first, but at the cost of the control over your privacy. the <strong>Monero community suggests to <span class=\"font-extrabold text-2xl underline decoration-double decoration-2 decoration-pink-500\">always run and use your own node</span></strong> to obtain the maximum possible privacy and to help decentralize the network.</p></div></div></div></section><!-- End Hero --><div class=\"flex flex-col max-w-6xl mx-auto mb-10\"><div class=\"min-w-full inline-block align-middle\">")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!-- Hero --><section class=\"relative overflow-hidden pt-6\"><!-- Gradients --><div aria-hidden=\"true\" class=\"flex absolute -top-96 start-1/2 transform -translate-x-1/2\"><div class=\"bg-gradient-to-r blur-3xl w-[25rem] h-[44rem] rotate-[-60deg] transform -translate-x-[10rem] from-amber-800/30 to-orange-800/40\"></div><div class=\"bg-gradient-to-tl blur-3xl w-[90rem] h-[50rem] rounded-fulls origin-top-left -rotate-12 -translate-x-[15rem] from-orange-900/60 via-orange-900/40 to-amber-900/80\"></div></div><!-- End Gradients --><div class=\"relative z-10\"><div class=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-10 lg:py-16\"><div class=\"text-center\"><!-- Title --><div class=\"mt-5\"><h1 class=\"block font-extrabold text-4xl md:text-5xl lg:text-6xl text-neutral-200\">Public Monero Remote Nodes List</h1></div><!-- End Title --><div class=\"mt-5\"><p class=\"text-lg text-neutral-300\"><strong>Monero remote node</strong> is a device on the internet running the Monero software with full copy of the Monero blockchain that doesn't run on the same local machine where the Monero wallet is located.</p></div><hr class=\"mt-6\"></div><div class=\"max-w-3xl text-center mx-auto mt-8 prose prose-invert\"><p>Remote node can be used by people who, for their own reasons (usually because of hardware requirements, disk space, or technical abilities), cannot/don't want to run their own node and prefer to relay on one publicly available on the Monero network.</p><p>Using an open node will allow to make a transaction instantaneously, without the need to download the blockchain and sync to the Monero network first, but at the cost of the control over your privacy. the <strong>Monero community suggests to <span class=\"font-extrabold text-2xl underline decoration-double decoration-2 decoration-pink-500\">always run and use your own node</span></strong> to obtain the maximum possible privacy and to help decentralize the network.</p></div></div></div></section><!-- End Hero -->")
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = TableNodes(data, q, p).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TableNodes(data monero.Nodes, q monero.QueryNodes, p paging.Pagination) 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_Var2 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var2 == nil {
|
|
||||||
templ_7745c5c3_Var2 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div id=\"tbl_nodes\" class=\"bg-neutral-800 border border-neutral-700 rounded-xl shadow-sm overflow-hidden\"><div class=\"px-6 py-4 grid gap-3 md:flex md:justify-between md:items-center border-b border-neutral-700\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = DtRowPerPage("/remote-nodes", "#tbl_nodes", q.Limit, q).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"overflow-x-auto\"><table class=\"dt\"><thead><tr><th scope=\"col\">Host:Port</th><th scope=\"col\">Nettype</th><th scope=\"col\">Protocol</th><th scope=\"col\">Country</th><th scope=\"col\">Status</th><th scope=\"col\">Estimate Fee</th><th scope=\"col\">Uptime</th><th scope=\"col\">Check</th></tr></thead> <tbody>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
for _, row := range data.Items {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<tr><td>")
|
|
||||||
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)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = cellNettype(row.Nettype, row.Height).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var3 string
|
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(row.Protocol)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 77, Col: 25}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var4 string
|
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(row.CountryCode)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 78, Col: 28}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var5 string
|
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", row.EstimateFee))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 79, Col: 47}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td><td>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var6 string
|
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(time.Unix(row.LastChecked, 0).Format("2006-01-02 15:04"))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 80, Col: 69}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</td></tr>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</tbody></table></div><div class=\"px-6 py-4 grid gap-3 md:flex md:justify-between md:items-center border-t border-neutral-700\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = DtRowCount(p.CurrentPage, data.RowsPerPage, data.TotalRows).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
templ_7745c5c3_Err = DtPagination("/remote-nodes", "#tbl_nodes", q, p).Render(ctx, templ_7745c5c3_Buffer)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func cellHostPort(ips, hostname string, port uint, 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_Var7 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var7 == nil {
|
|
||||||
templ_7745c5c3_Var7 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
if isTor {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!-- TODO: Add modal --> <button class=\"max-w-40 truncate text-orange-400\">👁 ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var8 string
|
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(hostname)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 97, Col: 18}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button><br>.onion:<span class=\"text-indigo-400\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var9 string
|
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", port))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 100, Col: 64}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span> <span class=\"text-neutral-400\">(TOR)</span>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var templ_7745c5c3_Var10 string
|
|
||||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(ip.FormatHostname(hostname))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 103, Col: 31}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(":<span class=\"text-indigo-400\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var11 string
|
|
||||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", port))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 103, Col: 89}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span><br><div class=\"max-w-40 text-ellipsis overflow-x-auto md:overflow-hidden hover:overflow-visible\"><span class=\"whitespace-break-spaces text-gray-400\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var12 string
|
|
||||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(strings.ReplaceAll(ips, ",", " "))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 106, Col: 90}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span> ")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
if ipv6Only {
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<span class=\"text-rose-400\">(IPv6 only)</span>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func cellNettype(nettype string, height uint) 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_Var13 := templ.GetChildren(ctx)
|
|
||||||
if templ_7745c5c3_Var13 == nil {
|
|
||||||
templ_7745c5c3_Var13 = templ.NopComponent
|
|
||||||
}
|
|
||||||
ctx = templ.ClearChildren(ctx)
|
|
||||||
switch nettype {
|
|
||||||
case "stagenet":
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<span class=\"font-semibold uppercase text-sky-500\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var14 string
|
|
||||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(nettype)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 117, Col: 63}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
case "testnet":
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<span class=\"font-semibold uppercase text-rose-500\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var15 string
|
|
||||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(nettype)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 119, Col: 64}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<span class=\"font-semibold uppercase text-green-500\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var16 string
|
|
||||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(nettype)
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 121, Col: 65}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<br>")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ_7745c5c3_Err
|
|
||||||
}
|
|
||||||
var templ_7745c5c3_Var17 string
|
|
||||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", height))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/remote_nodes.templ`, Line: 124, Col: 28}
|
|
||||||
}
|
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,32 +25,3 @@ a.btn-link {
|
||||||
button.copy-input {
|
button.copy-input {
|
||||||
@apply px-2 shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:brightness-125 focus:outline-none focus:bg-orange-700 disabled:opacity-50 disabled:pointer-events-none;
|
@apply px-2 shrink-0 inline-flex justify-center items-center gap-x-2 text-sm font-semibold rounded-e-md border border-transparent bg-orange-600 text-white hover:brightness-125 focus:outline-none focus:bg-orange-700 disabled:opacity-50 disabled:pointer-events-none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* table */
|
|
||||||
table.dt {
|
|
||||||
@apply min-w-full divide-y divide-neutral-700;
|
|
||||||
}
|
|
||||||
table.dt thead {
|
|
||||||
@apply bg-neutral-800;
|
|
||||||
}
|
|
||||||
table.dt thead tr th {
|
|
||||||
@apply px-3 py-3 text-start text-xs font-semibold uppercase text-neutral-200;
|
|
||||||
}
|
|
||||||
table.dt tbody {
|
|
||||||
@apply divide-y divide-neutral-700;
|
|
||||||
}
|
|
||||||
table.dt tbody tr {
|
|
||||||
@apply odd:bg-neutral-900 even:bg-neutral-800;
|
|
||||||
}
|
|
||||||
table.dt tbody tr th,
|
|
||||||
table.dt tbody tr td {
|
|
||||||
@apply px-3 py-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pagination */
|
|
||||||
nav.pagination button.active {
|
|
||||||
@apply py-1.5 px-2 inline-flex items-center gap-x-2 text-sm font-bold rounded-lg border border-orange-500 bg-orange-500 text-white shadow-sm hover:brightness-125 disabled:opacity-90 disabled:pointer-events-none;
|
|
||||||
}
|
|
||||||
nav.pagination button {
|
|
||||||
@apply py-1.5 px-2 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg bg-neutral-800 border border-neutral-700 text-white shadow-sm hover:brightness-125 disabled:opacity-50 disabled:pointer-events-none;
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,16 +26,3 @@ func SliceToString(ips []net.IP) string {
|
||||||
|
|
||||||
return strings.Join(r, ",")
|
return strings.Join(r, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add brackets based on whether the given string is IPv6 or not.
|
|
||||||
// If the input is an IPv6 address, wraps it in square brackets `[ ]`.
|
|
||||||
// Otherwise, it returns the input string as-is (for domain names or IPv4
|
|
||||||
// addresses).
|
|
||||||
func FormatHostname(hostname string) string {
|
|
||||||
ip := net.ParseIP(hostname)
|
|
||||||
if ip != nil && ip.To4() == nil {
|
|
||||||
return "[" + hostname + "]"
|
|
||||||
}
|
|
||||||
|
|
||||||
return hostname
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"math"
|
|
||||||
"net"
|
"net"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -14,7 +13,6 @@ import (
|
||||||
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/database"
|
"github.com/ditatompel/xmr-remote-nodes/internal/database"
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/ip"
|
"github.com/ditatompel/xmr-remote-nodes/internal/ip"
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/paging"
|
|
||||||
"github.com/jmoiron/sqlx/types"
|
"github.com/jmoiron/sqlx/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -52,7 +50,7 @@ type Node struct {
|
||||||
Latitude float64 `json:"latitude" db:"lat"`
|
Latitude float64 `json:"latitude" db:"lat"`
|
||||||
Longitude float64 `json:"longitude" db:"lon"`
|
Longitude float64 `json:"longitude" db:"lon"`
|
||||||
DateEntered uint `json:"date_entered,omitempty" db:"date_entered"`
|
DateEntered uint `json:"date_entered,omitempty" db:"date_entered"`
|
||||||
LastChecked int64 `json:"last_checked" db:"last_checked"`
|
LastChecked uint `json:"last_checked" db:"last_checked"`
|
||||||
FailedCount uint `json:"failed_count,omitempty" db:"failed_count"`
|
FailedCount uint `json:"failed_count,omitempty" db:"failed_count"`
|
||||||
LastCheckStatus types.JSONText `json:"last_check_statuses" db:"last_check_status"`
|
LastCheckStatus types.JSONText `json:"last_check_statuses" db:"last_check_status"`
|
||||||
CORSCapable bool `json:"cors" db:"cors_capable"`
|
CORSCapable bool `json:"cors" db:"cors_capable"`
|
||||||
|
@ -76,13 +74,18 @@ func (r *moneroRepo) Node(id int) (Node, error) {
|
||||||
|
|
||||||
// QueryNodes represents database query parameters
|
// QueryNodes represents database query parameters
|
||||||
type QueryNodes struct {
|
type QueryNodes struct {
|
||||||
paging.Paging
|
Host string
|
||||||
Host string `url:"host,omitempty"`
|
|
||||||
Nettype string // Can be "any", mainnet, stagenet, testnet. Default: "any"
|
Nettype string // Can be "any", mainnet, stagenet, testnet. Default: "any"
|
||||||
Protocol string // Can be "any", tor, http, https. Default: "any"
|
Protocol string // Can be "any", tor, http, https. Default: "any"
|
||||||
CC string `url:"cc,omitempty"` // 2 letter country code
|
CC string // 2 letter country code
|
||||||
Status int
|
Status int
|
||||||
CORS int
|
CORS int
|
||||||
|
|
||||||
|
// pagination
|
||||||
|
RowsPerPage int
|
||||||
|
Page int
|
||||||
|
SortBy string
|
||||||
|
SortDirection string
|
||||||
}
|
}
|
||||||
|
|
||||||
// toSQL generates SQL query from query parameters
|
// toSQL generates SQL query from query parameters
|
||||||
|
@ -130,14 +133,8 @@ func (q *QueryNodes) toSQL() (args []interface{}, where string) {
|
||||||
if !slices.Contains([]string{"last_checked", "uptime"}, q.SortBy) {
|
if !slices.Contains([]string{"last_checked", "uptime"}, q.SortBy) {
|
||||||
q.SortBy = "last_checked"
|
q.SortBy = "last_checked"
|
||||||
}
|
}
|
||||||
|
|
||||||
// deprecated: Use SortDir instead
|
|
||||||
if q.SortDirection != "asc" {
|
if q.SortDirection != "asc" {
|
||||||
q.SortDir = "DESC"
|
q.SortDirection = "DESC"
|
||||||
}
|
|
||||||
|
|
||||||
if q.SortDir != "asc" {
|
|
||||||
q.SortDir = "DESC"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return args, where
|
return args, where
|
||||||
|
@ -146,7 +143,6 @@ func (q *QueryNodes) toSQL() (args []interface{}, where string) {
|
||||||
// Nodes represents a list of nodes
|
// Nodes represents a list of nodes
|
||||||
type Nodes struct {
|
type Nodes struct {
|
||||||
TotalRows int `json:"total_rows"`
|
TotalRows int `json:"total_rows"`
|
||||||
TotalPages int `json:"total_pages"` // total pages
|
|
||||||
RowsPerPage int `json:"rows_per_page"`
|
RowsPerPage int `json:"rows_per_page"`
|
||||||
Items []*Node `json:"items"`
|
Items []*Node `json:"items"`
|
||||||
}
|
}
|
||||||
|
@ -157,7 +153,7 @@ func (r *moneroRepo) Nodes(q QueryNodes) (Nodes, error) {
|
||||||
|
|
||||||
var nodes Nodes
|
var nodes Nodes
|
||||||
|
|
||||||
nodes.RowsPerPage = q.Limit
|
nodes.RowsPerPage = q.RowsPerPage
|
||||||
|
|
||||||
qTotal := fmt.Sprintf(`
|
qTotal := fmt.Sprintf(`
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -170,8 +166,7 @@ func (r *moneroRepo) Nodes(q QueryNodes) (Nodes, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nodes, err
|
return nodes, err
|
||||||
}
|
}
|
||||||
nodes.TotalPages = int(math.Ceil(float64(nodes.TotalRows) / float64(q.Limit)))
|
args = append(args, q.RowsPerPage, (q.Page-1)*q.RowsPerPage)
|
||||||
args = append(args, q.Limit, (q.Page-1)*q.Limit)
|
|
||||||
|
|
||||||
query := fmt.Sprintf(`
|
query := fmt.Sprintf(`
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -183,7 +178,7 @@ func (r *moneroRepo) Nodes(q QueryNodes) (Nodes, error) {
|
||||||
%s
|
%s
|
||||||
%s
|
%s
|
||||||
LIMIT ?
|
LIMIT ?
|
||||||
OFFSET ?`, where, q.SortBy, q.SortDir)
|
OFFSET ?`, where, q.SortBy, q.SortDirection)
|
||||||
err = r.db.Select(&nodes.Items, query, args...)
|
err = r.db.Select(&nodes.Items, query, args...)
|
||||||
|
|
||||||
return nodes, err
|
return nodes, err
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/config"
|
"github.com/ditatompel/xmr-remote-nodes/internal/config"
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/database"
|
"github.com/ditatompel/xmr-remote-nodes/internal/database"
|
||||||
"github.com/ditatompel/xmr-remote-nodes/internal/paging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var testMySQL = true
|
var testMySQL = true
|
||||||
|
@ -40,56 +39,50 @@ func init() {
|
||||||
// go test -race ./internal/monero -run=TestQueryNodes_toSQL -v
|
// go test -race ./internal/monero -run=TestQueryNodes_toSQL -v
|
||||||
func TestQueryNodes_toSQL(t *testing.T) {
|
func TestQueryNodes_toSQL(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
query QueryNodes
|
query QueryNodes
|
||||||
wantArgs []interface{}
|
wantArgs []interface{}
|
||||||
wantWhere string
|
wantWhere string
|
||||||
wantSortBy string
|
wantSortBy string
|
||||||
wantSortDir string
|
wantSortDirection string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Default query",
|
name: "Default query",
|
||||||
query: QueryNodes{
|
query: QueryNodes{
|
||||||
Paging: paging.Paging{
|
Host: "",
|
||||||
Limit: 10,
|
Nettype: "any",
|
||||||
Page: 1,
|
Protocol: "any",
|
||||||
SortBy: "last_checked",
|
CC: "any",
|
||||||
SortDir: "desc",
|
Status: -1,
|
||||||
SortDirection: "desc", // deprecated
|
CORS: -1,
|
||||||
},
|
RowsPerPage: 10,
|
||||||
Host: "",
|
Page: 1,
|
||||||
Nettype: "any",
|
SortBy: "last_checked",
|
||||||
Protocol: "any",
|
SortDirection: "desc",
|
||||||
CC: "any",
|
|
||||||
Status: -1,
|
|
||||||
CORS: -1,
|
|
||||||
},
|
},
|
||||||
wantArgs: []interface{}{},
|
wantArgs: []interface{}{},
|
||||||
wantWhere: "",
|
wantWhere: "",
|
||||||
wantSortBy: "last_checked",
|
wantSortBy: "last_checked",
|
||||||
wantSortDir: "DESC",
|
wantSortDirection: "DESC",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "With host query",
|
name: "With host query",
|
||||||
query: QueryNodes{
|
query: QueryNodes{
|
||||||
Paging: paging.Paging{
|
Host: "test",
|
||||||
Limit: 10,
|
Nettype: "any",
|
||||||
Page: 1,
|
Protocol: "any",
|
||||||
SortBy: "last_checked",
|
CC: "any",
|
||||||
SortDir: "desc",
|
Status: -1,
|
||||||
SortDirection: "desc", // deprecated
|
CORS: -1,
|
||||||
},
|
RowsPerPage: 10,
|
||||||
Host: "test",
|
Page: 1,
|
||||||
Nettype: "any",
|
SortBy: "last_checked",
|
||||||
Protocol: "any",
|
SortDirection: "desc",
|
||||||
CC: "any",
|
|
||||||
Status: -1,
|
|
||||||
CORS: -1,
|
|
||||||
},
|
},
|
||||||
wantArgs: []interface{}{"%test%", "%test%"},
|
wantArgs: []interface{}{"%test%", "%test%"},
|
||||||
wantWhere: "WHERE (hostname LIKE ? OR ip_addr LIKE ?)",
|
wantWhere: "WHERE (hostname LIKE ? OR ip_addr LIKE ?)",
|
||||||
wantSortBy: "last_checked",
|
wantSortBy: "last_checked",
|
||||||
wantSortDir: "DESC",
|
wantSortDirection: "DESC",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
@ -104,8 +97,8 @@ func TestQueryNodes_toSQL(t *testing.T) {
|
||||||
if tt.query.SortBy != tt.wantSortBy {
|
if tt.query.SortBy != tt.wantSortBy {
|
||||||
t.Errorf("QueryNodes.toSQL() gotSortBy = %v, want %v", tt.query.SortBy, tt.wantSortBy)
|
t.Errorf("QueryNodes.toSQL() gotSortBy = %v, want %v", tt.query.SortBy, tt.wantSortBy)
|
||||||
}
|
}
|
||||||
if tt.query.SortDir != tt.wantSortDir {
|
if tt.query.SortDirection != tt.wantSortDirection {
|
||||||
t.Errorf("QueryNodes.toSQL() gotSortDir = %v, want %v", tt.query.SortDir, tt.wantSortDir)
|
t.Errorf("QueryNodes.toSQL() gotSortDirection = %v, want %v", tt.query.SortDirection, tt.wantSortDirection)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -115,19 +108,16 @@ func TestQueryNodes_toSQL(t *testing.T) {
|
||||||
// go test ./internal/monero -bench QueryNodes_toSQL -benchmem -run=^$ -v
|
// go test ./internal/monero -bench QueryNodes_toSQL -benchmem -run=^$ -v
|
||||||
func Benchmark_QueryNodes_toSQL(b *testing.B) {
|
func Benchmark_QueryNodes_toSQL(b *testing.B) {
|
||||||
q := QueryNodes{
|
q := QueryNodes{
|
||||||
Paging: paging.Paging{
|
Host: "test",
|
||||||
Limit: 10,
|
Nettype: "any",
|
||||||
Page: 1,
|
Protocol: "any",
|
||||||
SortBy: "last_checked",
|
CC: "any",
|
||||||
SortDir: "desc",
|
Status: -1,
|
||||||
SortDirection: "desc", // deprecated
|
CORS: -1,
|
||||||
},
|
RowsPerPage: 10,
|
||||||
Host: "test",
|
Page: 1,
|
||||||
Nettype: "any",
|
SortBy: "last_checked",
|
||||||
Protocol: "any",
|
SortDirection: "desc",
|
||||||
CC: "any",
|
|
||||||
Status: -1,
|
|
||||||
CORS: -1,
|
|
||||||
}
|
}
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = q.toSQL()
|
_, _ = q.toSQL()
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
package paging
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/google/go-querystring/query"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Paging struct {
|
|
||||||
Limit int `url:"limit,omitempty"` // rows per page
|
|
||||||
Page int `url:"page"`
|
|
||||||
SortBy string `url:"sort_by,omitempty"`
|
|
||||||
SortDir string `url:"sort_dir,omitempty"`
|
|
||||||
|
|
||||||
SortDirection string `url:"sort_direction,omitempty"` // DEPRECATED: use SortDir
|
|
||||||
|
|
||||||
// Refresh interval
|
|
||||||
Refresh int `url:"refresh,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// a-h templ helpers
|
|
||||||
func EncodedQuery(q interface{}, exclude interface{}) string {
|
|
||||||
arr := reflect.ValueOf(exclude)
|
|
||||||
v, _ := query.Values(q)
|
|
||||||
|
|
||||||
for i := 0; i < arr.Len(); i++ {
|
|
||||||
v.Del(arr.Index(i).String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.Encode()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Pagination struct {
|
|
||||||
CurrentPage int
|
|
||||||
TotalPages int
|
|
||||||
Pages []int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPagination(currentPage, totalPages int) Pagination {
|
|
||||||
var pages []int
|
|
||||||
const maxButtons = 5
|
|
||||||
|
|
||||||
if totalPages <= maxButtons {
|
|
||||||
for i := 1; i <= totalPages; i++ {
|
|
||||||
pages = append(pages, i)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
start := max(1, currentPage-2)
|
|
||||||
end := min(totalPages, currentPage+2)
|
|
||||||
|
|
||||||
if currentPage <= 3 {
|
|
||||||
end = maxButtons
|
|
||||||
} else if currentPage > totalPages-3 {
|
|
||||||
start = totalPages - (maxButtons - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := start; i <= end; i++ {
|
|
||||||
pages = append(pages, i)
|
|
||||||
}
|
|
||||||
if start > 1 {
|
|
||||||
pages = append([]int{1, -1}, pages...) // -1 indicates ellipsis
|
|
||||||
}
|
|
||||||
if end < totalPages {
|
|
||||||
pages = append(pages, -1, totalPages) // -1 indicates ellipsis
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Pagination{
|
|
||||||
CurrentPage: currentPage,
|
|
||||||
TotalPages: totalPages,
|
|
||||||
Pages: pages,
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue