from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Request from fastapi.middleware.cors import CORSMiddleware from contextlib import asynccontextmanager import logging import time from app.core.config import settings from app.api import api_router from app.services.websocket_manager import websocket_manager from app.core.database import engine, Base from app.models import models # 모델 임포트 # 로깅 설정 logging.basicConfig( level=getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO), format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """애플리케이션 시작/종료 시 실행될 코드""" logger.info("Starting Auto Stock Trading System...") # DB 테이블 자동 생성 try: logger.info(f"Connecting to database: {settings.DATABASE_URL.split('@')[1] if '@' in settings.DATABASE_URL else 'unknown'}") async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) logger.info("✅ Database tables created successfully") except ConnectionRefusedError as e: logger.error(f"❌ Database connection refused: {e}") logger.error("Please check if PostgreSQL server is running and accessible") raise except Exception as e: logger.error(f"❌ Failed to create database tables: {e}") import traceback logger.error(traceback.format_exc()) raise yield # 종료 시 정리 작업 logger.info("Shutting down Auto Stock Trading System...") await engine.dispose() app = FastAPI( title=settings.APP_NAME, description="Claude AI와 MCP를 활용한 자동 주식 매매 시스템", version="1.0.0", lifespan=lifespan, debug=settings.DEBUG, ) # CORS 설정 (반드시 라우터 등록 전에) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], expose_headers=["*"], ) logger.info("CORS middleware configured") # 요청 로깅 미들웨어 @app.middleware("http") async def log_requests(request: Request, call_next): start_time = time.time() logger.info(f"🔵 Request: {request.method} {request.url}") response = await call_next(request) process_time = time.time() - start_time logger.info(f"🟢 Response: {request.method} {request.url} - Status: {response.status_code} - Time: {process_time:.3f}s") return response # API 라우터 등록 app.include_router(api_router, prefix="/api/v1") @app.get("/") async def root(): return { "message": "Auto Stock Trading System API", "version": "1.0.0", "status": "running" } @app.get("/health") async def health_check(): return {"status": "healthy"} @app.websocket("/ws/{client_id}") async def websocket_endpoint(websocket: WebSocket, client_id: str): """WebSocket 연결 엔드포인트""" await websocket_manager.connect(websocket, client_id) try: while True: data = await websocket.receive_text() # 메시지 처리 await websocket_manager.send_personal_message( f"Echo: {data}", client_id ) except WebSocketDisconnect: websocket_manager.disconnect(client_id) logger.info(f"Client {client_id} disconnected") if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host=settings.BACKEND_HOST, port=settings.BACKEND_PORT, reload=settings.DEBUG, )