import os from flask import Blueprint, render_template, request, jsonify import markdown from bs4 import BeautifulSoup import re from functools import lru_cache from tools import isCLI, getClientIP, getHandshakeScript app = Blueprint("blog", __name__, url_prefix="/blog") @lru_cache(maxsize=32) def list_page_files(): blog_pages = os.listdir("data/blog") # Sort pages by modified time, newest first blog_pages.sort( key=lambda x: os.path.getmtime(os.path.join("data/blog", x)), reverse=True ) # Remove .md extension blog_pages = [ page.removesuffix(".md") for page in blog_pages if page.endswith(".md") ] return blog_pages @lru_cache(maxsize=64) def get_blog_content(date): """Get and cache blog content.""" if not os.path.exists(f"data/blog/{date}.md"): return None with open(f"data/blog/{date}.md", "r") as f: return f.read() @lru_cache(maxsize=64) def render_markdown_to_html(content): """Convert markdown to HTML with caching.""" html = markdown.markdown( content, extensions=["sane_lists", "codehilite", "fenced_code"] ) # Add target="_blank" to all links html = html.replace(' tag containing numbered steps paragraphs = soup.find_all("p") for p in paragraphs: content = p.decode_contents() # type: ignore # Check for likely numbered step structure if re.search(r"1\.\s", content): # Split into pre-list and numbered steps # Match:
, optional whitespace, then a number and dot parts = re.split(r"(?:)?\s*(\d+)\.\s", content) # Result: [pre-text, '1', step1, '2', step2, ..., '10', step10] pre_text = parts[0].strip() steps = parts[1:] # Assemble the ordered list ol_items = [] for i in range(0, len(steps), 2): if i + 1 < len(steps): step_html = steps[i + 1].strip() ol_items.append(f"
  • {step_html}
  • ") # Build the final list HTML ol_html = "
      \n" + "\n".join(ol_items) + "\n
    " # Rebuild paragraph with optional pre-text new_html = f"{pre_text}
    \n{ol_html}" if pre_text else ol_html # Replace old

    with parsed version new_fragment = BeautifulSoup(new_html, "html.parser") p.replace_with(new_fragment) break # Only process the first matching

    return str(soup) def render_home(handshake_scripts: str | None = None): # Get a list of pages blog_pages = list_page_files() # Create a html list of pages blog_pages = [ f"""

  • {page.replace("_", " ")}

  • """ for page in blog_pages ] # Join the list blog_pages = "\n".join(blog_pages) # Render the template return render_template( "blog/blog.html", blogs=blog_pages, handshake_scripts=handshake_scripts, ) @app.route("/", strict_slashes=False) def index(): if not isCLI(request): return render_home(handshake_scripts=getHandshakeScript(request.host)) # Get a list of pages blog_pages = list_page_files() # Create a html list of pages blog_pages = [ { "name": page.replace("_", " "), "url": f"/blog/{page}", "download": f"/blog/{page}.md", } for page in blog_pages ] # Render the template return jsonify( { "status": 200, "message": "Check out my various blog postsa", "ip": getClientIP(request), "blogs": blog_pages, } ), 200 @app.route("/") def path(path): if not isCLI(request): return render_page(path, handshake_scripts=getHandshakeScript(request.host)) # Get cached content content = get_blog_content(path) if content is None: return render_template("404.html"), 404 # Get the title from the file name title = path.replace("_", " ") return jsonify( { "status": 200, "message": f"Blog post: {title}", "ip": getClientIP(request), "title": title, "content": content, "download": f"/blog/{path}.md", } ), 200 @app.route("/.md") def path_md(path): content = get_blog_content(path) if content is None: return render_template("404.html"), 404 # Return the raw markdown file return content, 200, {"Content-Type": "text/plain; charset=utf-8"}