Adriano ee54e9a1f4 Migrazione: allinea i bottoni promo col vecchio testo-freccia
Il commit 1522d97 ha spostato la freccia "→" dei bottoni promo nel CSS
(::after) e il maiuscolo in text-transform, cambiando il seed in minuscolo
senza freccia. Ma il seed è INSERT OR IGNORE: sui DB esistenti (produzione)
le righe restavano a "LE NOSTRE PROPOSTE →" / "ACQUISTA →", e il ::after
avrebbe prodotto una doppia freccia.

Aggiunge una migrazione guardata dal vecchio valore (idempotente, non tocca
le righe modificate dal pannello) che le allinea al seed corrente. Verificata
su copia del DB di produzione: frecce corrette e 40 edit del pannello intatti.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 22:28:20 +02:00

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 richtext, e il form di contatti invia email tramite SMTP.

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)
  • 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 e proprietà degli articoli per ruolo.
  • Gestione utenti con tre ruoli (admin, superuser, user).
  • 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]

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)).

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 unatantum, cambio ruolo ed eliminazione, con guardie contro l'autoeliminazione 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; 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.

Licenza

Proprietà di InsanityLab.

S
Description
No description provided
Readme 72 MiB
Languages
HTML 86.7%
TypeScript 7.6%
Astro 3.5%
JavaScript 1.2%
CSS 0.5%
Other 0.4%