Auto-mount defpages: eliminate Python route stubs across all 9 services
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:
2026-03-03 19:03:15 +00:00
parent 4ba63bda17
commit 293f7713d6
63 changed files with 1340 additions and 1216 deletions

View File

@@ -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"'

View File

@@ -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))