ソースを参照

Aggiornato il README.md con la documentazione per la gestione del admin

main
Lorenzo Pollutri 4週間前
コミット
45853e4952
1個のファイルの変更66行の追加4行の削除
  1. +66
    -4
      README.md

+ 66
- 4
README.md ファイルの表示

@@ -18,8 +18,9 @@ CMS per portali captive: gestione di card informative, gallerie, flip-book e con
10. [Factory Preset (developer)](#factory-preset-developer) 10. [Factory Preset (developer)](#factory-preset-developer)
11. [Font](#font) 11. [Font](#font)
12. [Deploy sotto sotto-percorso (basePath) dietro Apache](#deploy-sotto-sotto-percorso-basepath-dietro-apache) 12. [Deploy sotto sotto-percorso (basePath) dietro Apache](#deploy-sotto-sotto-percorso-basepath-dietro-apache)
13. [Prerequisiti di sistema](#prerequisiti-di-sistema)
14. [Risoluzione problemi](#risoluzione-problemi)
13. [Protezione dell'amministrazione (Keycloak) e routing](#protezione-dellamministrazione-keycloak-e-routing)
14. [Prerequisiti di sistema](#prerequisiti-di-sistema)
15. [Risoluzione problemi](#risoluzione-problemi)


--- ---


@@ -87,7 +88,7 @@ Per cambiare un qualunque limite: modifica il numero in `lib/config.ts` e ricost
| `IMAGE_GALLERY` | Image Gallery | Galleria di immagini/video/PDF sfogliabile a schermo intero. | | `IMAGE_GALLERY` | Image Gallery | Galleria di immagini/video/PDF sfogliabile a schermo intero. |
| `BOOK` | Flip-Book | Sfoglialibro in formato A4 (due pagine affiancate). | | `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. | | `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`. |
| `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:** **Note sulla Fullscreen Lock:**
- Se ci sono più card lock, viene usata la prima per ordine di visualizzazione. - Se ci sono più card lock, viene usata la prima per ordine di visualizzazione.
@@ -132,7 +133,7 @@ La transcodifica richiede `ffmpeg`/`ffprobe` sul server — vedi [Prerequisiti](


Ogni campo compilabile dall'admin ha un limite (vedi [`TEXT_LIMITS`](#limiti-testo-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. Ogni campo compilabile dall'admin ha un limite (vedi [`TEXT_LIMITS`](#limiti-testo-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.
Gli URL (`actionUrl`) accettano solo gli schemi `http`, `https`, `mailto`, `tel`. Schemi come `javascript:` vengono rifiutati. Per le card **External Link** l'URL è **obbligatorio**: il salvataggio viene rifiutato (UI + server `400`) se il campo è vuoto.


### Caratteri non-ASCII (emoji, cirillico, CJK…) ### Caratteri non-ASCII (emoji, cirillico, CJK…)


@@ -315,6 +316,67 @@ Punti chiave:
- Con `BASE_PATH = '/cards'` l'app vive **sempre** sotto `/cards`, anche **senza** proxy: in locale la raggiungi su `http://localhost:3000/cards` (la radice `/` dà 404). Il proxy serve solo a esporla pubblicamente. - Con `BASE_PATH = '/cards'` l'app vive **sempre** sotto `/cards`, anche **senza** proxy: in locale la raggiungi su `http://localhost:3000/cards` (la radice `/` dà 404). Il proxy serve solo a esporla pubblicamente.
- Un singolo build **non** può rispondere contemporaneamente su `/` e su `/cards`: per cambiare percorso modifica `BASE_PATH` e ricompila. - Un singolo build **non** può rispondere contemporaneamente su `/` e su `/cards`: per cambiare percorso modifica `BASE_PATH` e ricompila.


## Protezione dell'amministrazione (Keycloak) e routing

L'autorizzazione **non** è gestita dall'app Next ma da **Apache `mod_auth_openidc`** (Keycloak), già usato dal captive portal. L'app non contiene codice di auth: si fida del gate del reverse proxy.

### Cosa è protetto
- **Pagina `/cards/admin`** e **API di scrittura**: accessibili solo all'utente Keycloak con `preferred_username == admin`.
- Chi non è autenticato o non è admin → **redirect alla home** del portale (nessun errore).
- **Restano pubbliche** (servono al portale per tutti gli utenti WiFi): le **GET** di `/cards/api/cards` e `/cards/api/portals`, e `/cards/api/files`, `/cards/api/fonts`.
- Protette su **tutti i metodi**: `/cards/api/upload`, `/cards/api/transcode`, `/cards/api/admin/*` (incluse le GET come backup e stato factory-preset).

### Configurazione Apache
1. Il cookie di sessione OIDC deve coprire `/cards`: nel vhost imposta `OIDCCookiePath /` (non un sotto-percorso come `/general`), altrimenti `mod_auth_openidc` non vede la sessione su `/cards`.
2. Aggiungi nel/i vhost (dove l'OIDC è configurato) i blocchi di protezione:
```apache
# Pagina admin: solo utente 'admin'; non autenticato/non admin → home
<Location "/cards/admin">
AuthType openid-connect
OIDCUnAuthAction 401
Require claim preferred_username:admin
ErrorDocument 401 https://<host>/cards/
ErrorDocument 403 https://<host>/cards/
</Location>

# API: GET pubblica, scrittura solo admin
<Location "/cards/api/cards">
<LimitExcept GET HEAD>
AuthType openid-connect
OIDCUnAuthAction 401
Require claim preferred_username:admin
</LimitExcept>
</Location>
<Location "/cards/api/portals">
<LimitExcept GET HEAD>
AuthType openid-connect
OIDCUnAuthAction 401
Require claim preferred_username:admin
</LimitExcept>
</Location>

# upload / transcode / admin: tutti i metodi solo admin
<Location "/cards/api/upload">
AuthType openid-connect
OIDCUnAuthAction 401
Require claim preferred_username:admin
</Location>
<Location "/cards/api/transcode">
AuthType openid-connect
OIDCUnAuthAction 401
Require claim preferred_username:admin
</Location>
<Location "/cards/api/admin">
AuthType openid-connect
OIDCUnAuthAction 401
Require claim preferred_username:admin
</Location>
```
Note: `OIDCUnAuthAction 401` fa sì che il non autenticato riceva `401` (poi `ErrorDocument` → home) invece di essere mandato al login. L'`ErrorDocument` di redirect è solo sulla pagina admin: sulle API un accesso non autorizzato riceve `401/403` (corretto per una chiamata API). L'admin, una volta autenticato via Keycloak come utente `admin` (tramite il normale flusso del portale), invia il cookie di sessione con le richieste e può salvare/caricare/fare backup.

### 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 ## Prerequisiti di sistema


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


読み込み中…
キャンセル
保存