fireindexer-front/server.py

298 lines
8.1 KiB
Python
Raw Permalink Normal View History

2025-02-06 22:50:56 +11:00
from decimal import Decimal
2025-02-05 17:15:44 +11:00
from functools import cache
import json
from flask import (
Flask,
make_response,
redirect,
request,
jsonify,
render_template,
send_from_directory,
send_file,
)
import os
import json
import requests
from datetime import datetime
import dotenv
2025-02-05 18:08:31 +11:00
import hsd
2025-02-06 22:50:56 +11:00
import indexerClasses
import db
from datetime import datetime, timezone
from indexerClasses import Block, Transaction, Covenant
2025-02-05 17:15:44 +11:00
dotenv.load_dotenv()
app = Flask(__name__)
2025-02-06 22:50:56 +11:00
dbCon = db.DBConnection()
2025-02-05 17:15:44 +11:00
def find(name, path):
for root, dirs, files in os.walk(path):
if name in files:
return os.path.join(root, name)
# region Main routes
@app.route("/")
def index():
2025-02-06 22:50:56 +11:00
# txs = hsd.get_mempool()
2025-02-05 18:37:13 +11:00
2025-02-06 22:50:56 +11:00
# mempool_info = hsd.get_mempool_info()
# if mempool_info['result']:
# mempool_info = mempool_info['result']
2025-02-05 18:37:13 +11:00
2025-02-06 22:50:56 +11:00
# mempool_info['txs'] = txs
return render_template("index.html")
2025-02-05 18:37:13 +11:00
2025-02-06 22:50:56 +11:00
@app.route("/search")
def search():
if not request.args.get("q"):
return render_template("index.html")
query = request.args.get("q")
tx = dbCon.getTransaction(query)
if tx:
return redirect(f"/tx/{query}")
block = dbCon.getBlock(query)
if block:
return redirect(f"/block/{query}")
block = dbCon.getBlockHash(query)
if block:
return redirect(f"/block/{query}")
name = hsd.get_name(query)
if not name['error']:
return redirect(f"/name/{query}")
return render_template("index.html",message="No results found",query=query)
2025-02-05 17:15:44 +11:00
2025-02-05 18:08:31 +11:00
@app.route("/name")
def name():
if request.args.get("name"):
name = request.args.get("name")
data = hsd.get_name(name)
dns = hsd.get_name_resource(name)
data = json.dumps(data, indent=4) + "<br><br>" + json.dumps(dns, indent=4)
return render_template("data.html", data=data)
else:
return render_template("index.html")
2025-02-06 22:50:56 +11:00
@app.route("/tx/<tx>")
def txPage(tx):
tx = dbCon.getTransaction(tx)
if tx:
return render_template("tx.html", tx=tx)
return render_template("index.html",message="Transaction not found")
2025-02-05 18:08:31 +11:00
2025-02-06 22:50:56 +11:00
@app.route("/block/<block>")
def blockPage(block):
block = dbCon.getBlock(block)
if block:
return render_template("block.html", block=block)
return render_template("index.html",message="Block not found")
@app.route("/address/<address>")
def addressPage(address):
# if address:
# return render_template("address.html", address=address)
return render_template("index.html",message="Not implemented")
2025-02-05 18:08:31 +11:00
2025-02-05 17:15:44 +11:00
@app.route("/<path:path>")
def catch_all(path: str):
if os.path.isfile("templates/" + path):
return render_template(path)
# Try with .html
if os.path.isfile("templates/" + path + ".html"):
return render_template(path + ".html")
if os.path.isfile("templates/" + path.strip("/") + ".html"):
return render_template(path.strip("/") + ".html")
# Try to find a file matching
if path.count("/") < 1:
# Try to find a file matching
filename = find(path, "templates")
if filename:
return send_file(filename)
return render_template("404.html"), 404
# endregion
2025-02-05 18:08:31 +11:00
# region API routes
@app.route("/api/v1/version")
def api_version():
return jsonify({"version": "1.0.0"})
@app.route("/api/v1/tx/<txid>")
def api_tx(txid):
2025-02-06 22:50:56 +11:00
tx = dbCon.getTransaction(txid)
2025-02-05 18:08:31 +11:00
if tx:
2025-02-06 22:50:56 +11:00
return jsonify(tx.toJSON())
2025-02-05 18:08:31 +11:00
else:
return jsonify({"error": "tx not found"}), 404
2025-02-06 22:50:56 +11:00
@app.route("/api/v1/block/<blockheight>")
def api_block(blockheight):
block = dbCon.getBlock(blockheight)
2025-02-05 18:08:31 +11:00
if block:
2025-02-06 22:50:56 +11:00
print("Found block from height")
return jsonify(block.toJSON())
block = dbCon.getBlockHash(blockheight)
if block:
print("Found block from hash")
return jsonify(block.toJSON())
return jsonify({"error": "block not found"}), 404
2025-02-05 18:08:31 +11:00
@app.route("/api/v1/name/<name>")
def api_name(name):
name = hsd.get_name(name)
if name:
return jsonify(name)
else:
return jsonify({"error": "name not found"}), 404
@app.route("/api/v1/name/<name>/resource")
@app.route("/api/v1/name/<name>/dns")
@app.route("/api/v1/resource/<name>")
@app.route("/api/v1/dns/<name>")
def api_name_resource(name):
name = hsd.get_name_resource(name)
if name:
return jsonify(name)
else:
return jsonify({"error": "name not found"}), 404
@app.route("/api/v1/address/<address>")
def api_address(address):
address = hsd.get_address(address)
if address:
return jsonify(address)
else:
return jsonify({"error": "address not found"}), 404
2025-02-05 18:37:13 +11:00
@app.route("/api/v1/mempool")
def api_mempool():
mempool = hsd.get_mempool()
if mempool:
return jsonify(mempool)
else:
return jsonify({"error": "mempool not found"}), 404
@app.route("/api/v1/mempool/info")
def api_mempool_info():
mempool_info = hsd.get_mempool_info()
if mempool_info:
return jsonify(mempool_info)
else:
return jsonify({"error": "mempool info not found"}), 404
2025-02-05 18:08:31 +11:00
# endregion
2025-02-05 17:15:44 +11:00
# region Error Catching
# 404 catch all
@app.errorhandler(404)
def not_found(e):
return render_template("404.html"), 404
2025-02-06 22:50:56 +11:00
# endregion
# region Assets routes
@app.route("/assets/<path:path>")
def send_assets(path):
if path.endswith(".json"):
return send_from_directory(
"templates/assets", path, mimetype="application/json"
)
if os.path.isfile("templates/assets/" + path):
return send_from_directory("templates/assets", path)
# Try looking in one of the directories
filename: str = path.split("/")[-1]
if (
filename.endswith(".png")
or filename.endswith(".jpg")
or filename.endswith(".jpeg")
or filename.endswith(".svg")
):
if os.path.isfile("templates/assets/img/" + filename):
return send_from_directory("templates/assets/img", filename)
if os.path.isfile("templates/assets/img/favicon/" + filename):
return send_from_directory("templates/assets/img/favicon", filename)
return render_template("404.html"), 404
@app.route("/favicon.png")
def faviconPNG():
return send_from_directory("templates/assets/img", "favicon.png")
@app.route("/.well-known/<path:path>")
def wellknown(path):
# Try to proxy to https://nathan.woodburn.au/.well-known/
req = requests.get(f"https://nathan.woodburn.au/.well-known/{path}")
return make_response(
req.content, 200, {"Content-Type": req.headers["Content-Type"]}
)
@app.template_filter('datetimeformat')
def datetimeformat(value, format='%Y-%m-%d %H:%M:%S'):
return datetime.fromtimestamp(value, tz=timezone.utc).strftime(format)
@app.template_filter('convert_bits_to_difficulty')
def convert_bits_to_difficulty(bits):
"""Convert compact bits format to difficulty."""
bits_hex = f"{bits:08x}" # Convert to 8-char hex string
exp = int(bits_hex[:2], 16) # First byte (exponent)
coeff = int(bits_hex[2:], 16) # Remaining 3 bytes (coefficient)
# Compute target from bits
target = coeff * (256 ** (exp - 3))
# Maximum target (difficulty 1)
max_target = 0xFFFF * (256 ** (0x1D - 3))
# Compute difficulty
difficulty = Decimal(max_target) / Decimal(target)
return f"{difficulty:,.0f}"
@app.template_filter('hexToAscii')
def hexToAscii(hex_string):
# Convert the hex string to bytes
bytes_obj = bytes.fromhex(hex_string)
# Decode the bytes object to an ASCII string
ascii_string = bytes_obj.decode('ascii')
return ascii_string
@app.template_filter('getTX')
def getTX(txid):
tx = dbCon.getTransaction(txid)
if tx:
return tx.toJSON()
else:
return {"error": "tx not found"}
@app.template_filter("parse_covenant")
def parse_covenant(covenant):
covenant = Covenant(covenant)
if covenant.name:
return f"{covenant.action} {covenant.name}"
elif covenant.type == 0:
return f"{covenant.action}"
name = dbCon.getNameByHash(covenant.nameHash)
if name:
return f"{covenant.action} {name}"
return f"{covenant.action} Unknown Name"
2025-02-05 17:15:44 +11:00
# endregion
2025-02-06 22:50:56 +11:00
2025-02-05 17:15:44 +11:00
if __name__ == "__main__":
app.run(debug=True, port=5000, host="0.0.0.0")