129 lines
4.1 KiB
Python
129 lines
4.1 KiB
Python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from typing import List
|
|
import json
|
|
import logging
|
|
from app.core.config import settings
|
|
from app.api.api import api_router
|
|
|
|
# Import models to ensure they're registered with SQLAlchemy
|
|
from app.models.ppr import PPRRecord, User, Airport, Aircraft
|
|
from app.models.journal import JournalEntry
|
|
from app.models.local_flight import LocalFlight
|
|
from app.models.departure import Departure
|
|
from app.models.arrival import Arrival
|
|
from app.models.circuit import Circuit
|
|
from app.models.movement import Movement
|
|
from app.models.drone_request import DroneRequest
|
|
|
|
# Set up logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
app = FastAPI(
|
|
title=settings.project_name,
|
|
openapi_url=f"{settings.api_v1_str}/openapi.json",
|
|
description="Prior Permission Required (PPR) system API for aircraft operations management",
|
|
version="2.0.0"
|
|
)
|
|
|
|
# Set up CORS middleware
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"], # Configure this properly for production
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# WebSocket connection manager for real-time updates
|
|
class ConnectionManager:
|
|
def __init__(self):
|
|
self.active_connections: List[WebSocket] = []
|
|
|
|
async def connect(self, websocket: WebSocket):
|
|
await websocket.accept()
|
|
self.active_connections.append(websocket)
|
|
logger.info(f"WebSocket connected. Total connections: {len(self.active_connections)}")
|
|
|
|
def disconnect(self, websocket: WebSocket):
|
|
if websocket in self.active_connections:
|
|
self.active_connections.remove(websocket)
|
|
logger.info(f"WebSocket disconnected. Total connections: {len(self.active_connections)}")
|
|
|
|
async def send_personal_message(self, message: str, websocket: WebSocket):
|
|
await websocket.send_text(message)
|
|
|
|
async def broadcast(self, message: dict):
|
|
"""Broadcast an update to every websocket connected to this process."""
|
|
message_str = json.dumps(message)
|
|
dead_connections = []
|
|
for connection in list(self.active_connections):
|
|
try:
|
|
await connection.send_text(message_str)
|
|
except Exception as e:
|
|
logger.warning(f"Failed to send to connection: {e}")
|
|
dead_connections.append(connection)
|
|
|
|
# Remove dead connections
|
|
for connection in dead_connections:
|
|
if connection in self.active_connections:
|
|
self.active_connections.remove(connection)
|
|
|
|
if dead_connections:
|
|
logger.info(f"Removed {len(dead_connections)} dead connections")
|
|
|
|
manager = ConnectionManager()
|
|
|
|
@app.websocket("/ws/tower-updates")
|
|
async def websocket_endpoint(websocket: WebSocket):
|
|
await manager.connect(websocket)
|
|
try:
|
|
while True:
|
|
# Keep connection alive
|
|
data = await websocket.receive_text()
|
|
# Echo back for heartbeat
|
|
await websocket.send_text(f"Heartbeat: {data}")
|
|
except WebSocketDisconnect:
|
|
manager.disconnect(websocket)
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
return {
|
|
"message": "Airfield PPR API",
|
|
"version": "2.0.0",
|
|
"docs": "/docs"
|
|
}
|
|
|
|
@app.get("/health")
|
|
async def health_check():
|
|
"""Health check endpoint with database connectivity verification"""
|
|
from datetime import datetime
|
|
from sqlalchemy import text
|
|
from app.db.session import SessionLocal
|
|
|
|
health_status = {
|
|
"status": "healthy",
|
|
"timestamp": datetime.utcnow().isoformat() + "Z",
|
|
"version": "2.0.0"
|
|
}
|
|
|
|
# Check database connectivity
|
|
try:
|
|
db = SessionLocal()
|
|
db.execute(text("SELECT 1"))
|
|
db.close()
|
|
health_status["database"] = "connected"
|
|
except Exception as e:
|
|
health_status["status"] = "unhealthy"
|
|
health_status["database"] = "disconnected"
|
|
health_status["error"] = str(e)
|
|
|
|
return health_status
|
|
|
|
# Include API router
|
|
app.include_router(api_router, prefix=settings.api_v1_str)
|
|
|
|
# Make connection manager available to the app
|
|
app.state.connection_manager = manager
|