From 7fe46cee31c7972bb1493d7682a356c87626601a Mon Sep 17 00:00:00 2001
From: qqqasdwx <673179458@qq.com>
Date: Sat, 23 May 2026 10:15:13 +0000
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20Cloudflare=20Temp?=
=?UTF-8?q?=20Email=20=E5=9B=BA=E5=AE=9A=E5=AD=90=E5=9F=9F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
background.js | 26 ++++-
background/generated-email-helpers.js | 16 ++-
cloudflare-temp-email-utils.js | 25 +++++
...77\347\224\250\346\225\231\347\250\213.md" | 2 +-
...46\345\206\231\346\250\241\346\235\277.md" | 2 +-
...77\347\224\250\350\257\264\346\230\216.md" | 35 +++++-
sidepanel/sidepanel.html | 19 ++++
sidepanel/sidepanel.js | 104 +++++++++++++++++-
...und-cloudflare-temp-email-settings.test.js | 31 +++++-
.../background-generated-email-module.test.js | 78 +++++++++++++
tests/cloudflare-temp-email-utils.test.js | 34 ++++++
...dflare-temp-email-random-subdomain.test.js | 49 ++++++++-
tests/sidepanel-contribution-mode.test.js | 3 +
tests/sidepanel-icloud-provider.test.js | 7 ++
tests/sidepanel-mail2925-base-email.test.js | 3 +
...epanel-phone-verification-settings.test.js | 3 +
...76\350\267\257\350\257\264\346\230\216.md" | 5 +-
...23\346\236\204\350\257\264\346\230\216.md" | 9 +-
18 files changed, 424 insertions(+), 27 deletions(-)
diff --git a/background.js b/background.js
index 02ef4c82..7799ffb6 100644
--- a/background.js
+++ b/background.js
@@ -371,6 +371,7 @@ const {
} = self.LuckMailUtils;
const {
DEFAULT_MAIL_PAGE_SIZE: CLOUDFLARE_TEMP_EMAIL_DEFAULT_PAGE_SIZE,
+ buildCloudflareTempEmailEffectiveDomain,
buildCloudflareTempEmailHeaders,
getCloudflareTempEmailAddressFromResponse,
joinCloudflareTempEmailUrl,
@@ -379,6 +380,7 @@ const {
normalizeCloudflareTempEmailDomain,
normalizeCloudflareTempEmailDomains,
normalizeCloudflareTempEmailMailApiMessages,
+ normalizeCloudflareTempEmailSubdomainPrefix,
} = self.CloudflareTempEmailUtils;
const {
DEFAULT_MAIL_PAGE_SIZE: CLOUD_MAIL_DEFAULT_PAGE_SIZE,
@@ -1428,6 +1430,8 @@ const PERSISTED_SETTING_DEFAULTS = {
cloudflareTempEmailLookupMode: DEFAULT_CLOUDFLARE_TEMP_EMAIL_LOOKUP_MODE,
cloudflareTempEmailReceiveMailbox: '',
cloudflareTempEmailUseRandomSubdomain: false,
+ cloudflareTempEmailUseFixedSubdomain: false,
+ cloudflareTempEmailSubdomainPrefix: '',
cloudflareTempEmailDomain: '',
cloudflareTempEmailDomains: [],
cloudMailBaseUrl: '',
@@ -2899,16 +2903,25 @@ function getHotmailServiceSettings(state = {}) {
}
function getCloudflareTempEmailConfig(state = {}) {
- return {
+ const useFixedSubdomain = Boolean(state.cloudflareTempEmailUseFixedSubdomain);
+ const subdomainPrefix = normalizeCloudflareTempEmailSubdomainPrefix(state.cloudflareTempEmailSubdomainPrefix);
+ const domain = normalizeCloudflareTempEmailDomain(state.cloudflareTempEmailDomain);
+ const config = {
baseUrl: normalizeCloudflareTempEmailBaseUrl(state.cloudflareTempEmailBaseUrl),
adminAuth: String(state.cloudflareTempEmailAdminAuth || ''),
customAuth: String(state.cloudflareTempEmailCustomAuth || ''),
lookupMode: normalizeCloudflareTempEmailLookupMode(state.cloudflareTempEmailLookupMode),
receiveMailbox: normalizeCloudflareTempEmailReceiveMailbox(state.cloudflareTempEmailReceiveMailbox),
- useRandomSubdomain: Boolean(state.cloudflareTempEmailUseRandomSubdomain),
- domain: normalizeCloudflareTempEmailDomain(state.cloudflareTempEmailDomain),
+ useRandomSubdomain: useFixedSubdomain ? false : Boolean(state.cloudflareTempEmailUseRandomSubdomain),
+ useFixedSubdomain,
+ subdomainPrefix,
+ domain,
domains: normalizeCloudflareTempEmailDomains(state.cloudflareTempEmailDomains),
};
+ return {
+ ...config,
+ effectiveDomain: buildCloudflareTempEmailEffectiveDomain(config),
+ };
}
function normalizeCloudflareTempEmailReceiveMailbox(value = '') {
@@ -3402,6 +3415,7 @@ function normalizePersistentSettingValue(key, value) {
case 'autoDeleteUsedIcloudAlias':
case 'accountRunHistoryTextEnabled':
case 'cloudflareTempEmailUseRandomSubdomain':
+ case 'cloudflareTempEmailUseFixedSubdomain':
return Boolean(value);
case 'icloudHostPreference':
return normalizeIcloudHost(value) || 'auto';
@@ -3455,6 +3469,8 @@ function normalizePersistentSettingValue(key, value) {
return normalizeCloudflareTempEmailLookupMode(value);
case 'cloudflareTempEmailReceiveMailbox':
return normalizeCloudflareTempEmailReceiveMailbox(value);
+ case 'cloudflareTempEmailSubdomainPrefix':
+ return normalizeCloudflareTempEmailSubdomainPrefix(value);
case 'cloudflareTempEmailDomain':
return normalizeCloudflareTempEmailDomain(value);
case 'cloudflareTempEmailDomains':
@@ -3613,6 +3629,9 @@ function buildPersistentSettingsPayload(input = {}, options = {}) {
}
payload.cloudflareTempEmailDomains = domains;
}
+ if (payload.cloudflareTempEmailUseFixedSubdomain) {
+ payload.cloudflareTempEmailUseRandomSubdomain = false;
+ }
if (payload.cloudMailDomains) {
const domains = normalizeCloudMailDomains(payload.cloudMailDomains);
if (payload.cloudMailDomain && !domains.includes(payload.cloudMailDomain)) {
@@ -11840,6 +11859,7 @@ function getCurrentPayPalAccount(state = null) {
const generatedEmailHelpers = self.MultiPageGeneratedEmailHelpers?.createGeneratedEmailHelpers({
addLog,
buildGeneratedAliasEmail,
+ buildCloudflareTempEmailEffectiveDomain,
buildCloudflareTempEmailHeaders,
CLOUDFLARE_TEMP_EMAIL_GENERATOR,
CUSTOM_EMAIL_POOL_GENERATOR,
diff --git a/background/generated-email-helpers.js b/background/generated-email-helpers.js
index 81367d5c..e6f6ddb3 100644
--- a/background/generated-email-helpers.js
+++ b/background/generated-email-helpers.js
@@ -5,6 +5,7 @@
const {
addLog,
buildGeneratedAliasEmail,
+ buildCloudflareTempEmailEffectiveDomain,
buildCloudflareTempEmailHeaders,
CLOUDFLARE_TEMP_EMAIL_GENERATOR,
CUSTOM_EMAIL_POOL_GENERATOR,
@@ -92,6 +93,12 @@
if (requireDomain && !config.domain) {
throw new Error('Cloudflare Temp Email 域名为空或格式无效。');
}
+ if (config.useFixedSubdomain && !config.effectiveDomain && typeof buildCloudflareTempEmailEffectiveDomain === 'function') {
+ config.effectiveDomain = buildCloudflareTempEmailEffectiveDomain(config);
+ }
+ if (requireDomain && config.useFixedSubdomain && !config.effectiveDomain) {
+ throw new Error('Cloudflare Temp Email 固定子域前缀为空或格式无效。');
+ }
return config;
}
@@ -159,11 +166,16 @@
requireDomain: true,
});
const requestedName = String(options.localPart || options.name || '').trim().toLowerCase() || generateCloudflareAliasLocalPart();
+ const effectiveDomain = config.effectiveDomain || (
+ typeof buildCloudflareTempEmailEffectiveDomain === 'function'
+ ? buildCloudflareTempEmailEffectiveDomain(config)
+ : config.domain
+ );
const payload = {
enablePrefix: true,
- enableRandomSubdomain: Boolean(config.useRandomSubdomain),
+ enableRandomSubdomain: config.useFixedSubdomain ? false : Boolean(config.useRandomSubdomain),
name: requestedName,
- domain: config.domain,
+ domain: effectiveDomain,
};
const result = await requestCloudflareTempEmailJson(config, '/admin/new_address', {
method: 'POST',
diff --git a/cloudflare-temp-email-utils.js b/cloudflare-temp-email-utils.js
index 08f63eb0..ed6371d8 100644
--- a/cloudflare-temp-email-utils.js
+++ b/cloudflare-temp-email-utils.js
@@ -58,6 +58,29 @@
return domains;
}
+ function normalizeCloudflareTempEmailSubdomainPrefix(rawValue = '') {
+ let value = String(rawValue || '').trim().toLowerCase();
+ if (!value) return '';
+ value = value.replace(/^@+/, '');
+ value = value.replace(/^https?:\/\//, '');
+ value = value.replace(/\/.*$/, '');
+ value = value.replace(/^\.+|\.+$/g, '');
+ if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(value)) {
+ return '';
+ }
+ return value;
+ }
+
+ function buildCloudflareTempEmailEffectiveDomain(config = {}) {
+ const domain = normalizeCloudflareTempEmailDomain(config.domain);
+ if (!domain) return '';
+ const useFixedSubdomain = Boolean(config.useFixedSubdomain);
+ const subdomainPrefix = normalizeCloudflareTempEmailSubdomainPrefix(config.subdomainPrefix);
+ if (!useFixedSubdomain) return domain;
+ if (!subdomainPrefix) return '';
+ return `${subdomainPrefix}.${domain}`;
+ }
+
function buildCloudflareTempEmailHeaders(config = {}, options = {}) {
const headers = {};
const adminAuth = firstNonEmptyString([config.adminAuth, config.cloudflareTempEmailAdminAuth]);
@@ -563,6 +586,7 @@
return {
DEFAULT_MAIL_PAGE_SIZE,
+ buildCloudflareTempEmailEffectiveDomain,
buildCloudflareTempEmailHeaders,
getCloudflareTempEmailAddressFromResponse,
joinCloudflareTempEmailUrl,
@@ -572,5 +596,6 @@
normalizeCloudflareTempEmailDomains,
normalizeCloudflareTempEmailMailApiMessages,
normalizeCloudflareTempEmailMessage,
+ normalizeCloudflareTempEmailSubdomainPrefix,
};
});
diff --git "a/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\344\275\277\347\224\250\346\225\231\347\250\213.md" "b/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\344\275\277\347\224\250\346\225\231\347\250\213.md"
index 831e7aed..ec4741a5 100644
--- "a/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\344\275\277\347\224\250\346\225\231\347\250\213.md"
+++ "b/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\344\275\277\347\224\250\346\225\231\347\250\213.md"
@@ -22,7 +22,7 @@
|---|---|---|---|
| 01 | 相关项目地址与部署说明 | `cpa`、`sub2api`、项目地址、部署前提、部署环境 | `docs/使用教程/分部分/01-相关项目地址与部署说明.md` |
| 02 | 更新扩展 | `git pull`、`GitHub Desktop`、手动覆盖更新、扩展重新加载 | `docs/使用教程/分部分/02-更新扩展.md` |
-| 03 | Cloudflare Temp Email 使用说明 | `Cloudflare Temp Email`、`Admin Auth`、`Custom Auth`、随机子域、邮件接收 | `docs/使用教程/分部分/03-Cloudflare-Temp-Email-使用说明.md` |
+| 03 | Cloudflare Temp Email 使用说明 | `Cloudflare Temp Email`、`Admin Auth`、`Custom Auth`、随机子域、固定子域、邮件接收 | `docs/使用教程/分部分/03-Cloudflare-Temp-Email-使用说明.md` |
| 04 | iCloud 隐私邮箱使用方法 | `iCloud+`、`隐藏邮件地址`、Apple ID、转发邮箱、刷新隐私邮箱 | `docs/使用教程/分部分/04-iCloud-隐私邮箱使用方法.md` |
| 05 | QQ 邮箱切换邮箱使用教程 | `QQ 邮箱`、英文邮箱、`Foxmail`、删除后重建 | `docs/使用教程/分部分/05-QQ-邮箱切换邮箱使用教程.md` |
| 06 | PayPal 注册与绑卡使用教程 | `PayPal`、注册、绑卡、钱包、身份认证、右上角通知 | `docs/使用教程/分部分/06-PayPal-注册与绑卡使用教程.md` |
diff --git "a/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\344\275\277\347\224\250\346\225\231\347\250\213\344\271\246\345\206\231\346\250\241\346\235\277.md" "b/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\344\275\277\347\224\250\346\225\231\347\250\213\344\271\246\345\206\231\346\250\241\346\235\277.md"
index 7feb77c4..1908ccae 100644
--- "a/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\344\275\277\347\224\250\346\225\231\347\250\213\344\271\246\345\206\231\346\250\241\346\235\277.md"
+++ "b/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\344\275\277\347\224\250\346\225\231\347\250\213\344\271\246\345\206\231\346\250\241\346\235\277.md"
@@ -72,7 +72,7 @@ AI 维护时默认按下面的范围归类:
|---|---|---|---|
| 01 | 相关项目地址与部署说明 | `cpa`、`sub2api`、项目地址、部署前提、部署环境 | `docs/使用教程/分部分/01-相关项目地址与部署说明.md` |
| 02 | 更新扩展 | `git pull`、`GitHub Desktop`、手动覆盖更新、扩展重新加载 | `docs/使用教程/分部分/02-更新扩展.md` |
-| 03 | Cloudflare Temp Email 使用说明 | `Cloudflare Temp Email`、`Admin Auth`、`Custom Auth`、随机子域、邮件接收 | `docs/使用教程/分部分/03-Cloudflare-Temp-Email-使用说明.md` |
+| 03 | Cloudflare Temp Email 使用说明 | `Cloudflare Temp Email`、`Admin Auth`、`Custom Auth`、随机子域、固定子域、邮件接收 | `docs/使用教程/分部分/03-Cloudflare-Temp-Email-使用说明.md` |
| 04 | iCloud 隐私邮箱使用方法 | `iCloud+`、`隐藏邮件地址`、Apple ID、转发邮箱、刷新隐私邮箱 | `docs/使用教程/分部分/04-iCloud-隐私邮箱使用方法.md` |
| 05 | QQ 邮箱切换邮箱使用教程 | `QQ 邮箱`、英文邮箱、`Foxmail`、删除后重建 | `docs/使用教程/分部分/05-QQ-邮箱切换邮箱使用教程.md` |
| 06 | PayPal 注册与绑卡使用教程 | `PayPal`、注册、绑卡、钱包、身份认证、右上角通知 | `docs/使用教程/分部分/06-PayPal-注册与绑卡使用教程.md` |
diff --git "a/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\345\210\206\351\203\250\345\210\206/03-Cloudflare-Temp-Email-\344\275\277\347\224\250\350\257\264\346\230\216.md" "b/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\345\210\206\351\203\250\345\210\206/03-Cloudflare-Temp-Email-\344\275\277\347\224\250\350\257\264\346\230\216.md"
index ad332320..82465d2a 100644
--- "a/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\345\210\206\351\203\250\345\210\206/03-Cloudflare-Temp-Email-\344\275\277\347\224\250\350\257\264\346\230\216.md"
+++ "b/docs/\344\275\277\347\224\250\346\225\231\347\250\213/\345\210\206\351\203\250\345\210\206/03-Cloudflare-Temp-Email-\344\275\277\347\224\250\350\257\264\346\230\216.md"
@@ -3,7 +3,7 @@
## 部分信息
- `section_slug`: `cloudflare-temp-email`
-- `适用主题`: `Cloudflare Temp Email`、`Admin Auth`、`Custom Auth`、随机子域、邮件接收
+- `适用主题`: `Cloudflare Temp Email`、`Admin Auth`、`Custom Auth`、随机子域、固定子域、邮件接收
- `维护方式`: `直接更新本文件`
## 适用场景
@@ -16,6 +16,7 @@
- 一个可用的 `Cloudflare Temp Email` 后端地址
- 如果要使用随机子域,对应域名解析已经提前配置好
+- 如果要使用固定子域,后端已经开启 `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH`
- 后端的 `admin auth`
- 如果站点额外设置了访问密码,对应的访问认证信息
- 一个真正用于接收转发邮件的收件邮箱
@@ -52,23 +53,39 @@
### 第五步:配置 `Temp 域名`
-这里填写允许创建邮箱的基础域名。
-即使你开启了 `随机子域`,这里仍然填写基础域名,而不是随机出来的子域名。
+这里选择允许创建邮箱的基础域名。
+即使你开启了 `随机子域` 或 `固定子域`,这里仍然选择基础域名,而不是最终子域名。
### 第六步:按需开启 `随机子域`
只有在 `邮箱生成 = Cloudflare Temp Email` 时,这一项才会生效。
+`随机子域` 与 `固定子域` 互斥,只能开启其中一个。
启用前需要先确认:
- 后端已经配置 `RANDOM_SUBDOMAIN_DOMAINS`
- Cloudflare DNS 已经设置 `MX *`
-### 第七步:作为 `邮箱服务` 时填写 `邮件接收`
+### 第七步:按需开启 `固定子域`
+
+只有在 `邮箱生成 = Cloudflare Temp Email` 时,这一项才会生效。
+开启 `固定子域` 后再填写 `子域前缀`,例如:
+
+- `子域前缀 = a`
+- `Temp 域名 = b.com`
+- 最终提交给后端的域名是 `a.b.com`
+
+切换 `Temp 域名` 后,子域前缀会继续复用。例如选择 `c.com` 后会生成 `xxx@a.c.com`。
+启用前需要先确认:
+
+- 后端已经开启 `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH`
+- 目标子域名的邮件解析已经准备好,或通配 MX 可以覆盖该子域
+
+### 第八步:作为 `邮箱服务` 时填写 `邮件接收`
如果 `邮箱服务` 也选了 `Cloudflare Temp Email`,还需要填写真正的收件邮箱。
后续转发邮件会送到这里。
-### 第八步:查看后端搭建参考
+### 第九步:查看后端搭建参考
如果你还没有部署后端,可以参考:
@@ -88,8 +105,14 @@
通常是因为后端没有配置 `RANDOM_SUBDOMAIN_DOMAINS`,或者 Cloudflare DNS 没有完成 `MX *` 设置。
+### 为什么固定子域没有生效?
+
+通常是因为后端没有开启 `ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH`,或者 `子域前缀` 不符合单段 DNS label 规则。
+前缀只能使用小写字母、数字和中横线,不能包含点号、下划线,也不能以中横线开头或结尾。
+
## 注意事项
- 如果同时把它用作 `邮箱生成` 和 `邮箱服务`,要把两边相关字段都检查一遍
- `Custom Auth` 只有额外访问密码场景才需要填写
-- 开启随机子域前,先确认后端和 DNS 已经准备好
+- `随机子域` 与 `固定子域` 互斥,开启固定子域时会自动关闭随机子域
+- 开启随机子域或固定子域前,先确认后端和 DNS 已经准备好
diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html
index 401a97f2..c4fcec89 100644
--- a/sidepanel/sidepanel.html
+++ b/sidepanel/sidepanel.html
@@ -826,6 +826,25 @@
依赖后端 RANDOM_SUBDOMAIN_DOMAINS
+
Temp 域名
diff --git a/sidepanel/sidepanel.js b/sidepanel/sidepanel.js
index 837d9767..b7d432cf 100644
--- a/sidepanel/sidepanel.js
+++ b/sidepanel/sidepanel.js
@@ -299,6 +299,10 @@ const rowTempEmailReceiveMailbox = document.getElementById('row-temp-email-recei
const inputTempEmailReceiveMailbox = document.getElementById('input-temp-email-receive-mailbox');
const rowTempEmailRandomSubdomainToggle = document.getElementById('row-temp-email-random-subdomain-toggle');
const inputTempEmailUseRandomSubdomain = document.getElementById('input-temp-email-use-random-subdomain');
+const rowTempEmailFixedSubdomainToggle = document.getElementById('row-temp-email-fixed-subdomain-toggle');
+const inputTempEmailUseFixedSubdomain = document.getElementById('input-temp-email-use-fixed-subdomain');
+const rowTempEmailFixedSubdomainPrefix = document.getElementById('row-temp-email-fixed-subdomain-prefix');
+const inputTempEmailSubdomainPrefix = document.getElementById('input-temp-email-subdomain-prefix');
const rowTempEmailDomain = document.getElementById('row-temp-email-domain');
const selectTempEmailDomain = document.getElementById('select-temp-email-domain');
const tempEmailDomainPickerRoot = document.getElementById('temp-email-domain-picker');
@@ -4092,6 +4096,27 @@ function normalizeCloudflareTempEmailDomainValue(value = '') {
return normalizeCloudflareDomainValue(value);
}
+function normalizeCloudflareTempEmailSubdomainPrefixValue(value = '') {
+ let normalized = String(value || '').trim().toLowerCase();
+ if (!normalized) return '';
+ normalized = normalized.replace(/^@+/, '');
+ normalized = normalized.replace(/^https?:\/\//, '');
+ normalized = normalized.replace(/\/.*$/, '');
+ normalized = normalized.replace(/^\.+|\.+$/g, '');
+ if (!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(normalized)) {
+ return '';
+ }
+ return normalized;
+}
+
+function buildCloudflareTempEmailEffectiveDomainValue(domain = '', subdomainPrefix = '', options = {}) {
+ const normalizedDomain = normalizeCloudflareTempEmailDomainValue(domain);
+ if (!normalizedDomain) return '';
+ const normalizedPrefix = normalizeCloudflareTempEmailSubdomainPrefixValue(subdomainPrefix);
+ if (options.requirePrefix && !normalizedPrefix) return '';
+ return normalizedPrefix ? `${normalizedPrefix}.${normalizedDomain}` : normalizedDomain;
+}
+
function normalizeCloudflareTempEmailDomains(values = []) {
const seen = new Set();
const domains = [];
@@ -4182,7 +4207,16 @@ function applyCloudflareTempEmailSettingsState(state = {}) {
inputTempEmailReceiveMailbox.value = state?.cloudflareTempEmailReceiveMailbox || '';
setCloudflareTempEmailLookupMode(state?.cloudflareTempEmailLookupMode);
if (inputTempEmailUseRandomSubdomain) {
- inputTempEmailUseRandomSubdomain.checked = Boolean(state?.cloudflareTempEmailUseRandomSubdomain);
+ inputTempEmailUseRandomSubdomain.checked = Boolean(state?.cloudflareTempEmailUseRandomSubdomain)
+ && !Boolean(state?.cloudflareTempEmailUseFixedSubdomain);
+ }
+ if (inputTempEmailUseFixedSubdomain) {
+ inputTempEmailUseFixedSubdomain.checked = Boolean(state?.cloudflareTempEmailUseFixedSubdomain);
+ }
+ if (inputTempEmailSubdomainPrefix) {
+ inputTempEmailSubdomainPrefix.value = normalizeCloudflareTempEmailSubdomainPrefixValue(
+ state?.cloudflareTempEmailSubdomainPrefix || ''
+ );
}
renderCloudflareTempEmailDomainOptions(state?.cloudflareTempEmailDomain || '');
setCloudflareTempEmailDomainEditMode(false, { clearInput: true });
@@ -5128,7 +5162,12 @@ function collectSettingsPayload() {
? getSelectedCloudflareTempEmailLookupMode()
: 'receive-mailbox',
cloudflareTempEmailReceiveMailbox: normalizeCloudflareTempEmailReceiveMailboxValue(inputTempEmailReceiveMailbox.value),
- cloudflareTempEmailUseRandomSubdomain: Boolean(inputTempEmailUseRandomSubdomain?.checked),
+ cloudflareTempEmailUseRandomSubdomain: Boolean(inputTempEmailUseRandomSubdomain?.checked)
+ && !Boolean(inputTempEmailUseFixedSubdomain?.checked),
+ cloudflareTempEmailUseFixedSubdomain: Boolean(inputTempEmailUseFixedSubdomain?.checked),
+ cloudflareTempEmailSubdomainPrefix: normalizeCloudflareTempEmailSubdomainPrefixValue(
+ inputTempEmailSubdomainPrefix?.value || ''
+ ),
cloudflareTempEmailDomain: selectedCloudflareTempEmailDomain,
cloudflareTempEmailDomains: tempEmailDomains,
cloudMailBaseUrl: normalizeCloudMailBaseUrlInput((typeof inputCloudMailBaseUrl !== 'undefined' && inputCloudMailBaseUrl) ? inputCloudMailBaseUrl.value : ''),
@@ -12959,6 +12998,9 @@ function updateMailProviderUI() {
const showIcloudTargetMailboxType = useIcloudProvider;
const showIcloudForwardMailProvider = useIcloudProvider && icloudTargetMailboxType === 'forward-mailbox';
const showCloudflareTempEmailRandomSubdomainToggle = useEmailGenerator && useCloudflareTempEmailGenerator;
+ const showCloudflareTempEmailFixedSubdomainToggle = useEmailGenerator && useCloudflareTempEmailGenerator;
+ const showCloudflareTempEmailFixedSubdomainPrefix = showCloudflareTempEmailFixedSubdomainToggle
+ && Boolean(inputTempEmailUseFixedSubdomain?.checked);
const showCloudflareTempEmailDomain = useEmailGenerator && useCloudflareTempEmailGenerator;
if (rowEmailGenerator) {
rowEmailGenerator.style.display = useEmailGenerator ? '' : 'none';
@@ -13018,6 +13060,12 @@ function updateMailProviderUI() {
if (rowTempEmailRandomSubdomainToggle) {
rowTempEmailRandomSubdomainToggle.style.display = showCloudflareTempEmailRandomSubdomainToggle ? '' : 'none';
}
+ if (rowTempEmailFixedSubdomainToggle) {
+ rowTempEmailFixedSubdomainToggle.style.display = showCloudflareTempEmailFixedSubdomainToggle ? '' : 'none';
+ }
+ if (rowTempEmailFixedSubdomainPrefix) {
+ rowTempEmailFixedSubdomainPrefix.style.display = showCloudflareTempEmailFixedSubdomainPrefix ? '' : 'none';
+ }
rowTempEmailDomain.style.display = showCloudflareTempEmailDomain ? '' : 'none';
const { domains: tempEmailDomains } = getCloudflareTempEmailDomainsFromState();
if (showCloudflareTempEmailDomain) {
@@ -13119,6 +13167,17 @@ function updateMailProviderUI() {
if (autoHintText && showCloudflareTempEmailRandomSubdomainToggle && inputTempEmailUseRandomSubdomain?.checked) {
autoHintText.textContent = '已启用随机子域名:扩展会按当前选中的 Temp 域名提交,并额外携带 enableRandomSubdomain;是否生效取决于后端 RANDOM_SUBDOMAIN_DOMAINS 配置。';
}
+ if (autoHintText && showCloudflareTempEmailFixedSubdomainPrefix && inputTempEmailUseFixedSubdomain?.checked) {
+ const { activeDomain: tempEmailActiveDomain } = getCloudflareTempEmailDomainsFromState();
+ const effectiveDomain = buildCloudflareTempEmailEffectiveDomainValue(
+ selectTempEmailDomain?.value || tempEmailActiveDomain,
+ inputTempEmailSubdomainPrefix?.value || '',
+ { requirePrefix: true }
+ );
+ autoHintText.textContent = effectiveDomain
+ ? `已启用固定子域名:本次会向后端提交 ${effectiveDomain},后端需开启 ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH。`
+ : '已启用固定子域名:请填写有效的子域前缀;后端需开启 ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH。';
+ }
if (autoHintText && useIcloudProvider && showIcloudForwardMailProvider) {
const forwardProvider = normalizeIcloudForwardMailProvider(icloudForwardMailProviderValue);
const forwardProviderLabel = ICLOUD_FORWARD_MAIL_PROVIDER_LABELS[forwardProvider]
@@ -16508,12 +16567,37 @@ inputTempEmailReceiveMailbox.addEventListener('blur', () => {
});
inputTempEmailUseRandomSubdomain?.addEventListener('change', () => {
+ if (inputTempEmailUseRandomSubdomain.checked && inputTempEmailUseFixedSubdomain) {
+ inputTempEmailUseFixedSubdomain.checked = false;
+ }
updateMailProviderUI();
clearRegistrationEmail({ silent: true }).catch(() => { });
markSettingsDirty(true);
saveSettings({ silent: true }).catch(() => { });
});
+inputTempEmailUseFixedSubdomain?.addEventListener('change', () => {
+ if (inputTempEmailUseFixedSubdomain.checked && inputTempEmailUseRandomSubdomain) {
+ inputTempEmailUseRandomSubdomain.checked = false;
+ }
+ updateMailProviderUI();
+ clearRegistrationEmail({ silent: true }).catch(() => { });
+ markSettingsDirty(true);
+ saveSettings({ silent: true }).catch(() => { });
+});
+
+inputTempEmailSubdomainPrefix?.addEventListener('input', () => {
+ updateMailProviderUI();
+ clearRegistrationEmail({ silent: true }).catch(() => { });
+ markSettingsDirty(true);
+ scheduleSettingsAutoSave();
+});
+inputTempEmailSubdomainPrefix?.addEventListener('blur', () => {
+ inputTempEmailSubdomainPrefix.value = normalizeCloudflareTempEmailSubdomainPrefixValue(inputTempEmailSubdomainPrefix.value);
+ updateMailProviderUI();
+ saveSettings({ silent: true }).catch(() => { });
+});
+
inputAutoSkipFailuresThreadIntervalMinutes.addEventListener('input', () => {
markSettingsDirty(true);
scheduleSettingsAutoSave();
@@ -17689,13 +17773,27 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
inputTempEmailReceiveMailbox.value = message.payload.cloudflareTempEmailReceiveMailbox || '';
}
if (message.payload.cloudflareTempEmailUseRandomSubdomain !== undefined && inputTempEmailUseRandomSubdomain) {
- inputTempEmailUseRandomSubdomain.checked = Boolean(message.payload.cloudflareTempEmailUseRandomSubdomain);
+ inputTempEmailUseRandomSubdomain.checked = Boolean(message.payload.cloudflareTempEmailUseRandomSubdomain)
+ && !Boolean(message.payload.cloudflareTempEmailUseFixedSubdomain ?? latestState?.cloudflareTempEmailUseFixedSubdomain);
+ }
+ if (message.payload.cloudflareTempEmailUseFixedSubdomain !== undefined && inputTempEmailUseFixedSubdomain) {
+ inputTempEmailUseFixedSubdomain.checked = Boolean(message.payload.cloudflareTempEmailUseFixedSubdomain);
+ if (inputTempEmailUseFixedSubdomain.checked && inputTempEmailUseRandomSubdomain) {
+ inputTempEmailUseRandomSubdomain.checked = false;
+ }
+ }
+ if (message.payload.cloudflareTempEmailSubdomainPrefix !== undefined && inputTempEmailSubdomainPrefix) {
+ inputTempEmailSubdomainPrefix.value = normalizeCloudflareTempEmailSubdomainPrefixValue(
+ message.payload.cloudflareTempEmailSubdomainPrefix || ''
+ );
}
if (message.payload.cloudflareTempEmailDomain !== undefined || message.payload.cloudflareTempEmailDomains !== undefined) {
renderCloudflareTempEmailDomainOptions(message.payload.cloudflareTempEmailDomain || latestState?.cloudflareTempEmailDomain || '');
}
if (
message.payload.cloudflareTempEmailUseRandomSubdomain !== undefined
+ || message.payload.cloudflareTempEmailUseFixedSubdomain !== undefined
+ || message.payload.cloudflareTempEmailSubdomainPrefix !== undefined
|| message.payload.cloudflareTempEmailLookupMode !== undefined
|| message.payload.cloudflareTempEmailDomain !== undefined
|| message.payload.cloudflareTempEmailDomains !== undefined
diff --git a/tests/background-cloudflare-temp-email-settings.test.js b/tests/background-cloudflare-temp-email-settings.test.js
index 12cb135a..37c3e27e 100644
--- a/tests/background-cloudflare-temp-email-settings.test.js
+++ b/tests/background-cloudflare-temp-email-settings.test.js
@@ -73,6 +73,8 @@ const PERSISTED_SETTING_DEFAULTS = {
accountRunHistoryTextEnabled: false,
cloudflareTempEmailLookupMode: 'receive-mailbox',
cloudflareTempEmailUseRandomSubdomain: false,
+ cloudflareTempEmailUseFixedSubdomain: false,
+ cloudflareTempEmailSubdomainPrefix: '',
cloudflareTempEmailDomain: '',
cloudflareTempEmailDomains: [],
};
@@ -97,6 +99,16 @@ function normalizeCloudflareDomains(value) { return Array.isArray(value) ? value
function normalizeCloudflareTempEmailBaseUrl(value) { return String(value || '').trim(); }
function normalizeCloudflareTempEmailAddress(value = '') { return String(value || '').trim().toLowerCase(); }
function normalizeCloudflareTempEmailDomain(value) { return String(value || '').trim().toLowerCase(); }
+function normalizeCloudflareTempEmailSubdomainPrefix(value) {
+ const normalized = String(value || '').trim().toLowerCase();
+ return /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(normalized) ? normalized : '';
+}
+function buildCloudflareTempEmailEffectiveDomain(config = {}) {
+ const domain = normalizeCloudflareTempEmailDomain(config.domain);
+ const prefix = normalizeCloudflareTempEmailSubdomainPrefix(config.subdomainPrefix);
+ if (!domain) return '';
+ return config.useFixedSubdomain ? (prefix ? \`\${prefix}.\${domain}\` : '') : domain;
+}
function normalizeCloudflareTempEmailDomains(value) {
const seen = new Set();
const domains = [];
@@ -120,17 +132,24 @@ return {
`)();
assert.equal(api.normalizePersistentSettingValue('cloudflareTempEmailUseRandomSubdomain', 1), true);
+ assert.equal(api.normalizePersistentSettingValue('cloudflareTempEmailUseFixedSubdomain', 1), true);
+ assert.equal(api.normalizePersistentSettingValue('cloudflareTempEmailSubdomainPrefix', 'Team-1'), 'team-1');
+ assert.equal(api.normalizePersistentSettingValue('cloudflareTempEmailSubdomainPrefix', 'team.one'), '');
assert.equal(api.normalizePersistentSettingValue('cloudflareTempEmailLookupMode', 'registration-email'), 'registration-email');
assert.equal(api.normalizePersistentSettingValue('cloudflareTempEmailLookupMode', 'bad'), 'receive-mailbox');
const payload = api.buildPersistentSettingsPayload({
cloudflareTempEmailLookupMode: 'registration-email',
cloudflareTempEmailUseRandomSubdomain: true,
+ cloudflareTempEmailUseFixedSubdomain: true,
+ cloudflareTempEmailSubdomainPrefix: 'Team',
cloudflareTempEmailDomain: 'mail.example.com',
cloudflareTempEmailDomains: ['mail.example.com', 'alt.example.com'],
});
assert.equal(payload.cloudflareTempEmailLookupMode, 'registration-email');
- assert.equal(payload.cloudflareTempEmailUseRandomSubdomain, true);
+ assert.equal(payload.cloudflareTempEmailUseRandomSubdomain, false);
+ assert.equal(payload.cloudflareTempEmailUseFixedSubdomain, true);
+ assert.equal(payload.cloudflareTempEmailSubdomainPrefix, 'team');
assert.equal(payload.cloudflareTempEmailDomain, 'mail.example.com');
assert.deepEqual(payload.cloudflareTempEmailDomains, ['mail.example.com', 'alt.example.com']);
@@ -141,6 +160,8 @@ return {
cloudflareTempEmailLookupMode: 'registration-email',
cloudflareTempEmailReceiveMailbox: 'Forward@Example.com',
cloudflareTempEmailUseRandomSubdomain: true,
+ cloudflareTempEmailUseFixedSubdomain: true,
+ cloudflareTempEmailSubdomainPrefix: 'Team',
cloudflareTempEmailDomain: 'mail.example.com',
cloudflareTempEmailDomains: ['mail.example.com'],
});
@@ -150,9 +171,12 @@ return {
customAuth: 'custom-secret',
lookupMode: 'registration-email',
receiveMailbox: 'forward@example.com',
- useRandomSubdomain: true,
+ useRandomSubdomain: false,
+ useFixedSubdomain: true,
+ subdomainPrefix: 'team',
domain: 'mail.example.com',
domains: ['mail.example.com'],
+ effectiveDomain: 'team.mail.example.com',
});
});
@@ -169,6 +193,8 @@ const PERSISTED_SETTING_DEFAULTS = {
autoDeleteUsedIcloudAlias: false,
accountRunHistoryTextEnabled: false,
cloudflareTempEmailUseRandomSubdomain: false,
+ cloudflareTempEmailUseFixedSubdomain: false,
+ cloudflareTempEmailSubdomainPrefix: '',
cloudflareTempEmailDomain: '',
cloudflareTempEmailDomains: [],
};
@@ -200,6 +226,7 @@ function normalizeCloudflareTempEmailBaseUrl(value) { return String(value || '')
function normalizeCloudflareTempEmailAddress(value = '') { return String(value || '').trim().toLowerCase(); }
function normalizeCloudflareTempEmailReceiveMailbox(value = '') { return String(value || '').trim().toLowerCase(); }
function normalizeCloudflareTempEmailDomain(value) { return String(value || '').trim().toLowerCase(); }
+function normalizeCloudflareTempEmailSubdomainPrefix(value) { return String(value || '').trim().toLowerCase(); }
function normalizeCloudflareTempEmailDomains(value) { return Array.isArray(value) ? value.map((item) => String(item || '').trim().toLowerCase()).filter(Boolean) : []; }
function normalizeHotmailAccounts(value) { return Array.isArray(value) ? value : []; }
function normalizeMail2925Accounts(value) { return Array.isArray(value) ? value : []; }
diff --git a/tests/background-generated-email-module.test.js b/tests/background-generated-email-module.test.js
index b6a55dc5..8fde2689 100644
--- a/tests/background-generated-email-module.test.js
+++ b/tests/background-generated-email-module.test.js
@@ -561,6 +561,84 @@ test('generated email helper requests random subdomain creation while preserving
});
});
+test('generated email helper uses fixed subdomain as the effective temp email domain', async () => {
+ const api = loadGeneratedEmailHelpersApi();
+ const requests = [];
+ const savedEmails = [];
+
+ const helpers = api.createGeneratedEmailHelpers({
+ addLog: async () => {},
+ buildGeneratedAliasEmail: () => {
+ throw new Error('should not build managed alias');
+ },
+ buildCloudflareTempEmailEffectiveDomain: (config) => `${config.subdomainPrefix}.${config.domain}`,
+ buildCloudflareTempEmailHeaders: () => ({ 'x-admin-auth': 'admin-secret' }),
+ CLOUDFLARE_TEMP_EMAIL_GENERATOR: 'cloudflare-temp-email',
+ DUCK_AUTOFILL_URL: 'https://duckduckgo.com/email',
+ fetch: async (url, options = {}) => {
+ requests.push({
+ url,
+ method: options.method,
+ body: options.body ? JSON.parse(options.body) : null,
+ });
+ return {
+ ok: true,
+ text: async () => JSON.stringify({ address: 'user@team.mail.example.com' }),
+ };
+ },
+ fetchIcloudHideMyEmail: async () => {
+ throw new Error('should not use icloud generator');
+ },
+ getCloudflareTempEmailAddressFromResponse: (payload) => payload.address,
+ getCloudflareTempEmailConfig: () => ({
+ baseUrl: 'https://temp.example.com',
+ adminAuth: 'admin-secret',
+ customAuth: '',
+ useRandomSubdomain: true,
+ useFixedSubdomain: true,
+ subdomainPrefix: 'team',
+ domain: 'mail.example.com',
+ }),
+ getState: async () => ({
+ mailProvider: '163',
+ emailGenerator: 'cloudflare-temp-email',
+ }),
+ ensureMail2925AccountForFlow: async () => {
+ throw new Error('should not allocate mail2925 account');
+ },
+ joinCloudflareTempEmailUrl: (baseUrl, path) => `${baseUrl}${path}`,
+ normalizeCloudflareDomain: () => '',
+ normalizeCloudflareTempEmailAddress: (value) => String(value || '').trim().toLowerCase(),
+ normalizeEmailGenerator: (value) => String(value || '').trim().toLowerCase(),
+ isGeneratedAliasProvider: () => false,
+ reuseOrCreateTab: async () => {},
+ sendToContentScript: async () => {
+ throw new Error('should not use duck generator');
+ },
+ setEmailState: async (email) => {
+ savedEmails.push(email);
+ },
+ throwIfStopped: () => {},
+ });
+
+ const email = await helpers.fetchGeneratedEmail({
+ emailGenerator: 'cloudflare-temp-email',
+ }, {
+ generator: 'cloudflare-temp-email',
+ localPart: 'user',
+ });
+
+ assert.equal(email, 'user@team.mail.example.com');
+ assert.deepEqual(savedEmails, ['user@team.mail.example.com']);
+ assert.equal(requests.length, 1);
+ assert.deepEqual(requests[0].body, {
+ enablePrefix: true,
+ enableRandomSubdomain: false,
+ name: 'user',
+ domain: 'team.mail.example.com',
+ });
+});
+
test('generated email helper honors iCloud always-new fetch mode', async () => {
const api = loadGeneratedEmailHelpersApi();
const icloudOptions = [];
diff --git a/tests/cloudflare-temp-email-utils.test.js b/tests/cloudflare-temp-email-utils.test.js
index 7de385c6..75276abd 100644
--- a/tests/cloudflare-temp-email-utils.test.js
+++ b/tests/cloudflare-temp-email-utils.test.js
@@ -2,12 +2,14 @@ const test = require('node:test');
const assert = require('node:assert/strict');
const {
+ buildCloudflareTempEmailEffectiveDomain,
buildCloudflareTempEmailHeaders,
getCloudflareTempEmailAddressFromResponse,
normalizeCloudflareTempEmailBaseUrl,
normalizeCloudflareTempEmailDomain,
normalizeCloudflareTempEmailDomains,
normalizeCloudflareTempEmailMailApiMessages,
+ normalizeCloudflareTempEmailSubdomainPrefix,
} = require('../cloudflare-temp-email-utils.js');
const {
pickVerificationMessageWithTimeFallback,
@@ -34,6 +36,38 @@ test('normalizeCloudflareTempEmailDomain and domains de-duplicate valid entries'
);
});
+test('cloudflare temp email fixed subdomain helpers validate one label and build effective domain', () => {
+ assert.equal(normalizeCloudflareTempEmailSubdomainPrefix(' Team-1 '), 'team-1');
+ assert.equal(normalizeCloudflareTempEmailSubdomainPrefix('a.example'), '');
+ assert.equal(normalizeCloudflareTempEmailSubdomainPrefix('-team'), '');
+ assert.equal(normalizeCloudflareTempEmailSubdomainPrefix('team_1'), '');
+
+ assert.equal(
+ buildCloudflareTempEmailEffectiveDomain({
+ domain: 'Mail.Example.com',
+ useFixedSubdomain: true,
+ subdomainPrefix: 'Team',
+ }),
+ 'team.mail.example.com'
+ );
+ assert.equal(
+ buildCloudflareTempEmailEffectiveDomain({
+ domain: 'mail.example.com',
+ useFixedSubdomain: false,
+ subdomainPrefix: 'team',
+ }),
+ 'mail.example.com'
+ );
+ assert.equal(
+ buildCloudflareTempEmailEffectiveDomain({
+ domain: 'mail.example.com',
+ useFixedSubdomain: true,
+ subdomainPrefix: '',
+ }),
+ ''
+ );
+});
+
test('buildCloudflareTempEmailHeaders includes auth headers and content type when needed', () => {
assert.deepEqual(
buildCloudflareTempEmailHeaders(
diff --git a/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js b/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js
index 8a4e7674..1aa46494 100644
--- a/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js
+++ b/tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js
@@ -69,6 +69,10 @@ test('sidepanel html places cloudflare temp email controls in a standalone secti
assert.match(html, /data-temp-email-lookup-mode="registration-email"/);
assert.match(html, /id="row-temp-email-random-subdomain-toggle"/);
assert.match(html, /id="input-temp-email-use-random-subdomain"/);
+ assert.match(html, /id="row-temp-email-fixed-subdomain-toggle"/);
+ assert.match(html, /id="input-temp-email-use-fixed-subdomain"/);
+ assert.match(html, /id="row-temp-email-fixed-subdomain-prefix"/);
+ assert.match(html, /id="input-temp-email-subdomain-prefix"/);
assert.match(html, /id="btn-temp-email-domain-mode"[^>]*>更新);
assert.doesNotMatch(html, /id="row-temp-email-random-subdomain-domain"/);
});
@@ -142,6 +146,8 @@ const inputTempEmailAdminAuth = { value: '' };
const inputTempEmailCustomAuth = { value: '' };
const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false };
+const inputTempEmailUseFixedSubdomain = { checked: false };
+const inputTempEmailSubdomainPrefix = { value: '' };
const calls = {
domainOptions: [],
domainEditMode: [],
@@ -150,6 +156,10 @@ const calls = {
function renderCloudflareTempEmailDomainOptions(value) { calls.domainOptions.push(value); }
function setCloudflareTempEmailDomainEditMode(editing, options) { calls.domainEditMode.push({ editing, options }); }
function setCloudflareTempEmailLookupMode(value) { calls.lookupModes.push(value); }
+function normalizeCloudflareTempEmailSubdomainPrefixValue(value = '') {
+ const normalized = String(value || '').trim().toLowerCase();
+ return /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(normalized) ? normalized : '';
+}
${bundle}
return {
applyCloudflareTempEmailSettingsState,
@@ -159,6 +169,8 @@ return {
inputTempEmailCustomAuth,
inputTempEmailReceiveMailbox,
inputTempEmailUseRandomSubdomain,
+ inputTempEmailUseFixedSubdomain,
+ inputTempEmailSubdomainPrefix,
};
`)();
@@ -169,6 +181,8 @@ return {
cloudflareTempEmailLookupMode: 'registration-email',
cloudflareTempEmailReceiveMailbox: 'relay@example.com',
cloudflareTempEmailUseRandomSubdomain: true,
+ cloudflareTempEmailUseFixedSubdomain: true,
+ cloudflareTempEmailSubdomainPrefix: 'Team',
cloudflareTempEmailDomain: 'mail.example.com',
});
@@ -176,7 +190,9 @@ return {
assert.equal(api.inputTempEmailAdminAuth.value, 'admin-secret');
assert.equal(api.inputTempEmailCustomAuth.value, 'custom-secret');
assert.equal(api.inputTempEmailReceiveMailbox.value, 'relay@example.com');
- assert.equal(api.inputTempEmailUseRandomSubdomain.checked, true);
+ assert.equal(api.inputTempEmailUseRandomSubdomain.checked, false);
+ assert.equal(api.inputTempEmailUseFixedSubdomain.checked, true);
+ assert.equal(api.inputTempEmailSubdomainPrefix.value, 'team');
assert.deepEqual(api.calls.lookupModes, ['registration-email']);
assert.deepEqual(api.calls.domainOptions, ['mail.example.com']);
assert.deepEqual(api.calls.domainEditMode, [{ editing: false, options: { clearInput: true } }]);
@@ -416,6 +432,8 @@ const rowTempEmailCustomAuth = ${JSON.stringify(createRow('none'))};
const rowTempEmailLookupMode = ${JSON.stringify(createRow('none'))};
const rowTempEmailReceiveMailbox = ${JSON.stringify(createRow('none'))};
const rowTempEmailRandomSubdomainToggle = ${JSON.stringify(createRow('none'))};
+const rowTempEmailFixedSubdomainToggle = ${JSON.stringify(createRow('none'))};
+const rowTempEmailFixedSubdomainPrefix = ${JSON.stringify(createRow('none'))};
const rowTempEmailDomain = ${JSON.stringify(createRow('none'))};
const cloudflareTempEmailSection = ${JSON.stringify(createRow('none'))};
const hotmailSection = ${JSON.stringify(createRow('none'))};
@@ -436,7 +454,10 @@ const rowHotmailLocalBaseUrl = ${JSON.stringify(createRow('none'))};
const inputMail2925UseAccountPool = { checked: false };
const selectMailProvider = { value: '163' };
const selectEmailGenerator = { value: 'cloudflare-temp-email', disabled: false };
+const selectTempEmailDomain = { value: 'mail.example.com' };
const inputTempEmailUseRandomSubdomain = { checked: false };
+const inputTempEmailUseFixedSubdomain = { checked: false };
+const inputTempEmailSubdomainPrefix = { value: 'team' };
const calls = {
tempDomainEditMode: [],
};
@@ -460,6 +481,14 @@ function getSelectedHotmailServiceMode() { return 'local'; }
function getCloudflareDomainsFromState() { return { domains: [], activeDomain: '' }; }
function setCloudflareDomainEditMode() {}
function getCloudflareTempEmailDomainsFromState() { return { domains: ['mail.example.com'], activeDomain: 'mail.example.com' }; }
+function normalizeCloudflareTempEmailDomainValue(value = '') { return String(value || '').trim().toLowerCase(); }
+function normalizeCloudflareTempEmailSubdomainPrefixValue(value = '') { return String(value || '').trim().toLowerCase(); }
+function buildCloudflareTempEmailEffectiveDomainValue(domain = '', subdomainPrefix = '', options = {}) {
+ const normalizedDomain = normalizeCloudflareTempEmailDomainValue(domain);
+ const normalizedPrefix = normalizeCloudflareTempEmailSubdomainPrefixValue(subdomainPrefix);
+ if (options.requirePrefix && !normalizedPrefix) return '';
+ return normalizedPrefix ? \`\${normalizedPrefix}.\${normalizedDomain}\` : normalizedDomain;
+}
function setCloudflareTempEmailDomainEditMode(editing) { calls.tempDomainEditMode.push(editing); }
function queueIcloudAliasRefresh() {}
function hideIcloudLoginHelp() {}
@@ -477,8 +506,12 @@ return {
rowTempEmailLookupMode,
rowTempEmailReceiveMailbox,
rowTempEmailRandomSubdomainToggle,
+ rowTempEmailFixedSubdomainToggle,
+ rowTempEmailFixedSubdomainPrefix,
rowTempEmailDomain,
inputTempEmailUseRandomSubdomain,
+ inputTempEmailUseFixedSubdomain,
+ inputTempEmailSubdomainPrefix,
selectMailProvider,
selectEmailGenerator,
setLookupMode(value) {
@@ -492,6 +525,8 @@ return {
api.updateMailProviderUI();
assert.equal(api.cloudflareTempEmailSection.style.display, '');
assert.equal(api.rowTempEmailRandomSubdomainToggle.style.display, '');
+ assert.equal(api.rowTempEmailFixedSubdomainToggle.style.display, '');
+ assert.equal(api.rowTempEmailFixedSubdomainPrefix.style.display, 'none');
assert.equal(api.rowTempEmailDomain.style.display, '');
api.selectMailProvider.value = 'cloudflare-temp-email';
@@ -513,4 +548,16 @@ return {
assert.equal(api.cloudflareTempEmailSection.style.display, '');
assert.equal(api.rowTempEmailDomain.style.display, '');
assert.match(api.autoHintText.textContent, /RANDOM_SUBDOMAIN_DOMAINS/);
+
+ api.inputTempEmailUseRandomSubdomain.checked = false;
+ api.inputTempEmailUseFixedSubdomain.checked = true;
+ api.inputTempEmailSubdomainPrefix.value = 'team';
+ api.updateMailProviderUI();
+ assert.equal(api.rowTempEmailFixedSubdomainPrefix.style.display, '');
+ assert.match(api.autoHintText.textContent, /team\.mail\.example\.com/);
+ assert.match(api.autoHintText.textContent, /ENABLE_CREATE_ADDRESS_SUBDOMAIN_MATCH/);
+
+ api.inputTempEmailSubdomainPrefix.value = '';
+ api.updateMailProviderUI();
+ assert.match(api.autoHintText.textContent, /请填写有效的子域前缀/);
});
diff --git a/tests/sidepanel-contribution-mode.test.js b/tests/sidepanel-contribution-mode.test.js
index 69494716..08e1d71f 100644
--- a/tests/sidepanel-contribution-mode.test.js
+++ b/tests/sidepanel-contribution-mode.test.js
@@ -245,6 +245,8 @@ const inputTempEmailAdminAuth = { value: 'admin-secret' };
const inputTempEmailCustomAuth = { value: 'custom-secret' };
const inputTempEmailReceiveMailbox = { value: 'relay@example.com' };
const inputTempEmailUseRandomSubdomain = { checked: true };
+const inputTempEmailUseFixedSubdomain = { checked: false };
+const inputTempEmailSubdomainPrefix = { value: '' };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '5' };
const inputAutoStepDelaySeconds = { value: '10' };
@@ -293,6 +295,7 @@ function normalizeLuckmailBaseUrl(value) { return String(value || '').trim(); }
function normalizeLuckmailEmailType(value) { return String(value || '').trim(); }
function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
+function normalizeCloudflareTempEmailSubdomainPrefixValue(value) { return String(value || '').trim().toLowerCase(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number.isFinite(Number(value)) ? Number(value) : fallback; }
diff --git a/tests/sidepanel-icloud-provider.test.js b/tests/sidepanel-icloud-provider.test.js
index 4526d32b..e29931fb 100644
--- a/tests/sidepanel-icloud-provider.test.js
+++ b/tests/sidepanel-icloud-provider.test.js
@@ -130,6 +130,8 @@ const inputTempEmailAdminAuth = { value: '' };
const inputTempEmailCustomAuth = { value: '' };
const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false };
+const inputTempEmailUseFixedSubdomain = { checked: false };
+const inputTempEmailSubdomainPrefix = { value: '' };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
const inputAutoStepDelaySeconds = { value: '' };
@@ -215,6 +217,7 @@ function normalizeLuckmailBaseUrl(value) { return String(value || '').trim(); }
function normalizeLuckmailEmailType(value) { return String(value || '').trim() || 'ms_graph'; }
function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
+function normalizeCloudflareTempEmailSubdomainPrefixValue(value) { return String(value || '').trim().toLowerCase(); }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
@@ -273,6 +276,8 @@ const rowTempEmailAdminAuth = createRow();
const rowTempEmailCustomAuth = createRow();
const rowTempEmailReceiveMailbox = createRow();
const rowTempEmailRandomSubdomainToggle = createRow();
+const rowTempEmailFixedSubdomainToggle = createRow();
+const rowTempEmailFixedSubdomainPrefix = createRow();
const rowTempEmailDomain = createRow();
const cloudflareTempEmailSection = createRow();
const hotmailSection = createRow();
@@ -299,6 +304,8 @@ const selectIcloudTargetMailboxType = { value: 'icloud-inbox' };
const selectIcloudForwardMailProvider = { value: 'gmail' };
const selectIcloudHostPreference = { value: 'icloud.com.cn' };
const inputTempEmailUseRandomSubdomain = { checked: false };
+const inputTempEmailUseFixedSubdomain = { checked: false };
+const inputTempEmailSubdomainPrefix = { value: '' };
const inputRunCount = { disabled: false };
const currentAutoRun = { autoRunning: false };
const MAIL_PROVIDER_LOGIN_CONFIGS = { gmail: { label: 'Gmail 邮箱' } };
diff --git a/tests/sidepanel-mail2925-base-email.test.js b/tests/sidepanel-mail2925-base-email.test.js
index 3b737f6c..8e72565e 100644
--- a/tests/sidepanel-mail2925-base-email.test.js
+++ b/tests/sidepanel-mail2925-base-email.test.js
@@ -201,6 +201,8 @@ const inputTempEmailAdminAuth = { value: '' };
const inputTempEmailCustomAuth = { value: '' };
const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false };
+const inputTempEmailUseFixedSubdomain = { checked: false };
+const inputTempEmailSubdomainPrefix = { value: '' };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
const inputAutoStepDelaySeconds = { value: '' };
@@ -253,6 +255,7 @@ function normalizeLuckmailBaseUrl(value) { return String(value || '').trim(); }
function normalizeLuckmailEmailType(value) { return String(value || '').trim() || 'ms_graph'; }
function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
+function normalizeCloudflareTempEmailSubdomainPrefixValue(value) { return String(value || '').trim().toLowerCase(); }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
diff --git a/tests/sidepanel-phone-verification-settings.test.js b/tests/sidepanel-phone-verification-settings.test.js
index 4cbf7756..debf2f20 100644
--- a/tests/sidepanel-phone-verification-settings.test.js
+++ b/tests/sidepanel-phone-verification-settings.test.js
@@ -976,6 +976,8 @@ const inputTempEmailAdminAuth = { value: '' };
const inputTempEmailCustomAuth = { value: '' };
const inputTempEmailReceiveMailbox = { value: '' };
const inputTempEmailUseRandomSubdomain = { checked: false };
+const inputTempEmailUseFixedSubdomain = { checked: false };
+const inputTempEmailSubdomainPrefix = { value: '' };
const inputAutoSkipFailures = { checked: false };
const inputAutoSkipFailuresThreadIntervalMinutes = { value: '0' };
const inputAutoStepDelaySeconds = { value: '' };
@@ -1067,6 +1069,7 @@ function normalizeLuckmailBaseUrl(value) { return String(value || '').trim(); }
function normalizeLuckmailEmailType(value) { return String(value || '').trim() || 'ms_graph'; }
function normalizeCloudflareTempEmailBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeCloudflareTempEmailReceiveMailboxValue(value) { return String(value || '').trim(); }
+function normalizeCloudflareTempEmailSubdomainPrefixValue(value) { return String(value || '').trim().toLowerCase(); }
function normalizeAccountRunHistoryHelperBaseUrlValue(value) { return String(value || '').trim(); }
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
diff --git "a/\351\241\271\347\233\256\345\256\214\346\225\264\351\223\276\350\267\257\350\257\264\346\230\216.md" "b/\351\241\271\347\233\256\345\256\214\346\225\264\351\223\276\350\267\257\350\257\264\346\230\216.md"
index 80078ddb..61745994 100644
--- "a/\351\241\271\347\233\256\345\256\214\346\225\264\351\223\276\350\267\257\350\257\264\346\230\216.md"
+++ "b/\351\241\271\347\233\256\345\256\214\346\225\264\351\223\276\350\267\257\350\257\264\346\230\216.md"
@@ -59,7 +59,7 @@
- 展示 `Plus 模式` 开关、`账号接入策略` 与 `Plus 支付` 配置;`账号接入策略` 固定放在 `Plus 支付` 上方,只提供 `OAuth / 使用会话 JSON 导入` 两种接入方式,会话导入的 CPA / SUB2API 目标由上方 `来源` 自动决定,不支持会话导入的来源会直接禁用为仅 OAuth;支付方式支持 PayPal / GoPay / GPC,PayPal 展示账号池下拉与添加按钮,GoPay 展示手机号和 PIN,GPC 展示只读 API 地址、API Key、专用手机号、OTP 渠道、本地 OTP helper 开关与 URL、PIN;GPC 的 `购买卡密` 按钮打开卡密购买页,`转换 API Key` 按钮打开 `https://gpc.qlhazycoder.top/`;步骤列表会按支付方式和账号接入策略动态切换,普通模式的注册成功等待步骤不再显示或执行;Plus 模式下手机号注册不可用,只允许邮箱注册
- 展示 `执行范围` 配置;当前 UI 只在 codex/openai flow 显示,保存为通用的 `stepExecutionRangeByFlow`,用于指定允许执行的起止步骤。开启后,范围外节点在侧栏中显示为禁用态,手动按钮、手动跳过入口与进度计数只按允许节点计算。
- 为 Hotmail / 2925 账号池复用同一套“添加账号 / 取消添加 / 批量导入 / 收起列表”表单交互;共享的显隐控制放在 `sidepanel/account-pool-ui.js`,各自 manager 只保留 provider 相关字段校验与业务操作
-- `Cloudflare Temp Email` 在侧栏中有独立区域,支持“按注册邮箱查信 / 按接收邮箱查信”切换、可编辑域名列表、随机子域名开关,以及教程/部署入口;域名选择由 `sidepanel/editable-list-picker.js` 统一承接
+- `Cloudflare Temp Email` 在侧栏中有独立区域,支持“按注册邮箱查信 / 按接收邮箱查信”切换、可编辑域名列表、随机子域名开关、固定子域名开关与子域前缀输入,以及教程/部署入口;域名选择由 `sidepanel/editable-list-picker.js` 统一承接
- `SUB2API` 目标在分组配置下方额外暴露 `账号优先级`;该值会随设置一起持久化,并在后台直连创建账号时原样透传给 SUB2API 管理接口
- 点击“自动”或其他自动化入口时,sidepanel 会附带当前浏览器窗口 ID;后台后续查找、复用和接管标签页时优先锁定在这一个 automation window 内,避免串到其他窗口
@@ -273,7 +273,7 @@
- 2925 是否启用号池模式 `mail2925UseAccountPool`
- 2925 当前选中的号池账号 ID `currentMail2925AccountId`
- Cloudflare / Temp Email 设置
-- Cloudflare Temp Email 细分配置:`cloudflareTempEmailLookupMode / cloudflareTempEmailUseRandomSubdomain / cloudflareTempEmailDomain / cloudflareTempEmailDomains`
+- Cloudflare Temp Email 细分配置:`cloudflareTempEmailLookupMode / cloudflareTempEmailUseRandomSubdomain / cloudflareTempEmailUseFixedSubdomain / cloudflareTempEmailSubdomainPrefix / cloudflareTempEmailDomain / cloudflareTempEmailDomains`
- 注册方式 `signupMethod`
- 接码开关、接码平台 `phoneSmsProvider`,以及 HeroSMS / 5sim 各自的 API Key、国家/地区、价格上限和平台扩展设置
- iCloud 相关偏好
@@ -1251,4 +1251,3 @@ Hide My Email 获取与管理链路:
- `2925` 在 Step 4 / Step 8 现在会携带固定的步骤开始时间窗口,实际筛选下限为“步骤开始时间向前回看 10 分钟”。
- 为了保留这段固定时间窗内已经到达的验证码邮件,后台不再在轮询开始前预先清空 2925 邮箱。
- `2925` 验证码最终提交成功后,后台仍会异步发送 `DELETE_ALL_EMAILS` 做收尾清理。
-
diff --git "a/\351\241\271\347\233\256\346\226\207\344\273\266\347\273\223\346\236\204\350\257\264\346\230\216.md" "b/\351\241\271\347\233\256\346\226\207\344\273\266\347\273\223\346\236\204\350\257\264\346\230\216.md"
index 3e90ee6d..20d7b57f 100644
--- "a/\351\241\271\347\233\256\346\226\207\344\273\266\347\273\223\346\236\204\350\257\264\346\230\216.md"
+++ "b/\351\241\271\347\233\256\346\226\207\344\273\266\347\273\223\346\236\204\350\257\264\346\230\216.md"
@@ -23,7 +23,7 @@
- `README.md`:面向使用者的精简说明文档,主要介绍项目用途、功能范围、快速开始与文档入口;不再承载过多技术实现细节。
- `background.js`:扩展后台 Service Worker 入口壳,负责模块装配、初始化、全局常量、少量保留的领域函数与运行入口;当前已按 `activeFlowId` 装配 flow-aware 的步骤定义、执行注册表、自动运行与状态同步,OAuth 后链 5 分钟总预算只由 `openai + cpa` target capability 自动启用,其他来源与 flow 不启用该预算;`openai` flow 继续承接 `stepExecutionRangeByFlow`,`kiro` flow 走独立的“注册页 1-6 步 -> 桌面授权 7-8 步 -> `kiro.rs` 上传 9 步”链路,`grok` flow 走独立的“注册页 1-4 步 -> SSO Cookie 提取 5 步 -> `webchat2api` 上传 6 步”链路。
- `cloudmail-utils.js`:Cloud Mail / SkyMail 相关的纯工具函数,负责 API 地址、域名、鉴权头、邮件列表响应与邮件正文归一化。
-- `cloudflare-temp-email-utils.js`:Cloudflare Temp Email 相关的纯工具函数,负责 URL、域名、邮件内容与 MIME 数据归一化。
+- `cloudflare-temp-email-utils.js`:Cloudflare Temp Email 相关的纯工具函数,负责 URL、基础域名、固定子域有效域名、邮件内容与 MIME 数据归一化。
- `gopay-utils.js`:GoPay / GPC Plus 支付相关纯工具函数,负责支付方式归一化、GoPay 手机号/OTP/PIN 归一化、GPC API 地址归一化、API Key 请求头、任务创建/查询/OTP/PIN/停止 URL 与余额响应解析。
- `hotmail-utils.js`:Hotmail 账号与验证码提取相关的纯工具函数,负责账号筛选、验证码匹配、第三方接口数据归一化。
- `icloud-utils.js`:iCloud 隐私邮箱相关的纯工具函数,负责 host、别名列表、保留状态、已用状态等归一化。
@@ -255,7 +255,7 @@
- `tests/background-account-history-settings.test.js`:测试账号运行历史的独立配置项归一化,不再复用 Hotmail 模式作为启停条件。
- `tests/background-auth-chain-guard.test.js`:测试认证顶层链路的去重、Stop 重抛、OAuth 总预算窗口和 `fetch-signup-code` 网络重试边界。
- `tests/background-auth-tab-snapshot.test.js`:测试认证标签快照在脚本执行失败时会回退到标签页 URL 做页面状态判断。
-- `tests/background-cloudflare-temp-email-settings.test.js`:测试 Cloudflare Temp Email 随机子域名设置与 OAuth 总超时配置的持久化归一化。
+- `tests/background-cloudflare-temp-email-settings.test.js`:测试 Cloudflare Temp Email 随机/固定子域名设置与 OAuth 总超时配置的持久化归一化。
- `tests/background-cpa-api.test.js`:测试 CPA 管理接口模块的地址归一化、OAuth 地址获取、callback 提交,以及 ChatGPT 会话导入 payload 构建与返回结果。
- `tests/background-cpa-session-import.test.js`:测试 CPA 会话导入尾节点的标签页选择、会话读取、日志上报、结果透传和后台接线约束。
- `tests/background-cpa-state-plumbing.test.js`:测试步骤 1 与消息路由对 CPA OAuth 运行态字段的保存、回写和 reset 接线。
@@ -338,7 +338,7 @@
- `tests/qq-mail-content.test.js`:测试 QQ 邮箱内容脚本会把 runtime mail rule 的验证码模式转发到新邮件匹配链路。
- `tests/run-count-unlimited.test.js`:测试自动运行轮数不再被 50 封顶,且旧 active 状态不会把手动输入轮数重置回去。
- `tests/cloudflare-temp-email-provider.test.js`:测试 Cloudflare Temp Email provider 的轮询与目标邮箱选择逻辑。
-- `tests/cloudflare-temp-email-utils.test.js`:测试 Cloudflare Temp Email 工具层的 URL、域名、邮件解析逻辑。
+- `tests/cloudflare-temp-email-utils.test.js`:测试 Cloudflare Temp Email 工具层的 URL、域名、固定子域有效域名与邮件解析逻辑。
- `tests/cloudmail-provider.test.js`:测试 Cloud Mail provider 的验证码轮询和生成/转发收件目标邮箱选择逻辑,并覆盖生成邮箱时对共享注册邮箱状态持久化的接入。
- `tests/cloudmail-utils.test.js`:测试 Cloud Mail 工具层的 URL、域名、鉴权头、Token 与邮件响应归一化逻辑。
- `tests/content-operation-delay.test.js`:测试内容脚本操作间延迟层的执行顺序、默认启用、排除步骤、悬挂恢复和 stop-aware sleep。
@@ -382,7 +382,7 @@
- `tests/grok-runner.test.js`:测试 Grok 注册页执行器对邮箱提交、验证码轮询、资料提交、SSO Cookie 提取、账号记录收尾和完成状态持久化的关键链路。
- `tests/sidepanel-auto-run-content-refresh.test.js`:测试点击“自动”时会先冻结当前轮数并刷新贡献内容更新摘要,且刷新失败或启动前状态回灌不会阻塞或改写自动流程启动目标。
- `tests/sidepanel-auto-run-risk-warning.test.js`:测试自动运行在高轮数且未配置节点轮询时会弹出风险提醒。
-- `tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js`:测试 Cloudflare Temp Email 独立区域、随机子域名开关、域名列表恢复与提示文案联动。
+- `tests/sidepanel-cloudflare-temp-email-random-subdomain.test.js`:测试 Cloudflare Temp Email 独立区域、随机/固定子域名开关、域名列表恢复与提示文案联动。
- `tests/sidepanel-header-links.test.js`:测试侧边栏头部仓库首页与 Releases 入口的 HTML 接线与链接解析。
- `tests/sidepanel-icloud-manager.test.js`:测试侧边栏 iCloud 管理器模块接线与空态渲染。
- `tests/sidepanel-icloud-provider.test.js`:测试侧边栏 iCloud 登录地址解析、目标邮箱类型 / 转发邮箱 provider 保存、回显和显隐联动。
@@ -433,4 +433,3 @@
- `tests/verification-stop-propagation.test.js`:测试验证码流程在 Stop 场景下的错误传播与不中途降级。
- `tests/verification-flow-polling.test.js`:测试 2925 长轮询参数、验证码提交流程中的 `beforeSubmit` 钩子执行顺序,以及步骤 8 提交验证码后进入手机号页时会把“继续手机号验证”状态交给步骤 9 处理。
-