Files
MediaCrawler/docs/api.md
T
butubb 556680c613
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s
docs: API 接口清单(从 OpenAPI schema 生成)
58 个接口,按 auth/crawler/creator/data/monitor/settings 分组,逐条列出方法、路径、
是否需要登录、说明、参数(含类型/必填/默认)、请求体 schema;附录展开请求体的字段。

**生成的,不是手写的** —— 手写的接口文档从改代码那天起就开始腐烂,而且烂得无声无息。
`docs/gen_api_doc.py` 只读 app 对象,不连库不起服务。

两处值得说的:

* 鉴权**不能从 schema 里读** —— require_auth 是普通依赖不是 security scheme,不会进
  OpenAPI 的 security 字段。所以那份「哪些路径免登录」的名单是照着 include_router
  核的,而且 auth 那一组不能一刀切(/me 和 /password 各自另有保护)。
* 生成完**顺手验一遍表格**:参数类型里的 `integer | null` 到处是,一个没转义的竖线
  就会把整张表切歪,而 markdown 不报错、只是安静地多一格。验过这个检查确实会拦。

接口的响应没有结构可列 —— 它们返回普通 dict,没声明 response_model。
2026-10-12 08:50:53 +08:00

258 lines
14 KiB
Markdown
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.
# API 接口清单
从 FastAPI 自己的 OpenAPI schema 生成的,**不是手写的** ——
手写的接口文档从改代码那天起就开始腐烂,而且烂得无声无息。
重新生成:
```bash
./.venv/Scripts/python.exe docs/gen_api_doc.py
```
## 通用约定
| | |
|---|---|
| 前缀 | 所有接口都在 `/api` 下(WebSocket 是 `/api/ws/...`)|
| 鉴权 | **Cookie 优先,Bearer 次之**:`token_from_request()` 先读会话 Cookie,读不到再读 `Authorization: Bearer <token>`。浏览器用 Cookie,脚本用 Bearer |
| 会话怎么来 | `POST /api/auth/login`(密码),响应种下会话 Cookie;有效期由服务端 `resolve_session()` 判定 |
| 错误形状 | FastAPI 默认的 `{"detail": "..."}`;401 是登录失效 |
| **没有 `/docs`** | 特意关掉的:`/docs`、`/redoc`、`/openapi.json` 默认**不需要登录**,等于把整份接口地图连同「Try it out」一起送出去。要看交互式的,得挂在鉴权后面 |
| WebSocket | `GET /api/ws/logs`(升级为 ws)—— 实时日志流。**不在这里的清单里**:OpenAPI 描述不了 WebSocket。它自己用 `require_ws_auth` 校验会话 Cookie |
## 目录
- [认证](#认证)(4 个)
- [采集](#采集)(4 个)
- [运营(博主账号)](#运营(博主账号))(8 个)
- [数据](#数据)(4 个)
- [监控](#监控)(36 个)
- [设置](#设置)(2 个)
## 认证
这一组**没有**统一挂 `require_auth`(登录不能要求先登录),所以逐个标。
| 方法 | 路径 | 登录 | 说明 | 参数 | 请求体 |
|---|---|:--:|---|---|---|
| `POST` | `/api/auth/login` | — | Login | | LoginPayload |
| `POST` | `/api/auth/logout` | — | Logout | | |
| `GET` | `/api/auth/me` | ✓ | Identity probe. The SPA treats a 401 here as "show the login page". | | |
| `POST` | `/api/auth/password` | ✓ | Change the password and log every device out. | | ChangePasswordPayload |
## 采集
| 方法 | 路径 | 登录 | 说明 | 参数 | 请求体 |
|---|---|:--:|---|---|---|
| `GET` | `/api/crawler/logs` | ✓ | Get recent logs | `limit`(query integer 可选=100) | |
| `POST` | `/api/crawler/start` | ✓ | Start crawler task. | | CrawlerStartRequest |
| `GET` | `/api/crawler/status` | ✓ | Get crawler status | | |
| `POST` | `/api/crawler/stop` | ✓ | Stop crawler task | | |
## 运营(博主账号)
| 方法 | 路径 | 登录 | 说明 | 参数 | 请求体 |
|---|---|:--:|---|---|---|
| `GET` | `/api/creator/accounts` | ✓ | 账号列表。**不含 cookie**,只给 ``has_cookie``。 | | |
| `DELETE` | `/api/creator/accounts/{account_id}` | ✓ | Delete Account | `account_id`(path integer 必填) | |
| `GET` | `/api/creator/accounts/{account_id}` | ✓ | Get Account Detail | `account_id`(path integer 必填) | |
| `POST` | `/api/creator/accounts/{account_id}/check` | ✓ | 重测登录态与数据权限。 | `account_id`(path integer 必填) | |
| `POST` | `/api/creator/accounts/{account_id}/sync` | ✓ | 拉取作品数据。 | `account_id`(path integer 必填)<br>`days`(query integer 可选=90) | |
| `DELETE` | `/api/creator/login` | ✓ | Cancel Login | | |
| `GET` | `/api/creator/login` | ✓ | 轮询扫码结果;一旦成功就把账号落库并返回它。 | | |
| `POST` | `/api/creator/login` | ✓ | Start Login | | |
## 数据
| 方法 | 路径 | 登录 | 说明 | 参数 | 请求体 |
|---|---|:--:|---|---|---|
| `GET` | `/api/data/download/{file_path}` | ✓ | Download file | `file_path`(path string 必填) | |
| `GET` | `/api/data/files` | ✓ | Get data file list | `platform`(query string \| null 可选)<br>`file_type`(query string \| null 可选) | |
| `GET` | `/api/data/files/{file_path}` | ✓ | Get file content or preview | `file_path`(path string 必填)<br>`preview`(query boolean 可选=True)<br>`limit`(query integer 可选=100) | |
| `GET` | `/api/data/stats` | ✓ | Get data statistics | | |
## 监控
| 方法 | 路径 | 登录 | 说明 | 参数 | 请求体 |
|---|---|:--:|---|---|---|
| `GET` | `/api/monitor/comment-notes` | ✓ | Works that have comments, newest first, with counts. | `task_id`(query integer \| null 可选)<br>`platform`(query string \| null 可选) | |
| `GET` | `/api/monitor/comments` | ✓ | Comments, each carrying the work it belongs to. | `task_id`(query integer \| null 可选)<br>`note_id`(query string \| null 可选)<br>`group_by`(query string \| null 可选)<br>`limit`(query integer 可选=200)<br>`platform`(query string \| null 可选) | |
| `DELETE` | `/api/monitor/cookie` | ✓ | Clear Cookie Endpoint | `platform`(query string 可选='xhs') | |
| `GET` | `/api/monitor/cookie` | ✓ | Cookie health only -- deliberately never returns the cookie value. | `platform`(query string 可选='xhs') | |
| `POST` | `/api/monitor/cookie` | ✓ | Set Cookie Endpoint | `platform`(query string 可选='xhs') | CookiePayload |
| `GET` | `/api/monitor/covers/{note_id}` | ✓ | 作品封面,从本地缓存读。 | `note_id`(path string 必填) | |
| `PUT` | `/api/monitor/creators/{creator_hash}` | ✓ | 给博主起个备注(界面上的「备注」)。 | `creator_hash`(path string 必填)<br>`platform`(query string 可选='xhs') | CreatorAliasPayload |
| `GET` | `/api/monitor/events` | ✓ | List Events | `task_id`(query integer \| null 可选)<br>`type`(query string \| null 可选)<br>`since_id`(query integer \| null 可选)<br>`limit`(query integer 可选=200)<br>`platform`(query string \| null 可选) | |
| `POST` | `/api/monitor/events/read` | ✓ | Mark Events Read | `task_id`(query integer \| null 可选) | |
| `GET` | `/api/monitor/export` | ✓ | Download collected data as CSV or Excel. | `kind`(query string 必填)<br>`task_id`(query integer[] \| null 可选)<br>`note_id`(query string \| null 可选)<br>`start_date`(query string \| null 可选)<br>`end_date`(query string \| null 可选)<br>`days`(query integer 可选=7)<br>`format`(query string 可选='csv')<br>`platform`(query string \| null 可选) | |
| `DELETE` | `/api/monitor/login/qr` | ✓ | Drop our tab and stop polling. | | |
| `GET` | `/api/monitor/login/qr` | ✓ | Poll the live session: waiting -> success / expired / error. | | |
| `POST` | `/api/monitor/login/qr` | ✓ | Open the login page in the CDP browser and return its QR code. | `platform`(query string 可选='xhs') | |
| `GET` | `/api/monitor/login/state` | ✓ | Ask the browser itself whether it is signed in. | `force`(query boolean 可选=False) | |
| `GET` | `/api/monitor/notes` | ✓ | List Notes | `task_id`(query integer \| null 可选)<br>`only_new`(query boolean 可选=False)<br>`limit`(query integer 可选=200)<br>`platform`(query string \| null 可选)<br>`tag_id`(query integer[] \| null 可选) | |
| `PUT` | `/api/monitor/notes/{note_id}` | ✓ | 给**作品**起个备注。 | `note_id`(path string 必填)<br>`platform`(query string 可选='xhs') | NoteAliasPayload |
| `GET` | `/api/monitor/notes/{note_id}/series` | ✓ | Metric time series for a single note. | `note_id`(path string 必填)<br>`task_id`(query integer \| null 可选) | |
| `PUT` | `/api/monitor/notes/{note_id}/tags` | ✓ | 把一条作品的标签**整体替换**成这一组(空列表 = 全摘掉)。 | `note_id`(path string 必填)<br>`platform`(query string 可选='xhs') | NoteTagsPayload |
| `GET` | `/api/monitor/overview` | ✓ | Headline numbers for the dashboard tiles, scoped to one platform. | `platform`(query string \| null 可选) | |
| `GET` | `/api/monitor/report` | ✓ | Daily new-content counts and interaction deltas for the selected tasks. | `task_id`(query integer[] \| null 可选)<br>`start_date`(query string \| null 可选)<br>`end_date`(query string \| null 可选)<br>`days`(query integer 可选=7)<br>`platform`(query string \| null 可选) | |
| `GET` | `/api/monitor/tags` | ✓ | 标签词表 + 各自被用在多少条作品上(删之前能看清代价)。 | | |
| `POST` | `/api/monitor/tags` | ✓ | Create Tag | | TagPayload |
| `DELETE` | `/api/monitor/tags/{tag_id}` | ✓ | 删标签,连同它在所有作品上的贴纸。 | `tag_id`(path integer 必填) | |
| `PUT` | `/api/monitor/tags/{tag_id}` | ✓ | Update Tag | `tag_id`(path integer 必填) | TagPayload |
| `GET` | `/api/monitor/tasks` | ✓ | List Tasks | `platform`(query string \| null 可选) | |
| `POST` | `/api/monitor/tasks` | ✓ | Create Task | | MonitorTaskCreate |
| `DELETE` | `/api/monitor/tasks/{task_id}` | ✓ | Delete Task | `task_id`(path integer 必填) | |
| `PATCH` | `/api/monitor/tasks/{task_id}` | ✓ | Update Task | `task_id`(path integer 必填) | MonitorTaskUpdate |
| `POST` | `/api/monitor/tasks/{task_id}/run` | ✓ | Queue a run immediately and return; the crawl itself takes minutes. | `task_id`(path integer 必填) | |
| `GET` | `/api/monitor/tasks/{task_id}/runs` | ✓ | List Runs | `task_id`(path integer 必填)<br>`limit`(query integer 可选=50) | |
| `GET` | `/api/monitor/upstream` | ✓ | 最近一次上游检查的结果。 | | |
| `POST` | `/api/monitor/upstream/check` | ✓ | 立刻检查一次上游仓库,并把结果写回缓存。 | | |
| `DELETE` | `/api/monitor/webhook` | ✓ | Clear Webhook | | |
| `GET` | `/api/monitor/webhook` | ✓ | Get Webhook | | |
| `POST` | `/api/monitor/webhook` | ✓ | Set Webhook | | WebhookPayload |
| `POST` | `/api/monitor/webhook/test` | ✓ | Send a test message so the user can verify the robot works before relying on it. | | WebhookTestPayload |
## 设置
| 方法 | 路径 | 登录 | 说明 | 参数 | 请求体 |
|---|---|:--:|---|---|---|
| `GET` | `/api/settings` | ✓ | Settings for one platform, plus the system-wide ones. | `platform`(query string 可选='xhs') | |
| `PUT` | `/api/settings` | ✓ | Partial update: only the keys present in the body are written. | `platform`(query string 可选='xhs') | SettingsUpdatePayload |
## 请求体结构
### ChangePasswordPayload
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `current` | string | ✓ | | |
| `new` | string | ✓ | | |
### CookiePayload
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `cookie` | string | ✓ | | |
### CrawlerStartRequest
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `platform` | PlatformEnum | ✓ | | |
| `login_type` | LoginTypeEnum | | 'qrcode' | |
| `crawler_type` | CrawlerTypeEnum | | 'search' | |
| `keywords` | string | | '' | |
| `specified_ids` | string | | '' | |
| `creator_ids` | string | | '' | |
| `start_page` | integer | | 1 | |
| `enable_comments` | boolean | | True | |
| `enable_sub_comments` | boolean | | False | |
| `enable_media` | boolean | | False | |
| `save_option` | SaveDataOptionEnum | | 'jsonl' | |
| `cookies` | string | | '' | |
| `headless` | boolean | | False | |
| `max_notes_count` | integer \| null | | | |
| `max_comments_count` | integer \| null | | | |
| `save_data_path` | string \| null | | | |
| `enable_cdp_mode` | boolean \| null | | | |
| `inject_all_cookies` | boolean \| null | | | |
| `save_login_state` | boolean \| null | | | |
| `cookies_file` | string \| null | | | |
| `max_concurrency_num` | integer \| null | | | |
| `crawler_max_sleep_sec` | integer \| null | | | |
| `enable_ip_proxy` | boolean \| null | | | |
| `ip_proxy_pool_count` | integer \| null | | | |
| `ip_proxy_provider_name` | string \| null | | | |
| `static_proxy_url` | string \| null | | | |
### CreatorAliasPayload
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `alias` | string | | '' | |
### LoginPayload
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `password` | string | ✓ | | |
### MonitorTaskCreate
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `name` | string | ✓ | | |
| `platform` | string | | 'xhs' | |
| `mode` | string | ✓ | | |
| `interval_minutes` | integer \| null | | | |
| `schedule_mode` | string | | 'interval' | |
| `schedule_hours` | integer[] | | | |
| `schedule_days` | integer[] | | | |
| `schedule_minute` | integer | | 0 | |
| `max_notes_count` | integer \| null | | | |
| `enable_comments` | boolean | | True | |
| `max_comments_count` | integer \| null | | | |
| `run_timeout_seconds` | integer | | 3600 | |
| `enabled` | boolean | | True | |
| `notify_enabled` | boolean | | False | |
| `notify_failures` | boolean | | True | |
| `targets` | string[] | ✓ | | |
### MonitorTaskUpdate
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `name` | string \| null | | | |
| `enabled` | boolean \| null | | | |
| `interval_minutes` | integer \| null | | | |
| `schedule_mode` | string \| null | | | |
| `schedule_hours` | integer[] \| null | | | |
| `schedule_days` | integer[] \| null | | | |
| `schedule_minute` | integer \| null | | | |
| `max_notes_count` | integer \| null | | | |
| `enable_comments` | boolean \| null | | | |
| `max_comments_count` | integer \| null | | | |
| `run_timeout_seconds` | integer \| null | | | |
| `notify_enabled` | boolean \| null | | | |
| `notify_failures` | boolean \| null | | | |
| `targets` | string[] \| null | | | |
### NoteAliasPayload
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `alias` | string | | '' | |
### NoteTagsPayload
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `tag_ids` | integer[] | | | |
### SettingsUpdatePayload
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
### TagPayload
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `name` | string | ✓ | | |
| `color` | string | | '' | |
### WebhookPayload
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `url` | string | | '' | 企业微信机器人 Webhook 地址,留空表示停用 |
### WebhookTestPayload
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|:--:|---|---|
| `url` | string \| null | | | 不传则使用已保存的地址 |
> **响应没有结构可列。** 这些接口返回的是普通 dict,没有声明 `response_model`,所以 OpenAPI 里没有响应的 shape。要看响应就调一次,或者读对应的 `service.py`。