feat: platform-aware storefront routing and billing improvements

Overhaul storefront URL routing to be platform-aware:
- Dev: /platforms/{code}/storefront/{store_code}/
- Prod: subdomain.platform.lu/ (internally rewritten to /storefront/)
- Add subdomain detection in PlatformContextMiddleware
- Add /storefront/ path rewrite for prod mode (subdomain/custom domain)
- Remove all silent platform fallbacks (platform_id=1)
- Add require_platform dependency for clean endpoint validation
- Update route registration, templates, module definitions, base_url calc
- Update StoreContextMiddleware for /storefront/ path detection
- Remove /stores/ from FrontendDetector STOREFRONT_PATH_PREFIXES

Billing service improvements:
- Add store_platform_sync_service to keep store_platforms in sync
- Make tier lookups platform-aware across billing services
- Add tiers for all platforms in seed data
- Add demo subscriptions to seed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 23:42:41 +01:00
parent d36783a7f1
commit 32acc76b49
56 changed files with 951 additions and 306 deletions

View File

@@ -63,7 +63,7 @@
{# Store Logo #}
<div class="flex items-center">
<a href="{{ base_url }}storefront/" class="flex items-center space-x-3">
<a href="{{ base_url }}" 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"
@@ -96,7 +96,7 @@
{% 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">
<a href="{{ base_url }}{{ page.slug }}" class="text-gray-700 dark:text-gray-300 hover:text-primary">
{{ page.title }}
</a>
{% endfor %}
@@ -117,7 +117,7 @@
{% if 'cart' in enabled_modules|default([]) %}
{# Cart #}
<a href="{{ base_url }}storefront/cart" class="relative p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
<a href="{{ base_url }}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>
@@ -187,7 +187,7 @@
{% endif %}
{# Account #}
<a href="{{ base_url }}storefront/account" class="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
<a href="{{ base_url }}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>
@@ -228,7 +228,7 @@
</a>
{% endfor %}
{% for page in header_pages|default([]) %}
<a href="{{ base_url }}storefront/{{ page.slug }}" @click="closeMobileMenu()"
<a href="{{ base_url }}{{ page.slug }}" @click="closeMobileMenu()"
class="block px-3 py-2 rounded-lg text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700">
{{ page.title }}
</a>
@@ -293,10 +293,10 @@
<h4 class="font-semibold mb-4">Quick Links</h4>
<ul class="space-y-2">
{% 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>
<li><a href="{{ base_url }}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 }}storefront/{{ page.slug }}" class="text-gray-600 hover:text-primary dark:text-gray-400">{{ page.title }}</a></li>
<li><a href="{{ base_url }}{{ page.slug }}" class="text-gray-600 hover:text-primary dark:text-gray-400">{{ page.title }}</a></li>
{% endfor %}
</ul>
</div>
@@ -307,7 +307,7 @@
<h4 class="font-semibold mb-4">Information</h4>
<ul class="space-y-2">
{% for page in col2_pages %}
<li><a href="{{ base_url }}storefront/{{ page.slug }}" class="text-gray-600 hover:text-primary dark:text-gray-400">{{ page.title }}</a></li>
<li><a href="{{ base_url }}{{ page.slug }}" class="text-gray-600 hover:text-primary dark:text-gray-400">{{ page.title }}</a></li>
{% endfor %}
</ul>
</div>
@@ -318,19 +318,19 @@
<h4 class="font-semibold mb-4">Quick Links</h4>
<ul class="space-y-2">
{% 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>
<li><a href="{{ base_url }}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>
<li><a href="{{ base_url }}about" class="text-gray-600 hover:text-primary dark:text-gray-400">About Us</a></li>
<li><a href="{{ base_url }}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 }}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>
<li><a href="{{ base_url }}faq" class="text-gray-600 hover:text-primary dark:text-gray-400">FAQ</a></li>
<li><a href="{{ base_url }}shipping" class="text-gray-600 hover:text-primary dark:text-gray-400">Shipping</a></li>
<li><a href="{{ base_url }}returns" class="text-gray-600 hover:text-primary dark:text-gray-400">Returns</a></li>
</ul>
</div>
{% endif %}