feat!: Added Add node form and action

This commit is contained in:
Cristian Ditaputratama 2024-11-04 23:53:09 +07:00
parent 0acf12a277
commit a8c94ca0aa
Signed by: ditatompel
GPG key ID: 31D3D06D77950979
8 changed files with 178 additions and 6 deletions

View file

@ -33,6 +33,29 @@ func (s *fiberServer) homeHandler(c *fiber.Ctx) error {
// Render Add Node Page // Render Add Node Page
func (s *fiberServer) addNodeHandler(c *fiber.Ctx) error { func (s *fiberServer) addNodeHandler(c *fiber.Ctx) error {
switch c.Method() {
case fiber.MethodPut:
type formData struct {
Protocol string `form:"protocol"`
Hostname string `form:"hostname"`
Port int `form:"port"`
}
var f formData
if err := c.BodyParser(&f); err != nil {
handler := adaptor.HTTPHandler(templ.Handler(views.Alert("error", "Cannot parse the request body")))
return handler(c)
}
moneroRepo := monero.New()
if err := moneroRepo.Add(f.Protocol, f.Hostname, uint(f.Port)); err != nil {
handler := adaptor.HTTPHandler(templ.Handler(views.Alert("error", err.Error())))
return handler(c)
}
handler := adaptor.HTTPHandler(templ.Handler(views.Alert("success", "Node added successfully")))
return handler(c)
}
p := views.Meta{ p := views.Meta{
Title: "Add Monero Node", Title: "Add Monero Node",
Description: "You can use this page to add known remote node to the system so my bots can monitor it.", Description: "You can use this page to add known remote node to the system so my bots can monitor it.",
@ -256,6 +279,8 @@ func ProbeLogs(c *fiber.Ctx) error {
} }
// Handles `POST /nodes` request to add a new node // Handles `POST /nodes` request to add a new node
//
// Deprecated: AddNode is deprecated, use s.addNodeHandler with put method instead
func AddNode(c *fiber.Ctx) error { func AddNode(c *fiber.Ctx) error {
formPort := c.FormValue("port") formPort := c.FormValue("port")
port, err := strconv.Atoi(formPort) port, err := strconv.Atoi(formPort)

View file

@ -5,13 +5,14 @@ func (s *fiberServer) Routes() {
s.App.Get("/remote-nodes", s.remoteNodesHandler) s.App.Get("/remote-nodes", s.remoteNodesHandler)
s.App.Get("/remote-nodes/id/:id", s.nodeHandler) s.App.Get("/remote-nodes/id/:id", s.nodeHandler)
s.App.Get("/add-node", s.addNodeHandler) s.App.Get("/add-node", s.addNodeHandler)
s.App.Put("/add-node", s.addNodeHandler)
// V1 API routes // V1 API routes
v1 := s.App.Group("/api/v1") v1 := s.App.Group("/api/v1")
// these routes are public, they don't require a prober api key // these routes are public, they don't require a prober api key
v1.Get("/nodes", Nodes) v1.Get("/nodes", Nodes)
v1.Post("/nodes", AddNode) v1.Post("/nodes", AddNode) // old add node form action endpoint. Deprecated: Use PUT /add-node instead
v1.Get("/nodes/id/:id", Node) v1.Get("/nodes/id/:id", Node)
v1.Get("/nodes/logs", ProbeLogs) v1.Get("/nodes/logs", ProbeLogs)
v1.Get("/fees", NetFees) v1.Get("/fees", NetFees)

View file

@ -15,7 +15,61 @@ templ AddNode() {
<div class="mt-5"> <div class="mt-5">
<p class="text-lg text-neutral-300">You can use this page to add known remote node to the system so my bots can monitor it.</p> <p class="text-lg text-neutral-300">You can use this page to add known remote node to the system so my bots can monitor it.</p>
</div> </div>
<hr class="mt-6"/> </div>
<hr class="my-6 border-orange-400 mx-auto max-w-3xl"/>
<div class="max-w-4xl mx-auto px-4">
<div class="p-4 bg-blue-800/10 border border-blue-900 text-sm text-white rounded-lg" role="alert" tabindex="-1" aria-labelledby="add-node-notice">
<div class="flex">
<div class="ms-4">
<h2 id="add-node-notice" class="text-xl font-bold text-center">Important Note</h2>
<div class="mt-2 text-sm">
<ul class="list-disc space-y-1 ps-5">
<li>As an administrator of this instance, I have full rights to delete, and blacklist any submitted node with or without providing any reason.</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<p class="mt-1 text-center">
Enter your Monero node information below (IPv6 host check is experimental):
</p>
<div class="mt-12">
<form method="put" hx-swap="transition:true" hx-target="#form-result" hx-disabled-elt=".form" hx-on::after-request="this.reset()">
<div class="grid grid-cols-1 sm:grid-cols-4 gap-6">
<div>
<label for="protocol" class="block text-neutral-200">Protocol *</label>
<select
id="protocol"
name="protocol"
class="frameless form"
autocomplete="off"
>
<option value="http">HTTP</option>
<option value="https">HTTPS</option>
</select>
</div>
<div class="md:col-span-2">
<label for="hostname" class="block text-neutral-200">Host / IP *</label>
<input type="text" name="hostname" id="hostname" class="frameless form" autocomplete="off" placeholder="Eg: node.example.com or 172.16.17.18" required/>
</div>
<div>
<label for="port" class="block text-neutral-200">Port *</label>
<input type="text" name="port" id="port" class="frameless form" autocomplete="off" placeholder="Eg: 18081" required/>
</div>
</div>
<div class="mt-6 grid">
<button type="submit" class="form w-full py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-bold rounded-lg border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none disabled:opacity-60 disabled:pointer-events-none">Submit</button>
</div>
</form>
<div id="form-result" class="max-w-4xl mx-auto my-6"></div>
<div class="mt-3 text-center">
<p class="text-sm text-gray-500 dark:text-neutral-500">
Existing remote nodes can be found in <a href="/remote-nodes">/remote-nodes</a> page.
</p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -37,7 +37,7 @@ func AddNode() 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("<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\">Add Monero Node</h1></div><!-- End Title --><div class=\"mt-5\"><p class=\"text-lg text-neutral-300\">You can use this page to add known remote node to the system so my bots can monitor it.</p></div><hr class=\"mt-6\"></div></div></div></section><!-- End Hero -->") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<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\">Add Monero Node</h1></div><!-- End Title --><div class=\"mt-5\"><p class=\"text-lg text-neutral-300\">You can use this page to add known remote node to the system so my bots can monitor it.</p></div></div><hr class=\"my-6 border-orange-400 mx-auto max-w-3xl\"><div class=\"max-w-4xl mx-auto px-4\"><div class=\"p-4 bg-blue-800/10 border border-blue-900 text-sm text-white rounded-lg\" role=\"alert\" tabindex=\"-1\" aria-labelledby=\"add-node-notice\"><div class=\"flex\"><div class=\"ms-4\"><h2 id=\"add-node-notice\" class=\"text-xl font-bold text-center\">Important Note</h2><div class=\"mt-2 text-sm\"><ul class=\"list-disc space-y-1 ps-5\"><li>As an administrator of this instance, I have full rights to delete, and blacklist any submitted node with or without providing any reason.</li></ul></div></div></div></div></div><div class=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-6\"><p class=\"mt-1 text-center\">Enter your Monero node information below (IPv6 host check is experimental):</p><div class=\"mt-12\"><form method=\"put\" hx-swap=\"transition:true\" hx-target=\"#form-result\" hx-disabled-elt=\".form\" hx-on::after-request=\"this.reset()\"><div class=\"grid grid-cols-1 sm:grid-cols-4 gap-6\"><div><label for=\"protocol\" class=\"block text-neutral-200\">Protocol *</label> <select id=\"protocol\" name=\"protocol\" class=\"frameless form\" autocomplete=\"off\"><option value=\"http\">HTTP</option> <option value=\"https\">HTTPS</option></select></div><div class=\"md:col-span-2\"><label for=\"hostname\" class=\"block text-neutral-200\">Host / IP *</label> <input type=\"text\" name=\"hostname\" id=\"hostname\" class=\"frameless form\" autocomplete=\"off\" placeholder=\"Eg: node.example.com or 172.16.17.18\" required></div><div><label for=\"port\" class=\"block text-neutral-200\">Port *</label> <input type=\"text\" name=\"port\" id=\"port\" class=\"frameless form\" autocomplete=\"off\" placeholder=\"Eg: 18081\" required></div></div><div class=\"mt-6 grid\"><button type=\"submit\" class=\"form w-full py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-bold rounded-lg border border-transparent bg-orange-600 text-white hover:bg-orange-500 focus:outline-none disabled:opacity-60 disabled:pointer-events-none\">Submit</button></div></form><div id=\"form-result\" class=\"max-w-4xl mx-auto my-6\"></div><div class=\"mt-3 text-center\"><p class=\"text-sm text-gray-500 dark:text-neutral-500\">Existing remote nodes can be found in <a href=\"/remote-nodes\">/remote-nodes</a> page.</p></div></div></div></div></div></section><!-- End Hero -->")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

View file

@ -61,7 +61,7 @@ templ Home() {
</div> </div>
</div> </div>
</a> </a>
<a href="/remote-nodes" class="group flex flex-col text-center bg-neutral-900 border border-orange-400 shadow-sm rounded-xl transition hover:shadow-md hover:brightness-125"> <a href="/add-node" class="group flex flex-col text-center bg-neutral-900 border border-orange-400 shadow-sm rounded-xl transition hover:shadow-md hover:brightness-125">
<div class="p-4 md:p-5"> <div class="p-4 md:p-5">
<div class="grow"> <div class="grow">
<p class="font-semibold text-orange-400">Add Node</p> <p class="font-semibold text-orange-400">Add Node</p>

File diff suppressed because one or more lines are too long

View file

@ -96,3 +96,14 @@ templ heroGradient() {
<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 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> </div>
} }
templ Alert(status, message string) {
switch status {
case "success":
<div class="mt-2 bg-green-600 text-white rounded-lg p-4"><strong>Success:</strong> { message }</div>
case "error":
<div class="mt-2 bg-red-600 text-white rounded-lg p-4"><strong>Error:</strong> { message }</div>
default:
<div class="mt-2 bg-blue-600 text-white rounded-lg p-4">{ message }</div>
}
}

View file

@ -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><div id=\"modal-section\" class=\"hs-overlay hidden size-full fixed top-0 start-0 z-[80] overflow-x-hidden overflow-y-auto pointer-events-none\" role=\"dialog\" tabindex=\"-1\" aria-labelledby=\"modal-section-label\"></div></html>") _, 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><div id=\"modal-section\" class=\"hs-overlay hidden size-full fixed top-0 start-0 z-[80] overflow-x-hidden overflow-y-auto pointer-events-none\" role=\"dialog\" tabindex=\"-1\" aria-labelledby=\"modal-section-label\"></div></body></html>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -394,4 +394,85 @@ func heroGradient() templ.Component {
}) })
} }
func Alert(status, message string) 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_Var21 := templ.GetChildren(ctx)
if templ_7745c5c3_Var21 == nil {
templ_7745c5c3_Var21 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
switch status {
case "success":
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"mt-2 bg-green-600 text-white rounded-lg p-4\"><strong>Success:</strong> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(message)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 103, Col: 95}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
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
}
case "error":
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"mt-2 bg-red-600 text-white rounded-lg p-4\"><strong>Error:</strong> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var23 string
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(message)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 105, Col: 91}
}
_, 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("</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
default:
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"mt-2 bg-blue-600 text-white rounded-lg p-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var24 string
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(message)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/handler/views/layout.templ`, Line: 107, Col: 68}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
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
})
}
var _ = templruntime.GeneratedTemplate var _ = templruntime.GeneratedTemplate