starting frontend revamp for fun w/ new geoip on nodes

revamp-ui
lza_menace 2 years ago
parent 2a6d0a8e9c
commit 8e5f35b0d3

@ -30,4 +30,6 @@ Werkzeug==2.2.3
WTForms==3.0.1
yarl==1.8.2
zipp==3.15.0
dnspython==1.15.0
ipwhois==1.2.0
matplotlib==3.7.1
numpy==1.24.3

@ -8,7 +8,7 @@ import requests
from flask import Blueprint
from urllib.parse import urlparse
from xmrnodes.helpers import determine_crypto, is_onion, is_i2p, make_request
from xmrnodes.helpers import determine_crypto, is_onion, is_i2p, make_request, get_whois
from xmrnodes.helpers import retrieve_peers, rw_cache, get_highest_block, get_geoip
from xmrnodes.models import Node, HealthCheck, Peer
from xmrnodes import config
@ -23,7 +23,7 @@ def init():
@bp.cli.command("check")
def check_nodes():
diff = datetime.utcnow() - timedelta(hours=72)
diff = datetime.utcnow() - timedelta(hours=96)
checks = HealthCheck.select().where(HealthCheck.datetime <= diff)
for check in checks:
print("Deleting check", check.id)
@ -194,6 +194,13 @@ def validate():
node.lat = geoip.location.latitude
node.lon = geoip.location.longitude
logging.info(f"found geo data for {node.url} - {node.country_code}, {node.country_name}, {node.city}")
whois = get_whois(node.url)
if whois:
logging.info(f"found whois data for {node.url}")
node.asn = whois['asn']
node.asn_cidr = whois['asn_cidr']
node.asn_country_code = whois['asn_country_code']
node.asn_description = whois['asn_description']
node.save()
logging.info("success")
else:

@ -4,6 +4,7 @@ import pickle
from os import path
import geoip2.database
from ipwhois import ipwhois
from requests import get as r_get
from urllib.parse import urlparse
from levin.bucket import Bucket
@ -159,18 +160,21 @@ def get_highest_block(nettype, crypto):
else:
return 0
def get_geoip(ip_or_dns):
host = urlparse(ip_or_dns).netloc.split(':')[0]
def get_host(node_url):
host = urlparse(node_url).netloc.split(':')[0]
resolved = socket.gethostbyname(host)
host = host if resolved == host else resolved
return host if resolved == host else resolved
def get_geoip(node_url):
host = get_host(node_url)
with geoip2.database.Reader("./data/GeoLite2-City.mmdb") as reader:
return reader.city(host)
def get_whois(ip_or_dns):
pass
# asn
# asn_cidr
# asn_country_code
# asn_description
def get_whois(node_url):
host = get_host(node_url)
try:
who = ipwhois.IPWhois(host).lookup_rdap()
return who
except:
return None

@ -1,4 +1,5 @@
from urllib.parse import urlparse
from statistics import mean
from datetime import datetime
from peewee import *
@ -28,6 +29,10 @@ class Node(Model):
postal = IntegerField(null=True)
lat = FloatField(null=True)
lon = FloatField(null=True)
asn = CharField(null=True)
asn_cidr = CharField(null=True)
asn_country_code = CharField(null=True)
asn_description = CharField(null=True)
datetime_entered = DateTimeField(default=datetime.utcnow)
datetime_checked = DateTimeField(default=None, null=True)
datetime_failed = DateTimeField(default=None, null=True)
@ -47,6 +52,16 @@ class Node(Model):
hcs = HealthCheck.select().where(HealthCheck.node == self)
return hcs
@property
def uptime(self):
hcs = self.healthchecks
ups = [1 for i in hcs if i.health == True]
downs = [0 for i in hcs if i.health == False]
if (len(ups + downs)):
m = mean(ups + downs)
return int(m * 100)
return 0
class Meta:
database = db
@ -79,6 +94,10 @@ class HealthCheck(Model):
datetime = DateTimeField(default=datetime.utcnow)
health = BooleanField()
@property
def color(self):
return 'green' if self.health else 'red'
class Meta:
database = db

