from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse import asyncio import json from typing import List, Dict from app.database import engine, SessionLocal from app.models import player, court, booking, match, tournament # Create all tables player.Base.metadata.create_all(bind=engine) court.Base.metadata.create_all(bind=engine) booking.Base.metadata.create_all(bind=engine) match.Base.metadata.create_all(bind=engine) tournament.Base.metadata.create_all(bind=engine) from app.api import players, courts, matches, tournaments, screen app = FastAPI( title="ServeSync API", description="Pickleball Court Management System", version="1.0.0", ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Include routers app.include_router(players.router, prefix="/api") app.include_router(courts.router, prefix="/api") app.include_router(matches.router, prefix="/api") app.include_router(tournaments.router, prefix="/api") app.include_router(screen.router, prefix="/api") # WebSocket connection manager class ConnectionManager: def __init__(self): self.active_connections: Dict[str, List[WebSocket]] = {} async def connect(self, websocket: WebSocket, channel: str): await websocket.accept() if channel not in self.active_connections: self.active_connections[channel] = [] self.active_connections[channel].append(websocket) def disconnect(self, websocket: WebSocket, channel: str): if channel in self.active_connections: self.active_connections[channel].remove(websocket) async def broadcast(self, channel: str, data: dict): if channel in self.active_connections: disconnected = [] for connection in self.active_connections[channel]: try: await connection.send_text(json.dumps(data)) except Exception: disconnected.append(connection) for conn in disconnected: self.active_connections[channel].remove(conn) manager = ConnectionManager() @app.websocket("/ws/court/{court_id}") async def websocket_court(websocket: WebSocket, court_id: int): """WebSocket for real-time court display updates""" channel = f"court_{court_id}" await manager.connect(websocket, channel) try: while True: # Send court data every 3 seconds db = SessionLocal() try: from app.api.screen import get_court_display data = get_court_display(court_id, db) await websocket.send_text(json.dumps(data)) finally: db.close() await asyncio.sleep(3) except WebSocketDisconnect: manager.disconnect(websocket, channel) @app.websocket("/ws/overview") async def websocket_overview(websocket: WebSocket): """WebSocket for real-time overview display""" channel = "overview" await manager.connect(websocket, channel) try: while True: db = SessionLocal() try: from app.api.screen import get_overview data = get_overview(db) await websocket.send_text(json.dumps(data)) finally: db.close() await asyncio.sleep(3) except WebSocketDisconnect: manager.disconnect(websocket, channel) @app.websocket("/ws/matches") async def websocket_matches(websocket: WebSocket): """WebSocket for real-time match lobby updates""" channel = "matches" await manager.connect(websocket, channel) try: while True: db = SessionLocal() try: from app.api.matches import get_lobby from app.api.matches import get_active_matches data = { "lobby": get_lobby(db), "active": get_active_matches(db), } await websocket.send_text(json.dumps(data)) finally: db.close() await asyncio.sleep(2) except WebSocketDisconnect: manager.disconnect(websocket, channel) @app.get("/") def root(): return {"message": "ServeSync API v1.0", "status": "running"} @app.get("/health") def health(): return {"status": "healthy"}