企業內部新聞彙整與分析系統 - 自動新聞抓取 (Digitimes, 經濟日報, 工商時報) - AI 智慧摘要 (OpenAI/Claude/Ollama) - 群組管理與訂閱通知 - 已清理 Python 快取檔案 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
137 lines
4.2 KiB
Python
137 lines
4.2 KiB
Python
"""
|
|
每日報導 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"""
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head><title>{settings.app_name}</title></head>
|
|
<body>
|
|
<h1>{settings.app_name}</h1>
|
|
<p>版本: 1.0.0</p>
|
|
<p>企業內部新聞彙整與分析系統</p>
|
|
<ul>
|
|
<li><a href="/docs">API 文檔 (Swagger)</a></li>
|
|
<li><a href="/redoc">API 文檔 (ReDoc)</a></li>
|
|
<li><a href="/health">健康檢查</a></li>
|
|
<li><a href="/api/v1">API 端點</a></li>
|
|
</ul>
|
|
</body>
|
|
</html>
|
|
""")
|
|
|
|
# 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
|
|
)
|