fix: make storefront API referer extraction platform-aware and fix script loading
Some checks failed
CI / ruff (push) Successful in 12s
CI / validate (push) Successful in 26s
CI / dependency-scanning (push) Successful in 29s
CI / pytest (push) Failing after 3h11m9s
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled

Two bugs causing "Program Not Available" on storefront enrollment:

1. extract_store_from_referer() was not platform-aware — used
   settings.main_domain (wizard.lu) instead of platform.domain
   (rewardflow.lu) for subdomain detection, and restricted path-based
   extraction to localhost only. Now mirrors the platform-aware logic
   from _detect_store_from_host_and_path(): checks platform.domain for
   subdomain detection (fashionhub.rewardflow.lu → fashionhub) and
   allows path-based extraction on platform domains
   (rewardflow.lu/storefront/FASHIONHUB/... → FASHIONHUB).

2. Storefront JS scripts (enroll, dashboard, history) were missing
   defer attribute, causing them to execute before log-config.js and
   crash on window.LogConfig access. Also fix quote escaping in
   server-side rendered x-text expressions for French translations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 20:01:07 +01:00
parent adec17cd02
commit bc7431943a
4 changed files with 56 additions and 18 deletions

View File

@@ -230,5 +230,5 @@
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}
<script src="{{ url_for('loyalty_static', path='storefront/js/loyalty-dashboard.js') }}"></script> <script defer src="{{ url_for('loyalty_static', path='storefront/js/loyalty-dashboard.js') }}"></script>
{% endblock %} {% endblock %}

View File

@@ -119,7 +119,7 @@
class="w-full py-3 px-4 text-white font-semibold rounded-lg transition-colors disabled:opacity-50" class="w-full py-3 px-4 text-white font-semibold rounded-lg transition-colors disabled:opacity-50"
:style="'background-color: ' + (program?.card_color || 'var(--color-primary)')"> :style="'background-color: ' + (program?.card_color || 'var(--color-primary)')">
<span x-show="enrolling" x-html="$icon('spinner', 'w-5 h-5 inline animate-spin mr-2')"></span> <span x-show="enrolling" x-html="$icon('spinner', 'w-5 h-5 inline animate-spin mr-2')"></span>
<span x-text="enrolling ? '{{ _('loyalty.enrollment.form.joining') }}' : '{{ _('loyalty.enrollment.form.join_button', points=program.welcome_bonus_points if program else 0) }}'"></span> <span x-text="enrolling ? '{{ _('loyalty.enrollment.form.joining')|replace("'", "\\'") }}' : '{{ _('loyalty.enrollment.form.join_button', points=program.welcome_bonus_points if program else 0)|replace("'", "\\'") }}'"></span>
</button> </button>
</form> </form>
@@ -170,5 +170,5 @@
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}
<script src="{{ url_for('loyalty_static', path='storefront/js/loyalty-enroll.js') }}"></script> <script defer src="{{ url_for('loyalty_static', path='storefront/js/loyalty-enroll.js') }}"></script>
{% endblock %} {% endblock %}

View File

@@ -105,5 +105,5 @@
{% endblock %} {% endblock %}
{% block extra_scripts %} {% block extra_scripts %}
<script src="{{ url_for('loyalty_static', path='storefront/js/loyalty-history.js') }}"></script> <script defer src="{{ url_for('loyalty_static', path='storefront/js/loyalty-history.js') }}"></script>
{% endblock %} {% endblock %}

View File

