mirror of
https://github.com/ditatompel/xmr-remote-nodes.git
synced 2025-04-09 21:16:28 +07:00
This is useful if we want to expose this service to the Tor network because browsers usually refuse serving images from https protocol to insecure http protocol.
321 lines
9.6 KiB
Svelte
321 lines
9.6 KiB
Svelte
<script>
|
|
import { DataHandler } from '@vincjo/datatables/remote';
|
|
import { format, formatDistance } from 'date-fns';
|
|
import { loadData, loadFees, loadCountries } from './api-handler';
|
|
import { onMount } from 'svelte';
|
|
import {
|
|
DtSrRowsPerPage,
|
|
DtSrThSort,
|
|
DtSrThFilter,
|
|
DtSrRowCount,
|
|
DtSrPagination,
|
|
DtSrAutoRefresh
|
|
} from '$lib/components/datatables/server';
|
|
import {
|
|
HostPortCell,
|
|
NetTypeCell,
|
|
ProtocolCell,
|
|
CountryCellWithAsn,
|
|
StatusCell,
|
|
UptimeCell,
|
|
EstimateFeeCell
|
|
} from '$lib/components/datatables/xmr';
|
|
|
|
export let data;
|
|
let filterNettype = 'any';
|
|
let filterProtocol = 'any';
|
|
let filterCc = 'any';
|
|
let filterStatus = -1;
|
|
let checkboxCors = false;
|
|
|
|
/** @type {{total_nodes: number, cc: string, name: string}[]} */
|
|
let countries = [];
|
|
let fees = [];
|
|
|
|
const handler = new DataHandler([], { rowsPerPage: 10, totalRows: 0 });
|
|
let rows = handler.getRows();
|
|
|
|
/** @type {Object.<string, number>} */
|
|
let majorityFee;
|
|
|
|
onMount(() => {
|
|
loadFees().then((data) => {
|
|
fees = data;
|
|
majorityFee = fees.reduce(
|
|
/**
|
|
* @param {Object.<string, number>} o
|
|
* @param {{ nettype: string, estimate_fee: number }} key
|
|
* @returns {Object.<string, number>}
|
|
*/
|
|
(o, key) => ({
|
|
...o,
|
|
[key.nettype]: key.estimate_fee
|
|
}),
|
|
{}
|
|
);
|
|
handler.onChange((state) => loadData(state));
|
|
handler.invalidate();
|
|
});
|
|
loadCountries().then((data) => {
|
|
countries = data;
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<header id="hero" class="hero-gradient py-7">
|
|
<div class="section-container text-center">
|
|
<h1 class="h1 pb-2 font-extrabold">{data.meta.title}</h1>
|
|
<!-- prettier-ignore -->
|
|
<p class="mx-auto max-w-3xl">
|
|
<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>
|
|
<div class="mx-auto w-full max-w-3xl px-20">
|
|
<hr class="!border-primary-400-500-token !border-t-4 !border-double" />
|
|
</div>
|
|
</header>
|
|
|
|
<!-- prettier-ignore -->
|
|
<section id="introduction ">
|
|
<div class="section-container text-center !max-w-4xl">
|
|
<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 always run your own node</strong> to obtain the maximum possible privacy and to help decentralize the network.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="monero-remote-node">
|
|
<div class="section-container">
|
|
<div class="space-y-2 overflow-x-auto">
|
|
<div class="flex justify-between">
|
|
<DtSrRowsPerPage {handler} />
|
|
<div class="invisible flex place-items-center md:visible">
|
|
<DtSrAutoRefresh {handler} />
|
|
</div>
|
|
<div class="flex place-items-center">
|
|
<button
|
|
id="reloadDt"
|
|
name="reloadDt"
|
|
class="variant-filled-primary btn"
|
|
on:click={() => handler.invalidate()}>Reload</button
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="table table-hover table-compact w-full table-auto">
|
|
<thead>
|
|
<tr>
|
|
<th>Host:Port</th>
|
|
<th>Nettype</th>
|
|
<th>Protocol</th>
|
|
<th>Country</th>
|
|
<th>Status</th>
|
|
<th>Est. Fee</th>
|
|
<DtSrThSort {handler} orderBy="uptime">Uptime</DtSrThSort>
|
|
<DtSrThSort {handler} orderBy="last_checked">Check</DtSrThSort>
|
|
</tr>
|
|
<tr>
|
|
<DtSrThFilter {handler} filterBy="host" placeholder="Filter Host / IP" />
|
|
<th>
|
|
<select
|
|
id="nettype"
|
|
name="nettype"
|
|
class="select variant-form-material"
|
|
bind:value={filterNettype}
|
|
on:change={() => {
|
|
handler.filter(filterNettype, 'nettype');
|
|
handler.invalidate();
|
|
}}
|
|
>
|
|
<option value="any">Any</option>
|
|
<option value="mainnet">MAINNET</option>
|
|
<option value="stagenet">STAGENET</option>
|
|
<option value="testnet">TESTNET</option>
|
|
</select>
|
|
</th>
|
|
<th>
|
|
<select
|
|
id="protocol"
|
|
name="protocol"
|
|
class="select variant-form-material"
|
|
bind:value={filterProtocol}
|
|
on:change={() => {
|
|
handler.filter(filterProtocol, 'protocol');
|
|
handler.invalidate();
|
|
}}
|
|
>
|
|
<option value="any">Any</option>
|
|
<option value="tor">TOR</option>
|
|
<option value="http">HTTP</option>
|
|
<option value="https">HTTPS</option>
|
|
</select>
|
|
</th>
|
|
<th>
|
|
<select
|
|
id="cc"
|
|
name="cc"
|
|
class="select variant-form-material"
|
|
bind:value={filterCc}
|
|
on:change={() => {
|
|
handler.filter(filterCc, 'cc');
|
|
handler.invalidate();
|
|
}}
|
|
>
|
|
<option value="any">Any</option>
|
|
{#each countries as country}
|
|
{#if country.cc === ''}
|
|
<option value="UNKNOWN">UNKNOWN ({country.total_nodes})</option>
|
|
{:else}
|
|
<option value={country.cc}
|
|
>{country.name === '' ? country.cc : country.name} ({country.total_nodes})</option
|
|
>
|
|
{/if}
|
|
{/each}
|
|
</select>
|
|
</th>
|
|
<th colspan="2">
|
|
<select
|
|
id="status"
|
|
name="status"
|
|
class="select variant-form-material"
|
|
bind:value={filterStatus}
|
|
on:change={() => {
|
|
handler.filter(filterStatus, 'status');
|
|
handler.invalidate();
|
|
}}
|
|
>
|
|
<option value={-1}>Any</option>
|
|
<option value="0">Offline</option>
|
|
<option value="1">Online</option>
|
|
</select>
|
|
</th>
|
|
<th colspan="2">
|
|
<label for="cors" class="flex items-center justify-center space-x-2">
|
|
<input
|
|
id="cors"
|
|
name="cors"
|
|
class="checkbox"
|
|
type="checkbox"
|
|
bind:checked={checkboxCors}
|
|
on:change={() => {
|
|
handler.filter(checkboxCors === true ? 1 : -1, 'cors');
|
|
handler.invalidate();
|
|
}}
|
|
/>
|
|
<p>CORS</p>
|
|
</label>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{#each $rows as row (row.id)}
|
|
<tr>
|
|
<td
|
|
><HostPortCell
|
|
ip={row.ip}
|
|
is_tor={row.is_tor}
|
|
hostname={row.hostname}
|
|
port={row.port}
|
|
/>
|
|
<a class="anchor" href="/remote-nodes/logs/?node_id={row.id}">[Logs]</a>
|
|
</td>
|
|
<td><NetTypeCell nettype={row.nettype} height={row.height} /></td>
|
|
<td><ProtocolCell protocol={row.protocol} cors={row.cors} /></td>
|
|
<td
|
|
><CountryCellWithAsn
|
|
cc={row.cc}
|
|
country_name={row.country_name}
|
|
city={row.city}
|
|
asn={row.asn}
|
|
asn_name={row.asn_name}
|
|
/></td
|
|
>
|
|
<td
|
|
><StatusCell
|
|
is_available={row.is_available}
|
|
statuses={row.last_check_statuses}
|
|
/></td
|
|
>
|
|
<td>
|
|
<EstimateFeeCell
|
|
estimate_fee={row.estimate_fee}
|
|
majority_fee={majorityFee[row.nettype]}
|
|
/>
|
|
</td>
|
|
<td><UptimeCell uptime={row.uptime} /></td>
|
|
<td>
|
|
{format(row.last_checked * 1000, 'PP HH:mm')}<br />
|
|
{formatDistance(row.last_checked * 1000, new Date(), { addSuffix: true })}
|
|
</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="flex justify-between mb-2">
|
|
<DtSrRowCount {handler} />
|
|
<DtSrPagination {handler} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="page-info" class="mx-auto w-full max-w-4xl px-4 pb-7">
|
|
<div class="alert card shadow-xl">
|
|
<div class="alert-message">
|
|
<h2 class="h3">Info</h2>
|
|
<ul class="list-inside list-disc">
|
|
<li>
|
|
Uptime percentage calculated is the <strong>last 1 month</strong> uptime.
|
|
</li>
|
|
<li>
|
|
<strong>Est. Fee</strong> here is just fee estimation / byte from
|
|
<code class="code text-rose-900 font-bold">get_fee_estimate</code> RPC call method.
|
|
</li>
|
|
<li>
|
|
Malicious actors who running remote nodes <a
|
|
class="link"
|
|
href="/img/node-tx-fee.jpg"
|
|
rel="noopener">still can return high fee only if you about to create a transactions</a
|
|
>.
|
|
</li>
|
|
<li><strong>The best and safest way is running your own node</strong>!</li>
|
|
<li>
|
|
Nodes with 0% uptime within 1 month with more than 300 check attempt will be removed. You
|
|
can always add your node again latter.
|
|
</li>
|
|
<li>
|
|
You can filter remote node by selecting on <strong>nettype</strong>,
|
|
<strong>protocol</strong>, <strong>country</strong>,
|
|
<strong>tor</strong>, and <strong>online status</strong> option.
|
|
</li>
|
|
<li>
|
|
If you know one or more remote node that we don't currently monitor, please add them using <a
|
|
href="/add-node">this form</a
|
|
>.
|
|
</li>
|
|
<li>
|
|
I deliberately cut the long Tor addresses, click the <span
|
|
class="text-orange-800 dark:text-orange-300">👁 torhostname...</span
|
|
> to see the full Tor address.
|
|
</li>
|
|
<li>
|
|
You can found larger remote nodes database from <a
|
|
class="external"
|
|
href="https://monero.fail/"
|
|
role="button"
|
|
target="_blank"
|
|
rel="noopener">monero.fail</a
|
|
>.
|
|
</li>
|
|
<li>
|
|
If you are developer or power user who like to fetch Monero remote node above in JSON
|
|
format, you can read <a
|
|
class="external"
|
|
href="https://insights.ditatompel.com/en/blog/2022/01/public-api-monero-remote-node-list/"
|
|
>Public API Monero Remote Node List</a
|
|
> blog post for more detailed information.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</section>
|