Parte IV — Chioma, suolo e cura
09 · La chioma: il sito
La chioma è ciò che si vede da lontano. In Baobáxia è il sito in SvelteKit, con lenti che guardano gli stessi saberes in modi diversi — ogni lente con un simbolo adinkra.
| Lente | Simbolo | Rotta |
|---|---|---|
| Archivio | /[lang]/busca, /[lang]/saber/[id] | |
| Blog | /[lang]/blog | |
| Mappa | /[lang]/mapa | |
| Agenda | /[lang]/agenda | |
| Timeline | /[lang]/timeline | |
| Mocambola | /[lang]/mocambolas | |
| Territori | /[lang]/territorios |
File da leggere
svelte.config.js,vite.config.ts— adapter-node, PWA.src/hooks.server.ts+src/lib/server/api.ts— il token per richiesta sul lato SvelteKit che gira sulla mucua (AsyncLocalStorage) e la cache breve degli elenchi.src/lib/api/client.ts— il client dell'API (≈ 1200 righe),fetchComEspera.src/routes/[lang]/+layout.server.tse+layout.svelte— ciò che ogni pagina carica.src/lib/stores/—auth,permissoes,tocador,galaxias,filtros.src/routes/[lang]/saber/[id]/+page.svelte— la pagina più completa.src/lib/components/RaizHistoria.svelte,Tocador.svelte,TeiaConstelacao.svelte,BaobaVivo.svelte— le viste con più disegno.src/lib/i18n/— pt-BR, en-US, es-ES, it-IT e aw-AW (A'uwẽ).
Struttura
flowchart TB
subgraph Navegador["Browser"]
PG["+page.svelte"]
ST["stores<br/>auth · permissoes · tocador"]
CL["ApiClient<br/>token dal localStorage"]
end
subgraph Node["SvelteKit sulla mucua (adapter-node, :3000)"]
HK["hooks.server.ts<br/>legge il cookie auth_token"]
ALS["AsyncLocalStorage<br/>{token, versione}"]
LD["+page.server.ts / +layout.server.ts<br/>load()"]
SAC["ServerApiClient<br/>cache di 30 s degli elenchi"]
end
API[("API /api/v3<br/>127.0.0.1:8000")]
PG --> ST --> CL -->|"/api/v3 via nginx"| API
HK --> ALS --> LD --> SAC -->|"SSR diretto"| API
Perché AsyncLocalStorage
Il client dell'API è un singleton nel processo Node. Se il token fosse un suo campo, la richiesta della persona A potrebbe usare il token della persona B a metà di un await. Ogni richiesta gira in un contesto proprio (authContext.run), e il ServerApiClient legge il token solo da quel contesto. La cache degli elenchi mette l'hash del token nella chiave: un visitatore non riceve mai ciò che è stato preso per l'admin.
Permessi nel front
Il front non decide mai i permessi: legge i booleani da GET /auth/me/permissoes, calcolati dallo stesso motore che il backend applica. Servono a nascondere pulsanti; il 403 resta la verità.
Radici: liste, storie e libri
La lente Radici (/[lang]/raizes) mostra le collezioni, e ogni formato ha il suo modo di essere visto: la lista, la storia (una radice che scende lungo la pagina) e il libro. Un libro è una collezione con formato: livro (data.livro contiene sottotitolo, autori, introduzione, copertina). I capitoli sono i membri, in ordine; il campo parte di ogni membro raggruppa l'indice. src/lib/livros/livro.ts monta il libro a partire da GET /colecoes/{uuid}; /raizes/[id] mostra la copertina con l'indice e /raizes/[id]/[n] è la lettura di un capitolo. I diagrammi Mermaid sono disegnati dall'action lib/actions/mermaid.ts, con la libreria inclusa nel build (funziona senza internet). Titolo, corpo, parti e introduzione possono essere in più lingue ({pt: …, en: …, es: …}): il sito mostra la lingua dell'URL e torna al portoghese quando manca la traduzione — questo corso è fatto così.
I capitoli vivono nel libro: l'elenco dell'Archivio e la home non li mostrano (SaberService.search con application="acervo" e senza testo lascia fuori i membri delle raccolte formato: livro). In una ricerca per testo compaiono; ?capitulos=true li include sempre.
Galassia in zip
In Galassie, chi amministra la mucua può esportare una galassia come .zip (cronologia git + allegati; services/galaxia_zip.py) e importare uno zip per installare la galassia senza rete — la stessa strada dell'installazione da URL, solo che l'origine è il file.
Il lettore e le storie
stores/tocador.svelte.ts mantiene una coda di tracce (audio/video) che attraversa le pagine. Una storia (collezione nel formato historia) conserva, per file (uuid::arquivo in historia.itens), i brani da riprodurre (cortes: [{de, ate}]), con titolo e testo propri. RaizHistoria.svelte disegna la storia come una radice che scende lungo la pagina.
Sicurezza nella chioma
- Il markdown dei saberes è reso da
lib/util/markdown.ts: fa l'escape dell'HTML e, dalla 3.3, accetta solo linkhttp(s),mailto,xmpp,tel,geoo relativi (hrefSeguro). Ognihrefche viene dai dati deve passare di lì. - Il token vive nel
localStoragee in un cookie letto dall'SSR — qualsiasi XSS ruba la sessione. Vedi il modulo 11.
Esercizi
- Esegui
npm run deve apri la pagina di un saber con la scheda di rete aperta. Quali chiamate partono dal browser e quali sono arrivate pronte dall'SSR? - Aggiungi una parola nuova nei cinque file di
src/lib/i18n/locales/e usala in un componente con$_('…'). - In
markdown.ts, scrivi un test (anche connode --experimental-strip-types) che garantisca che[x](javascript:alert(1))non diventi un link. npm run checkmostra ~55 vecchi errori di tipo. Scegli un file e azzera i suoi errori.
Da discutere
- Cosa significa avere l'interfaccia dell'archivio in una lingua indigena, oltre a tradurre parole? (Date, numeri, l'ordine delle cose, chi decide i termini.)
