feat: Add alternative route for fonts
This commit is contained in:
108
server.py
108
server.py
@@ -18,9 +18,20 @@ import qrcode
|
|||||||
from qrcode.constants import ERROR_CORRECT_L, ERROR_CORRECT_H
|
from qrcode.constants import ERROR_CORRECT_L, ERROR_CORRECT_H
|
||||||
from ansi2html import Ansi2HTMLConverter
|
from ansi2html import Ansi2HTMLConverter
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
# Import blueprints
|
# Import blueprints
|
||||||
from blueprints import now, blog, wellknown, api, podcast, acme, spotify
|
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
|
from curl import curl_response
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
@@ -50,18 +61,14 @@ REDIRECT_ROUTES = {
|
|||||||
"/meeting": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr",
|
"/meeting": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr",
|
||||||
"/appointment": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr",
|
"/appointment": "https://cloud.woodburn.au/apps/calendar/appointment/PamrmmspWJZr",
|
||||||
}
|
}
|
||||||
DOWNLOAD_ROUTES = {
|
DOWNLOAD_ROUTES = {"pgp": "data/nathanwoodburn.asc"}
|
||||||
"pgp": "data/nathanwoodburn.asc"
|
|
||||||
}
|
|
||||||
|
|
||||||
SITES = []
|
SITES = []
|
||||||
if os.path.isfile("data/sites.json"):
|
if os.path.isfile("data/sites.json"):
|
||||||
with open("data/sites.json") as file:
|
with open("data/sites.json") as file:
|
||||||
SITES = json.load(file)
|
SITES = json.load(file)
|
||||||
# Remove any sites that are not enabled
|
# Remove any sites that are not enabled
|
||||||
SITES = [
|
SITES = [site for site in SITES if "enabled" not in site or site["enabled"]]
|
||||||
site for site in SITES if "enabled" not in site or site["enabled"]
|
|
||||||
]
|
|
||||||
|
|
||||||
PROJECTS = []
|
PROJECTS = []
|
||||||
PROJECTS_UPDATED = 0
|
PROJECTS_UPDATED = 0
|
||||||
@@ -114,6 +121,13 @@ def asset(path):
|
|||||||
return error_response(request)
|
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")
|
||||||
@app.route("/sitemap.xml")
|
@app.route("/sitemap.xml")
|
||||||
def sitemap():
|
def sitemap():
|
||||||
@@ -153,6 +167,7 @@ def download(path):
|
|||||||
|
|
||||||
return error_response(request, message="File not found")
|
return error_response(request, message="File not found")
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
# region PWA routes
|
# region PWA routes
|
||||||
|
|
||||||
@@ -177,6 +192,7 @@ def manifest():
|
|||||||
def serviceWorker():
|
def serviceWorker():
|
||||||
return send_from_directory("pwa", "sw.js")
|
return send_from_directory("pwa", "sw.js")
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
@@ -185,12 +201,14 @@ def serviceWorker():
|
|||||||
def links():
|
def links():
|
||||||
return render_template("link.html")
|
return render_template("link.html")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/actions.json")
|
@app.route("/actions.json")
|
||||||
def sol_actions():
|
def sol_actions():
|
||||||
return jsonify(
|
return jsonify(
|
||||||
{"rules": [{"pathPattern": "/donate**", "apiPath": "/api/v1/donate**"}]}
|
{"rules": [{"pathPattern": "/donate**", "apiPath": "/api/v1/donate**"}]}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/<path:function>")
|
@app.route("/api/<path:function>")
|
||||||
def api_legacy(function):
|
def api_legacy(function):
|
||||||
# Check if function is in api blueprint
|
# Check if function is in api blueprint
|
||||||
@@ -200,6 +218,7 @@ def api_legacy(function):
|
|||||||
return redirect(f"/api/v1/{function}", code=301)
|
return redirect(f"/api/v1/{function}", code=301)
|
||||||
return error_response(request, message="404 Not Found", code=404)
|
return error_response(request, message="404 Not Found", code=404)
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region Main routes
|
# region Main routes
|
||||||
@@ -263,9 +282,11 @@ def index():
|
|||||||
print(f"Error getting git data: {e}")
|
print(f"Error getting git data: {e}")
|
||||||
|
|
||||||
# Get only repo names for the newest updates
|
# Get only repo names for the newest updates
|
||||||
if PROJECTS == [] or PROJECTS_UPDATED < (datetime.datetime.now() - datetime.timedelta(
|
if (
|
||||||
hours=2
|
PROJECTS == []
|
||||||
)).timestamp():
|
or PROJECTS_UPDATED
|
||||||
|
< (datetime.datetime.now() - datetime.timedelta(hours=2)).timestamp()
|
||||||
|
):
|
||||||
projectsreq = requests.get(
|
projectsreq = requests.get(
|
||||||
"https://git.woodburn.au/api/v1/users/nathanwoodburn/repos"
|
"https://git.woodburn.au/api/v1/users/nathanwoodburn/repos"
|
||||||
)
|
)
|
||||||
@@ -288,11 +309,9 @@ def index():
|
|||||||
or project["avatar_url"] == ""
|
or project["avatar_url"] == ""
|
||||||
):
|
):
|
||||||
project["avatar_url"] = "/favicon.png"
|
project["avatar_url"] = "/favicon.png"
|
||||||
project["name"] = project["name"].replace(
|
project["name"] = project["name"].replace("_", " ").replace("-", " ")
|
||||||
"_", " ").replace("-", " ")
|
|
||||||
# Sort by last updated
|
# Sort by last updated
|
||||||
projectsList = sorted(
|
projectsList = sorted(PROJECTS, key=lambda x: x["updated_at"], reverse=True)
|
||||||
PROJECTS, key=lambda x: x["updated_at"], reverse=True)
|
|
||||||
PROJECTS = []
|
PROJECTS = []
|
||||||
projectNames = []
|
projectNames = []
|
||||||
projectNum = 0
|
projectNum = 0
|
||||||
@@ -305,8 +324,7 @@ def index():
|
|||||||
|
|
||||||
custom = ""
|
custom = ""
|
||||||
# Check for downtime
|
# Check for downtime
|
||||||
uptime = requests.get(
|
uptime = requests.get("https://uptime.woodburn.au/api/status-page/main/badge")
|
||||||
"https://uptime.woodburn.au/api/status-page/main/badge")
|
|
||||||
uptime = uptime.content.count(b"Up") > 1
|
uptime = uptime.content.count(b"Up") > 1
|
||||||
|
|
||||||
if uptime:
|
if uptime:
|
||||||
@@ -375,6 +393,7 @@ def index():
|
|||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
# region Donate
|
# region Donate
|
||||||
|
|
||||||
|
|
||||||
@@ -451,7 +470,7 @@ def donate():
|
|||||||
if not token:
|
if not token:
|
||||||
cryptoHTML += f"<br>Donate with {coinNames[crypto] if crypto in coinNames else crypto}:"
|
cryptoHTML += f"<br>Donate with {coinNames[crypto] if crypto in coinNames else crypto}:"
|
||||||
else:
|
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>'
|
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:
|
if proof:
|
||||||
@@ -459,12 +478,12 @@ def donate():
|
|||||||
elif token:
|
elif token:
|
||||||
if "address" in token:
|
if "address" in token:
|
||||||
address = token["address"]
|
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>'
|
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:
|
if proof:
|
||||||
cryptoHTML += proof
|
cryptoHTML += proof
|
||||||
else:
|
else:
|
||||||
cryptoHTML += f'<br>Invalid offchain token: {token["symbol"]}<br>'
|
cryptoHTML += f"<br>Invalid offchain token: {token['symbol']}<br>"
|
||||||
else:
|
else:
|
||||||
cryptoHTML += f"<br>Invalid chain: {crypto}<br>"
|
cryptoHTML += f"<br>Invalid chain: {crypto}<br>"
|
||||||
|
|
||||||
@@ -520,27 +539,30 @@ def qraddress(address):
|
|||||||
@app.route("/qrcode/<path:data>")
|
@app.route("/qrcode/<path:data>")
|
||||||
@app.route("/qr/<path:data>")
|
@app.route("/qr/<path:data>")
|
||||||
def qrcodee(data):
|
def qrcodee(data):
|
||||||
qr = qrcode.QRCode(
|
qr = qrcode.QRCode(error_correction=ERROR_CORRECT_H, box_size=10, border=2)
|
||||||
error_correction=ERROR_CORRECT_H, box_size=10, border=2)
|
|
||||||
qr.add_data(data)
|
qr.add_data(data)
|
||||||
qr.make()
|
qr.make()
|
||||||
|
|
||||||
qr_image: Image.Image = qr.make_image(
|
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
|
# Add logo
|
||||||
logo = Image.open("templates/assets/img/favicon/logo.png")
|
logo = Image.open("templates/assets/img/favicon/logo.png")
|
||||||
basewidth = qr_image.size[0] // 3
|
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)))
|
hsize = int((float(logo.size[1]) * float(wpercent)))
|
||||||
logo = logo.resize((basewidth, hsize), Image.Resampling.LANCZOS)
|
logo = logo.resize((basewidth, hsize), Image.Resampling.LANCZOS)
|
||||||
pos = ((qr_image.size[0] - logo.size[0]) // 2,
|
pos = (
|
||||||
(qr_image.size[1] - logo.size[1]) // 2)
|
(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.paste(logo, pos, mask=logo)
|
||||||
|
|
||||||
qr_image.save("/tmp/qr_code.png")
|
qr_image.save("/tmp/qr_code.png")
|
||||||
return send_file("/tmp/qr_code.png", mimetype="image/png")
|
return send_file("/tmp/qr_code.png", mimetype="image/png")
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
@@ -580,15 +602,18 @@ def hosting_post():
|
|||||||
|
|
||||||
# Check email rate limit
|
# Check email rate limit
|
||||||
if email in EMAIL_REQUEST_COUNT:
|
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
|
# Reset counter if the time window has passed
|
||||||
EMAIL_REQUEST_COUNT[email] = {
|
EMAIL_REQUEST_COUNT[email] = {"count": 1, "last_reset": current_time}
|
||||||
"count": 1, "last_reset": current_time}
|
|
||||||
else:
|
else:
|
||||||
# Increment counter
|
# Increment counter
|
||||||
EMAIL_REQUEST_COUNT[email]["count"] += 1
|
EMAIL_REQUEST_COUNT[email]["count"] += 1
|
||||||
if EMAIL_REQUEST_COUNT[email]["count"] > EMAIL_RATE_LIMIT:
|
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:
|
else:
|
||||||
# First request for this email
|
# First request for this email
|
||||||
EMAIL_REQUEST_COUNT[email] = {"count": 1, "last_reset": current_time}
|
EMAIL_REQUEST_COUNT[email] = {"count": 1, "last_reset": current_time}
|
||||||
@@ -602,7 +627,9 @@ def hosting_post():
|
|||||||
# Increment counter
|
# Increment counter
|
||||||
IP_REQUEST_COUNT[ip]["count"] += 1
|
IP_REQUEST_COUNT[ip]["count"] += 1
|
||||||
if IP_REQUEST_COUNT[ip]["count"] > IP_RATE_LIMIT:
|
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:
|
else:
|
||||||
# First request for this IP
|
# First request for this IP
|
||||||
IP_REQUEST_COUNT[ip] = {"count": 1, "last_reset": current_time}
|
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, "Failed to send enquiry", 500)
|
||||||
return json_response(request, "Enquiry sent", 200)
|
return json_response(request, "Enquiry sent", 200)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/resume")
|
@app.route("/resume")
|
||||||
def resume():
|
def resume():
|
||||||
# Check if arg for support is passed
|
# Check if arg for support is passed
|
||||||
support = request.args.get("support")
|
support = request.args.get("support")
|
||||||
return render_template(
|
return render_template("resume.html", support=support)
|
||||||
"resume.html", support=support)
|
|
||||||
|
|
||||||
@app.route("/resume.pdf")
|
@app.route("/resume.pdf")
|
||||||
def resume_pdf():
|
def resume_pdf():
|
||||||
@@ -683,13 +711,14 @@ def tools():
|
|||||||
return curl_response(request)
|
return curl_response(request)
|
||||||
return render_template("tools.html", tools=get_tools_data())
|
return render_template("tools.html", tools=get_tools_data())
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
# region Error Catching
|
# region Error Catching
|
||||||
|
|
||||||
|
|
||||||
# Catch all for GET requests
|
# Catch all for GET requests
|
||||||
@app.route("/<path:path>")
|
@app.route("/<path:path>")
|
||||||
def catch_all(path: str):
|
def catch_all(path: str):
|
||||||
|
|
||||||
if path.lower().replace(".html", "") in RESTRICTED_ROUTES:
|
if path.lower().replace(".html", "") in RESTRICTED_ROUTES:
|
||||||
return error_response(request, message="Restricted route", code=403)
|
return error_response(request, message="Restricted route", code=403)
|
||||||
|
|
||||||
@@ -702,17 +731,23 @@ def catch_all(path: str):
|
|||||||
|
|
||||||
# If file exists, load it
|
# If file exists, load it
|
||||||
if os.path.isfile("templates/" + path):
|
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
|
# Try with .html
|
||||||
if os.path.isfile("templates/" + path + ".html"):
|
if os.path.isfile("templates/" + path + ".html"):
|
||||||
return render_template(
|
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"):
|
if os.path.isfile("templates/" + path.strip("/") + ".html"):
|
||||||
return render_template(
|
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
|
# Try to find a file matching
|
||||||
@@ -729,6 +764,7 @@ def catch_all(path: str):
|
|||||||
def not_found(e):
|
def not_found(e):
|
||||||
return error_response(request)
|
return error_response(request)
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user