diff --git a/server.py b/server.py index 14fec7f..1f19ac9 100644 --- a/server.py +++ b/server.py @@ -1,5 +1,14 @@ import json -from flask import Flask, make_response, redirect, request, jsonify, render_template, send_from_directory, send_file +from flask import ( + Flask, + make_response, + redirect, + request, + jsonify, + render_template, + send_from_directory, + send_file, +) from flask_cors import CORS import os import dotenv @@ -29,35 +38,47 @@ dotenv.load_dotenv() handshake_scripts = '' -restricted = ['ascii'] +restricted = ["ascii"] sites = [] -if os.path.isfile('data/sites.json'): - with open('data/sites.json') as file: +if os.path.isfile("data/sites.json"): + with open("data/sites.json") as file: sites = json.load(file) # Remove any sites that are not enabled - sites = [site for site in sites if 'enabled' not in site or site['enabled'] == True] + sites = [ + site for site in sites if "enabled" not in site or site["enabled"] == True + ] projects = [] projectsUpdated = 0 + +ncConfig = requests.get( + "https://cloud.woodburn.au/s/4ToXgFe3TnnFcN7/download/website-conf.json" +) +ncConfig = ncConfig.json() + + @cache -def getAddress(coin:str) -> str: - address = '' - if os.path.isfile('.well-known/wallets/' + coin.upper()): - with open('.well-known/wallets/' + coin.upper()) as file: +def getAddress(coin: str) -> str: + address = "" + if os.path.isfile(".well-known/wallets/" + coin.upper()): + with open(".well-known/wallets/" + coin.upper()) as file: address = file.read() return address -#Assets routes -@app.route('/assets/') -def send_report(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) - +# Assets routes +@app.route("/assets/") +def send_report(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) + # Custom matching for images pathMap = { "img/hns/w": "img/external/HNS/white", @@ -70,126 +91,138 @@ def send_report(path): if path.startswith(key): tmpPath = path.replace(key, pathMap[key]) print(tmpPath) - if os.path.isfile('templates/assets/' + tmpPath): - return send_from_directory('templates/assets', tmpPath) - + if os.path.isfile("templates/assets/" + tmpPath): + return send_from_directory("templates/assets", tmpPath) # 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) + 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 + return render_template("404.html"), 404 -# Special routes -@app.route('/links') +# region Special routes +@app.route("/links") def links(): - return render_template('link.html') + return render_template("link.html") -@app.route('/sitemap') -@app.route('/sitemap.xml') + +@app.route("/sitemap") +@app.route("/sitemap.xml") def sitemap(): # Remove all .html from sitemap - with open('templates/sitemap.xml') as file: + with open("templates/sitemap.xml") as file: sitemap = file.read() - sitemap = sitemap.replace('.html', '') - return make_response(sitemap, 200, {'Content-Type': 'application/xml'}) + sitemap = sitemap.replace(".html", "") + return make_response(sitemap, 200, {"Content-Type": "application/xml"}) -@app.route('/favicon.png') + +@app.route("/favicon.png") def faviconPNG(): - return send_from_directory('templates/assets/img/favicon', 'favicon.png') + return send_from_directory("templates/assets/img/favicon", "favicon.png") -@app.route('/favicon.svg') + +@app.route("/favicon.svg") def faviconSVG(): - return send_from_directory('templates/assets/img/favicon', 'favicon.svg') + return send_from_directory("templates/assets/img/favicon", "favicon.svg") -@app.route('/https.js') -@app.route('/handshake.js') -@app.route('/redirect.js') + +@app.route("/https.js") +@app.route("/handshake.js") +@app.route("/redirect.js") def handshake(): # return request.path - return send_from_directory('templates/assets/js', request.path.split('/')[-1]) + return send_from_directory("templates/assets/js", request.path.split("/")[-1]) -@app.route('/generator/') + +@app.route("/generator/") def removeTrailingSlash(): - return render_template(request.path.split('/')[-2] + '.html') + return render_template(request.path.split("/")[-2] + ".html") -@app.route('/.well-known/wallets/') + +@app.route("/.well-known/wallets/") def wallet(path): if path[0] == ".": - return send_from_directory('.well-known/wallets', path, mimetype='application/json') - elif os.path.isfile('.well-known/wallets/' + path): - address = '' - with open('.well-known/wallets/' + path) as file: + return send_from_directory( + ".well-known/wallets", path, mimetype="application/json" + ) + elif os.path.isfile(".well-known/wallets/" + path): + address = "" + with open(".well-known/wallets/" + path) as file: address = file.read() address = address.strip() - return make_response(address, 200, {'Content-Type': 'text/plain'}) + return make_response(address, 200, {"Content-Type": "text/plain"}) - if os.path.isfile('.well-known/wallets/' + path.upper()): - return redirect('/.well-known/wallets/' + path.upper(), code=302) + if os.path.isfile(".well-known/wallets/" + path.upper()): + return redirect("/.well-known/wallets/" + path.upper(), code=302) - return render_template('404.html'), 404 + return render_template("404.html"), 404 -@app.route('/.well-known/nostr.json') + +@app.route("/.well-known/nostr.json") def nostr(): # Get name parameter - name = request.args.get('name') + name = request.args.get("name") if not name: - return jsonify({'error': 'No name provided'}) - return jsonify({ - 'names': { - name: 'b57b6a06fdf0a4095eba69eee26e2bf6fa72bd1ce6cbe9a6f72a7021c7acaa82' + return jsonify({"error": "No name provided"}) + return jsonify( + { + "names": { + name: "b57b6a06fdf0a4095eba69eee26e2bf6fa72bd1ce6cbe9a6f72a7021c7acaa82" + } } - }) + ) -@app.route('/.well-known/xrp-ledger.toml') + +@app.route("/.well-known/xrp-ledger.toml") def xrpLedger(): # Create a response with the xrp-ledger.toml file - with open('.well-known/xrp-ledger.toml') as file: + with open(".well-known/xrp-ledger.toml") as file: toml = file.read() - - response = make_response(toml, 200, {'Content-Type': 'application/toml'}) + + response = make_response(toml, 200, {"Content-Type": "application/toml"}) # Set cors headers - response.headers['Access-Control-Allow-Origin'] = '*' + response.headers["Access-Control-Allow-Origin"] = "*" return response - -@app.route('/manifest.json') +@app.route("/manifest.json") def manifest(): host = request.host - if host == 'nathan.woodburn.au': - return send_from_directory('templates', 'manifest.json') - + if host == "nathan.woodburn.au": + return send_from_directory("templates", "manifest.json") + # Read as json - with open('templates/manifest.json') as file: + with open("templates/manifest.json") as file: manifest = json.load(file) - if host != 'localhost:5000' and host != '127.0.0.1:5000': - manifest['start_url'] = f'https://{host}/' + if host != "localhost:5000" and host != "127.0.0.1:5000": + manifest["start_url"] = f"https://{host}/" else: - manifest['start_url'] = 'http://127.0.0.1:5000/' + manifest["start_url"] = "http://127.0.0.1:5000/" return jsonify(manifest) -# region Sol Links -@app.route('/actions.json') -def actionsJson(): - return jsonify({ - "rules": [ - { - "pathPattern":"/donate**", - "apiPath":"/api/donate**" - } - ]}) -@app.route('/api/donate', methods=['GET','OPTIONS']) +# region Sol Links +@app.route("/actions.json") +def actionsJson(): + return jsonify( + {"rules": [{"pathPattern": "/donate**", "apiPath": "/api/donate**"}]} + ) + + +@app.route("/api/donate", methods=["GET", "OPTIONS"]) def donateAPI(): - + data = { "icon": "https://nathan.woodburn.au/assets/img/profile.png", "label": "Donate to Nathan.Woodburn/", @@ -197,41 +230,32 @@ def donateAPI(): "description": "Student, developer, and crypto enthusiast", "links": { "actions": [ - { - "label": "0.01 SOL", - "href": "/api/donate/0.01" - }, - { - "label": "0.1 SOL", - "href": "/api/donate/0.1" - }, - { - "label": "1 SOL", - "href": "/api/donate/1" - }, + {"label": "0.01 SOL", "href": "/api/donate/0.01"}, + {"label": "0.1 SOL", "href": "/api/donate/0.1"}, + {"label": "1 SOL", "href": "/api/donate/1"}, { "href": "/api/donate/{amount}", "label": "Donate", "parameters": [ - { - "name": "amount", - "label": "Enter a custom SOL amount" - } - ] - } + {"name": "amount", "label": "Enter a custom SOL amount"} + ], + }, ] - } + }, } - response = make_response(jsonify(data), 200, {'Content-Type': 'application/json'}) + response = make_response(jsonify(data), 200, {"Content-Type": "application/json"}) - if request.method == 'OPTIONS': - response.headers['Access-Control-Allow-Origin'] = '*' - response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS' - response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization,Content-Encoding,Accept-Encoding' + if request.method == "OPTIONS": + response.headers["Access-Control-Allow-Origin"] = "*" + response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, OPTIONS" + response.headers["Access-Control-Allow-Headers"] = ( + "Content-Type,Authorization,Content-Encoding,Accept-Encoding" + ) return response -@app.route('/api/donate/') + +@app.route("/api/donate/") def donateAmount(amount): data = { "icon": "https://nathan.woodburn.au/assets/img/profile.png", @@ -241,26 +265,31 @@ def donateAmount(amount): } return jsonify(data) -@app.route('/api/donate/', methods=['POST']) + +@app.route("/api/donate/", methods=["POST"]) def donateAmountPost(amount): if not request.json: - return jsonify({'message': 'Error: No JSON data provided'}) + return jsonify({"message": "Error: No JSON data provided"}) - if 'account' not in request.json: - return jsonify({'message': 'Error: No account provided'}) + if "account" not in request.json: + return jsonify({"message": "Error: No account provided"}) - sender = request.json['account'] + sender = request.json["account"] # Make sure amount is a number try: amount = float(amount) except: - return jsonify({'message': 'Error: Invalid amount'}) - + return jsonify({"message": "Error: Invalid amount"}) + # Create transaction sender = Pubkey.from_string(sender) - receiver = Pubkey.from_string('AJsPEEe6S7XSiVcdZKbeV8GRp1QuhFUsG8mLrqL4XgiU') - transfer_ix = transfer(TransferParams(from_pubkey=sender, to_pubkey=receiver, lamports=int(amount * 1000000000))) + receiver = Pubkey.from_string("AJsPEEe6S7XSiVcdZKbeV8GRp1QuhFUsG8mLrqL4XgiU") + transfer_ix = transfer( + TransferParams( + from_pubkey=sender, to_pubkey=receiver, lamports=int(amount * 1000000000) + ) + ) solana_client = Client("https://api.mainnet-beta.solana.com") blockhashData = solana_client.get_latest_blockhash() blockhash = blockhashData.value.blockhash @@ -274,88 +303,108 @@ def donateAmountPost(amount): tx = VersionedTransaction(message=msg, keypairs=[NullSigner(sender)]) tx = bytes(tx).hex() raw_bytes = binascii.unhexlify(tx) - base64_string = base64.b64encode(raw_bytes).decode('utf-8') + base64_string = base64.b64encode(raw_bytes).decode("utf-8") + + return jsonify({"message": "Success", "transaction": base64_string}) - return jsonify({'message': 'Success', 'transaction': base64_string}) # endregion +# endregion + # region Main routes -@app.route('/') +@app.route("/") def index(): # Check if host if podcast.woodburn.au if "podcast.woodburn.au" in request.host: - return render_template('podcast.html') - + return render_template("podcast.html") + loaded = False if request.referrer: # Check if referrer includes nathan.woodburn.au if "nathan.woodburn.au" in request.referrer: loaded = True - # Check if crawler - if request.user_agent.browser != 'Googlebot' and request.user_agent.browser != 'Bingbot': + if ( + request.user_agent.browser != "Googlebot" + and request.user_agent.browser != "Bingbot" + ): # Check if cookie is set - if not request.cookies.get('loaded') and not loaded: + if not request.cookies.get("loaded") and not loaded: # Set cookie - resp = make_response(render_template('loading.html'), 200, {'Content-Type': 'text/html'}) - resp.set_cookie('loaded', 'true', max_age=604800) + resp = make_response( + render_template("loading.html"), 200, {"Content-Type": "text/html"} + ) + resp.set_cookie("loaded", "true", max_age=604800) return resp - global handshake_scripts global projects global projectsUpdated try: - git=requests.get('https://git.woodburn.au/api/v1/users/nathanwoodburn/activities/feeds?only-performed-by=true&limit=1', - headers={'Authorization': os.getenv('git_token')}) + git = requests.get( + "https://git.woodburn.au/api/v1/users/nathanwoodburn/activities/feeds?only-performed-by=true&limit=1", + headers={"Authorization": os.getenv("git_token")}, + ) git = git.json() git = git[0] - repo_name=git['repo']['name'] - repo_name=repo_name.lower() - repo_description=git['repo']['description'] + repo_name = git["repo"]["name"] + repo_name = repo_name.lower() + repo_description = git["repo"]["description"] except: repo_name = "nathanwoodburn.github.io" repo_description = "Personal website" - git = {'repo': {'html_url': 'https://nathan.woodburn.au', 'name': 'nathanwoodburn.github.io', 'description': 'Personal website'}} + git = { + "repo": { + "html_url": "https://nathan.woodburn.au", + "name": "nathanwoodburn.github.io", + "description": "Personal website", + } + } print("Error getting git data") - + # Get only repo names for the newest updates - if projects == [] or projectsUpdated < datetime.datetime.now() - datetime.timedelta(hours=2): - projectsreq = requests.get('https://git.woodburn.au/api/v1/users/nathanwoodburn/repos') - + if projects == [] or projectsUpdated < datetime.datetime.now() - datetime.timedelta( + hours=2 + ): + projectsreq = requests.get( + "https://git.woodburn.au/api/v1/users/nathanwoodburn/repos" + ) + projects = projectsreq.json() # Check for next page pageNum = 1 - while 'rel="next"' in projectsreq.headers['link']: - projectsreq = requests.get('https://git.woodburn.au/api/v1/users/nathanwoodburn/repos?page=' + str(pageNum)) + while 'rel="next"' in projectsreq.headers["link"]: + projectsreq = requests.get( + "https://git.woodburn.au/api/v1/users/nathanwoodburn/repos?page=" + + str(pageNum) + ) projects += projectsreq.json() pageNum += 1 - - + for project in projects: - if project['avatar_url'] == 'https://git.woodburn.au/': - project['avatar_url'] = '/favicon.png' - project['name'] = project['name'].replace('_', ' ').replace('-', ' ') + if project["avatar_url"] == "https://git.woodburn.au/": + project["avatar_url"] = "/favicon.png" + project["name"] = project["name"].replace("_", " ").replace("-", " ") # Sort by last updated - projectsList = sorted(projects, key=lambda x: x['updated_at'], reverse=True) + projectsList = sorted(projects, key=lambda x: x["updated_at"], reverse=True) projects = [] projectNames = [] projectNum = 0 while len(projects) < 3: - if projectsList[projectNum]['name'] not in projectNames: + if projectsList[projectNum]["name"] not in projectNames: projects.append(projectsList[projectNum]) - projectNames.append(projectsList[projectNum]['name']) + projectNames.append(projectsList[projectNum]["name"]) projectNum += 1 projectsUpdated = datetime.datetime.now() custom = "" # Check for downtime - uptime = requests.get('https://uptime.woodburn.au/api/status-page/main/badge') - uptime = uptime.content.count(b'Up') > 1 + uptime = requests.get("https://uptime.woodburn.au/api/status-page/main/badge") + uptime = uptime.content.count(b"Up") > 1 if uptime: custom += "" @@ -365,128 +414,217 @@ def index(): if repo_name == "nathanwoodburn.github.io": repo_name = "Nathan.Woodburn/" - html_url=git['repo']['html_url'] - repo = "" + repo_name + "" + html_url = git["repo"]["html_url"] + repo = '' + repo_name + "" # If localhost, don't load handshake - if request.host == "localhost:5000" or request.host == "127.0.0.1:5000" or os.getenv('dev') == "true" or request.host == "test.nathan.woodburn.au": + if ( + request.host == "localhost:5000" + or request.host == "127.0.0.1:5000" + or os.getenv("dev") == "true" + or request.host == "test.nathan.woodburn.au" + ): handshake_scripts = "" + # Get time + timezone_offset = datetime.timedelta(hours=ncConfig["time-zone"]) + timezone = datetime.timezone(offset=timezone_offset) + time = datetime.datetime.now(tz=timezone) + + time = time.strftime("%B %d") + time += """ + + " + HNSaddress = getAddress("HNS") SOLaddress = getAddress("SOL") BTCaddress = getAddress("BTC") ETHaddress = getAddress("ETH") # Set cookie - resp = make_response(render_template('index.html', handshake_scripts=handshake_scripts, - HNS=HNSaddress, SOL=SOLaddress, BTC=BTCaddress, - ETH=ETHaddress, repo=repo, - repo_description=repo_description, - custom=custom,sites=sites,projects=projects), 200, {'Content-Type': 'text/html'}) - resp.set_cookie('loaded', 'true', max_age=604800) + resp = make_response( + render_template( + "index.html", + handshake_scripts=handshake_scripts, + HNS=HNSaddress, + SOL=SOLaddress, + BTC=BTCaddress, + ETH=ETHaddress, + repo=repo, + repo_description=repo_description, + custom=custom, + sites=sites, + projects=projects, + time=time, + ), + 200, + {"Content-Type": "text/html"}, + ) + resp.set_cookie("loaded", "true", max_age=604800) return resp # region Now Pages -@app.route('/now') -@app.route('/now/') +@app.route("/now") +@app.route("/now/") def now(): global handshake_scripts - - # If localhost, don't load handshake - if request.host == "localhost:5000" or request.host == "127.0.0.1:5000" or os.getenv('dev') == "true" or request.host == "test.nathan.woodburn.au": - handshake_scripts = "" - - # Get latest now page - files = os.listdir('templates/now') - # Remove template - files = [file for file in files if file != 'template.html' and file != 'old.html'] - files.sort(reverse=True) - date = files[0].strip('.html') - # Convert to date - date = datetime.datetime.strptime(date, '%y_%m_%d') - date = date.strftime('%A, %B %d, %Y') - return render_template('now/' + files[0], handshake_scripts=handshake_scripts, DATE=date) -@app.route('/now/') + # If localhost, don't load handshake + if ( + request.host == "localhost:5000" + or request.host == "127.0.0.1:5000" + or os.getenv("dev") == "true" + or request.host == "test.nathan.woodburn.au" + ): + handshake_scripts = "" + + # Get latest now page + files = os.listdir("templates/now") + # Remove template + files = [file for file in files if file != "template.html" and file != "old.html"] + files.sort(reverse=True) + date = files[0].strip(".html") + # Convert to date + date = datetime.datetime.strptime(date, "%y_%m_%d") + date = date.strftime("%A, %B %d, %Y") + return render_template( + "now/" + files[0], handshake_scripts=handshake_scripts, DATE=date + ) + + +@app.route("/now/") def now_path(path): global handshake_scripts # If localhost, don't load handshake - if request.host == "localhost:5000" or request.host == "127.0.0.1:5000" or os.getenv('dev') == "true" or request.host == "test.nathan.woodburn.au": + if ( + request.host == "localhost:5000" + or request.host == "127.0.0.1:5000" + or os.getenv("dev") == "true" + or request.host == "test.nathan.woodburn.au" + ): handshake_scripts = "" date = path - date = date.strip('.html') + date = date.strip(".html") try: # Convert to date - date = datetime.datetime.strptime(date, '%y_%m_%d') - date = date.strftime('%A, %B %d, %Y') + date = datetime.datetime.strptime(date, "%y_%m_%d") + date = date.strftime("%A, %B %d, %Y") except: date = "" - - if path.lower().replace('.html','') == 'template': - return render_template('404.html'), 404 + + if path.lower().replace(".html", "") == "template": + return render_template("404.html"), 404 # If file exists, load it - if os.path.isfile('templates/now/' + path): - return render_template('now/' + path, handshake_scripts=handshake_scripts, DATE=date) - if os.path.isfile('templates/now/' + path + '.html'): - return render_template('now/' + path + '.html', handshake_scripts=handshake_scripts, DATE=date) - - return render_template('404.html'), 404 + if os.path.isfile("templates/now/" + path): + return render_template( + "now/" + path, handshake_scripts=handshake_scripts, DATE=date + ) + if os.path.isfile("templates/now/" + path + ".html"): + return render_template( + "now/" + path + ".html", handshake_scripts=handshake_scripts, DATE=date + ) -@app.route('/old') -@app.route('/old/') -@app.route('/now/old') -@app.route('/now/old/') + return render_template("404.html"), 404 + + +@app.route("/old") +@app.route("/old/") +@app.route("/now/old") +@app.route("/now/old/") def now_old(): global handshake_scripts # If localhost, don't load handshake - if request.host == "localhost:5000" or request.host == "127.0.0.1:5000" or os.getenv('dev') == "true" or request.host == "test.nathan.woodburn.au": + if ( + request.host == "localhost:5000" + or request.host == "127.0.0.1:5000" + or os.getenv("dev") == "true" + or request.host == "test.nathan.woodburn.au" + ): handshake_scripts = "" - now_pages = os.listdir('templates/now') - now_pages = [page for page in now_pages if page != 'template.html' and page != 'old.html'] + now_pages = os.listdir("templates/now") + now_pages = [ + page for page in now_pages if page != "template.html" and page != "old.html" + ] now_pages.sort(reverse=True) html = '
    ' latest = " (Latest)" for page in now_pages: - link = page.strip('.html') - date = datetime.datetime.strptime(link, '%y_%m_%d') - date = date.strftime('%A, %B %d, %Y') + link = page.strip(".html") + date = datetime.datetime.strptime(link, "%y_%m_%d") + date = date.strftime("%A, %B %d, %Y") html += f'
  • {date}{latest}
  • ' latest = "" - html += '
