import logging import json from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from contextlib import asynccontextmanager from app.routers import auth, meetings, analyses from app.config import settings from app.services.websocket_service import manager class JSONFormatter(logging.Formatter): def format(self, record): log_record = { "level": record.levelname, "message": record.getMessage(), "logger": record.name, "time": self.formatTime(record), } if record.exc_info: log_record["exception"] = self.formatException(record.exc_info) return json.dumps(log_record, ensure_ascii=False) handler = logging.StreamHandler() handler.setFormatter(JSONFormatter()) logging.basicConfig(handlers=[handler], level=logging.INFO, force=True) logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): logger.info(json.dumps({"event": "startup", "message": "Meeting Assistant API starting"})) yield logger.info(json.dumps({"event": "shutdown", "message": "Meeting Assistant API stopping"})) app = FastAPI( title="Meeting Assistant API", description="Orchestration server for Meeting Assistant - middleware between Mobile App and AI services", version="1.0.0", lifespan=lifespan, docs_url="/docs", redoc_url="/redoc", ) origins = settings.ALLOWED_ORIGINS.split(",") if settings.ALLOWED_ORIGINS != "*" else ["*"] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(auth.router, prefix="/api/auth", tags=["auth"]) app.include_router(meetings.router, prefix="/api/meetings", tags=["meetings"]) app.include_router(analyses.router, prefix="/api", tags=["analyses"]) @app.get("/health", tags=["health"]) async def health_check(): return { "status": "healthy", "service": "meeting-assistant-api", "version": "1.0.0", } @app.websocket("/ws/meetings/{meeting_id}") async def websocket_endpoint(websocket: WebSocket, meeting_id: str): await manager.connect(websocket, meeting_id) try: while True: data = await websocket.receive_text() await manager.send_message(meeting_id, {"type": "ping", "data": data}) except WebSocketDisconnect: manager.disconnect(websocket, meeting_id) @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): logger.error(json.dumps({ "event": "unhandled_exception", "error": str(exc), "path": str(request.url), "method": request.method, })) return JSONResponse( status_code=500, content={"detail": "Internal server error"}, )