Nathanwoodburn.github.io/server.py

1071 lines
34 KiB
Python
Raw Normal View History

2024-04-15 21:01:28 +10:00
import json
2024-08-15 13:43:35 +10:00
from flask import (
Flask,
make_response,
redirect,
request,
jsonify,
render_template,
send_from_directory,
send_file,
)
2024-02-28 17:36:18 +11:00
from flask_cors import CORS
2023-11-02 20:26:15 +11:00
import os
import dotenv
2023-11-02 21:51:49 +11:00
import requests
2024-07-01 13:17:50 +10:00
from cloudflare import Cloudflare
2024-02-23 17:27:38 +11:00
import datetime
2024-04-19 11:28:07 +10:00
import qrcode
import re
2024-07-03 14:59:30 +10:00
import binascii
import base64
2024-04-19 11:28:07 +10:00
from ansi2html import Ansi2HTMLConverter
from functools import cache
2024-07-03 14:59:30 +10:00
from solders.keypair import Keypair
from solders.pubkey import Pubkey
from solana.rpc.api import Client
from solders.system_program import TransferParams, transfer
from solana.transaction import Transaction
from solders.hash import Hash
from solders.message import MessageV0
from solders.transaction import VersionedTransaction
from solders.null_signer import NullSigner
2024-10-09 16:50:40 +11:00
from PIL import Image
from mail import sendEmail
import now
2023-11-02 20:26:15 +11:00
app = Flask(__name__)
2024-07-10 19:52:43 +10:00
CORS(app)
2024-02-28 17:36:18 +11:00
2023-11-02 20:26:15 +11:00
dotenv.load_dotenv()
handshake_scripts = '<script src="https://nathan.woodburn/handshake.js" domain="nathan.woodburn" async></script><script src="https://nathan.woodburn/https.js" async></script>'
2024-08-15 13:43:35 +10:00
restricted = ["ascii"]
2024-09-27 13:34:26 +10:00
redirects = {
"contact":"/#contact"
}
downloads = {
"pgp": "data/nathanwoodburn.asc"
}
2024-04-19 11:28:07 +10:00
sites = []
2024-08-15 13:43:35 +10:00
if os.path.isfile("data/sites.json"):
with open("data/sites.json") as file:
2024-04-19 11:28:07 +10:00
sites = json.load(file)
# Remove any sites that are not enabled
2024-08-15 13:43:35 +10:00
sites = [
site for site in sites if "enabled" not in site or site["enabled"] == True
]
projects = []
projectsUpdated = 0
2024-08-15 13:43:35 +10:00
ncConfig = requests.get(
"https://cloud.woodburn.au/s/4ToXgFe3TnnFcN7/download/website-conf.json"
)
ncConfig = ncConfig.json()
@cache
2024-08-15 13:43:35 +10:00
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
2023-11-02 21:06:43 +11:00
def find(name, path):
for root, dirs, files in os.walk(path):
if name in files:
return os.path.join(root, name)
2024-08-15 13:43:35 +10:00
# Assets routes
@app.route("/assets/<path:path>")
2023-11-02 20:26:15 +11:00
def send_report(path):
2024-08-15 13:43:35 +10:00
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)
2024-02-18 12:51:59 +11:00
# Custom matching for images
pathMap = {
"img/hns/w": "img/external/HNS/white",
"img/hns/b": "img/external/HNS/black",
"img/hns": "img/external/HNS/black",
}
for key in pathMap:
print(path, key)
if path.startswith(key):
tmpPath = path.replace(key, pathMap[key])
print(tmpPath)
2024-08-15 13:43:35 +10:00
if os.path.isfile("templates/assets/" + tmpPath):
return send_from_directory("templates/assets", tmpPath)
# Try looking in one of the directories
2024-08-15 13:43:35 +10:00
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("/meet")
@app.route("/meeting")
@app.route("/appointment")
def meet():
2024-08-20 13:02:45 +10:00
return redirect(
"https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr", code=302
)
2024-08-15 13:43:35 +10:00
@app.route("/links")
2023-11-02 20:45:36 +11:00
def links():
2024-08-15 13:43:35 +10:00
return render_template("link.html")
2023-11-02 20:45:36 +11:00
2024-08-15 13:43:35 +10:00
@app.route("/sitemap")
@app.route("/sitemap.xml")
2023-11-02 20:45:36 +11:00
def sitemap():
# Remove all .html from sitemap
2024-08-15 13:43:35 +10:00
with open("templates/sitemap.xml") as file:
2023-11-02 20:45:36 +11:00
sitemap = file.read()
2024-08-15 13:43:35 +10:00
sitemap = sitemap.replace(".html", "")
return make_response(sitemap, 200, {"Content-Type": "application/xml"})
2023-11-02 20:45:36 +11:00
2024-08-15 13:43:35 +10:00
@app.route("/favicon.png")
2023-11-02 20:45:36 +11:00
def faviconPNG():
2024-08-15 13:43:35 +10:00
return send_from_directory("templates/assets/img/favicon", "favicon.png")
2023-11-02 20:45:36 +11:00
2024-08-15 13:43:35 +10:00
@app.route("/favicon.svg")
2023-11-02 20:45:36 +11:00
def faviconSVG():
2024-08-15 13:43:35 +10:00
return send_from_directory("templates/assets/img/favicon", "favicon.svg")
2024-10-05 20:04:38 +10:00
@app.route("/favicon.ico")
def faviconICO():
return send_from_directory("templates/assets/img/favicon", "favicon.ico")
2023-11-02 20:45:36 +11:00
2024-08-15 13:43:35 +10:00
@app.route("/https.js")
@app.route("/handshake.js")
@app.route("/redirect.js")
def handshake():
# return request.path
2024-08-15 13:43:35 +10:00
return send_from_directory("templates/assets/js", request.path.split("/")[-1])
2024-08-15 13:43:35 +10:00
@app.route("/generator/")
2023-11-02 21:37:18 +11:00
def removeTrailingSlash():
2024-08-15 13:43:35 +10:00
return render_template(request.path.split("/")[-2] + ".html")
2023-11-02 20:45:36 +11:00
2024-08-15 13:43:35 +10:00
@app.route("/.well-known/wallets/<path:path>")
2023-11-02 21:51:49 +11:00
def wallet(path):
2024-04-16 12:24:10 +10:00
if path[0] == ".":
2024-08-15 13:43:35 +10:00
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:
2024-04-15 21:01:28 +10:00
address = file.read()
address = address.strip()
2024-08-15 13:43:35 +10:00
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)
2023-11-02 21:51:49 +11:00
2024-08-15 13:43:35 +10:00
return render_template("404.html"), 404
2024-04-16 12:24:10 +10:00
2024-04-21 12:34:47 +10:00
2024-08-15 13:43:35 +10:00
@app.route("/.well-known/nostr.json")
2024-04-21 12:34:47 +10:00
def nostr():
# Get name parameter
2024-08-15 13:43:35 +10:00
name = request.args.get("name")
if name:
return jsonify(
{
"names": {
name: "b57b6a06fdf0a4095eba69eee26e2bf6fa72bd1ce6cbe9a6f72a7021c7acaa82"
}
}
)
2024-08-15 13:43:35 +10:00
return jsonify(
{
"names": {
"nathan": "b57b6a06fdf0a4095eba69eee26e2bf6fa72bd1ce6cbe9a6f72a7021c7acaa82",
"_": "b57b6a06fdf0a4095eba69eee26e2bf6fa72bd1ce6cbe9a6f72a7021c7acaa82",
2024-08-15 13:43:35 +10:00
}
2024-04-21 12:34:47 +10:00
}
2024-08-15 13:43:35 +10:00
)
2023-11-02 21:51:49 +11:00
2024-08-15 13:43:35 +10:00
@app.route("/.well-known/xrp-ledger.toml")
2024-07-02 13:48:16 +10:00
def xrpLedger():
# Create a response with the xrp-ledger.toml file
2024-08-15 13:43:35 +10:00
with open(".well-known/xrp-ledger.toml") as file:
2024-07-02 13:48:16 +10:00
toml = file.read()
2024-08-15 13:43:35 +10:00
response = make_response(toml, 200, {"Content-Type": "application/toml"})
2024-07-02 13:48:16 +10:00
# Set cors headers
2024-08-15 13:43:35 +10:00
response.headers["Access-Control-Allow-Origin"] = "*"
2024-07-02 13:48:16 +10:00
return response
2024-08-15 13:43:35 +10:00
@app.route("/manifest.json")
def manifest():
host = request.host
2024-08-15 13:43:35 +10:00
if host == "nathan.woodburn.au":
return send_from_directory("templates", "manifest.json")
# Read as json
2024-08-15 13:43:35 +10:00
with open("templates/manifest.json") as file:
manifest = json.load(file)
2024-08-15 13:43:35 +10:00
if host != "localhost:5000" and host != "127.0.0.1:5000":
manifest["start_url"] = f"https://{host}/"
else:
2024-08-15 13:43:35 +10:00
manifest["start_url"] = "http://127.0.0.1:5000/"
return jsonify(manifest)
2024-08-15 13:43:35 +10:00
2024-07-03 14:59:30 +10:00
# region Sol Links
2024-08-15 13:43:35 +10:00
@app.route("/actions.json")
2024-07-03 14:59:30 +10:00
def actionsJson():
2024-08-15 13:43:35 +10:00
return jsonify(
{"rules": [{"pathPattern": "/donate**", "apiPath": "/api/donate**"}]}
)
2024-07-03 14:59:30 +10:00
2024-08-15 13:43:35 +10:00
@app.route("/api/donate", methods=["GET", "OPTIONS"])
2024-07-03 14:59:30 +10:00
def donateAPI():
2024-08-15 13:43:35 +10:00
2024-07-03 14:59:30 +10:00
data = {
"icon": "https://nathan.woodburn.au/assets/img/profile.png",
2024-07-10 17:59:47 +10:00
"label": "Donate to Nathan.Woodburn/",
2024-07-03 14:59:30 +10:00
"title": "Donate to Nathan.Woodburn/",
"description": "Student, developer, and crypto enthusiast",
"links": {
"actions": [
2024-08-15 13:43:35 +10:00
{"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"},
2024-07-03 14:59:30 +10:00
{
"href": "/api/donate/{amount}",
"label": "Donate",
"parameters": [
2024-08-15 13:43:35 +10:00
{"name": "amount", "label": "Enter a custom SOL amount"}
],
},
2024-07-03 14:59:30 +10:00
]
2024-08-15 13:43:35 +10:00
},
2024-07-03 14:59:30 +10:00
}
headers = {
"Content-Type": "application/json",
"X-Action-Version": "2.4.2",
"X-Blockchain-Ids": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
}
response = make_response(jsonify(data), 200, headers)
2024-07-10 19:52:43 +10:00
2024-08-15 13:43:35 +10:00
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,X-Action-Version,X-Blockchain-Ids"
2024-08-15 13:43:35 +10:00
)
2024-07-10 19:52:43 +10:00
return response
2024-07-03 14:59:30 +10:00
2024-08-15 13:43:35 +10:00
@app.route("/api/donate/<amount>")
2024-07-03 14:59:30 +10:00
def donateAmount(amount):
data = {
"icon": "https://nathan.woodburn.au/assets/img/profile.png",
2024-07-10 17:59:47 +10:00
"label": f"Donate {amount} SOL to Nathan.Woodburn/",
2024-07-03 14:59:30 +10:00
"title": "Donate to Nathan.Woodburn/",
"description": f"Donate {amount} SOL to Nathan.Woodburn/",
2024-07-03 14:59:30 +10:00
}
return jsonify(data)
2024-08-15 13:43:35 +10:00
@app.route("/api/donate/<amount>", methods=["POST"])
2024-07-03 14:59:30 +10:00
def donateAmountPost(amount):
if not request.json:
2024-08-15 13:43:35 +10:00
return jsonify({"message": "Error: No JSON data provided"})
2024-07-03 14:59:30 +10:00
2024-08-15 13:43:35 +10:00
if "account" not in request.json:
return jsonify({"message": "Error: No account provided"})
2024-07-03 14:59:30 +10:00
2024-08-15 13:43:35 +10:00
sender = request.json["account"]
2024-07-03 14:59:30 +10:00
headers = {
"Content-Type": "application/json",
"X-Action-Version": "2.4.2",
"X-Blockchain-Ids": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
}
2024-07-03 14:59:30 +10:00
# Make sure amount is a number
try:
amount = float(amount)
except:
return jsonify({"message": "Error: Invalid amount"}), 400, headers
if amount < 0.0001:
return jsonify({"message": "Error: Amount too small"}), 400, headers
2024-08-15 13:43:35 +10:00
2024-07-03 14:59:30 +10:00
# Create transaction
sender = Pubkey.from_string(sender)
2024-08-15 13:43:35 +10:00
receiver = Pubkey.from_string("AJsPEEe6S7XSiVcdZKbeV8GRp1QuhFUsG8mLrqL4XgiU")
transfer_ix = transfer(
TransferParams(
from_pubkey=sender, to_pubkey=receiver, lamports=int(amount * 1000000000)
)
)
2024-07-03 14:59:30 +10:00
solana_client = Client("https://api.mainnet-beta.solana.com")
blockhashData = solana_client.get_latest_blockhash()
blockhash = blockhashData.value.blockhash
2024-07-03 14:59:30 +10:00
msg = MessageV0.try_compile(
payer=sender,
instructions=[transfer_ix],
address_lookup_table_accounts=[],
recent_blockhash=blockhash,
2024-07-03 14:59:30 +10:00
)
tx = VersionedTransaction(message=msg, keypairs=[NullSigner(sender)])
tx = bytes(tx).hex()
raw_bytes = binascii.unhexlify(tx)
2024-08-15 13:43:35 +10:00
base64_string = base64.b64encode(raw_bytes).decode("utf-8")
return jsonify({"message": "Success", "transaction": base64_string}), 200, headers
2024-07-03 14:59:30 +10:00
2024-07-10 17:59:47 +10:00
2024-07-03 14:59:30 +10:00
# endregion
#region Other API routes
@app.route("/api/time")
def time():
timezone_offset = datetime.timedelta(hours=ncConfig["time-zone"])
timezone = datetime.timezone(offset=timezone_offset)
time = datetime.datetime.now(tz=timezone)
return jsonify({
"timestring": time.strftime("%A, %B %d, %Y %I:%M %p"),
"timestamp": time.timestamp(),
"timezone": ncConfig["time-zone"],
"timeISO": time.isoformat()
})
@app.route("/api/timezone")
def timezone():
return jsonify({"timezone": ncConfig["time-zone"]})
@app.route("/api/timezone", methods=["POST"])
def timezonePost():
# Refresh config
global ncConfig
conf = requests.get("https://cloud.woodburn.au/s/4ToXgFe3TnnFcN7/download/website-conf.json")
if conf.status_code != 200:
return jsonify({"message": "Error: Could not get timezone"})
if not conf.json():
return jsonify({"message": "Error: Could not get timezone"})
conf = conf.json()
if "time-zone" not in conf:
return jsonify({"message": "Error: Could not get timezone"})
ncConfig = conf
return jsonify({"message": "Successfully pulled latest timezone", "timezone": ncConfig["time-zone"]})
@app.route("/api/message")
def nc():
return jsonify({"message": ncConfig["message"]})
@app.route("/api/ip")
def ip():
return jsonify({"ip": request.remote_addr})
@app.route("/api/email", methods=["POST"])
def email():
# Verify json
if not request.is_json:
return jsonify({
"status": 400,
"error": "Bad request JSON Data missing"
})
2024-12-19 12:33:56 +11:00
# Check if api key sent
data = request.json
if "key" not in data:
return jsonify({
"status": 401,
"error": "Unauthorized 'key' missing"
})
2024-12-19 12:33:56 +11:00
if data["key"] != os.getenv("EMAIL_KEY"):
return jsonify({
"status": 401,
"error": "Unauthorized 'key' invalid"
})
return sendEmail(data)
#endregion
2024-08-15 13:43:35 +10:00
# endregion
# region Main routes
2024-08-15 13:43:35 +10:00
@app.route("/")
2024-12-19 12:33:56 +11:00
def index():
global handshake_scripts
global projects
global projectsUpdated
2024-05-19 21:53:10 +10:00
# Check if host if podcast.woodburn.au
if "podcast.woodburn.au" in request.host:
2024-08-15 13:43:35 +10:00
return render_template("podcast.html")
loaded = False
2024-06-17 21:47:44 +10:00
if request.referrer:
# Check if referrer includes nathan.woodburn.au
if "nathan.woodburn.au" in request.referrer:
loaded = True
2024-06-17 21:47:44 +10:00
# Check if crawler
2024-12-19 12:33:56 +11:00
if request.headers and request.headers.get("User-Agent"):
# Check if curl
if "curl" in request.headers.get("User-Agent"):
return jsonify(
{
"message": "Welcome to Nathan.Woodburn/! This is a personal website. For more information, visit https://nathan.woodburn.au",
"ip": request.remote_addr,
"dev": handshake_scripts == "",
}
)
2024-10-08 19:03:02 +11:00
if "Googlebot" not in request.headers.get(
"User-Agent"
) and "Bingbot" not in request.headers.get("User-Agent"):
# Check if cookie is set
if not request.cookies.get("loaded") and not loaded:
# Set cookie
resp = make_response(
render_template("loading.html").replace(
"https://nathan.woodburn.au/loading", "https://nathan.woodburn.au/"
),
200,
{"Content-Type": "text/html"},
)
resp.set_cookie("loaded", "true", max_age=604800)
return resp
2024-12-19 12:33:56 +11:00
2024-04-15 21:01:28 +10:00
try:
2024-08-15 13:43:35 +10:00
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")},
)
2024-04-15 21:01:28 +10:00
git = git.json()
git = git[0]
2024-08-15 13:43:35 +10:00
repo_name = git["repo"]["name"]
repo_name = repo_name.lower()
repo_description = git["repo"]["description"]
2024-04-15 21:01:28 +10:00
except:
repo_name = "nathanwoodburn.github.io"
repo_description = "Personal website"
2024-08-15 13:43:35 +10:00
git = {
"repo": {
"html_url": "https://nathan.woodburn.au",
"name": "nathanwoodburn.github.io",
"description": "Personal website",
}
}
print("Error getting git data")
2024-08-15 13:43:35 +10:00
# Get only repo names for the newest updates
2024-08-15 13:43:35 +10:00
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
2024-08-15 13:43:35 +10:00
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
2024-08-15 13:43:35 +10:00
for project in projects:
if (
project["avatar_url"] == "https://git.woodburn.au/"
or project["avatar_url"] == ""
):
2024-08-15 13:43:35 +10:00
project["avatar_url"] = "/favicon.png"
project["name"] = project["name"].replace("_", " ").replace("-", " ")
# Sort by last updated
2024-08-15 13:43:35 +10:00
projectsList = sorted(projects, key=lambda x: x["updated_at"], reverse=True)
projects = []
projectNames = []
projectNum = 0
while len(projects) < 3:
2024-08-15 13:43:35 +10:00
if projectsList[projectNum]["name"] not in projectNames:
projects.append(projectsList[projectNum])
2024-08-15 13:43:35 +10:00
projectNames.append(projectsList[projectNum]["name"])
projectNum += 1
projectsUpdated = datetime.datetime.now()
2024-06-19 13:16:30 +10:00
custom = ""
2023-11-03 14:11:34 +11:00
# Check for downtime
2024-08-15 13:43:35 +10:00
uptime = requests.get("https://uptime.woodburn.au/api/status-page/main/badge")
uptime = uptime.content.count(b"Up") > 1
2023-11-03 14:11:34 +11:00
if uptime:
custom += "<style>#downtime{display:none !important;}</style>"
else:
custom += "<style>#downtime{opacity:1;}</style>"
2023-11-03 11:51:10 +11:00
# Special names
if repo_name == "nathanwoodburn.github.io":
repo_name = "Nathan.Woodburn/"
2024-08-15 13:43:35 +10:00
html_url = git["repo"]["html_url"]
repo = '<a href="' + html_url + '" target="_blank">' + repo_name + "</a>"
2023-11-02 20:26:15 +11:00
# If localhost, don't load handshake
2024-08-15 13:43:35 +10:00
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"
):
2023-11-02 20:26:15 +11:00
handshake_scripts = ""
2023-11-02 22:12:15 +11:00
2024-08-15 13:43:35 +10:00
# 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 += """
<span id=\"time\"></span>
<script>
function startClock(timezoneOffset) {
function updateClock() {
const now = new Date();
const localTime = new Date(now.getTime() + timezoneOffset * 3600 * 1000);
const tzName = timezoneOffset >= 0 ? `UTC+${timezoneOffset}` : `UTC`;
const hours = String(localTime.getUTCHours()).padStart(2, '0');
const minutes = String(localTime.getUTCMinutes()).padStart(2, '0');
const seconds = String(localTime.getUTCSeconds()).padStart(2, '0');
const timeString = `${hours}:${minutes}:${seconds} ${tzName}`;
document.getElementById('time').textContent = timeString;
}
updateClock();
setInterval(updateClock, 1000);
}
"""
time += f"startClock({ncConfig['time-zone']});"
time += "</script>"
HNSaddress = getAddress("HNS")
SOLaddress = getAddress("SOL")
BTCaddress = getAddress("BTC")
ETHaddress = getAddress("ETH")
2023-11-02 22:12:15 +11:00
# Set cookie
2024-08-15 13:43:35 +10:00
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,
message=ncConfig["message"],
2024-08-15 13:43:35 +10:00
),
200,
{"Content-Type": "text/html"},
)
resp.set_cookie("loaded", "true", max_age=604800)
2023-11-02 22:12:15 +11:00
return resp
2023-11-02 20:26:15 +11:00
# region Now Pages
2024-08-15 13:43:35 +10:00
@app.route("/now")
@app.route("/now/")
def now_page():
global handshake_scripts
2024-08-15 13:43:35 +10:00
2024-02-18 21:47:35 +11:00
# If localhost, don't load handshake
2024-08-15 13:43:35 +10:00
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"
):
2024-02-18 21:47:35 +11:00
handshake_scripts = ""
2024-08-15 13:43:35 +10:00
return now.render_latest_now(handshake_scripts)
2024-08-15 13:43:35 +10:00
2024-02-23 17:27:38 +11:00
2024-08-15 13:43:35 +10:00
@app.route("/now/<path:path>")
def now_path(path):
global handshake_scripts
# If localhost, don't load handshake
2024-08-15 13:43:35 +10:00
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 = ""
2024-12-19 12:33:56 +11:00
2024-02-23 17:27:38 +11:00
return now.render_now_page(path,handshake_scripts)
2024-08-15 13:43:35 +10:00
@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
2024-08-15 13:43:35 +10:00
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 = ""
2024-12-19 12:33:56 +11:00
now_dates = now.list_now_dates()[1:]
html = '<ul class="list-group">'
html += f'<a style="text-decoration:none;" href="/now"><li class="list-group-item" style="background-color:#000000;color:#ffffff;">{now.get_latest_now_date(True)}</li></a>'
2024-12-19 12:33:56 +11:00
for date in now_dates:
link = date
date = datetime.datetime.strptime(date, "%y_%m_%d")
2024-08-15 13:43:35 +10:00
date = date.strftime("%A, %B %d, %Y")
html += f'<a style="text-decoration:none;" href="/now/{link}"><li class="list-group-item" style="background-color:#000000;color:#ffffff;">{date}</li></a>'
2024-08-15 13:43:35 +10:00
html += "</ul>"
return render_template(
"now/old.html", handshake_scripts=handshake_scripts, now_pages=html
)
2024-10-07 17:39:25 +11:00
@app.route("/now.rss")
2024-11-12 14:08:30 +11:00
@app.route("/now.xml")
@app.route("/rss.xml")
2024-10-07 17:39:25 +11:00
def now_rss():
host = "https://" + request.host
if ":" in request.host:
host = "http://" + request.host
2024-10-07 17:39:25 +11:00
# Generate RSS feed
now_pages = now.list_now_page_files()
2024-10-07 17:39:25 +11:00
rss = f'<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Nathan.Woodburn/</title><link>{host}</link><description>See what I\'ve been up to</description><language>en-us</language><lastBuildDate>{datetime.datetime.now(tz=datetime.timezone.utc).strftime("%a, %d %b %Y %H:%M:%S %z")}</lastBuildDate><atom:link href="{host}/now.rss" rel="self" type="application/rss+xml" />'
for page in now_pages:
link = page.strip(".html")
date = datetime.datetime.strptime(link, "%y_%m_%d")
date = date.strftime("%A, %B %d, %Y")
rss += f'<item><title>What\'s Happening {date}</title><link>{host}/now/{link}</link><description>Latest updates for {date}</description><guid>{host}/now/{link}</guid></item>'
2024-10-07 17:39:25 +11:00
rss += "</channel></rss>"
return make_response(rss, 200, {"Content-Type": "application/rss+xml"})
2024-10-07 18:03:04 +11:00
@app.route("/now.json")
def now_json():
now_pages = now.list_now_page_files()
2024-10-07 18:03:04 +11:00
host = "https://" + request.host
if ":" in request.host:
host = "http://" + request.host
now_pages = [{"url":host+"/now/"+page.strip(".html"), "date":datetime.datetime.strptime(page.strip(".html"), "%y_%m_%d").strftime("%A, %B %d, %Y"), "title":"What's Happening "+datetime.datetime.strptime(page.strip(".html"), "%y_%m_%d").strftime("%A, %B %d, %Y")} for page in now_pages]
return jsonify(now_pages)
2024-08-15 13:43:35 +10:00
# endregion
2024-02-18 21:47:35 +11:00
# region Donate
2024-08-15 13:43:35 +10:00
@app.route("/donate")
2024-04-15 21:01:28 +10:00
def donate():
global handshake_scripts
# If localhost, don't load handshake
2024-08-15 13:43:35 +10:00
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"
):
2024-04-15 21:01:28 +10:00
handshake_scripts = ""
2024-08-15 13:43:35 +10:00
coinList = os.listdir(".well-known/wallets")
coinList = [file for file in coinList if file[0] != "."]
2024-04-15 21:01:28 +10:00
coinList.sort()
tokenList = []
2024-08-15 13:43:35 +10:00
with open(".well-known/wallets/.tokens") as file:
2024-04-15 21:01:28 +10:00
tokenList = file.read()
tokenList = json.loads(tokenList)
coinNames = {}
2024-08-15 13:43:35 +10:00
with open(".well-known/wallets/.coins") as file:
2024-04-15 21:01:28 +10:00
coinNames = file.read()
coinNames = json.loads(coinNames)
2024-08-15 13:43:35 +10:00
coins = ""
default_coins = ["btc", "eth", "hns", "sol", "xrp", "ada", "dot"]
2024-04-15 21:01:28 +10:00
for file in coinList:
if file in coinNames:
coins += f'<a class="dropdown-item" style="{"display:none;" if file.lower() not in default_coins else ""}" href="?c={file.lower()}">{coinNames[file]}</a>'
else:
coins += f'<a class="dropdown-item" style="{"display:none;" if file.lower() not in default_coins else ""}" href="?c={file.lower()}">{file}</a>'
for token in tokenList:
2024-08-15 13:43:35 +10:00
if token["chain"] != "null":
coins += f'<a class="dropdown-item" style="display:none;" href="?t={token["symbol"].lower()}&c={token["chain"].lower()}">{token["name"]} ({token["symbol"] + " on " if token["symbol"] != token["name"] else ""}{token["chain"]})</a>'
else:
2024-08-15 13:43:35 +10:00
coins += f'<a class="dropdown-item" style="display:none;" href="?t={token["symbol"].lower()}&c={token["chain"].lower()}">{token["name"]} ({token["symbol"] if token["symbol"] != token["name"] else ""})</a>'
2024-04-15 21:01:28 +10:00
2024-08-15 13:43:35 +10:00
crypto = request.args.get("c")
2024-04-15 21:01:28 +10:00
if not crypto:
2024-08-15 13:43:35 +10:00
instructions = (
"<br>Donate with cryptocurrency:<br>Select a coin from the dropdown above."
)
return render_template(
"donate.html",
handshake_scripts=handshake_scripts,
coins=coins,
default_coins=default_coins,
crypto=instructions,
)
2024-04-15 21:01:28 +10:00
crypto = crypto.upper()
2024-08-15 13:43:35 +10:00
token = request.args.get("t")
2024-04-15 21:01:28 +10:00
if token:
token = token.upper()
for t in tokenList:
2024-08-15 13:43:35 +10:00
if t["symbol"].upper() == token and t["chain"].upper() == crypto:
2024-04-15 21:01:28 +10:00
token = t
break
if not isinstance(token, dict):
2024-08-15 13:43:35 +10:00
token = {"name": "Unknown token", "symbol": token, "chain": crypto}
2024-04-15 21:01:28 +10:00
2024-08-15 13:43:35 +10:00
address = ""
domain = ""
cryptoHTML = ""
2024-07-18 14:30:12 +10:00
2024-08-15 13:43:35 +10:00
proof = ""
if os.path.isfile(f".well-known/wallets/.{crypto}.proof"):
2024-07-18 14:30:12 +10:00
proof = f'<a href="/.well-known/wallets/.{crypto}.proof" target="_blank"><img src="/assets/img/proof.png" alt="Proof of ownership" style="width: 100%; max-width: 30px; margin-left: 10px;"></a>'
2024-08-15 13:43:35 +10:00
if os.path.isfile(f".well-known/wallets/{crypto}"):
with open(f".well-known/wallets/{crypto}") as file:
2024-04-15 21:01:28 +10:00
address = file.read()
if not token:
2024-08-15 13:43:35 +10:00
cryptoHTML += f"<br>Donate with {coinNames[crypto] if crypto in coinNames else crypto}:"
2024-04-15 21:01:28 +10:00
else:
cryptoHTML += f'<br>Donate with {token["name"]} {"("+token["symbol"]+") " if token["symbol"] != token["name"] else ""}on {crypto}:'
2024-07-18 14:30:12 +10:00
cryptoHTML += f'<br><code data-bs-toggle="tooltip" data-bss-tooltip="" id="crypto-address" class="address" style="color: rgb(242,90,5);display: inline-block;" data-bs-original-title="Click to copy">{address}</code>'
if proof:
cryptoHTML += proof
elif token:
2024-08-15 13:43:35 +10:00
if "address" in token:
address = token["address"]
cryptoHTML += f'<br>Donate with {token["name"]} {"("+token["symbol"]+")" if token["symbol"] != token["name"] else ""}{" on "+crypto if crypto != "NULL" else ""}:'
2024-07-18 14:30:12 +10:00
cryptoHTML += f'<br><code data-bs-toggle="tooltip" data-bss-tooltip="" id="crypto-address" class="address" style="color: rgb(242,90,5);display: inline-block;" data-bs-original-title="Click to copy">{address}</code>'
if proof:
cryptoHTML += proof
else:
cryptoHTML += f'<br>Invalid offchain token: {token["symbol"]}<br>'
2024-04-15 21:01:28 +10:00
else:
cryptoHTML += f"<br>Invalid chain: {crypto}<br>"
2024-04-15 21:01:28 +10:00
2024-08-15 13:43:35 +10:00
if os.path.isfile(f".well-known/wallets/.domains"):
2024-04-15 21:01:28 +10:00
# Get json of all domains
2024-08-15 13:43:35 +10:00
with open(f".well-known/wallets/.domains") as file:
2024-04-15 21:01:28 +10:00
domains = file.read()
domains = json.loads(domains)
2024-08-15 13:43:35 +10:00
2024-04-15 21:01:28 +10:00
if crypto in domains:
domain = domains[crypto]
2024-08-15 13:43:35 +10:00
cryptoHTML += "<br>Or send to this domain on compatible wallets:<br>"
2024-04-15 21:01:28 +10:00
cryptoHTML += f'<code data-bs-toggle="tooltip" data-bss-tooltip="" id="crypto-domain" class="address" style="color: rgb(242,90,5);display: block;" data-bs-original-title="Click to copy">{domain}</code>'
if address:
2024-08-15 13:43:35 +10:00
cryptoHTML += (
2024-10-09 16:50:40 +11:00
'<br><img src="/address/'
2024-08-15 13:43:35 +10:00
+ address
+ '" alt="QR Code" style="width: 100%; max-width: 200px; margin: 20px auto;">'
)
2024-04-19 11:28:07 +10:00
copyScript = '<script>document.getElementById("crypto-address").addEventListener("click", function() {navigator.clipboard.writeText(this.innerText);this.setAttribute("data-bs-original-title", "Copied!");const tooltips = document.querySelectorAll(".tooltip-inner");tooltips.forEach(tooltip => {tooltip.innerText = "Copied!";});});document.getElementById("crypto-domain").addEventListener("click", function() {navigator.clipboard.writeText(this.innerText);this.setAttribute("data-bs-original-title", "Copied!");const tooltips = document.querySelectorAll(".tooltip-inner");tooltips.forEach(tooltip => {tooltip.innerText = "Copied!";});});</script>'
2024-04-15 21:01:28 +10:00
cryptoHTML += copyScript
2024-08-15 13:43:35 +10:00
return render_template(
"donate.html",
handshake_scripts=handshake_scripts,
crypto=cryptoHTML,
coins=coins,
default_coins=default_coins,
)
2024-07-18 14:30:12 +10:00
2024-04-15 21:01:28 +10:00
2024-10-09 16:50:40 +11:00
@app.route("/address/<path:address>")
def addressQR(address:str):
2024-04-19 11:28:07 +10:00
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=10,
border=4,
)
2024-10-09 16:50:40 +11:00
qr.add_data(address)
2024-04-19 11:28:07 +10:00
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)
# Return the QR code image as a response
return send_file(qr_image_path, mimetype="image/png")
2024-10-09 16:50:40 +11:00
@app.route("/qrcode/<path:data>")
@app.route("/qr/<path:data>")
def qr(data:str):
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H,box_size=10,border=2)
qr.add_data(data)
qr.make()
qr_image:Image.Image = qr.make_image(fill_color="black", back_color="white").convert('RGB')
# Add logo
logo = Image.open("templates/assets/img/favicon/logo.png")
basewidth = qr_image.size[0]//3
wpercent = (basewidth / float(logo.size[0]))
hsize = int((float(logo.size[1]) * float(wpercent)))
logo = logo.resize((basewidth, hsize),Image.Resampling.LANCZOS)
pos = ((qr_image.size[0] - logo.size[0]) // 2,
(qr_image.size[1] - logo.size[1]) // 2)
qr_image.paste(logo, pos, mask=logo)
qr_image.save("/tmp/qr_code.png")
return send_file("/tmp/qr_code.png", mimetype="image/png")
# endregion
2024-08-15 13:43:35 +10:00
@app.route("/supersecretpath")
2024-04-19 11:28:07 +10:00
def supersecretpath():
2024-08-15 13:43:35 +10:00
ascii_art = ""
if os.path.isfile("data/ascii.txt"):
with open("data/ascii.txt") as file:
2024-04-19 11:28:07 +10:00
ascii_art = file.read()
2024-08-15 13:43:35 +10:00
2024-04-19 11:28:07 +10:00
converter = Ansi2HTMLConverter()
ascii_art_html = converter.convert(ascii_art)
2024-08-15 13:43:35 +10:00
return render_template("ascii.html", ascii_art=ascii_art_html)
@app.route("/download/<path:path>")
def download(path):
# Check if file exists
if path in downloads:
path = downloads[path]
if os.path.isfile(path):
return send_file(path)
return render_template("404.html"), 404
2024-04-19 11:28:07 +10:00
2024-08-15 13:43:35 +10:00
@app.route("/<path:path>")
def catch_all(path: str):
global handshake_scripts
2023-11-02 20:26:15 +11:00
# If localhost, don't load handshake
2024-08-15 13:43:35 +10:00
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"
):
2023-11-02 20:26:15 +11:00
handshake_scripts = ""
2024-04-19 11:28:07 +10:00
2024-08-15 13:43:35 +10:00
if path.lower().replace(".html", "") in restricted:
return render_template("404.html"), 404
2024-09-27 13:34:26 +10:00
if path in redirects:
return redirect(redirects[path], code=302)
2023-11-02 20:26:15 +11:00
# If file exists, load it
2024-08-15 13:43:35 +10:00
if os.path.isfile("templates/" + path):
return render_template(path, handshake_scripts=handshake_scripts, sites=sites)
2023-11-02 20:26:15 +11:00
# Try with .html
2024-08-15 13:43:35 +10:00
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
)
# 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)
if request.headers:
# Check if curl
if "curl" in request.headers.get("User-Agent"):
return jsonify(
{
"status": 404,
"message": "Page not found",
"ip": request.remote_addr,
}
), 404
2024-08-15 13:43:35 +10:00
return render_template("404.html"), 404
2023-11-02 20:26:15 +11:00
2024-02-23 17:27:38 +11:00
# endregion
2023-11-02 20:26:15 +11:00
2024-08-15 13:43:35 +10:00
# region ACME
@app.route("/hnsdoh-acme", methods=["POST"])
2023-12-22 15:07:24 +11:00
def hnsdoh_acme():
# Get the TXT record from the request
if not request.json:
2024-08-15 13:43:35 +10:00
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"})
cf = Cloudflare(api_token=os.getenv("CF_TOKEN"))
2024-08-20 13:02:45 +10:00
zone = cf.zones.list(name="hnsdoh.com").to_dict()
zone_id = zone["result"][0]["id"]
existing_records = cf.dns.records.list(
zone_id=zone_id, type="TXT", name="_acme-challenge.hnsdoh.com"
).to_dict()
record_id = existing_records["result"][0]["id"]
cf.dns.records.delete(dns_record_id=record_id, zone_id=zone_id)
cf.dns.records.create(
zone_id=zone_id,
type="TXT",
name="_acme-challenge",
content=txt,
2024-08-15 13:43:35 +10:00
)
return jsonify({"status": "success"})
2023-12-22 15:07:24 +11:00
2024-08-15 13:43:35 +10:00
# endregion
2023-12-22 15:07:24 +11:00
2024-08-15 13:43:35 +10:00
# region Podcast
@app.route("/ID1")
2024-05-19 22:05:03 +10:00
def ID1():
# Proxy to ID1 url
2024-08-15 13:43:35 +10:00
req = requests.get("https://id1.woodburn.au/ID1")
return make_response(
req.content, 200, {"Content-Type": req.headers["Content-Type"]}
)
2024-05-19 22:46:32 +10:00
2024-08-15 13:43:35 +10:00
@app.route("/ID1/")
2024-05-19 22:46:32 +10:00
def ID1_slash():
# Proxy to ID1 url
2024-08-15 13:43:35 +10:00
req = requests.get("https://id1.woodburn.au/ID1/")
return make_response(
req.content, 200, {"Content-Type": req.headers["Content-Type"]}
)
2024-05-19 22:05:03 +10:00
2024-08-15 13:43:35 +10:00
@app.route("/ID1/<path:path>")
2024-05-19 22:05:03 +10:00
def ID1_path(path):
# Proxy to ID1 url
2024-08-15 13:43:35 +10:00
req = requests.get("https://id1.woodburn.au/ID1/" + path)
return make_response(
req.content, 200, {"Content-Type": req.headers["Content-Type"]}
)
2024-05-19 22:05:03 +10:00
2024-08-15 13:43:35 +10:00
@app.route("/ID1.xml")
2024-05-19 22:05:03 +10:00
def ID1_xml():
# Proxy to ID1 url
2024-08-15 13:43:35 +10:00
req = requests.get("https://id1.woodburn.au/ID1.xml")
return make_response(
req.content, 200, {"Content-Type": req.headers["Content-Type"]}
)
2024-05-19 22:05:03 +10:00
2024-08-15 13:43:35 +10:00
@app.route("/podsync.opml")
2024-05-19 22:05:03 +10:00
def podsync():
2024-08-15 13:43:35 +10:00
req = requests.get("https://id1.woodburn.au/podsync.opml")
return make_response(
req.content, 200, {"Content-Type": req.headers["Content-Type"]}
)
2024-05-19 22:05:03 +10:00
2024-08-15 13:43:35 +10:00
# endregion
# region Error Catching
2023-11-02 20:26:15 +11:00
# 404 catch all
@app.errorhandler(404)
def not_found(e):
if request.headers:
# Check if curl
if "curl" in request.headers.get("User-Agent"):
return jsonify(
{
"status": 404,
"message": "Page not found",
"ip": request.remote_addr,
}
), 404
2024-08-15 13:43:35 +10:00
return render_template("404.html"), 404
# endregion
2023-11-02 20:26:15 +11:00
2024-08-15 13:43:35 +10:00
if __name__ == "__main__":
app.run(debug=True, port=5000, host="0.0.0.0")