v2.4.0 修 Email Routing 假成功 + 加 API Token 接入与邮箱/域名页交互改进
问题 6(收不到邮件、Dashboard 邮件路由里看不到域名)的根因:
上一版把「开启 Email Routing」改成了 PUT /zones/{id}/email/routing,
但实测该接口(以及 PATCH)是**纯回显** —— 返回 success:true 但 enabled 纹丝不动,
域名在 Cloudflare 侧仍是 unconfigured、连 MX 记录都没有。Console 却照它的返回值
把域名标成「已配置 Email Workers」,于是「显示配好了却收不到邮件」。
实测只有 POST /zones/{id}/email/routing/enable 真的开启(线上对 adx9.com 与
galaxyemail.com 逐一验证)。
后端
- cfapi: 改用 /enable,并在开启后回读 GET 校验 enabled===true,不满足即判失败;
新增 CFApiError(带 Cloudflare 错误码)与 getEmailRoutingSettings
- cfapi: 错误码 2008(Non-Cloudflare MX records exist)翻译成中文并点名冲突的 MX 记录
- provision: Email Worker 统一命名 wmailhub-email(原先按账号 id 拼名),
部署走 PUT,同名即覆盖更新
- db: 新增 decryptAccountToken —— 按账号取 token,不再取「最近一条连接」,
并跳过已过期的 OAuth token;oauth_connections.account_id 真正写入
- resources: 新增 POST /api/accounts/token(API Token 方式添加账号):
先校验 token 与账户 ID,确认可用才建账号并自动接入
前端
- 账号:「添加账号」改为模态窗口,OAuth / API Token 两个 tab,各自带说明
- 域名: 删除「Email Worker」列;「设置 Email Workers / 重新配置」先弹确认框
(说明会改 MX 与路由),执行期间按钮 loading 且互相禁用;error 状态改成可读的 Alert + 重试
- 邮箱: 删除「状态」列(已由「启用」开关承担);地址列加复制按钮(带弹跳+变绿动画);
「添加邮箱」窗口的域名只列已装 Email Workers 的、支持搜索、默认选中最近使用的域名;
地址框可一键从 namedb 取随机用户名;勾选 catch-all 后地址框禁用
- 收件箱: 域名筛选只显示已装 Email Workers 的域名
测试
- cf-mock 复刻「PUT/PATCH 回 success 但不改 enabled」与 2008,锁住本次修复
- oauth.test 新增 4 例:2008 冲突、回读校验、API Token 接入、Token/账户 ID 校验失败
- ui-smoke 覆盖新增的添加账号/添加邮箱/确认框等交互(28/28 通过)
文档: docs/02 补「三种开启接口只有 /enable 有效」的实测表、2008 处理、
两种接入方式与按账号取 token 的说明
This commit is contained in:
@@ -140,6 +140,15 @@ export const updateAccount = (id: string, body: { name?: string }) =>
|
||||
|
||||
export const deleteAccount = (id: string) => api(`/api/accounts/${id}`, "DELETE");
|
||||
|
||||
/** 用 Cloudflare API Token 添加账号(与 OAuth 并列的第二种接入方式)。
|
||||
* 后端会先用 token 探一次 Cloudflare 确认可用、且与填写的账户 ID 一致,再建账号并自动接入。 */
|
||||
export const createAccountByToken = (body: {
|
||||
name: string;
|
||||
cloudflare_account_id?: string;
|
||||
token: string;
|
||||
}) =>
|
||||
api<{ ok: boolean; account_id: string; error?: string }>("/api/accounts/token", "POST", body);
|
||||
|
||||
/** 轮换 Worker credential:secret 仅本次返回可见;已配置域名自动重新部署 */
|
||||
export const rotateAccount = (id: string) =>
|
||||
api<{ ok: boolean; worker_id: string; credential_id: string; secret: string; redeployed: string[] }>(
|
||||
@@ -355,3 +364,19 @@ export const createApiKey = () =>
|
||||
|
||||
/** 撤销并删除 Key */
|
||||
export const deleteApiKey = () => api("/api/settings/api-key", "DELETE");
|
||||
|
||||
/* ---- 随机邮箱名 ---- */
|
||||
|
||||
/** # 成员变量: 随机用户名来源(namedb,返回 { username, first, last, gender }) **/
|
||||
const NAMEDB_URL = "https://namedb.adx9.com/api/username";
|
||||
|
||||
/** 从 namedb 拉一个随机用户名(用于邮箱地址的本地部分)。
|
||||
* 返回值: 用户名字符串;失败时抛错,由调用方决定提示文案。 */
|
||||
export async function fetchRandomUsername(): Promise<string> {
|
||||
const resp = await fetch(NAMEDB_URL);
|
||||
if (!resp.ok) throw new Error(`随机用户名服务返回 ${resp.status}`);
|
||||
const data = (await resp.json()) as { username?: string };
|
||||
const name = (data.username ?? "").trim();
|
||||
if (!name) throw new Error("随机用户名服务没有返回可用名字");
|
||||
return name;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user