diff --git a/README.md b/README.md index 65edfa6..c0fcbf2 100644 --- a/README.md +++ b/README.md @@ -62,13 +62,117 @@ Per dettagli tecnici, consulta la cartella `ai-prompts/`: ## πŸ” Credenziali Test -- **Badge Validatore:** `999999` -- **Password:** `focolari` +- **Password Validatore:** `focolari` +- **Badge Test:** Qualsiasi badge (es. `0008988288`, `0007399575`) + +I badge di test con anagrafica sono documentati in `backend-mock/API_SPECIFICATION.md`. ## πŸ” Debug Accedi a `/debug` per diagnostica RFID in tempo reale. +--- + +## 🏭 Build e Deploy in Produzione + +### Compilazione Frontend + +Il frontend React deve essere compilato in file statici prima del deploy. + +```bash +# Build automatica (usa dev.sh) +./dev.sh build + +# Oppure manualmente +cd frontend +npm install +npm run build +``` + +I file compilati vengono generati in `frontend/dist/`. + +### Struttura Build Output + +``` +frontend/dist/ +β”œβ”€β”€ index.html +β”œβ”€β”€ favicon.jpg +└── assets/ + β”œβ”€β”€ index-XXXXX.js # Bundle JS minificato + β”œβ”€β”€ index-XXXXX.css # CSS minificato + └── FocolareMovLogo-XXXXX.jpg +``` + +### Deploy + +#### Opzione 1: Backend Mock (test/demo) + +Il backend mock Python serve automaticamente il frontend dalla cartella `frontend/dist/`: + +```bash +./dev.sh server --host 0.0.0.0 --port 8000 +``` + +L'applicazione completa sarΓ  disponibile su `http://:8000/`. + +#### Opzione 2: Backend Reale + +Il backend di produzione deve: + +1. **Implementare le API** secondo le specifiche in `backend-mock/API_SPECIFICATION.md` +2. **Servire i file statici** dalla cartella `frontend/dist/` sulla root `/` +3. **Configurare CORS** se frontend e backend sono su domini diversi + +Esempio con un web server (nginx, Apache, etc.): + +```nginx +server { + listen 80; + server_name voting.focolari.org; + + # Frontend statico + location / { + root /var/www/focolari/frontend/dist; + try_files $uri $uri/ /index.html; + } + + # API proxy verso backend + location /info-room { proxy_pass http://localhost:8080; } + location /login-validate { proxy_pass http://localhost:8080; } + location /anagrafica/ { proxy_pass http://localhost:8080; } + location /entry-request { proxy_pass http://localhost:8080; } +} +``` + +#### Opzione 3: Tutto-in-uno (consigliata) + +Il backend reale serve direttamente i file statici: + +```python +# Esempio FastAPI +from fastapi.staticfiles import StaticFiles +from fastapi.responses import FileResponse + +app.mount("/assets", StaticFiles(directory="frontend/dist/assets")) + +@app.get("/") +async def serve_frontend(): + return FileResponse("frontend/dist/index.html") +``` + +### Variabili d'Ambiente + +Il frontend non richiede variabili d'ambiente. Le API sono chiamate con path relativi (`/info-room`, etc.), quindi funziona automaticamente indipendentemente dal dominio o porta. + +### Requisiti Sistema Produzione + +- **Python 3.11+** con `pipenv` (per backend mock) +- **Node.js 18+** con `npm` (solo per build frontend) +- **Browser moderno** (Chrome, Safari, Firefox) sul tablet +- **Lettore RFID** configurato come tastiera HID + +--- + ## πŸ“„ Licenza Progetto privato - Movimento dei Focolari diff --git a/ai-prompts/01-backend-plan.md b/ai-prompts/01-backend-plan.md index 242eeea..dd8a96c 100644 --- a/ai-prompts/01-backend-plan.md +++ b/ai-prompts/01-backend-plan.md @@ -42,7 +42,7 @@ backend-mock/ - [x] `EntryRequest` - user_badge + validator_password - [x] `UserResponse` - dati utente + warning opzionale - [x] `RoomInfoResponse` - nome sala + meeting_id + **server_start_time** -- [x] `LoginResponse` - success + message + token +- [x] `LoginResponse` - success + message (senza token) - [x] `EntryResponse` - success + message (SENZA welcome_message) - [x] Spostare modelli in file dedicato diff --git a/ai-prompts/02-frontend-plan.md b/ai-prompts/02-frontend-plan.md index 6ced21d..4392974 100644 --- a/ai-prompts/02-frontend-plan.md +++ b/ai-prompts/02-frontend-plan.md @@ -34,10 +34,10 @@ Ottimizzata per tablet in orizzontale. - [x] `RoomInfo` - info sala + **server_start_time** - [x] `User` - dati utente -- [x] `LoginRequest/Response` +- [x] `LoginRequest/Response` (senza token) - [x] `EntryRequest/Response` (SENZA welcome_message) - [x] `AppState` - stati applicazione -- [x] `ValidatorSession` - sessione validatore + **serverStartTime** +- [x] `ValidatorSession` - sessione validatore (badge, password, serverStartTime) - [x] `RFIDScannerState` - stato scanner - [x] `RFIDScanResult` - risultato scan diff --git a/backend-mock/API_SPECIFICATION.md b/backend-mock/API_SPECIFICATION.md index 78a9737..f3b2d19 100644 --- a/backend-mock/API_SPECIFICATION.md +++ b/backend-mock/API_SPECIFICATION.md @@ -100,8 +100,7 @@ Verifica le credenziali del validatore. **IMPORTANTE:** Qualsiasi badge puΓ² div ```json { "success": true, - "message": "Login effettuato con successo", - "token": "optional-jwt-token" + "message": "Login effettuato con successo" } ``` @@ -123,11 +122,9 @@ Verifica le credenziali del validatore. **IMPORTANTE:** Qualsiasi badge puΓ² div **Note:** Il frontend gestisce entrambi i codici di errore (401 e 403) mostrando il messaggio ricevuto nel campo `detail`. -**Note:** - La password Γ¨ l'unico fattore di autenticazione - Il badge viene memorizzato dal frontend come "badge validatore" per conferme successive -- Il token Γ¨ opzionale (per future implementazioni di autenticazione JWT) --- @@ -144,12 +141,13 @@ Il badge Γ¨ una **stringa**, non un numero. Va confrontato **esattamente** carat Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge **diversi**. **Ruoli Ammessi:** -| Ruolo | Descrizione | -|-------|-------------| -| `Convocato` | Con diritto di voto | -| `Invitato` | Senza diritto di voto | -| `Tecnico` | Staff tecnico | -| `Staff` | Personale organizzativo | + +| Ruolo | Descrizione | +|-------------|-------------------------| +| `Convocato` | Con diritto di voto | +| `Invitato` | Senza diritto di voto | +| `Tecnico` | Staff tecnico | +| `Staff` | Personale organizzativo | #### Response `200 OK` (Utente trovato e ammesso) @@ -160,13 +158,7 @@ Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge ** "cognome": "Bianchi", "url_foto": "https://example.com/foto.jpg", "ruolo": "Votante", - "ammesso": true - # - <-- - utente - ammesso - all - 'ingresso + "ammesso": true # <-- utente ammesso all 'ingresso } ``` @@ -179,14 +171,7 @@ Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge ** "cognome": "Verdi", "url_foto": "https://example.com/foto.jpg", "ruolo": "Tecnico", - "ammesso": false, - # - <-- - utente - NON - ammesso - all - 'ingresso + "ammesso": false, # <-- utente NON ammesso all 'ingresso "warning": "Utente non ammesso all'ingresso" } ``` diff --git a/backend-mock/api/routes.py b/backend-mock/api/routes.py index 062fa1d..ca01aeb 100644 --- a/backend-mock/api/routes.py +++ b/backend-mock/api/routes.py @@ -87,8 +87,7 @@ async def login_validate(request: LoginRequest): return LoginResponse( success=True, - message="Login validatore effettuato con successo", - token=f"focolare-token-{clean}" + message="Login validatore effettuato con successo" ) diff --git a/backend-mock/schemas/models.py b/backend-mock/schemas/models.py index 5f6063d..caf28c5 100644 --- a/backend-mock/schemas/models.py +++ b/backend-mock/schemas/models.py @@ -41,7 +41,6 @@ class LoginResponse(BaseModel): """Risposta login""" success: bool message: str - token: Optional[str] = None class EntryResponse(BaseModel): diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1e279a6..07d3fb7 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -240,7 +240,6 @@ function App() { const session: ValidatorSession = { badge: pendingValidatorBadge, password: password, // Salvo la password per le conferme ingresso - token: response.token || '', loginTime: Date.now(), expiresAt: Date.now() + SESSION_DURATION_MS, serverStartTime: roomInfo.server_start_time, // Per invalidare se server riparte diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index a931c06..53e7afe 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -25,7 +25,6 @@ export interface User { export interface LoginResponse { success: boolean; message: string; - token?: string; } export interface EntryResponse { @@ -63,7 +62,6 @@ export type AppState = export interface ValidatorSession { badge: string; password: string; - token: string; loginTime: number; expiresAt: number; serverStartTime: number; // Per invalidare se il server riparte