import logging from contextlib import asynccontextmanager from config.setting import SUMMARY_HTML_DIR from fastapi import Depends, FastAPI, HTTPException, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from prometheus_fastapi_instrumentator import Instrumentator from routers import * from services.api_key_service import load_api_keys_from_file from utils.checking_keys import get_admin_key, get_api_key from utils.minio_utils import get_minio_client from utils.redis_utils import get_redis_client logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s - %(message)s" ) @asynccontextmanager async def lifespan(app: FastAPI): # 애플리케이션 시작 시 파일에서 API 키 로드 print("Loading API keys from file...") load_api_keys_from_file() yield app = FastAPI( title="LLM GATEWAY", description="LLM 모델이 업로드된 문서를 분석하여 구조화된 JSON으로 변환하는 API 서비스입니다.", docs_url=None, lifespan=lifespan, ) app.add_middleware( CORSMiddleware, allow_origins=[ "http://172.16.42.101", "http://gsim.hanmaceng.co.kr", "http://gsim.hanmaceng.co.kr:6464", "https://overseas.projectmastercloud.com", "http://localhost:5174", # 이민규 연구원 ], allow_origin_regex=r"http://(localhost:5174|172\.16\.\d{1,3}\.\d{1,3}|gsim\.hanmaceng\.co\.kr)(:\d+)?", allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # API 키 검증을 위한 의존성 설정 api_key_dependency = Depends(get_api_key) admin_key_dependency = Depends(get_admin_key) # ✅ Prometheus Metrics Exporter 활성화 instrumentator = Instrumentator() # 커스텀 라벨 콜백 함수 def custom_labels(info): # info.request 는 Starlette의 Request 객체 return {"job_id": info.request.headers.get("X-Job-ID", "unknown")} instrumentator = Instrumentator() instrumentator.add(custom_labels) instrumentator.instrument(app).expose(app) # ✅ 생성된 요약 HTML 결과 파일 서빙 경로 등록 app.mount( "/view/generated_html", StaticFiles(directory=SUMMARY_HTML_DIR), name="summary_html" ) app.mount( "/static", StaticFiles(directory="/workspace/workspace/static"), name="static" ) # 🔑 가이드북, 상태 확인, 문서는 API 키 없이 접근 가능 app.include_router(guide_router) # ✅ 가이드북 HTML 서빙 # ⭐️ 관리자 전용: API 키 관리 라우터 (마스터 키 필요, 문서에서 숨김) app.include_router( api_key_router, dependencies=[admin_key_dependency], include_in_schema=False, ) # 🔑 아래의 모든 라우터는 일반 API 키 검증이 필요 app.include_router(model_router, dependencies=[api_key_dependency]) # ✅ 모델 관리 API app.include_router( download_router, dependencies=[api_key_dependency] ) # ✅ 다운로드 API app.include_router( general_router, dependencies=[api_key_dependency] ) # ✅ 일반 추론 API app.include_router( extract_router, dependencies=[api_key_dependency] ) # ✅ 문서 추출 API app.include_router(dummy_router, dependencies=[api_key_dependency]) # ✅ 더미 API app.include_router(ocr_router, dependencies=[api_key_dependency]) # ✅ OCR API app.include_router( stt_router, dependencies=[api_key_dependency], include_in_schema=False ) # STT로 호출하는 기능 임시 제외 - 2025.07.29 app.include_router(llm_summation, dependencies=[api_key_dependency]) app.include_router(yolo_router, dependencies=[api_key_dependency]) app.include_router(stt_router, dependencies=[api_key_dependency]) # ✅ # /docs URL에 커스터마이징된 Swagger UI 연결 app.mount( "/docs", StaticFiles(directory="/workspace/swagger-ui", html=True), name="docs" ) @app.get("/health/API") async def health_check(): """애플리케이션 상태 확인""" return {"status": "API ok"} @app.get("/health/Redis") def redis_health_check(): client = get_redis_client() if client is None: raise HTTPException(status_code=500, detail="Redis connection failed") try: client.ping() return {"status": "Redis ok"} except Exception: raise HTTPException(status_code=500, detail="Redis ping failed") @app.get("/health/MinIO") def minio_health_check(): try: client = get_minio_client() return {"status": "MinIO ok"} except Exception as e: raise HTTPException( status_code=500, detail=f"MinIO health check failed: {str(e)}" )