|
|
il y a 1 mois | |
|---|---|---|
| app | il y a 1 mois | |
| components | il y a 1 mois | |
| data | il y a 1 mois | |
| lib | il y a 1 mois | |
| public | il y a 1 mois | |
| scripts | il y a 1 mois | |
| types | il y a 1 mois | |
| .gitignore | il y a 2 mois | |
| AGENTS.md | il y a 2 mois | |
| CLAUDE.md | il y a 2 mois | |
| README.md | il y a 1 mois | |
| eslint.config.mjs | il y a 2 mois | |
| next.config.ts | il y a 1 mois | |
| package-lock.json | il y a 1 mois | |
| package.json | il y a 1 mois | |
| postcss.config.mjs | il y a 2 mois | |
| tsconfig.json | il y a 2 mois | |
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.
lib/config.ts)data/)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 installlì. Installa le dipendenze su una macchina con internet (stesso OS, Linux), poi copia l’intera cartellanode_modulessul server insieme al progetto buildato. Su Ubuntu bastanpm run build(senode_modulesè presente) +npm start.
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. |
TEXT_LIMITS)card: {
title: 200,
shortDescription: 500,
fullContent: 20_000,
actionUrl: 2000,
},
portal: {
title: 200,
welcomeText: 1000,
},
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à.
| 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:
/admin resta sempre accessibile anche con una lock attiva.Gli upload passano per tre controlli in cascata. Se uno fallisce, nessun file viene salvato e l’admin riceve un messaggio d’errore.
| 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>).
Il contenuto reale del file viene confrontato con l’estensione dichiarata. Esempi rifiutati:
.jpg → rifiutato (contenuto ≠ estensione)..png → rifiutato (tipo non riconosciuto)..jpg → rifiutato (mismatch interno alla famiglia immagini).mov e mp4 sono intercambiabili (entrambi container ISO BMFF).
Solo i video possono essere ricodificati. Alla ricezione il server sonda i codec:
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.
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.
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.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.themeColor): accettato solo nel formato #RRGGBB.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.
Disponibile dall’admin in Settings → Backup & Restore.
interceptop-backup-<data>.zip con card, configurazione, media e font (esclude i file temporanei).data/ precedente viene conservata come data.bak-<timestamp>/ come rete di sicurezza.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.
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).
factory/preset.zip.data/ precedente resta come data.bak-<timestamp>/.# 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
cd /percorso/del/progetto/data
mkdir -p ../factory
zip -r ../factory/preset.zip cards.txt portals.txt uploads fonts -x "uploads/.tmp/*"
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.
I font vanno collocati in data/fonts/ nei formati .woff2, .woff, .ttf, .otf. Vengono inclusi automaticamente nei backup.
italic/bold nel nome (si usa la variante “regular”; i pesi vengono gestiti dal browser).DEFAULT_FONT in lib/config.ts.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
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.