@ -1,9 +1,15 @@
import re
from io import BytesIO
from random import shuffle
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from urllib.parse import urlparse
from flask import request, redirect, Blueprint
from flask import render_template, flash, Response
from urllib.parse import urlparse
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from xmrnodes.helpers import rw_cache, get_highest_block
from xmrnodes.forms import SubmitNode
@ -61,6 +67,33 @@ def index():
)
@bp.route('/plot/<int:node_id>.png')
def plot_health(node_id):
node = Node.get(node_id).healthchecks
if not node:
return None
df = pd.DataFrame(node.dicts())
df['health'] = df['health'].astype(int)
fig = Figure(figsize=(3,3), tight_layout=True)
output = BytesIO()
axis = fig.add_subplot()
dff = df.groupby(['health']).agg({'health': 'count'})
if 0 not in dff.index:
dff.loc[0] = 0
print(dff.index)
dff = dff.sort_index() # sorting by index
axis.pie(
dff['health'],
colors=['#B76D68', '#78BC61'],
radius=8,
wedgeprops={"linewidth": 2, "edgecolor": "white"},
frame=True
)
axis.axis('off')
FigureCanvas(fig).print_png(output)
return Response(output.getvalue(), mimetype='image/png')
@bp.route("/map")
def map():
try:

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

