monero.fail/xmrnodes/app.py

195 lines
6.0 KiB
Python
Raw Normal View History

import arrow
2020-10-13 05:32:21 +01:00
import json
import requests
import re
import logging
2020-10-22 06:50:40 +01:00
import click
2020-10-13 05:32:21 +01:00
from os import makedirs
from datetime import datetime
2020-10-13 05:32:21 +01:00
from flask import Flask, request, redirect
from flask import render_template, flash, url_for
from urllib.parse import urlparse
2020-10-24 06:55:34 +01:00
from xmrnodes.helpers import determine_crypto, is_onion, make_request
from xmrnodes.forms import SubmitNode
2020-10-24 09:29:10 +01:00
from xmrnodes.models import Node, HealthCheck
2020-10-22 06:12:24 +01:00
from xmrnodes import config
2020-10-13 05:32:21 +01:00
logging.basicConfig(
level=logging.INFO,
2020-10-22 06:12:24 +01:00
format="%(asctime)s - %(levelname)s - %(message)s"
)
2020-10-13 05:32:21 +01:00
app = Flask(__name__)
app.config.from_envvar("FLASK_SECRETS")
app.secret_key = app.config["SECRET_KEY"]
@app.route("/", methods=["GET", "POST"])
def index():
form = SubmitNode()
2020-10-13 05:32:21 +01:00
itp = 20
page = request.args.get("page", 1)
try:
page = int(page)
except:
flash("Wow, wtf hackerman. Cool it.")
page = 1
2020-10-18 09:18:35 +01:00
nettype = request.args.get("nettype", "mainnet")
crypto = request.args.get("crypto", "monero")
2020-10-24 07:46:14 +01:00
onion = request.args.get("onion", False)
2020-10-18 09:18:35 +01:00
nodes = Node.select().where(
Node.validated==True
).where(
Node.nettype==nettype
).where(
Node.crypto==crypto
).order_by(
Node.datetime_entered.desc()
2020-10-17 23:01:38 +01:00
)
2020-10-24 07:46:14 +01:00
if onion:
nodes = nodes.where(Node.is_tor==True)
2020-10-24 09:29:10 +01:00
2020-10-17 23:01:38 +01:00
paginated = nodes.paginate(page, itp)
total_pages = nodes.count() / itp
return render_template(
"index.html",
2020-10-17 23:01:38 +01:00
nodes=paginated,
page=page,
total_pages=total_pages,
form=form
)
2020-10-13 05:32:21 +01:00
@app.route("/add", methods=["GET", "POST"])
def add():
if request.method == "POST":
url = request.form.get("node_url")
2020-10-13 05:32:21 +01:00
regex = re.compile(
2020-10-22 06:12:24 +01:00
r"^(?:http)s?://" # http:// or https://
r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" #domain...
r"localhost|" #localhost...
r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" # ...or ip
r"(?::\d+)?" # optional port
r"(?:/?|[/?]\S+)$", re.IGNORECASE
)
2020-10-13 05:32:21 +01:00
re_match = re.match(regex, url)
if re_match is None:
2020-10-22 06:12:24 +01:00
flash("This doesn\"t look like a valid URL")
2020-10-13 05:32:21 +01:00
else:
_url = urlparse(url)
url = f"{_url.scheme}://{_url.netloc}"
if Node.select().where(Node.url == url).exists():
flash("This node is already in the database.")
else:
flash("Seems like a valid node URL. Added to the database and will check soon.")
node = Node(url=url)
node.save()
2020-10-13 05:32:21 +01:00
return redirect("/")
2020-10-18 00:27:54 +01:00
@app.cli.command("check")
def check():
nodes = Node.select().where(Node.validated == True)
for node in nodes:
now = datetime.utcnow()
2020-10-24 09:29:10 +01:00
hc = HealthCheck(node=node)
2020-10-18 00:27:54 +01:00
logging.info(f"Attempting to check {node.url}")
try:
2020-10-24 06:55:34 +01:00
r = make_request(node.url)
2020-10-18 00:27:54 +01:00
assert "status" in r.json()
assert "offline" in r.json()
assert "height" in r.json()
if r.json()["status"] == "OK":
logging.info("success")
node.available = True
node.last_height = r.json()["height"]
2020-10-24 09:29:10 +01:00
hc.health = True
2020-10-18 00:27:54 +01:00
else:
raise
except:
logging.info("fail")
node.datetime_failed = now
node.available = False
2020-10-24 09:29:10 +01:00
hc.health = False
2020-10-18 00:27:54 +01:00
finally:
node.datetime_checked = now
node.save()
2020-10-24 09:29:10 +01:00
hc.save()
2020-10-18 00:27:54 +01:00
@app.cli.command("validate")
def validate():
nodes = Node.select().where(Node.validated == False)
for node in nodes:
now = datetime.utcnow()
logging.info(f"Attempting to validate {node.url}")
try:
2020-10-24 06:55:34 +01:00
r = make_request(node.url)
assert "height" in r.json()
assert "nettype" in r.json()
nettype = r.json()["nettype"]
2020-10-18 09:18:35 +01:00
crypto = determine_crypto(node.url)
logging.info("success")
if nettype in ["mainnet", "stagenet", "testnet"]:
node.nettype = nettype
node.available = True
node.validated = True
2020-10-18 00:27:54 +01:00
node.last_height = r.json()["height"]
node.datetime_checked = now
2020-10-18 09:18:35 +01:00
node.crypto = crypto
2020-10-24 06:55:34 +01:00
node.is_tor = is_onion(node.url)
node.save()
else:
logging.info("unexpected nettype")
except requests.exceptions.ConnectTimeout:
logging.info("connection timed out")
node.delete_instance()
except requests.exceptions.SSLError:
logging.info("invalid certificate")
node.delete_instance()
except requests.exceptions.ConnectionError:
logging.info("connection error")
node.delete_instance()
except requests.exceptions.HTTPError:
logging.info("http error, 4xx or 5xx")
node.delete_instance()
except Exception as e:
logging.info("failed for reasons unknown")
node.delete_instance()
2020-10-13 05:32:21 +01:00
2020-10-22 06:13:20 +01:00
@app.cli.command("export")
def export():
all_nodes = []
export_dir = f"{config.DATA_DIR}/export.txt"
nodes = Node.select().where(Node.validated == True)
for node in nodes:
2020-10-22 06:50:40 +01:00
logging.info(f"Adding {node.url}")
2020-10-22 06:13:20 +01:00
all_nodes.append(node.url)
with open(export_dir, "w") as f:
f.write("\n".join(all_nodes))
logging.info(f"{nodes.count()} nodes written to {export_dir}")
2020-10-22 06:50:40 +01:00
@app.cli.command("import")
def import_():
all_nodes = []
export_dir = f"{config.DATA_DIR}/export.txt"
with open(export_dir, 'r') as f:
for url in f.readlines():
2020-10-24 09:29:10 +01:00
try:
n = url.rstrip()
logging.info(f"Adding {n}")
node = Node(url=n)
node.save()
all_nodes.append(n)
except:
pass
2020-10-22 06:50:40 +01:00
logging.info(f"{len(all_nodes)} node urls imported and ready to be validated")
2020-10-22 06:12:24 +01:00
@app.template_filter("humanize")
def humanize(d):
2020-10-22 06:12:24 +01:00
t = arrow.get(d, "UTC")
return t.humanize()
2020-10-13 05:32:21 +01:00
if __name__ == "__main__":
app.run()