refactor: session-based external auth, dynamic sources, drop netease UID
Auth (external API): - ID: 16-digit random (non-sequential); Secret: SeaReport- + 32 hex - POST /auth/session: ID+Secret -> Bearer SESSION (24h, single-session, old session invalidated on re-issue, disabled client invalidates) - clientAuth now validates Bearer SESSION via api_sessions JOIN api_clients Sources (dynamic, no default, open-source friendly): - sources table + CRUD route (/api/sources, owner; delete guarded by usage) - users/user_identities.source ENUM -> VARCHAR, seeded netease/skin - register/admin create/identity bind: validate against enabled sources - UI: 来源管理 page; source dropdowns loaded dynamically everywhere (register, dashboard identity, users admin, bans), labels dynamic UID removal: - game_uid/reporter_game_uid no longer required (db default '', validations dropped, frontend fields optional) Docs: EXTERNAL-API.md session flow + new credential format; API.md updated Verified: 37 checks (syntax, session logic, source CRUD, UID removal, docs)
This commit is contained in:
@@ -346,11 +346,12 @@ Base URL: `http://<host>:3100/api`
|
||||
| PUT /external/clients/:id | Bearer(owner/admin) | 启用/停用客户端 |
|
||||
| DELETE /external/clients/:id | Bearer(owner/admin) | 删除客户端 |
|
||||
|
||||
### 外部鉴权(唯一方式)
|
||||
### 外部鉴权(SESSION 机制)
|
||||
|
||||
| 方式 | 请求头 | 说明 |
|
||||
| 阶段 | 请求头 | 说明 |
|
||||
|------|--------|------|
|
||||
| ID + Secret(唯一) | `x-api-client-id` + `x-api-secret` | 后台可创建/停用多个客户端;无凭据或凭据错误一律 401 |
|
||||
| 换取 SESSION | `x-api-client-id` + `x-api-secret` | 仅 `POST /external/auth/session` 使用;ID 为 16 位随机数字,Secret 为 `SeaReport-` + 32 位 |
|
||||
| 后续请求 | `Authorization: Bearer <SESSION>` | 所有其余接口;24 小时有效,重换即旧 SESSION 失效,客户端停用立即失效 |
|
||||
|
||||
### 子服务器定位(`server` 参数)
|
||||
|
||||
|
||||
@@ -2,31 +2,60 @@
|
||||
|
||||
适用对象:QQ 官方机器人、游戏服务器插件、统计面板等**无法做网页登录**的外部系统。
|
||||
|
||||
- Base URL:`https://<你的域名>/api/external`
|
||||
- Base URL:`https://report.sea-studio.top/api/external`
|
||||
- 数据格式:JSON(`Content-Type: application/json`)
|
||||
- 本文档所有接口**不需要用户登录**,只需要客户端凭据(见下)
|
||||
- 本文档所有接口都需要客户端凭据(见下)
|
||||
|
||||
---
|
||||
|
||||
## 一、鉴权(唯一方式:ID + Secret)
|
||||
## 一、鉴权流程(ID + Secret 换取 SESSION)
|
||||
|
||||
在站点后台 →「外部API」页面创建客户端,获得一对凭据:
|
||||
|
||||
```text
|
||||
Client ID: c_1a2b3c4d5e6f7a8b9c0d1e2f
|
||||
Secret: s_9f8e7d6c5b4a39281726354a1b2c3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e
|
||||
Client ID: 1829473056482917 # 16 位纯数字, 随机生成
|
||||
Secret: SeaReport-a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4 # SeaReport- + 32 位
|
||||
```
|
||||
|
||||
> ⚠️ Secret 只在创建时显示一次,请立即保存。支持创建多个客户端、单独停用/删除,互不影响。
|
||||
> 无凭据 / 凭据错误 / 客户端停用 → 一律返回 `401`。
|
||||
|
||||
调用时在请求头携带:
|
||||
### 第一步:换取 SESSION(唯一使用 ID+Secret 的接口)
|
||||
|
||||
```
|
||||
POST /api/external/auth/session
|
||||
```
|
||||
|
||||
请求头:
|
||||
|
||||
| 请求头 | 值 |
|
||||
|--------|-----|
|
||||
| `x-api-client-id` | 你的 Client ID |
|
||||
| `x-api-secret` | 你的 Secret |
|
||||
|
||||
**成功响应 200:**
|
||||
|
||||
```json
|
||||
{
|
||||
"session_token": "5f8a2b3c...96位十六进制",
|
||||
"expires_in": 86400,
|
||||
"client_id": "1829473056482917",
|
||||
"message": "SESSION 有效期 24 小时, 请用 Authorization: Bearer <session> 访问其余接口"
|
||||
}
|
||||
```
|
||||
|
||||
### 第二步:后续请求携带 SESSION
|
||||
|
||||
所有其余接口(工单/封禁/统计等)使用:
|
||||
|
||||
| 请求头 | 值 |
|
||||
|--------|-----|
|
||||
| `Authorization` | `Bearer <session_token>` |
|
||||
|
||||
- SESSION 有效期 **24 小时**;过期后重新调用第一步换取。
|
||||
- 同一客户端重新换取时,**旧 SESSION 立即失效**(单会话)。
|
||||
- 客户端被停用 → 已发 SESSION 立即失效(401)。
|
||||
- 无 SESSION / SESSION 无效或过期 → 一律 `401`。
|
||||
|
||||
---
|
||||
|
||||
## 二、快速开始(Python / Node 示例)
|
||||
@@ -37,20 +66,21 @@ Secret: s_9f8e7d6c5b4a39281726354a1b2c3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e
|
||||
import requests
|
||||
|
||||
BASE = "https://你的域名/api/external"
|
||||
HEADERS = {
|
||||
"x-api-client-id": "c_1a2b3c4d5e6f7a8b9c0d1e2f",
|
||||
"x-api-secret": "s_9f8e7d6c...",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
# 提交举报工单
|
||||
# 1) 用 ID + Secret 换取 SESSION
|
||||
r = requests.post(f"{BASE}/auth/session", headers={
|
||||
"x-api-client-id": "1829473056482917",
|
||||
"x-api-secret": "SeaReport-a1b2c3d4...",
|
||||
})
|
||||
session = r.json()["session_token"]
|
||||
HEADERS = {"Authorization": f"Bearer {session}", "Content-Type": "application/json"}
|
||||
|
||||
# 2) 提交举报工单(带 SESSION)
|
||||
r = requests.post(f"{BASE}/tickets", headers=HEADERS, json={
|
||||
"type": "report",
|
||||
"title": "恶意破坏",
|
||||
"reporter_game_name": "Steve",
|
||||
"reporter_game_uid": "df273bda10b94aa18db345574d5a1e1d",
|
||||
"target_game_name": "Alex",
|
||||
"target_game_uid": "SKIN_UUID_002",
|
||||
"reason": "刷屏+破坏他人建筑",
|
||||
"description": "多次警告无效",
|
||||
"server": "survival", # 可选: 子服别名
|
||||
@@ -62,13 +92,16 @@ print(r.json()) # {"id": 123, "tracking_token": "..."}
|
||||
|
||||
```javascript
|
||||
const BASE = 'https://你的域名/api/external';
|
||||
const H = {
|
||||
'x-api-client-id': 'c_1a2b3c...',
|
||||
'x-api-secret': 's_9f8e7d6c...',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
// 查询工单进度(从提交到结束全程可查)
|
||||
// 1) 换取 SESSION
|
||||
const authRes = await fetch(`${BASE}/auth/session`, {
|
||||
method: 'POST',
|
||||
headers: { 'x-api-client-id': '1829473056482917', 'x-api-secret': 'SeaReport-a1b2c3d4...' },
|
||||
});
|
||||
const { session_token } = await authRes.json();
|
||||
const H = { Authorization: `Bearer ${session_token}`, 'Content-Type': 'application/json' };
|
||||
|
||||
// 2) 查询工单进度(从提交到结束全程可查)
|
||||
const res = await fetch(`${BASE}/tickets/track?token=你的tracking_token`, { headers: H });
|
||||
console.log(await res.json());
|
||||
```
|
||||
@@ -91,6 +124,12 @@ console.log(await res.json());
|
||||
|
||||
## 四、接口清单
|
||||
|
||||
### 0. 换取 SESSION(见第一节, 其余接口均需 Bearer SESSION)
|
||||
|
||||
```
|
||||
POST /api/external/auth/session
|
||||
```
|
||||
|
||||
### 1. 提交工单
|
||||
|
||||
```
|
||||
@@ -102,7 +141,7 @@ POST /api/external/tickets
|
||||
| type | string | ✅ | `report` / `suggestion` / `appeal` |
|
||||
| title | string | ✅ | 标题 |
|
||||
| reporter_game_name | string | ✅ | 提交人游戏名 |
|
||||
| reporter_game_uid | string | ✅ | 提交人 UID / UUID |
|
||||
| reporter_game_uid | string | 否 | 提交人 UID / UUID(选填) |
|
||||
| target_game_name | string | 条件 | 被举报人游戏名(report 建议填) |
|
||||
| target_game_uid | string | 条件 | 被举报人 UID |
|
||||
| reason | string | 条件 | 举报原因(report/appeal 必填) |
|
||||
|
||||
Reference in New Issue
Block a user