diff --git a/README.md b/README.md index 89e3392..592399c 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,40 @@ Scert/ --- +--- + +## 📖 核心 API 规范与接口说明 + +Scert 遵循 RESTful 设计标准,前后端交互采用 JSON 格式,凭据与敏感字段全量脱敏入库,写操作经过严格的同源保护(Same-Origin Check)。 + +详细接口契约、参数定义、权限约束及返回示例参见文档:[《Scert 后端接口说明文档》(doc/后端接口说明文档.md)](doc/后端接口说明文档.md)。 + +### 主要 API 模块概览 + +1. **系统初始化与状态就绪 (Bootstrap API)**: + - GET /readyz / GET /healthz:系统运行探针与服务健康度检查。 + - GET /api/v1/setup/status:获取当前向导初始化进度及配置项状态。 + - POST /api/v1/setup:提交并初始化超级管理员账号、数据库连接及系统配置。 +2. **认证与当前会话 (Auth & Session API)**: + - POST /api/v1/auth/login / POST /api/v1/auth/logout:用户登录与安全登出。 + - GET /api/v1/user/info:获取当前登录用户基本信息、所属项目与角色权限。 +3. **用户与权限控制 (User Management API)**: + - 支持超级管理员对平台成员进行增删改查、状态启停(启用/禁用)及角色授权。 +4. **项目与多租户协作 (Project Management API)**: + - 支持项目资源隔离(项目资产独享)、成员角色(项目管理员、成员)细粒度分配。 +5. **托管域名与证书生命周期 (Domains & Certificates API)**: + - 域名的添加、修改、删除、泛域名/多级子域名支持。 + - 证书自动化申请 (issue)、在线校验 ( alidate)、手动导入 (import)、历史版本查看与回滚 (history/rollback)。 +6. **远程部署与目标服务器 (Servers & Deployments API)**: + - SSH/SFTP 目标服务器纳管、主机指纹核验与免密连接。 + - 证书远程下发、Nginx 重载与原子回滚保护。 +7. **DNS 账户与凭据配置 (DNS Accounts API)**: + - 多云厂商(阿里云、腾讯云等)DNS AccessKey 统一安全存储与项目归属绑定。 +8. **异步任务与审计日志 (Tasks & Audit Logs API)**: + - 全局异步任务轮询、日志查看、执行明细报告与终止操作。 +9. **系统全局设置 (System Settings API)**: + - 全局 SMTP 邮件通知配置、邮件模板测试及证书续期轮询周期策略配置。 + ## 🛠️ 本地开发与构建 ### 1. 环境准备 diff --git a/doc/后端接口说明文档.md b/doc/后端接口说明文档.md index 23c5591..f29df65 100644 --- a/doc/后端接口说明文档.md +++ b/doc/后端接口说明文档.md @@ -1,770 +1,174 @@ -# 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": "
HTML正文
", - "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": "

这是 HTML 正文。

