feat: 支持从 CFTE 同步 Temp 域名列表
This commit is contained in:
@@ -725,14 +725,14 @@
|
|||||||
<div id="temp-email-domain-picker" class="editable-list-picker">
|
<div id="temp-email-domain-picker" class="editable-list-picker">
|
||||||
<button id="btn-temp-email-domain-menu" class="data-select editable-list-trigger" type="button"
|
<button id="btn-temp-email-domain-menu" class="data-select editable-list-trigger" type="button"
|
||||||
aria-haspopup="listbox" aria-expanded="false">
|
aria-haspopup="listbox" aria-expanded="false">
|
||||||
<span id="temp-email-domain-current" class="truncate">请先添加域名</span>
|
<span id="temp-email-domain-current" class="truncate">请先更新域名</span>
|
||||||
<span class="editable-list-caret" aria-hidden="true"></span>
|
<span class="editable-list-caret" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<div id="temp-email-domain-menu" class="editable-list-menu" role="listbox" hidden></div>
|
<div id="temp-email-domain-menu" class="editable-list-menu" role="listbox" hidden></div>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" id="input-temp-email-domain" class="data-input" placeholder="例如 mail.example.com"
|
<input type="text" id="input-temp-email-domain" class="data-input" placeholder="例如 mail.example.com"
|
||||||
style="display:none;" />
|
style="display:none;" />
|
||||||
<button id="btn-temp-email-domain-mode" class="btn btn-outline btn-sm" type="button">添加</button>
|
<button id="btn-temp-email-domain-mode" class="btn btn-outline btn-sm" type="button">更新</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+153
-25
@@ -1310,7 +1310,7 @@ const tempEmailDomainPicker = createEditableListPicker({
|
|||||||
trigger: btnTempEmailDomainMenu,
|
trigger: btnTempEmailDomainMenu,
|
||||||
current: tempEmailDomainCurrent,
|
current: tempEmailDomainCurrent,
|
||||||
menu: tempEmailDomainMenu,
|
menu: tempEmailDomainMenu,
|
||||||
emptyLabel: '请先添加域名',
|
emptyLabel: '请先更新域名',
|
||||||
itemLabel: '域名',
|
itemLabel: '域名',
|
||||||
normalizeItems: normalizeCloudflareTempEmailDomains,
|
normalizeItems: normalizeCloudflareTempEmailDomains,
|
||||||
normalizeValue: normalizeCloudflareTempEmailDomainValue,
|
normalizeValue: normalizeCloudflareTempEmailDomainValue,
|
||||||
@@ -2981,16 +2981,11 @@ function setCloudflareDomainEditMode(editing, options = {}) {
|
|||||||
|
|
||||||
function setCloudflareTempEmailDomainEditMode(editing, options = {}) {
|
function setCloudflareTempEmailDomainEditMode(editing, options = {}) {
|
||||||
const { clearInput = false } = options;
|
const { clearInput = false } = options;
|
||||||
cloudflareTempEmailDomainEditMode = Boolean(editing);
|
cloudflareTempEmailDomainEditMode = false;
|
||||||
tempEmailDomainPicker.setVisible(!cloudflareTempEmailDomainEditMode);
|
tempEmailDomainPicker.setVisible(true);
|
||||||
inputTempEmailDomain.style.display = cloudflareTempEmailDomainEditMode ? '' : 'none';
|
inputTempEmailDomain.style.display = 'none';
|
||||||
btnTempEmailDomainMode.textContent = cloudflareTempEmailDomainEditMode ? '保存' : '添加';
|
btnTempEmailDomainMode.textContent = '更新';
|
||||||
if (cloudflareTempEmailDomainEditMode) {
|
if (clearInput) {
|
||||||
if (clearInput) {
|
|
||||||
inputTempEmailDomain.value = '';
|
|
||||||
}
|
|
||||||
inputTempEmailDomain.focus();
|
|
||||||
} else if (clearInput) {
|
|
||||||
inputTempEmailDomain.value = '';
|
inputTempEmailDomain.value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10427,6 +10422,152 @@ async function saveCloudflareTempEmailDomainSettings(domains, activeDomain, opti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function joinCloudflareTempEmailSettingsUrl(baseUrl, path) {
|
||||||
|
const normalizedBaseUrl = normalizeCloudflareTempEmailBaseUrlValue(baseUrl);
|
||||||
|
const normalizedPath = String(path || '').trim();
|
||||||
|
if (!normalizedBaseUrl || !normalizedPath) {
|
||||||
|
return normalizedBaseUrl || '';
|
||||||
|
}
|
||||||
|
return `${normalizedBaseUrl}${normalizedPath.startsWith('/') ? '' : '/'}${normalizedPath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCloudflareTempEmailSyncHeaders(options = {}) {
|
||||||
|
const { includeAdminAuth = false } = options;
|
||||||
|
const headers = {
|
||||||
|
Accept: 'application/json',
|
||||||
|
};
|
||||||
|
const customAuth = String(inputTempEmailCustomAuth?.value || '').trim();
|
||||||
|
if (customAuth) {
|
||||||
|
headers['x-custom-auth'] = customAuth;
|
||||||
|
}
|
||||||
|
if (includeAdminAuth) {
|
||||||
|
const adminAuth = String(inputTempEmailAdminAuth?.value || '').trim();
|
||||||
|
if (adminAuth) {
|
||||||
|
headers['x-admin-auth'] = adminAuth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function requestCloudflareTempEmailSyncPayload(baseUrl, path, options = {}) {
|
||||||
|
const url = joinCloudflareTempEmailSettingsUrl(baseUrl, path);
|
||||||
|
if (!url) {
|
||||||
|
throw new Error('Cloudflare Temp Email 服务地址为空或格式无效。');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
cache: 'no-store',
|
||||||
|
headers: buildCloudflareTempEmailSyncHeaders(options),
|
||||||
|
});
|
||||||
|
const rawText = await response.text();
|
||||||
|
let payload = rawText;
|
||||||
|
try {
|
||||||
|
payload = rawText ? JSON.parse(rawText) : {};
|
||||||
|
} catch {
|
||||||
|
payload = rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const message = typeof payload === 'object' && payload
|
||||||
|
? (payload.message || payload.error || payload.msg)
|
||||||
|
: '';
|
||||||
|
throw new Error(message || `HTTP ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeCloudflareTempEmailDomains(existingDomains, fetchedDomains) {
|
||||||
|
const mergedDomains = [];
|
||||||
|
const seen = new Set();
|
||||||
|
for (const domain of normalizeCloudflareTempEmailDomains(fetchedDomains)) {
|
||||||
|
if (seen.has(domain)) continue;
|
||||||
|
seen.add(domain);
|
||||||
|
mergedDomains.push(domain);
|
||||||
|
}
|
||||||
|
for (const domain of normalizeCloudflareTempEmailDomains(existingDomains)) {
|
||||||
|
if (seen.has(domain)) continue;
|
||||||
|
seen.add(domain);
|
||||||
|
mergedDomains.push(domain);
|
||||||
|
}
|
||||||
|
return mergedDomains;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchCloudflareTempEmailAvailableDomains(baseUrl) {
|
||||||
|
let openSettingsError = null;
|
||||||
|
try {
|
||||||
|
const payload = await requestCloudflareTempEmailSyncPayload(baseUrl, '/open_api/settings');
|
||||||
|
const domains = normalizeCloudflareTempEmailDomains(payload?.domains || []);
|
||||||
|
if (domains.length) {
|
||||||
|
return {
|
||||||
|
domains,
|
||||||
|
source: 'open_api/settings',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
openSettingsError = new Error('公开设置未返回可用域名。');
|
||||||
|
} catch (error) {
|
||||||
|
openSettingsError = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const adminAuth = String(inputTempEmailAdminAuth?.value || '').trim();
|
||||||
|
if (!adminAuth) {
|
||||||
|
throw openSettingsError || new Error('未获取到可用域名。');
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = await requestCloudflareTempEmailSyncPayload(baseUrl, '/admin/worker/configs', {
|
||||||
|
includeAdminAuth: true,
|
||||||
|
});
|
||||||
|
const domains = normalizeCloudflareTempEmailDomains(payload?.DOMAINS || []);
|
||||||
|
if (!domains.length) {
|
||||||
|
throw openSettingsError || new Error('管理配置未返回可用域名。');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
domains,
|
||||||
|
source: 'admin/worker/configs',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function syncCloudflareTempEmailDomainsFromService() {
|
||||||
|
const normalizedBaseUrl = normalizeCloudflareTempEmailBaseUrlValue(inputTempEmailBaseUrl?.value || '');
|
||||||
|
if (!normalizedBaseUrl) {
|
||||||
|
throw new Error('请先填写有效的 Cloudflare Temp Email 服务地址。');
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousButtonText = btnTempEmailDomainMode.textContent;
|
||||||
|
btnTempEmailDomainMode.disabled = true;
|
||||||
|
btnTempEmailDomainMode.textContent = '更新中...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { domains: fetchedDomains, source } = await fetchCloudflareTempEmailAvailableDomains(normalizedBaseUrl);
|
||||||
|
const { domains: existingDomains, activeDomain } = getCloudflareTempEmailDomainsFromState();
|
||||||
|
const currentDomain = normalizeCloudflareTempEmailDomainValue(selectTempEmailDomain.value || activeDomain);
|
||||||
|
const mergedDomains = mergeCloudflareTempEmailDomains(existingDomains, fetchedDomains);
|
||||||
|
const nextActiveDomain = mergedDomains.includes(currentDomain)
|
||||||
|
? currentDomain
|
||||||
|
: (fetchedDomains[0] || mergedDomains[0] || '');
|
||||||
|
const existingSet = new Set(normalizeCloudflareTempEmailDomains(existingDomains));
|
||||||
|
const addedCount = fetchedDomains.filter((domain) => !existingSet.has(domain)).length;
|
||||||
|
|
||||||
|
await saveCloudflareTempEmailDomainSettings(mergedDomains, nextActiveDomain, { silent: true });
|
||||||
|
if (addedCount > 0) {
|
||||||
|
showToast(`已更新 Cloudflare Temp Email 域名,新增 ${addedCount} 个。`, 'success', 2200);
|
||||||
|
} else {
|
||||||
|
showToast('已同步 Cloudflare Temp Email 域名,暂无新增项。', 'info', 2200);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
domains: mergedDomains,
|
||||||
|
activeDomain: nextActiveDomain,
|
||||||
|
source,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
const message = error?.message || String(error || '未知错误');
|
||||||
|
throw new Error(`更新 Cloudflare Temp Email 域名失败:${message}`);
|
||||||
|
} finally {
|
||||||
|
btnTempEmailDomainMode.disabled = false;
|
||||||
|
btnTempEmailDomainMode.textContent = previousButtonText || '更新';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleDeleteCloudflareDomain(domain) {
|
async function handleDeleteCloudflareDomain(domain) {
|
||||||
const targetDomain = normalizeCloudflareDomainValue(domain);
|
const targetDomain = normalizeCloudflareDomainValue(domain);
|
||||||
if (!targetDomain) {
|
if (!targetDomain) {
|
||||||
@@ -12736,20 +12877,7 @@ btnCfDomainMode.addEventListener('click', async () => {
|
|||||||
|
|
||||||
btnTempEmailDomainMode.addEventListener('click', async () => {
|
btnTempEmailDomainMode.addEventListener('click', async () => {
|
||||||
try {
|
try {
|
||||||
if (!cloudflareTempEmailDomainEditMode) {
|
await syncCloudflareTempEmailDomainsFromService();
|
||||||
setCloudflareTempEmailDomainEditMode(true, { clearInput: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newDomain = normalizeCloudflareTempEmailDomainValue(inputTempEmailDomain.value);
|
|
||||||
if (!newDomain) {
|
|
||||||
showToast('请输入有效的 Cloudflare Temp Email 域名。', 'warn');
|
|
||||||
inputTempEmailDomain.focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { domains } = getCloudflareTempEmailDomainsFromState();
|
|
||||||
await saveCloudflareTempEmailDomainSettings([...domains, newDomain], newDomain);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(err.message, 'error');
|
showToast(err.message, 'error');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ test('sidepanel html places cloudflare temp email controls in a standalone secti
|
|||||||
assert.match(html, /id="btn-cloudflare-temp-email-github"/);
|
assert.match(html, /id="btn-cloudflare-temp-email-github"/);
|
||||||
assert.match(html, /id="row-temp-email-random-subdomain-toggle"/);
|
assert.match(html, /id="row-temp-email-random-subdomain-toggle"/);
|
||||||
assert.match(html, /id="input-temp-email-use-random-subdomain"/);
|
assert.match(html, /id="input-temp-email-use-random-subdomain"/);
|
||||||
|
assert.match(html, /id="btn-temp-email-domain-mode"[^>]*>更新</);
|
||||||
assert.doesNotMatch(html, /id="row-temp-email-random-subdomain-domain"/);
|
assert.doesNotMatch(html, /id="row-temp-email-random-subdomain-domain"/);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -173,6 +174,215 @@ return {
|
|||||||
assert.deepEqual(api.calls.domainEditMode, [{ editing: false, options: { clearInput: true } }]);
|
assert.deepEqual(api.calls.domainEditMode, [{ editing: false, options: { clearInput: true } }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('setCloudflareTempEmailDomainEditMode keeps the selector visible and the button in update mode', () => {
|
||||||
|
const bundle = extractFunction('setCloudflareTempEmailDomainEditMode');
|
||||||
|
|
||||||
|
const api = new Function(`
|
||||||
|
let cloudflareTempEmailDomainEditMode = true;
|
||||||
|
const tempEmailDomainPicker = {
|
||||||
|
visibility: [],
|
||||||
|
setVisible(value) {
|
||||||
|
this.visibility.push(value);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const inputTempEmailDomain = { value: 'old.example.com', style: { display: '' } };
|
||||||
|
const btnTempEmailDomainMode = { textContent: '' };
|
||||||
|
${bundle}
|
||||||
|
return {
|
||||||
|
setCloudflareTempEmailDomainEditMode,
|
||||||
|
tempEmailDomainPicker,
|
||||||
|
inputTempEmailDomain,
|
||||||
|
btnTempEmailDomainMode,
|
||||||
|
getMode() {
|
||||||
|
return cloudflareTempEmailDomainEditMode;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
`)();
|
||||||
|
|
||||||
|
api.setCloudflareTempEmailDomainEditMode(true, { clearInput: true });
|
||||||
|
|
||||||
|
assert.equal(api.getMode(), false);
|
||||||
|
assert.deepEqual(api.tempEmailDomainPicker.visibility, [true]);
|
||||||
|
assert.equal(api.inputTempEmailDomain.style.display, 'none');
|
||||||
|
assert.equal(api.inputTempEmailDomain.value, '');
|
||||||
|
assert.equal(api.btnTempEmailDomainMode.textContent, '更新');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('syncCloudflareTempEmailDomainsFromService merges domains from open settings and preserves the current selection', async () => {
|
||||||
|
const bundle = [
|
||||||
|
extractFunction('joinCloudflareTempEmailSettingsUrl'),
|
||||||
|
extractFunction('buildCloudflareTempEmailSyncHeaders'),
|
||||||
|
extractFunction('requestCloudflareTempEmailSyncPayload'),
|
||||||
|
extractFunction('mergeCloudflareTempEmailDomains'),
|
||||||
|
extractFunction('fetchCloudflareTempEmailAvailableDomains'),
|
||||||
|
extractFunction('syncCloudflareTempEmailDomainsFromService'),
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const api = new Function(`
|
||||||
|
const fetchCalls = [];
|
||||||
|
const saveCalls = [];
|
||||||
|
const toastCalls = [];
|
||||||
|
const btnTempEmailDomainMode = { textContent: '更新', disabled: false };
|
||||||
|
const inputTempEmailBaseUrl = { value: 'https://temp.example.com' };
|
||||||
|
const inputTempEmailCustomAuth = { value: 'custom-secret' };
|
||||||
|
const inputTempEmailAdminAuth = { value: 'admin-secret' };
|
||||||
|
const selectTempEmailDomain = { value: 'old.example.com' };
|
||||||
|
function normalizeCloudflareTempEmailBaseUrlValue(value = '') {
|
||||||
|
const raw = String(value || '').trim();
|
||||||
|
if (!raw) return '';
|
||||||
|
const candidate = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:\\/\\//.test(raw) ? raw : \`https://\${raw}\`;
|
||||||
|
const parsed = new URL(candidate);
|
||||||
|
parsed.hash = '';
|
||||||
|
parsed.search = '';
|
||||||
|
const pathname = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\\/+$/, '');
|
||||||
|
return \`\${parsed.origin}\${pathname}\`;
|
||||||
|
}
|
||||||
|
function normalizeCloudflareTempEmailDomainValue(value = '') {
|
||||||
|
let normalized = String(value || '').trim().toLowerCase();
|
||||||
|
if (!normalized) return '';
|
||||||
|
normalized = normalized.replace(/^@+/, '');
|
||||||
|
normalized = normalized.replace(/^https?:\\/\\//, '');
|
||||||
|
normalized = normalized.replace(/\\/.*$/, '');
|
||||||
|
return /^[a-z0-9.-]+\\.[a-z]{2,}$/i.test(normalized) ? normalized : '';
|
||||||
|
}
|
||||||
|
function normalizeCloudflareTempEmailDomains(values = []) {
|
||||||
|
const seen = new Set();
|
||||||
|
const domains = [];
|
||||||
|
for (const value of Array.isArray(values) ? values : []) {
|
||||||
|
const normalized = normalizeCloudflareTempEmailDomainValue(value);
|
||||||
|
if (!normalized || seen.has(normalized)) continue;
|
||||||
|
seen.add(normalized);
|
||||||
|
domains.push(normalized);
|
||||||
|
}
|
||||||
|
return domains;
|
||||||
|
}
|
||||||
|
async function fetch(url, options = {}) {
|
||||||
|
fetchCalls.push({ url, options });
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
status: 200,
|
||||||
|
async text() {
|
||||||
|
return JSON.stringify({
|
||||||
|
domains: ['new.example.com', 'old.example.com'],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function getCloudflareTempEmailDomainsFromState() {
|
||||||
|
return {
|
||||||
|
domains: ['old.example.com', 'local-only.example.com'],
|
||||||
|
activeDomain: 'old.example.com',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async function saveCloudflareTempEmailDomainSettings(domains, activeDomain, options) {
|
||||||
|
saveCalls.push({ domains, activeDomain, options });
|
||||||
|
}
|
||||||
|
function showToast(message, type, duration) {
|
||||||
|
toastCalls.push({ message, type, duration });
|
||||||
|
}
|
||||||
|
${bundle}
|
||||||
|
return {
|
||||||
|
syncCloudflareTempEmailDomainsFromService,
|
||||||
|
fetchCalls,
|
||||||
|
saveCalls,
|
||||||
|
toastCalls,
|
||||||
|
btnTempEmailDomainMode,
|
||||||
|
};
|
||||||
|
`)();
|
||||||
|
|
||||||
|
const result = await api.syncCloudflareTempEmailDomainsFromService();
|
||||||
|
|
||||||
|
assert.equal(result.source, 'open_api/settings');
|
||||||
|
assert.deepEqual(api.fetchCalls.map((item) => item.url), ['https://temp.example.com/open_api/settings']);
|
||||||
|
assert.deepEqual(api.saveCalls, [{
|
||||||
|
domains: ['new.example.com', 'old.example.com', 'local-only.example.com'],
|
||||||
|
activeDomain: 'old.example.com',
|
||||||
|
options: { silent: true },
|
||||||
|
}]);
|
||||||
|
assert.match(api.toastCalls[0].message, /新增 1 个/);
|
||||||
|
assert.equal(api.toastCalls[0].type, 'success');
|
||||||
|
assert.equal(api.btnTempEmailDomainMode.disabled, false);
|
||||||
|
assert.equal(api.btnTempEmailDomainMode.textContent, '更新');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fetchCloudflareTempEmailAvailableDomains falls back to admin worker configs when open settings has no domains', async () => {
|
||||||
|
const bundle = [
|
||||||
|
extractFunction('joinCloudflareTempEmailSettingsUrl'),
|
||||||
|
extractFunction('buildCloudflareTempEmailSyncHeaders'),
|
||||||
|
extractFunction('requestCloudflareTempEmailSyncPayload'),
|
||||||
|
extractFunction('fetchCloudflareTempEmailAvailableDomains'),
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const api = new Function(`
|
||||||
|
const fetchCalls = [];
|
||||||
|
const inputTempEmailCustomAuth = { value: 'custom-secret' };
|
||||||
|
const inputTempEmailAdminAuth = { value: 'admin-secret' };
|
||||||
|
function normalizeCloudflareTempEmailBaseUrlValue(value = '') {
|
||||||
|
const raw = String(value || '').trim();
|
||||||
|
if (!raw) return '';
|
||||||
|
const candidate = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:\\/\\//.test(raw) ? raw : \`https://\${raw}\`;
|
||||||
|
const parsed = new URL(candidate);
|
||||||
|
parsed.hash = '';
|
||||||
|
parsed.search = '';
|
||||||
|
const pathname = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\\/+$/, '');
|
||||||
|
return \`\${parsed.origin}\${pathname}\`;
|
||||||
|
}
|
||||||
|
function normalizeCloudflareTempEmailDomainValue(value = '') {
|
||||||
|
let normalized = String(value || '').trim().toLowerCase();
|
||||||
|
if (!normalized) return '';
|
||||||
|
normalized = normalized.replace(/^@+/, '');
|
||||||
|
normalized = normalized.replace(/^https?:\\/\\//, '');
|
||||||
|
normalized = normalized.replace(/\\/.*$/, '');
|
||||||
|
return /^[a-z0-9.-]+\\.[a-z]{2,}$/i.test(normalized) ? normalized : '';
|
||||||
|
}
|
||||||
|
function normalizeCloudflareTempEmailDomains(values = []) {
|
||||||
|
const seen = new Set();
|
||||||
|
const domains = [];
|
||||||
|
for (const value of Array.isArray(values) ? values : []) {
|
||||||
|
const normalized = normalizeCloudflareTempEmailDomainValue(value);
|
||||||
|
if (!normalized || seen.has(normalized)) continue;
|
||||||
|
seen.add(normalized);
|
||||||
|
domains.push(normalized);
|
||||||
|
}
|
||||||
|
return domains;
|
||||||
|
}
|
||||||
|
async function fetch(url, options = {}) {
|
||||||
|
fetchCalls.push({ url, options });
|
||||||
|
if (url.endsWith('/open_api/settings')) {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
status: 200,
|
||||||
|
async text() {
|
||||||
|
return JSON.stringify({ domains: [] });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
status: 200,
|
||||||
|
async text() {
|
||||||
|
return JSON.stringify({ DOMAINS: ['admin.example.com'] });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
${bundle}
|
||||||
|
return {
|
||||||
|
fetchCloudflareTempEmailAvailableDomains,
|
||||||
|
fetchCalls,
|
||||||
|
};
|
||||||
|
`)();
|
||||||
|
|
||||||
|
const result = await api.fetchCloudflareTempEmailAvailableDomains('https://temp.example.com');
|
||||||
|
|
||||||
|
assert.equal(result.source, 'admin/worker/configs');
|
||||||
|
assert.deepEqual(result.domains, ['admin.example.com']);
|
||||||
|
assert.deepEqual(api.fetchCalls.map((item) => item.url), [
|
||||||
|
'https://temp.example.com/open_api/settings',
|
||||||
|
'https://temp.example.com/admin/worker/configs',
|
||||||
|
]);
|
||||||
|
assert.equal(api.fetchCalls[1].options.headers['x-admin-auth'], 'admin-secret');
|
||||||
|
});
|
||||||
|
|
||||||
test('updateMailProviderUI keeps the temp domain selector visible and updates the hint when random subdomain is enabled', () => {
|
test('updateMailProviderUI keeps the temp domain selector visible and updates the hint when random subdomain is enabled', () => {
|
||||||
const bundle = extractFunction('updateMailProviderUI');
|
const bundle = extractFunction('updateMailProviderUI');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user