Scert/doc/后端接口说明文档.md

770 lines
23 KiB
Markdown
Raw Permalink 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.

# Smail 后端接口说明文档
> 本文档用于说明当前 Smail 后端 HTTP API 的实际调用方式,并对照原版 PMail《后端接口文档》核对接口是否仍然存在。
>
> 核对基准:当前代码分支 `main`,提交 `6ec4628`,核对日期:2026-08-27。
## 1. 文档范围和结论
原版 PMail 文档包含 9 个基础接口。当前 Smail 仍然保留其中 8 个接口的功能入口,但有以下路径或调用方式变化:
| 编号 | 原版接口 | 当前实际接口 | 当前状态 | 主要差异 |
|---|---|---|---|---|
| 1 | `/api/ping` | `/api/ping` | 存在 | 当前返回统一 JSON,不再是纯文本 `pong` |
| 2 | `/api/login` | `/api/login` | 存在 | 只接受 `POST`;使用会话 Cookie 和 CSRF Cookie,不使用原版文档所述的 `token` 请求头 |
| 3 | `/api/group` | `/api/group` | 部分兼容 | 当前只接受 `GET`,原版写的 `POST` 不再可用;另有 `/api/group/list` |
| 4 | `/api/email/list` | `/api/email/list` | 存在 | 只接受 `POST`,分页参数增加范围限制 |
| 5 | `/api/detail` | `/api/email/detail` | 路径已变更 | 原路径不存在,必须使用 `/api/email/detail` |
| 6 | `/api/email/send` | `/api/email/send` | 存在 | 只接受 `POST`,增加发件人校验、附件和收件人数限制 |
| 7 | `/api/settings/modify_password` | `/api/settings/modify_password` | 存在 | 当前需要同时提交 `current_password` 和 `password` |
| 8 | `/attachments/` | `/attachments/{邮件ID}/{内容标识}` | 存在 | 当前只接受 `GET`,用于内联展示附件内容 |
| 9 | `/attachments/download/` | `/attachments/download/{邮件ID}/{附件序号}` | 存在 | 当前只接受 `GET`,最后一段是附件序号,不是附件数据库 ID |
当前路由注册以 `server/listen/http_server/http_server.go` 为准,接口处理逻辑以 `server/controllers` 和 `server/controllers/email` 为准。
## 2. 通用调用约定
### 2.1 基础地址
以下示例使用:
```text
https://mail.example.com
```
请替换为实际的 Smail Web 地址。开发环境也可以使用:
```text
http://127.0.0.1:80
```
生产环境应优先使用 HTTPS。
### 2.2 通用响应格式
除附件内容下载接口外,JSON 接口统一返回以下结构:
```json
{
"errorNo": 0,
"errorMsg": "",
"data": {}
}
```
字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| `errorNo` | number | `0` 表示成功;非 `0` 表示失败。常见值:`100` 参数错误、`403` 未登录、`405` 无权限、`500` 服务端错误 |
| `errorMsg` | string | 错误说明。生产环境不应依赖错误文本判断业务分支,应优先判断 `errorNo` |
| `data` | any | 成功数据或错误附加数据 |
请求方法不正确时,路由层返回 HTTP `405 Method Not Allowed`,并通过 `Allow` 响应头提示允许的方法;此时不一定返回上面的 JSON 结构。
### 2.3 登录态和 CSRF
除探活、登录和公开的密码恢复流程外,邮件、分组、密码修改等接口都需要登录态。
登录成功后,服务端会设置:
1. 会话 Cookie,用于识别登录用户;
2. `smail_csrf` Cookie,用于保存 CSRF Token;
3. 后续状态修改请求需要在 `X-CSRF-Token` 请求头中提交与 `smail_csrf` Cookie 相同的值。
对于携带登录会话 Cookie 的状态修改请求,还需要使用同源的 `Origin`;跨站请求或 CSRF Token 不匹配时会被拒绝。
建议使用浏览器或 HTTP 客户端的 Cookie Jar 保存登录后的 Cookie,不要手工复制会话 Cookie 到代码或日志中。
### 2.4 请求头
JSON 请求通常应携带:
```http
Content-Type: application/json
```
需要登录的状态修改请求示例:
```http
Cookie: 会话Cookie; smail_csrf=CSRF_TOKEN
X-CSRF-Token: CSRF_TOKEN
Origin: https://mail.example.com
```
`Lang` 请求头可以指定返回语言,例如:
```http
Lang: zh-CN
```
未指定时由服务端使用默认语言处理。
### 2.5 通用错误处理
调用方建议按以下顺序处理:
1. 先检查 HTTP 状态码;
2. 对 JSON 响应解析 `errorNo`、`errorMsg` 和 `data`;
3. 收到 `403` 或业务错误码表示登录失效时,重新登录;
4. 收到 `405` 时检查请求方法,不要重试相同方法;
5. 不要把密码、会话 Cookie、CSRF Token、完整邮箱地址写入日志。
## 3. 原版基础接口对照和调用说明
## 3.1 服务探活
### 接口信息
| 项目 | 内容 |
|---|---|
| 原版路径 | `/api/ping` |
| 当前路径 | `/api/ping` |
| 当前方法 | `GET`、`POST` 均可到达处理器;建议使用 `GET` |
| 是否需要登录 | 否 |
| 请求体 | 无 |
| 返回类型 | JSON |
### 调用示例
```bash
curl -X GET "https://mail.example.com/api/ping"
```
也可以使用:
```bash
curl -X POST "https://mail.example.com/api/ping"
```
### 返回示例
```json
{
"errorNo": 0,
"errorMsg": "",
"data": "pong"
}
```
### 与原版差异
原版文档把返回格式写成 `text`,示例为纯文本 `pong`。当前代码通过统一响应封装返回 JSON,实际数据位于 `data` 字段中。
## 3.2 用户登录
### 接口信息
| 项目 | 内容 |
|---|---|
| 原版路径 | `/api/login` |
| 当前路径 | `/api/login` |
| 方法 | `POST` |
| 是否需要登录 | 否 |
| 请求格式 | `application/json` |
| 返回类型 | JSON |
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `identifier` | string | 否 | 登录标识。当前优先使用该字段,可填写账号或完整邮箱 |
| `account` | string | 否 | 兼容原版字段。当 `identifier` 为空时使用该字段 |
| `password` | string | 是 | 登录密码 |
`identifier` 和 `account` 至少提供一个。为了兼容原版客户端,可以继续提交 `account`;新客户端建议使用 `identifier`。
### 调用示例
兼容原版字段:
```bash
curl -X POST "https://mail.example.com/api/login" \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{
"account": "admin",
"password": "your-password"
}'
```
使用新的登录标识字段:
```bash
curl -X POST "https://mail.example.com/api/login" \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{
"identifier": "admin@example.com",
"password": "your-password"
}'
```
### 成功返回示例
普通登录直接完成时,返回:
```json
{
"errorNo": 0,
"errorMsg": "",
"data": {
"account": "admin",
"name": "管理员",
"role": "super_admin",
"is_admin": 1,
"domains": [
"example.com"
]
}
}
```
登录成功时,服务端同时通过 `Set-Cookie` 设置会话 Cookie 和 CSRF Cookie。客户端必须保存这两个 Cookie,后续接口才能继续调用。
### 二次验证情况
如果当前账号启用了登录二次验证,密码正确后可能返回一个待完成的验证数据,而不是直接建立完整登录态。客户端应根据返回的 `data` 调用登录验证接口:
```text
POST /api/security/login/verify
```
该流程不属于原版 PMail 基础接口,详细接口以本文第 4 节的当前安全接口表和前端调用为准。
### 失败返回示例
```json
{
"errorNo": 100,
"errorMsg": "参数错误",
"data": ""
}
```
为了防止账号枚举,登录失败、账号不存在、密码错误和登录限流通常使用相同的业务错误响应,调用方不要根据错误文本判断账号是否存在。
### 与原版差异
原版文档提到可以通过请求头 `token` 进行验签。当前代码路由和认证逻辑未发现该兼容方式,当前登录态以服务端会话 Cookie 为准,不应使用 `token` 请求头替代登录。
## 3.3 获取邮件分组树
### 接口信息
| 项目 | 内容 |
|---|---|
| 原版路径 | `/api/group` |
| 当前路径 | `/api/group` |
| 方法 | `GET` |
| 原版方法 | `POST/GET` |
| 是否需要登录 | 是 |
| 请求体 | 无 |
| 返回类型 | JSON |
当前 `/api/group` 只注册了 `GET`。使用原版文档中的 `POST` 调用会返回 HTTP `405`。
### 调用示例
```bash
curl -X GET "https://mail.example.com/api/group" \
-b cookies.txt
```
如果是浏览器外的状态修改场景,读取接口不需要提交 CSRF Token;但必须携带有效会话 Cookie。
### 返回示例
```json
{
"errorNo": 0,
"errorMsg": "",
"data": [
{
"label": "收件箱",
"tag": "{\"type\":0,\"status\":-1,\"group_id\":0}"
},
{
"label": "发件箱",
"tag": "{\"type\":1,\"status\":-1}"
},
{
"label": "草稿箱",
"tag": "{\"type\":0,\"status\":4}"
}
]
}
```
### 当前相关分组接口
| 方法 | 路径 | 说明 | 请求体 |
|---|---|---|---|
| `GET` | `/api/group` | 获取前端使用的分组树 | 无 |
| `GET` | `/api/group/list` | 获取默认分组和用户自定义分组列表 | 无 |
| `POST` | `/api/group/add` | 创建自定义分组 | `{"name":"项目邮件","parent_id":0}` |
| `POST` | `/api/group/del` | 删除自定义分组 | `{"id":1}` |
`POST /api/group/add` 和 `POST /api/group/del` 属于状态修改接口,需要登录态和 CSRF Token。
## 3.4 获取邮件列表
### 接口信息
| 项目 | 内容 |
|---|---|
| 路径 | `/api/email/list` |
| 方法 | `POST` |
| 是否需要登录 | 是 |
| 请求格式 | `application/json` |
| 返回类型 | JSON |
### 请求参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---:|---:|---|
| `keyword` | string | 否 | 空字符串 | 邮件搜索关键词,最多 512 字节 |
| `tag` | string | 否 | 全部邮件 | JSON 字符串形式的筛选条件 |
| `current_page` | integer | 否 | `1` | 当前页,从 `1` 开始,最大 `10000` |
| `page_size` | integer | 否 | `15` | 每页数量,最大 `100` |
`tag` 是一个字符串字段,内容需要再次编码为 JSON,例如:
```json
"{\"type\":0,\"status\":-1,\"group_id\":0}"
```
### 调用示例
```bash
curl -X POST "https://mail.example.com/api/email/list" \
-H "Content-Type: application/json" \
-b cookies.txt \
-H "X-CSRF-Token: CSRF_TOKEN" \
-H "Origin: https://mail.example.com" \
-d '{
"keyword": "验证码",
"tag": "{\"type\":0,\"status\":-1,\"group_id\":0}",
"current_page": 1,
"page_size": 15
}'
```
### 返回示例
```json
{
"errorNo": 0,
"errorMsg": "",
"data": {
"current_page": 1,
"total_page": 1,
"list": [
{
"id": 1,
"title": "邮件标题",
"desc": "邮件正文摘要",
"datetime": "2026-08-27 08:44:00",
"is_read": false,
"sender": {
"Name": "发件人",
"EmailAddress": "sender@example.com"
},
"to": [
{
"Name": "收件人",
"EmailAddress": "admin@example.com"
}
],
"dangerous": false,
"error": ""
}
]
}
}
```
### 分页说明
- 不提交分页参数时使用 `current_page=1`、`page_size=15`;
- `current_page` 小于 `1` 或大于 `10000` 会被拒绝;
- `page_size` 小于 `1` 或大于 `100` 会被拒绝;
- `keyword` 超过 512 字节会被拒绝;
- `total_page` 在没有邮件时为 `0`。
## 3.5 获取邮件详情
### 接口信息
| 项目 | 内容 |
|---|---|
| 原版路径 | `/api/detail` |
| 当前路径 | `/api/email/detail` |
| 方法 | `POST` |
| 是否需要登录 | 是 |
| 请求格式 | `application/json` |
| 返回类型 | JSON |
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `id` | integer | 是 | 邮件 ID,必须大于 `0` |
### 调用示例
```bash
curl -X POST "https://mail.example.com/api/email/detail" \
-H "Content-Type: application/json" \
-b cookies.txt \
-H "X-CSRF-Token: CSRF_TOKEN" \
-H "Origin: https://mail.example.com" \
-d '{
"id": 1
}'
```
### 返回示例
```json
{
"errorNo": 0,
"errorMsg": "",
"data": {
"id": 1,
"type": 0,
"subject": "邮件标题",
"reply_to": "",
"from_name": "发件人",
"from_address": "sender@example.com",
"to": "[{\"Name\":\"收件人\",\"EmailAddress\":\"admin@example.com\"}]",
"bcc": "[]",
"cc": "[]",
"sender": "{\"Name\":\"发件人\",\"EmailAddress\":\"sender@example.com\"}",
"spf_check": 1,
"dkim_check": 1,
"status": 1,
"send_time": "2026-08-27 08:44:00",
"send_date": "2026-08-27 08:44:00",
"update_time": "2026-08-27 08:44:00",
"create_time": "2026-08-27 08:44:00",
"text": "纯文本正文",
"html": "<div>HTML正文</div>",
"error": "",
"attachments": [
{
"Filename": "report.pdf",
"ContentType": "application/pdf",
"Index": 0
}
]
}
}
```
### 与原版差异
原版文档使用 `/api/detail`。当前该路径没有注册,必须改为 `/api/email/detail`。邮件详情请求会标记邮件为已读,因此调用该接口不是纯读取操作;使用 `POST` 也符合当前路由约束。
## 3.6 发送邮件
### 接口信息
| 项目 | 内容 |
|---|---|
| 路径 | `/api/email/send` |
| 方法 | `POST` |
| 是否需要登录 | 是 |
| 请求格式 | `application/json` |
| 返回类型 | JSON |
| 请求体上限 | 40 MiB |
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `reply_to` | array | 否 | 回复地址列表,每项包含 `name`、`email` |
| `from` | object | 否 | 发件人,包含 `name`、`email`;邮箱为空时使用当前账号地址 |
| `to` | array | 是 | 收件人列表,每项包含 `name`、`email` |
| `bcc` | array | 否 | 密送列表 |
| `cc` | array | 否 | 抄送列表 |
| `subject` | string | 是 | 邮件标题 |
| `text` | string | 否 | 纯文本正文 |
| `html` | string | 否 | HTML 正文 |
| `sender` | object | 否 | SMTP Envelope Sender;为空时使用 `from` |
| `read_receipt` | array | 否 | 已读回执地址列表 |
| `attrs` | array | 否 | 附件列表,每项包含 `name`、`data` |
`attrs[].data` 必须是 Data URL 格式的 Base64 数据,例如:
```text
data:application/pdf;base64,BASE64_DATA
```
### 调用示例
```bash
curl -X POST "https://mail.example.com/api/email/send" \
-H "Content-Type: application/json" \
-b cookies.txt \
-H "X-CSRF-Token: CSRF_TOKEN" \
-H "Origin: https://mail.example.com" \
-d '{
"from": {
"name": "管理员",
"email": "admin@example.com"
},
"to": [
{
"name": "收件人",
"email": "user@example.com"
}
],
"cc": [],
"bcc": [],
"subject": "测试邮件",
"text": "这是纯文本正文。",
"html": "<p>这是 HTML 正文。</p>",
"attrs": []
}'
```
### 成功返回
```json
{
"errorNo": 0,
"errorMsg": "",
"data": "操作成功"
}
```
接口返回成功表示邮件已经通过参数校验并创建发送任务。实际投递结果可能在异步处理后才确定,失败信息会写入邮件状态,调用方应通过邮件列表或详情接口检查最终状态。
### 当前限制
- `to` 至少包含一个收件人;
- `to`、`cc`、`bcc`、`reply_to` 合计最多 `100` 个地址;
- 附件最多 `20` 个;
- 单个附件最大 `10 MiB`;
- 附件总大小最大 `25 MiB`;
- 邮件正文最大约 `8 MiB`;
- `subject` 必填;
- 普通用户不能伪造未授权的内部发件人地址;
- 附件文件名会进行路径和控制字符清理。
## 3.7 修改当前账号密码
### 接口信息
| 项目 | 内容 |
|---|---|
| 原版路径 | `/api/settings/modify_password` |
| 当前路径 | `/api/settings/modify_password` |
| 方法 | `POST` |
| 是否需要登录 | 是 |
| 请求格式 | `application/json` |
| 返回类型 | JSON |
### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `current_password` | string | 是 | 当前密码 |
| `password` | string | 是 | 新密码,长度为 8 到 256 字节 |
### 调用示例
```bash
curl -X POST "https://mail.example.com/api/settings/modify_password" \
-H "Content-Type: application/json" \
-b cookies.txt \
-H "X-CSRF-Token: CSRF_TOKEN" \
-H "Origin: https://mail.example.com" \
-d '{
"current_password": "old-password",
"password": "new-password"
}'
```
### 成功返回
```json
{
"errorNo": 0,
"errorMsg": "",
"data": "操作成功"
}
```
修改成功后,当前账号的安全凭据和登录会话会按服务端策略刷新或撤销。调用方如果收到会话失效,应重新登录并重新获取 CSRF Token。
### 与原版差异
原版示例中的 `passowrd` 是拼写错误,当前代码使用正确字段 `password`。当前接口还要求提供 `current_password`,不能只提交新密码。
## 3.8 内联获取附件内容
### 接口信息
| 项目 | 内容 |
|---|---|
| 原版路径 | `/attachments/` |
| 当前路径 | `/attachments/{邮件ID}/{内容标识}` |
| 方法 | `GET` |
| 原版方法 | 文档写成 `POST`,但请求示例使用 `GET` |
| 是否需要登录 | 是 |
| 返回类型 | 附件原始内容 |
其中:
- `{邮件ID}` 是邮件 ID;
- `{内容标识}` 是邮件详情中附件的内容标识,通常对应内联资源的 Content-ID;
- 当前处理器会按路径读取这两个值,并检查当前用户是否有权访问该邮件附件。
### 调用示例
```bash
curl -X GET "https://mail.example.com/attachments/1/image001.png" \
-b cookies.txt \
-o image001.png
```
### 返回说明
成功时返回附件原始字节,并设置对应的 `Content-Type`,例如:
```http
Content-Type: image/png
```
失败时返回统一 JSON 错误响应。常见原因包括路径格式错误、附件不存在、附件不可用或当前用户无权访问。
## 3.9 下载附件
### 接口信息
| 项目 | 内容 |
|---|---|
| 原版路径 | `/attachments/download/` |
| 当前路径 | `/attachments/download/{邮件ID}/{附件序号}` |
| 方法 | `GET` |
| 是否需要登录 | 是 |
| 返回类型 | 文件流 |
其中 `{附件序号}` 是邮件详情 `attachments` 数组中的 `Index`,当前从 `0` 开始,不是附件数据库 ID。
### 调用示例
```bash
curl -X GET "https://mail.example.com/attachments/download/1/0" \
-b cookies.txt \
-o report.pdf
```
### 返回响应头
成功时服务端返回:
```http
Content-Type: application/octet-stream
X-Content-Type-Options: nosniff
Content-Disposition: attachment; filename="attachment"; filename*=UTF-8''report.pdf
```
服务端会清理文件名中的路径分隔符、换行符和控制字符,避免下载文件名被用于路径穿越或响应头注入。
## 4. 当前新增安全和账户接口
这些接口不在原版 PMail 的 9 个基础接口中,但已经注册在当前 Smail 路由中。它们属于当前前端登录、安全设置和密码恢复流程的一部分。
### 4.1 登录和恢复流程
| 方法 | 路径 | 是否公开 | 说明 |
|---|---|---:|---|
| `POST` | `/api/security/verification/request` | 否 | 请求验证信息 |
| `POST` | `/api/security/verification/verify` | 否 | 校验验证信息 |
| `POST` | `/api/security/login/verify` | 是 | 完成登录二次验证 |
| `POST` | `/api/security/recovery/begin` | 是 | 开始密码恢复流程 |
| `POST` | `/api/security/recovery/verify` | 是 | 校验密码恢复凭据 |
| `POST` | `/api/security/recovery/complete` | 是 | 完成密码恢复 |
| `POST` | `/api/security/recovery/webauthn/begin` | 是 | 开始 WebAuthn 恢复验证 |
| `POST` | `/api/security/recovery/webauthn/verify` | 是 | 完成 WebAuthn 恢复验证 |
| `POST` | `/api/logout` | 否 | 退出当前会话 |
### 4.2 WebAuthn 和恢复码
| 方法 | 路径 | 说明 |
|---|---|---|
| `POST` | `/api/security/webauthn/registration/begin` | 开始注册安全密钥 |
| `POST` | `/api/security/webauthn/registration/finish` | 完成注册安全密钥 |
| `GET` | `/api/security/webauthn/credentials` | 查询当前账号安全密钥 |
| `POST` | `/api/security/webauthn/credentials/rename` | 重命名安全密钥 |
| `POST` | `/api/security/webauthn/credentials/revoke` | 撤销安全密钥 |
| `POST` | `/api/security/webauthn/login/begin` | 开始登录安全密钥验证 |
| `POST` | `/api/security/webauthn/login/finish` | 完成登录安全密钥验证 |
| `GET` | `/api/security/credentials/recovery-codes` | 查询恢复码状态,不返回历史恢复码原文 |
| `POST` | `/api/security/credentials/recovery-codes/generate` | 生成新的恢复码 |
| `POST` | `/api/security/credentials/recovery-codes/revoke` | 撤销恢复码 |
| 未限制 | `/api/security/credentials/question` | 密保问题相关流程入口,实际方法由处理器判断 |
### 4.3 外部安全邮箱
| 方法 | 路径 | 说明 |
|---|---|---|
| `GET` | `/api/security/external-email/list` | 获取当前账号外部安全邮箱状态 |
| `POST` | `/api/security/external-email/bind` | 绑定外部安全邮箱 |
| `POST` | `/api/security/external-email/verify` | 验证外部安全邮箱 |
| `POST` | `/api/security/external-email/disable` | 禁用外部安全邮箱 |
以上安全接口的请求参数和恢复状态机依赖当前安全设计,不应按照原版 PMail 的 `token` 模式调用。
## 5. 当前邮件和账户管理接口补充
| 方法 | 路径 | 说明 |
|---|---|---|
| `POST` | `/api/email/del` | 删除邮件 |
| `POST` | `/api/email/read` | 标记邮件已读 |
| `POST` | `/api/email/move` | 移动邮件到分组 |
| `POST` | `/api/rule/get` | 获取邮件规则 |
| `POST` | `/api/rule/add` | 新增邮件规则 |
| `POST` | `/api/rule/update` | 更新邮件规则 |
| `POST` | `/api/rule/del` | 删除邮件规则 |
| `POST` | `/api/user/create` | 创建用户,权限由当前角色控制 |
| `POST` | `/api/user/edit` | 编辑用户 |
| `POST` | `/api/user/info` | 获取用户信息 |
| `POST` | `/api/user/list` | 获取用户列表 |
| `GET` | `/api/plugin/list` | 获取插件列表 |
| `GET`、`POST` | `/api/plugin/settings/{插件名}/{操作}` | 读取或保存插件设置 |
配置管理接口使用 `/api/v1/config/` 和 `/api/v1/audits/config` 路径,属于新的配置系统,不属于原版 PMail 基础接口;调用时需要按当前配置授权、审计和 CSRF 规则处理。
## 6. 原版接口迁移清单
使用原版 PMail 客户端迁移到当前 Smail 时,至少需要修改以下内容:
| 原版调用 | 当前调用 |
|---|---|
| `POST /api/detail` | `POST /api/email/detail` |
| `POST /api/group` | `GET /api/group` |
| `POST /attachments/{邮件ID}/{附件标识}` | `GET /attachments/{邮件ID}/{附件标识}` |
| 仅提交 `password` 修改密码 | 同时提交 `current_password` 和 `password` |
| 使用 `token` 请求头认证 | 保存登录会话 Cookie,并在状态修改请求中提交 `X-CSRF-Token` |
| 将 `/api/ping` 当作纯文本读取 | 解析 JSON,并读取 `data` 中的 `pong` |
## 7. 代码核对位置
本次对照使用以下当前代码位置作为实现依据:
- 路由注册:`server/listen/http_server/http_server.go`
- 请求上下文、登录态和 CSRF 校验:`server/listen/http_server/https_server.go`
- 登录:`server/controllers/login.go`
- 分组:`server/controllers/group.go`
- 密码修改:`server/controllers/settings.go`
- 附件:`server/controllers/attachments.go`
- 邮件列表:`server/controllers/email/list.go`
- 邮件详情:`server/controllers/email/detail.go`
- 邮件发送:`server/controllers/email/send.go`
- 统一 JSON 响应:`server/dto/response/response.go`
原版对照来源为 PMail Wiki 的《后端接口文档》。原版文档仅作为历史兼容性参考,接口是否可调用以当前 Smail 路由和处理器实现为准。