diff --git a/docs/superpowers/specs/2026-07-25-campus-design.md b/docs/superpowers/specs/2026-07-25-campus-design.md new file mode 100644 index 0000000..beaa35c --- /dev/null +++ b/docs/superpowers/specs/2026-07-25-campus-design.md @@ -0,0 +1,107 @@ +# Sezione Campus — design + +Data: 2026-07-25 · Branch: `feature/campus` · Stato: approvato (approccio A) + +## Obiettivo + +Integrare le guide di studio del corso Biohacking Campus (progetto esterno +`/data/Sorgenti/AI-OS/projects/personale/biohacking-campus`) come sezione +riservata del sito InsanityLab, visibile soltanto agli utenti autenticati con +un ruolo dedicato. Le pagine devono adottare lo stile grafico del sito +(font, colori, CSS globale), non quello del generatore statico originale. + +## Requisiti + +1. **Nuovo ruolo `campus`**: vede soltanto la sezione `/campus`; nessun + accesso al pannello admin. `admin` accede a tutto. `superuser` e `user` + non accedono alla sezione campus. +2. **Generazione contenuti**: uno script legge le guide sorgente + (`*.studio.md`) dal progetto biohacking-campus e produce i contenuti nel + repo del sito. Rigenerabile a ogni aggiornamento del corso. +3. **Gestione utenti**: la pagina `/admin/users` esistente deve permettere di + creare/assegnare il ruolo `campus`. +4. **Navigazione dedicata**: dentro `/campus` non compaiono Header e Footer + del sito; navigazione propria (sidebar aree/capitoli) più un solo + richiamo "← Torna alla home" verso `/`. +5. **Protezione totale**: pagine e immagini (assets slide) mai servite a + utenti non autorizzati. + +## Architettura + +### Ruoli e accessi (`src/lib/auth.ts`, `src/middleware.ts`) + +- `Role = 'admin' | 'superuser' | 'user' | 'campus'`. +- `RULES` esteso: `/campus/*` → `['campus', 'admin']` (admin passa sempre già + oggi per short-circuit). +- Middleware: `/campus/*` diventa percorso protetto (oltre a `/admin`, + `/api/admin`). Non autenticato → redirect a `/admin/login`. +- `landingFor('campus')` → `/campus` (dopo il login l'utente campus atterra + direttamente sulla sezione). +- Il ruolo `campus` NON deve accedere ai percorsi comuni dei loggati del + pannello (blog admin, upload): `canAccessAdminPath` ritorna `false` per + `campus` su `/admin/*` e `/api/admin/*` non esplicitamente consentiti. +- `/admin/users`: aggiunta option `campus` nelle due select (nuovo utente, + cambio ruolo). + +### Sync contenuti (`scripts/sync-campus.mjs`) + +- Input: root del progetto biohacking-campus (arg CLI, default il path locale + di sviluppo). +- Porta la struttura dati del generatore Python (`CHAPTERS`, `AREAS`, regex + intestazioni) in JS. Legge i `*.studio.md` da `studio/`, `qa/`, `slide/`, + `accelerator/`, `lezioni-live/`, `speaker/`, `consulenze/`, `masterclass/`. +- Converte markdown → HTML con `marked` (nuova devDependency) e riscrive i + riferimenti alle immagini slide verso `/campus/assets/...`. +- Output in `campus-content/` (root repo, committato): + - `nav.json` — aree, capitoli, lezioni ordinate (slug, titolo, sottotitolo, + docente, prev/next); + - `pages/.html` — frammento HTML del corpo di ogni guida; + - `assets/` — immagini slide copiate (~37 MB, commit una tantum). +- Idempotente: rigenera tutto e rimuove i file orfani. + +### Rendering (`src/pages/campus/`, `src/layouts/Campus.astro`) + +- `Campus.astro`: usa `global.css` e i font del sito; nessun `Header`/`Footer`; + sidebar con aree/capitoli/lezioni (voce corrente evidenziata) e link + "← Torna alla home"; responsive con menu mobile. +- Route SSR (runtime, non prerender — servono i file in `campus-content/`): + - `/campus` — portale con le card delle aree (da `nav.json`); + - `/campus/[...slug]` — pagina area (indice capitoli) o pagina guida + (frammento HTML + breadcrumb + prev/next). Slug non trovato → 404. +- `/campus/assets/[...path]` — endpoint che serve le immagini da + `campus-content/assets/` (protetto dal middleware come tutto `/campus`); + path traversal rifiutato; `Content-Type` da estensione; cache privata. +- Lettura file a runtime da `CAMPUS_DIR` (env, default `./campus-content`), + con cache in memoria di `nav.json`. + +### Deploy (`Dockerfile`) + +- Lo stage runtime copia solo `dist` e `scripts`; si aggiunge + `COPY --from=build /app/campus-content ./campus-content`. +- `/app/data` e `/app/uploads` sono volumi: `campus-content/` resta fuori da + entrambi, dentro l'immagine. Nessuna modifica a `compose.yaml`. + +## Error handling + +- `campus-content/` assente (sync mai eseguito): `/campus` risponde 404 con + messaggio esplicito nei log; il resto del sito non è impattato. +- Slug o asset inesistente → 404. Path traversal su assets → 400. +- Sync: file `.studio.md` con intestazione non riconosciuta → warning e skip, + exit code ≠ 0 solo per errori I/O. + +## Testing + +- `tests/auth-campus.test.ts`: matrice ruoli × percorsi (`campus` su + `/campus` sì, su `/admin/content` no; `user` su `/campus` no; ecc.), + `landingFor`, `isRole('campus')`. +- `tests/sync-campus.test.ts`: parsing intestazioni (lezione, Q&A, slide, + accelerator, consulenza, masterclass, speaker), generazione nav e + frammenti su fixture minima, riscrittura path immagini. +- Verifica manuale: login con utente `campus` (vede solo campus), `user` + (non vede campus), navigazione sidebar, immagini slide, mobile. + +## Fuori scope + +- Nessun tracciamento progressi/completamento lezioni. +- Nessuna ricerca full-text. +- Nessuna migrazione automatica degli utenti esistenti al nuovo ruolo.