Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
Lorenzo Pollutri 240dc17f4d factory reset/preset implementati il y a 1 mois
app factory reset/preset implementati il y a 1 mois
components Implementate: backup, edit del welcome text, card kiosk-mode il y a 1 mois
data add font handling il y a 1 mois
lib factory reset/preset implementati il y a 1 mois
public Primo commit il y a 1 mois
scripts Normalizzazione dei formati vidio e immagine immessi il y a 1 mois
types Implementate: backup, edit del welcome text, card kiosk-mode il y a 1 mois
.gitignore Initial commit from Create Next App il y a 2 mois
AGENTS.md Initial commit from Create Next App il y a 2 mois
CLAUDE.md Initial commit from Create Next App il y a 2 mois
README.md factory reset/preset implementati il y a 1 mois
eslint.config.mjs Initial commit from Create Next App il y a 2 mois
next.config.ts Normalizzazione dei formati vidio e immagine immessi il y a 1 mois
package-lock.json Normalizzazione dei formati vidio e immagine immessi il y a 1 mois
package.json Normalizzazione dei formati vidio e immagine immessi il y a 1 mois
postcss.config.mjs Initial commit from Create Next App il y a 2 mois
tsconfig.json Initial commit from Create Next App il y a 2 mois

README.md

Captive Portal CMS — Casa della Scuola

CMS per portali captive: gestione di card informative, gallerie, flip-book e contenuti kiosk a schermo intero, con un’area di amministrazione locale. Stack: Next.js 16 (App Router, Turbopack), React 19, TypeScript, Tailwind v4. Persistenza su file (nessun database). Pensato per girare su server Ubuntu offline.


Indice

  1. Avvio
  2. Configurazione (lib/config.ts)
  3. Tipi di card
  4. File consentiti negli upload
  5. Limiti di testo
  6. Sicurezza degli input
  7. Struttura dei dati (data/)
  8. Backup e ripristino
  9. Factory Preset (developer)
  10. Font
  11. Prerequisiti di sistema
  12. Risoluzione problemi

Avvio

Sviluppo:

npm install
npm run dev

Apri http://localhost:3000 (portale pubblico) e http://localhost:3000/admin (amministrazione).

Produzione:

npm run build
npm start

Server offline: la macchina di produzione non ha accesso a internet. NON eseguire npm install lì. Installa le dipendenze su una macchina con internet (stesso OS, Linux), poi copia l’intera cartella node_modules sul server insieme al progetto buildato. Su Ubuntu basta npm run build (se node_modules è presente) + npm start.


Configurazione (lib/config.ts)

Tutte le impostazioni globali sono flag build-time nel file lib/config.ts. Dopo ogni modifica serve ricostruire: npm run build.

Variabile Default Descrizione
EXTERNAL_LINK_ENABLED true Mostra il tipo di card “External Link” nel menu dell’admin. Le card di quel tipo già esistenti restano comunque visibili e cliccabili anche se messo a false.
FACTORY_RESET_ENABLED false Mostra la sezione “Factory Preset” nell’admin (funzione developer). Vedi Factory Preset. Gli endpoint API restano attivi a prescindere.
DEFAULT_FONT '' Font di default se il portale non ne ha impostato uno. Stringa vuota = font di sistema (Arial). Altrimenti il nome esatto di un file in data/fonts/ (es. "Geist-Variable.woff2").
TEXT_LIMITS vedi sotto Limiti caratteri di tutti i campi testuali.
UPLOAD_LIMITS vedi sotto Dimensioni massime upload per famiglia di file.

Limiti testo (TEXT_LIMITS)

card: {
  title: 200,
  shortDescription: 500,
  fullContent: 20_000,
  actionUrl: 2000,
},
portal: {
  title: 200,
  welcomeText: 1000,
},

Limiti upload (UPLOAD_LIMITS)

image: 25 MB,
pdf:   20 MB,
video: 1 GB,

Per cambiare un qualunque limite: modifica il numero in lib/config.ts e ricostruisci. Il valore è condiviso tra interfaccia (contatore / maxLength), validazione server e check di upload — un solo punto di verità.


Tipi di card

Tipo Nome in admin Comportamento
INFO_PAGE Info Page Pagina informativa: solo cover, niente galleria.
IMAGE_GALLERY Image Gallery Galleria di immagini/video/PDF sfogliabile a schermo intero.
BOOK Flip-Book Sfoglialibro in formato A4 (due pagine affiancate).
FULLSCREEN_LOCK Fullscreen Lock (kiosk) Takeover totale: se presente, il portale pubblico mostra SOLO il suo contenuto (immagine o video) a tutto schermo, senza hero, griglia o pulsanti di chiusura. Le altre card vengono nascoste. Utile per redirect/segnaletica kiosk.
EXTERNAL_LINK External Link Apre un URL esterno. Visibile nel menu solo se EXTERNAL_LINK_ENABLED = true.