@ -0,0 +1,271 @@
/*!*****************************************************
Freak Flags, Copyright ©2023 Michael P. Cohen. Freak flags is licenced under the MIT licence.
For complete information visit: www.freakflagsprite.com
******************************************************/
.fflag {
background-image:url(flagSprite42.png);
background-repeat:no-repeat;
background-size: 100% 49494%;
display: inline-block;
overflow: hidden;
position: relative;
vertical-align: middle;
box-sizing: content-box;
}
.fflag-CH,
.fflag-NP {box-shadow: none!important}
.fflag-DZ {background-position:center 0.2287%}
.fflag-AO {background-position:center 0.4524%}
.fflag-BJ {background-position:center 0.6721%}
.fflag-BW {background-position:center 0.8958%}
.fflag-BF {background-position:center 1.1162%}
.fflag-BI {background-position:center 1.3379%}
.fflag-CM {background-position:center 1.5589%}
.fflag-CV {background-position:center 1.7805%}
.fflag-CF {background-position:center 2.0047%}
.fflag-TD {background-position:center 2.2247%}
.fflag-CD {background-position:left 2.4467%}
.fflag-DJ {background-position:left 2.6674%}
.fflag-EG {background-position:center 2.8931%}
.fflag-GQ {background-position:center 3.1125%}
.fflag-ER {background-position:left 3.3325%}
.fflag-ET {background-position:center 3.5542%}
.fflag-GA {background-position:center 3.7759%}
.fflag-GM {background-position:center 4.0015%}
.fflag-GH {background-position:center 4.2229%}
.fflag-GN {background-position:center 4.441%}
.fflag-GW {background-position:left 4.66663%}
.fflag-CI {background-position:center 4.8844%}
.fflag-KE {background-position:center 5.1061%}
.fflag-LS {background-position:center 5.3298%}
.fflag-LR {background-position:left 5.5495%}
.fflag-LY {background-position:center 5.7712%}
.fflag-MG {background-position:center 5.994%}
.fflag-MW {background-position:center 6.2156%}
.fflag-ML {background-position:center 6.4363%}
.fflag-MR {background-position:center 6.658%}
.fflag-MU {background-position:center 6.8805%}
.fflag-YT {background-position:center 7.1038%}
.fflag-MA {background-position:center 7.3231%}
.fflag-MZ {background-position:left 7.5448%}
.fflag-NA {background-position:left 7.7661%}
.fflag-NE {background-position:center 7.98937%}
.fflag-NG {background-position:center 8.2099%}
.fflag-CG {background-position:center 8.4316%}
.fflag-RE {background-position:center 8.6533%}
.fflag-RW {background-position:right 8.875%}
.fflag-SH {background-position:center 9.0967%}
.fflag-ST {background-position:center 9.32237%}
.fflag-SN {background-position:center 9.5426%}
.fflag-SC {background-position:left 9.7628%}
.fflag-SL {background-position:center 9.9845%}
.fflag-SO {background-position:center 10.2052%}
.fflag-ZA {background-position:left 10.4269%}
.fflag-SS {background-position:left 10.6486%}
.fflag-SD {background-position:center 10.8703%}
.fflag-SR {background-position:center 11.0945%}
.fflag-SZ {background-position:center 11.3135%}
.fflag-TG {background-position:left 11.5354%}
.fflag-TN {background-position:center 11.7593%}
.fflag-UG {background-position:center 11.9799%}
.fflag-TZ {background-position:center 12.2005%}
.fflag-EH {background-position:center 12.4222%}
.fflag-YE {background-position:center 12.644%}
.fflag-ZM {background-position:center 12.8664%}
.fflag-ZW {background-position:left 13.0873%}
.fflag-AI {background-position:center 13.309%}
.fflag-AG {background-position:center 13.5307%}
.fflag-AR {background-position:center 13.7524%}
.fflag-AW {background-position:left 13.9741%}
.fflag-BS {background-position:left 14.1958%}
.fflag-BB {background-position:center 14.4175%}
.fflag-BQ {background-position:center 14.6415%}
.fflag-BZ {background-position:center 14.8609%}
.fflag-BM {background-position:center 15.0826%}
.fflag-BO {background-position:center 15.306%}
.fflag-VG {background-position:center 15.528%}
.fflag-BR {background-position:center 15.7496%}
.fflag-CA {background-position:center 15.9694%}
.fflag-KY {background-position:center 16.1911%}
.fflag-CL {background-position:left 16.4128%}
.fflag-CO {background-position:left 16.6345%}
.fflag-KM {background-position:center 16.8562%}
.fflag-CR {background-position:center 17.0779%}
.fflag-CU {background-position:left 17.2996%}
.fflag-CW {background-position:center 17.5213%}
.fflag-DM {background-position:center 17.743%}
.fflag-DO {background-position:center 17.968%}
.fflag-EC {background-position:center 18.1864%}
.fflag-SV {background-position:center 18.4081%}
.fflag-FK {background-position:center 18.6298%}
.fflag-GF {background-position:center 18.8515%}
.fflag-GL {background-position:left 19.0732%}
.fflag-GD {background-position:center 19.2987%}
.fflag-GP {background-position:center 19.518%}
.fflag-GT {background-position:center 19.7383%}
.fflag-GY {background-position:center 19.96%}
.fflag-HT {background-position:center 20.1817%}
.fflag-HN {background-position:center 20.4034%}
.fflag-JM {background-position:center 20.6241%}
.fflag-MQ {background-position:center 20.8468%}
.fflag-MX {background-position:center 21.0685%}
.fflag-MS {background-position:center 21.2902%}
.fflag-NI {background-position:center 21.5119%}
.fflag-PA {background-position:center 21.7336%}
.fflag-PY {background-position:center 21.9553%}
.fflag-PE {background-position:center 22.177%}
.fflag-PR {background-position:left 22.4002%}
.fflag-BL {background-position:center 22.6204%}
.fflag-KN {background-position:center 22.8421%}
.fflag-LC {background-position:center 23.0638%}
.fflag-PM {background-position:center 23.2855%}
.fflag-VC {background-position:center 23.5072%}
.fflag-SX {background-position:left 23.732%}
.fflag-TT {background-position:center 23.9506%}
.fflag-TC {background-position:center 24.1723%}
.fflag-US {background-position:center 24.392%}
.fflag-VI {background-position:center 24.6157%}
.fflag-UY {background-position:left 24.8374%}
.fflag-VE {background-position:center 25.0591%}
.fflag-AB {background-position:center 25.279%}
.fflag-AF {background-position:center 25.5025%}
.fflag-AZ {background-position:center 25.7242%}
.fflag-BD {background-position:center 25.9459%}
.fflag-BT {background-position:center 26.1676%}
.fflag-BN {background-position:center 26.3885%}
.fflag-KH {background-position:center 26.611%}
.fflag-CN {background-position:left 26.8327%}
.fflag-GE {background-position:center 27.0544%}
.fflag-HK {background-position:center 27.2761%}
.fflag-IN {background-position:center 27.4978%}
.fflag-ID {background-position:center 27.7195%}
.fflag-JP {background-position:center 27.9412%}
.fflag-KZ {background-position:center 28.1615%}
.fflag-LA {background-position:center 28.3846%}
.fflag-MO {background-position:center 28.6063%}
.fflag-MY {background-position:center 28.829%}
.fflag-MV {background-position:center 29.0497%}
.fflag-MN {background-position:left 29.2714%}
.fflag-MM {background-position:center 29.4931%}
.fflag-NP {background-position:left 29.7148%}
.fflag-KP {background-position:left 29.9365%}
.fflag-MP {background-position:center 30.1582%}
.fflag-PW {background-position:center 30.3799%}
.fflag-PG {background-position:center 30.6016%}
.fflag-PH {background-position:left 30.8233%}
.fflag-SG {background-position:left 31.045%}
.fflag-KR {background-position:center 31.2667%}
.fflag-LK {background-position:right 31.4884%}
.fflag-TW {background-position:left 31.7101%}
.fflag-TJ {background-position:center 31.9318%}
.fflag-TH {background-position:center 32.1535%}
.fflag-TL {background-position:left 32.3752%}
.fflag-TM {background-position:center 32.5969%}
.fflag-VN {background-position:center 32.8186%}
.fflag-AX {background-position:center 33.0403%}
.fflag-AL {background-position:center 33.25975%}
.fflag-AD {background-position:center 33.4837%}
.fflag-AM {background-position:center 33.7054%}
.fflag-AT {background-position:center 33.9271%}
.fflag-BY {background-position:left 34.1488%}
.fflag-BE {background-position:center 34.3705%}
.fflag-BA {background-position:center 34.5922%}
.fflag-BG {background-position:center 34.8139%}
.fflag-HR {background-position:center 35.0356%}
.fflag-CY {background-position:center 35.2555%}
.fflag-CZ {background-position:left 35.479%}
.fflag-DK {background-position:center 35.7007%}
.fflag-EE {background-position:center 35.9224%}
.fflag-FO {background-position:center 36.1441%}
.fflag-FI {background-position:center 36.3658%}
.fflag-FR {background-position:center 36.5875%}
.fflag-DE {background-position:center 36.8092%}
.fflag-GI {background-position:center 37.0309%}
.fflag-GR {background-position:left 37.2526%}
.fflag-GG {background-position:center 37.4743%}
.fflag-HU {background-position:center 37.696%}
.fflag-IS {background-position:center 37.9177%}
.fflag-IE {background-position:center 38.1394%}
.fflag-IM {background-position:center 38.3611%}
.fflag-IT {background-position:center 38.5828%}
.fflag-JE {background-position:center 38.8045%}
.fflag-XK {background-position:center 39.0262%}
.fflag-LV {background-position:center 39.2479%}
.fflag-LI {background-position:left 39.4696%}
.fflag-LT {background-position:center 39.6913%}
.fflag-LU {background-position:center 39.913%}
.fflag-MT {background-position:left 40.1347%}
.fflag-MD {background-position:center 40.3564%}
.fflag-MC {background-position:center 40.5781%}
.fflag-ME {background-position:center 40.7998%}
.fflag-NL {background-position:center 41.0215%}
.fflag-MK {background-position:center 41.2432%}
.fflag-NO {background-position:center 41.4649%}
.fflag-PL {background-position:center 41.6866%}
.fflag-PT {background-position:center 41.9083%}
.fflag-RO {background-position:center 42.13%}
.fflag-RU {background-position:center 42.3517%}
.fflag-SM {background-position:center 42.5734%}
.fflag-RS {background-position:center 42.7951%}
.fflag-SK {background-position:center 43.0168%}
.fflag-SI {background-position:center 43.2385%}
.fflag-ES {background-position:left 43.4602%}
.fflag-SE {background-position:center 43.6819%}
.fflag-CH {background-position:center 43.9036%}
.fflag-TR {background-position:center 44.1253%}
.fflag-UA {background-position:center 44.347%}
.fflag-GB {background-position:center 44.5687%}
.fflag-VA {background-position:right 44.7904%}
.fflag-BH {background-position:center 45.0121%}
.fflag-IR {background-position:center 45.2338%}
.fflag-IQ {background-position:center 45.4555%}
.fflag-IL {background-position:center 45.6772%}
.fflag-KW {background-position:left 45.897%}
.fflag-JO {background-position:left 46.1206%}
.fflag-KG {background-position:center 46.3423%}
.fflag-LB {background-position:center 46.561%}
.fflag-OM {background-position:left 46.7857%}
.fflag-PK {background-position:center 47.0074%}
.fflag-PS {background-position:center 47.2291%}
.fflag-QA {background-position:center 47.4508%}
.fflag-SA {background-position:center 47.6725%}
.fflag-SY {background-position:center 47.8942%}
.fflag-AE {background-position:center 48.1159%}
.fflag-UZ {background-position:left 48.3376%}
.fflag-AS {background-position:right 48.5593%}
.fflag-AU {background-position:center 48.781%}
.fflag-CX {background-position:center 49.002%}
.fflag-CC {background-position:center 49.2244%}
.fflag-CK {background-position:center 49.4445%}
.fflag-FJ {background-position:center 49.6678%}
.fflag-PF {background-position:center 49.8895%}
.fflag-GU {background-position:center 50.1112%}
.fflag-KI {background-position:center 50.3329%}
.fflag-MH {background-position:left 50.5546%}
.fflag-FM {background-position:center 50.7763%}
.fflag-NC {background-position:center 50.998%}
.fflag-NZ {background-position:center 51.2197%}
.fflag-NR {background-position:left 51.4414%}
.fflag-NU {background-position:center 51.6631%}
.fflag-NF {background-position:center 51.8848%}
.fflag-WS {background-position:left 52.1065%}
.fflag-SB {background-position:left 52.3282%}
.fflag-TK {background-position:center 52.5499%}
.fflag-TO {background-position:left 52.7716%}
.fflag-TV {background-position:center 52.9933%}
.fflag-VU {background-position:left 53.215%}
.fflag-WF {background-position:center 53.4385%}
.fflag-AQ {background-position:center 53.6584%}
.fflag-EU {background-position:center 53.875%}
.fflag-JR {background-position:center 54.099%}
.fflag-OLY {background-position:center 54.32%}
.fflag-UN {background-position:center 54.54%}
.fflag.ff-sm {width: 18px;height: 11px}
.fflag.ff-md {width: 27px;height: 17px}
.fflag.ff-lg {width: 42px;height: 27px}
.fflag.ff-xl {width: 60px;height: 37px}

