""" 每日報導 APP - FastAPI 主應用程式 """ from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware from app.core.config import settings, validate_secrets from app.core.logging_config import logger from app.api.v1.router import api_router @asynccontextmanager async def lifespan(app: FastAPI): """應用程式生命週期管理""" # 啟動時執行 # 驗證生產環境的密鑰設定 try: validate_secrets() except ValueError as e: logger.error(f"設定驗證失敗: {e}") raise logger.info(f"🚀 {settings.app_name} 啟動中...") logger.info(f"📊 環境: {settings.app_env}") # 不輸出完整的資料庫連線資訊,避免洩露敏感資訊 logger.info(f"🔗 資料庫連線: {settings.db_host}:{settings.db_port}/{settings.db_name[:3]}***") yield # 關閉時執行 logger.info(f"👋 {settings.app_name} 關閉中...") def create_app() -> FastAPI: """建立 FastAPI 應用程式""" # 生產環境強制關閉 Debug if settings.app_env == "production" and settings.debug: import warnings warnings.warn("生產環境不應啟用 Debug 模式,已自動關閉") settings.debug = False app = FastAPI( title=settings.app_name, description="企業內部新聞彙整與分析系統 API", version="1.0.0", docs_url="/docs" if settings.debug else None, # 生產環境關閉 redoc_url="/redoc" if settings.debug else None, # 生產環境關閉 lifespan=lifespan ) # CORS 設定 # 生產環境必須明確指定來源 if settings.app_env == "production": if "*" in settings.cors_origins: raise ValueError("生產環境不允許使用 CORS origins = ['*']") app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins if not settings.debug else ["*"], allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], allow_headers=["Content-Type", "Authorization"], max_age=3600, ) # 註冊路由 app.include_router(api_router, prefix="/api/v1") # 掛載靜態檔案目錄 static_dir = Path(__file__).parent.parent / "templates" / "js" if static_dir.exists(): app.mount("/static/js", StaticFiles(directory=str(static_dir)), name="static_js") # 根路徑 - UI 介面 @app.get("/", response_class=HTMLResponse) async def root(): """返回 UI 介面""" ui_file = Path(__file__).parent.parent / "templates" / "index.html" if ui_file.exists(): return ui_file.read_text(encoding="utf-8") # 如果沒有 UI 文件,返回 JSON 資訊 return HTMLResponse(content=f""" {settings.app_name}

{settings.app_name}

版本: 1.0.0

企業內部新聞彙整與分析系統

""") # API 資訊端點 @app.get("/api/info") async def api_info(): """API 資訊""" return { "app": settings.app_name, "version": "1.0.0", "description": "企業內部新聞彙整與分析系統", "docs": "/docs" if settings.debug else None, "redoc": "/redoc" if settings.debug else None, "health": "/health", "api": "/api/v1" } # 健康檢查端點 @app.get("/health") async def health_check(): return {"status": "healthy", "app": settings.app_name} return app app = create_app() if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host="127.0.0.1", port=8000, reload=settings.debug )