From 092cf22d7a24412fc5dc4fbc77a65f02a3398521 Mon Sep 17 00:00:00 2001 From: mediabot-pt <295750538+mediabot-pt@users.noreply.github.com> Date: Mon, 29 Jun 2026 17:09:17 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20PAR=20Server=20?= =?UTF-8?q?=E9=9B=86=E6=88=90=E5=BC=80=E5=8F=91=E6=96=87=E6=A1=A3=20(API.m?= =?UTF-8?q?d)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 认证鉴权: PSK 签名步骤 + Bearer Token 说明 - 全部公开 API 接口参考 (系统/认证/站点/配置/清单) - 数据结构: 统一响应格式/Site/SiteConfig - 工作流程: 首次接入/系统重建/配置更新 - Python + Node.js 完整代码示例 --- docs/API.md | 435 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 docs/API.md diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..98bf8a1 --- /dev/null +++ b/docs/API.md @@ -0,0 +1,435 @@ +# PAR Server 集成开发文档 + +> 版本: v1.0 | Base URL: `http://:9012` + +## 目录 +- [1. 认证鉴权](#1-认证鉴权) +- [2. 接口参考](#2-接口参考) +- [3. 数据结构](#3-数据结构) +- [4. 工作流程](#4-工作流程) +- [5. 代码示例](#5-代码示例) + +--- + +## 1. 认证鉴权 + +### PSK 签名方式 + +``` +X-Timestamp: 1782800000 +X-Signature: Base64( HMAC-SHA256(PSK, "METHOD:path:timestamp") ) +X-Email: your@email.com (身份标识,可传多个) +``` + +#### 签名步骤 + +``` +① 获取服务端时间 + GET /api/v1/time → {"timestamp": 1782800000} + +② 拼接签名内容 + signContent = "POST:/api/v1/sites/pthome/submit:1782800000" + +③ 计算签名 + signature = Base64( HMAC-SHA256(PSK, signContent) ) + +④ 发送请求 + curl -H "X-Timestamp: 1782800000" \ + -H "X-Signature: oNTk7GIA..." \ + -H "X-Email: mediabot@example.com" \ + ... +``` + +#### 注意事项 +- 时间窗口默认 60 秒(可配置) +- 建议先调 `/api/v1/time` 获取服务端时间 +- PSK 由管理员在 PAR 管理后台生成和分发 +- `X-Email` 不参与签名,仅用于审计和身份关联 + +### Bearer Token 方式(管理后台用) + +``` +Authorization: Bearer +``` + +Token 通过 `POST /api/v1/auth/login` 获取,24 小时有效。 + +--- + +## 2. 接口参考 + +### 2.1 系统 + +| 方法 | 路径 | 认证 | 说明 | +|------|------|------|------| +| GET | `/health` | 无 | 健康检查 | +| GET | `/api/v1/time` | 无 | 服务端时间戳(秒) | + +### 2.2 认证 + +| 方法 | 路径 | 认证 | 说明 | +|------|------|------|------| +| POST | `/api/v1/auth/register` | 无 | 注册账户(密码可选) | +| POST | `/api/v1/auth/login` | 无 | 登录,返回 Bearer token | +| POST | `/api/v1/auth/recover` | PSK | PSK 签名恢复账户(自动创建+提权) | + +#### POST /api/v1/auth/register + +```json +// Request +{ "email": "mediabot@example.com", "password": "optional" } + +// Response 200 +{ + "code": 200, + "data": { + "account": { + "id": 5, + "email": "mediabot@example.com", + "trustLevel": "MEMBER", + "isActive": true, + "createdAt": "2026-06-29T14:00:00" + } + } +} +``` + +> 💡 携带 `X-Medibot-Timestamp` + `X-Medibot-Signature` PSK 签名头可自动提权为 TRUSTED + +#### POST /api/v1/auth/recover + +```json +// Request (带 PSK 签名头) +{ "email": "mediabot@example.com" } + +// Response 200 +{ + "code": 200, + "data": { + "account": { "id": 5, "email": "...", "trustLevel": "TRUSTED", ... } + } +} +``` + +### 2.3 全局清单 + +#### GET /api/v1/manifest + +```json +// Response 200 +{ + "code": 200, + "data": { + "version": "1.0", + "sites": [ + { + "siteId": "pthome", + "name": "PThome", + "siteType": "private", + "schemaVersion": "1.0.0", + "status": "active", + "latestVersion": "1.2.0", + "configHash": "a1b2c3d4...", + "updatedAt": "2026-06-29T14:00:00" + } + ] + } +} +``` + +### 2.4 站点 + +| 方法 | 路径 | 认证 | 说明 | +|------|------|------|------| +| GET | `/api/v1/sites` | 无 | 所有活跃站点 | +| GET | `/api/v1/sites/{siteId}` | 无 | 单个站点详情 | +| POST | `/api/v1/sites/{siteId}/submit` | PSK | 提交站点收录 | + +#### POST /api/v1/sites/{siteId}/submit + +```json +// Request +{ + "name": "PThome", // 必填 + "baseUrl": "https://pthome.net", // 必填 + "description": "PT 资源站", + "iconUrl": "https://...", + "siteType": "private" // private/public +} + +// Response 200 +{ + "code": 200, + "data": { + "id": 1, + "siteId": "pthome", + "name": "PThome", + "baseUrl": "https://pthome.net", + "siteType": "private", + "schemaVersion": "1.0.0", + "status": "active", + "createdAt": "2026-06-29T14:00:00" + } +} +``` + +### 2.5 配置/适配脚本 + +| 方法 | 路径 | 认证 | 说明 | +|------|------|------|------| +| GET | `/api/v1/configs/{siteId}` | 无 | 已批准配置版本列表 | +| GET | `/api/v1/configs/{siteId}/{version}` | 无 | 下载指定版本配置 | +| POST | `/api/v1/configs/{siteId}/submit` | PSK | 提交新配置版本 | + +#### GET /api/v1/configs/{siteId} + +```json +// Response 200 +{ + "code": 200, + "data": [ + { + "id": 10, + "siteId": "pthome", + "version": "1.2.0", + "configHash": "a1b2c3...", + "fileSize": 8192, + "schemaVersion": "1.0.0", + "status": "approved", + "isLatest": true, + "createdAt": "2026-06-29T14:00:00" + } + ] +} +``` + +#### GET /api/v1/configs/{siteId}/{version} + +支持 `If-None-Match` 条件请求(ETag = configHash),返回 304 表示内容未变化。 + +```json +// Response 200 (Content-Type: application/json) +"{\"rules\": [...]}" // 原始配置 JSON 内容 +``` + +#### POST /api/v1/configs/{siteId}/submit + +```json +// Request +{ + "version": "1.3.0", // 必填 + "config": "{...json...}", // 必填:适配脚本 JSON + "schemaVersion": "1.0.0" +} + +// Response 200 +{ + "code": 200, + "data": { + "id": 11, + "siteId": "pthome", + "version": "1.3.0", + "configHash": "e5f6a7...", + "status": "pending", + "createdAt": "2026-06-29T14:00:00" + } +} +``` + +--- + +## 3. 数据结构 + +### 统一响应格式 + +```json +{ + "code": 200, + "message": "success", + "data": { ... }, + "timestamp": 1782800000 +} +``` + +| code | 说明 | +|------|------| +| 200 | 成功 | +| 400 | 参数错误 | +| 401 | 认证失败 | +| 403 | 权限不足 | +| 404 | 资源不存在 | +| 500 | 服务器错误 | + +### 站点 (Site) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | Long | 内部 ID | +| siteId | String | 站点唯一标识(如 `pthome`) | +| name | String | 显示名称 | +| description | String | 描述 | +| baseUrl | String | 站点 URL | +| iconUrl | String | 图标 URL | +| siteType | String | `private` / `public` | +| schemaVersion | String | 当前 Schema 版本 | +| status | String | `active` / `pending` / `deprecated` | +| createdAt | DateTime | 创建时间 | + +### 配置版本 (SiteConfig) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | Long | 内部 ID | +| siteId | String | 关联站点 | +| version | String | 版本号(如 `1.2.3`) | +| configHash | String | 内容 SHA-256(用于 ETag) | +| fileSize | Long | 文件大小(字节) | +| schemaVersion | String | Schema 版本 | +| status | String | `pending` / `approved` / `rejected` | +| isLatest | Boolean | 是否最新版本 | + +--- + +## 4. 工作流程 + +### 4.1 首次接入 + +``` +① 管理员在 PAR 生成 PSK → 提供给 mediabot + +② medibot 用 PSK 注册(自动提权) + POST /api/v1/auth/register + Headers: X-Medibot-Timestamp, X-Medibot-Signature + Body: {"email": "mediabot@example.com"} + +③ 提交站点 + POST /api/v1/sites/pthome/submit (带 PSK 签名) + +④ 提交适配脚本 + POST /api/v1/configs/pthome/submit (带 PSK 签名) + +⑤ 配置经管理员审核后变为 approved +``` + +### 4.2 系统重建恢复 + +``` +① medibot 检测 401 → 调 POST /api/v1/auth/recover + (PSK 签名 → 自动重建账户 + TRUSTED) + +② 所有旧凭据无需修改,签名方式不变 +``` + +### 4.3 配置更新 + +``` +① GET /api/v1/configs/{siteId} 查看当前版本 +② 准备新配置 JSON → POST /api/v1/configs/{siteId}/submit +③ 等待管理员审核通过 +``` + +--- + +## 5. 代码示例 + +### Python + +```python +import hashlib, hmac, base64, time, requests + +PSK = "your-psk-from-admin" +BASE = "http://par-server:9012/api/v1" +EMAIL = "mediabot@example.com" + +def get_server_time(): + return requests.get(f"{BASE}/time").json()["timestamp"] + +def hmac_headers(method, path): + ts = get_server_time() + sign_content = f"{method}:{path}:{ts}" + sig = base64.b64encode( + hmac.new(PSK.encode(), sign_content.encode(), hashlib.sha256).digest() + ).decode() + return { + "X-Timestamp": str(ts), + "X-Signature": sig, + "X-Email": EMAIL + } + +def submit_site(site_id, name, base_url, site_type="private"): + path = f"/api/v1/sites/{site_id}/submit" + return requests.post(f"{BASE}{path}", + json={"name": name, "baseUrl": base_url, "siteType": site_type}, + headers=hmac_headers("POST", path) + ).json() + +def submit_config(site_id, version, config_json, schema_version="1.0.0"): + path = f"/api/v1/configs/{site_id}/submit" + return requests.post(f"{BASE}{path}", + json={"version": version, "config": config_json, "schemaVersion": schema_version}, + headers=hmac_headers("POST", path) + ).json() + +def list_configs(site_id): + return requests.get(f"{BASE}/configs/{site_id}").json() + +def download_config(site_id, version): + resp = requests.get(f"{BASE}/configs/{site_id}/{version}") + if resp.status_code == 304: + return None # 未变化 + return resp.json() + +# ---- 使用 ---- +submit_site("pthome", "PThome", "https://pthome.net") +submit_config("pthome", "1.0.0", '{"rules":[...]}') +print(list_configs("pthome")) +``` + +### JavaScript (Node.js) + +```javascript +const crypto = require('crypto'); +const PSK = 'your-psk-from-admin'; +const BASE = 'http://par-server:9012/api/v1'; +const EMAIL = 'mediabot@example.com'; + +async function getServerTime() { + const res = await fetch(`${BASE}/time`); + return (await res.json()).timestamp; +} + +function sign(method, path, timestamp) { + const content = `${method}:${path}:${timestamp}`; + const sig = crypto.createHmac('sha256', PSK).update(content).digest('base64'); + return sig; +} + +async function hmacHeaders(method, path) { + const ts = await getServerTime(); + return { + 'X-Timestamp': String(ts), + 'X-Signature': sign(method, path, ts), + 'X-Email': EMAIL + }; +} + +async function submitSite(siteId, name, baseUrl, siteType = 'private') { + const path = `/api/v1/sites/${siteId}/submit`; + const res = await fetch(`${BASE}${path}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...await hmacHeaders('POST', path) }, + body: JSON.stringify({ name, baseUrl, siteType }) + }); + return res.json(); +} + +async function submitConfig(siteId, version, config, schemaVersion = '1.0.0') { + const path = `/api/v1/configs/${siteId}/submit`; + const res = await fetch(`${BASE}${path}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...await hmacHeaders('POST', path) }, + body: JSON.stringify({ version, config, schemaVersion }) + }); + return res.json(); +} +```