Files
daily-news-app/app/main.py
donald db0f0bbfe7 Initial commit: Daily News App
企業內部新聞彙整與分析系統
- 自動新聞抓取 (Digitimes, 經濟日報, 工商時報)
- AI 智慧摘要 (OpenAI/Claude/Ollama)
- 群組管理與訂閱通知
- 已清理 Python 快取檔案

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 23:53:24 +08:00

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
)