fc3g: serve /extension/<filename> with x-xpinstall MIME so Firefox installs in one click
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+44
-2
@@ -1,13 +1,18 @@
|
||||
"""Serves the built Vue SPA from frontend/dist/ with history-mode fallback,
|
||||
and the on-disk image library + thumbnails from /images.
|
||||
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_from_directory
|
||||
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__)
|
||||
|
||||
@@ -31,6 +36,43 @@ async def serve_image(subpath: str):
|
||||
return await send_from_directory(IMAGES_ROOT, subpath)
|
||||
|
||||
|
||||
@frontend_bp.route("/extension/<filename>")
|
||||
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("/<path:subpath>")
|
||||
async def serve_spa(subpath: str = ""):
|
||||
|
||||
Reference in New Issue
Block a user