# MiniWeb Golang - Pages 模块接口文档

> 本文档根据 `service/interface/pages.go` 的 `Api_Pages()` 生成，涵盖文章（Article）、评论（Comment）、图片上传与第三方博客导入相关接口。

## 一、基础信息

| 项目 | 说明 |
| --- | --- |
| 基础地址 | `https://api.linwute.com:9200`（见 `service/all/const.go` 的 `HTTPS` + `Port`） |
| 协议 | HTTP / HTTPS |
| 数据格式 | 请求 / 响应均为 `JSON`（上传类接口为 `multipart/form-data`） |
| 字符编码 | `UTF-8` |

### 1.1 通用请求头（Header）

除标注「无需鉴权」的接口外，所有接口都需在请求头中携带以下字段用于 `MatchTokenKey` 鉴权：

| 请求头 | 必填 | 说明 |
| --- | --- | --- |
| `token` | 是 | 令牌，格式：`one-two-three-four-five`（base36 编码，`-` 分隔），有时效校验 |
| `sign` | 是 | 签名，不能为空 |
| `device` | 是 | 访问设备标识，不能为空 |
| `User-Agent` | 是 | 长度需 ≥ 20，否则判定「请求设备不安全」 |
| `language` | 否 | 语言标识（如 `cn` / `en`），用于错误提示文案 |
| `edit-key` | 条件必填 | 文章编辑密钥（默认 `linwute`，见 `all.EditKey`），新增/删除/上传文章等写操作必须 |
| `X-Forwarded-For` / `X-Real-IP` | 否 | 评论接口用于获取客户端真实 IP（可选，缺失时取 `RemoteAddr`） |

> **鉴权失败**：返回 `code=401`，`msg` 为具体原因（如「密钥过期」「密钥不匹配」「非法访问」等）。

### 1.2 通用响应结构

所有接口统一返回如下 JSON（`all.Return`）：

```json
{
  "code": 0,       // 0 表示成功；4xx/5xx 表示错误
  "msg": "提示信息",
  "data": {},      // 业务数据
  "info": null     // 附加信息
}
```

---

## 二、接口列表

### 1. 获取文章详情
- **地址**：`GET /getArticle`
- **鉴权**：需要（token / sign / device）
- **请求参数（Query）**：

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `area` | string | 否 | 地区标识（如 `cn`） |
| `id` | string | 是 | 文章 ID |

- **响应**：成功返回 `all.Article`（`data`）；文章不存在（`title` 为空）返回错误。

---

### 2. 获取文章列表
- **地址**：`GET /getArticlesList`
- **鉴权**：需要
- **请求参数（Query）**：

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `area` | string | 否 | 地区 |
| `blok` | string | 否 | 分类区块 |
| `page` | string | 否 | 页码 |
| `sum` | string | 否 | 每页数量 |

- **响应**：成功返回文章列表数据。

---

### 3. 搜索文章
- **地址**：`GET /getArticlesSearch`
- **鉴权**：需要
- **请求参数（Query）**：

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `area` | string | 否 | 地区 |
| `blok` | string | 否 | 分类区块 |
| `search` | string | 否 | 搜索关键词 |
| `page` | string | 否 | 页码 |
| `sum` | string | 否 | 每页数量 |

- **响应**：成功返回匹配的文章列表。

---

### 4. 获取文章区块（分类）列表
- **地址**：`GET /getArticleBlok`
- **鉴权**：需要
- **请求参数（Query）**：

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `area` | string | 否 | 地区 |

- **响应**：成功返回区块/分类数据（`ArticleBlok` 列表：`blok`、`type`）。

---

### 5. 新增 / 更新文章（保存文章）
- **地址**：`POST /saveArticle`
- **鉴权**：需要（token / sign / device）**+ 编辑密钥 `edit-key`**
- **请求头**：额外需要 `edit-key`（为空返回 410，错误返回 406）
- **请求参数（Body，JSON）**：`all.Article` 结构

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | int | 否 | >0 表示更新，≤0 表示新增 |
| `title` | string | 是（新增时） | 文章标题 |
| `content` | string | 否 | 正文内容 |
| `blok` | string | 否 | 分类区块 |
| `type` | string | 否 | 类型 `text`/`next`/`images` |
| `date` | string | 否 | 时间，缺省自动取当前时间 |
| `name` | string | 否 | 名称 |
| `area` | string | 否 | 地区 |
| `language` | string | 否 | 语言 |
| `images` | []string | 否 | 图片列表 |
| 其他 | - | - | 见 `all.Article` 定义 |

- **响应**：成功返回 `{ "id": <int>, "msg": "文章发布成功" / "文章更新成功" }`（`code=200`）。

---

### 6. 删除文章（软删除）
- **地址**：`POST /deleteArticle`
- **鉴权**：需要（token / sign / device）
- **请求参数（Body，JSON）**：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | int | 是 | 文章 ID（≤0 返回 410） |
| `edit_key` | string | 是 | 编辑密钥（缺失返回 410） |

