|
|
|
@@ -6,43 +6,92 @@ CMS per portali captive: gestione di card informative, gallerie, flip-book e con |
|
|
|
|
|
|
|
## 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) |
|
|
|
1. [Prerequisiti di sistema](#prerequisiti-di-sistema) |
|
|
|
2. [Avvio](#avvio) |
|
|
|
3. [Configurazione (`lib/config.ts`)](#configurazione-libconfigts) |
|
|
|
4. [Tipi di card](#tipi-di-card) |
|
|
|
5. [File consentiti negli upload](#file-consentiti-negli-upload) |
|
|
|
6. [Limiti di testo](#limiti-di-testo) |
|
|
|
7. [Sicurezza degli input](#sicurezza-degli-input) |
|
|
|
8. [Struttura dei dati (`data/`)](#struttura-dei-dati-data) |
|
|
|
9. [Stato zero, rilasci e aggiornamenti del codice](#stato-zero-rilasci-e-aggiornamenti-del-codice) |
|
|
|
10. [Backup e ripristino](#backup-e-ripristino) |
|
|
|
11. [Factory Preset (developer)](#factory-preset-developer) |
|
|
|
12. [Font](#font) |
|
|
|
13. [Deploy sotto sotto-percorso (basePath) dietro Apache](#deploy-sotto-sotto-percorso-basepath-dietro-apache) |
|
|
|
14. [Protezione dell'amministrazione (Keycloak) e routing](#protezione-dellamministrazione-keycloak-e-routing) |
|
|
|
15. [Risoluzione problemi](#risoluzione-problemi) |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## 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 | Pulsanti "Save backup (ZIP)" e "Save as Factory Preset (dev)" → `503` | |
|
|
|
| `unzip` | Ripristino backup / factory reset | Pulsanti di ripristino → `503` | |
|
|
|
|
|
|
|
Verifica su server: |
|
|
|
```bash |
|
|
|
which ffmpeg ffprobe zip unzip |
|
|
|
``` |
|
|
|
Se mancano, installali con il gestore pacchetti del sistema (i nomi dei pacchetti sono `ffmpeg`, `zip`, `unzip`). |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Avvio |
|
|
|
|
|
|
|
**Sviluppo:** |
|
|
|
**Installazione:** |
|
|
|
```bash |
|
|
|
mkdri -p /data/service/captive-portal-cms |
|
|
|
mkdir -p /data/service/captive-portal-cms |
|
|
|
cd /data/service/captive-portal-cms |
|
|
|
git clone https://git.afasystems.it/pollutri/captive-portal-cms.git |
|
|
|
cd captive-portal-cms |
|
|
|
npm install |
|
|
|
npm run build |
|
|
|
``` |
|
|
|
|
|
|
|
**In fase di sviluppo avviarlo con:** |
|
|
|
```bash |
|
|
|
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:** |
|
|
|
**In produzione avviarlo con:** |
|
|
|
```bash |
|
|
|
/conf/etc/rc.d/rc.custom start |
|
|
|
``` |
|
|
|
|
|
|
|
**Esempio di rc.custom per CPC:** |
|
|
|
```bash |
|
|
|
. /etc/mnvars |
|
|
|
. /etc/mnsuper.conf |
|
|
|
. $MN_rcconfig |
|
|
|
|
|
|
|
BIN="xxxxx" |
|
|
|
|
|
|
|
return=$rc_done |
|
|
|
case "$1" in |
|
|
|
start) |
|
|
|
cd /data/service/captive-portal-cms/captive-portal-cms && npm start & |
|
|
|
;; |
|
|
|
stop) |
|
|
|
fuser -k 3000/tcp |
|
|
|
;; |
|
|
|
restart) |
|
|
|
$0 stop && $0 start || return=$rc_failed |
|
|
|
;; |
|
|
|
*) |
|
|
|
echo "Usage: $0 {start|stop|restart}" |
|
|
|
exit 1 |
|
|
|
esac |
|
|
|
``` |
|
|
|
|
|
|
|
> **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 |
|
|
|
@@ -267,11 +316,48 @@ Copia `factory/preset.zip` sulle altre installazioni: l'admin lo vedrà subito i |
|
|
|
|
|
|
|
## Font |
|
|
|
|
|
|
|
I font vanno collocati in `data/fonts/` nei formati `.woff2`, `.woff`, `.ttf`, `.otf`. Vengono inclusi automaticamente nei backup. |
|
|
|
I font vivono in `data/fonts/` nei formati `.woff2`, `.woff`, `.ttf`, `.otf`. Vengono inclusi automaticamente nei backup. |
|
|
|
|
|
|
|
### Caricarli dall'admin |
|
|
|
Settings → **Portal font** → pulsante **Upload font…** Il font appena caricato viene auto-selezionato come font del portale. Sotto al dropdown c'è anche il link **Delete selected font** per rimuovere quello attivo (i portali che lo usavano fanno fallback al font di sistema). |
|
|
|
|
|
|
|
L'upload è automaticamente protetto dal gate Apache: solo gli utenti in whitelist sul blocco `<Location /cards/api/admin>` possono chiamare `POST` e `DELETE` su `/cards/api/admin/fonts`. Il `GET /cards/api/fonts` (lettura/serving) resta pubblico per i client del portale. |
|
|
|
|
|
|
|
### Controlli e validazione lato server |
|
|
|
|
|
|
|
- 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. |
|
|
|
Ogni upload passa per **quattro controlli in cascata**. Se anche solo uno fallisce, il file NON viene salvato e l'API risponde con un errore HTTP descrittivo: |
|
|
|
|
|
|
|
| Controllo | Cosa verifica | Errore in caso di fallimento | |
|
|
|
|---|---|---| |
|
|
|
| **Estensione** | il file ha una delle estensioni ammesse: `.woff2`, `.woff`, `.ttf`, `.otf` | `400 Unsupported font extension` | |
|
|
|
| **Nome file** | è solo un basename (no path traversal), contiene solo `[a-zA-Z0-9_.-]`, non comincia con `.` | `400 Invalid font filename` | |
|
|
|
| **Dimensione** | il file pesa al massimo **5 MB** (vedi `UPLOAD_LIMITS.font` in [`lib/config.ts`](lib/config.ts)) | `413 Font too large` | |
|
|
|
| **Magic-bytes** | il **contenuto reale** del file corrisponde all'estensione dichiarata: i primi byte devono essere quelli di un font WOFF/WOFF2/TTF/OTF | `400 Font content does not match extension` (o `400 unknown format` se non riconoscibile) | |
|
|
|
|
|
|
|
`.ttf` e `.otf` condividono il container SFNT, quindi sono trattati come intercambiabili dalla validazione magic-bytes (un file `.ttf` con header OTF passa e viceversa). |
|
|
|
|
|
|
|
### Convenzioni di naming (pesi e corsivi) |
|
|
|
|
|
|
|
I file caricati conservano il **nome originale** (modulo la sanitizzazione di cui sopra). Questo è essenziale perché il rendering del portale cerca automaticamente i pesi e i corsivi sulla base di pattern del nome file: |
|
|
|
|
|
|
|
- File regular: `Name.woff2` |
|
|
|
- Italic: `Name-Italic.woff2` o `Name Italic.woff2` |
|
|
|
- Bold: `Name-Bold.woff2` |
|
|
|
- Bold Italic: `Name-BoldItalic.woff2` |
|
|
|
|
|
|
|
L'elenco mostrato nel dropdown esclude i file con `italic`/`bold` nel nome (si seleziona sempre la variante regular; i pesi/corsivi vengono associati automaticamente da [`app/layout.tsx`](app/layout.tsx)). |
|
|
|
|
|
|
|
### Alternative all'upload UI |
|
|
|
|
|
|
|
- **Copia diretta**: si possono mettere i file in `data/fonts/` via SCP/FTP, eseguendo manualmente le stesse regole di naming. |
|
|
|
- **Curl** (developer): |
|
|
|
```bash |
|
|
|
curl -X POST -F "file=@Roboto.woff2" https://<host>/cards/api/admin/fonts |
|
|
|
curl -X DELETE "https://<host>/cards/api/admin/fonts?name=Roboto.woff2" |
|
|
|
``` |
|
|
|
- **Default globale**: se vuoi imporre un font su tutti i portali che non ne hanno scelto uno, imposta `DEFAULT_FONT` in [`lib/config.ts`](lib/config.ts) col nome esatto del file (stringa vuota = font di sistema). |
|
|
|
|
|
|
|
Il welcome text formattato (grassetto/corsivo) eredita automaticamente il font selezionato dal portale. |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
@@ -346,7 +432,7 @@ L'autorizzazione **non** è gestita dall'app Next ma da **Apache `mod_auth_openi |
|
|
|
|
|
|
|
1. Apri l'**Admin Console** di Keycloak. |
|
|
|
2. Seleziona il realm del captive portal (es. `interceptor1`). |
|
|
|
3. **Users** → verifica che esista un utente con username **`admin`** (o crea l'utente se non c'è: bottone **Add user** → Username: `admin` → **Create** → imposta una password dal tab **Credentials**). |
|
|
|
3. **Users** → verifica che esista un utente con username **`admin`** (o crea l'utente se non c'è: dall'amministrazione majornet). |
|
|
|
4. Per più amministratori, NON è necessario fare nulla in Keycloak: basta aggiungere altri username alla whitelist Apache (vedi punto B). Crea solo gli utenti Keycloak corrispondenti. |
|
|
|
5. *(Verifica opzionale)* **Clients** → `interceptor1-client` → tab **Client scopes** → **Evaluate** → scegli l'utente → **Generated ID token** → controlla che il claim `preferred_username` valga `admin`. Quel claim è incluso di default nei token Keycloak. |
|
|
|
|
|
|
|
@@ -451,7 +537,7 @@ L'amministratore è identificato dal **`preferred_username` Keycloak**, controll |
|
|
|
1. **In Keycloak** (Admin Console → Users): crea/verifica gli utenti con gli username che vuoi promuovere ad admin (es. `admin`, `alice`, `bob`). Imposta le credenziali normalmente. |
|
|
|
2. **In Apache** (in tutti i blocchi `<Location "/cards/admin">`, `<Location "/cards/api/...">`): elenca gli username separati da spazi sulla riga `Require claim`: |
|
|
|
```apache |
|
|
|
Require claim preferred_username:admin alice bob |
|
|
|
Require claim preferred_username:admin pollutri russi |
|
|
|
``` |
|
|
|
(I valori multipli sono in OR — passa chiunque abbia uno di quegli username.) |
|
|
|
3. **Reload Apache**: `apachectl configtest && systemctl reload apache2`. |
|
|
|
@@ -481,24 +567,6 @@ A quel punto, ogni nuovo admin si gestisce solo in Keycloak (assegnando/rimuoven |
|
|
|
### Pagine inesistenti → redirect alla home |
|
|
|
Qualsiasi percorso non corrispondente a una pagina o API reale del progetto **non mostra una schermata d'errore**: viene reindirizzato alla home. È gestito lato Next da una rotta catch-all ([app/[...not_found]/page.tsx](app/%5B...not_found%5D/page.tsx)) che esegue `redirect('/')`. Le rotte reali (`/`, `/admin`, `/api/*`) hanno priorità; solo i path inesistenti finiscono lì. Le API inesistenti restano `404` (il redirect riguarda le pagine). |
|
|
|
|
|
|
|
## 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 | Pulsanti "Save backup (ZIP)" e "Save as Factory Preset (dev)" → `503` | |
|
|
|
| `unzip` | Ripristino backup / factory reset | Pulsanti di ripristino → `503` | |
|
|
|
|
|
|
|
Verifica su server: |
|
|
|
```bash |
|
|
|
which ffmpeg ffprobe zip unzip |
|
|
|
``` |
|
|
|
Se mancano, installali con il gestore pacchetti del sistema (i nomi dei pacchetti sono `ffmpeg`, `zip`, `unzip`). |
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
## Risoluzione problemi |
|
|
|
|
|
|
|
|