32 Commits

Author SHA1 Message Date
Adriano 59dff13e22 Consulenza: bottone nel menu in alto su ogni pagina, voce nel menu mobile (#22)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 05:19:40 +00:00
Adriano 5c39b04c31 Consulenza: la nota sta sotto il suo bottone, non sotto Contattaci (#22)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 20:39:15 +00:00
Adriano ae98c4b889 Consulenza: nota sotto i bottoni con lo stile giusto (:global sul componente T) (#22)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 20:32:54 +00:00
Adriano c65effb0ed Consulenza: sotto il bottone la sede, via Leandro Alberti 76 (#22)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 20:32:07 +00:00
Adriano 67dc554810 Reformer Class: bottone consulenza conoscitiva gratuita (#22)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-29 20:30:09 +00:00
Adriano 9126af28ce Reformer Class: bottone calendario taupe, meno scuro (#19)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 19:50:48 +00:00
Adriano 3fff3cd2f8 Il repo resta al sito: app in radice, piattaforme nel loro repo
apps/sito passa in radice, apps/platforms si toglie: vive in
gitea:Adriano/InsanityLab-platforms, con la storia del repo comune. I piani e
le specifiche di Longevity e Campus lasciano docs/ e seguono le piattaforme.
README e compose: deploy da /opt/docker/insanitylab/src; al primo deploy .env
va spostato da apps/sito/.env alla radice.

Refs #20

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 14:25:12 +00:00
Adriano 9efd9a7680 Reformer Class: bottone Contattaci e calendario evidenziato
Refs #19

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 14:06:40 +00:00
Adriano 680224e773 longevity: #15 bersagli touch >=44px per radio, pulsanti e link-pulsante
Tablet del centro: label di radio (lg-field--consenso, alternative del
check-up), button di lg-form__nav e lg-btn a min-height 44px, senza
cambiare l'aspetto desktop.

Refs #15
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 08:21:36 +02:00
Adriano 8aeb25f930 longevity: #15 pagine del flusso guidato: consulenza, check-up, scheda cliente
Nuova pagina consulenza.astro (radio per livello e obiettivo, niente
select). checkup.astro mostra "Passo 3 di 4" e porta avanti flusso=nuovo,
con «Salta» verso la scheda. La scheda cliente ([code].astro) mostra la
sezione "Prima consulenza" (letta, Modifica/Compila) e, con
flusso=questionario, il riquadro "Passo 4 di 4" che rimanda a "Crea
accesso" se il cliente non lo ha ancora.

Refs #15
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 08:21:33 +02:00
Adriano 575a3988b7 longevity: #15 API della prima consulenza e flusso guidato nel check-up
Nuova API consulenza.ts (POST, autore da locals.user, redirect a
checkup?flusso=nuovo o alla scheda cliente). clienti.ts ora apre il flusso
guidato verso /consulenza?flusso=nuovo invece della scheda. checkup.ts:
un check-up NUOVO con flusso=nuovo prosegue con flusso=questionario; una
correzione lo ignora sempre.

Refs #15
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 08:21:28 +02:00
Adriano 498395dddb longevity: #15 libreria e schema della prima consulenza, con test
Tabella consulenza (client_code, UPSERT, nessuno storico) e modulo
lib/longevity/consulenza.ts: consulenzaDi, salvaConsulenza con validazioni
(livello 1-3, obiettivo nell'elenco chiuso, data AAAA-MM-GG, disponibilita
0-7, testo max 5000). 22 test: modulo, schema, API consulenza.ts e
checkup.ts (flusso guidato), redirect di clienti.ts, nessuna pagina cliente
importa consulenza.

Refs #15
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 08:21:24 +02:00
Adriano b0f30f9e4c longevity: correzioni passo 3 su #13 unità B — scarto alternative e nota
testIdScartatiDalleAlternative: una sezione senza scelta in `scelte` (radio
assente dal form) non scarta più nulla, invece di ripiegare in silenzio
sulla prima alternativa (perdeva campi come f_plank_sec senza alt_core).

API checkup.ts: la nota operatore si valida (lunghezza max 5000, TESTO_MAX
esportato da giornale.ts) PRIMA di salvare il check-up, non più dopo — una
nota troppo lunga non faceva più fallire aggiungiVoce in silenzio ma
perdeva comunque la nota col check-up già scritto. maxlength sulla
textarea in checkup.astro.

Suite: 314 verdi, 4 rossi di piattaforma (baseline, invariati). tsc e
build puliti.

Refs #13
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 22:59:19 +02:00
Adriano 81021d18f3 longevity: test unità B — struttura modulo, alternative, nota su giornale (#13)
gestionale-modulo-checkup.test.ts: ordine sezioni, pressione/SpO2 esclusi,
dismesso in «Altre misure» su data vecchia, dettagli MUTT, preselezione
alternativa (prima/più recente/ultimo check-up). checkup-alternative-api.test.ts:
API scarta i campi dell'alternativa non scelta (radio inviata o assente),
nota operatore aggiunge voce di giornale (data check-up, autore operatore),
nota vuota o assente (correzione) non aggiunge nulla.

Suite completa: 313 verdi, 4 rossi di piattaforma (baseline, invariati).
tsc --noEmit e npm run build puliti.

Refs #13
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 22:57:02 +02:00
Adriano c8187ce4db longevity: modulo check-up con sezioni e alternative del manuale (#13)
strutturaCheckup in gestionale.ts (ordine Pre-test..Strumenti, sezioni ad
alternative con soglia testuale, «Altre misure» per i test dismessi in
correzione, pressione/SpO2 esclusi); checkup.astro con radio button e
fieldset (niente <select>); API checkup.ts scarta i campi delle
alternative non scelte e aggiunge una voce di giornale dalla nota
operatore sul check-up nuovo.

Refs #13
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 22:54:26 +02:00
Adriano cffb0a624d longevity: correzione etichette sit-to-stand e push-up (unità A)
sit-to-stand era descritto come 30 secondi, il test dura 1 minuto;
push-up non diceva che le donne lo fanno sulle ginocchia.

Refs #13
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 22:49:24 +02:00
Adriano f57a07a964 longevity: test unitari e di sessione per la regola dei gruppi (#13)
motore-fisici-gruppi.test.ts (nuovo): un caso per ogni alternativa che da
punteggio (handgrip/spinta/arti_inferiori/vo2max/hrr/flamingo/back_scratch,
media dx/sx e storico), un lato solo -> nessun punteggio, recenza fra
alternative (pushup/panca in entrambe le direzioni), alternativa piu recente
ma incompleta esclusa (sit-to-stand senza altezza), i test "solo registrati"
non producono chiavi.

motore-fisici-sessione.test.ts: due nuovi casi end-to-end su DB (due sessioni,
due test diversi per la stessa sotto-metrica) che confermano che lo score usa
il test piu recente, in entrambe le direzioni.

Refs #13

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 22:48:07 +02:00
Adriano abd0626ab0 longevity: registro e motore per la nuova batteria test (manuale Nicola 27/09)
Nuovi test_id nel registro (altezza, FC riposo, handgrip/back scratch/flamingo
dx-sx, panca manubri/bilanciere, trazioni, sit-to-stand, plank ginocchia,
6MWT, 2km walk UKK, MILFIT, MUTT FC max/1'/2'/step) con attivo_da, e i quattro
test_id storici sostituiti (handgrip_kg, back_scratch_cm, flamingo_cadute,
hrr_1min_bpm) marcati attivo_a 2026-09-28 senza cancellarli.

motore/index.ts: nuova recenzaValideAlla (posizione di recenza per test_id,
stesso ORDER BY di misureValideAlla, fattorizzata la query condivisa).

motore/fisici.ts: regola dei gruppi in sottoDominiFisici (recenza opzionale) -
per ogni sotto-metrica con piu' test alternativi si sceglie l'alternativa
COMPLETA la cui misura piu' recente vince; senza recenza resta l'ordine di
priorita' di oggi. Handgrip/back_scratch/flamingo ora mediano dx/sx; spinta e
arti_inferiori aggiungono panca bilanciere e sit-to-stand; vo2max aggiunge
MILFIT e 2km walk alla cascata esistente.

Refs #13

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 22:45:26 +02:00
Adriano 2231a476c7 longevity: sezione Giornale di bordo nella scheda cliente
Form (data, nota) ed elenco voci dopo Anagrafica, cancella visibile solo ad
autore o admin, messaggi per ?giornale=aggiunta|cancellata.
Refs #16

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 22:24:22 +02:00
Adriano cef08aaf9f longevity: API giornale di bordo per cliente (aggiungi/cancella)
POST unico con campo azione, autore e ruolo sempre da locals.user, redirect
303 alla scheda cliente #giornale.
Refs #16

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 22:23:03 +02:00
Adriano 37b1a316d8 longevity: test unitari giornale di bordo per cliente
Aggiunta/lettura, rifiuti di aggiungiVoce, cancellaVoce (autore/admin/altro
trainer/id inesistente), schema idempotente, nessuna pagina cliente importa
giornale.ts.
Refs #16

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 22:22:40 +02:00
Adriano 00ecfe86c3 longevity: schema e libreria giornale di bordo per cliente
Tabella giornale in longevity.db (solo client_code) e giornale.ts:
vociDelGiornale, aggiungiVoce (validazioni), cancellaVoce (autore o admin).
Refs #16

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 22:22:07 +02:00
Adriano 6bbfcd8f3c longevity: pagina dettaglio sessione e modifica check-up dalla scheda (#12)
Nuova pagina .../sessione/[id].astro: dettaglio misure per asse, con
link "Modifica" per un checkup. checkup.astro diventa correzione con
?sessione=<id> (campi precompilati, attivi alla data della sessione).
La scheda cliente linka ogni sessione e mostra l'esito della correzione.

Refs #12

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TkWhSnSLvAyRCoWBuCpXvp
2026-09-27 22:13:41 +02:00
Adriano 9e566ad7b1 longevity: API checkup accetta sessione_id per correggere (#12)
Un sessione_id nel form fa passare da correggiCheckup invece di
salvaCheckup, stesso endpoint. Il client_code per validazioni e
redirect si legge sempre dalla sessione nel DB, mai dal form.

Refs #12

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TkWhSnSLvAyRCoWBuCpXvp
2026-09-27 22:12:00 +02:00
Adriano 19035e67b0 longevity: correggiCheckup e misureDellaSessione, sovrascrittura misure
Aggiunge la correzione di un check-up gia' salvato: aggiorna sessione e
misure (cambiate/tolte/nuove), azzera la verifica sulle misure cambiate,
ricalcola i punteggi della sessione e delle successive. Estratto
fuoriRangeDi in misure.ts per non duplicare il calcolo min/max.

Refs #12

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TkWhSnSLvAyRCoWBuCpXvp
2026-09-27 22:09:15 +02:00
Adriano 880c928f43 docs: README e spec Longevity al 26/09
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 14:02:23 +00:00
Adriano 558def3d52 docs: README piattaforme e spec Longevity aggiornati al gestionale in produzione
README: host piattaforme.tielogic.xyz, conteggio test, sezione Longevity (ruoli,
pagine, regola della misura valida più recente), procedura di aggiornamento con
backup, dati nel volume, apertura pigra del DB, trappole dei test e node_modules.
Spec: §13 con i punti di §11/§12 chiusi da #1 #2 #5 #6 #7 #8 e quelli ancora aperti;
decisioni del 03/09 e 26/09.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 14:02:22 +00:00
Adriano 8f95a385d9 Longevity: nessuna pagina senza uscita (#10)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 13:48:42 +00:00
Adriano 22a400b854 longevity: nessuna pagina senza uscita, "Il mio referto" solo a chi ha un fascicolo
Refs #10

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-26 13:48:03 +00:00
Adriano 12b2e91503 Longevity: gestionale trainer, test fisici, progressione, verifica misure, accesso cliente (#1 #2 #5 #7 #8 #9)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 13:15:29 +00:00
Adriano e74266f9a0 node_modules fuori da git: il symlink tracciato puntava al checkout di produzione
Il pattern 'node_modules/' ignora solo le cartelle, non i symlink: un symlink
apps/platforms/node_modules -> /opt/docker/insanitylab-apps/... era finito in
git (13d8cdd). Nei cloni npm ci passava dal symlink e svuotava la cartella del
checkout di produzione; nel checkout di produzione il symlink punterebbe a se'
stesso. Ora il pattern e' 'node_modules' (cartelle e symlink).

Fixes #9

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 13:15:24 +00:00
Adriano 2c9a74f849 longevity: verifica misure fuori range in scheda cliente, link a crea accesso
Scheda cliente ([code].astro): sezione "Misure da verificare" da
misureDaVerificare (id, range) al posto di misureFuoriRange (nessun id,
nessuna azione); ogni riga ha due form verso /api/longevity/gestionale/verifica
(conferma/scarta), messaggio dopo la decisione, e link "Crea accesso al
referto" quando il cliente non ha ancora un accesso.

gestionale.ts: rimossa misureFuoriRange (e il tipo MisuraFuoriRange), sostituita
ovunque da misureDaVerificare (misure.ts), l'unica con id e range utile alle
azioni. accesso.astro: link "Torna alla scheda" in tutti gli stati.

Refs #7
Refs #8

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-26 13:11:38 +00:00
600 changed files with 107 additions and 101251 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
node_modules/ node_modules
dist/ dist/
.astro/ .astro/
.env .env
View File
+9 -3
View File
@@ -207,13 +207,17 @@ Gli articoli del blog non viaggiano con il codice: per caricarli in produzione,
Proprietà di InsanityLab. Proprietà di InsanityLab.
## Deploy (dopo la separazione del 03/09/2026) ## Deploy (dopo la separazione del 03/09/2026 e i due repo del 29/09/2026)
Dal 29/09/2026 questo repo contiene **solo il sito**, in radice. Le aree riservate hanno il
loro repo, `gitea:Adriano/InsanityLab-platforms` (issue #20); fino ad allora le due app erano
`apps/sito` e `apps/platforms` dello stesso repo.
Il progetto compose si chiama **`insanitylab`** (`name:` esplicito nel compose), non piu' Il progetto compose si chiama **`insanitylab`** (`name:` esplicito nel compose), non piu'
`src` come quando la cartella era `/opt/docker/insanitylab/src`. `src` come quando la cartella era `/opt/docker/insanitylab/src`.
``` ```
cd /opt/docker/insanitylab/src/apps/sito cd /opt/docker/insanitylab/src
docker compose build # il sito resta in piedi: non tocca il container docker compose build # il sito resta in piedi: non tocca il container
docker compose up -d # sostituisce il container docker compose up -d # sostituisce il container
``` ```
@@ -221,7 +225,9 @@ docker compose up -d # sostituisce il container
⚠️ **`.env` non e' in git** (e non deve esserlo): resta dov'e' ed e' l'unico file che un ⚠️ **`.env` non e' in git** (e non deve esserlo): resta dov'e' ed e' l'unico file che un
`git merge` non porta con se'. Il 04/09/2026 lo spostamento sotto `apps/sito/` ha lasciato `git merge` non porta con se'. Il 04/09/2026 lo spostamento sotto `apps/sito/` ha lasciato
`.env` alla vecchia radice, l'`up` e' fallito **dopo** che il `down` aveva gia' tolto il `.env` alla vecchia radice, l'`up` e' fallito **dopo** che il `down` aveva gia' tolto il
container, e il sito e' rimasto giu' **9,7 secondi**. Prima di ogni swap: container, e il sito e' rimasto giu' **9,7 secondi**. ⚠️ Il 29/09/2026 il sito torna in radice:
al primo deploy dopo quel cambio `.env` va spostato a mano da `apps/sito/.env` a `.env`.
Prima di ogni swap:
``` ```
docker compose config >/dev/null # legge env_file e fallisce SUBITO se manca qualcosa docker compose config >/dev/null # legge env_file e fallisce SUBITO se manca qualcosa
-7
View File
@@ -1,7 +0,0 @@
node_modules
dist
data
.env
.git
tests
*.md
-30
View File
@@ -1,30 +0,0 @@
# Stage 1: build
FROM node:22-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: runtime
FROM node:22-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /app/dist ./dist
COPY scripts ./scripts
# Contenuti del Biohacking Campus (generati con sync-campus e committati nel repo)
COPY campus-content ./campus-content
# Tre database distinti, tutti nello stesso volume:
# - piattaforme.db utenti e sessioni di quest'app (NON quelli del sito)
# - longevity.db misure e punteggi, senza nomi
# - identity.db la mappatura codice ↔ persona, l'unico file con dei nomi dentro
ENV HOST=0.0.0.0 \
PORT=4321 \
DB_PATH=/app/data/piattaforme.db \
LONGEVITY_DB_PATH=/app/data/longevity.db \
IDENTITY_DB_PATH=/app/data/identity.db
EXPOSE 4321
CMD ["node", "./dist/server/entry.mjs"]
-69
View File
@@ -1,69 +0,0 @@
# InsanityLab — aree riservate (`apps.insanitylab.it`)
Applicazione Astro autosufficiente con le tre aree riservate: **Biohacking Campus**,
**Stress Index** e **Longevity**. Nasce il 03/09/2026 separando dal sito
(`apps/sito`, `insanitylab.it`) tutto ciò che sta dietro un login.
## Cosa la rende separata
| | Sito | Quest'app |
|---|---|---|
| Host | `insanitylab.it` | `apps.insanitylab.it` |
| Utenti | 3 redazionali (`admin`, `superuser`, `user`) | uno per ogni cliente del centro (`piattaforme`, `cliente`, `trainer`, `admin`) |
| Database | `insanitylab.db` (contenuti, articoli, utenti del sito) | `piattaforme.db` + `longevity.db` + `identity.db` |
| Login | `/admin/login`, solo redazione | `/login`, ed è l'unica porta |
Non c'è SSO e non c'è cookie condiviso: **una sessione del sito qui non vale**, ed è la
separazione, non un difetto. Le due `lib/auth.ts` sono parenti ma non sono la stessa cosa —
~170 righe duplicate una volta sola, libere di divergere come già divergono i ruoli.
## Sviluppo
```bash
npm ci
npm run dev # http://localhost:4321
npm test # 26 file, 197 test
npm run create-user -- <nome> <password> <ruolo> # ruoli: piattaforme, cliente, trainer, admin
```
I dati stanno in `data/` (gitignorata): `piattaforme.db`, `longevity.db`, `identity.db`.
## Rilascio
⚠️⚠️ **Il DNS viene prima del container.** `apps.insanitylab.it` deve già risolvere verso
l'IP della VPS quando si alza lo stack: avviarlo prima significa una catena di sfide TLS
fallite, e Let's Encrypt limita proprio quelle. La zona è su **Aruba**, serve un record
**A** `apps` → l'IP della macchina.
```bash
mkdir -p /opt/docker/insanitylab-platforms/data # i dati stanno FUORI dal clone git
docker compose up -d --build
node scripts/migra-utenti.mjs # prova a vuoto
node scripts/migra-utenti.mjs --applica # porta gli utenti dal DB del sito
```
## Trappole già pagate
⚠️ **Le versioni si fissano esatte.** `^7.0.5` sulla nuova app aveva risolto ad **astro
7.3.0**, che con `@astrojs/node 11.1.5` non si parla (`"./_internal/logger" is not
exported`) e il build muore. Astro, l'adapter node e l'integrazione react sono pinnati
alle stesse versioni che girano in produzione sul sito.
⚠️ **`security.allowedDomains` in `astro.config.mjs` non è decorativo.** Dietro Traefik il
TLS termina al proxy: senza quell'elenco Astro ignora `X-Forwarded-Proto`/`Host` e il
controllo CSRF respinge **ogni** POST con 403 — cioè il login smette di funzionare e
l'errore sembra «credenziali sbagliate».
⚠️ **`name: insanitylab-platforms` nel compose.** Senza, il progetto si chiamerebbe
`platforms` (dal nome della cartella) e un `down --remove-orphans` dato altrove potrebbe
toccare container che non sono suoi.
⚠️ **Il default di `canAccessPath` è chiuso**, al contrario del sito da cui viene: là le
rotte non elencate (blog, upload) erano di chiunque fosse loggato, qui ogni sezione è di
qualcuno. Una rotta nuova senza regola risulta **vietata**, e lo si scopre provandola.
⚠️ **Le API rispondono con uno stato, non con un redirect** (401/403 su `/api/*`): mandare
un 302 verso il login a chi ha chiesto del JSON produce un 200 con dentro dell'HTML.
⚠️ **Stress Index non ha un backend**: `src/lib/stress-index/data.ts` sono dieci clienti
inventati scritti a mano. In linea c'è la sua interfaccia, non i suoi dati.
-27
View File
@@ -1,27 +0,0 @@
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
import react from '@astrojs/react';
export default defineConfig({
site: 'https://apps.insanitylab.it',
output: 'server',
// ⚠️ Dietro Traefik il TLS termina al proxy: senza allowedDomains Astro ignora
// X-Forwarded-Proto/Host e il checkOrigin CSRF respinge OGNI POST con 403 — cioè
// il login smette di funzionare, e l'errore sembra "credenziali sbagliate".
// Stessa configurazione del sito, con l'host di quest'app.
security: {
allowedDomains: [
// L'host definitivo, quando il record A su Aruba ci sara'.
{ hostname: 'apps.insanitylab.it', protocol: 'https' },
// ⚠️ L'host in uso oggi (03/09/2026): il dominio del cliente resta su Aruba e le aree
// riservate stanno su un sottodominio nostro. Finche' e' in uso deve stare qui:
// senza, dietro Traefik il controllo CSRF di Astro respinge ogni POST con 403 e il
// login sembra rifiutare le credenziali.
{ hostname: 'piattaforme.tielogic.xyz', protocol: 'https' },
],
},
adapter: node({ mode: 'standalone' }),
integrations: [react()],
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 581 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 676 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 632 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 837 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 765 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 791 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 702 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 939 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 654 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 639 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 654 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 611 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

Some files were not shown because too many files have changed in this diff Show More