Note sulla Fullscreen Lock:

  • Se ci sono più card lock, viene usata la prima per ordine di visualizzazione.
  • /admin resta sempre accessibile anche con una lock attiva.
  • Per tornare al portale normale: elimina (o cambia tipo a) la card lock.

File consentiti negli upload

Gli upload passano per tre controlli in cascata. Se uno fallisce, nessun file viene salvato e l’admin riceve un messaggio d’errore.

1. Whitelist estensioni

Famiglia Estensioni Limite
Immagini png jpg jpeg gif webp 25 MB
Video mp4 m4v webm mov ogv ogg 1 GB
Documenti pdf 20 MB

Tutto il resto (es. svg, heic, bmp, avi, exe) viene rifiutato. SVG è escluso di proposito (può contenere <script>).

2. Controllo “magic-bytes”

Il contenuto reale del file viene confrontato con l’estensione dichiarata. Esempi rifiutati:

  • Un PDF rinominato .jpg → rifiutato (contenuto ≠ estensione).
  • Un eseguibile rinominato .png → rifiutato (tipo non riconosciuto).
  • Un PNG rinominato .jpg → rifiutato (mismatch interno alla famiglia immagini).

mov e mp4 sono intercambiabili (entrambi container ISO BMFF).

3. Transcodifica video automatica

Solo i video possono essere ricodificati. Alla ricezione il server sonda i codec:

  • Video già compatibile (H.264 + AAC/MP3) → nessuna ricodifica, salvataggio immediato.
  • Video non compatibile (HEVC iPhone, VP9, AV1, audio Opus/Vorbis…) → messo in coda e ricodificato in background con ffmpeg verso MP4 H.264 + AAC, max 720p. L’admin vede un badge “Transcoding XX%” sulla miniatura; quando finisce il file diventa riproducibile su tutti i browser.

Le immagini e i PDF non vengono mai trasformati: sono salvati identici al file caricato.

La transcodifica richiede ffmpeg/ffprobe sul server — vedi Prerequisiti. Se mancano, gli upload di video che richiedono ricodifica rispondono 503.


Limiti di testo

Ogni campo compilabile dall’admin ha un limite (vedi TEXT_LIMITS). L’interfaccia mostra un contatore quando ci si avvicina al limite (rosso se superato) e blocca l’inserimento oltre il massimo. Lato server, una richiesta che sfora viene rifiutata con 400 e l’elenco dei campi fuori limite — nessun troncamento silenzioso.

Gli URL (actionUrl) accettano solo gli schemi http, https, mailto, tel. Schemi come javascript: vengono rifiutati.


Sicurezza degli input

  • HTML delle card (fullContent): sanificato in scrittura con una whitelist (p, br, strong, em, b, i, u, ul, ol, li, a, h1–h6, blockquote, span). I link ricevono automaticamente rel="noopener noreferrer" target="_blank". Script e attributi pericolosi vengono rimossi.
  • Welcome text del portale: sanificato con whitelist ridotta (b, i, strong, em, br, p, div, span) — solo grassetto, corsivo e a-capo, nessun link. Si modifica con il mini-editor (pulsanti B/I) nelle impostazioni.
  • Colore tema (themeColor): accettato solo nel formato #RRGGBB.
  • Nomi file: normalizzati (accenti rimossi, niente caratteri speciali, niente path traversal) e resi univoci con timestamp + stringa casuale.

Struttura dei dati (data/)

Tutto lo stato applicativo vive nella cartella data/ alla radice del progetto. Non contiene codice, solo dati caricati dagli utenti:

data/
├── cards.txt              ← tutte le card (JSON array)
├── portals.txt            ← configurazione del portale (JSON array)
├── fonts/                 ← font caricati (.woff2/.woff/.ttf/.otf)
├── uploads/               ← media caricati (immagini, video, pdf)
│   └── .tmp/              ← buffer temporaneo upload/transcoding (svuotabile)
└── transcode-jobs.json    ← stato della coda di transcodifica (creato all'occorrenza)

Copiare via questa cartella = backup completo. Sostituirla = ripristino completo. Nessun database, nessuna migrazione.


Backup e ripristino

Disponibile dall’admin in Settings → Backup & Restore.

Dall’interfaccia

  • ⬇ Scarica backup ZIP — scarica interceptop-backup-<data>.zip con card, configurazione, media e font (esclude i file temporanei).
  • ⤴ Ripristina da ZIP… — carica uno zip; dopo conferma sovrascrive lo stato attuale e ricarica la pagina. La cartella data/ precedente viene conservata come data.bak-<timestamp>/ come rete di sicurezza.

Da riga di comando (Linux)

Creare un backup (il cd data è essenziale: mette i file alla radice dello zip):

cd /percorso/del/progetto/data
zip -r ~/backup-$(date +%Y%m%d-%H%M%S).zip cards.txt portals.txt uploads fonts -x "uploads/.tmp/*"

Verificare la struttura (devi vedere cards.txt e portals.txt in cima, NON una cartella data/):

unzip -l ~/backup-*.zip

Lo zip così prodotto è caricabile direttamente dal pulsante “Ripristina da ZIP…".

Struttura obbligatoria: i file devono stare alla radice dello zip. Uno zip con tutto dentro una cartella data/ verrà rifiutato con “cards.txt assente”. Deve essere uno ZIP, non un .tar.


Factory Preset (developer)

Stato “di fabbrica” ripristinabile con un click. Pensato per preparare preset standard (es. banner + icona + una card di redirect) da distribuire a più macchine MajorNet.

Visibile nell’admin solo se FACTORY_RESET_ENABLED = true in lib/config.ts. Gli endpoint API restano comunque attivi anche con il flag a false.

Il preset è un file fisso: factory/preset.zip alla radice del progetto (fuori da data/, quindi non viene toccato dai reset; fuori da public/, quindi non scaricabile via web).

Dall’interfaccia (con flag attivo)

  • 💾 Salva stato attuale come Factory Preset — congela lo stato corrente in factory/preset.zip.
  • 🏭 Factory Reset — ripristina tutto al preset (disabilitato se il preset non esiste). La data/ precedente resta come data.bak-<timestamp>/.

Da riga di comando / API

# salva lo stato corrente come preset
curl -X POST http://localhost:3000/api/admin/factory-preset

# esegui il factory reset
curl -X POST http://localhost:3000/api/admin/factory-reset

# verifica se il preset esiste
curl http://localhost:3000/api/admin/factory-preset

Creare il preset a mano (Linux)

cd /percorso/del/progetto/data
mkdir -p ../factory
zip -r ../factory/preset.zip cards.txt portals.txt uploads fonts -x "uploads/.tmp/*"

Distribuzione su altre macchine

Copia factory/preset.zip sulle altre installazioni: l’admin (con flag attivo) vedrà il preset e potrà fare Factory Reset per allinearsi allo stato standard.


Font

I font vanno collocati in data/fonts/ nei formati .woff2, .woff, .ttf, .otf. Vengono inclusi automaticamente nei backup.

  • L’elenco mostrato in admin esclude i file con italic/bold nel nome (si usa la variante “regular”; i pesi vengono gestiti dal browser).
  • Si seleziona il font del portale dalle impostazioni; in alternativa si imposta DEFAULT_FONT in lib/config.ts.
  • Il welcome text formattato (grassetto/corsivo) eredita il font selezionato.

Prerequisiti di sistema

Sul server servono alcuni binari di sistema (richiamati direttamente, non via npm):

Binario A cosa serve Se manca
ffmpeg Transcodifica video non compatibili Upload video che richiedono ricodifica → 503
ffprobe Riconoscimento codec video Come sopra
zip Creazione backup / preset Pulsante “Scarica backup” e “Salva preset” → 503
unzip Ripristino backup / factory reset Pulsanti di ripristino → 503

Verifica su Ubuntu:

which ffmpeg ffprobe zip unzip

Se mancano:

sudo apt install ffmpeg zip unzip

Risoluzione problemi

npm run build → “Module not found: file-type / sanitize-html” anche se sono installati: cache Turbopack corrotta. Soluzione:

rm -rf .next && npm run build

Un video caricato non parte nel browser: probabilmente la transcodifica è fallita o ffmpeg non è installato. Controlla which ffmpeg ffprobe e i log del server.

Il ripristino dice “cards.txt assente”: lo zip ha una cartella data/ di troppo al suo interno. Ricrealo facendo cd data PRIMA del comando zip (vedi Backup).

Recupero dopo un ripristino sbagliato: lo stato precedente è in data.bak-<timestamp>/. Ferma il server, rinomina quella cartella in data/ e riavvia.

La sezione Factory Preset non compare: è dietro il flag FACTORY_RESET_ENABLED in lib/config.ts. Mettilo a true e ricostruisci.