feat: Add alternative route for fonts
All checks were successful
Check Code Quality / RuffCheck (push) Successful in 2m28s
Build Docker / BuildImage (push) Successful in 2m31s

This commit is contained in:
2025-11-15 14:38:12 +11:00
parent e1ff6e42a5
commit 06526ca92d

108
server.py
View File

@@ -18,9 +18,20 @@ import qrcode
from qrcode.constants import ERROR_CORRECT_L, ERROR_CORRECT_H
from ansi2html import Ansi2HTMLConverter
from PIL import Image
# Import blueprints
from blueprints import now, blog, wellknown, api, podcast, acme, spotify
from tools import isCLI, isCrawler, getAddress, getFilePath, error_response, getClientIP, json_response, getHandshakeScript, get_tools_data
from tools import (
isCLI,
isCrawler,
getAddress,
getFilePath,
error_response,
getClientIP,
json_response,
getHandshakeScript,
get_tools_data,
)
from curl import curl_response
app = Flask(__name__)
@@ -50,18 +61,14 @@ REDIRECT_ROUTES = {
"/meeting": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr",
"/appointment": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr",
}
DOWNLOAD_ROUTES = {
"pgp": "data/nathanwoodburn.asc"
}
DOWNLOAD_ROUTES = {"pgp": "data/nathanwoodburn.asc"}
SITES = []
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"]
]
SITES = [site for site in SITES if "enabled" not in site or site["enabled"]]
PROJECTS = []
PROJECTS_UPDATED = 0
@@ -114,6 +121,13 @@ def asset(path):
return error_response(request)
@app.route("/fonts/<path:path>")
def fonts(path):
if os.path.isfile("templates/assets/fonts/" + path):
return send_from_directory("templates/assets/fonts", path)
return error_response(request)
@app.route("/sitemap")
@app.route("/sitemap.xml")
def sitemap():
@@ -153,6 +167,7 @@ def download(path):
return error_response(request, message="File not found")
# endregion
# region PWA routes
@@ -177,6 +192,7 @@ def manifest():
def serviceWorker():
return send_from_directory("pwa", "sw.js")
# endregion
@@ -185,12 +201,14 @@ def serviceWorker():
def links():
return render_template("link.html")
@app.route("/actions.json")
def sol_actions():
return jsonify(
{"rules": [{"pathPattern": "/donate**", "apiPath": "/api/v1/donate**"}]}
)
@app.route("/api/<path:function>")
def api_legacy(function):
# Check if function is in api blueprint
@@ -200,6 +218,7 @@ def api_legacy(function):
return redirect(f"/api/v1/{function}", code=301)
return error_response(request, message="404 Not Found", code=404)
# endregion
# region Main routes
@@ -263,9 +282,11 @@ def index():
print(f"Error getting git data: {e}")
# Get only repo names for the newest updates
if PROJECTS == [] or PROJECTS_UPDATED < (datetime.datetime.now() - datetime.timedelta(
hours=2
)).timestamp():
if (
PROJECTS == []
or PROJECTS_UPDATED
< (datetime.datetime.now() - datetime.timedelta(hours=2)).timestamp()
):
projectsreq = requests.get(
"https://git.woodburn.au/api/v1/users/nathanwoodburn/repos"
)
@@ -288,11 +309,9 @@ def index():
or project["avatar_url"] == ""
):
project["avatar_url"] = "/favicon.png"
project["name"] = project["name"].replace(
"_", " ").replace("-", " ")
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
@@ -305,8 +324,7 @@ def index():
custom = ""
# Check for downtime
uptime = requests.get(
"https://uptime.woodburn.au/api/status-page/main/badge")
uptime = requests.get("https://uptime.woodburn.au/api/status-page/main/badge")
uptime = uptime.content.count(b"Up") > 1
if uptime:
@@ -375,6 +393,7 @@ def index():
return resp
# region Donate
@@ -451,7 +470,7 @@ def donate():
if not token:
cryptoHTML += f"<br>Donate with {coinNames[crypto] if crypto in coinNames else crypto}:"
else:
cryptoHTML += f'<br>Donate with {token["name"]} {"("+token["symbol"]+") " if token["symbol"] != token["name"] else ""}on {crypto}:'
cryptoHTML += f"<br>Donate with {token['name']} {'(' + token['symbol'] + ') ' if token['symbol'] != token['name'] else ''}on {crypto}:"
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:
@@ -459,12 +478,12 @@ def donate():
elif token:
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 ""}:'
cryptoHTML += f"<br>Donate with {token['name']} {'(' + token['symbol'] + ')' if token['symbol'] != token['name'] else ''}{' on ' + crypto if crypto != 'NULL' else ''}:"
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>'
cryptoHTML += f"<br>Invalid offchain token: {token['symbol']}<br>"
else:
cryptoHTML += f"<br>Invalid chain: {crypto}<br>"
@@ -520,27 +539,30 @@ def qraddress(address):
@app.route("/qrcode/<path:data>")
@app.route("/qr/<path:data>")
def qrcodee(data):
qr = qrcode.QRCode(
error_correction=ERROR_CORRECT_H, box_size=10, border=2)
qr = qrcode.QRCode(error_correction=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') # type: ignore
fill_color="black", back_color="white"
).convert("RGB") # type: ignore
# Add logo
logo = Image.open("templates/assets/img/favicon/logo.png")
basewidth = qr_image.size[0] // 3
wpercent = (basewidth / float(logo.size[0]))
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)
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
@@ -580,15 +602,18 @@ def hosting_post():
# Check email rate limit
if email in EMAIL_REQUEST_COUNT:
if (current_time - EMAIL_REQUEST_COUNT[email]["last_reset"]) > RATE_LIMIT_WINDOW:
if (
current_time - EMAIL_REQUEST_COUNT[email]["last_reset"]
) > RATE_LIMIT_WINDOW:
# Reset counter if the time window has passed
EMAIL_REQUEST_COUNT[email] = {
"count": 1, "last_reset": current_time}
EMAIL_REQUEST_COUNT[email] = {"count": 1, "last_reset": current_time}
else:
# Increment counter
EMAIL_REQUEST_COUNT[email]["count"] += 1
if EMAIL_REQUEST_COUNT[email]["count"] > EMAIL_RATE_LIMIT:
return json_response(request, "Rate limit exceeded. Please try again later.", 429)
return json_response(
request, "Rate limit exceeded. Please try again later.", 429
)
else:
# First request for this email
EMAIL_REQUEST_COUNT[email] = {"count": 1, "last_reset": current_time}
@@ -602,7 +627,9 @@ def hosting_post():
# Increment counter
IP_REQUEST_COUNT[ip]["count"] += 1
if IP_REQUEST_COUNT[ip]["count"] > IP_RATE_LIMIT:
return json_response(request, "Rate limit exceeded. Please try again later.", 429)
return json_response(
request, "Rate limit exceeded. Please try again later.", 429
)
else:
# First request for this IP
IP_REQUEST_COUNT[ip] = {"count": 1, "last_reset": current_time}
@@ -661,12 +688,13 @@ def hosting_post():
return json_response(request, "Failed to send enquiry", 500)
return json_response(request, "Enquiry sent", 200)
@app.route("/resume")
def resume():
# Check if arg for support is passed
support = request.args.get("support")
return render_template(
"resume.html", support=support)
return render_template("resume.html", support=support)
@app.route("/resume.pdf")
def resume_pdf():
@@ -683,13 +711,14 @@ def tools():
return curl_response(request)
return render_template("tools.html", tools=get_tools_data())
# endregion
# region Error Catching
# Catch all for GET requests
@app.route("/<path:path>")
def catch_all(path: str):
if path.lower().replace(".html", "") in RESTRICTED_ROUTES:
return error_response(request, message="Restricted route", code=403)
@@ -702,17 +731,23 @@ def catch_all(path: str):
# If file exists, load it
if os.path.isfile("templates/" + path):
return render_template(path, handshake_scripts=getHandshakeScript(request.host), sites=SITES)
return render_template(
path, handshake_scripts=getHandshakeScript(request.host), sites=SITES
)
# Try with .html
if os.path.isfile("templates/" + path + ".html"):
return render_template(
path + ".html", handshake_scripts=getHandshakeScript(request.host), sites=SITES
path + ".html",
handshake_scripts=getHandshakeScript(request.host),
sites=SITES,
)
if os.path.isfile("templates/" + path.strip("/") + ".html"):
return render_template(
path.strip("/") + ".html", handshake_scripts=getHandshakeScript(request.host), sites=SITES
path.strip("/") + ".html",
handshake_scripts=getHandshakeScript(request.host),
sites=SITES,
)
# Try to find a file matching
@@ -729,6 +764,7 @@ def catch_all(path: str):
def not_found(e):
return error_response(request)
# endregion