@ -0,0 +1,82 @@
body {
background-color: #1B2432;
color: #f9f9f9;
font-family: monospace;
font-size: 14px;
}
.media-content .title {
color: #1B2432;
}
.title {
color: #f9f9f9;
}
.card {
background-color: #F1F2EB;
}
.card .media:not(:last-child) {
margin-bottom: 0;
}
.card-content {
padding: 1em;
margin: 1em;
}
.smol {
font-size: .75em;
}
.donationAddress {
font-size: .8em;
user-select: all;
color: #ff006e;
display: block;
padding-top: .35em;
word-wrap: break-word;
}
.asn {
font-size: .8em;
color: #ff006e;
display: block;
word-wrap: break-word;
}
.nodeURL {
user-select: all;
font-size: 1em;
}
.messages {
width: 30em;
padding: 1em;
position: absolute;
right: 0;
}
#addNodeForm {
width: 50%;
}
#webCompat {
margin: 1em;
}
.dot {
height: 10px;
width: 10px;
border-radius: 50%;
display: inline-block;
}
.glowing-green {
background-color: #78BC61;
}
.glowing-red {
background-color: #B76D68;
}

@ -134,6 +134,7 @@ input[type="text"] {
.nodeURL {
user-select: all;
font-size: 1em;
}
td img {
@ -149,3 +150,11 @@ td img {
padding-top: .35em;
word-wrap: break-word;
}
.asn {
font-size: .8em;
color: #ff006e;
display: block;
padding-top: .35em;
word-wrap: break-word;
}

File diff suppressed because one or more lines are too long

@ -19,36 +19,69 @@
<meta name="application-name" content="XMR Nodes">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="keywords" content="wownero, monero, xmr, bitmonero, cryptocurrency">
<link href="/static/css/normalize.css" rel="stylesheet">
<link href="/static/css/pure.css" rel="stylesheet">
<link href="/static/css/style.css" rel="stylesheet">
<link href="/static/css/freakflags.css" rel="stylesheet">
<link href="/static/css/bulma.min.css" rel="stylesheet">
<link href="/static/css/main.css" rel="stylesheet">
<title>XMR Nodes</title>
</head>
<body>
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="/">
<img src="/static/images/monero.svg" width="25" height="28">.fail
</a>
<a role="button" class="navbar-burger" data-target="navMenu" aria-label="menu" aria-expanded="false">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navMenu" class="navbar-menu">
<div class="navbar-start">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
Links
</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="{{ url_for('meta.about') }}">
About
</a>
<a class="navbar-item" href="{{ url_for('meta.map') }}">
Map
</a>
<a class="navbar-item" href="mailto:lza_menace@protonmail.com" target="_blank">
Contact
</a>
<a class="navbar-item" href="https://github.com/lalanza808/monero.fail" target="_blank">
Source Code
</a>
<hr class="navbar-divider">
<a class="navbar-item" href="https://moneroinfodump.neocities.org/" target="_blank">
Info Dump
</a>
</div>
</div>
</div>
</div>
</nav>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes pure-u-1 center">
<div class="messages">
{% for message in messages %}
<li>{{ message }}</li>
<article class="message is-info">
<div class="message-body">
{{ message }}
</div>
</article>
{% endfor %}
</ul>
</div>
{% endif %}
{% endwith %}
<div id="" class="center">
<br>
<a href="{{ url_for('meta.about') }}">about</a>
-
<a href="{{ url_for('meta.map') }}">map</a>
-
<a href="https://twitter.com/lza_menace" target="_blank">contact</a>
-
<a href="https://github.com/lalanza808/monero.fail" target="_blank">source</a>
-
<a href="https://moneroinfodump.neocities.org/" target="_blank">infodump</a>
</div>
<!-- Page Content -->
{% block content %} {% endblock %}
@ -58,5 +91,19 @@
<a href="#">Go to top</a>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
$navbarBurgers.forEach( el => {
el.addEventListener('click', () => {
const target = el.dataset.target;
const $target = document.getElementById(target);
el.classList.toggle('is-active');
$target.classList.toggle('is-active');
});
});
});
</script>
</body>
</html>

