Append synthetic artdag nav entry to blog's nav-tree handler so Art-DAG appears in the shared navigation across all 6 coop apps. Register artdag_url as Jinja global. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
107 lines
3.6 KiB
Python
107 lines
3.6 KiB
Python
"""Blog app fragment endpoints.
|
|
|
|
Exposes HTML fragments at ``/internal/fragments/<type>`` for consumption
|
|
by other coop apps via the fragment client.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from quart import Blueprint, Response, g, render_template, request
|
|
|
|
from shared.infrastructure.fragments import FRAGMENT_HEADER
|
|
from shared.services.navigation import get_navigation_tree
|
|
|
|
|
|
def register():
|
|
bp = Blueprint("fragments", __name__, url_prefix="/internal/fragments")
|
|
|
|
# Registry of fragment handlers: type -> async callable returning HTML str
|
|
_handlers: dict[str, object] = {}
|
|
|
|
@bp.before_request
|
|
async def _require_fragment_header():
|
|
if not request.headers.get(FRAGMENT_HEADER):
|
|
return Response("", status=403)
|
|
|
|
@bp.get("/<fragment_type>")
|
|
async def get_fragment(fragment_type: str):
|
|
handler = _handlers.get(fragment_type)
|
|
if handler is None:
|
|
return Response("", status=200, content_type="text/html")
|
|
html = await handler()
|
|
return Response(html, status=200, content_type="text/html")
|
|
|
|
# --- nav-tree fragment ---
|
|
async def _nav_tree_handler():
|
|
app_name = request.args.get("app_name", "")
|
|
path = request.args.get("path", "/")
|
|
first_seg = path.strip("/").split("/")[0]
|
|
menu_items = list(await get_navigation_tree(g.s))
|
|
|
|
# Append Art-DAG as a synthetic nav entry (not a DB MenuNode)
|
|
class _NavItem:
|
|
__slots__ = ("slug", "label", "feature_image")
|
|
def __init__(self, slug, label, feature_image=None):
|
|
self.slug = slug
|
|
self.label = label
|
|
self.feature_image = feature_image
|
|
|
|
menu_items.append(_NavItem("artdag", "art-dag"))
|
|
|
|
return await render_template(
|
|
"fragments/nav_tree.html",
|
|
menu_items=menu_items,
|
|
frag_app_name=app_name,
|
|
frag_first_seg=first_seg,
|
|
)
|
|
|
|
_handlers["nav-tree"] = _nav_tree_handler
|
|
|
|
# --- link-card fragment ---
|
|
async def _link_card_handler():
|
|
from shared.services.registry import services
|
|
from shared.infrastructure.urls import blog_url
|
|
|
|
slug = request.args.get("slug", "")
|
|
keys_raw = request.args.get("keys", "")
|
|
|
|
# Batch mode
|
|
if keys_raw:
|
|
slugs = [k.strip() for k in keys_raw.split(",") if k.strip()]
|
|
parts = []
|
|
for s in slugs:
|
|
parts.append(f"<!-- fragment:{s} -->")
|
|
post = await services.blog.get_post_by_slug(g.s, s)
|
|
if post:
|
|
parts.append(await render_template(
|
|
"fragments/link_card.html",
|
|
title=post.title,
|
|
feature_image=post.feature_image,
|
|
excerpt=post.custom_excerpt or post.excerpt,
|
|
published_at=post.published_at,
|
|
link=blog_url(f"/{post.slug}"),
|
|
))
|
|
return "\n".join(parts)
|
|
|
|
# Single mode
|
|
if not slug:
|
|
return ""
|
|
post = await services.blog.get_post_by_slug(g.s, slug)
|
|
if not post:
|
|
return ""
|
|
return await render_template(
|
|
"fragments/link_card.html",
|
|
title=post.title,
|
|
feature_image=post.feature_image,
|
|
excerpt=post.custom_excerpt or post.excerpt,
|
|
published_at=post.published_at,
|
|
link=blog_url(f"/{post.slug}"),
|
|
)
|
|
|
|
_handlers["link-card"] = _link_card_handler
|
|
|
|
# Store handlers dict on blueprint so app code can register handlers
|
|
bp._fragment_handlers = _handlers
|
|
|
|
return bp
|