", - "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 路由和处理器实现为准。 \ No newline at end of file +# Scert 后端接口说明文档 + +> 本文档用于完整说明 Scert 证书管理系统当前所有 HTTP API 路由、请求参数、返回契约及权限约束。 +> 核对基准:当前代码分支 main(版本: 1.52.5)。 + +--- + +## 1. 架构原则与通用调用约定 + +### 1.1 基础地址与反向代理 +- 生产环境默认管理地址:https://odacm.cn +- 服务默认本地监听:127.0.0.1:8080(通过 Nginx 进行 TLS 终止及反向代理) + +### 1.2 认证模式与安全边界 +1. **会话认证(Session-Based)**: + - 登录成功后通过 Set-Cookie 写入 HttpOnly 会话凭据 scert_session。 + - 所有写入请求(POST / PUT / DELETE)均经过同源保护检查(Same-Origin Check),严格校验 Origin 与 Referer。 +2. **三级权限控制**: + - **普通用户(user)**:仅可访问被授权参与的项目、查看项目内域名/服务器/证书、发起证书申请与部署。 + - **管理员(admin)**:具备其管理分组/项目范围内的成员管理、项目资源调配权限,可查看全局只读统计。 + - **超级管理员(super_admin)**:拥有全局最高权限,唯一具备系统配置(SMTP、默认 DNS 凭据、续期策略)、用户账号增删改查权限。 +3. **敏感凭据脱敏**: + - DNS AccessKeySecret、SSH 登录密码等敏感数据入库加密,查询接口一律返回脱敏标识(如掩码或布尔指示),禁止明文泄露。 + +### 1.3 统一响应契约 +所有 JSON 接口统一遵循以下响应规范: +- 200 OK / 201 Created:请求成功处理。 +- 400 Bad Request:参数校验未通过或业务前置条件不满足。 +- 401 Unauthorized:未登录或会话已过期。 +- 403 Forbidden:权限不足(如非超管访问系统设置)。 +- 404 Not Found:请求资源不存在。 +- 409 Conflict:资源冲突(如域名/账号已存在)。 +- 500 Internal Server Error:服务端内部异常。 + +--- + +## 2. 系统初始化接口 (Bootstrap API) + +在系统首次启动且未完成向导配置时生效,初始化完成后自动关闭。 + +| 方法 | 路径 | 权限要求 | 说明 | +| :--- | :--- | :--- | :--- | +| GET | /readyz | 公开 | 就绪状态探针,返回服务初始化状态及就绪信息 | +| GET | /healthz | 公开 | 健康检查探针 | +| GET | /api/v1/setup/status | 公开 | 获取当前向导初始化进度、数据库驱动及支持清单 | +| GET | /api/v1/setup/dns/providers | 公开 | 获取支持的 DNS 服务商列表(如 Aliyun) | +| POST | /api/v1/setup/dns/test | 初始化临时会话 | 测试 DNS 服务商凭据连通性与 API 权限 | +| POST | /api/v1/setup | 初始化临时会话 | 提交并应用系统首次初始化配置(超管、数据库、凭据) | + +--- + +## 3. 认证与会话管理 (Auth API) + +| 方法 | 路径 | 权限要求 | 说明 | +| :--- | :--- | :--- | :--- | +| GET | /api/v1/status | 公开 | 获取系统运行时状态与版本号(如 v1.52.5) | +| GET | /api/v1/auth/session | 公开 | 获取当前会话状态及登录用户信息(账号、角色等) | +| POST | /api/v1/auth/login | 公开 (需同源) | 账号密码登录,成功后写入会话 Cookie | +| POST | /api/v1/auth/logout | 已登录 (需同源) | 退出登录并销毁当前服务端会话与 Cookie | +| POST | /api/v1/auth/password | 已登录 | 修改当前已登录账号的登录密码 | +| POST | /api/v1/password-reset/resolve | 公开 | 密码重置指引接口(本地 IPC 引导保护) | + +--- + +## 4. 用户与权限管理 (Users API) + +仅超级管理员具备用户完全管理权限;普通用户与管理员受限于其组织权限。 + +| 方法 | 路径 | 权限要求 | 说明 | +| :--- | :--- | :--- | :--- | +| GET | /api/v1/users | 已登录 | 获取系统用户列表(普通用户受限查看) | +| POST | /api/v1/users | 超级管理员 | 创建新用户(支持设置账号名、密码、邮箱、初始角色) | +| GET | /api/v1/users/:id | 已登录 | 获取指定用户的详细资料 | +| PUT | /api/v1/users/:id | 超级管理员 | 更新指定用户的账号状态、所属角色或重置密码 | +| DELETE | /api/v1/users/:id | 超级管理员 | 删除指定用户(主超级管理员不可删除) | + +--- + +## 5. 项目与成员协作 (Project Management API) + +支持多项目隔离管理,项目可独立绑定成员、域名、DNS 凭据和部署服务器。 + +| 方法 | 路径 | 权限要求 | 说明 | +| :--- | :--- | :--- | :--- | +| GET | /api/v1/projects | 已登录 | 获取当前账号可见的项目列表(分页) | +| POST | /api/v1/projects | 超管 / 权限账户 | 创建新项目(填写项目名称、项目说明) | +| PUT | /api/v1/projects/:projectID | 项目管理员 | 修改项目资料(更新名称与说明) | +| DELETE | /api/v1/projects/:projectID | 项目管理员 | 删除项目(默认项目不可删除,有资产关联时不可删除) | +| GET | /api/v1/projects/:projectID/members | 项目成员 | 查询指定项目的成员列表及被赋予的项目角色 | +| POST | /api/v1/projects/:projectID/members | 项目管理员 | 为项目成员授予角色(项目管理员、普通操作者、只读用户) | +| DELETE | /api/v1/projects/:projectID/members/:userID/roles/:role | 项目管理员 | 撤销成员在项目中的指定角色 | +| GET | /api/v1/audit-logs | 已登录 | 查询当前项目与全局的操作审计流水 | + +--- + +## 6. 托管域名与证书生命周期管理 (Domains & Certificate API) + +| 方法 | 路径 | 权限要求 | 说明 | +| :--- | :--- | :--- | :--- | +| GET | /api/v1/domains | 项目授权 | 获取当前上下文下的托管域名列表 | +| POST | /api/v1/domains | 项目授权 | 添加托管域名(支持单域名、泛域名、多级子域名配置) | +| PUT | /api/v1/domains/:id | 项目授权 | 修改托管域名参数(环境、自动续期开关、部署配置) | +| DELETE | /api/v1/domains/:id | 项目授权 | 删除托管域名配置 | +| POST | /api/v1/domains/:id/issue | 项目授权 | 触发申请/签发 Let's Encrypt 证书任务 | +| POST | /api/v1/domains/:id/deploy | 项目授权 | 手动触发证书向关联部署服务器的推送部署任务 | +| POST | /api/v1/domains/:id/certificate/validate | 项目授权 | 手动触发针对已部署证书与 HTTPS 连通性的端到端校验 | +| POST | /api/v1/domains/:id/certificate/import | 项目授权 | 手工导入外部证书文件(证书 fullchain、私钥 key、中间证书) | +| GET | /api/v1/domains/:id/certificate/versions | 项目授权 | 获取该域名历史证书版本归档记录 | +| POST | /api/v1/domains/:id/certificate/versions/:version/restore | 项目授权 | 回滚恢复并激活指定的历史证书版本 | +| GET | /api/v1/certificates | 项目授权 | 查询系统当前所有活跃证书基本信息与到期日 | +| GET | /api/v1/certificates/:id | 项目授权 | 查看指定证书详情(SAN 列表、签发机构、有效期、指纹) | + +--- + +## 7. 部署服务器与绑定管理 (Servers API) + +| 方法 | 路径 | 权限要求 | 说明 | +| :--- | :--- | :--- | :--- | +| GET | /api/v1/servers | 项目授权 | 获取可用的远程部署服务器列表 | +| POST | /api/v1/servers | 项目授权 | 添加新的部署服务器(IP/主机名、SSH 端口、认证方式、证书路径) | +| GET | /api/v1/servers/:id | 项目授权 | 查看指定部署服务器详细信息 | +| PUT | /api/v1/servers/:id | 项目授权 | 更新部署服务器配置(Web 服务类型、重载命令等) | +| DELETE | /api/v1/servers/:id | 项目授权 | 删除部署服务器(清理关联绑定) | +| GET | /api/v1/domains/:id/servers | 项目授权 | 获取指定域名已绑定的目标部署服务器列表 | +| PUT | /api/v1/domains/:id/servers | 项目授权 | 批量更新并替换指定域名关联的部署服务器列表 | +| GET | /api/v1/servers/:id/domains | 项目授权 | 查询特定服务器关联的所有托管域名 | + +--- + +## 8. DNS 账户与提供商管理 (DNS Accounts API) + +| 方法 | 路径 | 权限要求 | 说明 | +| :--- | :--- | :--- | :--- | +| GET | /api/v1/dns/providers | 已登录 | 获取系统支持的 DNS 服务商驱动列表 | +| GET | /api/v1/dns/accounts | 项目授权 | 获取可见的已配置 DNS 账户列表(脱敏) | +| POST | /api/v1/dns/accounts | 项目授权 | 新建 DNS 账户(填入提供商、AccessKeyId、AccessKeySecret、归属项目) | +| GET | /api/v1/dns/accounts/:id | 项目授权 | 查看特定 DNS 账户详细信息与校验状态 | +| PUT | /api/v1/dns/accounts/:id | 项目授权 | 重命名或修改 DNS 账户基础信息(包括调整归属项目) | +| PUT | /api/v1/dns/accounts/:id/status | 项目授权 | 启用或停用指定的 DNS 账户 | +| PUT | /api/v1/dns/accounts/:id/credentials | 项目授权 | 轮换/更新 DNS 账户的 API 访问凭据(Secret) | +| POST | /api/v1/dns/accounts/:id/verify | 项目授权 | 在线测试并校验 DNS 账户凭据在服务商处的 API 鉴权有效性 | +| GET | /api/v1/domains/:id/dns-account | 项目授权 | 查询特定域名当前绑定的专用 DNS 账户 | +| PUT | /api/v1/domains/:id/dns-account | 项目授权 | 为特定域名指定或切换所使用的 DNS 账户 | + +--- + +## 9. 异步任务与执行审计 (Tasks API) + +证书的签发、远程推送部署、回环 HTTPS 校验均为异步任务执行引擎驱动。 + +| 方法 | 路径 | 权限要求 | 说明 | +| :--- | :--- | :--- | :--- | +| GET | /api/v1/tasks | 项目授权 | 获取异步任务列表(支持按域名、任务类型、状态过滤) | +| GET | /api/v1/tasks/:id | 项目授权 | 获取指定任务的当前阶段、执行状态、结果消息 | +| DELETE | /api/v1/tasks/:id | 项目授权 | 取消处于等待中(pending)或运行中的任务 | +| POST | /api/v1/tasks/:id/retry | 项目授权 | 对执行失败(failed)的任务发起重新执行 | +| GET | /api/v1/tasks/:id/attempts | 项目授权 | 查询该任务的所有执行尝试流水记录与重试日志 | +| GET | /api/v1/tasks/:id/deployment-results| 项目授权 | 查询部署任务针对各台目标服务器的详细推送结果与执行状态 | +| GET | /api/v1/tasks/:id/validation-results| 项目授权 | 查询证书校验任务对各节点的 HTTPS 握手与证书链详细诊断数据 | + +--- + +## 10. 系统全局设置与超级管理 (Settings & Admin API) + +全部接口仅限超级管理员角色访问。 + +| 方法 | 路径 | 权限要求 | 说明 | +| :--- | :--- | :--- | :--- | +| GET | /api/v1/settings | 超级管理员 | 获取系统全局配置(邮件通知设置、默认 DNS、证书续期策略) | +| PUT | /api/v1/settings | 超级管理员 | 保存/更新系统配置(分项独立更新) | +| POST | /api/v1/dns/test | 超级管理员 | 验证系统默认 DNS API 凭据连通性 | +| POST | /api/v1/notification/test | 超级管理员 | 实时测试 SMTP 邮件发信连通性,发送测试邮件验证配置 | +| POST | /api/v1/dns/aliyun/check | 超级管理员 | 深度校验阿里云 RAM 凭据对特定域名的操作授权(解析记录读写权) | +| POST | /api/v1/renewal/run | 超级管理员 | 立即手动触发全局证书自动续期巡检引擎 | diff --git a/frontend/src/views/Dashboard/DashboardLayout.css b/frontend/src/views/Dashboard/DashboardLayout.css index 37094f4..0419d41 100644 --- a/frontend/src/views/Dashboard/DashboardLayout.css +++ b/frontend/src/views/Dashboard/DashboardLayout.css @@ -51,6 +51,7 @@ } .dashboard-workspace { + box-sizing: border-box; display: grid; grid-template-columns: minmax(0, 1fr) 200px; grid-template-areas: "main submenu"; @@ -86,15 +87,20 @@ } .dashboard-content > .panel, +.dashboard-content .panel, .dashboard-content :deep(.user-management), .dashboard-content :deep(.project-management), -.dashboard-content :deep(.dns-accounts-panel) { +.dashboard-content :deep(.dns-accounts-panel), +.dashboard-content :deep(.panel) { + box-sizing: border-box; + width: 100%; margin: 0; min-height: 100%; padding: 24px; - border: none; - box-shadow: none; - border-radius: 0; + border: none !important; + box-shadow: none !important; + border-radius: 0 !important; + background: transparent; } .overview-section { diff --git a/frontend/src/views/ProjectManagement/ProjectManagement.css b/frontend/src/views/ProjectManagement/ProjectManagement.css index 0854109..5c51b71 100644 --- a/frontend/src/views/ProjectManagement/ProjectManagement.css +++ b/frontend/src/views/ProjectManagement/ProjectManagement.css @@ -1,10 +1,12 @@ .project-management { - padding: 24px; + box-sizing: border-box; + width: 100%; + padding: 0; color: #1e293b; - border: 1px solid #e2e8f0; - border-radius: 20px; - background: #ffffff; - box-shadow: 0 4px 20px -2px rgba(15, 23, 42, 0.05); + border: none; + border-radius: 0; + background: transparent; + box-shadow: none; } .project-heading { @@ -35,8 +37,9 @@ .project-layout { display: grid; grid-template-columns: 280px minmax(0, 1fr); - gap: 28px; - align-items: start; + gap: 24px; + align-items: stretch; + width: 100%; } .project-directory { @@ -45,8 +48,9 @@ padding: 18px; background: #f8fafc; border: 1px solid #e2e8f0; - border-radius: 16px; + border-radius: 14px; min-width: 0; + box-sizing: border-box; } .project-members {