@ -5,33 +5,31 @@
<div id="index" class="container">
<div id="addnode" class="pure-g center section">
<div class="title pure-u-1">
<h2>Add A Node</h2>
</div>
<form method="POST" action="{{ url_for('meta.add') }}" class="pure-form pure-u-1">
<div id="addnode" class="section">
<h2 class="title is-4">Add A Node</h2>
<form method="POST" action="{{ url_for('meta.add') }}" id="addNodeForm">
{{ form.csrf_token }}
{% for f in form %}
{% if f.name != 'csrf_token' %}
<div class="form-group">
{{ f.label }}
{{ f }}
<button type="submit" class="pure-button pure-button-primary">Submit</button>
<div class="field">
<div class="control">
<input class="input" type="text" placeholder="http://mynode.com:18081" id="node_url" name="node_url" required>
</div>
</div>
<div class="field is-grouped">
<div class="control">
<button class="button is-link">Submit</button>
</div>
</div>
{% endif %}
{% endfor %}
<ul>
{% for field, errors in form.errors.items() %}
<li>{{ form[field].label }}: {{ ', '.join(errors) }}</li>
{% endfor %}
</ul>
</form>
</div>
<div id="nodes" class="pure-u-1 section">
<h2 class="center">Find a Node</h2>
<div class="center" id="filters">
<div id="nodes" class="section">
<h2 class="title is-4">Find a Node</h2>
<div class="" id="filters">
<form>
<span>
<label for="chainSelect">Chain:</label>
@ -40,7 +38,6 @@
<option value="wownero" {% if request.args.get('chain') == 'wownero' %}selected{% endif %}>Wownero</option>
</select>
</span>
<span>
<label for="networkSelect">Network:</label>
<select name="network" id="networkSelect">
@ -49,48 +46,42 @@
<option value="stagenet" {% if request.args.get('network') == 'stagenet' %}selected{% endif %}>Stagenet</option>
</select>
</span>
<span>
<label for="cors">Web (CORS):</label>
<label for="cors">CORS:</label>
<input type="checkbox" name="cors" id="cors" {% if request.args.get('cors') == 'on' %}checked{% endif %}>
</span>
<span>
<label for="onion">Onion:</label>
<input type="checkbox" name="onion" id="onion" {% if request.args.get('onion') == 'on' %}checked{% endif %}>
</span>
<span>
<label for="i2p">I2P:</label>
<input type="checkbox" name="i2p" id="i2p" {% if request.args.get('i2p') == 'on' %}checked{% endif %}>
</span>
<span>
<input type="submit" value="Filter" class="pure-button pure-pink">
</span>
<span>
<a href="/"><input type="button" value="Reset" class="pure-button pure-grey"></a>
</span>
</form>
</div>
{% if nodes %}
<div class="xmrnodes">
<p class="center">
<p class="">
{% if web_compatible %}
<strong>
<div class="notification is-info is-light" id="webCompat">
Web compatible means the node is run in such a way that it allows web clients to access their APIs (CORS headers allow all and secure HTTP / TLS terminated).
<br>
The more nodes there are running with these settings the more robust web clients will be in the future.
</strong>
<br><br>
</div>
{% endif %}
<a href="{{ url_for('meta.haproxy', chain=request.args.get('chain'), network=request.args.get('network'), cors=request.args.get('cors'), onion=request.args.get('onion')) }}">Download HAProxy config</a><br /><br />
Tracking {{ nodes_all }} {{ nettype }} {{ crypto | capitalize }} nodes in the database.
<br>
Of those, {{ nodes_unhealthy }} nodes failed their last check-in (unresponsive to ping or over 500 blocks away from highest reported block).
</p>
<p class="center">Showing {{ nodes | length }} nodes.
<p class="">Showing {{ nodes | length }} nodes.
{% if 'all' not in request.args %}
<a href="{% if request.args %}{{ request.url }}&{% else %}?{% endif %}all=true">Show All</a>
{% else %}
@ -98,67 +89,125 @@
{% endif %}
</p>
<br>
<table class="pure-table pure-table-horizontal pure-table-striped js-sort-table">
<thead>
<tr>
<th class="js-sort-string">Type</th>
<th class="js-sort-string">URL</th>
<th class="js-sort-number">Height</th>
<th class="js-sort-none">Up</th>
<th class="js-sort-none">Web<br/>Compatible</th>
<th class="js-sort-none">Network</th>
<th class="js-sort-none">Last Checked</th>
<th class="js-sort-none">History</th>
</tr>
</thead>
<tbody>
<div>
<div class="columns">
<div class="column">
{% for node in nodes %}
<tr class="js-sort-table">
<td>
<div class="card">
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-32x32">
{% if node.is_tor %}
<img src="/static/images/tor.svg" height="20px">
<span class="hidden">tor</span>
<img src="/static/images/tor.svg" />
{% elif node.is_i2p %}
<img src="/static/images/i2p.svg" height="20px">
<span class="hidden">i2p</span>
{% else %}
<img src="/static/images/clearnet.svg" height="20px">
<span class="hidden">clear</span>
{% endif %}
</td>
<td>
<span class="nodeURL">{{ node.url }}</span>
{% if node.donation_address | seems_legit %}
<span class="donationAddress">{{ node.donation_address }}</span>
{% endif %}
</td>
<td>{{ node.last_height }}</td>
<td>
{% if node.available %}
<span class="dot glowing-green"></span>
{% else %}
<span class="dot glowing-red""></span>
{% endif %}
</td>
<td>
{% if node.web_compatible %}
<img src="/static/images/success.svg" class="filter-green" width=16px>
<img src="/static/images/i2p.svg" />
{% else %}
<img src="/static/images/error.svg" class="filter-red" width=16px>
<span class="fflag fflag-{{ node.country_code }} ff-lg mr-4" title="{{ node.asn_description }}"></span>
{% endif %}
</td>
<td>{{ node.nettype }}</td>
<td>{{ node.datetime_checked | humanize }}</td>
<td>{% for hc in node.healthchecks %}
{% if loop.index > loop.length - 6 %}
<span class="dot glowing-{% if hc.health %}green{% else %}red{% endif %}"></span>
</figure>
</div>
<div class="media-content">
<p class="title is-6 nodeURL">{{ node.url }}</p>
{% if node.asn %}
<p class="subtitle asn">
ASN: {{ node.asn }} -
{{ node.asn_description}}
</p>
{% endif %}
<div class="statusDots subtitle">
{% for check in node.healthchecks %}
<span class="dot glowing-{{ check.color }}"></span>
{% endfor %}
</td>
</tr>
</div>
</div>
</div>
<div class="content">
<br>
<p class="smol">
last checked {{ node.datetime_checked | humanize }}<br />
{{ node.uptime }}% uptime
</p>
</div>
</div>
<footer class="card-footer">
<p class="card-footer-item">
<span>
View on <a href="https://twitter.com/codinghorror/status/506010907021828096">Twitter</a>
</span>
</p>
<p class="card-footer-item">
<span>
Share on <a href="#">Facebook</a>
</span>
</p>
</footer>
</div>
{% endfor %}
</tbody>
</table>
</div>
<div class="column">
<article class="panel is-info">
<p class="panel-heading">
Info
</p>
<p class="panel-tabs">
<a class="is-active">All</a>
<a>Public</a>
<a>Private</a>
<a>Sources</a>
<a>Forks</a>
</p>
<div class="panel-block">
<p class="control has-icons-left">
<input class="input is-info" type="text" placeholder="Search">
<span class="icon is-left">
<i class="fas fa-search" aria-hidden="true"></i>
</span>
</p>
</div>
<a class="panel-block is-active">
<span class="panel-icon">
<i class="fas fa-book" aria-hidden="true"></i>
</span>
bulma
</a>
<a class="panel-block">
<span class="panel-icon">
<i class="fas fa-book" aria-hidden="true"></i>
</span>
marksheet
</a>
<a class="panel-block">
<span class="panel-icon">
<i class="fas fa-book" aria-hidden="true"></i>
</span>
minireset.css
</a>
<a class="panel-block">
<span class="panel-icon">
<i class="fas fa-book" aria-hidden="true"></i>
</span>
jgthms.github.io
</a>
</article>
</div>
</div>
<!-- </tbody>
</table> -->
</div>
</div>
{% else %}
<p class="center">No nodes in the database yet...</p>

