From f5c222576b5149f9ab2106d2f2a8fae2e7dc704b Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Thu, 28 Dec 2023 18:04:38 +1100 Subject: [PATCH] feat: Add search page --- account.py | 11 ++- domainLookup.py | 22 +++++ main.py | 82 +++++++++++++++++-- render.py | 73 ++++++++++++++++- templates/404.html | 10 +-- templates/assets/css/styles.min.css | 1 + templates/index.html | 10 +-- templates/login.html | 1 + templates/receive.html | 10 +-- templates/register.html | 1 + templates/search.html | 123 ++++++++++++++++++++++++++++ templates/send.html | 10 +-- templates/success.html | 10 +-- templates/tx.html | 10 +-- 14 files changed, 330 insertions(+), 44 deletions(-) create mode 100644 templates/assets/css/styles.min.css create mode 100644 templates/search.html diff --git a/account.py b/account.py index bd1857b..52ddcb8 100644 --- a/account.py +++ b/account.py @@ -173,4 +173,13 @@ def send(account,address,amount): } return { "tx": response['result'] - } \ No newline at end of file + } + +def getDomain(domain: str): + # Get the domain + response = hsd.rpc_getNameInfo(domain) + if response['error'] is not None: + return { + "error": response['error']['message'] + } + return response['result'] \ No newline at end of file diff --git a/domainLookup.py b/domainLookup.py index 8c92a82..197bca2 100644 --- a/domainLookup.py +++ b/domainLookup.py @@ -8,6 +8,7 @@ import datetime import dns.asyncresolver import httpx from requests_doh import DNSOverHTTPSSession, add_dns_provider +import requests def hip2(domain: str): @@ -134,3 +135,24 @@ def resolve_TLSA_with_doh(query_name, doh_url="https://hnsdoh.com/dns-query"): tlsa = r.answer[0][0] return tlsa + +def niami_info(domain: str): + response = requests.get(f"https://api.niami.io/hsd/{domain}") + if response.status_code != 200: + return False + + response = response.json() + output = { + "owner": response["data"]["owner_tx_data"]["address"], + "dns": response["data"]["dnsData"] + } + + transactions = requests.get(f"https://api.niami.io/txs/{domain}") + if transactions.status_code != 200: + return False + + transactions = transactions.json() + output["txs"] = transactions["txs"] + return output + + diff --git a/main.py b/main.py index 03b2aa3..1b3555d 100644 --- a/main.py +++ b/main.py @@ -6,6 +6,7 @@ import account as account_module import render import re from flask_qrcode import QRcode +import domainLookup dotenv.load_dotenv() @@ -40,8 +41,8 @@ def index(): return render_template("index.html", account=account, available=available, total=total, pending=pending, domains=domains, domain_count=domain_count) - - + +#region Transactions @app.route('/tx') def transactions(): # Check if the user is logged in @@ -151,7 +152,6 @@ def success(): tx = request.args.get("tx") return render_template("success.html", account=account, tx=tx) - @app.route('/checkaddress') def check_address(): address = request.args.get("address") @@ -159,6 +159,80 @@ def check_address(): return jsonify({"result": "Invalid address"}) return jsonify({"result": account_module.check_address(address)}) +#endregion + +#region Domains +@app.route('/search') +def search(): + # Check if the user is logged in + if request.cookies.get("account") is None: + return redirect("/login") + + account = account_module.check_account(request.cookies.get("account")) + if not account: + return redirect("/logout") + + search_term = request.args.get("q") + search_term = search_term.lower().strip() + + + if len(search_term) == 0: + return redirect("/") + domain = account_module.getDomain(search_term) + + if 'error' in domain: + return render_template("search.html", account=account, search_term=search_term, domain=domain['error']) + + print(domain) + if domain['info'] is None: + return render_template("search.html", account=account, search_term=search_term, + domain=search_term, state="AVAILABLE", next="Available Now") + + + state = domain['info']['state'] + if state == 'CLOSED': + if not domain['info']['registered']: + state = 'AVAILABLE' + next = "Available Now" + else: + state = 'REGISTERED' + expires = domain['info']['stats']['daysUntilExpire'] + next = f"Expires in ~{expires} days" + + + domain_info = domainLookup.niami_info(search_term) + owner = 'Unknown' + dns = [] + txs = [] + + if domain_info: + owner = domain_info['owner'] + dns = domain_info['dns'] + txs = domain_info['txs'] + + own_domains = account_module.getDomains(account) + own_domains = [x['name'] for x in own_domains] + own_domains = [x.lower() for x in own_domains] + if search_term in own_domains: + owner = "You" + + dns = render.dns(dns) + txs = render.txs(txs) + + + + + return render_template("search.html", account=account, search_term=search_term, + domain=domain['info']['name'],raw=domain, + state=state, next=next, owner=owner, dns=dns, txs=txs) + + + + + + +#endregion + #region Account @app.route('/login') @@ -199,13 +273,11 @@ def logout(): #endregion - #region Assets and default pages @app.route('/qr/') def qr(data): return send_file(qrcode(data, mode="raw"), mimetype="image/png") - @app.route('/assets/') def send_assets(path): return send_from_directory('templates/assets', path) diff --git a/render.py b/render.py index d1ce409..7b1d292 100644 --- a/render.py +++ b/render.py @@ -1,3 +1,6 @@ +import datetime + + def domains(domains): html = '' for domain in domains: @@ -13,7 +16,7 @@ def domains(domains): - html += f'{domain["name"]}{expires} days{paid} HNSManage' + html += f'{domain["name"]}{expires} days{paid} HNSManage' return html @@ -64,4 +67,70 @@ def transactions(txs): - return html \ No newline at end of file + return html + + +def dns(data): + html_output = "" + + for entry in data: + html_output += f"{entry['type']}\n" + + if entry['type'] != 'DS' and not entry['type'].startswith("GLUE") and not entry['type'].startswith("SYNTH"): + for key, value in entry.items(): + if key != 'type': + if isinstance(value, list): + html_output += "\n" + for val in value: + html_output += f"{val}
\n" + + html_output += "\n" + else: + html_output += f"{value}\n" + + + elif entry['type'] == 'DS': + ds = str(entry['keyTag']) + " " + str(entry['algorithm']) + " " + str(entry['digestType']) + " " + entry['digest'] + html_output += f"{ds}\n" + + else: + value = "" + for key, val in entry.items(): + if key != 'type': + value += str(val) + " " + html_output += f"{value}\n" + + html_output += " \n" + return html_output + +def txs(data): + data = data[::-1] + + html_output = "" + + for entry in data: + html_output += f"{entry['action']}\n" + html_output += f"{entry['txid'][:8]}...\n" + amount = entry['amount'] + amount = amount / 1000000 + amount = round(amount, 2) + + if entry['blind'] == None: + html_output += f"{amount} HNS\n" + else: + blind = entry['blind'] + blind = blind / 1000000 + blind = round(blind, 2) + html_output += f"{amount} + {blind} HNS\n" + + html_output += f"{timestamp_to_readable_time(entry['time'])}\n" + html_output += f"\n" + + return html_output + + +def timestamp_to_readable_time(timestamp): + # Assuming the timestamp is in seconds + dt_object = datetime.datetime.fromtimestamp(timestamp) + readable_time = dt_object.strftime("%H:%M:%S %d %b %Y") + return readable_time diff --git a/templates/404.html b/templates/404.html index 5e189c5..41b47fc 100644 --- a/templates/404.html +++ b/templates/404.html @@ -14,6 +14,7 @@ + @@ -27,10 +28,7 @@
@@ -40,8 +38,8 @@