Consume coop fragments for unified navigation
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m2s
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:
@@ -147,6 +147,47 @@ def create_app() -> FastAPI:
|
|||||||
)
|
)
|
||||||
return response
|
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
|
# Initialize Jinja2 templates
|
||||||
template_dir = Path(__file__).parent / "templates"
|
template_dir = Path(__file__).parent / "templates"
|
||||||
app.state.templates = create_jinja_env(template_dir)
|
app.state.templates = create_jinja_env(template_dir)
|
||||||
|
|||||||
@@ -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="/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="/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>
|
<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>
|
<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://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://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>
|
<a href="https://account.rose-ash.com/" class="text-gray-400 hover:text-white text-sm">Account</a>
|
||||||
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block nav_right %}
|
{% block nav_right %}
|
||||||
{% if user %}
|
|
||||||
<div class="flex items-center space-x-4">
|
<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>
|
<span class="text-gray-400">{{ user.username }}</span>
|
||||||
<a href="/auth/logout" class="text-gray-300 hover:text-white">Logout</a>
|
<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>
|
</div>
|
||||||
{% else %}
|
|
||||||
<a href="/auth/login" class="text-gray-300 hover:text-white">Login</a>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -73,6 +73,10 @@ services:
|
|||||||
# IPFS_API multiaddr - used for all IPFS operations (add, cat, pin)
|
# IPFS_API multiaddr - used for all IPFS operations (add, cat, pin)
|
||||||
- IPFS_API=/dns/ipfs/tcp/5001
|
- IPFS_API=/dns/ipfs/tcp/5001
|
||||||
- CACHE_DIR=/data/cache
|
- 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,
|
# DATABASE_URL, ADMIN_TOKEN, ARTDAG_CLUSTER_KEY,
|
||||||
# L2_SERVER, L2_DOMAIN, IPFS_GATEWAY_URL from .env file
|
# L2_SERVER, L2_DOMAIN, IPFS_GATEWAY_URL from .env file
|
||||||
healthcheck:
|
healthcheck:
|
||||||
|
|||||||
Reference in New Issue
Block a user