"""Serves the built Vue SPA from frontend/dist/ with history-mode fallback, the on-disk image library + thumbnails from /images, and the signed Firefox extension XPI from frontend/dist/extension/. """ import re from pathlib import Path from quart import Blueprint, abort, send_file, send_from_directory FRONTEND_DIST = Path(__file__).resolve().parent.parent.parent / "frontend" / "dist" IMAGES_ROOT = Path("/images") XPI_DIR = FRONTEND_DIST / "extension" _XPI_NAME_RE = re.compile(r"^fabledcurator-[\w.-]+\.xpi$") frontend_bp = Blueprint("frontend", __name__) @frontend_bp.route("/images/") async def serve_image(subpath: str): """Serve a file from the /images volume (originals + thumbnails). Without this route the SPA catch-all below would swallow image requests and return index.html, leaving the browser to render the aspect-ratio-shaped grey placeholder. """ target = (IMAGES_ROOT / subpath).resolve() # Defend against path-traversal: refuse anything that escapes /images. try: target.relative_to(IMAGES_ROOT) except ValueError: abort(404) if not target.is_file(): abort(404) return await send_from_directory(IMAGES_ROOT, subpath) @frontend_bp.route("/extension/") async def serve_extension(filename: str): """Serve the signed FC Firefox extension XPI. Path whitelist: filename must match fabledcurator-*.xpi. The special name fabledcurator-latest.xpi serves the most-recently-modified XPI in the directory. The application/x-xpinstall MIME tells Firefox to show its native install prompt instead of downloading the file as a blob. """ if not _XPI_NAME_RE.fullmatch(filename): abort(404) if not XPI_DIR.is_dir(): abort(404) if filename == "fabledcurator-latest.xpi": xpis = sorted(XPI_DIR.glob("fabledcurator-*.xpi"), key=lambda p: p.stat().st_mtime) if not xpis: abort(404) latest = xpis[-1] return await send_file( latest, mimetype="application/x-xpinstall", attachment_filename=latest.name, ) target = (XPI_DIR / filename).resolve() try: target.relative_to(XPI_DIR) except ValueError: abort(404) if not target.is_file(): abort(404) return await send_file( target, mimetype="application/x-xpinstall", attachment_filename=filename, ) @frontend_bp.route("/") @frontend_bp.route("/") async def serve_spa(subpath: str = ""): if subpath and (FRONTEND_DIST / subpath).is_file(): return await send_from_directory(FRONTEND_DIST, subpath) return await send_from_directory(FRONTEND_DIST, "index.html")