Consume coop fragments for unified navigation
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m2s

Add middleware to fetch nav-tree, auth-menu, and cart-mini fragments
from coop apps. Update base.html to render coop nav with fallback.
Add internal URL env vars for Docker networking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-24 22:28:31 +00:00
parent 956da6df2e
commit 28a5cc37d0
3 changed files with 65 additions and 4 deletions

View File

@@ -147,6 +147,47 @@ def create_app() -> FastAPI:
)
return response
# Coop fragment pre-fetch — inject nav-tree, auth-menu, cart-mini into
# request.state for full-page HTML renders. Skips HTMX, API, and
# internal paths. Failures are silent (fragments default to "").
_FRAG_SKIP = ("/auth/", "/api/", "/internal/", "/health", "/oembed",
"/ipfs/", "/download/", "/inbox", "/static/")
@app.middleware("http")
async def coop_fragments_middleware(request: Request, call_next):
path = request.url.path
if (
request.method != "GET"
or any(path.startswith(p) for p in _FRAG_SKIP)
or request.headers.get("hx-request")
or request.headers.get(fragments.FRAGMENT_HEADER)
):
request.state.nav_tree_html = ""
request.state.auth_menu_html = ""
request.state.cart_mini_html = ""
return await call_next(request)
from artdag_common.fragments import fetch_fragments as _fetch_frags
user = get_user_from_cookie(request)
auth_params = {"email": user.email} if user else {}
nav_params = {"app_name": "artdag", "path": path}
try:
nav_tree_html, auth_menu_html, cart_mini_html = await _fetch_frags([
("blog", "nav-tree", nav_params),
("account", "auth-menu", auth_params or None),
("cart", "cart-mini", None),
])
except Exception:
nav_tree_html = auth_menu_html = cart_mini_html = ""
request.state.nav_tree_html = nav_tree_html
request.state.auth_menu_html = auth_menu_html
request.state.cart_mini_html = cart_mini_html
return await call_next(request)
# Initialize Jinja2 templates
template_dir = Path(__file__).parent / "templates"
app.state.templates = create_jinja_env(template_dir)

View File

@@ -14,20 +14,36 @@ Art-DAG
<a href="/media" class="text-gray-300 hover:text-white {% if active_tab == 'media' %}text-white font-medium{% endif %}">Media{% if nav_counts and nav_counts.media %} ({{ nav_counts.media }}){% endif %}</a>
<a href="/storage" class="text-gray-300 hover:text-white {% if active_tab == 'storage' %}text-white font-medium{% endif %}">Storage{% if nav_counts and nav_counts.storage %} ({{ nav_counts.storage }}){% endif %}</a>
<a href="/download/client" class="text-gray-300 hover:text-white" title="Download CLI client">Client</a>
{% if request and request.state.nav_tree_html %}
<span class="text-gray-600">|</span>
<div class="flex items-center space-x-3 [&_a]:text-gray-400 [&_a:hover]:text-white [&_a]:text-sm [&_a]:no-underline [&_img]:w-5 [&_img]:h-5">
{{ request.state.nav_tree_html | safe }}
</div>
{% else %}
<span class="text-gray-600">|</span>
<a href="https://blog.rose-ash.com/" class="text-gray-400 hover:text-white text-sm">Blog</a>
<a href="https://market.rose-ash.com/" class="text-gray-400 hover:text-white text-sm">Market</a>
<a href="https://account.rose-ash.com/" class="text-gray-400 hover:text-white text-sm">Account</a>
{% endif %}
</nav>
{% endblock %}
{% block nav_right %}
{% if user %}
<div class="flex items-center space-x-4">
{% if request and request.state.cart_mini_html %}
<div class="[&_a]:text-gray-300 [&_a:hover]:text-white">
{{ request.state.cart_mini_html | safe }}
</div>
{% endif %}
{% if request and request.state.auth_menu_html %}
<div class="[&_a]:text-gray-300 [&_a:hover]:text-white [&_a]:text-sm [&_a]:no-underline">
{{ request.state.auth_menu_html | safe }}
</div>
{% elif user %}
<span class="text-gray-400">{{ user.username }}</span>
<a href="/auth/logout" class="text-gray-300 hover:text-white">Logout</a>
{% else %}
<a href="/auth/login" class="text-gray-300 hover:text-white">Login</a>
{% endif %}
</div>
{% else %}
<a href="/auth/login" class="text-gray-300 hover:text-white">Login</a>
{% endif %}
{% endblock %}

View File

@@ -73,6 +73,10 @@ services:
# IPFS_API multiaddr - used for all IPFS operations (add, cat, pin)
- IPFS_API=/dns/ipfs/tcp/5001
- CACHE_DIR=/data/cache
# Coop app internal URLs for fragment composition
- INTERNAL_URL_BLOG=http://blog:8000
- INTERNAL_URL_CART=http://cart:8000
- INTERNAL_URL_ACCOUNT=http://account:8000
# DATABASE_URL, ADMIN_TOKEN, ARTDAG_CLUSTER_KEY,
# L2_SERVER, L2_DOMAIN, IPFS_GATEWAY_URL from .env file
healthcheck: