feat: Controllo accessi RFID completo con gestione sessioni
- Aggiunto supporto multi-pattern RFID (US/IT layout) - Implementata invalidazione sessioni al restart del server - Schermata "badge non trovato" con countdown 30s - Notifica quando badge validatore passato senza utente - Database aggiornato con badge reali di test - Layout ottimizzato per tablet orizzontale - Banner NumLock per desktop - Toggle visibilità password - Carosello benvenuto multilingua (10 lingue) - Pagina debug RFID (/debug)
This commit is contained in:
153
backend-mock/api/routes.py
Normal file
153
backend-mock/api/routes.py
Normal file
@@ -0,0 +1,153 @@
|
||||
"""
|
||||
Focolari Voting System - API Routes
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from schemas import (
|
||||
LoginRequest,
|
||||
EntryRequest,
|
||||
UserResponse,
|
||||
RoomInfoResponse,
|
||||
LoginResponse,
|
||||
EntryResponse,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Timestamp di avvio del server (per invalidare sessioni frontend)
|
||||
SERVER_START_TIME = int(time.time() * 1000)
|
||||
|
||||
# Dati caricati dinamicamente dal main
|
||||
_data = {
|
||||
"validator_password": "",
|
||||
"room": {"room_name": "", "meeting_id": ""},
|
||||
"users": []
|
||||
}
|
||||
|
||||
# Caratteri sentinel da pulire (tutti i layout supportati)
|
||||
SENTINEL_CHARS = [";", "?", "ò", "_", "ç", "+"]
|
||||
|
||||
|
||||
def init_data(data: dict):
|
||||
"""Inizializza i dati caricati dal file JSON"""
|
||||
global _data
|
||||
_data = data
|
||||
|
||||
|
||||
def clean_badge(badge: str) -> str:
|
||||
"""Rimuove i caratteri sentinel dal badge"""
|
||||
clean = badge.strip()
|
||||
for char in SENTINEL_CHARS:
|
||||
clean = clean.replace(char, "")
|
||||
return clean
|
||||
|
||||
|
||||
def find_user(badge_code: str) -> dict | None:
|
||||
"""Cerca un utente per badge code"""
|
||||
clean = clean_badge(badge_code)
|
||||
for user in _data["users"]:
|
||||
if user["badge_code"] == clean or user["badge_code"].lstrip("0") == clean.lstrip("0"):
|
||||
return user
|
||||
return None
|
||||
|
||||
|
||||
@router.get("/info-room", response_model=RoomInfoResponse)
|
||||
async def get_room_info():
|
||||
"""Restituisce le informazioni sulla sala e la riunione corrente."""
|
||||
return RoomInfoResponse(
|
||||
room_name=_data["room"]["room_name"],
|
||||
meeting_id=_data["room"]["meeting_id"],
|
||||
server_start_time=SERVER_START_TIME
|
||||
)
|
||||
|
||||
|
||||
@router.post("/login-validate", response_model=LoginResponse)
|
||||
async def login_validate(request: LoginRequest):
|
||||
"""
|
||||
Valida la password del validatore.
|
||||
|
||||
Il badge viene passato dal frontend ma attualmente non viene verificato
|
||||
lato server - serve solo per essere memorizzato nella sessione frontend.
|
||||
|
||||
TODO: Concordare con committenti se il badge debba essere verificato
|
||||
anche lato server (es. lista badge validatori autorizzati).
|
||||
"""
|
||||
if request.password != _data["validator_password"]:
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Password non corretta"
|
||||
)
|
||||
|
||||
# Il badge viene restituito per conferma, ma non è validato lato server
|
||||
clean = clean_badge(request.badge) if request.badge else "unknown"
|
||||
|
||||
return LoginResponse(
|
||||
success=True,
|
||||
message="Login validatore effettuato con successo",
|
||||
token=f"focolare-token-{clean}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/anagrafica/{badge_code}", response_model=UserResponse)
|
||||
async def get_user_anagrafica(badge_code: str):
|
||||
"""
|
||||
Cerca un utente tramite il suo badge code.
|
||||
"""
|
||||
user = find_user(badge_code)
|
||||
|
||||
if not user:
|
||||
clean = clean_badge(badge_code)
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Utente con badge {clean} non trovato nel sistema"
|
||||
)
|
||||
|
||||
response = UserResponse(
|
||||
badge_code=user["badge_code"],
|
||||
nome=user["nome"],
|
||||
cognome=user["cognome"],
|
||||
url_foto=user["url_foto"],
|
||||
ruolo=user["ruolo"],
|
||||
ammesso=user["ammesso"]
|
||||
)
|
||||
|
||||
if not user["ammesso"]:
|
||||
response.warning = "ATTENZIONE: Questo utente NON è autorizzato all'ingresso!"
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@router.post("/entry-request", response_model=EntryResponse)
|
||||
async def process_entry_request(request: EntryRequest):
|
||||
"""
|
||||
Processa una richiesta di ingresso.
|
||||
Risposta asettica senza messaggi multilingua.
|
||||
"""
|
||||
if request.validator_password != _data["validator_password"]:
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Password validatore non corretta"
|
||||
)
|
||||
|
||||
user = find_user(request.user_badge)
|
||||
|
||||
if not user:
|
||||
clean = clean_badge(request.user_badge)
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Utente con badge {clean} non trovato"
|
||||
)
|
||||
|
||||
if not user["ammesso"]:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail=f"L'utente {user['nome']} {user['cognome']} NON è autorizzato all'ingresso"
|
||||
)
|
||||
|
||||
return EntryResponse(
|
||||
success=True,
|
||||
message=f"Ingresso registrato per {user['nome']} {user['cognome']}"
|
||||
)
|
||||
Reference in New Issue
Block a user