From c8a6b318cb9cc0561cfe971be17461f28e34eeb7 Mon Sep 17 00:00:00 2001 From: AdrianoDev Date: Thu, 3 Sep 2026 12:53:32 +0000 Subject: [PATCH] Dopo il login si atterra sulla scelta della piattaforma Chiesto da Adriano: si entra e si sceglie dove andare, anche quando la scelta e' una sola. Prima ogni ruolo saltava dritto nella sua sezione. Non basta cambiare landingFor: /piattaforme era riservato ai ruoli admin e piattaforme, quindi mandarci un cliente avrebbe prodotto un CICLO di redirect - il middleware nega la pagina, rimanda a landingFor, landingFor rimanda dove il middleware nega. L'indice diventa la casa comune (aperto a ogni ruolo), mentre Stress Index, che sta sotto lo stesso percorso, resta riservato: la sua regola viene prima, perche' vince la prima che combacia. L'indice mostra a ognuno solo le sezioni che puo' aprire, quindi la scelta non espone mai una porta chiusa. Un test nuovo presidia l'invariante che rende impossibile il ciclo: la destinazione di landingFor deve essere accessibile a TUTTI i ruoli. Verificato in produzione: il cliente demo atterra sulla scelta e vede la sola card Longevity, adriano vede Campus e Stress Index, e da li' il percorso prosegue fino al referto. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EndnceRA5WnA6rvA5iV9WL --- apps/platforms/src/lib/auth.ts | 26 +++++++++++++++++++------- apps/platforms/tests/accessi.test.ts | 20 +++++++++++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/apps/platforms/src/lib/auth.ts b/apps/platforms/src/lib/auth.ts index 695d502..365d4de 100644 --- a/apps/platforms/src/lib/auth.ts +++ b/apps/platforms/src/lib/auth.ts @@ -61,9 +61,15 @@ export function logout(db: Database.Database, token: string): void { // /api/admin: anche /campus, /piattaforme e /longevity) vive in isProtectedPath, più sotto: // qui arrivano solo utenti già autenticati su una di quelle rotte. const RULES: [RegExp, Role[]][] = [ - // Le due piattaforme storiche: il Biohacking Campus (contenuti) e Stress Index. + // ⚠️ L'INDICE /piattaforme e' la casa comune: lo apre chiunque sia loggato, perche' e' + // la pagina su cui si atterra dopo il login (deciso da Adriano il 03/09/2026). Mostra a + // ognuno solo le sezioni che puo' aprire davvero — il filtro e' in data/piattaforme. + // Stress Index sta SOTTO quel percorso ma resta riservato, quindi la sua regola viene + // prima: vince la prima che combacia, e la piu' specifica deve poter parlare per prima. + [/^\/piattaforme\/stress-index(\/|$)/, ['admin', 'piattaforme']], + [/^\/piattaforme\/?$/, [...ROLES]], + // Il Biohacking Campus (contenuti del corso). [/^\/campus(\/|$)/, ['admin', 'piattaforme']], - [/^\/piattaforme(\/|$)/, ['admin', 'piattaforme']], // Longevity: il cliente vede solo il proprio spazio, il gestionale è del trainer. [/^\/longevity\/gestionale(\/|$)/, ['admin', 'trainer']], [/^\/api\/longevity\/gestionale(\/|$)/, ['admin', 'trainer']], @@ -130,12 +136,18 @@ export function isRole(v: unknown): v is Role { } /** - * Dove atterra chi ha appena fatto il login. ⚠️ `/admin` non esiste in quest'app: il - * fondo della scala è l'indice delle piattaforme, non un pannello. + * Dove atterra chi ha appena fatto il login: **sempre l'indice delle piattaforme**, per + * tutti i ruoli (deciso da Adriano il 03/09/2026 — si entra e si sceglie dove andare, + * anche quando la scelta e' una sola). + * + * ⚠️ Questa funzione e' anche il "torna a casa" del middleware quando un ruolo apre una + * sezione che non gli spetta. Quindi la sua destinazione DEVE essere aperta a ogni ruolo, + * o si forma un ciclo di redirect: il middleware nega la pagina, rimanda a landingFor, e + * landingFor rimanda dove il middleware nega di nuovo. E' esattamente il guasto che il + * sito ha rischiato lo stesso giorno con gli account emigrati. Un test lo presidia + * girando su tutti i ruoli. */ -export function landingFor(role: string): string { - if (role === 'cliente') return '/longevity/io'; - if (role === 'trainer') return '/longevity/gestionale'; +export function landingFor(_role: string): string { return '/piattaforme'; } diff --git a/apps/platforms/tests/accessi.test.ts b/apps/platforms/tests/accessi.test.ts index 9144210..bd7c183 100644 --- a/apps/platforms/tests/accessi.test.ts +++ b/apps/platforms/tests/accessi.test.ts @@ -34,13 +34,20 @@ describe('utenti e sessioni di quest\'app', () => { for (const finto of ['campus', 'superuser', 'user', 'Admin', '', 'cliente ']) expect(isRole(finto)).toBe(false); }); - it('landingFor manda ognuno a casa propria, e nessuno su /admin', () => { - expect(landingFor('cliente')).toBe('/longevity/io'); - expect(landingFor('trainer')).toBe('/longevity/gestionale'); - expect(landingFor('piattaforme')).toBe('/piattaforme'); - expect(landingFor('admin')).toBe('/piattaforme'); + it('dopo il login si atterra sulla scelta della piattaforma, qualunque sia il ruolo', () => { + // Deciso da Adriano il 03/09/2026: si entra e si sceglie, anche quando la scelta e' una + // sola. Prima ogni ruolo saltava direttamente nella sua sezione. + for (const ruolo of ROLES) expect(landingFor(ruolo)).toBe('/piattaforme'); for (const ruolo of ROLES) expect(landingFor(ruolo)).not.toMatch(/^\/admin/); }); + + it('⚠️ la pagina di atterraggio è aperta a TUTTI i ruoli, o è un ciclo di redirect', () => { + // landingFor e' anche il "torna a casa" del middleware quando un ruolo apre una sezione + // che non gli spetta: se la sua destinazione fosse a sua volta negata, il middleware + // rimanderebbe li' all'infinito. E' il guasto che il sito ha rischiato lo stesso giorno + // con gli account emigrati, in un punto diverso del codice. + for (const ruolo of ROLES) expect(canAccessPath(ruolo, landingFor(ruolo))).toBe(true); + }); }); describe('canAccessPath — le tre sezioni', () => { @@ -65,6 +72,8 @@ describe('canAccessPath — le tre sezioni', () => { expect(canAccessPath('cliente', '/api/longevity/gestionale/misure')).toBe(false); expect(canAccessPath('cliente', '/campus')).toBe(false); expect(canAccessPath('cliente', '/piattaforme/stress-index')).toBe(false); + // ...ma l'indice sì: è la pagina di scelta, ed è la sua casa dopo il login. + expect(canAccessPath('cliente', '/piattaforme')).toBe(true); }); it('trainer: entrambe le parti di Longevity, niente Campus né Stress Index', () => { @@ -73,6 +82,7 @@ describe('canAccessPath — le tre sezioni', () => { expect(canAccessPath('trainer', '/api/longevity/gestionale/misure')).toBe(true); expect(canAccessPath('trainer', '/campus')).toBe(false); expect(canAccessPath('trainer', '/piattaforme/stress-index')).toBe(false); + expect(canAccessPath('trainer', '/piattaforme')).toBe(true); }); it('admin passa sempre', () => {