feat: storefront subscription access guard + module-driven nav + URL rename
Add StorefrontAccessMiddleware that blocks storefront access for stores without an active subscription, returning a multilingual unavailable page (en/fr/de/lb) for page requests and JSON 403 for API requests. Multi-platform aware: resolves subscription for detected platform with fallback to primary. Also includes yesterday's session work: - Module-driven storefront navigation via FrontendType.STOREFRONT menu declarations - shop/ → storefront/ URL rename across 30+ templates - Subscription context (tier_code) passed to storefront templates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -63,7 +63,7 @@
|
||||
|
||||
{# Store Logo #}
|
||||
<div class="flex items-center">
|
||||
<a href="{{ base_url }}shop/" class="flex items-center space-x-3">
|
||||
<a href="{{ base_url }}storefront/" class="flex items-center space-x-3">
|
||||
{% if theme.branding.logo %}
|
||||
{# Show light logo in light mode, dark logo in dark mode #}
|
||||
<img x-show="!dark"
|
||||
@@ -84,25 +84,28 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{# Navigation #}
|
||||
{# Navigation — Home is always shown, module items are dynamic #}
|
||||
<nav class="hidden md:flex space-x-8">
|
||||
<a href="{{ base_url }}" class="text-gray-700 dark:text-gray-300 hover:text-primary">
|
||||
Home
|
||||
</a>
|
||||
<a href="{{ base_url }}shop/products" class="text-gray-700 dark:text-gray-300 hover:text-primary">
|
||||
Products
|
||||
{% for item in storefront_nav.get('nav', []) %}
|
||||
<a href="{{ base_url }}{{ item.route }}" class="text-gray-700 dark:text-gray-300 hover:text-primary">
|
||||
{{ _(item.label_key) }}
|
||||
</a>
|
||||
<a href="{{ base_url }}shop/about" class="text-gray-700 dark:text-gray-300 hover:text-primary">
|
||||
About
|
||||
</a>
|
||||
<a href="{{ base_url }}shop/contact" class="text-gray-700 dark:text-gray-300 hover:text-primary">
|
||||
Contact
|
||||
{% endfor %}
|
||||
{# CMS pages (About, Contact) are already dynamic via header_pages #}
|
||||
{% for page in header_pages|default([]) %}
|
||||
<a href="{{ base_url }}storefront/{{ page.slug }}" class="text-gray-700 dark:text-gray-300 hover:text-primary">
|
||||
{{ page.title }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
|
||||
{# Right side actions #}
|
||||
<div class="flex items-center space-x-4">
|
||||
|
||||
{% if 'catalog' in enabled_modules|default([]) %}
|
||||
{# Search #}
|
||||
<button @click="openSearch()" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -110,9 +113,11 @@
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
{% if 'cart' in enabled_modules|default([]) %}
|
||||
{# Cart #}
|
||||
<a href="{{ base_url }}shop/cart" class="relative p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<a href="{{ base_url }}storefront/cart" class="relative p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
|
||||
@@ -123,6 +128,7 @@
|
||||
style="background-color: var(--color-accent)">
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{# Theme toggle #}
|
||||
<button @click="toggleTheme()"
|
||||
@@ -181,7 +187,7 @@
|
||||
{% endif %}
|
||||
|
||||
{# Account #}
|
||||
<a href="{{ base_url }}shop/account" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<a href="{{ base_url }}storefront/account" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
|
||||
@@ -256,9 +262,11 @@
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4">Quick Links</h4>
|
||||
<ul class="space-y-2">
|
||||
<li><a href="{{ base_url }}shop/products" class="text-gray-600 hover:text-primary dark:text-gray-400">Products</a></li>
|
||||
{% if 'catalog' in enabled_modules|default([]) %}
|
||||
<li><a href="{{ base_url }}storefront/products" class="text-gray-600 hover:text-primary dark:text-gray-400">Products</a></li>
|
||||
{% endif %}
|
||||
{% for page in col1_pages %}
|
||||
<li><a href="{{ base_url }}shop/{{ page.slug }}" class="text-gray-600 hover:text-primary dark:text-gray-400">{{ page.title }}</a></li>
|
||||
<li><a href="{{ base_url }}storefront/{{ page.slug }}" class="text-gray-600 hover:text-primary dark:text-gray-400">{{ page.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -269,7 +277,7 @@
|
||||
<h4 class="font-semibold mb-4">Information</h4>
|
||||
<ul class="space-y-2">
|
||||
{% for page in col2_pages %}
|
||||
<li><a href="{{ base_url }}shop/{{ page.slug }}" class="text-gray-600 hover:text-primary dark:text-gray-400">{{ page.title }}</a></li>
|
||||
<li><a href="{{ base_url }}storefront/{{ page.slug }}" class="text-gray-600 hover:text-primary dark:text-gray-400">{{ page.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
@@ -279,18 +287,20 @@
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4">Quick Links</h4>
|
||||
<ul class="space-y-2">
|
||||
<li><a href="{{ base_url }}shop/products" class="text-gray-600 hover:text-primary dark:text-gray-400">Products</a></li>
|
||||
<li><a href="{{ base_url }}shop/about" class="text-gray-600 hover:text-primary dark:text-gray-400">About Us</a></li>
|
||||
<li><a href="{{ base_url }}shop/contact" class="text-gray-600 hover:text-primary dark:text-gray-400">Contact</a></li>
|
||||
{% if 'catalog' in enabled_modules|default([]) %}
|
||||
<li><a href="{{ base_url }}storefront/products" class="text-gray-600 hover:text-primary dark:text-gray-400">Products</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ base_url }}storefront/about" class="text-gray-600 hover:text-primary dark:text-gray-400">About Us</a></li>
|
||||
<li><a href="{{ base_url }}storefront/contact" class="text-gray-600 hover:text-primary dark:text-gray-400">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 class="font-semibold mb-4">Information</h4>
|
||||
<ul class="space-y-2">
|
||||
<li><a href="{{ base_url }}shop/faq" class="text-gray-600 hover:text-primary dark:text-gray-400">FAQ</a></li>
|
||||
<li><a href="{{ base_url }}shop/shipping" class="text-gray-600 hover:text-primary dark:text-gray-400">Shipping</a></li>
|
||||
<li><a href="{{ base_url }}shop/returns" class="text-gray-600 hover:text-primary dark:text-gray-400">Returns</a></li>
|
||||
<li><a href="{{ base_url }}storefront/faq" class="text-gray-600 hover:text-primary dark:text-gray-400">FAQ</a></li>
|
||||
<li><a href="{{ base_url }}storefront/shipping" class="text-gray-600 hover:text-primary dark:text-gray-400">Shipping</a></li>
|
||||
<li><a href="{{ base_url }}storefront/returns" class="text-gray-600 hover:text-primary dark:text-gray-400">Returns</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -321,7 +331,7 @@
|
||||
<script defer src="{{ url_for('static', path='shared/js/icons.js') }}"></script>
|
||||
|
||||
{# 4. Base Shop Layout (Alpine.js component - must load before Alpine) #}
|
||||
<script defer src="{{ url_for('static', path='storefront/js/storefront-layout.js') }}"></script>
|
||||
<script defer src="{{ url_for('core_static', path='storefront/js/storefront-layout.js') }}"></script>
|
||||
|
||||
{# 5. Utilities #}
|
||||
<script defer src="{{ url_for('static', path='shared/js/utils.js') }}"></script>
|
||||
|
||||
Reference in New Issue
Block a user