Spec: sezione campus riservata con ruolo dedicato
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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/<slug>.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.
|
||||
Reference in New Issue
Block a user