feat: rimosso il token dalla risposta di login e aggiornati i modelli di dati
This commit is contained in:
108
README.md
108
README.md
@@ -62,13 +62,117 @@ Per dettagli tecnici, consulta la cartella `ai-prompts/`:
|
|||||||
|
|
||||||
## 🔐 Credenziali Test
|
## 🔐 Credenziali Test
|
||||||
|
|
||||||
- **Badge Validatore:** `999999`
|
- **Password Validatore:** `focolari`
|
||||||
- **Password:** `focolari`
|
- **Badge Test:** Qualsiasi badge (es. `0008988288`, `0007399575`)
|
||||||
|
|
||||||
|
I badge di test con anagrafica sono documentati in `backend-mock/API_SPECIFICATION.md`.
|
||||||
|
|
||||||
## 🔍 Debug
|
## 🔍 Debug
|
||||||
|
|
||||||
Accedi a `/debug` per diagnostica RFID in tempo reale.
|
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://<IP>: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
|
## 📄 Licenza
|
||||||
|
|
||||||
Progetto privato - Movimento dei Focolari
|
Progetto privato - Movimento dei Focolari
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ backend-mock/
|
|||||||
- [x] `EntryRequest` - user_badge + validator_password
|
- [x] `EntryRequest` - user_badge + validator_password
|
||||||
- [x] `UserResponse` - dati utente + warning opzionale
|
- [x] `UserResponse` - dati utente + warning opzionale
|
||||||
- [x] `RoomInfoResponse` - nome sala + meeting_id + **server_start_time**
|
- [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] `EntryResponse` - success + message (SENZA welcome_message)
|
||||||
- [x] Spostare modelli in file dedicato
|
- [x] Spostare modelli in file dedicato
|
||||||
|
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ Ottimizzata per tablet in orizzontale.
|
|||||||
|
|
||||||
- [x] `RoomInfo` - info sala + **server_start_time**
|
- [x] `RoomInfo` - info sala + **server_start_time**
|
||||||
- [x] `User` - dati utente
|
- [x] `User` - dati utente
|
||||||
- [x] `LoginRequest/Response`
|
- [x] `LoginRequest/Response` (senza token)
|
||||||
- [x] `EntryRequest/Response` (SENZA welcome_message)
|
- [x] `EntryRequest/Response` (SENZA welcome_message)
|
||||||
- [x] `AppState` - stati applicazione
|
- [x] `AppState` - stati applicazione
|
||||||
- [x] `ValidatorSession` - sessione validatore + **serverStartTime**
|
- [x] `ValidatorSession` - sessione validatore (badge, password, serverStartTime)
|
||||||
- [x] `RFIDScannerState` - stato scanner
|
- [x] `RFIDScannerState` - stato scanner
|
||||||
- [x] `RFIDScanResult` - risultato scan
|
- [x] `RFIDScanResult` - risultato scan
|
||||||
|
|
||||||
|
|||||||
@@ -100,8 +100,7 @@ Verifica le credenziali del validatore. **IMPORTANTE:** Qualsiasi badge può div
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
"message": "Login effettuato con successo",
|
"message": "Login effettuato con successo"
|
||||||
"token": "optional-jwt-token"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -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
|
**Note:** Il frontend gestisce entrambi i codici di errore (401 e 403) mostrando il messaggio ricevuto nel campo
|
||||||
`detail`.
|
`detail`.
|
||||||
**Note:**
|
|
||||||
|
|
||||||
- La password è l'unico fattore di autenticazione
|
- La password è l'unico fattore di autenticazione
|
||||||
- Il badge viene memorizzato dal frontend come "badge validatore" per conferme successive
|
- Il badge viene memorizzato dal frontend come "badge validatore" per conferme successive
|
||||||
- Il token è opzionale (per future implementazioni di autenticazione JWT)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -144,8 +141,9 @@ Il badge è una **stringa**, non un numero. Va confrontato **esattamente** carat
|
|||||||
Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge **diversi**.
|
Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge **diversi**.
|
||||||
|
|
||||||
**Ruoli Ammessi:**
|
**Ruoli Ammessi:**
|
||||||
|
|
||||||
| Ruolo | Descrizione |
|
| Ruolo | Descrizione |
|
||||||
|-------|-------------|
|
|-------------|-------------------------|
|
||||||
| `Convocato` | Con diritto di voto |
|
| `Convocato` | Con diritto di voto |
|
||||||
| `Invitato` | Senza diritto di voto |
|
| `Invitato` | Senza diritto di voto |
|
||||||
| `Tecnico` | Staff tecnico |
|
| `Tecnico` | Staff tecnico |
|
||||||
@@ -160,13 +158,7 @@ Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge **
|
|||||||
"cognome": "Bianchi",
|
"cognome": "Bianchi",
|
||||||
"url_foto": "https://example.com/foto.jpg",
|
"url_foto": "https://example.com/foto.jpg",
|
||||||
"ruolo": "Votante",
|
"ruolo": "Votante",
|
||||||
"ammesso": true
|
"ammesso": true # <-- utente ammesso all 'ingresso
|
||||||
#
|
|
||||||
<--
|
|
||||||
utente
|
|
||||||
ammesso
|
|
||||||
all
|
|
||||||
'ingresso
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -179,14 +171,7 @@ Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge **
|
|||||||
"cognome": "Verdi",
|
"cognome": "Verdi",
|
||||||
"url_foto": "https://example.com/foto.jpg",
|
"url_foto": "https://example.com/foto.jpg",
|
||||||
"ruolo": "Tecnico",
|
"ruolo": "Tecnico",
|
||||||
"ammesso": false,
|
"ammesso": false, # <-- utente NON ammesso all 'ingresso
|
||||||
#
|
|
||||||
<--
|
|
||||||
utente
|
|
||||||
NON
|
|
||||||
ammesso
|
|
||||||
all
|
|
||||||
'ingresso
|
|
||||||
"warning": "Utente non ammesso all'ingresso"
|
"warning": "Utente non ammesso all'ingresso"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -87,8 +87,7 @@ async def login_validate(request: LoginRequest):
|
|||||||
|
|
||||||
return LoginResponse(
|
return LoginResponse(
|
||||||
success=True,
|
success=True,
|
||||||
message="Login validatore effettuato con successo",
|
message="Login validatore effettuato con successo"
|
||||||
token=f"focolare-token-{clean}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ class LoginResponse(BaseModel):
|
|||||||
"""Risposta login"""
|
"""Risposta login"""
|
||||||
success: bool
|
success: bool
|
||||||
message: str
|
message: str
|
||||||
token: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class EntryResponse(BaseModel):
|
class EntryResponse(BaseModel):
|
||||||
|
|||||||
@@ -240,7 +240,6 @@ function App() {
|
|||||||
const session: ValidatorSession = {
|
const session: ValidatorSession = {
|
||||||
badge: pendingValidatorBadge,
|
badge: pendingValidatorBadge,
|
||||||
password: password, // Salvo la password per le conferme ingresso
|
password: password, // Salvo la password per le conferme ingresso
|
||||||
token: response.token || '',
|
|
||||||
loginTime: Date.now(),
|
loginTime: Date.now(),
|
||||||
expiresAt: Date.now() + SESSION_DURATION_MS,
|
expiresAt: Date.now() + SESSION_DURATION_MS,
|
||||||
serverStartTime: roomInfo.server_start_time, // Per invalidare se server riparte
|
serverStartTime: roomInfo.server_start_time, // Per invalidare se server riparte
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export interface User {
|
|||||||
export interface LoginResponse {
|
export interface LoginResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
message: string;
|
message: string;
|
||||||
token?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EntryResponse {
|
export interface EntryResponse {
|
||||||
@@ -63,7 +62,6 @@ export type AppState =
|
|||||||
export interface ValidatorSession {
|
export interface ValidatorSession {
|
||||||
badge: string;
|
badge: string;
|
||||||
password: string;
|
password: string;
|
||||||
token: string;
|
|
||||||
loginTime: number;
|
loginTime: number;
|
||||||
expiresAt: number;
|
expiresAt: number;
|
||||||
serverStartTime: number; // Per invalidare se il server riparte
|
serverStartTime: number; // Per invalidare se il server riparte
|
||||||
|
|||||||
Reference in New Issue
Block a user