@@ -295,13 +295,17 @@ class StoreContextManager:
Extract store context from Referer header. Extract store context from Referer header.
Used for storefront API requests where store context comes from the page Used for storefront API requests where store context comes from the page
that made the API call (e.g., JavaScript on /stores/orion/storefront/products that made the API call (e.g., JavaScript on /storefront/FASHIONHUB/loyalty/join
calling /api/v1/storefront/products). calling /api/v1/storefront/loyalty/program).
Platform-aware: uses request.state.platform.domain (e.g. rewardflow.lu)
for subdomain detection, not just settings.main_domain (wizard.lu).
Extracts store from Referer URL patterns: Extracts store from Referer URL patterns:
- http://localhost:8000/stores/orion/storefront/... → orion - localhost:8000/platforms/loyalty/storefront/FASHIONHUB/... → FASHIONHUB (dev path)
- http://orion.platform.com/storefront/... → orion (subdomain) # noqa - rewardflow.lu/storefront/FASHIONHUB/... → FASHIONHUB (prod platform domain path)
- http://custom-domain.com/storefront/... → custom-domain.com # noqa - fashionhub.rewardflow.lu/... → fashionhub (prod platform subdomain)
- custom-domain.com/... → custom-domain.com (prod custom domain)
Returns store context dict or None if unable to extract. Returns store context dict or None if unable to extract.
""" """
@@ -331,20 +335,34 @@ class StoreContextManager:
}, },
) )
# Method 1: Path-based detection from referer path (local hosts only) # Determine platform domain for platform-aware detection
# /platforms/oms/storefront/WIZATECH/products → WIZATECH # (e.g. rewardflow.lu from the loyalty platform object)
# /stores/orion/storefront/products → orion platform = getattr(request.state, "platform", None)
# /storefront/WIZATECH/products → WIZATECH platform_own_domain = getattr(platform, "domain", None) if platform else None
# Note: For subdomain/custom domain hosts, the store code is NOT in the path is_platform_domain = (
# (e.g., orion.platform.com/storefront/products — "products" is a page, not a store) platform_own_domain and referer_host == platform_own_domain
)
is_subdomain_of_platform = (
platform_own_domain
and referer_host != platform_own_domain
and referer_host.endswith(f".{platform_own_domain}")
)
# Method 1: Path-based detection from referer path
# Works on localhost (dev) AND on platform domains (prod path-based routing)
# /platforms/oms/storefront/WIZATECH/products → WIZATECH (dev)
# /storefront/FASHIONHUB/loyalty/join → FASHIONHUB (prod platform domain)
# Note: For subdomain hosts, path segments after /storefront/ are pages, not store codes
is_local_referer = referer_host in ("localhost", "127.0.0.1", "testserver") is_local_referer = referer_host in ("localhost", "127.0.0.1", "testserver")
if is_local_referer and referer_path.startswith("/platforms/"): use_path_detection = is_local_referer or is_platform_domain
if use_path_detection and referer_path.startswith("/platforms/"):
# Strip /platforms/{code}/ to get clean path # Strip /platforms/{code}/ to get clean path
after_platforms = referer_path[11:] # Remove "/platforms/" after_platforms = referer_path[11:] # Remove "/platforms/"
parts = after_platforms.split("/", 1) parts = after_platforms.split("/", 1)
referer_path = "/" + parts[1] if len(parts) > 1 and parts[1] else "/" referer_path = "/" + parts[1] if len(parts) > 1 and parts[1] else "/"
if is_local_referer and referer_path.startswith(("/stores/", "/store/", "/storefront/")): if use_path_detection and referer_path.startswith(("/stores/", "/store/", "/storefront/")):
if referer_path.startswith("/storefront/"): if referer_path.startswith("/storefront/"):
prefix = "/storefront/" prefix = "/storefront/"
elif referer_path.startswith("/stores/"): elif referer_path.startswith("/stores/"):
@@ -371,7 +389,25 @@ class StoreContextManager:
} }
# Method 2: Subdomain detection from referer host # Method 2: Subdomain detection from referer host
# orion.platform.com → orion # fashionhub.rewardflow.lu → fashionhub (platform subdomain)
# store1.wizard.lu → store1 (main domain subdomain)
if is_subdomain_of_platform:
subdomain = referer_host.split(".")[0]
logger.debug(
f"[STORE] Extracted store from Referer platform subdomain: {subdomain}",
extra={
"subdomain": subdomain,
"method": "referer_platform_subdomain",
"platform_domain": platform_own_domain,
},
)
return {
"subdomain": subdomain,
"detection_method": "referer_subdomain",
"host": referer_host,
"referer": referer,
}
main_domain = getattr(settings, "main_domain", "platform.com") main_domain = getattr(settings, "main_domain", "platform.com")
if "." in referer_host: if "." in referer_host:
parts = referer_host.split(".") parts = referer_host.split(".")
@@ -397,6 +433,8 @@ class StoreContextManager:
# custom-shop.com → custom-shop.com # custom-shop.com → custom-shop.com
is_custom_domain = ( is_custom_domain = (
referer_host referer_host
and not is_platform_domain
and not is_subdomain_of_platform
and not referer_host.endswith(f".{main_domain}") and not referer_host.endswith(f".{main_domain}")
and referer_host != main_domain and referer_host != main_domain
and referer_host not in ["localhost", "127.0.0.1"] and referer_host not in ["localhost", "127.0.0.1"]