- README riscritto come documentazione viva e completa: stack, funzionalita' (contenuti taggati + GUID + modifica inline, 3 ruoli admin/superuser/user, blog ownership, gestione utenti), env SMTP Aruba, deploy Docker/Traefik, dominio insanitylab.it, migrazioni DB e trappole tecniche note - rimossi docs/superpowers/plans e docs/superpowers/specs: piani gia' eseguiti e spec di design ora accorpati nel README (restano nella storia git) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
InsanityLab Website
Sito ufficiale di InsanityLab: vetrina aziendale, blog integrato, form di contatti 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 rich‑text, e il form di contatti invia email tramite SMTP.
Stack tecnologico
- Framework: Astro 7 (output
server,prerender = falsesulle pagine dinamiche) - Runtime: Node.js 22 (adapter
@astrojs/nodestandalone) - Database: SQLite via
better-sqlite3 - Autenticazione: sessioni con password hashate (
bcryptjs) - Email: Nodemailer (SMTP)
- Editor articoli: TipTap (rich text)
- Font:
@fontsource(Montserrat, Open Sans) — nessun CDN esterno - Sitemap: endpoint runtime
/sitemap.xml(route vetrina + articoli pubblicati) - Test: Vitest
- Linguaggio: TypeScript
- Deploy: Docker (multi‑stage,
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 e proprietà degli articoli per ruolo.
- Gestione utenti con tre ruoli (
admin,superuser,user). - Form contatti con validazione, honeypot anti‑spam, rate‑limit 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] |
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 ildata-astro-ciddel 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)).
Ruoli e accessi
Tre ruoli, con autorizzazione a matrice di prefissi in src/lib/auth.ts:
| Ruolo | Può fare |
|---|---|
admin |
Tutto: contenuti, blog, upload, gestione utenti (/admin/users) |
superuser |
Pannello contenuti + blog |
user |
Solo i propri articoli del blog |
Login pubblico su /login (?next sanitizzato contro open redirect). La gestione utenti (/admin/users, solo admin) consente creazione, reset password una‑tantum, cambio ruolo ed eliminazione, con guardie contro l'auto‑eliminazione e la rimozione dell'ultimo admin.
Struttura del progetto
insanitylab-website/
├── src/
│ ├── pages/ # Route Astro (vetrina, /admin, /api)
│ ├── components/ # Componenti (home/, content/, Header, Footer, …)
│ ├── layouts/ # Layout Base
│ ├── data/ # Dati statici + seed contenuti (services, trainings, site, content-seed)
│ ├── lib/ # db, auth, content, mailer, rate-limit, safe-next, env
│ ├── assets/img/ # Immagini ottimizzate da Astro
│ └── styles/ # global.css
├── data/ # SQLite (volume in produzione)
├── uploads/ # Upload immagini (volume in produzione)
├── scripts/ # create-user, 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 |
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
git pull
docker compose up -d --build
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.
Dominio e DNS
- Dominio ufficiale: insanitylab.it (canonico l'apex;
wwwfa redirect 301 all'apex tramite Traefik). - I record A di
@ewwwpuntano 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:
allowedDomainsinastro.config.mjsdeve elencare i domini serviti, altrimenti Astro ignoraX-Forwarded-Proto/Hoste 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.
Licenza
Proprietà di InsanityLab.