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 import subprocess dotenv.load_dotenv() app = Flask(__name__) def find(name, path): for root, dirs, files in os.walk(path): if name in files: return os.path.join(root, name) # Assets routes @app.route("/assets/") 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 # region Special routes @app.route("/favicon.png") def faviconPNG(): return send_from_directory("templates/assets/img", "favicon.png") @app.route("/.well-known/") 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.route("/verify", methods=["GET"]) def verify_domain(): # Get the domain from the query string domain = request.args.get("domain") if not domain: return jsonify({"error": "No domain provided"}), 400 # Get path from env apiKEY = os.getenv("API_KEY") # Get host or use 127.0.0.1 host = os.getenv("HSD_HOST", "127.0.0.1") nameInfo = requests.post( f"http://x:{apiKEY}@{host}:12037", json={ "method": "getnameinfo", "params": [domain] } ) # Verify error is none if nameInfo.status_code != 200: return jsonify({"error": "Failed to get name info"}), 500 # Convert response to JSON try: nameInfo = nameInfo.json() except json.JSONDecodeError: return jsonify({"error": "Failed to decode JSON response"}), 500 if nameInfo["error"] is not None: return jsonify({"error": nameInfo["error"]}), 500 # Get .result.info.owner.hash and .result.info.owner.index if "result" not in nameInfo or "info" not in nameInfo["result"]: return jsonify({"error": "Invalid response format"}), 500 if "owner" not in nameInfo["result"]["info"]: return jsonify({"error": "Owner not found in response"}), 500 if "hash" not in nameInfo["result"]["info"]["owner"] or "index" not in nameInfo["result"]["info"]["owner"]: return jsonify({"error": "Owner hash or index not found in response"}), 500 owner_hash = nameInfo["result"]["info"]["owner"]["hash"] owner_index = nameInfo["result"]["info"]["owner"]["index"] coinInfo = requests.get(f"http://x:{apiKEY}@{host}:12037/coin/{owner_hash}/{owner_index}") if coinInfo.status_code != 200: return jsonify({"error": "Failed to get coin info"}), 500 # Convert coinInfo response to JSON try: coinInfo = coinInfo.json() except json.JSONDecodeError: return jsonify({"error": "Failed to decode JSON response"}), 500 if "address" not in coinInfo: return jsonify({"error": "Address not found in coin info"}), 500 return jsonify({ "domain": domain, "owner_hash": owner_hash, "owner_index": owner_index, "address": coinInfo["address"] }), 200 @app.route("/renew", methods=["POST"]) def renew(): # Get post data data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 # Get the domain domain = data.get("domain") if not domain: return jsonify({"error": "No domain provided"}), 400 # Get path from env path = os.getenv("SCRIPT_PATH") wallet = os.getenv("WALLET") apiKEY = os.getenv("API_KEY") if not path: return jsonify({"error": "No HS-ANYONE found"}), 400 # Run the renewal print(f"Command: node {path} renew {domain} --wallet {wallet} --network main --broadcast true --apikey {apiKEY}") output = subprocess.run(["node", path, "renew", domain, "--wallet", wallet, "--network", "main", "--broadcast", "true", "--apikey", apiKEY], capture_output=True, text=True) return jsonify({"output": output.stdout,"error": output.stderr}), 200 # endregion # region Main routes @app.route("/") def index(): return render_template("index.html") @app.route("/") 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 # region Error Catching # 404 catch all @app.errorhandler(404) def not_found(e): return render_template("404.html"), 404 # endregion if __name__ == "__main__": app.run(debug=True, port=5000, host="0.0.0.0")