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>
This commit is contained in:
136
app/main.py
Normal file
136
app/main.py
Normal file
@@ -0,0 +1,136 @@
|
||||
"""
|
||||
每日報導 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
|
||||
)
|
||||
Reference in New Issue
Block a user