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>
751 lines
15 KiB
Markdown
751 lines
15 KiB
Markdown
# RSSKeeper API 接口文档
|
||
|
||
Base URL: `http://<host>:7329`
|
||
|
||
所有接口返回 JSON 格式数据。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
- [健康检查](#健康检查)
|
||
- [RSS 源管理](#rss-源管理)
|
||
- [获取源列表](#获取源列表)
|
||
- [获取源详情](#获取源详情)
|
||
- [添加源](#添加源)
|
||
- [更新源](#更新源)
|
||
- [删除源](#删除源)
|
||
- [触发抓取](#触发抓取)
|
||
- [批量抓取](#批量抓取)
|
||
- [自动发现](#自动发现)
|
||
- [获取分类列表](#获取分类列表)
|
||
- [导入 OPML](#导入-opml)
|
||
- [导出 OPML](#导出-opml)
|
||
- [文章管理](#文章管理)
|
||
- [获取文章列表](#获取文章列表)
|
||
- [获取文章详情](#获取文章详情)
|
||
- [全文搜索](#全文搜索)
|
||
- [标记已读](#标记已读)
|
||
- [仪表盘](#仪表盘)
|
||
- [统计概览](#统计概览)
|
||
- [健康度详情](#健康度详情)
|
||
- [最近活动](#最近活动)
|
||
- [外部 API(供 AI 集成)](#外部-api)
|
||
- [获取最近文章](#获取最近文章)
|
||
- [全文搜索](#全文搜索external)
|
||
- [获取源列表(含筛选)](#获取源列表含筛选)
|
||
- [获取指定源文章](#获取指定源文章)
|
||
- [获取每日摘要](#获取每日摘要)
|
||
|
||
---
|
||
|
||
## 通用字段说明
|
||
|
||
### 健康度 (health_status)
|
||
|
||
| 值 | 含义 |
|
||
|---|------|
|
||
| `healthy` | 健康:成功率 >= 90%,7天内有抓取 |
|
||
| `warning` | 警告:成功率 50%-90%,或超过3天未更新 |
|
||
| `unhealthy` | 异常:成功率 < 50%,或超过7天未更新 |
|
||
| `unknown` | 未知:尚未进行过任何抓取 |
|
||
|
||
### 错误类型 (error_type)
|
||
|
||
| 值 | 含义 |
|
||
|---|------|
|
||
| `url_invalid` | URL 已失效(404) |
|
||
| `forbidden` | 被站点拒绝(403) |
|
||
| `rate_limited` | 频率限制(429) |
|
||
| `timeout` | 连接超时 |
|
||
| `dns_failure` | DNS 解析失败 |
|
||
| `connection_refused` | 连接被拒绝 |
|
||
| `connection_reset` | 连接中断 |
|
||
| `ssl_error` | SSL/TLS 错误 |
|
||
| `unreachable` | 服务器不可达 |
|
||
| `url_malformed` | URL 格式错误 |
|
||
| `server_error` | 服务器错误(5xx) |
|
||
| `unknown` | 其他未知错误 |
|
||
|
||
---
|
||
|
||
## 健康检查
|
||
|
||
### `GET /api/health`
|
||
|
||
检查服务是否运行。
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"service": "rssKeeper"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## RSS 源管理
|
||
|
||
### 获取源列表
|
||
|
||
### `GET /api/feeds`
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `skip` | int | 否 | 0 | 跳过条数(分页偏移) |
|
||
| `limit` | int | 否 | 100 | 每页条数 |
|
||
| `category` | string | 否 | - | 按分类筛选 |
|
||
| `search` | string | 否 | - | 按名称/URL/描述搜索 |
|
||
| `is_active` | bool | 否 | - | 按启用状态筛选 |
|
||
| `health_status` | string | 否 | - | 按健康度筛选:`healthy`/`warning`/`unhealthy`/`unknown` |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"total": 383,
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"url": "https://example.com/feed.xml",
|
||
"title": "Example Feed",
|
||
"description": "Feed description",
|
||
"category": "科技",
|
||
"is_active": true,
|
||
"fetch_interval_minutes": 60,
|
||
"last_fetch_at": "2026-06-11T08:33:36.474905",
|
||
"last_fetch_status": "success",
|
||
"last_error": "",
|
||
"error_type": "",
|
||
"success_count": 5,
|
||
"fail_count": 0,
|
||
"article_count": 42,
|
||
"health_status": "healthy",
|
||
"next_fetch_time": "2026-06-11T09:33:36.000000+00:00",
|
||
"created_at": "2026-06-11T08:33:24.591074"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 获取源详情
|
||
|
||
### `GET /api/feeds/{feed_id}`
|
||
|
||
**路径参数:**
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `feed_id` | int | RSS 源 ID |
|
||
|
||
**响应:** 同列表中的单条 items 结构。
|
||
|
||
---
|
||
|
||
### 添加源
|
||
|
||
### `POST /api/feeds`
|
||
|
||
**请求体:**
|
||
|
||
```json
|
||
{
|
||
"url": "https://example.com/feed.xml",
|
||
"title": "Example",
|
||
"description": "",
|
||
"category": "科技",
|
||
"is_active": true,
|
||
"fetch_interval_minutes": 60
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `url` | string | **是** | - | RSS 源地址 |
|
||
| `title` | string | 否 | "" | 源名称(留空则自动抓取) |
|
||
| `description` | string | 否 | "" | 描述 |
|
||
| `category` | string | 否 | "" | 分类 |
|
||
| `is_active` | bool | 否 | true | 是否启用 |
|
||
| `fetch_interval_minutes` | int | 否 | 60 | 抓取间隔(分钟),最小 15 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"message": "RSS 源添加成功,正在后台抓取",
|
||
"url": "https://example.com/feed.xml"
|
||
}
|
||
```
|
||
|
||
添加成功后会自动在后台触发首次抓取。
|
||
|
||
**错误:** `409` — 该 RSS 源已存在
|
||
|
||
---
|
||
|
||
### 更新源
|
||
|
||
### `PUT /api/feeds/{feed_id}`
|
||
|
||
**请求体(只需传要修改的字段):**
|
||
|
||
```json
|
||
{
|
||
"title": "新名称",
|
||
"category": "新闻",
|
||
"is_active": false,
|
||
"fetch_interval_minutes": 120
|
||
}
|
||
```
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"message": "RSS 源更新成功"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 删除源
|
||
|
||
### `DELETE /api/feeds/{feed_id}`
|
||
|
||
删除 RSS 源,**级联删除**关联的所有文章和抓取日志。
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"message": "RSS 源已删除"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 触发抓取
|
||
|
||
### `POST /api/feeds/{feed_id}/fetch`
|
||
|
||
手动触发单个源的抓取。同步执行,返回抓取结果。
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"articles_count": 5,
|
||
"feed_title": "Example Feed"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 批量抓取
|
||
|
||
### `POST /api/feeds/batch-fetch`
|
||
|
||
并发同步抓取多个源。适用于"全部抓取"等场景。
|
||
|
||
**请求体:**
|
||
|
||
```json
|
||
{
|
||
"feed_ids": [1, 2, 3, 4, 5]
|
||
}
|
||
```
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"message": "完成:4 个成功,1 个失败",
|
||
"total": 5,
|
||
"success": 4,
|
||
"fail": 1
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 自动发现
|
||
|
||
### `POST /api/feeds/discover`
|
||
|
||
从任意网页自动发现 RSS/Atom feed URL。
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `url` | string | **是** | 网页地址 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"source_url": "https://example.com",
|
||
"found_feeds": [
|
||
"https://example.com/feed.xml",
|
||
"https://example.com/rss"
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 获取分类列表
|
||
|
||
### `GET /api/feeds/categories`
|
||
|
||
返回所有已使用的分类。
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
["科技", "新闻", "设计"]
|
||
```
|
||
|
||
---
|
||
|
||
### 导入 OPML
|
||
|
||
### `POST /api/feeds/import-opml`
|
||
|
||
从 OPML 内容批量导入 RSS 源。
|
||
|
||
**请求体:**
|
||
|
||
```json
|
||
{
|
||
"opml_content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><opml version=\"2.0\">..."
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `opml_content` | string | **是** | OPML 文件内容(最大 5MB) |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"added": 15,
|
||
"skipped": 3,
|
||
"message": "成功导入 15 个 RSS 源"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 导出 OPML
|
||
|
||
### `GET /api/feeds/export-opml`
|
||
|
||
导出所有 RSS 源为 OPML 格式。
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"opml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><opml version=\"2.0\">..."
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 文章管理
|
||
|
||
### 获取文章列表
|
||
|
||
### `GET /api/articles`
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `skip` | int | 否 | 0 | 分页偏移 |
|
||
| `limit` | int | 否 | 50 | 每页条数 |
|
||
| `feed_id` | int | 否 | - | 按源筛选 |
|
||
| `category` | string | 否 | - | 按分类筛选 |
|
||
| `search` | string | 否 | - | 按标题/链接搜索 |
|
||
| `since` | string | 否 | - | 起始时间(ISO 格式) |
|
||
| `until` | string | 否 | - | 截止时间(ISO 格式) |
|
||
| `is_read` | bool | 否 | - | 按已读状态筛选 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"total": 120,
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"feed_id": 1,
|
||
"title": "文章标题",
|
||
"link": "https://example.com/article",
|
||
"author": "作者",
|
||
"published_at": "2026-06-11T06:00:00",
|
||
"content": "文章正文内容...",
|
||
"summary": "文章摘要...",
|
||
"is_read": false,
|
||
"created_at": "2026-06-11T08:33:36.474905",
|
||
"feed_title": "Example Feed",
|
||
"category": "科技"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 获取文章详情
|
||
|
||
### `GET /api/articles/{article_id}`
|
||
|
||
**响应:** 同列表中的单条 items 结构。
|
||
|
||
---
|
||
|
||
### 全文搜索
|
||
|
||
### `GET /api/articles/search/fulltext`
|
||
|
||
使用 SQLite FTS5 进行全文搜索。
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `q` | string | **是** | - | 搜索关键词 |
|
||
| `skip` | int | 否 | 0 | 分页偏移 |
|
||
| `limit` | int | 否 | 50 | 每页条数 |
|
||
|
||
**响应:** 同文章列表格式。
|
||
|
||
---
|
||
|
||
### 标记已读
|
||
|
||
### `PUT /api/articles/{article_id}/read`
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"message": "已标记为已读"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 仪表盘
|
||
|
||
### 统计概览
|
||
|
||
### `GET /api/dashboard/stats`
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"total_feeds": 383,
|
||
"active_feeds": 383,
|
||
"total_articles": 1024,
|
||
"healthy_feeds": 202,
|
||
"warning_feeds": 0,
|
||
"unhealthy_feeds": 167,
|
||
"today_fetches": 45,
|
||
"today_success": 40,
|
||
"today_success_rate": 88.9
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 健康度详情
|
||
|
||
### `GET /api/dashboard/health`
|
||
|
||
获取每个 RSS 源的健康状态详情。
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `skip` | int | 否 | 0 | 分页偏移 |
|
||
| `limit` | int | 否 | 100 | 每页条数 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"total": 383,
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"title": "Example Feed",
|
||
"url": "https://example.com/feed.xml",
|
||
"is_active": true,
|
||
"health_status": "healthy",
|
||
"health_label": "健康",
|
||
"success_rate": 100.0,
|
||
"success_count": 5,
|
||
"fail_count": 0,
|
||
"total_fetches": 5,
|
||
"last_fetch_at": "2026-06-11T08:33:36.474905",
|
||
"days_since_fetch": 0,
|
||
"article_count": 42,
|
||
"last_error": "",
|
||
"recent_logs": [
|
||
{
|
||
"status": "success",
|
||
"articles_fetched": 3,
|
||
"response_time_ms": 450,
|
||
"created_at": "2026-06-11T08:33:36.474905",
|
||
"error_message": null
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 最近活动
|
||
|
||
### `GET /api/dashboard/recent-activity`
|
||
|
||
获取最近的抓取活动日志。
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `limit` | int | 否 | 20 | 返回条数 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"feed_id": 1,
|
||
"feed_title": "Example Feed",
|
||
"status": "success",
|
||
"articles_fetched": 3,
|
||
"error_message": "",
|
||
"response_time_ms": 450,
|
||
"created_at": "2026-06-11T08:33:36.474905"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 外部 API
|
||
|
||
供 AI 助手、外部系统调用的接口。前缀:`/api/v1/external`。
|
||
|
||
### 获取最近文章
|
||
|
||
### `GET /api/v1/external/recent`
|
||
|
||
获取最近 N 小时的文章,支持多条件组合筛选。
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `hours` | int | 否 | 24 | 回溯小时数 |
|
||
| `limit` | int | 否 | 50 | 最大返回条数 |
|
||
| `feed_id` | int | 否 | - | 按源 ID 筛选 |
|
||
| `category` | string | 否 | - | 按分类筛选 |
|
||
| `search` | string | 否 | - | 按标题/摘要关键词筛选 |
|
||
| `unread_only` | bool | 否 | false | 只返回未读文章 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"query": { "hours": 24, "limit": 50, "feed_id": null, "category": null, "search": null, "unread_only": false },
|
||
"count": 15,
|
||
"articles": [
|
||
{
|
||
"id": 1,
|
||
"title": "文章标题",
|
||
"link": "https://example.com/article",
|
||
"author": "作者",
|
||
"summary": "摘要文本",
|
||
"content": "正文内容(超过10000字符时返回摘要)",
|
||
"published_at": "2026-06-11T06:00:00",
|
||
"created_at": "2026-06-11T08:33:36",
|
||
"feed_title": "Example Feed",
|
||
"category": "科技"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 全文搜索
|
||
|
||
### `GET /api/v1/external/search`
|
||
|
||
使用 FTS5 全文搜索引擎检索文章内容。**供 AI 按关键词精准查找文章**。
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `q` | string | **是** | - | 搜索关键词 |
|
||
| `limit` | int | 否 | 50 | 最大返回条数(1-200) |
|
||
| `offset` | int | 否 | 0 | 分页偏移 |
|
||
| `category` | string | 否 | - | 按分类二次筛选 |
|
||
| `feed_id` | int | 否 | - | 按源 ID 二次筛选 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"query": "LLM",
|
||
"total": 12,
|
||
"offset": 0,
|
||
"limit": 50,
|
||
"articles": [
|
||
{
|
||
"id": 15674,
|
||
"title": "文章标题",
|
||
"summary": "匹配的摘要...",
|
||
"link": "https://example.com/article",
|
||
"published_at": "2026-06-11T06:00:00",
|
||
"created_at": "2026-06-11T08:33:36",
|
||
"feed_id": 1,
|
||
"feed_title": "Example Feed",
|
||
"category": "科技"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 获取源列表(含筛选)
|
||
|
||
### `GET /api/v1/external/feeds`
|
||
|
||
获取 RSS 源列表,支持按健康度、错误类型、分类等多维度筛选。
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `health_status` | string | 否 | - | 按健康度筛选:`healthy`/`warning`/`unhealthy`/`unknown` |
|
||
| `category` | string | 否 | - | 按分类筛选 |
|
||
| `error_type` | string | 否 | - | 按错误类型筛选(见通用字段说明) |
|
||
| `is_active` | bool | 否 | true | 按启用状态筛选 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"count": 167,
|
||
"feeds": [
|
||
{
|
||
"id": 1,
|
||
"title": "Example Feed",
|
||
"url": "https://example.com/feed.xml",
|
||
"category": "科技",
|
||
"is_active": true,
|
||
"health_status": "unhealthy",
|
||
"error_type": "timeout",
|
||
"article_count": 42,
|
||
"last_fetch_at": "2026-06-11T08:33:36",
|
||
"last_error": "HTTPSConnectionPool..."
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**示例:**
|
||
- 查看所有异常源:`/api/v1/external/feeds?health_status=unhealthy`
|
||
- 查看 URL 失效的源:`/api/v1/external/feeds?error_type=url_invalid`
|
||
- 查看指定分类:`/api/v1/external/feeds?category=科技`
|
||
|
||
---
|
||
|
||
### 获取指定源文章
|
||
|
||
### `GET /api/v1/external/feeds/{feed_id}/articles`
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `feed_id` | int | **是** | - | RSS 源 ID(路径参数) |
|
||
| `limit` | int | 否 | 100 | 最大返回条数 |
|
||
| `since` | string | 否 | - | 起始时间过滤 |
|
||
| `search` | string | 否 | - | 按标题/摘要关键词筛选 |
|
||
| `unread_only` | bool | 否 | false | 只返回未读文章 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"feed": { "id": 1, "title": "Example Feed", "url": "https://example.com/feed.xml" },
|
||
"count": 42,
|
||
"articles": [
|
||
{
|
||
"id": 1,
|
||
"title": "文章标题",
|
||
"link": "https://example.com/article",
|
||
"author": "作者",
|
||
"summary": "摘要",
|
||
"published_at": "2026-06-11T06:00:00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 获取每日摘要
|
||
|
||
### `GET /api/v1/external/summary`
|
||
|
||
获取指定日期的文章摘要,按分类分组。
|
||
|
||
**参数:**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| `date` | string | 否 | 今天 | 日期,格式 `YYYY-MM-DD` |
|
||
| `category` | string | 否 | - | 按分类筛选 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"date": "2026-06-11",
|
||
"total_articles": 35,
|
||
"by_category": {
|
||
"科技": [
|
||
{ "title": "文章标题", "link": "https://...", "feed": "Feed 名称", "summary": "文章摘要..." }
|
||
],
|
||
"新闻": [...]
|
||
}
|
||
}
|
||
```
|