feat: add SQL query tool, platform debug, loyalty settings, and multi-module improvements
Some checks failed
CI / ruff (push) Successful in 14s
CI / pytest (push) Failing after 50m12s
CI / validate (push) Successful in 25s
CI / dependency-scanning (push) Successful in 32s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped

- Add admin SQL query tool with saved queries, schema explorer presets,
  and collapsible category sections (dev_tools module)
- Add platform debug tool for admin diagnostics
- Add loyalty settings page with owner-only access control
- Fix loyalty settings owner check (use currentUser instead of window.__userData)
- Replace HTTPException with AuthorizationException in loyalty routes
- Expand loyalty module with PIN service, Apple Wallet, program management
- Improve store login with platform detection and multi-platform support
- Update billing feature gates and subscription services
- Add store platform sync improvements and remove is_primary column
- Add unit tests for loyalty (PIN, points, stamps, program services)
- Update i18n translations across dev_tools locales

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 20:08:07 +01:00
parent a77a8a3a98
commit 319900623a
77 changed files with 5341 additions and 401 deletions

View File

@@ -177,6 +177,52 @@ Injects: request.state.store = <Store object>
**Why it's needed**: Each store storefront can have custom branding
## Login Platform Resolution
The store login endpoint (`POST /api/v1/store/auth/login`) resolves the platform through a 3-source priority chain. This is necessary because on localhost the API path carries no platform information (unlike production where the domain does).
### Source Priority
```
Source 1: Middleware (request.state.platform)
↓ if null or "main"
Source 2: Request body (platform_code field)
↓ if null
Source 3: Fallback (store's first active platform)
```
### Resolution by URL Pattern
| Environment | Login Page URL | API Request Host | Source 1 | Source 2 | Source 3 |
|-------------|---------------|-----------------|----------|----------|----------|
| **Dev path-based** | `/platforms/loyalty/store/ACME/login` | `localhost:8000` | null (localhost → "main" → skipped) | `"loyalty"` from JS | — |
| **Dev no prefix** | `/store/ACME/login` (after logout) | `localhost:8000` | null | `"loyalty"` from localStorage | — |
| **Dev fresh browser** | `/store/ACME/login` (first visit) | `localhost:8000` | null | null | First active platform for store |
| **Prod domain** | `omsflow.lu/store/ACME/login` | `omsflow.lu` | `"oms"` (domain lookup) | — | — |
| **Prod subdomain** | `acme.omsflow.lu/store/login` | `acme.omsflow.lu` | `"oms"` (root domain lookup) | — | — |
| **Prod custom domain** | `wizatech.shop/store/login` | `wizatech.shop` | `"oms"` (StoreDomain lookup) | — | — |
### Client-Side Platform Persistence
On successful login, `login.js` saves the platform to localStorage:
```
localStorage.setItem('store_platform', response.platform_code)
```
On the login page, the platform_code sent in the body uses this priority:
```
window.STORE_PLATFORM_CODE || localStorage.getItem('store_platform') || null
```
- `window.STORE_PLATFORM_CODE` is set by the server template when the URL contains `/platforms/{code}/`
- `localStorage.store_platform` persists across logout (intentionally not cleared)
- This ensures the logout → login cycle preserves platform context in dev mode
### Diagnostic Tools
- **Backend**: `/admin/platform-debug` — traces the full resolution pipeline for arbitrary host/path combos
- **Frontend**: `Ctrl+Shift+P` on any store page (localhost only) — shows JWT platform, localStorage, window globals, and consistency checks
## Naming Conventions
### Middleware File Organization