4286731348
- 添加 HTTP 代理支持(国内直连、外网走代理) - 外部 API 新增全文搜索、源健康度/错误筛选、未读筛选 - 修复 APScheduler 线程静默崩溃(_safe_fetch 异常保护) - 健康检查暴露调度器状态 - Dashboard 新增每日文章数柱状图(按 published_at) - 文章列表 API 补上 content 字段,日期筛选修复时间范围 - 修复外部 API 双重 external 前缀 - User-Agent 改为 Chrome 标识缓解 403 - 添加完整 API 接口文档 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
79 lines
2.3 KiB
Python
79 lines
2.3 KiB
Python
"""仪表盘统计 API"""
|
|
from fastapi import APIRouter, Depends
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import func, text
|
|
from database import get_db
|
|
from health_checker import get_overall_stats, get_feed_health
|
|
|
|
router = APIRouter(prefix="/dashboard", tags=["dashboard"])
|
|
|
|
|
|
@router.get("/stats")
|
|
def dashboard_stats(db: Session = Depends(get_db)):
|
|
"""仪表盘统计数据"""
|
|
return get_overall_stats(db)
|
|
|
|
|
|
@router.get("/health")
|
|
def dashboard_health(
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""RSS 源健康度列表"""
|
|
all_health = get_feed_health(db)
|
|
total = len(all_health)
|
|
|
|
# 按健康状态排序:异常在前
|
|
status_order = {"unhealthy": 0, "warning": 1, "unknown": 2, "healthy": 3}
|
|
all_health.sort(key=lambda x: status_order.get(x["health_status"], 2))
|
|
|
|
items = all_health[skip:skip + limit]
|
|
return {"total": total, "items": items}
|
|
|
|
|
|
@router.get("/articles-daily")
|
|
def articles_daily(days: int = 30, db: Session = Depends(get_db)):
|
|
"""按发布日期统计文章数量"""
|
|
from models import Article
|
|
sql = text("""
|
|
SELECT DATE(published_at) as date, COUNT(*) as count
|
|
FROM articles
|
|
WHERE published_at IS NOT NULL
|
|
AND published_at >= DATE('now', '-' || :days || ' days')
|
|
GROUP BY DATE(published_at)
|
|
ORDER BY date DESC
|
|
""")
|
|
rows = db.execute(sql, {"days": days}).fetchall()
|
|
return {
|
|
"days": days,
|
|
"data": [{"date": str(r[0]), "count": r[1]} for r in rows],
|
|
}
|
|
|
|
|
|
@router.get("/recent-activity")
|
|
def recent_activity(limit: int = 20, db: Session = Depends(get_db)):
|
|
"""最近的抓取活动"""
|
|
from models import FetchLog, Feed
|
|
from sqlalchemy import desc
|
|
|
|
logs = db.query(FetchLog, Feed.title.label("feed_title")).join(Feed).order_by(
|
|
desc(FetchLog.created_at)
|
|
).limit(limit).all()
|
|
|
|
return {
|
|
"items": [
|
|
{
|
|
"id": log.id,
|
|
"feed_id": log.feed_id,
|
|
"feed_title": feed_title or "",
|
|
"status": log.status,
|
|
"articles_fetched": log.articles_fetched,
|
|
"response_time_ms": log.response_time_ms,
|
|
"error_message": log.error_message,
|
|
"created_at": log.created_at.isoformat(),
|
|
}
|
|
for log, feed_title in logs
|
|
]
|
|
}
|