@ -1,24 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<link rel="shortcut icon" href="/static/favicon.ico" type="image/x-icon" />
<meta property="fb:app_id" content="0" />
<meta property="og:image" content="https://www.getmonero.org/press-kit/symbols/monero-symbol-on-white-480.png" />
<meta property="og:description" content="xmrnodes" />
<meta property="og:url" content="http://localhost" />
<meta property="og:title" content="XMR Nodes" />
<meta property="og:type" content="website" />
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="theme-color" content="#ffffff">
<meta name="apple-mobile-web-app-title" content="XMR Nodes">
<meta name="application-name" content="XMR Nodes">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="keywords" content="wownero, monero, xmr, bitmonero, cryptocurrency">
{% extends 'base.html' %}
{% block content %}
<div class="center info">
<p>Peers seen ~24 hours: {{ peers | length }}</p>
<p>Source Node: {{ source_node }}</p>
<p>
This is not a full representation of the entire Monero network,
just a look into the peers being recursively crawled from the source node ({{ source_node }}).
New peers are searched for once per day.
Older peers are shown as more transparent and will be removed
if not seen again after {{ config.PEER_LIFETIME }} hours.
</p>
</div>
<div id="map" class="map"></div>
<div id="popup" class="popup" title="Welcome to OpenLayers"></div>
<link rel="stylesheet" href="//cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/css/ol.css" type="text/css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" type="text/css">
<style>
@ -40,37 +37,6 @@
<script src="//cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
<script src="//code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js"></script>
<title>XMR Nodes</title>
</head>
<body>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes pure-u-1 center">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<div class="center info">
<p>Peers seen ~24 hours: {{ peers | length }}</p>
<p>Source Node: {{ source_node }}</p>
<p>
This is not a full representation of the entire Monero network,
just a look into the peers being recursively crawled from the source node ({{ source_node }}).
New peers are searched for once per day.
Older peers are shown as more transparent and will be removed
if not seen again after {{ config.PEER_LIFETIME }} hours.
</p>
<br>
<a href="/">Go home</a>
</div>
<div id="map" class="map"></div>
<div id="popup" class="popup" title="Welcome to OpenLayers"></div>
<script>
// Marker layer
markerLayer = new ol.layer.Vector({
@ -157,6 +123,4 @@
});
});
</script>
</body>
</html>
{% endblock %}
Loading…
Cancel
Save