13 KiB
WmailHub API 文档
适用版本:v2.9.0(版本号以 /api/health 返回为准)。Base URL:生产 https://wmailhub.adx9.com,本地开发 http://127.0.0.1:18787。
目录
- 认证方式
- 健康检查与探测
- 登录会话 API(/api/auth)
- ZITADEL 统一登录(/auth)
- Console Web API(/api/*,Cookie 会话)
- 开放 API(/api/v1/*,Bearer Key)
- MCP(/api/v1/mcp)
- Webhook(收件推送)
- 内部 API(/internal,Email Worker 专用)
- 错误码约定
认证方式
三套通道并存,按端点区分:
| 通道 | 适用端点 | 凭据 |
|---|---|---|
| Cookie 会话 | /api/auth/*、/api/*(Console Web)、/api/admin/*、/api/settings/*、/api/oauth/* |
登录后 wmh_session Cookie(HttpOnly,7 天) |
| Bearer Key | /api/v1/*(含 /api/v1/mcp) |
Authorization: Bearer wmh_…(32 位;API 与 MCP 共用同一 Key) |
| HMAC 签名 | /internal/* |
Email Worker 专用,见 docs/04 / docs/11 |
- Key 在 设置 → 全局 Webhook → API / MCP Key 生成/轮换/撤销;明文仅生成时返回一次,服务端只存 SHA-256;新 Key 生成后旧 Key 立即失效。
- Cookie 会话的写请求有 CSRF 防护:带 Cookie 时
Origin/Referer必须与站点 Host 一致,否则 403。Bearer Key 调用不受影响。
健康检查与探测
GET /api/health
公开。返回 { ok: true, name: "WmailHub", version: "2.9.0" }。
GET /auth/status
公开。统一登录探测:返回 { enabled: boolean, issuer: string | null },登录页据此显示「ZITADEL 统一登录」按钮。
登录会话 API(/api/auth)
POST /api/auth/login
{ "username": "admin", "password": "admin" }
成功 200 { ok: true, user: { id, username } } 并下发会话 Cookie;失败 401 {"error":"invalid username or password"}。默认账号 admin / admin(首次启动自动播种)。
POST /api/auth/logout
清除会话 Cookie,返回 { ok: true }。(ZITADEL 登录的用户同样只清本地会话,IdP 侧 SSO 会话保留。)
GET /api/auth/me
返回当前用户 { user: { id, username }, now };未登录 401。用户名以库内实时数据为准。
ZITADEL 统一登录(/auth)
OIDC Authorization Code + PKCE(无 client secret),与密码登录双通道并存;登录成功即管理员(本平台没有权限设置)。配置入口:设置 → ZITADEL 统一登录(issuer + client_id;回调地址 {origin}/auth/callback 需登记到 ZITADEL 应用)。详细设计见 docs/12。
| 端点 | 说明 |
|---|---|
GET /auth/login |
发起授权,302 到 IdP;可选 ?return=/<站内路径> 指定回跳(默认 /)。未启用 302 → /login?error=oidc_disabled |
GET /auth/callback |
IdP 回调(浏览器重定向,无需调用):一次性 state 校验 → 换码 → userinfo → 建号/匹配 → 下发会话 Cookie → 302 回站内;失败 302 → /login?error=<code> |
错误码:oidc_disabled / oidc_unavailable / state_invalid / oidc_exchange_failed / oidc_not_linked(JIT 关闭且身份未绑定)。
Console Web API(/api/*,Cookie 会话)
以下端点未登录一律 401。分页参数统一 limit(默认 20,最大 100)与 offset,列表返回 { items, total }。
Cloudflare 账号 /api/accounts
| 方法与路径 | 说明 |
|---|---|
GET /api/accounts |
账号列表 |
GET /api/accounts/:id |
账号详情(接入步骤、OAuth 连接、域名、Worker 凭据) |
PATCH /api/accounts/:id |
改名 { name } |
DELETE /api/accounts/:id |
删除账号(级联清理 Cloudflare 侧 Email Routing/Worker,尽力而为) |
POST /api/accounts/batch-delete |
批量删除 { ids: string[] } → { ok, deleted, failed[] } |
POST /api/accounts/token |
API Token 接入 { name, token }(后端先探 Cloudflare 再接入)→ { ok, account_id } |
POST /api/accounts/:id/rotate |
轮换 Worker 凭据;secret 仅本次返回,已配域名自动重部署 |
GET /api/accounts/:id/worker-secret |
本地/运维:读取当前 Worker credential |
域名 /api/domains
| 方法与路径 | 说明 |
|---|---|
GET /api/domains |
域名列表(含各域名 Webhook 配置与邮箱计数) |
GET /api/domains/filters/accounts |
账号筛选项 |
POST /api/domains/:id/setup |
配置 Email Workers(部署 Worker + 开 Routing + catch-all)→ { ok, error? } |
PUT /api/domains/:id/webhook |
域名级 Webhook { url, headers?, key? }(url 空串清除;key undefined 保留、空格清除) |
DELETE /api/domains/:id/webhook |
清除域名级 Webhook |
DELETE /api/domains/:id / POST /api/domains/batch-delete |
删除(级联邮箱与邮件) |
邮箱 /api/mailboxes
| 方法与路径 | 说明 |
|---|---|
GET /api/mailboxes?domain_id= |
邮箱列表 |
POST /api/mailboxes |
创建 { domain_id, address, retention_seconds?, catch_all? } → { ok, id, address };重名 409 |
PATCH /api/mailboxes/:id |
{ status?: "active"|"disabled", retention_seconds?: number|null }(null=回到全局默认 86400 秒) |
PUT /api/mailboxes/:id/webhook |
邮箱级 Webhook,语义同域名级 |
DELETE /api/mailboxes/:id/webhook |
清除邮箱级 Webhook |
DELETE /api/mailboxes/:id / POST /api/mailboxes/batch-delete |
删除 |
Webhook 生效顺序:邮箱 → 域名 → 全局,取第一个已配置的。
邮件 /api/emails
| 方法与路径 | 说明 |
|---|---|
GET /api/emails?domain_id=&mailbox_id=&q=&limit=&offset= |
列表(仅未过期) |
GET /api/emails/:id |
详情(正文 text/html/markdown + 附件列表);过期 410 |
GET /api/emails/:id/attachments/:attId |
下载附件 |
GET /api/emails/:id/raw |
原始 EML |
DELETE /api/emails/:id / POST /api/emails/batch-delete |
删除 |
POST /api/emails/retain |
保留 { ids }(永不过期,Cron 跳过)→ { ok, retained } |
操作日志 /api/logs
GET /api/logs?target_type=&action=&q=&limit=&offset=。target_type ∈ account/domain/mailbox/email/webhook/user/settings;库里最多保留 10000 条。
设置 /api/settings
| 方法与路径 | 说明 |
|---|---|
GET /api/settings |
全量读取:当前用户名、全局 Webhook(key 只回 last4)、API Key 元信息、Cloudflare OAuth、ZITADEL 统一登录(含回调地址与当前账号绑定身份) |
PUT /api/settings |
保存 { webhook?, cf_oauth?, oidc? };webhook.key/cf_oauth.secret:undefined 保留、空串清除、非空设置;oidc: { issuer, client_id, enabled?, jit? }(开关缺省沿用已存值) |
PUT /api/settings/password |
改密码 { old_password, new_password }(≥6 位) |
PUT /api/settings/username |
改用户名 { new_username, password }(需当前密码;2-32 位字母/数字/_ . -;成功重签会话 Cookie;重名 409) |
POST /api/settings/api-key |
生成(轮换)API/MCP Key → 201 { key, created_at }(明文仅此一次) |
DELETE /api/settings/api-key |
撤销并删除 Key |
DELETE /api/settings/cf-oauth |
清除 Cloudflare OAuth 凭据(env 兜底仍可用) |
DELETE /api/settings/oidc |
清除统一登录配置(已建立的绑定保留) |
Cloudflare OAuth /api/oauth
| 方法与路径 | 说明 |
|---|---|
GET /api/oauth/mode |
`{ mode: "cloudflare" |
GET /api/oauth/cloudflare/start |
生成一次性 state,返回 { url, state }(浏览器跳 url 完成授权) |
GET /api/oauth/cloudflare/callback |
授权回调(浏览器重定向,无需调用) |
GET /api/oauth/cloudflare/preflight |
授权配置自检(探测 Cloudflare 授权端点 + scope 探测) |
GET /api/oauth/connections |
已建立的 OAuth 连接(不含 token) |
管理 /api/admin
GET /api/admin/version;GET /api/admin/stats(Dashboard 统计 + 最近 8 封邮件);POST /api/admin/cron/run(手动触发 TTL 清理与 Webhook 投递)。
开放 API(/api/v1/*,Bearer Key)
认证:Authorization: Bearer wmh_…;无 Key/Key 无效 401。所有邮件读取强制只返回未过期邮件。
GET /api/v1/accounts
{ items: [{ id, name, cloudflare_account_id, worker_id, status, created_at }] }
GET /api/v1/domains
{ items: [{ id, name, status, account_id, created_at }] }
GET /api/v1/mailboxes
{ items: [{ id, address, status, is_catch_all, retention_seconds, domain_id, domain_name }] }
POST /api/v1/mailboxes
{ "domain_id": "dom_xxx", "address": "hello", "retention_seconds": 86400, "webhook": "https://hooks.example.com/wmail" }
domain_id可省略(v2.8.0 起):随机分配一个已启用 Email Workers 的域名;此时address必须为本地部分(不带@)。webhook可选(v2.8.0 起):创建时直接配置邮箱级 Webhook(先校验再建邮箱,校验失败不留半截数据;SSRF 规则同 Console——拒绝私网/localhost/元数据地址)。- 返回
201 { ok: true, id, address, webhook? };错误:400 address required/404 domain not found/409 mailbox already exists/409 no available domain (none has Email Workers enabled)。
DELETE /api/v1/mailboxes/:id
成功 { ok: true };不存在 404。
GET /api/v1/emails?mailbox_id=&limit=&offset=
{ items: [{ id, mailbox_id, mailbox_address, message_id, from_address, to_addresses, subject, size, received_at, expires_at }] }(仅未过期)。
GET /api/v1/emails/:id
{ email: { …全字段含 text_body/html_body/markdown_body }, attachments: [{ id, filename, content_type, size, content_id }] };不存在 404,已过期 410 {"error":"email expired"}。
curl 示例
KEY="wmh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
BASE="https://wmailhub.adx9.com"
# 随机域名建邮箱 + 邮箱级 Webhook
curl -s -X POST "$BASE/api/v1/mailboxes" \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"address":"hello","webhook":"https://hooks.example.com/wmail"}'
# 拉最近邮件(取第一条详情)
curl -s -H "Authorization: Bearer $KEY" "$BASE/api/v1/emails?limit=1"
MCP(/api/v1/mcp)
Streamable HTTP(JSON-RPC 2.0,协议版本 2025-06-18),仅 POST(GET → 405),Bearer Key 与 API 共用。方法:initialize → notifications/initialized → tools/list / tools/call;ping 可用。
MCP 客户端配置:
{
"mcpServers": {
"wmailhub": {
"type": "http",
"url": "https://wmailhub.adx9.com/api/v1/mcp",
"headers": { "Authorization": "Bearer wmh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}
工具清单(v2.9.0 共 6 个)
| 工具 | 参数 | 说明 |
|---|---|---|
list_domains |
— | 列出可用域名(创建邮箱的 domain_id 由此获取) |
list_mailboxes |
— | 列出邮箱(含域名与保留策略) |
list_emails |
mailbox_id?, limit? |
列出未过期邮件(按收到时间倒序) |
get_email |
email_id |
邮件详情(正文 + 附件);过期返回资源错误 -32002 |
create_mailbox |
address, domain_id?, retention_seconds?, webhook? |
创建邮箱;domain_id 省略时随机分配(此时 address 须为本地部分);webhook 为可选邮箱级 Webhook URL |
delete_mailbox |
mailbox_id |
删除邮箱 |
错误约定:-32601 未知方法/工具、-32602 参数错误、-32002 资源不存在(如邮件过期)。
Webhook(收件推送)
收到邮件时按 邮箱 → 域名 → 全局 取第一个已配置的 Webhook 入队投递。Payload:
{
"event": "email.received",
"id": "msg_xxx",
"mailbox": "hello@example.com",
"from": "user@example.org",
"to": ["hello@example.com"],
"subject": "Hello",
"text": "Hello World",
"markdown": "Hello **World**",
"received_at": "2026-09-30T10:00:00Z"
}
请求头:X-WmailHub-Event、X-WmailHub-Delivery、X-WmailHub-Timestamp、X-WmailHub-Signature(签名 = HMAC-SHA256(key, "<timestamp>.<body>"),配了 key 才有);配置 key 时默认携带 Authorization: Bearer <key>,自定义头优先。2xx 为成功;5xx/超时按指数退避重试,耗尽后置 exhausted;Cron 周期性重投 pending/failed。
内部 API(/internal,Email Worker 专用)
POST /internal/email/authorize 与 POST /internal/email/ingest:Email Worker 取收件凭据与投递邮件,HMAC 签名认证,协议见 docs/04 与 docs/11,不对外暴露。
错误码约定
| 状态码 | 场景 |
|---|---|
| 400 | 参数缺失/非法(webhook URL 不合法、用户名格式错误等) |
| 401 | 未登录 / Bearer Key 缺失或无效 |
| 403 | CSRF 校验失败(带 Cookie 的跨源写请求) |
| 404 | 资源不存在 |
| 409 | 冲突(邮箱已存在、无可用域名、用户名已存在) |
| 410 | 邮件已过期(Gone) |
| 503 | 依赖未配置(如 Cloudflare OAuth 未配置时添加账号) |
错误响应统一为 {"error": "<中文或英文消息>"}。