Make rich 404 resilient to cross-service failures
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m27s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m27s
Build a minimal context directly instead of relying on get_template_context() which runs the full context processor chain including cross-service fragment fetches. Each step (base_context, fragments, post hydration) is independently try/excepted so the page renders with whatever is available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -83,53 +83,72 @@ async def _rich_error_page(errnum: str, message: str, image: str | None = None)
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# If the app's url_value_preprocessor didn't run (no route match),
|
from shared.sexp.jinja_bridge import render
|
||||||
# manually extract the first path segment as a candidate slug and
|
from shared.sexp.helpers import full_page, call_url
|
||||||
# try to hydrate post data so context processors can use it.
|
|
||||||
|
# Build a minimal context — avoid get_template_context() which
|
||||||
|
# calls cross-service fragment fetches that may fail.
|
||||||
|
from shared.infrastructure.context import base_context
|
||||||
|
try:
|
||||||
|
ctx = await base_context()
|
||||||
|
except Exception:
|
||||||
|
ctx = {"base_title": "Rose Ash", "asset_url": "/static"}
|
||||||
|
|
||||||
|
# Try to fetch fragments, but don't fail if they're unreachable
|
||||||
|
try:
|
||||||
|
from shared.infrastructure.fragments import fetch_fragments
|
||||||
|
from shared.infrastructure.cart_identity import current_cart_identity
|
||||||
|
user = getattr(g, "user", None)
|
||||||
|
ident = current_cart_identity()
|
||||||
|
cart_params = {}
|
||||||
|
if ident["user_id"] is not None:
|
||||||
|
cart_params["user_id"] = ident["user_id"]
|
||||||
|
if ident["session_id"] is not None:
|
||||||
|
cart_params["session_id"] = ident["session_id"]
|
||||||
|
cart_mini_html, auth_menu_html, nav_tree_html = await fetch_fragments([
|
||||||
|
("cart", "cart-mini", cart_params or None),
|
||||||
|
("account", "auth-menu", {"email": user.email} if user else None),
|
||||||
|
("blog", "nav-tree", {"app_name": current_app.name, "path": request.path}),
|
||||||
|
])
|
||||||
|
ctx["cart_mini_html"] = cart_mini_html
|
||||||
|
ctx["auth_menu_html"] = auth_menu_html
|
||||||
|
ctx["nav_tree_html"] = nav_tree_html
|
||||||
|
except Exception:
|
||||||
|
ctx.setdefault("cart_mini_html", "")
|
||||||
|
ctx.setdefault("auth_menu_html", "")
|
||||||
|
ctx.setdefault("nav_tree_html", "")
|
||||||
|
|
||||||
|
# Try to hydrate post data from slug if not already available
|
||||||
segments = [s for s in request.path.strip("/").split("/") if s]
|
segments = [s for s in request.path.strip("/").split("/") if s]
|
||||||
slug = segments[0] if segments else None
|
slug = segments[0] if segments else None
|
||||||
|
post_data = getattr(g, "post_data", None)
|
||||||
|
|
||||||
if slug and not getattr(g, "post_data", None):
|
if slug and not post_data:
|
||||||
# Set g.post_slug / g.page_slug so app context processors
|
try:
|
||||||
# (inject_post, etc.) can pick it up.
|
from shared.infrastructure.data_client import fetch_data
|
||||||
from shared.infrastructure.data_client import fetch_data
|
raw = await fetch_data(
|
||||||
raw = await fetch_data(
|
"blog", "post-by-slug",
|
||||||
"blog", "post-by-slug",
|
params={"slug": slug},
|
||||||
params={"slug": slug},
|
required=False,
|
||||||
required=False,
|
)
|
||||||
)
|
if raw:
|
||||||
if raw:
|
post_data = {
|
||||||
g.post_slug = slug
|
"post": {
|
||||||
g.page_slug = slug # cart uses page_slug
|
"id": raw["id"],
|
||||||
g.post_data = {
|
"title": raw["title"],
|
||||||
"post": {
|
"slug": raw["slug"],
|
||||||
"id": raw["id"],
|
"feature_image": raw.get("feature_image"),
|
||||||
"title": raw["title"],
|
},
|
||||||
"slug": raw["slug"],
|
}
|
||||||
"feature_image": raw.get("feature_image"),
|
except Exception:
|
||||||
"status": raw["status"],
|
pass
|
||||||
"visibility": raw["visibility"],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
# Also set page_post for cart app
|
|
||||||
from shared.contracts.dtos import PostDTO, dto_from_dict
|
|
||||||
post_dto = dto_from_dict(PostDTO, raw)
|
|
||||||
if post_dto and getattr(post_dto, "is_page", False):
|
|
||||||
g.page_post = post_dto
|
|
||||||
|
|
||||||
# Build template context (runs app context processors → fragments)
|
|
||||||
from shared.sexp.page import get_template_context
|
|
||||||
ctx = await get_template_context()
|
|
||||||
|
|
||||||
# Build headers: root header + post header if available
|
|
||||||
from shared.sexp.jinja_bridge import render
|
|
||||||
from shared.sexp.helpers import (
|
|
||||||
root_header_html, call_url, full_page,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# Root header (site nav bar)
|
||||||
|
from shared.sexp.helpers import root_header_html
|
||||||
hdr = root_header_html(ctx)
|
hdr = root_header_html(ctx)
|
||||||
|
|
||||||
post = ctx.get("post") or {}
|
# Post breadcrumb if we resolved a post
|
||||||
|
post = (post_data or {}).get("post") or ctx.get("post") or {}
|
||||||
if post.get("slug"):
|
if post.get("slug"):
|
||||||
title = (post.get("title") or "")[:160]
|
title = (post.get("title") or "")[:160]
|
||||||
feature_image = post.get("feature_image")
|
feature_image = post.get("feature_image")
|
||||||
|
|||||||
Reference in New Issue
Block a user