v2.7.0: 顶栏新增文档入口,/docs 页提供 MCP 与 Console API(/api/v1)说明(认证、MCP 客户端配置与工具清单、REST 端点与 curl 示例)

This commit is contained in:
bigcode
2026-10-01 02:33:08 +08:00
parent c4b5e65159
commit fa0730c6dd
6 changed files with 216 additions and 1 deletions
+10
View File
@@ -11,6 +11,7 @@ import {
LogoutOutlined,
SettingOutlined,
HistoryOutlined,
BookOutlined,
} from "@ant-design/icons";
import { health, logout, me } from "./api.ts";
import { SettingsModal } from "./components/SettingsModal.tsx";
@@ -36,6 +37,7 @@ const PAGE_META: Record<string, { title: string; subtitle: string }> = {
"/mailboxes": { title: "邮箱", subtitle: "收件地址、保留策略与邮箱级 Webhook" },
"/inboxs": { title: "收件箱", subtitle: "已接收邮件(默认保留 1 天,可点保留永不过期),支持按域名筛选" },
"/logs": { title: "日志", subtitle: "账号 / 域名 / 邮箱 / Webhook 的操作记录(最多保留 10000 条)" },
"/docs": { title: "文档", subtitle: "MCP 与 Console API(/api/v1)使用说明" },
};
/** # 成员变量: 主题切换按钮的太阳/月亮 SVG 图标 **/
@@ -104,6 +106,14 @@ export function App() {
<div className="subtitle">{meta.subtitle}</div>
</div>
<div style={{ display: "flex", alignItems: "center", gap: 14 }}>
{/* 文档入口:MCP / API 说明页,位于主题切换左侧 */}
<Button
type="text"
aria-label="打开文档"
title="API 与 MCP 文档"
icon={<BookOutlined />}
onClick={() => navigate("/docs")}
/>
{/* 主题切换:亮↔暗,位于头像左侧 */}
<Button
type="text"
+2
View File
@@ -15,6 +15,7 @@ import { DomainsPage } from "./pages/Domains.tsx";
import { MailboxesPage } from "./pages/Mailboxes.tsx";
import { InboxPage } from "./pages/Inbox.tsx";
import { LogsPage } from "./pages/Logs.tsx";
import { DocsPage } from "./pages/Docs.tsx";
import { setUnauthorizedHandler } from "./api.ts";
/** # 成员变量: 路由表(邮件页路由 /inboxs,v2.1.0) **/
@@ -30,6 +31,7 @@ const router = createBrowserRouter([
{ path: "mailboxes", element: <MailboxesPage /> },
{ path: "inboxs", element: <InboxPage /> },
{ path: "logs", element: <LogsPage /> },
{ path: "docs", element: <DocsPage /> },
],
},
]);
+159
View File
@@ -0,0 +1,159 @@
/**
* 文档页(/docs,v2.7.0):MCP 与 Console API(/api/v1)的使用说明。
* 纯静态展示,接口地址按当前访问 origin 动态拼接;顶栏文档图标进入。
*/
import { Alert, Card, Space, Table, Tag, Typography } from "antd";
import { KeyOutlined, ApiOutlined, CodeOutlined } from "@ant-design/icons";
/** # 成员变量: 接口基地址与 MCP 端点(跟随当前访问域名) **/
const API_BASE = `${window.location.origin}/api/v1`;
const MCP_URL = `${window.location.origin}/api/v1/mcp`;
/** # 成员变量: MCP 工具清单(与 /api/v1/mcp tools/list 一致) **/
const MCP_TOOLS = [
{ name: "list_mailboxes", params: "—", desc: "列出所有邮箱(域名、catch-all 标记、保留秒数)" },
{ name: "list_emails", params: "mailbox_id? / limit?", desc: "列出未过期邮件,limit 默认 20、最大 100" },
{ name: "get_email", params: "id", desc: "单封邮件详情(正文 + 附件元数据);过期邮件报错" },
{ name: "create_mailbox", params: "domain_id / address / retention_seconds?", desc: "创建邮箱;address 可传本地部分或完整地址,保留默认 86400 秒" },
{ name: "delete_mailbox", params: "id", desc: "删除邮箱" },
];
/** # 成员变量: REST API 端点清单(与 routes/apiv1.ts 一致) **/
const API_ENDPOINTS = [
{ method: "GET", path: "/accounts", desc: "账号列表(不含已撤销)" },
{ method: "GET", path: "/domains", desc: "域名列表" },
{ method: "GET", path: "/mailboxes", desc: "邮箱列表" },
{ method: "POST", path: "/mailboxes", desc: "创建邮箱,body: { domain_id, address, retention_seconds? };409 已存在" },
{ method: "DELETE", path: "/mailboxes/:id", desc: "删除邮箱" },
{ method: "GET", path: "/emails", desc: "未过期邮件列表,query: mailbox_id? / limit / offset" },
{ method: "GET", path: "/emails/:id", desc: "邮件详情 + 附件元数据;410 已过期" },
];
/** # 函数
* 方法名 -> 彩色 Tag
*/
function MethodTag({ method }: { method: string }) {
const color = method === "GET" ? "blue" : method === "POST" ? "green" : "red";
return <Tag color={color} style={{ fontFamily: "ui-monospace, Menlo, monospace", minWidth: 56, textAlign: "center" }}>{method}</Tag>;
}
/** # 函数
* 文档页:认证说明 + MCP 接入(客户端配置、工具清单)+ REST API(端点清单、curl 示例)
*/
export function DocsPage() {
return (
<Space direction="vertical" size={16} style={{ width: "100%" }}>
<Card
title={
<span>
<KeyOutlined style={{ marginRight: 8 }} />
认证
</span>
}
>
<Typography.Paragraph>
Console API 与 MCP 共用同一个 <b>API/MCP Key</b>(<code className="mono">wmh_</code> 前缀):右上角头像 →{" "}
<b>设置</b> → <b>API / MCP Key</b> 生成。明文仅在生成时显示一次;生成新 Key 后旧 Key 立即失效。
</Typography.Paragraph>
<Typography.Paragraph>
所有请求携带请求头:<code className="mono">Authorization: Bearer wmh_xxxxxxxx</code>
</Typography.Paragraph>
</Card>
<Card
title={
<span>
<ApiOutlined style={{ marginRight: 8 }} />
MCP 接入(Model Context Protocol)
</span>
}
>
<Typography.Paragraph>
端点:<code className="mono">{MCP_URL}</code>(Streamable HTTP,JSON-RPC 2.0,协议版本 2025-06-18;仅接受
POST,无 SSE 流)
</Typography.Paragraph>
<Typography.Paragraph style={{ marginBottom: 8 }}>MCP 客户端配置示例:</Typography.Paragraph>
<pre className="docs-pre">{`{
"mcpServers": {
"wmailhub": {
"type": "http",
"url": "${MCP_URL}",
"headers": { "Authorization": "Bearer wmh_你的Key" }
}
}
}`}</pre>
<Table
size="small"
rowKey="name"
style={{ marginTop: 8 }}
pagination={false}
columns={[
{ title: "工具", dataIndex: "name", width: 160, render: (v: string) => <code className="mono">{v}</code> },
{ title: "参数", dataIndex: "params", width: 260, render: (v: string) => <code className="mono">{v}</code> },
{ title: "说明", dataIndex: "desc" },
]}
dataSource={MCP_TOOLS}
/>
</Card>
<Card
title={
<span>
<CodeOutlined style={{ marginRight: 8 }} />
REST API
</span>
}
>
<Typography.Paragraph>
基地址:<code className="mono">{API_BASE}</code>
</Typography.Paragraph>
<Table
size="small"
rowKey="path"
pagination={false}
columns={[
{
title: "方法",
dataIndex: "method",
width: 90,
render: (v: string) => <MethodTag method={v} />,
},
{
title: "路径",
dataIndex: "path",
width: 220,
render: (v: string) => <code className="mono">{v}</code>,
},
{ title: "说明", dataIndex: "desc" },
]}
dataSource={API_ENDPOINTS}
/>
<Typography.Paragraph style={{ margin: "16px 0 8px" }}>curl 示例:</Typography.Paragraph>
<pre className="docs-pre">{`# 列出邮箱
curl -H "Authorization: Bearer wmh_你的Key" ${API_BASE}/mailboxes
# 创建邮箱(保留 7 天)
curl -X POST -H "Authorization: Bearer wmh_你的Key" \\
-H "Content-Type: application/json" \\
-d '{"domain_id":"dom_xxx","address":"hello","retention_seconds":604800}' \\
${API_BASE}/mailboxes
# 最新 10 封未过期邮件
curl -H "Authorization: Bearer wmh_你的Key" "${API_BASE}/emails?limit=10"`}</pre>
</Card>
<Alert
type="info"
showIcon
message="备注"
description={
<ul style={{ margin: 0, paddingLeft: 18 }}>
<li>邮件读取强制检查未过期:列表不返回过期邮件,详情对已过期邮件返回 410 Gone。</li>
<li>邮箱保留时长默认 86400 秒(1 天);catch-all 邮箱地址形如 <code className="mono">*@域名</code>。</li>
<li>常见状态码:401 未认证(Key 缺失或已失效)、404 资源不存在、409 邮箱已存在、410 邮件已过期。</li>
</ul>
}
/>
</Space>
);
}
+19
View File
@@ -151,6 +151,25 @@ body,
font-size: 12.5px;
}
/* 文档页代码块:浅灰底,暗色主题换深底 */
.docs-pre {
margin: 0;
padding: 12px 14px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 12.5px;
line-height: 1.7;
background: rgba(5, 5, 5, 0.04);
border: 1px solid rgba(5, 5, 5, 0.08);
border-radius: 8px;
overflow-x: auto;
white-space: pre;
}
[data-theme="dark"] .docs-pre {
background: rgba(255, 255, 255, 0.06);
border-color: rgba(255, 255, 255, 0.14);
}
.muted {
color: rgba(0, 0, 0, 0.45);
}