mirror of
https://github.com/ditatompel/xmr-remote-nodes.git
synced 2024-11-17 09:27:37 +00:00
198 lines
7 KiB
Text
198 lines
7 KiB
Text
package views
|
|
|
|
import (
|
|
"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 -->
|
|
<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">
|
|
@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>
|
|
@cellProtocol(row.Protocol, row.CORSCapable)
|
|
</td>
|
|
<td>
|
|
@cellCountry(row.CountryCode, row.CountryName, row.City, row.ASNName, row.ASN)
|
|
</td>
|
|
<td>
|
|
@cellStatuses(row.IsAvailable, monero.ParseNodeStatuses(row.LastCheckStatus))
|
|
</td>
|
|
<td>{ fmt.Sprintf("%d", row.EstimateFee) }</td>
|
|
<td class="text-right">
|
|
@cellUptime(row.Uptime)
|
|
</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) }
|
|
}
|
|
|
|
templ cellProtocol(protocol string, cors bool) {
|
|
switch protocol {
|
|
case "http":
|
|
<span class="font-semibold uppercase text-sky-500">{ protocol }</span>
|
|
default:
|
|
<span class="font-semibold uppercase text-green-500">{ protocol }</span>
|
|
}
|
|
if cors {
|
|
<br/>
|
|
(CORS 💪)
|
|
}
|
|
}
|
|
|
|
templ cellCountry(cc, countryName, city, asnName string, asn uint) {
|
|
if cc != "" {
|
|
if city != "" {
|
|
{ city },
|
|
}
|
|
{ countryName }
|
|
<img class="inline-block" src={ fmt.Sprintf("/assets/img/cf/%s.svg", strings.ToLower(cc)) } alt={ fmt.Sprintf("%s Flag", cc) } width="22px"/>
|
|
}
|
|
if asn != 0 {
|
|
<br/>
|
|
<a
|
|
class="external font-semibold underline !text-purple-400"
|
|
href={ templ.URL(fmt.Sprintf("https://www.ditatompel.com/asn/%d", asn)) }
|
|
target="_blank"
|
|
rel="noopener"
|
|
>{ fmt.Sprintf("AS%d", asn) }</a>
|
|
(<span class="font-semibold text-green-500">{ asnName }</span>)
|
|
}
|
|
}
|
|
|
|
templ cellStatuses(isAvailable bool, statuses [5]int) {
|
|
if isAvailable {
|
|
<span class="font-semibold text-green-500">Online</span>
|
|
} else {
|
|
<span class="text-rose-400">Offline</span>
|
|
}
|
|
<br/>
|
|
for _, status := range statuses {
|
|
if status == 1 {
|
|
<span class="text-green-400 mr-1">•</span>
|
|
} else if status == 0 {
|
|
<span class="text-red-400 mr-1">•</span>
|
|
} else {
|
|
<span class="text-neutral-600 mr-1">•</span>
|
|
}
|
|
}
|
|
}
|
|
|
|
templ cellUptime(uptime float64) {
|
|
if uptime >= 98 {
|
|
<span class="text-green-500">{ formatFloat(uptime) }%</span>
|
|
} else if uptime < 98 && uptime >= 80 {
|
|
<span class="text-sky-500">{ formatFloat(uptime) }%</span>
|
|
} else if uptime < 80 && uptime > 75 {
|
|
<span class="text-orange-500">{ formatFloat(uptime) }%</span>
|
|
} else {
|
|
<span class="text-rose-500">{ formatFloat(uptime) }%</span>
|
|
}
|
|
}
|