mirror of https://github.com/certd/certd
chore: auto
parent
af5e1b805f
commit
896cd950e9
|
@ -62,12 +62,14 @@ export interface IDomainParser {
|
||||||
|
|
||||||
export type DnsVerifier = {
|
export type DnsVerifier = {
|
||||||
// dns直接校验
|
// dns直接校验
|
||||||
dnsProviderType: string;
|
dnsProviderType?: string;
|
||||||
dnsProviderAccessId: number;
|
dnsProviderAccessId?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CnameVerifier = {
|
export type CnameVerifier = {
|
||||||
cnameRecord: string;
|
hostRecord: string;
|
||||||
|
domain: string;
|
||||||
|
recordValue: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HttpVerifier = {
|
export type HttpVerifier = {
|
||||||
|
|
|
@ -269,11 +269,11 @@ export class AcmeService {
|
||||||
throw new Error("不支持的校验类型", domainVerifyPlan.type);
|
throw new Error("不支持的校验类型", domainVerifyPlan.type);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.logger.info("未找到域名校验计划,使用默认的dnsProvider");
|
this.logger.warn(`未找到域名${fullDomain}的校验计划,使用默认的dnsProvider`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!dnsProvider) {
|
if (!dnsProvider) {
|
||||||
this.logger.error("dnsProvider不存在,无法申请证书");
|
throw new Error(`域名${fullDomain}没有匹配到任何校验方式,证书申请失败`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dnsChallenge = getChallenge("dns-01");
|
const dnsChallenge = getChallenge("dns-01");
|
||||||
|
|
|
@ -66,15 +66,15 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
{ value: "cname", label: "CNAME代理验证" },
|
{ value: "cname", label: "CNAME代理验证" },
|
||||||
{ value: "http", label: "HTTP文件验证" },
|
{ value: "http", label: "HTTP文件验证" },
|
||||||
{ value: "dnses", label: "多DNS提供商" },
|
{ value: "dnses", label: "多DNS提供商" },
|
||||||
{ value: "auto", label: "自动选择" },
|
{ value: "auto", label: "自动匹配" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
helper: `1. <b>DNS直接验证</b>:域名dns解析是在阿里云/腾讯云/华为云/CF/NameSilo/西数/火山/dns.la/京东云/51dns的,选它
|
helper: `1. <b>DNS直接验证</b>:域名dns解析是在阿里云/腾讯云/华为云/CF/NameSilo/西数/火山/dns.la/京东云/51dns的,选它
|
||||||
2. <b>CNAME代理验证</b>:支持任何注册商的域名,第一次需要手动添加CNAME记录(建议将DNS服务器修改为阿里云/腾讯云的,然后使用DNS直接验证)
|
2. <b>CNAME代理验证</b>:支持任何注册商的域名,第一次需要手动添加[CNAME记录](#/certd/cname/record)(建议将DNS服务器修改为阿里云/腾讯云的,然后使用DNS直接验证)
|
||||||
3. <b>HTTP文件验证</b>:不支持泛域名,需要配置网站文件上传
|
3. <b>HTTP文件验证</b>:不支持泛域名,需要配置网站文件上传
|
||||||
4. <b>多DNS提供商</b>:每个域名可以选择独立的DNS提供商
|
4. <b>多DNS提供商</b>:每个域名可以选择独立的DNS提供商
|
||||||
5. <b>自动选择</b>:需要在[域名管理](#/certd/cert/domain)中事先配置好校验方式
|
5. <b>自动匹配</b>:需要在[域名管理](#/certd/cert/domain)中事先配置好校验方式
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
challengeType!: string;
|
challengeType!: string;
|
||||||
|
@ -469,13 +469,13 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
for (const fullDomain of domains) {
|
for (const fullDomain of domains) {
|
||||||
const domain = fullDomain.replaceAll("*.", "");
|
const domain = fullDomain.replaceAll("*.", "");
|
||||||
const mainDomain = await domainParser.parse(domain);
|
const mainDomain = await domainParser.parse(domain);
|
||||||
const planSetting = verifyPlanSetting[mainDomain];
|
const planSetting: DomainVerifyPlanInput = verifyPlanSetting[mainDomain];
|
||||||
if (planSetting.type === "dns") {
|
if (planSetting.type === "dns") {
|
||||||
await this.createDnsDomainVerifyPlan(planSetting[mainDomain], domain, mainDomain);
|
plan[domain] = await this.createDnsDomainVerifyPlan(planSetting, domain, mainDomain);
|
||||||
} else if (planSetting.type === "cname") {
|
} else if (planSetting.type === "cname") {
|
||||||
await this.createCnameDomainVerifyPlan(domain, mainDomain);
|
plan[domain] = await this.createCnameDomainVerifyPlan(domain, mainDomain);
|
||||||
} else if (planSetting.type === "http") {
|
} else if (planSetting.type === "http") {
|
||||||
await this.createHttpDomainVerifyPlan(planSetting.httpVerifyPlan[domain], domain, mainDomain);
|
plan[domain] = await this.createHttpDomainVerifyPlan(planSetting.httpVerifyPlan[domain], domain, mainDomain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return plan;
|
return plan;
|
||||||
|
@ -486,7 +486,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
// domain list
|
// domain list
|
||||||
const domainList = new Set<string>();
|
const domainList = new Set<string>();
|
||||||
//整理域名
|
//整理域名
|
||||||
for (let domain in this.domains) {
|
for (let domain of domains) {
|
||||||
domain = domain.replaceAll("*.", "");
|
domain = domain.replaceAll("*.", "");
|
||||||
domainList.add(domain);
|
domainList.add(domain);
|
||||||
}
|
}
|
||||||
|
@ -563,7 +563,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
domain,
|
domain,
|
||||||
mainDomain,
|
mainDomain,
|
||||||
cnameVerifyPlan: {
|
cnameVerifyPlan: {
|
||||||
domain,
|
domain: cnameRecord.cnameProvider.domain,
|
||||||
fullRecord: cnameRecord.recordValue,
|
fullRecord: cnameRecord.recordValue,
|
||||||
dnsProvider,
|
dnsProvider,
|
||||||
},
|
},
|
||||||
|
|
|
@ -714,6 +714,7 @@ export default {
|
||||||
},
|
},
|
||||||
domain: {
|
domain: {
|
||||||
domainManager: "Domain Manager",
|
domainManager: "Domain Manager",
|
||||||
|
domainDescription: "used to auto apply for certificate", //管理域名的校验方式,用于申请证书时自动选择验证方式
|
||||||
domain: "Domain",
|
domain: "Domain",
|
||||||
challengeType: "Challenge Type",
|
challengeType: "Challenge Type",
|
||||||
dnsProviderType: "DNS Provider Type",
|
dnsProviderType: "DNS Provider Type",
|
||||||
|
@ -722,5 +723,7 @@ export default {
|
||||||
httpUploaderAccess: "HTTP Uploader Access",
|
httpUploaderAccess: "HTTP Uploader Access",
|
||||||
httpUploadRootDir: "HTTP Upload Root Dir",
|
httpUploadRootDir: "HTTP Upload Root Dir",
|
||||||
disabled: "Disabled",
|
disabled: "Disabled",
|
||||||
|
challengeSetting: "Challenge Setting",
|
||||||
|
gotoCnameTip: "Please go to CNAME Record Page",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,8 @@ export default {
|
||||||
search: "Search",
|
search: "Search",
|
||||||
enabled: "Enabled",
|
enabled: "Enabled",
|
||||||
disabled: "Disabled",
|
disabled: "Disabled",
|
||||||
|
enable: "Enable",
|
||||||
|
disable: "Disable",
|
||||||
edit: "Edit",
|
edit: "Edit",
|
||||||
delete: "Delete",
|
delete: "Delete",
|
||||||
create: "Create",
|
create: "Create",
|
||||||
|
|
|
@ -717,6 +717,7 @@ export default {
|
||||||
},
|
},
|
||||||
domain: {
|
domain: {
|
||||||
domainManager: "域名管理",
|
domainManager: "域名管理",
|
||||||
|
domainDescription: "管理域名的校验方式,用于申请证书时自动选择验证方式",
|
||||||
domain: "域名",
|
domain: "域名",
|
||||||
challengeType: "校验类型",
|
challengeType: "校验类型",
|
||||||
dnsProviderType: "DNS提供商类型",
|
dnsProviderType: "DNS提供商类型",
|
||||||
|
@ -725,5 +726,7 @@ export default {
|
||||||
httpUploaderAccess: "上传授权信息",
|
httpUploaderAccess: "上传授权信息",
|
||||||
httpUploadRootDir: "网站根路径",
|
httpUploadRootDir: "网站根路径",
|
||||||
disabled: "禁用/启用",
|
disabled: "禁用/启用",
|
||||||
|
challengeSetting: "校验配置",
|
||||||
|
gotoCnameTip: "CNAME域名配置请前往CNAME记录页面添加",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,8 @@ export default {
|
||||||
search: "搜索",
|
search: "搜索",
|
||||||
enabled: "已启用",
|
enabled: "已启用",
|
||||||
disabled: "已禁用",
|
disabled: "已禁用",
|
||||||
|
enable: "启用",
|
||||||
|
disable: "禁用",
|
||||||
edit: "修改",
|
edit: "修改",
|
||||||
delete: "删除",
|
delete: "删除",
|
||||||
create: "新增",
|
create: "新增",
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { useUserStore } from "/@/store/user";
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
import { Dicts } from "/@/components/plugins/lib/dicts";
|
import { Dicts } from "/@/components/plugins/lib/dicts";
|
||||||
import { createAccessApi } from "/@/views/certd/access/api";
|
import { createAccessApi } from "/@/views/certd/access/api";
|
||||||
|
import { Modal } from "ant-design-vue";
|
||||||
|
|
||||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -73,13 +74,20 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
delRequest,
|
delRequest,
|
||||||
},
|
},
|
||||||
tabs: {
|
tabs: {
|
||||||
name: "status",
|
name: "challengeType",
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
},
|
},
|
||||||
|
form: {
|
||||||
|
beforeSubmit({ form }) {
|
||||||
|
if (form.challengeType === "cname") {
|
||||||
|
throw new Error("CNAME方式请前往CNAME记录页面进行管理");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
columns: {
|
columns: {
|
||||||
id: {
|
id: {
|
||||||
title: "ID",
|
title: "ID",
|
||||||
|
@ -114,11 +122,28 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
title: t("certd.domain.challengeType"),
|
title: t("certd.domain.challengeType"),
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
dict: Dicts.challengeTypeDict,
|
dict: Dicts.challengeTypeDict,
|
||||||
|
search: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
required: true,
|
required: true,
|
||||||
|
valueChange({ value }) {
|
||||||
|
if (value === "cname") {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t("certd.domain.gotoCnameTip"),
|
||||||
|
async onOk() {
|
||||||
|
router.push({
|
||||||
|
path: "/certd/cname/record",
|
||||||
|
});
|
||||||
|
crudExpose.getFormWrapperRef().close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
sorter: true,
|
sorter: true,
|
||||||
|
show: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
@ -196,6 +221,10 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
name: "AccessSelector",
|
name: "AccessSelector",
|
||||||
|
vModel: "modelValue",
|
||||||
|
type: compute(({ form }) => {
|
||||||
|
return form.httpUploaderType;
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
show: compute(({ form }) => {
|
show: compute(({ form }) => {
|
||||||
return form.challengeType === "http";
|
return form.challengeType === "http";
|
||||||
|
@ -226,16 +255,17 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
challengeSetting: {
|
challengeSetting: {
|
||||||
title: "校验配置",
|
title: t("certd.domain.challengeSetting"),
|
||||||
type: "text",
|
type: "text",
|
||||||
form: { show: false },
|
form: { show: false },
|
||||||
column: {
|
column: {
|
||||||
width: 400,
|
width: 600,
|
||||||
conditionalRender: false,
|
conditionalRender: false,
|
||||||
cellRender({ row }) {
|
cellRender({ row }) {
|
||||||
if (row.challengeType === "dns") {
|
if (row.challengeType === "dns") {
|
||||||
return (
|
return (
|
||||||
<div class={"flex"}>
|
<div class={"flex"}>
|
||||||
|
<fs-values-format modelValue={row.challengeType} dict={Dicts.challengeTypeDict} color={"auto"}></fs-values-format>
|
||||||
<fs-values-format modelValue={row.dnsProviderType} dict={dnsProviderTypeDict} color={"auto"}></fs-values-format>
|
<fs-values-format modelValue={row.dnsProviderType} dict={dnsProviderTypeDict} color={"auto"}></fs-values-format>
|
||||||
<fs-values-format class={"ml-5"} modelValue={row.dnsProviderAccess} dict={accessDict} color={"auto"}></fs-values-format>
|
<fs-values-format class={"ml-5"} modelValue={row.dnsProviderAccess} dict={accessDict} color={"auto"}></fs-values-format>
|
||||||
</div>
|
</div>
|
||||||
|
@ -243,9 +273,10 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
} else if (row.challengeType === "http") {
|
} else if (row.challengeType === "http") {
|
||||||
return (
|
return (
|
||||||
<div class={"flex"}>
|
<div class={"flex"}>
|
||||||
|
<fs-values-format modelValue={row.challengeType} dict={Dicts.challengeTypeDict} color={"auto"}></fs-values-format>
|
||||||
<fs-values-format modelValue={row.httpUploaderType} dict={httpUploaderTypeDict} color={"auto"}></fs-values-format>
|
<fs-values-format modelValue={row.httpUploaderType} dict={httpUploaderTypeDict} color={"auto"}></fs-values-format>
|
||||||
<fs-values-format class={"ml-5"} modelValue={row.httpUploaderAccess} dict={accessDict} color={"auto"}></fs-values-format>
|
<fs-values-format class={"ml-5"} modelValue={row.httpUploaderAccess} dict={accessDict} color={"auto"}></fs-values-format>
|
||||||
<a-tag class={"ml-5"}>{row.httpUploadRootDir}</a-tag>
|
<a-tag class={"ml-5 flex items-center"}>路径:{row.httpUploadRootDir}</a-tag>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -255,10 +286,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
disabled: {
|
disabled: {
|
||||||
title: t("certd.domain.disabled"),
|
title: t("certd.domain.disabled"),
|
||||||
type: "dict-switch",
|
type: "dict-switch",
|
||||||
|
search: { show: true },
|
||||||
dict: dict({
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "启用", value: false, color: "green" },
|
{ label: t("common.enabled"), value: false, color: "green" },
|
||||||
{ label: "禁用", value: true, color: "red" },
|
{ label: t("common.disabled"), value: true, color: "red" },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
{{ t("certd.domain.domainManager") }}
|
{{ t("certd.domain.domainManager") }}
|
||||||
<span class="sub"> </span>
|
<span class="sub">
|
||||||
|
{{ t("certd.domain.domainDescription") }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||||
|
|
|
@ -7,6 +7,8 @@ import {SubDomainService} from "../../pipeline/service/sub-domain-service.js";
|
||||||
import {DomainParser} from "@certd/plugin-cert/dist/dns-provider/domain-parser.js";
|
import {DomainParser} from "@certd/plugin-cert/dist/dns-provider/domain-parser.js";
|
||||||
import {DomainVerifiers} from "@certd/plugin-cert";
|
import {DomainVerifiers} from "@certd/plugin-cert";
|
||||||
import { SubDomainsGetter } from '../../pipeline/service/getter/sub-domain-getter.js';
|
import { SubDomainsGetter } from '../../pipeline/service/getter/sub-domain-getter.js';
|
||||||
|
import { CnameRecordService } from '../../cname/service/cname-record-service.js';
|
||||||
|
import { CnameRecordEntity } from "../../cname/entity/cname-record.js";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,6 +25,9 @@ export class DomainService extends BaseService<DomainEntity> {
|
||||||
@Inject()
|
@Inject()
|
||||||
subDomainService: SubDomainService;
|
subDomainService: SubDomainService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
cnameRecordService: CnameRecordService;
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
getRepository() {
|
getRepository() {
|
||||||
return this.repository;
|
return this.repository;
|
||||||
|
@ -96,10 +101,12 @@ export class DomainService extends BaseService<DomainEntity> {
|
||||||
//去重
|
//去重
|
||||||
allDomains = [...new Set(allDomains)]
|
allDomains = [...new Set(allDomains)]
|
||||||
|
|
||||||
|
//从 domain 表中获取配置
|
||||||
const domainRecords = await this.find({
|
const domainRecords = await this.find({
|
||||||
where: {
|
where: {
|
||||||
domain: In(allDomains),
|
domain: In(allDomains),
|
||||||
userId
|
userId,
|
||||||
|
disabled:false,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -107,16 +114,28 @@ export class DomainService extends BaseService<DomainEntity> {
|
||||||
pre[item.domain] = item
|
pre[item.domain] = item
|
||||||
return pre
|
return pre
|
||||||
}, {})
|
}, {})
|
||||||
const cnameMap = domainRecords.filter(item=>item.challengeType === 'cname').reduce((pre, item) => {
|
|
||||||
pre[item.domain] = item
|
|
||||||
return pre
|
|
||||||
}, {})
|
|
||||||
const httpMap = domainRecords.filter(item=>item.challengeType === 'http').reduce((pre, item) => {
|
const httpMap = domainRecords.filter(item=>item.challengeType === 'http').reduce((pre, item) => {
|
||||||
pre[item.domain] = item
|
pre[item.domain] = item
|
||||||
return pre
|
return pre
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
|
|
||||||
|
//从cname record表中获取配置
|
||||||
|
const cnameRecords = await this.cnameRecordService.find({
|
||||||
|
where: {
|
||||||
|
domain: In(allDomains),
|
||||||
|
userId,
|
||||||
|
status: "valid",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const cnameMap = cnameRecords.reduce((pre, item) => {
|
||||||
|
pre[item.domain] = item
|
||||||
|
return pre
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
//构建域名验证计划
|
||||||
const domainVerifiers:DomainVerifiers = {}
|
const domainVerifiers:DomainVerifiers = {}
|
||||||
|
|
||||||
for (const domain of domains) {
|
for (const domain of domains) {
|
||||||
|
@ -130,19 +149,21 @@ export class DomainService extends BaseService<DomainEntity> {
|
||||||
type: 'dns',
|
type: 'dns',
|
||||||
dns: {
|
dns: {
|
||||||
dnsProviderType: dnsRecord.dnsProviderType,
|
dnsProviderType: dnsRecord.dnsProviderType,
|
||||||
dnsProviderAccessId: dnsRecord.dnsProviderAccessId
|
dnsProviderAccessId: dnsRecord.dnsProviderAccess
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const cnameRecord = cnameMap[mainDomain]
|
const cnameRecord:CnameRecordEntity = cnameMap[mainDomain]
|
||||||
if (cnameRecord) {
|
if (cnameRecord) {
|
||||||
domainVerifiers[domain] = {
|
domainVerifiers[domain] = {
|
||||||
domain,
|
domain,
|
||||||
mainDomain,
|
mainDomain,
|
||||||
type: 'cname',
|
type: 'cname',
|
||||||
cname: {
|
cname: {
|
||||||
cnameRecord: cnameRecord.cnameRecord
|
domain: cnameRecord.domain,
|
||||||
|
hostRecord: cnameRecord.hostRecord,
|
||||||
|
recordValue: cnameRecord.recordValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Reference in New Issue