# 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 offline**. --- ## Indice 1. [Avvio](#avvio) 2. [Configurazione (`lib/config.ts`)](#configurazione-libconfigts) 3. [Tipi di card](#tipi-di-card) 4. [File consentiti negli upload](#file-consentiti-negli-upload) 5. [Limiti di testo](#limiti-di-testo) 6. [Sicurezza degli input](#sicurezza-degli-input) 7. [Struttura dei dati (`data/`)](#struttura-dei-dati-data) 8. [Stato zero, rilasci e aggiornamenti del codice](#stato-zero-rilasci-e-aggiornamenti-del-codice) 9. [Backup e ripristino](#backup-e-ripristino) 10. [Factory Preset (developer)](#factory-preset-developer) 11. [Font](#font) 12. [Deploy sotto sotto-percorso (basePath) dietro Apache](#deploy-sotto-sotto-percorso-basepath-dietro-apache) 13. [Protezione dell'amministrazione (Keycloak) e routing](#protezione-dellamministrazione-keycloak-e-routing) 14. [Prerequisiti di sistema](#prerequisiti-di-sistema) 15. [Risoluzione problemi](#risoluzione-problemi) --- ## Avvio **Sviluppo:** ```bash git clone https://git.afasystems.it/pollutri/ProgettoInterceptoCasaDellaScuola.git cd ProgettoInterceptoCasaDellaScuola npm install npm run build npm run dev ``` Con `BASE_PATH = '/cards'` (default) apri [http://localhost:3000/cards](http://localhost:3000/cards) (portale pubblico) e [http://localhost:3000/cards/admin](http://localhost:3000/cards/admin) (amministrazione). Le URL "nude" `/` e `/admin` reindirizzano automaticamente a quelle prefissate. Con `BASE_PATH = ''` l'app gira sulla radice (`/` e `/admin`). **Produzione:** ```bash /conf/etc/rc.d/rc.custom 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. Sul server basta `npm run build` (se `node_modules` è presente) + `npm start`. ### Prerequisiti per la produzione Per un deploy reale del CPC, oltre a far girare il processo Next servono altri tre setup, ciascuno con la sua sezione dedicata: 1. **Apache reverse proxy** che esponga il portale su `https:///cards/` → vedi [Deploy sotto sotto-percorso (basePath) dietro Apache](#deploy-sotto-sotto-percorso-basepath-dietro-apache). 2. **Autenticazione Keycloak** per `/cards/admin` e le API di scrittura → vedi [Protezione dell'amministrazione (Keycloak) e routing](#protezione-dellamministrazione-keycloak-e-routing). In sintesi: deve esistere in Keycloak un utente con username **`admin`** (lo username è la chiave di accesso). Per più amministratori si aggiungono altri username alla whitelist nei `` Apache. Tutti gli altri utenti (autenticati o no) vengono reindirizzati alla home pubblica. 3. **Binari di sistema** (`ffmpeg`, `ffprobe`, `zip`, `unzip`) per upload video / backup → vedi [Prerequisiti di sistema](#prerequisiti-di-sistema). --- ## Configurazione (`lib/config.ts`) Tutte le impostazioni globali sono **flag build-time** nel file [`lib/config.ts`](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_PRESET_SAVE_ENABLED` | `false` | Mostra il bottone "💾 Save as Factory Preset (dev)" nell'admin (funzione developer per ricreare/aggiornare il preset). Il bottone "🏭 Factory Reset" e lo stato del preset sono **sempre visibili** indipendentemente da questo flag — vedi [Factory Preset](#factory-preset-developer). L'endpoint API `POST /api/admin/factory-preset` resta attivo 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`) ```ts card: { title: 200, shortDescription: 500, fullContent: 20_000, actionUrl: 2000, }, portal: { title: 200, welcomeText: 1000, }, ``` ### Limiti upload (`UPLOAD_LIMITS`) ```ts 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. L'**URL è obbligatorio**: il salvataggio è bloccato (lato UI e lato server) se manca. 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 `