Auto-mount defpages: eliminate Python route stubs across all 9 services
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 16s
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 16s
Defpages are now declared with absolute paths in .sx files and auto-mounted directly on the Quart app, removing ~850 lines of blueprint mount_pages calls, before_request hooks, and g.* wrapper boilerplate. A new page = one defpage declaration, nothing else. Infrastructure: - async_eval awaits coroutine results from callable dispatch - auto_mount_pages() mounts all registered defpages on the app - g._defpage_ctx pattern passes helper data to layout context Migrated: sx, account, orders, federation, cart, market, events, blog Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -103,21 +103,16 @@ def create_app() -> "Quart":
|
||||
from sxc.pages import setup_market_pages
|
||||
setup_market_pages()
|
||||
|
||||
from shared.sx.pages import mount_pages
|
||||
|
||||
# All markets: / — global view across all pages
|
||||
all_markets_bp = register_all_markets()
|
||||
mount_pages(all_markets_bp, "market", names=["all-markets-index"])
|
||||
app.register_blueprint(all_markets_bp, url_prefix="/")
|
||||
|
||||
# Page markets: /<slug>/ — markets for a single page
|
||||
page_markets_bp = register_page_markets()
|
||||
mount_pages(page_markets_bp, "market", names=["page-markets-index"])
|
||||
app.register_blueprint(page_markets_bp, url_prefix="/<slug>")
|
||||
|
||||
# Page admin: /<slug>/admin/ — post-level admin for markets
|
||||
page_admin_bp = register_page_admin()
|
||||
mount_pages(page_admin_bp, "market", names=["page-admin"])
|
||||
app.register_blueprint(page_admin_bp, url_prefix="/<slug>/admin")
|
||||
|
||||
# Market blueprint nested under post slug: /<page_slug>/<market_slug>/
|
||||
@@ -135,6 +130,10 @@ def create_app() -> "Quart":
|
||||
app.register_blueprint(register_actions())
|
||||
app.register_blueprint(register_data())
|
||||
|
||||
# Auto-mount all defpages with absolute paths
|
||||
from shared.sx.pages import auto_mount_pages
|
||||
auto_mount_pages(app, "market")
|
||||
|
||||
# --- Auto-inject slugs into url_for() calls ---
|
||||
@app.url_value_preprocessor
|
||||
def pull_slugs(endpoint, values):
|
||||
|
||||
@@ -41,19 +41,6 @@ async def _load_markets(page, per_page=20):
|
||||
def register() -> Blueprint:
|
||||
bp = Blueprint("all_markets", __name__)
|
||||
|
||||
@bp.before_request
|
||||
async def _prepare_page_data():
|
||||
"""Load all-markets data for defpage routes."""
|
||||
endpoint = request.endpoint or ""
|
||||
if not endpoint.endswith("defpage_all_markets_index"):
|
||||
return
|
||||
page = int(request.args.get("page", 1))
|
||||
markets, has_more, page_info = await _load_markets(page)
|
||||
g.all_markets_data = {
|
||||
"markets": markets, "has_more": has_more,
|
||||
"page_info": page_info, "page": page,
|
||||
}
|
||||
|
||||
@bp.get("/all-markets")
|
||||
async def markets_fragment():
|
||||
page = int(request.args.get("page", 1))
|
||||
|
||||
@@ -29,10 +29,6 @@ def register():
|
||||
register_product(),
|
||||
)
|
||||
|
||||
# Mount defpage for market home (GET /)
|
||||
from shared.sx.pages import mount_pages
|
||||
mount_pages(browse_bp, "market", names=["market-home"])
|
||||
|
||||
@browse_bp.get("/all/")
|
||||
@cache_page(tag="browse")
|
||||
async def browse_all():
|
||||
|
||||
@@ -5,9 +5,4 @@ from quart import Blueprint
|
||||
|
||||
def register():
|
||||
bp = Blueprint("admin", __name__, url_prefix='/admin')
|
||||
|
||||
# Mount defpage for market admin (GET /)
|
||||
from shared.sx.pages import mount_pages
|
||||
mount_pages(bp, "market", names=["market-admin"])
|
||||
|
||||
return bp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from quart import Blueprint, g, render_template, make_response, url_for
|
||||
from quart import Blueprint, g, make_response, url_for
|
||||
|
||||
|
||||
from ..browse.routes import register as register_browse_bp
|
||||
|
||||
@@ -26,17 +26,6 @@ def _slugify(value: str, max_len: int = 255) -> str:
|
||||
def register():
|
||||
bp = Blueprint("page_admin", __name__)
|
||||
|
||||
@bp.before_request
|
||||
async def _prepare_page_data():
|
||||
"""Pre-render page admin content for defpage (async helper)."""
|
||||
endpoint = request.endpoint or ""
|
||||
if request.method != "GET" or not endpoint.endswith("defpage_page_admin"):
|
||||
return
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _markets_admin_panel_sx
|
||||
ctx = await get_template_context()
|
||||
g.page_admin_content = await _markets_admin_panel_sx(ctx)
|
||||
|
||||
@bp.post("/new/")
|
||||
@require_admin
|
||||
async def create_market(**kwargs):
|
||||
|
||||
@@ -23,20 +23,6 @@ async def _load_markets(post_id, page, per_page=20):
|
||||
def register() -> Blueprint:
|
||||
bp = Blueprint("page_markets", __name__)
|
||||
|
||||
@bp.before_request
|
||||
async def _prepare_page_data():
|
||||
"""Load page-markets data for defpage routes."""
|
||||
endpoint = request.endpoint or ""
|
||||
if not endpoint.endswith("defpage_page_markets_index"):
|
||||
return
|
||||
post = g.post_data["post"]
|
||||
page = int(request.args.get("page", 1))
|
||||
markets, has_more = await _load_markets(post["id"], page)
|
||||
g.page_markets_data = {
|
||||
"markets": markets, "has_more": has_more,
|
||||
"page": page, "post_slug": post.get("slug", ""),
|
||||
}
|
||||
|
||||
@bp.get("/page-markets")
|
||||
async def markets_fragment():
|
||||
post = g.post_data["post"]
|
||||
|
||||
@@ -98,67 +98,77 @@ def _register_market_helpers() -> None:
|
||||
})
|
||||
|
||||
|
||||
def _h_all_markets_content():
|
||||
async def _h_all_markets_content(**kw):
|
||||
from quart import g, url_for, request
|
||||
from shared.utils import route_prefix
|
||||
from shared.services.registry import services
|
||||
from shared.infrastructure.data_client import fetch_data
|
||||
from shared.contracts.dtos import PostDTO, dto_from_dict
|
||||
|
||||
data = getattr(g, "all_markets_data", None)
|
||||
if not data:
|
||||
page = int(request.args.get("page", 1))
|
||||
markets, has_more = await services.market.list_marketplaces(
|
||||
g.s, page=page, per_page=20,
|
||||
)
|
||||
|
||||
page_info = {}
|
||||
if markets:
|
||||
post_ids = list({m.container_id for m in markets if m.container_type == "page"})
|
||||
if post_ids:
|
||||
raw_posts = await fetch_data("blog", "posts-by-ids",
|
||||
params={"ids": ",".join(str(i) for i in post_ids)},
|
||||
required=False) or []
|
||||
for raw_p in raw_posts:
|
||||
p = dto_from_dict(PostDTO, raw_p)
|
||||
page_info[p.id] = {"title": p.title, "slug": p.slug}
|
||||
|
||||
if not markets:
|
||||
from sx.sx_components import _no_markets_sx
|
||||
return _no_markets_sx()
|
||||
|
||||
markets = data["markets"]
|
||||
has_more = data["has_more"]
|
||||
page_info = data["page_info"]
|
||||
page = data["page"]
|
||||
|
||||
prefix = route_prefix()
|
||||
next_url = prefix + url_for("all_markets.markets_fragment", page=page + 1)
|
||||
|
||||
from sx.sx_components import _market_cards_sx, _markets_grid, _no_markets_sx
|
||||
if markets:
|
||||
cards = _market_cards_sx(markets, page_info, page, has_more, next_url)
|
||||
content = _markets_grid(cards)
|
||||
else:
|
||||
content = _no_markets_sx()
|
||||
from sx.sx_components import _market_cards_sx, _markets_grid
|
||||
cards = _market_cards_sx(markets, page_info, page, has_more, next_url)
|
||||
content = _markets_grid(cards)
|
||||
return "(<> " + content + " " + '(div :class "pb-8")' + ")"
|
||||
|
||||
|
||||
def _h_page_markets_content():
|
||||
from quart import g, url_for
|
||||
async def _h_page_markets_content(slug=None, **kw):
|
||||
from quart import g, url_for, request
|
||||
from shared.utils import route_prefix
|
||||
from shared.services.registry import services
|
||||
|
||||
data = getattr(g, "page_markets_data", None)
|
||||
if not data:
|
||||
post = g.post_data["post"]
|
||||
page = int(request.args.get("page", 1))
|
||||
markets, has_more = await services.market.list_marketplaces(
|
||||
g.s, "page", post["id"], page=page, per_page=20,
|
||||
)
|
||||
post_slug = post.get("slug", "")
|
||||
|
||||
if not markets:
|
||||
from sx.sx_components import _no_markets_sx
|
||||
return _no_markets_sx("No markets for this page")
|
||||
|
||||
markets = data["markets"]
|
||||
has_more = data["has_more"]
|
||||
page = data["page"]
|
||||
post_slug = data.get("post_slug", "")
|
||||
|
||||
prefix = route_prefix()
|
||||
next_url = prefix + url_for("page_markets.markets_fragment", page=page + 1)
|
||||
|
||||
from sx.sx_components import _market_cards_sx, _markets_grid, _no_markets_sx
|
||||
if markets:
|
||||
cards = _market_cards_sx(markets, {}, page, has_more, next_url,
|
||||
show_page_badge=False, post_slug=post_slug)
|
||||
content = _markets_grid(cards)
|
||||
else:
|
||||
content = _no_markets_sx("No markets for this page")
|
||||
from sx.sx_components import _market_cards_sx, _markets_grid
|
||||
cards = _market_cards_sx(markets, {}, page, has_more, next_url,
|
||||
show_page_badge=False, post_slug=post_slug)
|
||||
content = _markets_grid(cards)
|
||||
return "(<> " + content + " " + '(div :class "pb-8")' + ")"
|
||||
|
||||
|
||||
def _h_page_admin_content():
|
||||
# Content pre-rendered by before_request (async _markets_admin_panel_sx)
|
||||
from quart import g
|
||||
content = getattr(g, "page_admin_content", "")
|
||||
async def _h_page_admin_content(slug=None, **kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _markets_admin_panel_sx
|
||||
ctx = await get_template_context()
|
||||
content = await _markets_admin_panel_sx(ctx)
|
||||
return '(div :id "main-panel" ' + content + ')'
|
||||
|
||||
|
||||
def _h_market_home_content():
|
||||
def _h_market_home_content(page_slug=None, market_slug=None, **kw):
|
||||
from quart import g
|
||||
post_data = getattr(g, "post_data", {})
|
||||
post = post_data.get("post", {})
|
||||
@@ -166,5 +176,5 @@ def _h_market_home_content():
|
||||
return _market_landing_content_sx(post)
|
||||
|
||||
|
||||
def _h_market_admin_content():
|
||||
def _h_market_admin_content(page_slug=None, market_slug=None, **kw):
|
||||
return '"market admin"'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
;; Market app defpage declarations.
|
||||
;;
|
||||
;; all-markets-index: / — global view across all pages
|
||||
;; page-markets-index: / (on page_markets bp, mounted at /<slug>)
|
||||
;; page-admin: / (on page_admin bp, mounted at /<slug>/admin)
|
||||
;; market-home: / (on browse bp, mounted at /<page_slug>/<market_slug>)
|
||||
;; market-admin: / (on admin bp, mounted at /<page_slug>/<market_slug>/admin)
|
||||
;; page-markets-index: /<slug>/ — markets for a single page
|
||||
;; page-admin: /<slug>/admin/ — post-level admin for markets
|
||||
;; market-home: /<page_slug>/<market_slug>/ — market landing page
|
||||
;; market-admin: /<page_slug>/<market_slug>/admin/ — market admin
|
||||
|
||||
(defpage all-markets-index
|
||||
:path "/"
|
||||
@@ -13,25 +13,25 @@
|
||||
:content (all-markets-content))
|
||||
|
||||
(defpage page-markets-index
|
||||
:path "/"
|
||||
:path "/<slug>/"
|
||||
:auth :public
|
||||
:layout :post
|
||||
:content (page-markets-content))
|
||||
|
||||
(defpage page-admin
|
||||
:path "/"
|
||||
:path "/<slug>/admin/"
|
||||
:auth :admin
|
||||
:layout (:post-admin :selected "markets")
|
||||
:content (page-admin-content))
|
||||
|
||||
(defpage market-home
|
||||
:path "/"
|
||||
:path "/<page_slug>/<market_slug>/"
|
||||
:auth :public
|
||||
:layout :market
|
||||
:content (market-home-content))
|
||||
|
||||
(defpage market-admin
|
||||
:path "/"
|
||||
:path "/<page_slug>/<market_slug>/admin/"
|
||||
:auth :admin
|
||||
:layout (:market-admin :selected "markets")
|
||||
:content (market-admin-content))
|
||||
|
||||
Reference in New Issue
Block a user