v2.7.0: 顶栏新增文档入口,/docs 页提供 MCP 与 Console API(/api/v1)说明(认证、MCP 客户端配置与工具清单、REST 端点与 curl 示例)
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
|||||||
LogoutOutlined,
|
LogoutOutlined,
|
||||||
SettingOutlined,
|
SettingOutlined,
|
||||||
HistoryOutlined,
|
HistoryOutlined,
|
||||||
|
BookOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { health, logout, me } from "./api.ts";
|
import { health, logout, me } from "./api.ts";
|
||||||
import { SettingsModal } from "./components/SettingsModal.tsx";
|
import { SettingsModal } from "./components/SettingsModal.tsx";
|
||||||
@@ -36,6 +37,7 @@ const PAGE_META: Record<string, { title: string; subtitle: string }> = {
|
|||||||
"/mailboxes": { title: "邮箱", subtitle: "收件地址、保留策略与邮箱级 Webhook" },
|
"/mailboxes": { title: "邮箱", subtitle: "收件地址、保留策略与邮箱级 Webhook" },
|
||||||
"/inboxs": { title: "收件箱", subtitle: "已接收邮件(默认保留 1 天,可点保留永不过期),支持按域名筛选" },
|
"/inboxs": { title: "收件箱", subtitle: "已接收邮件(默认保留 1 天,可点保留永不过期),支持按域名筛选" },
|
||||||
"/logs": { title: "日志", subtitle: "账号 / 域名 / 邮箱 / Webhook 的操作记录(最多保留 10000 条)" },
|
"/logs": { title: "日志", subtitle: "账号 / 域名 / 邮箱 / Webhook 的操作记录(最多保留 10000 条)" },
|
||||||
|
"/docs": { title: "文档", subtitle: "MCP 与 Console API(/api/v1)使用说明" },
|
||||||
};
|
};
|
||||||
|
|
||||||
/** # 成员变量: 主题切换按钮的太阳/月亮 SVG 图标 **/
|
/** # 成员变量: 主题切换按钮的太阳/月亮 SVG 图标 **/
|
||||||
@@ -104,6 +106,14 @@ export function App() {
|
|||||||
<div className="subtitle">{meta.subtitle}</div>
|
<div className="subtitle">{meta.subtitle}</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 14 }}>
|
<div style={{ display: "flex", alignItems: "center", gap: 14 }}>
|
||||||
|
{/* 文档入口:MCP / API 说明页,位于主题切换左侧 */}
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
aria-label="打开文档"
|
||||||
|
title="API 与 MCP 文档"
|
||||||
|
icon={<BookOutlined />}
|
||||||
|
onClick={() => navigate("/docs")}
|
||||||
|
/>
|
||||||
{/* 主题切换:亮↔暗,位于头像左侧 */}
|
{/* 主题切换:亮↔暗,位于头像左侧 */}
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { DomainsPage } from "./pages/Domains.tsx";
|
|||||||
import { MailboxesPage } from "./pages/Mailboxes.tsx";
|
import { MailboxesPage } from "./pages/Mailboxes.tsx";
|
||||||
import { InboxPage } from "./pages/Inbox.tsx";
|
import { InboxPage } from "./pages/Inbox.tsx";
|
||||||
import { LogsPage } from "./pages/Logs.tsx";
|
import { LogsPage } from "./pages/Logs.tsx";
|
||||||
|
import { DocsPage } from "./pages/Docs.tsx";
|
||||||
import { setUnauthorizedHandler } from "./api.ts";
|
import { setUnauthorizedHandler } from "./api.ts";
|
||||||
|
|
||||||
/** # 成员变量: 路由表(邮件页路由 /inboxs,v2.1.0) **/
|
/** # 成员变量: 路由表(邮件页路由 /inboxs,v2.1.0) **/
|
||||||
@@ -30,6 +31,7 @@ const router = createBrowserRouter([
|
|||||||
{ path: "mailboxes", element: <MailboxesPage /> },
|
{ path: "mailboxes", element: <MailboxesPage /> },
|
||||||
{ path: "inboxs", element: <InboxPage /> },
|
{ path: "inboxs", element: <InboxPage /> },
|
||||||
{ path: "logs", element: <LogsPage /> },
|
{ path: "logs", element: <LogsPage /> },
|
||||||
|
{ path: "docs", element: <DocsPage /> },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -151,6 +151,25 @@ body,
|
|||||||
font-size: 12.5px;
|
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 {
|
.muted {
|
||||||
color: rgba(0, 0, 0, 0.45);
|
color: rgba(0, 0, 0, 0.45);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "wmailhub",
|
"name": "wmailhub",
|
||||||
"version": "2.6.0",
|
"version": "2.7.0",
|
||||||
"description": "WmailHub - 基于 Cloudflare Email Workers 的轻量邮件接收与转发平台(Console 直连 Email Worker)",
|
"description": "WmailHub - 基于 Cloudflare Email Workers 的轻量邮件接收与转发平台(Console 直连 Email Worker)",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -278,6 +278,31 @@ check("勾选后工具条出现「保留选中(批量保留)」按钮", true);
|
|||||||
await page.waitForTimeout(400);
|
await page.waitForTimeout(400);
|
||||||
await page.screenshot({ path: `${SHOT_DIR}/16-inbox-retain.png` });
|
await page.screenshot({ path: `${SHOT_DIR}/16-inbox-retain.png` });
|
||||||
|
|
||||||
|
// 7. 文档页:顶栏文档图标进入 /docs,含 MCP 与 REST API 说明
|
||||||
|
await page.goto(`${BASE}/inboxs`, { waitUntil: "domcontentloaded" });
|
||||||
|
await page.waitForSelector(".ant-table-tbody", { timeout: 8000 });
|
||||||
|
check("顶栏文档图标存在(主题切换左侧)", (await page.locator("button[aria-label='打开文档']").count()) === 1);
|
||||||
|
await page.click("button[aria-label='打开文档']");
|
||||||
|
await page.waitForURL(`${BASE}/docs`, { timeout: 8000 });
|
||||||
|
await page.waitForSelector(".ant-card:has-text('MCP 接入')", { timeout: 8000 });
|
||||||
|
const headerTitle = (await page.locator(".app-header .title").textContent()) ?? "";
|
||||||
|
const docsText = (await page.locator(".app-content").innerText()) ?? "";
|
||||||
|
check(
|
||||||
|
"文档页内容完整(认证 / MCP 接入 / REST API / mcp 端点)",
|
||||||
|
headerTitle.trim() === "文档" &&
|
||||||
|
docsText.includes("认证") &&
|
||||||
|
docsText.includes("MCP 接入") &&
|
||||||
|
docsText.includes("REST API") &&
|
||||||
|
docsText.includes("/api/v1/mcp") &&
|
||||||
|
docsText.includes("Authorization: Bearer wmh_"),
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
"文档页两张清单表(5 个 MCP 工具 + 7 个 API 端点)",
|
||||||
|
(await page.locator(".ant-table-tbody tr").count()) >= 12,
|
||||||
|
);
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.screenshot({ path: `${SHOT_DIR}/17-docs.png` });
|
||||||
|
|
||||||
await browser.close();
|
await browser.close();
|
||||||
|
|
||||||
const failed = results.filter((r) => !r.ok);
|
const failed = results.filter((r) => !r.ok);
|
||||||
|
|||||||
Reference in New Issue
Block a user