diff --git a/frontend/src/views/Dashboard/DomainForm.js b/frontend/src/views/Dashboard/DomainForm.js index 49fd2c9..7d744ff 100644 --- a/frontend/src/views/Dashboard/DomainForm.js +++ b/frontend/src/views/Dashboard/DomainForm.js @@ -149,6 +149,14 @@ export function useDomainForm({ fetchAdmin, domains, servers, domainServers, not formError.value = "请输入主域名"; return; } + if (payload.certificate_type === "wildcard" && !payload.name.trim().startsWith("*.")) { + formError.value = "泛域名主域名必须以 *. 开头,例如 *.example.com"; + return; + } + if (payload.certificate_type !== "wildcard" && payload.name.trim().startsWith("*.")) { + formError.value = "只有泛域名证书类型允许使用 *. 域名"; + return; + } if (payload.certificate_type === "san" && payload.sans.length === 0) { formError.value = "SAN 证书至少需要填写一个附加域名"; return; diff --git a/frontend/src/views/Dashboard/DomainForm.test.js b/frontend/src/views/Dashboard/DomainForm.test.js index 7b1988c..f6dd3c2 100644 --- a/frontend/src/views/Dashboard/DomainForm.test.js +++ b/frontend/src/views/Dashboard/DomainForm.test.js @@ -67,6 +67,24 @@ test("HTTP 验证拒绝主泛域名和附加泛域名,不提交请求", async assert.equal(calls, 0); }); +test("泛域名必须填写带通配符的主域名,不提交无效配置", async (t) => { + let calls = 0; + const { state } = await mountForm(t, async () => { + calls += 1; + return Response.json({}); + }); + await startCreate(state); + state.form.value.certificate_type = "wildcard"; + state.form.value.name = "pcux.cn"; + await state.saveDomain(); + assert.equal(calls, 0); + assert.match(state.formError.value, /泛域名主域名必须以 \*\. 开头/); + + state.form.value.name = "*.pcux.cn"; + await state.saveDomain(); + assert.equal(calls, 1); +}); + test("编辑时保留已有 HTTP 验证方式", async (t) => { const { state } = await mountForm(t, async () => Response.json({ items: [] })); await state.openEdit({ ...firstDomain, challenge_type: "http-01" }); diff --git a/frontend/src/views/Dashboard/DomainModal.css b/frontend/src/views/Dashboard/DomainModal.css index 87f1c5b..f94d2af 100644 --- a/frontend/src/views/Dashboard/DomainModal.css +++ b/frontend/src/views/Dashboard/DomainModal.css @@ -68,6 +68,13 @@ gap: 6px; } +.domain-modal .field-hint { + margin-top: -1px; + color: #6d7f98; + font-size: 12px; + line-height: 1.45; +} + .domain-modal .switch-field { align-items: center; margin: 4px 0 10px; diff --git a/frontend/src/views/Dashboard/DomainModal.test.js b/frontend/src/views/Dashboard/DomainModal.test.js index ffc4645..50432c8 100644 --- a/frontend/src/views/Dashboard/DomainModal.test.js +++ b/frontend/src/views/Dashboard/DomainModal.test.js @@ -67,6 +67,10 @@ async function renderModal(overrides = {}) { return html; } +async function readModalSource() { + return readFile(new URL("./DomainModal.vue", import.meta.url), "utf8"); +} + test("域名表单使用复选框同时显示两台已关联服务器并提供复用提示", async () => { const html = await renderModal(); const inputs = html.match(/]*>/g) || []; @@ -81,6 +85,22 @@ test("域名表单使用复选框同时显示两台已关联服务器并提供 assert.match(html, /当前域名所属项目/); }); +test("域名表单不会通过背景点击误关闭,并提示泛域名格式", async () => { + const source = await readModalSource(); + const html = await renderModal({ form: { + name: "pcux.cn", + certificate_type: "wildcard", + sans: "", + auto_renew: false, + acme_environment: "production", + dns_provider: "aliyun", + server_ids: [], + } }); + assert.doesNotMatch(source, /class="modal-backdrop domain-backdrop"[^>]*@click/); + assert.match(html, /placeholder="\*\.example\.com"/); + assert.match(html, /泛域名必须以 \*\. 开头/); +}); + test("域名账户显示归属与服务商,原绑定不可用不被静默清空", async () => { const html = await renderModal({ dnsAccountID: "old-account", dnsBound: true, dnsUnavailableSelection: true, diff --git a/frontend/src/views/Dashboard/DomainModal.vue b/frontend/src/views/Dashboard/DomainModal.vue index 61566be..58535de 100644 --- a/frontend/src/views/Dashboard/DomainModal.vue +++ b/frontend/src/views/Dashboard/DomainModal.vue @@ -30,7 +30,7 @@ const { - + + />泛域名必须以 *. 开头,例如 *.example.com;不会自动包含根域名。 证书类型