Files
InsanityLab-web/README.md
Adriano 12f6b088de README aggiornato: piattaforme, Stress Index, cestino del blog e note di deploy
Documenta il ruolo piattaforme al posto di campus, la piattaforma Stress
Index con le scelte fatte sullo stile e sulle isole React, il cestino
degli articoli e il caricamento degli articoli via script, oltre alle
trappole incontrate: il percorso nuovo del progetto Campus, la cache
dell'indice in sviluppo e la lettura del database in WAL dopo il deploy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 22:03:52 +02:00

15 KiB
Raw Permalink Blame History

InsanityLab Website

Sito ufficiale di InsanityLab: vetrina aziendale, blog integrato, form di contatti, piattaforme riservate ai clienti abilitati e sistema di contenuti modificabili senza toccare il codice. In produzione su https://insanitylab.it.

Descrizione

Il sito è costruito su Astro 7 in modalità server (SSR) con adapter Node.js standalone, servito in un container Docker dietro Traefik (TLS Let's Encrypt automatico). I contenuti testuali e le immagini della parte vetrina sono "taggati" e salvati su SQLite: si modificano da un pannello di amministrazione o direttamente in pagina, senza deploy. Il blog è gestito internamente con editor richtext, e il form di contatti invia email tramite SMTP.

Accanto alla vetrina vivono due sezioni riservate, raccolte sotto /piattaforme e visibili solo agli utenti abilitati: il Biohacking Campus (guide di studio generate da un progetto esterno) e Stress Index (piattaforma di monitoraggio HRV). Quest'ultima è l'unica parte del sito che usa React, sotto forma di isole limitate ai grafici e alle viste interattive.

Stack tecnologico

  • Framework: Astro 7 (output server, prerender = false sulle pagine dinamiche)
  • Runtime: Node.js 22 (adapter @astrojs/node standalone)
  • Database: SQLite via better-sqlite3
  • Autenticazione: sessioni con password hashate (bcryptjs)
  • Email: Nodemailer (SMTP)
  • Editor articoli: TipTap (rich text)
  • Isole interattive: React 19 via @astrojs/react, solo nella piattaforma Stress Index
  • Grafici: Recharts (caricati unicamente sulle pagine della piattaforma)
  • Font: @fontsource (Montserrat, Open Sans) — nessun CDN esterno
  • Sitemap: endpoint runtime /sitemap.xml (route vetrina + articoli pubblicati)
  • Test: Vitest
  • Linguaggio: TypeScript
  • Deploy: Docker (multistage, node:22-slim) + Traefik su VPS

Funzionalità principali

  • Vetrina: home, chi siamo, training, servizi (con schede di dettaglio), blog, contatti, landing "Promo ed eventi".
  • Contenuti taggati: ogni testo/immagine ha un tag (es. home.hero.title-line-1) modificabile dal pannello o in pagina.
  • Modifica inline: chi ha i permessi può attivare l'overlay dei tag e modificare i contenuti direttamente mentre naviga il sito.
  • Blog con editor TipTap, immagini caricate, firma autore, proprietà degli articoli per ruolo, filtro per tematica senza ricaricare la pagina e conteggio degli articoli per categoria.
  • Cestino degli articoli: l'eliminazione sposta in /admin/cestino, da dove si ripristina o si cancella in via definitiva.
  • Gestione utenti con quattro ruoli (admin, superuser, user, piattaforme), creazione su pagina dedicata e password impostabile a mano o generata.
  • Piattaforme riservate (/piattaforme): indice delle sezioni accessibili ai soli utenti abilitati — Biohacking Campus e Stress Index.
  • Form contatti con validazione, honeypot antispam, ratelimit e invio email via SMTP.

Installazione (sviluppo)

Prerequisiti

  • Node.js 18+ e npm

Setup

npm install
cp .env.example .env          # poi compila i valori reali
npm run create-user -- admin <password> admin
npm run dev                   # http://localhost:4321

Il database data/insanitylab.db viene creato al primo avvio ed è popolato automaticamente con i contenuti di default (seed) e le migrazioni idempotenti.

Comandi

Comando Descrizione
npm run dev Server di sviluppo (localhost:4321)
npm run build Build di produzione
npm run preview Anteprima locale della build
npm run start Server di produzione (node --env-file=.env)
npm run test Test con Vitest
npm run create-user Crea/aggiorna utente: -- <nome> <password> [admin|superuser|user|piattaforme]
npm run sync-campus Rigenera i contenuti del Campus: -- <path-progetto-biohacking-campus>
npm run seed-blog Carica i venti articoli divulgativi tratti dal corso base del Campus (salta quelli già presenti)

Gestione contenuti

I testi e le immagini della vetrina sono blocchi taggati salvati nella tabella content_blocks. Ogni blocco ha un tag stabile (es. home.hero.title-line-1), un valore, eventuali stili predefiniti, un tipo (text / html / image) e un GUID univoco.

  • Pannello /admin/content: i blocchi sono raggruppati per pagina, con ricerca per tag o contenuto, upload delle immagini e ripristino all'originale.
  • Modifica inline: attivando "Mostra tag" dal menu utente, ogni blocco mostra un badge (nome tag + matita) che apre una finestra di modifica direttamente in pagina.
  • Componenti <T> e <TImg>: rendono i blocchi con fallback automatico al seed quando un tag non è ancora nel database.

Il seed dei contenuti è in src/data/content-seed.ts; le variazioni sui contenuti già esistenti (rinomine, testi aggiornati) si applicano tramite migrazioni idempotenti in src/lib/db.ts, così da non sovrascrivere le modifiche fatte dal pannello.

Nota tecnica: gli elementi resi dai componenti <T>/<TImg> non ereditano il data-astro-cid del componente che li usa. Uno stile scoped basato solo sulla classe (es. .hero__line) non li raggiunge: va ancorato a un contenitore che ha il cid usando :global() (es. .hero__text :global(.hero__line)).

Blog

Gli articoli stanno nella tabella posts e si gestiscono da /admin. La pagina pubblica /blog filtra per tematica e pagina senza ricaricare — si scarica la stessa pagina e si sostituiscono soltanto griglia e filtri — mantenendo indirizzi veri e cronologia del browser: senza JavaScript la navigazione funziona come prima.

L'eliminazione di un articolo non cancella nulla: valorizza deleted_at e lo sposta nel cestino (/admin/cestino), da dove si ripristina o si elimina davvero. Ogni lettura pubblica — elenco, recenti, archivio per mese, pagina del singolo articolo — esclude il cestino, e chi non ha ruolo di gestione vede nel cestino soltanto i propri articoli.

Lo script npm run seed-blog carica venti articoli divulgativi ricavati dal corso base del Campus, dieci tecnici e dieci educativi. È versionato apposta: il database di produzione è un volume del VPS e il deploy non lo tocca, quindi articoli creati a mano in locale resterebbero in locale. Lo script salta gli slug già presenti e crea, se mancano, i due utenti che firmano gli articoli — la firma pubblica è lo username dell'autore.

Ruoli e accessi

Quattro ruoli, con autorizzazione a matrice di prefissi in src/lib/auth.ts:

Ruolo Può fare
admin Tutto: contenuti, blog, upload, gestione utenti (/admin/users), piattaforme
superuser Pannello contenuti + blog
user Solo i propri articoli del blog
piattaforme Solo le sezioni riservate (/piattaforme, /campus); nessun accesso al pannello

Login pubblico su /login (?next sanitizzato contro open redirect). Dopo l'autenticazione ogni ruolo viene indirizzato alla propria pagina iniziale: il pannello per admin e user, i contenuti per superuser, l'indice delle piattaforme per il ruolo piattaforme. La gestione utenti (/admin/users, solo admin) consente creazione su pagina dedicata (/admin/users/new), cambio password — scelta a mano o generata, mostrata una sola volta perché nel database resta il solo hash — cambio ruolo ed eliminazione, con guardie contro l'autoeliminazione e la rimozione dell'ultimo admin.

Il ruolo campus è stato rinominato in piattaforme quando le sezioni riservate sono diventate due. Una migrazione in src/lib/db.ts aggiorna da sé gli utenti esistenti.

Piattaforme riservate

Le sezioni riservate sono raccolte sotto /piattaforme, che è anche la pagina di atterraggio del ruolo omonimo. L'elenco vive in src/data/piattaforme.ts: aggiungerne una è una riga, e il menu dell'header la mostra da sé a chi ha i permessi.

Biohacking Campus (/campus)

La sezione /campus raccoglie le guide di studio del corso Biohacking Campus. È protetta dal middleware come il pannello: un visitatore anonimo viene mandato al login, un utente con ruolo non abilitato torna alla propria pagina iniziale. Le pagine non montano l'header e il footer del sito — la navigazione avviene attraverso la barra laterale delle aree, con un unico richiamo per tornare all'indice delle piattaforme — ma usano i font, i colori e il CSS globale di InsanityLab.

I contenuti non vengono scritti a mano: lo script npm run sync-campus legge le guide sorgente (*.studio.md) dal progetto Biohacking Campus, le converte in frammenti HTML e produce la cartella campus-content/ (indice nav.json, pagine e immagini delle slide), che va committata perché il deploy la copia nell'immagine Docker. Lo script è idempotente: rigenerarlo dopo l'aggiunta di nuove lezioni aggiorna indice e pagine e rimuove quelle non più presenti. Le nuove guide vanno prima dichiarate nella struttura dei capitoli in scripts/campus/structure.mjs.

Le immagini delle slide sono servite dalla route /campus/assets/..., anch'essa protetta: non sono raggiungibili senza una sessione valida.

Il progetto sorgente sta in /home/adriano/Wasabi-Adp-Work/AI-OS/projects/personale/biohacking-campus (percorso predefinito dello script). In sviluppo, dopo un sync, il server va riavviato: src/lib/campus.ts tiene l'indice in cache in memoria e le guide nuove darebbero 404.

Stress Index (/piattaforme/stress-index)

Piattaforma di monitoraggio HRV portata dentro il sito da un prototipo Next.js esterno: nove viste — cruscotto giornaliero, elenco clienti, scheda cliente, analytics di studio, modulo sport con sessioni e atleti, team live, impostazioni, organizzazione. Il design system del prototipo (Tailwind, palette teal) è stato sostituito con quello del sito: lo stile sta in src/styles/stress-index.css, un foglio globale prefissato .si perché le stesse classi servono sia ai componenti Astro sia alle isole React, che lo scope di Astro non raggiungerebbe.

React è limitato a ciò che ha davvero bisogno di stato: la scheda cliente, la vista Analytics, il modulo Sport e i grafici Recharts. Il resto è Astro con qualche riga di JavaScript. I dati sono dimostrativi e stanno in src/lib/stress-index/data.ts; le aggregazioni con una logica propria (finestre temporali, distribuzioni, riepiloghi atleta) vivono in analytics.ts e sport.ts accanto, coperte da test.

Struttura del progetto

insanitylab-website/
├── src/
│   ├── pages/            # Route Astro (vetrina, /admin, /api, /campus, /piattaforme)
│   ├── components/       # Componenti (home/, content/, stress-index/, Header, Footer, …)
│   ├── layouts/          # Layout Base, Admin, Campus, StressIndex
│   ├── data/             # Dati statici + seed contenuti (services, trainings, site, piattaforme, content-seed)
│   ├── lib/              # db, auth, content, mailer, rate-limit, safe-next, env, stress-index/
│   ├── assets/img/       # Immagini ottimizzate da Astro
│   └── styles/           # global.css, stress-index.css
├── data/                 # SQLite (volume in produzione)
├── uploads/              # Upload immagini (volume in produzione)
├── campus-content/       # Contenuti del Campus generati da sync-campus
├── scripts/              # create-user, sync-campus, seed-blog-biohacking, smoke test
├── astro.config.mjs      # Config Astro (site, allowedDomains, redirects)
├── compose.yaml          # Stack Docker + label Traefik
├── Dockerfile            # Build multi-stage
├── .env.example          # Template variabili d'ambiente
└── README.md

Variabili d'ambiente

Il file .env (non versionato) richiede:

Variabile Descrizione
SMTP_HOST Host SMTP (produzione: smtps.aruba.it)
SMTP_PORT Porta SMTP (465 con SSL)
SMTP_USER Casella di invio (deve coincidere con CONTACT_FROM)
SMTP_PASS Password della casella
CONTACT_FROM Mittente delle email (= SMTP_USER)
CONTACT_TO Destinatario dei messaggi del form
DB_PATH Percorso del database SQLite
UPLOADS_DIR Cartella degli upload
CAMPUS_DIR Cartella dei contenuti del Campus (default ./campus-content)

La posta del dominio @insanitylab.it è ospitata su Aruba; l'invio del form usa l'SMTP autenticato Aruba (smtps.aruba.it:465). Aruba impone che il mittente sia la casella autenticata, quindi CONTACT_FROM deve essere uguale a SMTP_USER.

Deploy in produzione

Il sito gira in Docker su un VPS, dietro Traefik. Lo stack è in /opt/docker/insanitylab/ (repo in src/, volumi data/ e uploads/). Aggiornamento:

cd /opt/docker/insanitylab/src
sudo git -c safe.directory="$PWD" pull origin main    # il repo è di root: serve sudo, con password
docker compose up -d --build                          # docker gira senza sudo

Le migrazioni del database (in src/lib/db.ts) sono idempotenti e girano all'avvio del container: aggiornano lo schema e i contenuti senza perdere le modifiche fatte dal pannello. Le migrazioni che cambiano un valore sono sempre guardate dal valore precedente, quindi non sovrascrivono ciò che è stato modificato dal pannello — con la conseguenza, da tenere presente, che un contenuto già ritoccato a mano non riceve il nuovo default: va corretto dal pannello.

Prima di ogni deploy conviene fare il backup del database, che è in modalità WAL: un cp del solo .db perderebbe le modifiche non ancora consolidate.

sqlite3 -cmd ".timeout 10000" /opt/docker/insanitylab/data/insanitylab.db ".backup '/dest/insanitylab.db'"
sqlite3 /dest/insanitylab.db "PRAGMA integrity_check;"    # deve rispondere 'ok'

Gli articoli del blog non viaggiano con il codice: per caricarli in produzione, una volta sola, docker exec insanitylab node scripts/seed-blog-biohacking.mjs.

Dominio e DNS

  • Dominio ufficiale: insanitylab.it (canonico l'apex; www fa redirect 301 all'apex tramite Traefik).
  • I record A di @ e www puntano al VPS; la posta resta su Aruba (MX, SPF, webmail non vengono toccati).
  • TLS Let's Encrypt automatico via Traefik (certresolver mytlschallenge).

Attenzione dietro Traefik: allowedDomains in astro.config.mjs deve elencare i domini serviti, altrimenti Astro ignora X-Forwarded-Proto/Host e il controllo CSRF respinge i POST con 403.

Problemi comuni

better-sqlite3 non compila su npm install — servono i build tools:

  • Linux: sudo apt-get install build-essential python3
  • macOS: xcode-select --install
  • Windows: Visual Studio Build Tools

Modifiche allo <style> scoped di [slug].astro non compaiono in dev — l'HMR di Astro 7 non sempre ricompila lo stile scoped delle route dinamiche: riavviare il dev server (astro dev stop + npm run dev).

Database "locked" durante i test — assicurarsi che nessun processo stia accedendo a data/insanitylab.db.

Le guide nuove del Campus danno 404 in svilupposrc/lib/campus.ts tiene l'indice in cache in memoria: dopo npm run sync-campus va riavviato il server.

Il database di produzione sembra non aggiornato dopo il deploysqlite3 -readonly non legge il file -wal, dove stanno le modifiche non ancora consolidate, e mostra valori vecchi. Per verificare, interrogare il database in letturascrittura oppure — meglio — guardare direttamente le pagine del sito.

Licenza

Proprietà di InsanityLab.