' - return render_template('now/old.html', handshake_scripts=handshake_scripts,now_pages=html) + html += "" + return render_template( + "now/old.html", handshake_scripts=handshake_scripts, now_pages=html + ) + + # endregion -@app.route('/donate') +@app.route("/donate") def donate(): global handshake_scripts # If localhost, don't load handshake - if request.host == "localhost:5000" or request.host == "127.0.0.1:5000" or os.getenv('dev') == "true" or request.host == "test.nathan.woodburn.au": + if ( + request.host == "localhost:5000" + or request.host == "127.0.0.1:5000" + or os.getenv("dev") == "true" + or request.host == "test.nathan.woodburn.au" + ): handshake_scripts = "" - coinList = os.listdir('.well-known/wallets') - coinList = [file for file in coinList if file[0] != '.'] + coinList = os.listdir(".well-known/wallets") + coinList = [file for file in coinList if file[0] != "."] coinList.sort() tokenList = [] - with open('.well-known/wallets/.tokens') as file: + with open(".well-known/wallets/.tokens") as file: tokenList = file.read() tokenList = json.loads(tokenList) coinNames = {} - with open('.well-known/wallets/.coins') as file: + with open(".well-known/wallets/.coins") as file: coinNames = file.read() coinNames = json.loads(coinNames) - coins = '' - default_coins = ['btc', 'eth', 'hns','sol','xrp','ada','dot'] - + coins = "" + default_coins = ["btc", "eth", "hns", "sol", "xrp", "ada", "dot"] for file in coinList: if file in coinNames: @@ -495,44 +633,48 @@ def donate(): coins += f'{file}' for token in tokenList: - if token["chain"] != 'null': + if token["chain"] != "null": coins += f'' else: - coins += f'' + coins += f'' - crypto = request.args.get('c') + crypto = request.args.get("c") if not crypto: - instructions = '
Donate with cryptocurrency:
Select a coin from the dropdown above.' - return render_template('donate.html', handshake_scripts=handshake_scripts, coins=coins,default_coins=default_coins, crypto=instructions) + instructions = ( + "
Donate with cryptocurrency:
Select a coin from the dropdown above." + ) + return render_template( + "donate.html", + handshake_scripts=handshake_scripts, + coins=coins, + default_coins=default_coins, + crypto=instructions, + ) crypto = crypto.upper() - token = request.args.get('t') + token = request.args.get("t") if token: token = token.upper() for t in tokenList: - if t['symbol'].upper() == token and t['chain'].upper() == crypto: + if t["symbol"].upper() == token and t["chain"].upper() == crypto: token = t break if not isinstance(token, dict): - token = { - "name": "Unknown token", - "symbol": token, - "chain": crypto - } + token = {"name": "Unknown token", "symbol": token, "chain": crypto} - address = '' - domain = '' - cryptoHTML = '' + address = "" + domain = "" + cryptoHTML = "" - proof = '' - if os.path.isfile(f'.well-known/wallets/.{crypto}.proof'): + proof = "" + if os.path.isfile(f".well-known/wallets/.{crypto}.proof"): proof = f'Proof of ownership' - if os.path.isfile(f'.well-known/wallets/{crypto}'): - with open(f'.well-known/wallets/{crypto}') as file: + if os.path.isfile(f".well-known/wallets/{crypto}"): + with open(f".well-known/wallets/{crypto}") as file: address = file.read() if not token: - cryptoHTML += f'
Donate with {coinNames[crypto] if crypto in coinNames else crypto}:' + cryptoHTML += f"
Donate with {coinNames[crypto] if crypto in coinNames else crypto}:" else: cryptoHTML += f'
Donate with {token["name"]} {"("+token["symbol"]+") " if token["symbol"] != token["name"] else ""}on {crypto}:' cryptoHTML += f'
{address}' @@ -540,41 +682,47 @@ def donate(): if proof: cryptoHTML += proof elif token: - if 'address' in token: - address = token['address'] + if "address" in token: + address = token["address"] cryptoHTML += f'
Donate with {token["name"]} {"("+token["symbol"]+")" if token["symbol"] != token["name"] else ""}{" on "+crypto if crypto != "NULL" else ""}:' cryptoHTML += f'
{address}' if proof: cryptoHTML += proof else: - cryptoHTML += f'
Invalid coin: {crypto}
' + cryptoHTML += f"
Invalid coin: {crypto}
" else: - cryptoHTML += f'
Invalid coin: {crypto}
' - - + cryptoHTML += f"
Invalid coin: {crypto}
" - if os.path.isfile(f'.well-known/wallets/.domains'): + if os.path.isfile(f".well-known/wallets/.domains"): # Get json of all domains - with open(f'.well-known/wallets/.domains') as file: + with open(f".well-known/wallets/.domains") as file: domains = file.read() domains = json.loads(domains) - + if crypto in domains: domain = domains[crypto] - cryptoHTML += '
Or send to this domain on compatible wallets:
' + cryptoHTML += "
Or send to this domain on compatible wallets:
" cryptoHTML += f'{domain}' if address: - cryptoHTML += '
QR Code' - + cryptoHTML += ( + '
QR Code' + ) copyScript = '' cryptoHTML += copyScript - + return render_template( + "donate.html", + handshake_scripts=handshake_scripts, + crypto=cryptoHTML, + coins=coins, + default_coins=default_coins, + ) - return render_template('donate.html', handshake_scripts=handshake_scripts, crypto=cryptoHTML, coins=coins,default_coins=default_coins) -@app.route('/qrcode/') +@app.route("/qrcode/") def addressQR(data): qr = qrcode.QRCode( version=1, @@ -586,7 +734,6 @@ def addressQR(data): qr.make(fit=True) qr_image = qr.make_image(fill_color="#110033", back_color="white") - # Save the QR code image to a temporary file qr_image_path = "/tmp/qr_code.png" qr_image.save(qr_image_path) @@ -594,111 +741,147 @@ def addressQR(data): # Return the QR code image as a response return send_file(qr_image_path, mimetype="image/png") -@app.route('/supersecretpath') + +@app.route("/supersecretpath") def supersecretpath(): - ascii_art = '' - if os.path.isfile('data/ascii.txt'): - with open('data/ascii.txt') as file: + ascii_art = "" + if os.path.isfile("data/ascii.txt"): + with open("data/ascii.txt") as file: ascii_art = file.read() - + converter = Ansi2HTMLConverter() ascii_art_html = converter.convert(ascii_art) - return render_template('ascii.html', ascii_art=ascii_art_html) + return render_template("ascii.html", ascii_art=ascii_art_html) -@app.route('/') + +@app.route("/") def catch_all(path): global handshake_scripts # If localhost, don't load handshake - if request.host == "localhost:5000" or request.host == "127.0.0.1:5000" or os.getenv('dev') == "true" or request.host == "test.nathan.woodburn.au": + if ( + request.host == "localhost:5000" + or request.host == "127.0.0.1:5000" + or os.getenv("dev") == "true" + or request.host == "test.nathan.woodburn.au" + ): handshake_scripts = "" - if path.lower().replace('.html','') in restricted: - return render_template('404.html'), 404 + if path.lower().replace(".html", "") in restricted: + return render_template("404.html"), 404 # If file exists, load it - if os.path.isfile('templates/' + path): - return render_template(path, handshake_scripts=handshake_scripts,sites=sites) - + if os.path.isfile("templates/" + path): + return render_template(path, handshake_scripts=handshake_scripts, sites=sites) + # Try with .html - if os.path.isfile('templates/' + path + '.html'): - return render_template(path + '.html', handshake_scripts=handshake_scripts,sites=sites) + if os.path.isfile("templates/" + path + ".html"): + return render_template( + path + ".html", handshake_scripts=handshake_scripts, sites=sites + ) + + if os.path.isfile("templates/" + path.strip("/") + ".html"): + return render_template( + path.strip("/") + ".html", handshake_scripts=handshake_scripts, sites=sites + ) + + return render_template("404.html"), 404 - if os.path.isfile('templates/' + path.strip('/') + '.html'): - return render_template(path.strip('/') + '.html', handshake_scripts=handshake_scripts,sites=sites) - return render_template('404.html'), 404 # endregion -#region ACME -@app.route('/hnsdoh-acme', methods=['POST']) + +# region ACME +@app.route("/hnsdoh-acme", methods=["POST"]) def hnsdoh_acme(): # Get the TXT record from the request if not request.json: - return jsonify({'status': 'error', 'error': 'No JSON data provided'}) - if 'txt' not in request.json or 'auth' not in request.json: - return jsonify({'status': 'error', 'error': 'Missing required data'}) + return jsonify({"status": "error", "error": "No JSON data provided"}) + if "txt" not in request.json or "auth" not in request.json: + return jsonify({"status": "error", "error": "Missing required data"}) - txt = request.json['txt'] - auth = request.json['auth'] - if auth != os.getenv('CF_AUTH'): - return jsonify({'status': 'error', 'error': 'Invalid auth'}) + txt = request.json["txt"] + auth = request.json["auth"] + if auth != os.getenv("CF_AUTH"): + return jsonify({"status": "error", "error": "Invalid auth"}) + + cf = Cloudflare(api_token=os.getenv("CF_TOKEN")) + zone = cf.zones.get(params={"name": "hnsdoh.com"}) + zone_id = zone[0]["id"] + existing_records = cf.zones.dns_records.get( + zone_id, params={"type": "TXT", "name": "_acme-challenge.hnsdoh.com"} + ) - cf = Cloudflare(api_token=os.getenv('CF_TOKEN')) - zone = cf.zones.get(params={'name': 'hnsdoh.com'}) - zone_id = zone[0]['id'] - existing_records = cf.zones.dns_records.get(zone_id, params={'type': 'TXT', 'name': '_acme-challenge.hnsdoh.com'}) - # Delete existing TXT records for record in existing_records: print(record) - record_id = record['id'] + record_id = record["id"] cf.zones.dns_records.delete(zone_id, record_id) - - - - record = cf.zones.dns_records.post(zone_id, data={'type': 'TXT', 'name': '_acme-challenge', 'content': txt}) + record = cf.zones.dns_records.post( + zone_id, data={"type": "TXT", "name": "_acme-challenge", "content": txt} + ) print(record) - return jsonify({'status': 'success'}) -#endregion + return jsonify({"status": "success"}) -#region Podcast -@app.route('/ID1') + +# endregion + + +# region Podcast +@app.route("/ID1") def ID1(): # Proxy to ID1 url - req = requests.get('https://id1.woodburn.au/ID1') - return make_response(req.content, 200, {'Content-Type': req.headers['Content-Type']}) + req = requests.get("https://id1.woodburn.au/ID1") + return make_response( + req.content, 200, {"Content-Type": req.headers["Content-Type"]} + ) -@app.route('/ID1/') + +@app.route("/ID1/") def ID1_slash(): # Proxy to ID1 url - req = requests.get('https://id1.woodburn.au/ID1/') - return make_response(req.content, 200, {'Content-Type': req.headers['Content-Type']}) + req = requests.get("https://id1.woodburn.au/ID1/") + return make_response( + req.content, 200, {"Content-Type": req.headers["Content-Type"]} + ) -@app.route('/ID1/') + +@app.route("/ID1/") def ID1_path(path): # Proxy to ID1 url - req = requests.get('https://id1.woodburn.au/ID1/' + path) - return make_response(req.content, 200, {'Content-Type': req.headers['Content-Type']}) + req = requests.get("https://id1.woodburn.au/ID1/" + path) + return make_response( + req.content, 200, {"Content-Type": req.headers["Content-Type"]} + ) -@app.route('/ID1.xml') + +@app.route("/ID1.xml") def ID1_xml(): # Proxy to ID1 url - req = requests.get('https://id1.woodburn.au/ID1.xml') - return make_response(req.content, 200, {'Content-Type': req.headers['Content-Type']}) + req = requests.get("https://id1.woodburn.au/ID1.xml") + return make_response( + req.content, 200, {"Content-Type": req.headers["Content-Type"]} + ) -@app.route('/podsync.opml') + +@app.route("/podsync.opml") def podsync(): - req = requests.get('https://id1.woodburn.au/podsync.opml') - return make_response(req.content, 200, {'Content-Type': req.headers['Content-Type']}) -#endregion + req = requests.get("https://id1.woodburn.au/podsync.opml") + return make_response( + req.content, 200, {"Content-Type": req.headers["Content-Type"]} + ) -#region Error Catching +# endregion + + +# region Error Catching # 404 catch all @app.errorhandler(404) def not_found(e): - return render_template('404.html'), 404 -#endregion + return render_template("404.html"), 404 -if __name__ == '__main__': - app.run(debug=True, port=5000, host='0.0.0.0') \ No newline at end of file + +# endregion + +if __name__ == "__main__": + app.run(debug=True, port=5000, host="0.0.0.0") diff --git a/templates/assets/css/index.min.css b/templates/assets/css/index.min.css index 8685c12..4f0fb97 100644 --- a/templates/assets/css/index.min.css +++ b/templates/assets/css/index.min.css @@ -1 +1 @@ -#sites{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;padding:60px;font-family:Quicksand,sans-serif}.site-container{background:rgba(133,133,133,.2);box-shadow:0 4px 30px rgba(0,0,0,.1);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.2);border-radius:25px;padding:30px 0;width:min(1200px,100%)}.site-container>h1{font-size:2rem;font-weight:600;text-align:center;color:#dda3b6;margin:20px 0 40px}.swiper{width:80%;height:100%;margin-bottom:30px}.swiper-scrollbar{--swiper-scrollbar-bottom:0px;--swiper-scrollbar-drag-bg-color:#dda3b6;--swiper-scrollbar-size:5px}.site{position:relative;max-width:400px;padding:1rem;font-family:inherit;font-size:1rem;font-weight:500;color:var(--clr-text);background-color:transparent;border-radius:10px;isolation:isolate;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.site::before{content:"";position:absolute;top:0;left:0;right:0;bottom:15px;background:rgba(236,149,200,.2);box-shadow:0 4px 30px rgba(0,0,0,.1);border-radius:10px;z-index:-1}.site-img{width:100%;max-width:400px;object-fit:cover;overflow:hidden;aspect-ratio:1;border-radius:6px}.site-body{align-items:center;gap:8px;padding:15px 0;cursor:default}.site-name{font-size:.9rem;font-weight:600;margin-bottom:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.site-author{width:fit-content;font-size:.8rem;font-weight:600;opacity:.6;color:var(--clr-text)}.site-avatar{width:40px;aspect-ratio:1/1;object-fit:cover;border-radius:5px;cursor:pointer}.site-actions{position:relative}.site-actions-content{position:absolute;bottom:130%;right:0;padding:8px;border-radius:8px;background:rgba(172,172,172,.2);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);box-shadow:2px 2px 10px 2px hsl(0,0%,0%,.25);transition:opacity .25s,scale .25s;transform-origin:bottom right}.site-actions-content[data-visible=false]{pointer-events:none;opacity:0;scale:0}.site-actions-content[data-visible=true]{pointer-events:unset;scale:1;opacity:1}.site-actions-content li{padding:.5rem .65rem;border-radius:.25rem;list-style:none}.site-actions-content li:is(:hover,:focus-within){background-color:rgba(248,132,169,.7)}.site-actions-link{width:max-content;display:grid;grid-template-columns:1rem 1fr;align-items:center;gap:.6rem;color:inherit;text-decoration:none;cursor:pointer}.site-like{text-decoration:none;color:var(--clr-text);margin-right:5px;font-size:1.1rem;opacity:.65;border-radius:50%;overflow:hidden;transition:.35s}.site-actions-controller{border:0;background:0 0;color:var(--clr-text);cursor:pointer;opacity:.65}.site-actions-controller:hover,.site-like:hover{opacity:1}.site-like:focus{outline:0}.site-like.active{color:red;opacity:1;transform:scale(1.2)}@media (max-width:1200px){.swiper{width:80%}}@media (max-width:900px){#sites{padding:60px 80px}.swiper{width:50%}}@media (max-width:765px){.swiper{width:70%}}@media (max-width:550px){#sites{padding:40px}.swiper{width:80%}}img.no-drag{pointer-events:none}img.fog{pointer-events:none;position:absolute;left:0;top:0;width:100%;height:100%}#downtime{z-index:2;position:fixed;right:0;bottom:0;width:20%;transition:opacity .5s;opacity:0;cursor:pointer}blockquote.speech{position:absolute;display:inline-block;right:14vw;bottom:23vh;width:17vw;background:url(/assets/img/speech-bubble.svg) center;color:#000;padding-top:3%;padding-bottom:20%;background-repeat:no-repeat!important;margin:0 auto;text-align:center;box-sizing:content-box;line-height:1;font-family:SequentialistBB,cursive;font-size:1.2vw} \ No newline at end of file +#sites{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;padding:60px;font-family:Quicksand,sans-serif}.site-container{background:rgba(133,133,133,.2);box-shadow:0 4px 30px rgba(0,0,0,.1);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.2);border-radius:25px;padding:30px 0;width:min(1200px,100%)}.site-container>h1{font-size:2rem;font-weight:600;text-align:center;color:#dda3b6;margin:20px 0 40px}.swiper{width:80%;height:100%;margin-bottom:30px}.swiper-scrollbar{--swiper-scrollbar-bottom:0px;--swiper-scrollbar-drag-bg-color:#dda3b6;--swiper-scrollbar-size:5px}.site{position:relative;max-width:400px;padding:1rem;font-family:inherit;font-size:1rem;font-weight:500;color:var(--clr-text);background-color:transparent;border-radius:10px;isolation:isolate;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.site::before{content:"";position:absolute;top:0;left:0;right:0;bottom:15px;background:rgba(236,149,200,.2);box-shadow:0 4px 30px rgba(0,0,0,.1);border-radius:10px;z-index:-1}.site-img{width:100%;max-width:400px;object-fit:cover;overflow:hidden;aspect-ratio:1;border-radius:6px}.site-body{align-items:center;gap:8px;padding:15px 0;cursor:default}.site-name{font-size:.9rem;font-weight:600;margin-bottom:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.site-author{width:fit-content;font-size:.8rem;font-weight:600;opacity:.6;color:var(--clr-text)}.site-avatar{width:40px;aspect-ratio:1/1;object-fit:cover;border-radius:5px;cursor:pointer}.site-actions{position:relative}.site-actions-content{position:absolute;bottom:130%;right:0;padding:8px;border-radius:8px;background:rgba(172,172,172,.2);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);box-shadow:2px 2px 10px 2px hsl(0,0%,0%,.25);transition:opacity .25s,scale .25s;transform-origin:bottom right}.site-actions-content[data-visible=false]{pointer-events:none;opacity:0;scale:0}.site-actions-content[data-visible=true]{pointer-events:unset;scale:1;opacity:1}.site-actions-content li{padding:.5rem .65rem;border-radius:.25rem;list-style:none}.site-actions-content li:is(:hover,:focus-within){background-color:rgba(248,132,169,.7)}.site-actions-link{width:max-content;display:grid;grid-template-columns:1rem 1fr;align-items:center;gap:.6rem;color:inherit;text-decoration:none;cursor:pointer}.site-like{text-decoration:none;color:var(--clr-text);margin-right:5px;font-size:1.1rem;opacity:.65;border-radius:50%;overflow:hidden;transition:.35s}.site-actions-controller{border:0;background:0 0;color:var(--clr-text);cursor:pointer;opacity:.65}.site-actions-controller:hover,.site-like:hover{opacity:1}.site-like:focus{outline:0}.site-like.active{color:red;opacity:1;transform:scale(1.2)}@media (max-width:1200px){.swiper{width:80%}}@media (max-width:900px){#sites{padding:60px 80px}.swiper{width:50%}}@media (max-width:765px){.swiper{width:70%}}@media (max-width:550px){#sites{padding:40px}.swiper{width:80%}}img.no-drag{pointer-events:none}img.fog{pointer-events:none;position:absolute;left:0;top:0;width:100%;height:100%}#downtime{z-index:2;position:fixed;right:0;bottom:0;width:20%;transition:opacity .5s;opacity:0;cursor:pointer}blockquote.speech{position:absolute;display:inline-block;right:14vw;bottom:23vh;width:17vw;background:url(/assets/img/speech-bubble.svg) center;color:#000;padding-top:3%;padding-bottom:20%;background-repeat:no-repeat!important;margin:0 auto;text-align:center;box-sizing:content-box;line-height:1;font-family:SequentialistBB,cursive;font-size:1.2vw}.clock{bottom:0;position:sticky} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index bf848fa..5daf161 100644 --- a/templates/index.html +++ b/templates/index.html @@ -224,6 +224,10 @@ Check them out here! + + + {{time|safe}}