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" "github.com/ditatompel/xmr-remote-nodes/utils" "strings" "time" ) var nettypes = []string{"mainnet", "stagenet", "testnet"} var protocols = []string{"tor", "http", "https"} type nodeStatus struct { Code int Text string } var nodeStatuses = []nodeStatus{ {-1, "ANY"}, {1, "Online"}, {0, "Offline"}, } templ RemoteNodes(data monero.Nodes, countries []monero.Countries, q monero.QueryNodes, p paging.Pagination) {
@heroGradient()

Public Monero Remote Nodes List

Monero remote node 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.

@TableNodes(data, countries, q, 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.

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 Monero community suggests to always run and use your own node to obtain the maximum possible privacy and to help decentralize the network.

} templ TableNodes(data monero.Nodes, countries []monero.Countries, q monero.QueryNodes, p paging.Pagination) {
@DtRowPerPage("/remote-nodes", "#tbl_nodes", q.Limit, q)
@DtRefreshInterval("/remote-nodes", "#tbl_nodes", q.Refresh, q)
@DtReload("/remote-nodes", "#tbl_nodes", q)
@DtThSort("/remote-nodes", "#tbl_nodes", "Uptime", "uptime", q.SortBy, q.SortDirection, q) @DtThSort("/remote-nodes", "#tbl_nodes", "Check", "last_checked", q.SortBy, q.SortDirection, q) for _, row := range data.Items { }
Host:Port Nettype Protocol Country Status Estimate Fee
@cellHostPort(row.ID, row.Port, row.Hostname, row.IPAddresses, row.IsTor, row.IPv6Only) @cellNettype(row.Nettype, row.Height) @cellProtocol(row.Protocol, row.CORSCapable) @cellCountry(row.CountryCode, row.CountryName, row.City, row.ASNName, row.ASN) @cellStatuses(row.IsAvailable, monero.ParseNodeStatuses(row.LastCheckStatus)) { fmt.Sprintf("%d", row.EstimateFee) } @cellUptime(row.Uptime)
[Logs]
{ utils.TimeSince(row.LastChecked) }
@DtRowCount(p.CurrentPage, data.RowsPerPage, data.TotalRows) @DtPagination("/remote-nodes", "#tbl_nodes", q, p)
} templ Node(data monero.Node) {
Host:
  • { fmt.Sprintf("%s:%d", data.Hostname, data.Port) }
Protocol:
  • { data.Protocol }
if data.Nettype != "" {
Net Type:
  • { data.Nettype }
} if data.IPAddresses != "" {
IP Addresses:
  • { strings.ReplaceAll(data.IPAddresses, ",", ", ") }
}
} templ NodeDetails(data monero.Node, logs monero.FetchLogs, q monero.QueryLogs, p paging.Pagination) {
@heroGradient()

Monero Node #{ fmt.Sprintf("%d", data.ID) }


@Node(data)

Probe Logs

@TableLogs(fmt.Sprintf("/remote-nodes/id/%d", data.ID), logs, q, p)
} templ TableLogs(hxPath string, data monero.FetchLogs, q monero.QueryLogs, p paging.Pagination) {
@DtRowPerPage(hxPath, "#tbl_logs", q.Limit, q)
@DtRefreshInterval(hxPath, "#tbl_logs", q.Refresh, q)
@DtReload(hxPath, "#tbl_logs", q)
@DtThSort(hxPath, "#tbl_logs", "Est. Fee", "estimate_fee", q.SortBy, q.SortDirection, q) @DtThSort(hxPath, "#tbl_logs", "Check", "date_checked", q.SortBy, q.SortDirection, q) @DtThSort(hxPath, "#tbl_logs", "Runtime", "fetch_runtime", q.SortBy, q.SortDirection, q) for _, row := range data.Items { if row.Status == 1 { } else { } }
#ID Prober ID Status Height Adjusted Time DB Size Difficulty
{ fmt.Sprintf("%d", row.ID) } { fmt.Sprintf("%d", row.ProberID) }OK { fmt.Sprintf("%d", row.Height) } { time.Unix(row.AdjustedTime, 0).UTC().Format("Jan 2, 2006 15:04 MST") } { utils.FormatBytes(row.DatabaseSize, 0) } { utils.FormatHashes(float64(row.Difficulty)) } { fmt.Sprintf("%d", row.EstimateFee) }ERR { row.FailedReason }{ utils.TimeSince(row.DateChecked) } { utils.FormatFloat(row.FetchRuntime) }s
@DtRowCount(p.CurrentPage, data.RowsPerPage, data.TotalRows) @DtPagination(hxPath, "#tbl_logs", q, p)
} templ cellHostPort(id, port uint, hostname, ips string, isTor, ipv6Only bool) { if isTor {
.onion:{ fmt.Sprintf("%d", port) } (TOR) } else { { ip.FormatHostname(hostname) }:{ fmt.Sprintf("%d", port) }
{ strings.ReplaceAll(ips, ",", " ") } if ipv6Only { (IPv6 only) }
} } templ cellNettype(nettype string, height uint) { switch nettype { case "stagenet": { nettype } case "testnet": { nettype } default: { nettype } }
{ fmt.Sprintf("%d", height) } } templ cellProtocol(protocol string, cors bool) { switch protocol { case "http": { protocol } default: { protocol } } if cors {
(CORS 💪) } } templ cellCountry(cc, countryName, city, asnName string, asn uint) { if cc != "" { if city != "" { { city }, } { countryName } { } if asn != 0 {
{ fmt.Sprintf("AS%d", asn) } ({ asnName }) } } templ cellStatuses(isAvailable bool, statuses [5]int) { if isAvailable { Online } else { Offline }
for _, status := range statuses { if status == 1 { } else if status == 0 { } else { } } } templ cellUptime(uptime float64) { if uptime >= 98 { { utils.FormatFloat(uptime) }% } else if uptime < 98 && uptime >= 80 { { utils.FormatFloat(uptime) }% } else if uptime < 80 && uptime > 75 { { utils.FormatFloat(uptime) }% } else { { utils.FormatFloat(uptime) }% } }