193 lines
5.8 KiB
Python
193 lines
5.8 KiB
Python
"""
|
|
Focolari Voting System - Backend Mock
|
|
Sistema di controllo accessi per votazioni del Movimento dei Focolari
|
|
|
|
Utilizzo:
|
|
python main.py # Default: porta 8000, dati default
|
|
python main.py -p 9000 # Porta custom
|
|
python main.py -d data/users_test.json # Dataset custom
|
|
python main.py -p 9000 -d data/users_test.json
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import uvicorn
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import FileResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
|
|
from api.routes import router, init_data
|
|
|
|
# Configurazione default
|
|
DEFAULT_PORT = 8000
|
|
DEFAULT_HOST = "0.0.0.0"
|
|
DEFAULT_DATA = "data/users_default.json"
|
|
STATIC_DIR = Path(__file__).parent.parent / "frontend" / "dist"
|
|
|
|
|
|
def load_data(data_path: str) -> dict:
|
|
"""Carica i dati dal file JSON"""
|
|
path = Path(data_path)
|
|
|
|
if not path.is_absolute():
|
|
# Path relativo alla directory del main.py
|
|
base_dir = Path(__file__).parent
|
|
path = base_dir / path
|
|
|
|
if not path.exists():
|
|
print(f"❌ Errore: File dati non trovato: {path}")
|
|
sys.exit(1)
|
|
|
|
try:
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
print(f"📂 Dati caricati da: {path}")
|
|
print(f" - Password validatore: {'*' * len(data['validator_password'])}")
|
|
print(f" - Sala: {data['room']['room_name']}")
|
|
print(f" - Utenti: {len(data['users'])}")
|
|
return data
|
|
except json.JSONDecodeError as e:
|
|
print(f"❌ Errore parsing JSON: {e}")
|
|
sys.exit(1)
|
|
except KeyError as e:
|
|
print(f"❌ Errore struttura JSON: chiave mancante {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
def create_app(data: dict, serve_frontend: bool = True) -> FastAPI:
|
|
"""Crea e configura l'applicazione FastAPI"""
|
|
app = FastAPI(
|
|
title="Focolari Voting System API",
|
|
description="Backend mock per il sistema di controllo accessi",
|
|
version="1.0.0"
|
|
)
|
|
|
|
# CORS abilitato per tutti (utile in sviluppo)
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# Inizializza i dati nelle routes
|
|
init_data(data)
|
|
|
|
# Registra le routes API
|
|
app.include_router(router)
|
|
|
|
# Serve frontend statico se la cartella esiste
|
|
if serve_frontend and STATIC_DIR.exists():
|
|
print(f"🌐 Frontend statico servito da: {STATIC_DIR}")
|
|
|
|
# Serve index.html su /badge e sue sub-route
|
|
@app.get("/badge")
|
|
@app.get("/badge/") # Con trailing slash per Playwright
|
|
async def serve_badge_index():
|
|
return FileResponse(STATIC_DIR / "index.html")
|
|
|
|
@app.get("/badge/debug")
|
|
async def serve_badge_debug():
|
|
return FileResponse(STATIC_DIR / "index.html")
|
|
|
|
# Monta i file statici (JS, CSS, assets) sotto /badge
|
|
app.mount("/badge/assets", StaticFiles(directory=STATIC_DIR / "assets"), name="assets")
|
|
|
|
# Fallback per altri file statici sotto /badge (favicon, ecc.)
|
|
@app.get("/badge/{filename:path}")
|
|
async def serve_static(filename: str):
|
|
file_path = STATIC_DIR / filename
|
|
if file_path.exists() and file_path.is_file():
|
|
return FileResponse(file_path)
|
|
# Per route SPA sconosciute, serve index.html
|
|
return FileResponse(STATIC_DIR / "index.html")
|
|
else:
|
|
# API-only mode
|
|
@app.get("/")
|
|
async def root():
|
|
"""Endpoint di test per verificare che il server sia attivo"""
|
|
return {
|
|
"status": "ok",
|
|
"message": "Focolari Voting System API is running",
|
|
"room": data["room"]["room_name"],
|
|
"frontend": "not built - run 'npm run build' in frontend/"
|
|
}
|
|
|
|
return app
|
|
|
|
|
|
def parse_args():
|
|
"""Parse degli argomenti da linea di comando"""
|
|
parser = argparse.ArgumentParser(
|
|
description="Focolari Voting System - Backend Mock Server",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Esempi:
|
|
python main.py # Avvio standard
|
|
python main.py -p 9000 # Porta 9000
|
|
python main.py -d data/users_test.json # Dataset test
|
|
python main.py --host 127.0.0.1 -p 8080 # Solo localhost
|
|
python main.py --api-only # Solo API, no frontend
|
|
"""
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-p", "--port",
|
|
type=int,
|
|
default=DEFAULT_PORT,
|
|
help=f"Porta del server (default: {DEFAULT_PORT})"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-d", "--data",
|
|
type=str,
|
|
default=DEFAULT_DATA,
|
|
help=f"Path al file JSON con i dati (default: {DEFAULT_DATA})"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--host",
|
|
type=str,
|
|
default=DEFAULT_HOST,
|
|
help=f"Host di binding (default: {DEFAULT_HOST})"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--api-only",
|
|
action="store_true",
|
|
help="Avvia solo le API senza servire il frontend"
|
|
)
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
def main():
|
|
"""Entry point principale"""
|
|
args = parse_args()
|
|
|
|
print("🚀 Avvio Focolari Voting System Backend...")
|
|
print("=" * 50)
|
|
|
|
# Carica i dati
|
|
data = load_data(args.data)
|
|
|
|
print("=" * 50)
|
|
print(f"📍 Server in ascolto su http://{args.host}:{args.port}")
|
|
print(f"📚 Documentazione API su http://{args.host}:{args.port}/docs")
|
|
if not args.api_only and STATIC_DIR.exists():
|
|
print(f"🌐 Frontend disponibile su http://{args.host}:{args.port}/badge")
|
|
print("=" * 50)
|
|
|
|
# Crea e avvia l'app
|
|
app = create_app(data, serve_frontend=not args.api_only)
|
|
uvicorn.run(app, host=args.host, port=args.port)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|