129 lines
3.9 KiB
Python
129 lines
3.9 KiB
Python
from fastapi import FastAPI, Request, HTTPException
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
|
from fastapi.responses import JSONResponse
|
|
from utils.database import create_db_and_tables
|
|
from utils.containers import Container
|
|
from utils.middlewares import RateLimitMiddleware, LoggingMiddleware, SecurityHeadersMiddleware
|
|
from utils.exceptions import BaseAPIException
|
|
from contextlib import asynccontextmanager
|
|
|
|
from analysis.interface.controller.analysis_controller import router as analysis_router
|
|
from member.interface.controller.member_controller import router as member_router
|
|
import logging
|
|
from utils.logger import setup_logging
|
|
from config.config import get_settings
|
|
|
|
setup_logging()
|
|
settings = get_settings()
|
|
|
|
# 라이프사이클 관리
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
logger = logging.getLogger(__name__)
|
|
# 시작 시
|
|
logger.info("🚀 FastAPI 애플리케이션 시작")
|
|
create_db_and_tables()
|
|
logger.info("📊 데이터베이스 초기화 완료")
|
|
yield
|
|
# 종료 시
|
|
logger.info("🔄 애플리케이션 종료")
|
|
|
|
app = FastAPI(
|
|
title=settings.PROJECT_NAME,
|
|
version="1.0.0",
|
|
debug=settings.DEBUG,
|
|
lifespan=lifespan
|
|
)
|
|
|
|
# 컨테이너 설정
|
|
app.container = Container()
|
|
|
|
# 미들웨어 설정
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=settings.allowed_origins_list,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# 신뢰할 수 있는 호스트 설정
|
|
if settings.is_production:
|
|
app.add_middleware(TrustedHostMiddleware, allowed_hosts=["tradingagents.com", "*.tradingagents.com"])
|
|
|
|
# 커스텀 미들웨어 추가
|
|
app.add_middleware(SecurityHeadersMiddleware)
|
|
app.add_middleware(LoggingMiddleware)
|
|
app.add_middleware(
|
|
RateLimitMiddleware,
|
|
requests_per_minute=settings.RATE_LIMIT_REQUESTS,
|
|
period=settings.RATE_LIMIT_PERIOD
|
|
)
|
|
|
|
# 글로벌 예외 처리기
|
|
@app.exception_handler(BaseAPIException)
|
|
async def api_exception_handler(request: Request, exc: BaseAPIException):
|
|
return JSONResponse(
|
|
status_code=exc.status_code,
|
|
content={
|
|
"error": {
|
|
"code": exc.error_code,
|
|
"message": exc.detail,
|
|
"path": str(request.url)
|
|
}
|
|
}
|
|
)
|
|
|
|
@app.exception_handler(HTTPException)
|
|
async def http_exception_handler(request: Request, exc: HTTPException):
|
|
return JSONResponse(
|
|
status_code=exc.status_code,
|
|
content={
|
|
"error": {
|
|
"code": f"HTTP_{exc.status_code}",
|
|
"message": exc.detail,
|
|
"path": str(request.url)
|
|
}
|
|
}
|
|
)
|
|
|
|
@app.exception_handler(Exception)
|
|
async def global_exception_handler(request: Request, exc: Exception):
|
|
logger = logging.getLogger(__name__)
|
|
logger.error(f"예상치 못한 오류: {str(exc)}", exc_info=True)
|
|
|
|
return JSONResponse(
|
|
status_code=500,
|
|
content={
|
|
"error": {
|
|
"code": "INTERNAL_SERVER_ERROR",
|
|
"message": "Internal server error" if settings.is_production else str(exc),
|
|
"path": str(request.url)
|
|
}
|
|
}
|
|
)
|
|
|
|
# 라우터 등록
|
|
app.include_router(analysis_router, prefix=settings.API_V1_STR)
|
|
app.include_router(member_router, prefix=settings.API_V1_STR)
|
|
|
|
# 헬스 체크 엔드포인트
|
|
@app.get("/health")
|
|
async def health_check():
|
|
return {
|
|
"status": "healthy",
|
|
"environment": settings.ENVIRONMENT,
|
|
"timestamp": "2024-01-01T00:00:00Z"
|
|
}
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
logger = logging.getLogger(__name__)
|
|
logger.info("📍 루트 엔드포인트 호출됨")
|
|
return {
|
|
"message": "Trading Agents API",
|
|
"version": "1.0.0",
|
|
"environment": settings.ENVIRONMENT,
|
|
"docs_url": "/docs"
|
|
} |