From 03dae872720949cba64baf5d9801b992a4d285b3 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Thu, 30 Oct 2025 18:22:21 +1100 Subject: [PATCH] feat: Refactor blueprints to make them easier to import --- blueprints/acme.py | 4 ++-- blueprints/api.py | 36 ++++++++++++++++++------------------ blueprints/blog.py | 8 ++++---- blueprints/now.py | 18 +++++++++--------- blueprints/podcast.py | 12 ++++++------ blueprints/sol.py | 8 ++++---- blueprints/spotify.py | 10 +++++----- blueprints/template.py | 4 ++-- blueprints/wellknown.py | 10 +++++----- server.py | 21 ++++++--------------- templates/index.html | 2 +- 11 files changed, 62 insertions(+), 71 deletions(-) diff --git a/blueprints/acme.py b/blueprints/acme.py index f543551..bf1e3c2 100644 --- a/blueprints/acme.py +++ b/blueprints/acme.py @@ -3,10 +3,10 @@ import os from cloudflare import Cloudflare from tools import json_response -acme_bp = Blueprint('acme', __name__) +app = Blueprint('acme', __name__) -@acme_bp.route("/hnsdoh-acme", methods=["POST"]) +@app.route("/hnsdoh-acme", methods=["POST"]) def post(): # Get the TXT record from the request if not request.is_json or not request.json: diff --git a/blueprints/api.py b/blueprints/api.py index a8a00c9..d61f855 100644 --- a/blueprints/api.py +++ b/blueprints/api.py @@ -5,7 +5,7 @@ import requests import re from mail import sendEmail from tools import getClientIP, getGitCommit, json_response, parse_date, get_tools_data -from blueprints.sol import sol_bp +from blueprints import sol from dateutil import parser as date_parser from blueprints.spotify import get_spotify_track @@ -17,9 +17,9 @@ HTTP_NOT_FOUND = 404 HTTP_UNSUPPORTED_MEDIA = 415 HTTP_SERVER_ERROR = 500 -api_bp = Blueprint('api', __name__) +app = Blueprint('api', __name__, url_prefix='/api/v1') # Register solana blueprint -api_bp.register_blueprint(sol_bp) +app.register_blueprint(sol.app) # Load configuration NC_CONFIG = requests.get( @@ -30,8 +30,8 @@ if 'time-zone' not in NC_CONFIG: NC_CONFIG['time-zone'] = 10 -@api_bp.route("/") -@api_bp.route("/help") +@app.route("/") +@app.route("/help") def help(): """Provide API documentation and help.""" return jsonify({ @@ -57,18 +57,18 @@ def help(): "status": HTTP_OK }) -@api_bp.route("/status") -@api_bp.route("/ping") +@app.route("/status") +@app.route("/ping") def status(): return json_response(request, "200 OK", HTTP_OK) -@api_bp.route("/version") +@app.route("/version") def version(): """Get the current version of the website.""" return jsonify({"version": getGitCommit()}) -@api_bp.route("/time") +@app.route("/time") def time(): """Get the current time in the configured timezone.""" timezone_offset = datetime.timedelta(hours=NC_CONFIG["time-zone"]) @@ -84,7 +84,7 @@ def time(): }) -@api_bp.route("/timezone") +@app.route("/timezone") def timezone(): """Get the current timezone setting.""" return jsonify({ @@ -94,7 +94,7 @@ def timezone(): }) -@api_bp.route("/message") +@app.route("/message") def message(): """Get the message from the configuration.""" return jsonify({ @@ -104,7 +104,7 @@ def message(): }) -@api_bp.route("/ip") +@app.route("/ip") def ip(): """Get the client's IP address.""" return jsonify({ @@ -113,7 +113,7 @@ def ip(): }) -@api_bp.route("/email", methods=["POST"]) +@app.route("/email", methods=["POST"]) def email_post(): """Send an email via the API (requires API key).""" # Verify json @@ -135,7 +135,7 @@ def email_post(): return sendEmail(data) -@api_bp.route("/project") +@app.route("/project") def project(): """Get information about the current git project.""" gitinfo = { @@ -168,7 +168,7 @@ def project(): "status": HTTP_OK }) -@api_bp.route("/tools") +@app.route("/tools") def tools(): """Get a list of tools used by Nathan Woodburn.""" try: @@ -184,7 +184,7 @@ def tools(): return json_response(request, {"tools": tools}, HTTP_OK) -@api_bp.route("/playing") +@app.route("/playing") def playing(): """Get the currently playing Spotify track.""" track_info = get_spotify_track() @@ -193,7 +193,7 @@ def playing(): return json_response(request, {"spotify": track_info}, HTTP_OK) -@api_bp.route("/headers") +@app.route("/headers") def headers(): """Get the request headers.""" headers = dict(request.headers) @@ -216,7 +216,7 @@ def headers(): "status": HTTP_OK }) -@api_bp.route("/page_date") +@app.route("/page_date") def page_date(): url = request.args.get("url") if not url: diff --git a/blueprints/blog.py b/blueprints/blog.py index c94dc6f..948edd6 100644 --- a/blueprints/blog.py +++ b/blueprints/blog.py @@ -5,7 +5,7 @@ from bs4 import BeautifulSoup import re from tools import isCLI, getClientIP, getHandshakeScript -blog_bp = Blueprint('blog', __name__) +app = Blueprint('blog', __name__, url_prefix='/blog') def list_page_files(): @@ -108,7 +108,7 @@ def render_home(handshake_scripts: str | None = None): ) -@blog_bp.route("/") +@app.route("/") def index(): if not isCLI(request): return render_home(handshake_scripts=getHandshakeScript(request.host)) @@ -129,7 +129,7 @@ def index(): }), 200 -@blog_bp.route("/") +@app.route("/") def path(path): if not isCLI(request): return render_page(path, handshake_scripts=getHandshakeScript(request.host)) @@ -152,7 +152,7 @@ def path(path): }), 200 -@blog_bp.route("/.md") +@app.route("/.md") def path_md(path): if not os.path.exists(f"data/blog/{path}.md"): return render_template("404.html"), 404 diff --git a/blueprints/now.py b/blueprints/now.py index 913626c..b25f6c6 100644 --- a/blueprints/now.py +++ b/blueprints/now.py @@ -4,7 +4,7 @@ import os from tools import getHandshakeScript # Create blueprint -now_bp = Blueprint('now', __name__) +app = Blueprint('now', __name__, url_prefix='/now') def list_page_files(): @@ -51,18 +51,18 @@ def render(date, handshake_scripts=None): return render_template(f"now/{date}.html", DATE=date_formatted, handshake_scripts=handshake_scripts) -@now_bp.route("/") +@app.route("/") def index(): return render_latest(handshake_scripts=getHandshakeScript(request.host)) -@now_bp.route("/") +@app.route("/") def path(path): return render(path, handshake_scripts=getHandshakeScript(request.host)) -@now_bp.route("/old") -@now_bp.route("/old/") +@app.route("/old") +@app.route("/old/") def old(): now_dates = list_dates()[1:] html = '
    ' @@ -80,9 +80,9 @@ def old(): ) -@now_bp.route("/now.rss") -@now_bp.route("/now.xml") -@now_bp.route("/rss.xml") +@app.route("/now.rss") +@app.route("/now.xml") +@app.route("/rss.xml") def rss(): host = "https://" + request.host if ":" in request.host: @@ -99,7 +99,7 @@ def rss(): return make_response(rss, 200, {"Content-Type": "application/rss+xml"}) -@now_bp.route("/now.json") +@app.route("/now.json") def json(): now_pages = list_page_files() host = "https://" + request.host diff --git a/blueprints/podcast.py b/blueprints/podcast.py index 2810062..3411d9f 100644 --- a/blueprints/podcast.py +++ b/blueprints/podcast.py @@ -2,9 +2,9 @@ from flask import Blueprint, make_response, request from tools import error_response import requests -podcast_bp = Blueprint('podcast', __name__) +app = Blueprint('podcast', __name__) -@podcast_bp.route("/ID1") +@app.route("/ID1") def index(): # Proxy to ID1 url req = requests.get("https://podcasts.c.woodburn.au/ID1") @@ -16,7 +16,7 @@ def index(): ) -@podcast_bp.route("/ID1/") +@app.route("/ID1/") def contents(): # Proxy to ID1 url req = requests.get("https://podcasts.c.woodburn.au/ID1/") @@ -27,7 +27,7 @@ def contents(): ) -@podcast_bp.route("/ID1/") +@app.route("/ID1/") def path(path): # Proxy to ID1 url req = requests.get("https://podcasts.c.woodburn.au/ID1/" + path) @@ -38,7 +38,7 @@ def path(path): ) -@podcast_bp.route("/ID1.xml") +@app.route("/ID1.xml") def xml(): # Proxy to ID1 url req = requests.get("https://podcasts.c.woodburn.au/ID1.xml") @@ -49,7 +49,7 @@ def xml(): ) -@podcast_bp.route("/podsync.opml") +@app.route("/podsync.opml") def podsync(): req = requests.get("https://podcasts.c.woodburn.au/podsync.opml") if req.status_code != 200: diff --git a/blueprints/sol.py b/blueprints/sol.py index f64e8a6..7ad7b94 100644 --- a/blueprints/sol.py +++ b/blueprints/sol.py @@ -9,7 +9,7 @@ import binascii import base64 import os -sol_bp = Blueprint('sol', __name__) +app = Blueprint('sol', __name__) SOLANA_HEADERS = { "Content-Type": "application/json", @@ -55,7 +55,7 @@ def get_solana_address() -> str: raise ValueError("SOLANA_ADDRESS is not set. Please ensure the .well-known/wallets/SOL file exists and contains a valid address.") return str(SOLANA_ADDRESS) -@sol_bp.route("/donate", methods=["GET", "OPTIONS"]) +@app.route("/donate", methods=["GET", "OPTIONS"]) def sol_donate(): data = { "icon": "https://nathan.woodburn.au/assets/img/profile.png", @@ -90,7 +90,7 @@ def sol_donate(): return response -@sol_bp.route("/donate/") +@app.route("/donate/") def sol_donate_amount(amount): data = { "icon": "https://nathan.woodburn.au/assets/img/profile.png", @@ -101,7 +101,7 @@ def sol_donate_amount(amount): return jsonify(data), 200, SOLANA_HEADERS -@sol_bp.route("/donate/", methods=["POST"]) +@app.route("/donate/", methods=["POST"]) def sol_donate_post(amount): if not request.json: diff --git a/blueprints/spotify.py b/blueprints/spotify.py index 677e0ca..7cddc48 100644 --- a/blueprints/spotify.py +++ b/blueprints/spotify.py @@ -5,7 +5,7 @@ import requests import time import base64 -spotify_bp = Blueprint('spotify', __name__) +app = Blueprint('spotify', __name__, url_prefix='/spotify') CLIENT_ID = os.getenv("SPOTIFY_CLIENT_ID") CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRET") @@ -48,7 +48,7 @@ def refresh_access_token(): TOKEN_EXPIRES = time.time() + token_info.get("expires_in", 3600) return ACCESS_TOKEN -@spotify_bp.route("/login") +@app.route("/login") def login(): auth_query = ( f"{SPOTIFY_AUTH_URL}?response_type=code&client_id={CLIENT_ID}" @@ -56,7 +56,7 @@ def login(): ) return redirect(auth_query) -@spotify_bp.route("/callback") +@app.route("/callback") def callback(): code = request.args.get("code") if not code: @@ -89,8 +89,8 @@ def callback(): print("Refresh Token:", REFRESH_TOKEN) return redirect(url_for("spotify.currently_playing")) -@spotify_bp.route("/") -@spotify_bp.route("/currently-playing") +@app.route("/") +@app.route("/playing") def currently_playing(): """Public endpoint showing your current track.""" track = get_spotify_track() diff --git a/blueprints/template.py b/blueprints/template.py index beb6da9..ea3e3e7 100644 --- a/blueprints/template.py +++ b/blueprints/template.py @@ -1,9 +1,9 @@ from flask import Blueprint, request from tools import json_response -template_bp = Blueprint('template', __name__) +app = Blueprint('template', __name__) -@template_bp.route("/") +@app.route("/") def index(): return json_response(request, "Success", 200) \ No newline at end of file diff --git a/blueprints/wellknown.py b/blueprints/wellknown.py index 2007f4d..6334d14 100644 --- a/blueprints/wellknown.py +++ b/blueprints/wellknown.py @@ -1,15 +1,15 @@ from flask import Blueprint, render_template, make_response, request, jsonify, send_from_directory, redirect import os -wk_bp = Blueprint('well-known', __name__) +app = Blueprint('well-known', __name__, url_prefix='/.well-known') -@wk_bp.route("/") +@app.route("/") def index(path): return send_from_directory(".well-known", path) -@wk_bp.route("/wallets/") +@app.route("/wallets/") def wallets(path): if path[0] == "." and 'proof' not in path: return send_from_directory( @@ -28,7 +28,7 @@ def wallets(path): return render_template("404.html"), 404 -@wk_bp.route("/nostr.json") +@app.route("/nostr.json") def nostr(): # Get name parameter name = request.args.get("name") @@ -50,7 +50,7 @@ def nostr(): ) -@wk_bp.route("/xrp-ledger.toml") +@app.route("/xrp-ledger.toml") def xrp(): # Create a response with the xrp-ledger.toml file with open(".well-known/xrp-ledger.toml") as file: diff --git a/server.py b/server.py index b4b017f..4c46ac8 100644 --- a/server.py +++ b/server.py @@ -19,13 +19,7 @@ from qrcode.constants import ERROR_CORRECT_L, ERROR_CORRECT_H from ansi2html import Ansi2HTMLConverter from PIL import Image # Import blueprints -from blueprints.now import now_bp -from blueprints.blog import blog_bp -from blueprints.wellknown import wk_bp -from blueprints.api import api_bp -from blueprints.podcast import podcast_bp -from blueprints.acme import acme_bp -from blueprints.spotify import spotify_bp +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 curl import curl_response @@ -33,13 +27,9 @@ app = Flask(__name__) CORS(app) # Register blueprints -app.register_blueprint(now_bp, url_prefix='/now') -app.register_blueprint(blog_bp, url_prefix='/blog') -app.register_blueprint(wk_bp, url_prefix='/.well-known') -app.register_blueprint(api_bp, url_prefix='/api/v1') -app.register_blueprint(podcast_bp) -app.register_blueprint(acme_bp) -app.register_blueprint(spotify_bp, url_prefix='/spotify') +for module in [now, blog, wellknown, api, podcast, acme, spotify]: + app.register_blueprint(module.app) + dotenv.load_dotenv() @@ -54,7 +44,8 @@ RATE_LIMIT_WINDOW = 3600 # 1 hour in seconds RESTRICTED_ROUTES = ["ascii"] REDIRECT_ROUTES = { - "contact": "/#contact" + "contact": "/#contact", + "old": "/now/old" } DOWNLOAD_ROUTES = { "pgp": "data/nathanwoodburn.asc" diff --git a/templates/index.html b/templates/index.html index ab06300..5b25b97 100644 --- a/templates/index.html +++ b/templates/index.html @@ -432,7 +432,7 @@ let currentTrackId = null; // --- Spotify fetch --- async function updateSpotifyWidget() { try { - const res = await fetch('/spotify/'); + const res = await fetch('/api/v1/playing'); if (!res.ok) return; const data = await res.json();