diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts index 0054e4b2..fa496832 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts @@ -9,16 +9,24 @@ import { IDnsProvider, parseDomain } from "../../dns-provider/index.js"; import { HttpChallengeUploader } from "./uploads/api.js"; export type CnameVerifyPlan = { + type?: string; domain: string; fullRecord: string; dnsProvider: IDnsProvider; }; +export type HttpVerifyPlan = { + type: string; + domain: string; + httpUploader: HttpChallengeUploader; +}; + export type DomainVerifyPlan = { domain: string; type: "cname" | "dns" | "http"; dnsProvider?: IDnsProvider; cnameVerifyPlan?: Record; + httpVerifyPlan?: Record; }; export type DomainsVerifyPlan = { [key: string]: DomainVerifyPlan; @@ -171,7 +179,23 @@ export class AcmeService { const filePath = `.well-known/acme-challenge/${challenge.token}`; const fileContents = keyAuthorization; this.logger.info(`校验 ${fullDomain} ,准备上传文件:${filePath}`); - await providers.httpUploader.upload(filePath, fileContents); + + let httpUploaderPlan: HttpVerifyPlan = null; + if (providers.domainsVerifyPlan) { + //查找文件上传配置 + for (const mainDomain in providers.domainsVerifyPlan) { + const domainVerifyPlan = providers.domainsVerifyPlan[mainDomain]; + if (domainVerifyPlan && domainVerifyPlan.type === "http" && domainVerifyPlan.httpVerifyPlan[fullDomain]) { + httpUploaderPlan = domainVerifyPlan.httpVerifyPlan[fullDomain]; + break; + } + } + } + if (httpUploaderPlan == null) { + throw new Error(`未找到域名【${fullDomain}】的http校验计划`); + } + + await httpUploaderPlan.httpUploader.upload(filePath, fileContents); this.logger.info(`上传文件【${filePath}】成功`); } else if (challenge.type === "dns-01") { /* dns-01 */ @@ -204,8 +228,11 @@ export class AcmeService { } else { this.logger.error("未找到域名Cname校验计划,使用默认的dnsProvider"); } + } else if (domainVerifyPlan.type === "http") { + throw new Error("切换为http校验"); } else { - this.logger.error("不支持的校验类型", domainVerifyPlan.type); + // this.logger.error("不支持的校验类型", domainVerifyPlan.type); + throw new Error("不支持的校验类型", domainVerifyPlan.type); } } else { this.logger.info("未找到域名校验计划,使用默认的dnsProvider"); @@ -346,7 +373,7 @@ export class AcmeService { email: email, termsOfServiceAgreed: true, skipChallengeVerification: this.skipLocalVerify, - challengePriority: ["dns-01"], + challengePriority: ["dns-01", "http-01"], challengeCreateFn: async ( authz: acme.Authorization, challenge: Challenge, diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts index 50b28efb..e917d1c5 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts @@ -1,7 +1,7 @@ import { CancelError, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import { utils } from "@certd/basic"; -import type { CertInfo, CnameVerifyPlan, DomainsVerifyPlan, PrivateKeyType, SSLProvider } from "./acme.js"; +import type { CertInfo, CnameVerifyPlan, DomainsVerifyPlan, HttpVerifyPlan, PrivateKeyType, SSLProvider } from "./acme.js"; import { AcmeService } from "./acme.js"; import * as _ from "lodash-es"; import { createDnsProvider, DnsProviderContext, IDnsProvider } from "../../dns-provider/index.js"; @@ -9,7 +9,6 @@ import { CertReader } from "./cert-reader.js"; import { CertApplyBasePlugin } from "./base.js"; import { GoogleClient } from "../../libs/google.js"; import { EabAccess } from "../../access"; -import { HttpChallengeUploader } from "./uploads/api"; import { httpChallengeUploaderFactory } from "./uploads/factory.js"; export type { CertInfo }; @@ -67,8 +66,9 @@ export class CertApplyPlugin extends CertApplyBasePlugin { ], }, required: true, - helper: - "DNS直接验证:域名是在阿里云、腾讯云、华为云、Cloudflare、NameSilo、西数注册的,选它。\nCNAME代理验证:支持任何注册商注册的域名,但第一次需要手动添加CNAME记录", + helper: `DNS直接验证:域名是在阿里云、腾讯云、华为云、Cloudflare、NameSilo、西数注册的,选它; +CNAME代理验证:支持任何注册商注册的域名,但第一次需要手动添加CNAME记录; +HTTP文件验证:不支持泛域名,需要配置网站文件上传`, }) challengeType!: string; @@ -127,75 +127,13 @@ export class CertApplyPlugin extends CertApplyBasePlugin { }) dnsProviderAccess!: number; - @TaskInput({ - title: "文件上传方式", - component: { - name: "a-select", - vModel: "value", - options: [ - { value: "ftp", label: "FTP" }, - { value: "sftp", label: "SFTP" }, - { value: "alioss", label: "阿里云OSS" }, - { value: "tencentcos", label: "腾讯云COS" }, - { value: "qiniuoss", label: "七牛OSS" }, - ], - }, - mergeScript: ` - return { - show: ctx.compute(({form})=>{ - return form.challengeType === 'http' - }) - } - `, - required: true, - helper: "您的域名注册商,或者域名的dns服务器属于哪个平台\n如果这里没有,请选择CNAME代理验证校验方式", - }) - httpUploadType!: string; - - @TaskInput({ - title: "文件上传授权", - component: { - name: "access-selector", - }, - required: true, - helper: "请选择文件上传授权", - mergeScript: `return { - component:{ - type: ctx.compute(({form})=>{ - return form.httpUploadType - }) - }, - show: ctx.compute(({form})=>{ - return form.challengeType === 'http' - }) - } - `, - }) - httpUploadAccess!: number; - @TaskInput({ - title: "网站根路径", - component: { - name: "a-input", - }, - required: true, - helper: "请选择网站根路径,校验文件将上传到此目录下", - mergeScript: `return { - show: ctx.compute(({form})=>{ - return form.challengeType === 'http' - }) - } - `, - }) - httpUploadRootDir!: string; - @TaskInput({ title: "域名验证配置", component: { name: "domains-verify-plan-editor", }, - rules: [{ type: "checkCnameVerifyPlan" }], + rules: [{ type: "checkDomainVerifyPlan" }], required: true, - helper: "如果选择CNAME方式,请按照上面的显示,给要申请证书的域名添加CNAME记录,添加后,点击验证,验证成功后不要删除记录,申请和续期证书会一直用它", col: { span: 24, }, @@ -203,10 +141,20 @@ export class CertApplyPlugin extends CertApplyBasePlugin { component:{ domains: ctx.compute(({form})=>{ return form.domains + }), + defaultType: ctx.compute(({form})=>{ + return form.challengeType || 'cname' }) }, show: ctx.compute(({form})=>{ - return form.challengeType === 'cname' + return form.challengeType === 'cname' || form.challengeType === 'http' + }), + helper: ctx.compute(({form})=>{ + if(form.challengeType === 'cname' ){ + return '请按照上面的提示,给要申请证书的域名添加CNAME记录,添加后,点击验证,验证成功后不要删除记录,申请和续期证书会一直用它' + }else if (form.challengeType === 'http'){ + return '请按照上面的提示,给每个域名设置文件上传配置,证书申请过程中会上传校验文件到网站根目录下' + } }) } `, @@ -393,15 +341,8 @@ export class CertApplyPlugin extends CertApplyBasePlugin { let dnsProvider: IDnsProvider = null; let domainsVerifyPlan: DomainsVerifyPlan = null; - let httpUploader: HttpChallengeUploader = null; - if (this.challengeType === "cname") { + if (this.challengeType === "cname" || this.challengeType === "http") { domainsVerifyPlan = await this.createDomainsVerifyPlan(); - } else if (this.challengeType === "http") { - const access = await this.ctx.accessService.getById(this.httpUploadAccess); - httpUploader = await httpChallengeUploaderFactory.createUploaderByType(this.httpUploadType, { - rootDir: this.httpUploadRootDir, - access, - }); } else { const dnsProviderType = this.dnsProviderType; const access = await this.ctx.accessService.getById(this.dnsProviderAccess); @@ -414,7 +355,6 @@ export class CertApplyPlugin extends CertApplyBasePlugin { domains, dnsProvider, domainsVerifyPlan, - httpUploader, csrInfo, isTest: false, privateKeyType: this.privateKeyType, @@ -449,10 +389,11 @@ export class CertApplyPlugin extends CertApplyBasePlugin { const domainVerifyPlan = this.domainsVerifyPlan[domain]; let dnsProvider = null; const cnameVerifyPlan: Record = {}; + const httpVerifyPlan: Record = {}; if (domainVerifyPlan.type === "dns") { const access = await this.ctx.accessService.getById(domainVerifyPlan.dnsProviderAccessId); dnsProvider = await this.createDnsProvider(domainVerifyPlan.dnsProviderType, access); - } else { + } else if (domainVerifyPlan.type === "cname") { for (const key in domainVerifyPlan.cnameVerifyPlan) { const cnameRecord = await this.ctx.cnameProxyService.getByDomain(key); let dnsProvider = cnameRecord.commonDnsProvider; @@ -460,17 +401,39 @@ export class CertApplyPlugin extends CertApplyBasePlugin { dnsProvider = await this.createDnsProvider(cnameRecord.cnameProvider.dnsProviderType, cnameRecord.cnameProvider.access); } cnameVerifyPlan[key] = { + type: "cname", domain: cnameRecord.cnameProvider.domain, fullRecord: cnameRecord.recordValue, dnsProvider, }; } + } else if (domainVerifyPlan.type === "http") { + const httpUploaderContext = { + accessService: this.ctx.accessService, + logger: this.logger, + utils, + }; + for (const key in domainVerifyPlan.httpVerifyPlan) { + const httpRecord = domainVerifyPlan.httpVerifyPlan[key]; + const access = await this.ctx.accessService.getById(httpRecord.httpUploaderAccess); + const httpUploader = await httpChallengeUploaderFactory.createUploaderByType(httpRecord.httpUploaderType, { + access, + rootDir: httpRecord.httpUploadRootDir, + ctx: httpUploaderContext, + }); + httpVerifyPlan[key] = { + type: "http", + domain: key, + httpUploader, + }; + } } plan[domain] = { domain, type: domainVerifyPlan.type, dnsProvider, cnameVerifyPlan, + httpVerifyPlan, }; } return plan; diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/factory.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/factory.ts index 70f14dd4..cc580d2a 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/factory.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/factory.ts @@ -1,24 +1,33 @@ +import { HttpChallengeUploadContext } from "./api"; + export class HttpChallengeUploaderFactory { async getClassByType(type: string) { if (type === "alioss") { - return (await import("./impls/alioss.js")).AliossHttpChallengeUploader; + const module = await import("./impls/alioss.js"); + return module.AliossHttpChallengeUploader; } else if (type === "ssh") { - return (await import("./impls/ssh.js")).SshHttpChallengeUploader; + const module = await import("./impls/ssh.js"); + return module.SshHttpChallengeUploader; } else if (type === "ftp") { - return (await import("./impls/ftp.js")).FtpHttpChallengeUploader; + const module = await import("./impls/ftp.js"); + return module.FtpHttpChallengeUploader; } else if (type === "tencentcos") { - return (await import("./impls/tencentcos.js")).TencentCosHttpChallengeUploader; + const module = await import("./impls/tencentcos.js"); + return module.TencentCosHttpChallengeUploader; } else if (type === "qiniuoss") { - return (await import("./impls/qiniuoss.js")).QiniuOssHttpChallengeUploader; + const module = await import("./impls/qiniuoss.js"); + return module.QiniuOssHttpChallengeUploader; } else { throw new Error(`暂不支持此文件上传方式: ${type}`); } } - createUploaderByType(type: string, opts: { rootDir: string; access: any }) { - const cls = this.getClassByType(type); + async createUploaderByType(type: string, opts: { rootDir: string; access: any; ctx: HttpChallengeUploadContext }) { + const cls = await this.getClassByType(type); if (cls) { // @ts-ignore - return new cls(opts); + const instance = new cls(opts); + await instance.setCtx(opts.ctx); + return instance; } } } diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/alioss.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/alioss.ts index 9f2a5fe6..44fb4568 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/alioss.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/alioss.ts @@ -1,6 +1,6 @@ -import { BaseHttpChallengeUploader } from "../api"; +import { BaseHttpChallengeUploader } from "../api.js"; import { AliossAccess, AliyunAccess } from "@certd/plugin-lib"; -import { AliossClient } from "@certd/plugin-lib/dist/aliyun/lib/oss-client"; +import { AliossClient } from "@certd/plugin-lib"; export class AliossHttpChallengeUploader extends BaseHttpChallengeUploader { async upload(filePath: string, fileContent: string) { diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/ftp.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/ftp.ts index 8300a712..0066020d 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/ftp.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/ftp.ts @@ -1,6 +1,5 @@ -import { BaseHttpChallengeUploader } from "../api"; -import { FtpAccess } from "@certd/plugin-lib"; -import { FtpClient } from "@certd/plugin-lib/dist/ftp/client"; +import { BaseHttpChallengeUploader } from "../api.js"; +import { FtpAccess, FtpClient } from "@certd/plugin-lib"; export class FtpHttpChallengeUploader extends BaseHttpChallengeUploader { async upload(fileName: string, fileContent: string) { diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/qiniuoss.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/qiniuoss.ts index ef1065eb..ce9fb22a 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/qiniuoss.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/qiniuoss.ts @@ -1,7 +1,7 @@ -import { BaseHttpChallengeUploader } from "../api"; -import { FtpAccess } from "@certd/plugin-lib"; +import { BaseHttpChallengeUploader } from "../api.js"; +import { QiniuOssAccess } from "@certd/plugin-lib/dist/qiniu/access-oss"; -export class QiniuOssHttpChallengeUploader extends BaseHttpChallengeUploader { +export class QiniuOssHttpChallengeUploader extends BaseHttpChallengeUploader { async upload(fileName: string, fileContent: string) { return null; } diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/ssh.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/ssh.ts index f8a9d370..18423efe 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/ssh.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/ssh.ts @@ -1,4 +1,4 @@ -import { BaseHttpChallengeUploader } from "../api"; +import { BaseHttpChallengeUploader } from "../api.js"; import { SshAccess, SshClient } from "@certd/plugin-lib"; import path from "path"; import os from "os"; diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/tencentcos.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/tencentcos.ts index a63cc33a..b2da1461 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/tencentcos.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/uploads/impls/tencentcos.ts @@ -1,7 +1,7 @@ -import { BaseHttpChallengeUploader } from "../api"; -import { FtpAccess } from "@certd/plugin-lib"; +import { BaseHttpChallengeUploader } from "../api.js"; +import { TencentCosAccess } from "@certd/plugin-lib/dist/tencent/access-cos"; -export class TencentCosHttpChallengeUploader extends BaseHttpChallengeUploader { +export class TencentCosHttpChallengeUploader extends BaseHttpChallengeUploader { async upload(fileName: string, fileContent: string) { return null; } diff --git a/packages/plugins/plugin-lib/src/aliyun/lib/index.ts b/packages/plugins/plugin-lib/src/aliyun/lib/index.ts index 16d6ca66..a17b82cb 100644 --- a/packages/plugins/plugin-lib/src/aliyun/lib/index.ts +++ b/packages/plugins/plugin-lib/src/aliyun/lib/index.ts @@ -1,2 +1,3 @@ export * from "./base-client.js"; export * from "./ssl-client.js"; +export * from "./oss-client.js"; diff --git a/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/cname-verify-plan.vue b/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/cname-verify-plan.vue index ce4d571e..b0554a11 100644 --- a/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/cname-verify-plan.vue +++ b/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/cname-verify-plan.vue @@ -24,10 +24,7 @@ defineOptions({ name: "CnameVerifyPlan" }); -const emit = defineEmits<{ - "update:modelValue": any; - change: Record; -}>(); +const emit = defineEmits(["update:modelValue", "change"]); const props = defineProps<{ modelValue: Record; diff --git a/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/http-verify-plan.vue b/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/http-verify-plan.vue new file mode 100644 index 00000000..2c75a6f6 --- /dev/null +++ b/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/http-verify-plan.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/index.vue b/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/index.vue index 1a43dddc..18908dfd 100644 --- a/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/index.vue +++ b/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/index.vue @@ -13,7 +13,7 @@ - + @@ -59,7 +59,7 @@
- +
@@ -79,6 +79,7 @@ import { ref, watch } from "vue"; import { dict, FsDictSelect } from "@fast-crud/fast-crud"; import AccessSelector from "/@/views/certd/access/access-selector/index.vue"; import CnameVerifyPlan from "./cname-verify-plan.vue"; +import HttpVerifyPlan from "./http-verify-plan.vue"; import psl from "psl"; import { Form } from "ant-design-vue"; import { DomainsVerifyPlanInput } from "./type"; @@ -95,12 +96,17 @@ const challengeTypeOptions = ref([ { label: "CNAME验证", value: "cname" + }, + { + label: "HTTP验证", + value: "http" } ]); const props = defineProps<{ modelValue?: DomainsVerifyPlanInput; domains?: string[]; + defaultType?: string; }>(); const emit = defineEmits<{ @@ -132,6 +138,16 @@ function showError(error: string) { type DomainGroup = Record>; +watch( + () => { + return props.defaultType; + }, + (value: string) => { + planRef.value = {}; + onDomainsChanged(props.domains); + } +); + function onDomainsChanged(domains: string[]) { console.log("域名变化", domains); if (domains == null) { @@ -155,9 +171,7 @@ function onDomainsChanged(domains: string[]) { group = {}; domainGroups[mainDomain] = group; } - group[domain] = { - id: 0 - }; + group[domain] = {}; } for (const domain in domainGroups) { @@ -166,27 +180,43 @@ function onDomainsChanged(domains: string[]) { if (!planItem) { planItem = { domain, - type: "cname", + //@ts-ignore + type: props.defaultType || "cname", //@ts-ignore cnameVerifyPlan: { ...subDomains + }, + //@ts-ignore + httpVerifyPlan: { + ...subDomains } }; planRef.value[domain] = planItem; - } else { - const cnamePlan = planItem.cnameVerifyPlan; - for (const subDomain in subDomains) { - if (!cnamePlan[subDomain]) { - //@ts-ignore - cnamePlan[subDomain] = { - id: 0 - }; - } + } + const cnamePlan = planItem.cnameVerifyPlan; + for (const subDomain in subDomains) { + //@ts-ignore + cnamePlan[subDomain] = { + id: 0 + }; + } + for (const subDomain of Object.keys(cnamePlan)) { + if (!subDomains[subDomain]) { + delete cnamePlan[subDomain]; } - for (const subDomain of Object.keys(cnamePlan)) { - if (!subDomains[subDomain]) { - delete cnamePlan[subDomain]; - } + } + + // httpVerifyPlan + const httpPlan = planItem.httpVerifyPlan; + for (const subDomain in subDomains) { + //@ts-ignore + httpPlan[subDomain] = { + domain: subDomain + }; + } + for (const subDomain of Object.keys(httpPlan)) { + if (!subDomains[subDomain]) { + delete httpPlan[subDomain]; } } } diff --git a/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/validator.ts b/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/validator.ts index 411b5b69..b91b5536 100644 --- a/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/validator.ts +++ b/packages/ui/certd-client/src/components/plugins/cert/domains-verify-plan-editor/validator.ts @@ -1,12 +1,13 @@ import Validator from "async-validator"; import { DomainsVerifyPlanInput } from "./type"; -function checkCnameVerifyPlan(rule, value: DomainsVerifyPlanInput) { +function checkDomainVerifyPlan(rule: any, value: DomainsVerifyPlanInput) { if (value == null) { return true; } for (const domain in value) { - if (value[domain].type === "cname") { + const type = value[domain].type; + if (type === "cname") { const subDomains = Object.keys(value[domain].cnameVerifyPlan); if (subDomains.length > 0) { for (const subDomain of subDomains) { @@ -16,7 +17,21 @@ function checkCnameVerifyPlan(rule, value: DomainsVerifyPlanInput) { } } } - } else { + } else if (type === "http") { + const subDomains = Object.keys(value[domain].httpVerifyPlan); + if (subDomains.length > 0) { + for (const subDomain of subDomains) { + const plan = value[domain].httpVerifyPlan[subDomain]; + if (plan.httpUploaderType == null) { + throw new Error(`域名${subDomain}的上传方式必须填写`); + } else if (plan.httpUploaderAccess == null) { + throw new Error(`域名${subDomain}的上传授权信息必须填写`); + } else if (plan.httpUploadRootDir == null) { + throw new Error(`域名${subDomain}的网站根路径必须填写`); + } + } + } + } else if (type === "dns") { if (value[domain].dnsProviderType == null || value[domain].dnsProviderAccessId == null) { throw new Error(`DNS模式下,域名${domain}的DNS类型和授权信息必须填写`); } @@ -25,4 +40,4 @@ function checkCnameVerifyPlan(rule, value: DomainsVerifyPlanInput) { return true; } // 注册自定义验证器 -Validator.register("checkCnameVerifyPlan", checkCnameVerifyPlan); +Validator.register("checkDomainVerifyPlan", checkDomainVerifyPlan);
域名主域名 验证方式 验证计划