- **响应**：成功返回 `{ "msg": "文章已删除" }`（`code=205`）。

---

### 7. 上传图片附件
- **地址**：`POST /uploadImages`
- **鉴权**：需要（token / sign / device）**+ 编辑密钥 `edit-key`**
- **请求头**：额外需要 `edit-key`
- **请求参数（Form-Data，multipart）**：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `files` | file[] | 是 | 图片文件，单文件限制 32MB，按 `files` 字段名上传多个 |

- **响应**：成功返回上传后的 URL 数组（如 `["/uploads/2026/8/xxx.png"]`，`code=200`）。

---

### 8. 获取评论列表
- **地址**：`GET /getComments`
- **鉴权**：无需 token（仅读取）
- **请求参数（Query）**：

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `article_id` | string | 是 | 文章 ID（缺失返回 410） |
| `page` | string | 否 | 页码 |
| `sum` | string | 否 | 每页数量 |

- **响应**：成功返回
  ```json
  {
    "comments": [ all.Comment ],
    "total": <int>
  }
  ```

---

### 9. 提交评论
- **地址**：`POST /saveComment`
- **鉴权**：无需 token
- **请求参数（Body，JSON）**：`all.Comment` 结构

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `article_id` | int | 是 | 文章 ID（≤0 返回 410） |
| `content` | string | 是 | 评论内容（为空返回 410） |
| `name` | string | 否 | 昵称，缺省为「匿名用户」 |
| `date` | string | 否 | 时间，缺省自动取当前时间 |

> 同一 IP / 昵称对同一文章每日限评论一次（返回 409）。
- **响应**：成功返回新建评论数据（`code=209`）。

---

### 10. 验证编辑密钥
- **地址**：`POST /verifyEditKey`
- **鉴权**：无需 token（用于无登录态的编辑入口验证）
- **请求参数（Body，JSON）**：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `edit_key` | string | 是 | 编辑密钥（缺失返回 410，错误返回 406） |

- **响应**：成功返回 `{ "msg": "验证通过" }`（`code=200`）。

---

### 11. WordPress 导入
- **地址**：`POST /api/wpimport`
- **鉴权**：需要（token / sign / device）**+ 表单字段 `edit_key`**
- **请求参数（Form-Data，multipart）**：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `sqlfile` | file | 是 | 导入文件，支持 `.sql` / `.txt`（SQL 导出）或 `.xml`（WXR 导出），上限 128MB |
| `edit_key` | string | 是 | 编辑密钥 |
| `blok` | string | 否 | 导入分类，缺省为 `WordPress` |

- **响应**：成功返回导入统计信息（`code=200`）。

---

### 12. 查询 WordPress 导入状态
- **地址**：`GET /api/wpimport/status`
- **鉴权**：无需 token
- **响应**：返回导入统计 `WPImportStats`（`TotalParsed` / `TotalImported` / `TotalSkipped`，`code=200`）。

---

### 13. Typecho 导入
- **地址**：`POST /api/typechoimport`
- **鉴权**：需要（token / sign / device）**+ 表单字段 `edit_key`**
- **请求参数（Form-Data，multipart）**：与 WordPress 导入一致（`sqlfile`、`edit_key`、`blok`），解析器为 `ParseTypechoSQL`。
- **响应**：导入统计信息。

---

### 14. Z-Blog 导入
- **地址**：`POST /api/zblogimport`
- **鉴权**：需要（token / sign / device）**+ 表单字段 `edit_key`**
- **请求参数（Form-Data，multipart）**：与 WordPress 导入一致（`sqlfile`、`edit_key`、`blok`），解析器为 `ParseZBlogSQL`。
- **响应**：导入统计信息。

---

## 三、常见错误码

| 错误码 | 含义 |
| --- | --- |
| 0 | 成功（部分写操作使用 200/205/209 表示成功） |
| 400 | 参数错误 / 解析失败 / 服务器处理异常 |
| 401 | 鉴权失败（token / sign / device 校验未通过） |
| 406 | 编辑密钥不正确 |
| 409 | 评论触发每日频率限制 |
| 410 | 缺少必要参数（文章 ID / 编辑密钥 / 评论内容等） |

---

## 四、调用示例

### 获取文章详情
```bash
curl "https://api.linwute.com:9200/getArticle?area=cn&id=1" \
  -H "token: <token>" \
  -H "sign: <sign>" \
  -H "device: <device>" \
  -H "User-Agent: Mozilla/5.0 (compatible; MiniWebClient/1.0)"
```

### 保存文章（新增）
```bash
curl -X POST "https://api.linwute.com:9200/saveArticle" \
  -H "token: <token>" \
  -H "sign: <sign>" \
  -H "device: <device>" \
  -H "edit-key: linwute" \
  -H "Content-Type: application/json" \
  -d '{"title":"测试文章","content":"正文内容","blok":"blog","area":"cn"}'
```
