# 机场榜GateRank 发布令牌接入说明

给第三方系统与 AI 的正式发文接口文档

- 文档地址：https://gate-rank.com/publish-token-docs
- Base URL：https://gate-rank.com/api/v1/publish
- 更新时间：2026-03-29T00:00:00+08:00

## 总览

- 鉴权方式：Authorization: Bearer <publish_token>
- 封面字段：cover_image_url
- 发布模式字段：publish_mode

### 字段说明

- `title`：文章标题。
- `content_markdown`：正文 Markdown，发布时必填。
- `slug`：可选，不传时会按标题自动生成。
- `excerpt`：可选，不传时会根据正文自动提取摘要。
- `cover_image_url`：封面地址字段，可直接传本站已上传图片 URL。
- `publish_mode`：只支持 draft 或 publish，默认 draft。

### 行为定义

- `draft`：只在后台创建草稿，不出现在前台 News。
- `publish`：创建后立即上线，需要 news:create + news:publish。
- `article.id`：创建成功后返回文章 ID，后续更新和发布都基于这个 ID。

## 鉴权与安全

- `Authorization`：仅支持 Bearer Token，不接受 x-api-key。
- `401`：令牌不存在、已吊销、已过期，或 Authorization 格式不正确。
- `403`：令牌存在，但 scope 不足，无法访问当前接口。

- `plain_token`：明文令牌只在创建成功时返回一次，之后不可找回。
- `revoke`：吊销后立刻失效，后续请求全部按未授权处理。
- `article.id`：第三方系统应保存创建返回的 article.id，后续操作均基于此值。

> 建议将发布令牌视为后端服务密钥，仅保存在服务端配置或安全密钥系统中。不要把它写入前端页面、浏览器脚本或公开仓库。

## 快速开始

1. 先准备 Bearer 令牌，并确认该令牌具备所需 scope。
2. 如果有封面图，先调用 upload-image 拿到本站图片 URL。
3. 调用 POST /news，创建草稿或直接发布。
4. 如果创建的是草稿，再调用 /news/:id/publish 上线。

```bash
curl -X POST 'https://gate-rank.com/api/v1/publish/news' \
  -H 'Authorization: Bearer <publish_token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "title":"新文章",
    "content_markdown":"# Hello\n\n正文内容",
    "publish_mode":"draft"
  }'
```

## 状态流转

- Draft：创建草稿后，内容只在后台存在，前台 News 页面不会展示。适合由第三方系统先写入，再由你在后台复核。
- Publish：调用 publish 或在创建时直接使用 publish_mode=publish 后，文章会进入前台已发布集合。
- Archive：归档后文章不再作为前台已发布新闻展示，但数据仍保留在后台系统中，便于追溯和再次处理。

## 创建文章：`POST /api/v1/publish/news`

- 需要 Scope：news:create；若 publish_mode=publish，还需要 news:publish
- 说明：创建一篇新闻草稿，或直接创建并发布。

### Request Body

- `title` (string, 是)：文章标题
- `content_markdown` (string, 是)：正文 Markdown
- `slug` (string, 否)：可选，不传自动生成
- `excerpt` (string, 否)：可选，不传自动提取
- `cover_image_url` (string, 否)：封面地址字段
- `publish_mode` (draft | publish, 否)：默认 draft

```json
{
  "title": "新文章",
  "slug": "new-article",
  "excerpt": "可选摘要",
  "cover_image_url": "/uploads/news/1743240000000-cover.webp",
  "content_markdown": "# Hello\n\n正文内容",
  "publish_mode": "draft"
}
```

### Response

- `article.id` (number, 是)：文章主键，后续更新/发布/归档都基于此值
- `article.status` (string, 是)：draft 或 published
- `article.slug` (string, 是)：最终文章 slug
- `article.published_at` (string | null, 是)：未发布时为 null

```json
{
  "article": {
    "id": 123,
    "title": "新文章",
    "slug": "new-article",
    "status": "draft",
    "cover_image_url": "/uploads/news/1743240000000-cover.webp",
    "published_at": null
  }
}
```

## 上传封面：`POST /api/v1/publish/news/upload-image`

- 需要 Scope：news:upload
- 说明：上传正文图片或封面图片；如果 mode=cover，会按封面规则压缩处理。

- `file` (binary, 是)：图片文件本体
- `mode` (cover | inline, 否)：封面建议传 cover；不传时按普通正文图片处理

```bash
curl -X POST 'https://gate-rank.com/api/v1/publish/news/upload-image' \
  -H 'Authorization: Bearer <publish_token>' \
  -F 'mode=cover' \
  -F 'file=@/path/to/cover.png'
```

```json
{"url":"/uploads/news/1743240000000-cover.webp"}
```

## 后续操作

- `PATCH /api/v1/publish/news/:id`：news:update。更新已存在的文章内容。
- `POST /api/v1/publish/news/:id/publish`：news:publish。将草稿正式发布到前台。
- `POST /api/v1/publish/news/:id/archive`：news:archive。归档文章，使其不再作为前台已发布新闻展示。

```bash
# 更新文章
curl -X PATCH 'https://gate-rank.com/api/v1/publish/news/123' \
  -H 'Authorization: Bearer <publish_token>' \
  -H 'Content-Type: application/json' \
  -d '{"title":"更新后的标题","content_markdown":"# Updated"}'

# 发布草稿
curl -X POST 'https://gate-rank.com/api/v1/publish/news/123/publish' \
  -H 'Authorization: Bearer <publish_token>' \
  -H 'Content-Type: application/json' \
  -d '{}'

# 归档文章
curl -X POST 'https://gate-rank.com/api/v1/publish/news/123/archive' \
  -H 'Authorization: Bearer <publish_token>'
```

## 错误码

- `UNAUTHORIZED`：Bearer token 不存在、已吊销、格式不对，或已过期。
- `FORBIDDEN`：令牌存在，但缺少当前接口所需 scope。
- `NEWS_SLUG_CONFLICT`：slug 已存在，需要更换。
- `BAD_REQUEST`：字段缺失、publish_mode 非法、文件或参数格式错误。
- `NEWS_NOT_FOUND`：文章 ID 不存在。

```json
{
  "code": "FORBIDDEN",
  "message": "Publish token scope not allowed",
  "request_id": "..."
}
```

### 排查顺序

- `401`：先检查 Authorization 格式、令牌是否已吊销、是否已过期。
- `403`：再检查该 token 是否具备当前接口所需 scope。
- `BAD_REQUEST`：最后检查 JSON 字段、multipart 参数和文章 ID 是否正确。

### 接入建议

- `request_id`：建议在调用失败时记录返回中的 request_id 便于排查。
- `retry`：仅对网络错误或明确可重试场景做有限重试，不要对 401/403 盲目重放。
- `slug`：若系统自行生成 slug，需处理 NEWS_SLUG_CONFLICT 回退逻辑。

## 权限矩阵

- `/api/v1/publish/news`：news:create
- `/api/v1/publish/news（publish_mode=publish）`：news:create + news:publish
- `/api/v1/publish/news/:id`：news:update
- `/api/v1/publish/news/:id/publish`：news:publish
- `/api/v1/publish/news/:id/archive`：news:archive
- `/api/v1/publish/news/upload-image`：news:upload

> v1 不支持按 slug / external id 的 upsert，也不提供基于 token 的文章列表查询。第三方系统如需后续更新文章，请保存创建接口返回的 article.id。
