""" 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()