Files
InsightRadar/backend/app/main.py
T
2026-04-20 15:53:02 +08:00

120 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI辅助生成:deepseek-v3-22026年3月20日
import logging
import os
from pathlib import Path
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
import httpx
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, Request, staticfiles
from fastapi.middleware.cors import CORSMiddleware
from dotenv import load_dotenv
# 统一配置日志格式和级别,确保 delivery_service 等的 INFO 日志可见
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
# 降低 APScheduler 运行心跳日志,避免每分钟刷屏
logging.getLogger("apscheduler").setLevel(logging.WARNING)
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from app.services.fetcher_service import fetch_and_save_trending_data
from app.services.summary_service import generate_unified_summaries
from app.services.delivery_service import check_and_deliver
from app.database import engine
from app.models.models import Base
from app.initialize import init
# 路由总线
from app.api.router import api_router
CRAWL_INTERVAL = int(os.getenv("CRAWL_INTERVAL_MINUTES", 10))
SUMMARY_INTERVAL = int(os.getenv("SUMMARY_INTERVAL_MINUTES", 30))
scheduler = AsyncIOScheduler()
@asynccontextmanager
async def lifespan(app: FastAPI):
# 1. 数据库建表
logging.info("正在初始化数据库表...")
Base.metadata.create_all(bind=engine)
logging.info("数据库表初始化完成!")
logging.info("初始化订阅源")
init()
logging.info("订阅源初始化完毕")
# 爬取订阅源
scheduler.add_job(
fetch_and_save_trending_data,
'interval',
minutes=CRAWL_INTERVAL,
id='trending_fetch_job',
replace_existing=True
)
# 平台摘要
scheduler.add_job(
generate_unified_summaries,
'interval',
minutes=SUMMARY_INTERVAL,
id='ai_summary_job',
replace_existing=True
)
# 推送调度
scheduler.add_job(
check_and_deliver,
'interval',
minutes=1,
id='delivery_check_job',
replace_existing=True,
)
scheduler.start()
logging.info(f"定时抓取任务已启动,每 {CRAWL_INTERVAL} 分钟执行一次")
logging.info(f"AI 摘要生成任务已启动,每 {SUMMARY_INTERVAL} 分钟执行一次")
logging.info("邮件推送调度已启动,每分钟检查一次")
yield
scheduler.shutdown()
logging.info("定时任务已安全关闭")
app = FastAPI(title="AI 新闻聚合引擎 API", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
# allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"],
allow_origins=["*"],
# allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(api_router, prefix="/api/v1")
# AI辅助生成结束
@app.get("/api/{full_path:path}")
async def api_not_found(full_path: str):
return {"detail": "API Not Found"}
staticPath = staticfiles.StaticFiles(directory="app/static", html=True)
app.mount("/", staticPath, name="static")
INDEX_HTML = Path("app/static/index.html").read_text(encoding="utf-8")
@app.exception_handler(404)
async def not_found_handler(request: Request, exc: HTTPException):
if request.url.path.startswith("/api/"):
return JSONResponse({"detail": "Not Found"}, status_code=404)
return HTMLResponse(INDEX_HTML)
@app.get("/", tags=["健康检查"])
async def root():
return {"message": "Welcome to AI News Aggregator API", "status": "ok"}