fix: loyalty module end-to-end — merchant route, store menus, sidebar, API error handling
Some checks failed
CI / ruff (push) Successful in 10s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled

- Add merchant loyalty overview route and template (was 404)
- Fix store loyalty route paths to match menu URLs (/{store_code}/loyalty/...)
- Add loyalty rewards card to storefront account dashboard
- Fix merchant overview to resolve merchant via get_merchant_for_current_user_page
- Fix store login to use store's primary platform for JWT token (interim fix)
- Fix apiClient to attach status/errorCode to thrown errors (fixes error.status
  checks in 12+ JS files — loyalty settings, terminal, email templates, etc.)
- Hide "Add Product" sidebar button when catalog module is not enabled
- Add proposal doc for proper platform detection in store login flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 13:52:11 +01:00
parent 2833ff1476
commit cfce6c0ca4
8 changed files with 364 additions and 14 deletions

View File

@@ -0,0 +1,52 @@
# Store Login: JWT Token Gets Wrong Platform
**Status:** Open — needs design review on fallback strategy
**Date:** 2026-02-24
## Problem
When a user logs in to a store via `/platforms/loyalty/store/FASHIONHUB/login`, the JWT token should encode `platform_id=3` (loyalty). Instead, it gets `platform_id=2` (main). This causes the store sidebar menu to show the wrong modules — e.g., analytics (from "main") appears while loyalty (from "loyalty") is missing.
### Root Cause Chain
1. User visits `http://localhost:8000/platforms/loyalty/store/FASHIONHUB/login`
2. PlatformContextMiddleware detects `loyalty` and rewrites path to `/store/FASHIONHUB/login`**correct**
3. Login page renders with loyalty platform context — **correct**
4. JavaScript `login.js` POSTs to `apiClient.post('/store/auth/login', ...)` which resolves to `/api/v1/store/auth/login`**no platform prefix**
5. PlatformContextMiddleware sees `/api/v1/store/auth/login`, doesn't match `/platforms/*`, defaults to "main" platform
6. Store auth endpoint calls `get_current_platform(request)` → gets "main" (id=2) instead of "loyalty" (id=3)
7. Token encodes `platform_id=2`, all subsequent menu/API calls use the wrong platform
The Referer-based platform extraction in the middleware (`middleware/platform_context.py` lines 359-374) only handles `/api/v1/storefront/` paths, not `/api/v1/store/` paths.
### Why `is_primary` Is Wrong
A store can be subscribed to multiple platforms. The platform should be determined by the login URL context (which platform the user navigated from), not by a database default. Using `is_primary` would always pick the same platform regardless of how the user accessed the store.
## Key Constraint
- **Production:** One domain per platform (e.g., `omsflow.lu` for OMS, `loyaltyflow.lu` for loyalty). Store subdomains: `fashionhub.omsflow.lu`. Premium domains: `fashionhub.lu`.
- **Development:** Path-based: `/platforms/{code}/store/{store_code}/login`
- A store can be on multiple platforms and should show different menus depending on which platform URL the user logged in from.
## Current Workaround
`app/modules/tenancy/routes/api/store_auth.py` currently uses `is_primary` to resolve the platform from the store's `store_platforms` table. This works for single-platform stores but breaks for multi-platform stores.
## Files Involved
| File | Role |
|------|------|
| `middleware/platform_context.py` | Platform detection from URL/domain — doesn't cover `/api/v1/store/` paths |
| `middleware/store_context.py` | Store detection from URL/domain |
| `app/modules/tenancy/routes/api/store_auth.py` | Store login endpoint — creates JWT with platform_id |
| `app/modules/tenancy/static/store/js/login.js` | Frontend login — POSTs to `/api/v1/store/auth/login` |
| `static/shared/js/api-client.js` | API client — base URL is `/api/v1` (no platform prefix) |
| `models/schema/auth.py` | `UserLogin` schema — currently has `store_code` but not `platform_code` |
| `app/modules/core/routes/api/store_menu.py` | Menu API — reads `token_platform_id` from JWT |
## Open Questions
- What should the fallback strategy be when platform can't be determined from the login context?
- Should the solution also handle storefront customer login (which has the same issue)?
- Should the Referer-based detection in `platform_context.py` be extended to cover `/api/v1/store/` paths as a complementary fix?