From 451eab42ac4f1f0a588cacaccb960934dc50eb1f Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Wed, 6 May 2026 01:51:18 +0200 Subject: [PATCH] chore(rules): purge apps/portal from 102_multi_tenancy.mdc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Surgical updates reflecting post-WS-3 single-SPA reality. The OrganisationScope rules, three-level authorization, and invitation flow are unchanged — they're still the canonical guidance. Changes: - globs: drop apps/portal/**/*.{vue,ts} - Portal Architecture: "two access modes in apps/portal/" -> "two access modes under /portal/* routes within apps/app/" - Token flow URL example: portal.crewli.app -> crewli.app/portal/ with note about 301 redirect from legacy host - Login flow URL: portal.crewli.app -> crewli.app - CORS allowed_origins: drop FRONTEND_PORTAL_URL line - Production example: collapse dual-host to single-host with pointer to AUTH_ARCHITECTURE.md §11 for the legacy env key --- .cursor/rules/102_multi_tenancy.mdc | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.cursor/rules/102_multi_tenancy.mdc b/.cursor/rules/102_multi_tenancy.mdc index 55ca8961..5e9b5f0f 100644 --- a/.cursor/rules/102_multi_tenancy.mdc +++ b/.cursor/rules/102_multi_tenancy.mdc @@ -1,6 +1,6 @@ --- description: Multi-tenancy and portal architecture rules for Crewli -globs: ["api/**/*.php", "apps/portal/**/*.{vue,ts}"] +globs: ["api/**/*.php"] alwaysApply: true --- @@ -92,16 +92,20 @@ Route::middleware(['auth:sanctum', 'event.role:event_manager'])->group(...); ## Portal Architecture -### Two Access Modes in One App (`apps/portal/`) +### Two Access Modes Under `/portal/*` Routes (within `apps/app/`) + +Post-WS-3, the portal lives in the main SPA at `/portal/*` routes. +Two access modes coexist: | Mode | Middleware | Users | Token Source | -|------|-----------|-------|-------------| -| Login | `auth:sanctum` | Volunteers, Crew | Bearer token from login | +|------|------------|-------|--------------| +| Login | `auth:sanctum` | Volunteers, Crew | Bearer token from login (httpOnly cookie) | | Token | `portal.token` | Artists, Suppliers, Press | URL token param: `?token=ULID` | ### Token-Based Authentication Flow ``` -1. Artist/supplier receives email with link: https://portal.crewli.app/advance?token=01HQ3K... +1. Artist/supplier receives email with link: https://crewli.app/portal/advance?token=01HQ3K... + (Legacy portal.crewli.app links 301-redirect, preserving the token query param) 2. Portal detects token in URL query parameter 3. POST /api/v1/portal/token-auth { token: '01HQ3K...' } 4. Backend validates token against artists.portal_token or production_requests.token @@ -111,9 +115,9 @@ Route::middleware(['auth:sanctum', 'event.role:event_manager'])->group(...); ### Login-Based Authentication Flow ``` -1. Volunteer navigates to https://portal.crewli.app/login +1. Volunteer navigates to https://crewli.app/login 2. Enters email + password -3. POST /api/v1/auth/login (same endpoint as apps/app/) +3. POST /api/v1/auth/login 4. Returns user + organisations + event roles 5. Portal shows volunteer-specific views (My Shifts, Claim Shifts, Messages, Profile) ``` @@ -190,12 +194,11 @@ class PortalTokenMiddleware // config/cors.php 'allowed_origins' => [ env('FRONTEND_APP_URL', 'http://localhost:5174'), - env('FRONTEND_PORTAL_URL', 'http://localhost:5175'), ], 'supports_credentials' => true, ``` -Production example (subdomains on **crewli.app**): `FRONTEND_APP_URL=https://crewli.app`, `FRONTEND_PORTAL_URL=https://portal.crewli.app`, and `SANCTUM_STATEFUL_DOMAINS=crewli.app,portal.crewli.app`. +Production example (registered domain **crewli.app**): `FRONTEND_APP_URL=https://crewli.app` and `SANCTUM_STATEFUL_DOMAINS=crewli.app`. The legacy `FRONTEND_PORTAL_URL` env key is retained for outbound-email controllers (per AUTH_ARCHITECTURE.md §11), but resolves to the same host post-WS-3. ## Shift Claiming & Approval Flow