76 lines
2.1 KiB
Python
76 lines
2.1 KiB
Python
|
|
"""rssKeeper - FastAPI 入口"""
|
||
|
|
import os
|
||
|
|
from contextlib import asynccontextmanager
|
||
|
|
from fastapi import FastAPI
|
||
|
|
from fastapi.staticfiles import StaticFiles
|
||
|
|
from fastapi.responses import FileResponse
|
||
|
|
from starlette.middleware.cors import CORSMiddleware
|
||
|
|
from database import init_db, SessionLocal
|
||
|
|
from scheduler import init_feed_jobs, stop_scheduler
|
||
|
|
from routers import feeds, articles, dashboard, external_api
|
||
|
|
import config
|
||
|
|
|
||
|
|
|
||
|
|
@asynccontextmanager
|
||
|
|
async def lifespan(app: FastAPI):
|
||
|
|
"""应用生命周期管理"""
|
||
|
|
# 启动时:初始化数据库 + 注册定时任务
|
||
|
|
init_db()
|
||
|
|
db = SessionLocal()
|
||
|
|
try:
|
||
|
|
init_feed_jobs(db)
|
||
|
|
finally:
|
||
|
|
db.close()
|
||
|
|
|
||
|
|
yield
|
||
|
|
|
||
|
|
# 关闭时:停止调度器
|
||
|
|
stop_scheduler()
|
||
|
|
|
||
|
|
|
||
|
|
app = FastAPI(
|
||
|
|
title="rssKeeper",
|
||
|
|
description="RSS 抓取、管理与检索系统",
|
||
|
|
version="1.0.0",
|
||
|
|
lifespan=lifespan,
|
||
|
|
)
|
||
|
|
|
||
|
|
# CORS
|
||
|
|
app.add_middleware(
|
||
|
|
CORSMiddleware,
|
||
|
|
allow_origins=["*"],
|
||
|
|
allow_credentials=True,
|
||
|
|
allow_methods=["*"],
|
||
|
|
allow_headers=["*"],
|
||
|
|
)
|
||
|
|
|
||
|
|
# API 路由
|
||
|
|
app.include_router(feeds.router, prefix=config.API_PREFIX)
|
||
|
|
app.include_router(articles.router, prefix=config.API_PREFIX)
|
||
|
|
app.include_router(dashboard.router, prefix=config.API_PREFIX)
|
||
|
|
app.include_router(external_api.router, prefix=config.EXTERNAL_API_PREFIX)
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/api/health")
|
||
|
|
def health_check():
|
||
|
|
"""健康检查"""
|
||
|
|
return {"status": "ok", "service": "rssKeeper"}
|
||
|
|
|
||
|
|
|
||
|
|
# 静态文件服务(前端构建产物)
|
||
|
|
static_dir = os.path.join(config.BASE_DIR, "static")
|
||
|
|
if os.path.exists(static_dir):
|
||
|
|
app.mount("/static", StaticFiles(directory=static_dir), name="static")
|
||
|
|
|
||
|
|
@app.get("/{full_path:path}")
|
||
|
|
async def serve_spa(full_path: str):
|
||
|
|
"""Vue SPA 路由回退"""
|
||
|
|
# API 路由不走这里
|
||
|
|
if full_path.startswith("api/") or full_path.startswith("docs") or full_path.startswith("openapi.json"):
|
||
|
|
return {"detail": "Not found"}
|
||
|
|
|
||
|
|
index_path = os.path.join(static_dir, "index.html")
|
||
|
|
if os.path.exists(index_path):
|
||
|
|
return FileResponse(index_path)
|
||
|
|
return {"detail": "Frontend not built"}
|