mirror of https://github.com/certd/certd
chore:
parent
ec342708b2
commit
03b751fa13
|
@ -9,16 +9,24 @@ import { IDnsProvider, parseDomain } from "../../dns-provider/index.js";
|
||||||
import { HttpChallengeUploader } from "./uploads/api.js";
|
import { HttpChallengeUploader } from "./uploads/api.js";
|
||||||
|
|
||||||
export type CnameVerifyPlan = {
|
export type CnameVerifyPlan = {
|
||||||
|
type?: string;
|
||||||
domain: string;
|
domain: string;
|
||||||
fullRecord: string;
|
fullRecord: string;
|
||||||
dnsProvider: IDnsProvider;
|
dnsProvider: IDnsProvider;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type HttpVerifyPlan = {
|
||||||
|
type: string;
|
||||||
|
domain: string;
|
||||||
|
httpUploader: HttpChallengeUploader;
|
||||||
|
};
|
||||||
|
|
||||||
export type DomainVerifyPlan = {
|
export type DomainVerifyPlan = {
|
||||||
domain: string;
|
domain: string;
|
||||||
type: "cname" | "dns" | "http";
|
type: "cname" | "dns" | "http";
|
||||||
dnsProvider?: IDnsProvider;
|
dnsProvider?: IDnsProvider;
|
||||||
cnameVerifyPlan?: Record<string, CnameVerifyPlan>;
|
cnameVerifyPlan?: Record<string, CnameVerifyPlan>;
|
||||||
|
httpVerifyPlan?: Record<string, HttpVerifyPlan>;
|
||||||
};
|
};
|
||||||
export type DomainsVerifyPlan = {
|
export type DomainsVerifyPlan = {
|
||||||
[key: string]: DomainVerifyPlan;
|
[key: string]: DomainVerifyPlan;
|
||||||
|
@ -171,7 +179,23 @@ export class AcmeService {
|
||||||
const filePath = `.well-known/acme-challenge/${challenge.token}`;
|
const filePath = `.well-known/acme-challenge/${challenge.token}`;
|
||||||
const fileContents = keyAuthorization;
|
const fileContents = keyAuthorization;
|
||||||
this.logger.info(`校验 ${fullDomain} ,准备上传文件:${filePath}`);
|
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}】成功`);
|
this.logger.info(`上传文件【${filePath}】成功`);
|
||||||
} else if (challenge.type === "dns-01") {
|
} else if (challenge.type === "dns-01") {
|
||||||
/* dns-01 */
|
/* dns-01 */
|
||||||
|
@ -204,8 +228,11 @@ export class AcmeService {
|
||||||
} else {
|
} else {
|
||||||
this.logger.error("未找到域名Cname校验计划,使用默认的dnsProvider");
|
this.logger.error("未找到域名Cname校验计划,使用默认的dnsProvider");
|
||||||
}
|
}
|
||||||
|
} else if (domainVerifyPlan.type === "http") {
|
||||||
|
throw new Error("切换为http校验");
|
||||||
} else {
|
} else {
|
||||||
this.logger.error("不支持的校验类型", domainVerifyPlan.type);
|
// this.logger.error("不支持的校验类型", domainVerifyPlan.type);
|
||||||
|
throw new Error("不支持的校验类型", domainVerifyPlan.type);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.logger.info("未找到域名校验计划,使用默认的dnsProvider");
|
this.logger.info("未找到域名校验计划,使用默认的dnsProvider");
|
||||||
|
@ -346,7 +373,7 @@ export class AcmeService {
|
||||||
email: email,
|
email: email,
|
||||||
termsOfServiceAgreed: true,
|
termsOfServiceAgreed: true,
|
||||||
skipChallengeVerification: this.skipLocalVerify,
|
skipChallengeVerification: this.skipLocalVerify,
|
||||||
challengePriority: ["dns-01"],
|
challengePriority: ["dns-01", "http-01"],
|
||||||
challengeCreateFn: async (
|
challengeCreateFn: async (
|
||||||
authz: acme.Authorization,
|
authz: acme.Authorization,
|
||||||
challenge: Challenge,
|
challenge: Challenge,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { CancelError, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
import { CancelError, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||||
import { utils } from "@certd/basic";
|
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 { AcmeService } from "./acme.js";
|
||||||
import * as _ from "lodash-es";
|
import * as _ from "lodash-es";
|
||||||
import { createDnsProvider, DnsProviderContext, IDnsProvider } from "../../dns-provider/index.js";
|
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 { CertApplyBasePlugin } from "./base.js";
|
||||||
import { GoogleClient } from "../../libs/google.js";
|
import { GoogleClient } from "../../libs/google.js";
|
||||||
import { EabAccess } from "../../access";
|
import { EabAccess } from "../../access";
|
||||||
import { HttpChallengeUploader } from "./uploads/api";
|
|
||||||
import { httpChallengeUploaderFactory } from "./uploads/factory.js";
|
import { httpChallengeUploaderFactory } from "./uploads/factory.js";
|
||||||
|
|
||||||
export type { CertInfo };
|
export type { CertInfo };
|
||||||
|
@ -67,8 +66,9 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
helper:
|
helper: `DNS直接验证:域名是在阿里云、腾讯云、华为云、Cloudflare、NameSilo、西数注册的,选它;
|
||||||
"DNS直接验证:域名是在阿里云、腾讯云、华为云、Cloudflare、NameSilo、西数注册的,选它。\nCNAME代理验证:支持任何注册商注册的域名,但第一次需要手动添加CNAME记录",
|
CNAME代理验证:支持任何注册商注册的域名,但第一次需要手动添加CNAME记录;
|
||||||
|
HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
||||||
})
|
})
|
||||||
challengeType!: string;
|
challengeType!: string;
|
||||||
|
|
||||||
|
@ -127,75 +127,13 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
})
|
})
|
||||||
dnsProviderAccess!: number;
|
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({
|
@TaskInput({
|
||||||
title: "域名验证配置",
|
title: "域名验证配置",
|
||||||
component: {
|
component: {
|
||||||
name: "domains-verify-plan-editor",
|
name: "domains-verify-plan-editor",
|
||||||
},
|
},
|
||||||
rules: [{ type: "checkCnameVerifyPlan" }],
|
rules: [{ type: "checkDomainVerifyPlan" }],
|
||||||
required: true,
|
required: true,
|
||||||
helper: "如果选择CNAME方式,请按照上面的显示,给要申请证书的域名添加CNAME记录,添加后,点击验证,验证成功后不要删除记录,申请和续期证书会一直用它",
|
|
||||||
col: {
|
col: {
|
||||||
span: 24,
|
span: 24,
|
||||||
},
|
},
|
||||||
|
@ -203,10 +141,20 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
component:{
|
component:{
|
||||||
domains: ctx.compute(({form})=>{
|
domains: ctx.compute(({form})=>{
|
||||||
return form.domains
|
return form.domains
|
||||||
|
}),
|
||||||
|
defaultType: ctx.compute(({form})=>{
|
||||||
|
return form.challengeType || 'cname'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
show: ctx.compute(({form})=>{
|
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 dnsProvider: IDnsProvider = null;
|
||||||
let domainsVerifyPlan: DomainsVerifyPlan = null;
|
let domainsVerifyPlan: DomainsVerifyPlan = null;
|
||||||
let httpUploader: HttpChallengeUploader = null;
|
if (this.challengeType === "cname" || this.challengeType === "http") {
|
||||||
if (this.challengeType === "cname") {
|
|
||||||
domainsVerifyPlan = await this.createDomainsVerifyPlan();
|
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 {
|
} else {
|
||||||
const dnsProviderType = this.dnsProviderType;
|
const dnsProviderType = this.dnsProviderType;
|
||||||
const access = await this.ctx.accessService.getById(this.dnsProviderAccess);
|
const access = await this.ctx.accessService.getById(this.dnsProviderAccess);
|
||||||
|
@ -414,7 +355,6 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
domains,
|
domains,
|
||||||
dnsProvider,
|
dnsProvider,
|
||||||
domainsVerifyPlan,
|
domainsVerifyPlan,
|
||||||
httpUploader,
|
|
||||||
csrInfo,
|
csrInfo,
|
||||||
isTest: false,
|
isTest: false,
|
||||||
privateKeyType: this.privateKeyType,
|
privateKeyType: this.privateKeyType,
|
||||||
|
@ -449,10 +389,11 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
const domainVerifyPlan = this.domainsVerifyPlan[domain];
|
const domainVerifyPlan = this.domainsVerifyPlan[domain];
|
||||||
let dnsProvider = null;
|
let dnsProvider = null;
|
||||||
const cnameVerifyPlan: Record<string, CnameVerifyPlan> = {};
|
const cnameVerifyPlan: Record<string, CnameVerifyPlan> = {};
|
||||||
|
const httpVerifyPlan: Record<string, HttpVerifyPlan> = {};
|
||||||
if (domainVerifyPlan.type === "dns") {
|
if (domainVerifyPlan.type === "dns") {
|
||||||
const access = await this.ctx.accessService.getById(domainVerifyPlan.dnsProviderAccessId);
|
const access = await this.ctx.accessService.getById(domainVerifyPlan.dnsProviderAccessId);
|
||||||
dnsProvider = await this.createDnsProvider(domainVerifyPlan.dnsProviderType, access);
|
dnsProvider = await this.createDnsProvider(domainVerifyPlan.dnsProviderType, access);
|
||||||
} else {
|
} else if (domainVerifyPlan.type === "cname") {
|
||||||
for (const key in domainVerifyPlan.cnameVerifyPlan) {
|
for (const key in domainVerifyPlan.cnameVerifyPlan) {
|
||||||
const cnameRecord = await this.ctx.cnameProxyService.getByDomain(key);
|
const cnameRecord = await this.ctx.cnameProxyService.getByDomain(key);
|
||||||
let dnsProvider = cnameRecord.commonDnsProvider;
|
let dnsProvider = cnameRecord.commonDnsProvider;
|
||||||
|
@ -460,17 +401,39 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
dnsProvider = await this.createDnsProvider(cnameRecord.cnameProvider.dnsProviderType, cnameRecord.cnameProvider.access);
|
dnsProvider = await this.createDnsProvider(cnameRecord.cnameProvider.dnsProviderType, cnameRecord.cnameProvider.access);
|
||||||
}
|
}
|
||||||
cnameVerifyPlan[key] = {
|
cnameVerifyPlan[key] = {
|
||||||
|
type: "cname",
|
||||||
domain: cnameRecord.cnameProvider.domain,
|
domain: cnameRecord.cnameProvider.domain,
|
||||||
fullRecord: cnameRecord.recordValue,
|
fullRecord: cnameRecord.recordValue,
|
||||||
dnsProvider,
|
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] = {
|
plan[domain] = {
|
||||||
domain,
|
domain,
|
||||||
type: domainVerifyPlan.type,
|
type: domainVerifyPlan.type,
|
||||||
dnsProvider,
|
dnsProvider,
|
||||||
cnameVerifyPlan,
|
cnameVerifyPlan,
|
||||||
|
httpVerifyPlan,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return plan;
|
return plan;
|
||||||
|
|
|
@ -1,24 +1,33 @@
|
||||||
|
import { HttpChallengeUploadContext } from "./api";
|
||||||
|
|
||||||
export class HttpChallengeUploaderFactory {
|
export class HttpChallengeUploaderFactory {
|
||||||
async getClassByType(type: string) {
|
async getClassByType(type: string) {
|
||||||
if (type === "alioss") {
|
if (type === "alioss") {
|
||||||
return (await import("./impls/alioss.js")).AliossHttpChallengeUploader;
|
const module = await import("./impls/alioss.js");
|
||||||
|
return module.AliossHttpChallengeUploader;
|
||||||
} else if (type === "ssh") {
|
} 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") {
|
} 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") {
|
} 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") {
|
} else if (type === "qiniuoss") {
|
||||||
return (await import("./impls/qiniuoss.js")).QiniuOssHttpChallengeUploader;
|
const module = await import("./impls/qiniuoss.js");
|
||||||
|
return module.QiniuOssHttpChallengeUploader;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`暂不支持此文件上传方式: ${type}`);
|
throw new Error(`暂不支持此文件上传方式: ${type}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createUploaderByType(type: string, opts: { rootDir: string; access: any }) {
|
async createUploaderByType(type: string, opts: { rootDir: string; access: any; ctx: HttpChallengeUploadContext }) {
|
||||||
const cls = this.getClassByType(type);
|
const cls = await this.getClassByType(type);
|
||||||
if (cls) {
|
if (cls) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return new cls(opts);
|
const instance = new cls(opts);
|
||||||
|
await instance.setCtx(opts.ctx);
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { BaseHttpChallengeUploader } from "../api";
|
import { BaseHttpChallengeUploader } from "../api.js";
|
||||||
import { AliossAccess, AliyunAccess } from "@certd/plugin-lib";
|
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<AliossAccess> {
|
export class AliossHttpChallengeUploader extends BaseHttpChallengeUploader<AliossAccess> {
|
||||||
async upload(filePath: string, fileContent: string) {
|
async upload(filePath: string, fileContent: string) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { BaseHttpChallengeUploader } from "../api";
|
import { BaseHttpChallengeUploader } from "../api.js";
|
||||||
import { FtpAccess } from "@certd/plugin-lib";
|
import { FtpAccess, FtpClient } from "@certd/plugin-lib";
|
||||||
import { FtpClient } from "@certd/plugin-lib/dist/ftp/client";
|
|
||||||
|
|
||||||
export class FtpHttpChallengeUploader extends BaseHttpChallengeUploader<FtpAccess> {
|
export class FtpHttpChallengeUploader extends BaseHttpChallengeUploader<FtpAccess> {
|
||||||
async upload(fileName: string, fileContent: string) {
|
async upload(fileName: string, fileContent: string) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { BaseHttpChallengeUploader } from "../api";
|
import { BaseHttpChallengeUploader } from "../api.js";
|
||||||
import { FtpAccess } from "@certd/plugin-lib";
|
import { QiniuOssAccess } from "@certd/plugin-lib/dist/qiniu/access-oss";
|
||||||
|
|
||||||
export class QiniuOssHttpChallengeUploader extends BaseHttpChallengeUploader<FtpAccess> {
|
export class QiniuOssHttpChallengeUploader extends BaseHttpChallengeUploader<QiniuOssAccess> {
|
||||||
async upload(fileName: string, fileContent: string) {
|
async upload(fileName: string, fileContent: string) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { BaseHttpChallengeUploader } from "../api";
|
import { BaseHttpChallengeUploader } from "../api.js";
|
||||||
import { SshAccess, SshClient } from "@certd/plugin-lib";
|
import { SshAccess, SshClient } from "@certd/plugin-lib";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { BaseHttpChallengeUploader } from "../api";
|
import { BaseHttpChallengeUploader } from "../api.js";
|
||||||
import { FtpAccess } from "@certd/plugin-lib";
|
import { TencentCosAccess } from "@certd/plugin-lib/dist/tencent/access-cos";
|
||||||
|
|
||||||
export class TencentCosHttpChallengeUploader extends BaseHttpChallengeUploader<FtpAccess> {
|
export class TencentCosHttpChallengeUploader extends BaseHttpChallengeUploader<TencentCosAccess> {
|
||||||
async upload(fileName: string, fileContent: string) {
|
async upload(fileName: string, fileContent: string) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export * from "./base-client.js";
|
export * from "./base-client.js";
|
||||||
export * from "./ssl-client.js";
|
export * from "./ssl-client.js";
|
||||||
|
export * from "./oss-client.js";
|
||||||
|
|
|
@ -24,10 +24,7 @@ defineOptions({
|
||||||
name: "CnameVerifyPlan"
|
name: "CnameVerifyPlan"
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits(["update:modelValue", "change"]);
|
||||||
"update:modelValue": any;
|
|
||||||
change: Record<string, any>;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: Record<string, any>;
|
modelValue: Record<string, any>;
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
<template>
|
||||||
|
<table class="http-verify-plan">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 160px">网站域名</td>
|
||||||
|
<td style="width: 100px; text-align: center">上传方式</td>
|
||||||
|
<td style="width: 150px">上传授权</td>
|
||||||
|
<td style="width: 200px">网站根目录路径</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody v-if="records" class="http-record-body">
|
||||||
|
<template v-for="(item, key) of records" :key="key">
|
||||||
|
<tr>
|
||||||
|
<td class="domain">
|
||||||
|
{{ item.domain }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<fs-dict-select v-model:value="item.httpUploaderType" :dict="uploaderTypeDict" @change="onRecordChange"></fs-dict-select>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<access-selector v-model="item.httpUploaderAccess" :type="item.httpUploaderType" @change="onRecordChange"></access-selector>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a-input v-model:value="item.httpUploadRootDir" placeholder="网站根目录,如:/www/wwwroot" @change="onRecordChange"></a-input>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Ref, ref, watch } from "vue";
|
||||||
|
import { HttpRecord } from "/@/components/plugins/cert/domains-verify-plan-editor/type";
|
||||||
|
import { dict } from "@fast-crud/fast-crud";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "HttpVerifyPlan"
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:modelValue", "change"]);
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: Record<string, any>;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const records: Ref<Record<string, HttpRecord>> = ref({});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => {
|
||||||
|
return props.modelValue;
|
||||||
|
},
|
||||||
|
(value: any) => {
|
||||||
|
if (value) {
|
||||||
|
records.value = {
|
||||||
|
...value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function onRecordChange() {
|
||||||
|
emit("update:modelValue", records.value);
|
||||||
|
emit("change", records.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploaderTypeDict = dict({
|
||||||
|
data: [
|
||||||
|
{ label: "SFTP/SSH", value: "ssh" },
|
||||||
|
{ label: "FTP", value: "ftp" },
|
||||||
|
{ label: "阿里云OSS", value: "alioss" },
|
||||||
|
{ label: "腾讯云COS", value: "tencentcos" },
|
||||||
|
{ label: "七牛OSS", value: "qiniuoss" }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.http-verify-plan {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
tbody tr td {
|
||||||
|
border-top: 1px solid #e8e8e8 !important;
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
border: 0 !important;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//&:last-child {
|
||||||
|
// td {
|
||||||
|
// border-bottom: 0 !important;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -13,7 +13,7 @@
|
||||||
<table class="plan-table">
|
<table class="plan-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>域名</th>
|
<th style="min-width: 100px">主域名</th>
|
||||||
<th>验证方式</th>
|
<th>验证方式</th>
|
||||||
<th>验证计划</th>
|
<th>验证计划</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
<cname-verify-plan v-model="item.cnameVerifyPlan" @change="onPlanChanged" />
|
<cname-verify-plan v-model="item.cnameVerifyPlan" @change="onPlanChanged" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="item.type === 'http'" class="plan-http">
|
<div v-if="item.type === 'http'" class="plan-http">
|
||||||
<cname-verify-plan v-model="item.cnameVerifyPlan" @change="onPlanChanged" />
|
<http-verify-plan v-model="item.httpVerifyPlan" @change="onPlanChanged" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
@ -79,6 +79,7 @@ import { ref, watch } from "vue";
|
||||||
import { dict, FsDictSelect } from "@fast-crud/fast-crud";
|
import { dict, FsDictSelect } from "@fast-crud/fast-crud";
|
||||||
import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
|
import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
|
||||||
import CnameVerifyPlan from "./cname-verify-plan.vue";
|
import CnameVerifyPlan from "./cname-verify-plan.vue";
|
||||||
|
import HttpVerifyPlan from "./http-verify-plan.vue";
|
||||||
import psl from "psl";
|
import psl from "psl";
|
||||||
import { Form } from "ant-design-vue";
|
import { Form } from "ant-design-vue";
|
||||||
import { DomainsVerifyPlanInput } from "./type";
|
import { DomainsVerifyPlanInput } from "./type";
|
||||||
|
@ -95,12 +96,17 @@ const challengeTypeOptions = ref<any[]>([
|
||||||
{
|
{
|
||||||
label: "CNAME验证",
|
label: "CNAME验证",
|
||||||
value: "cname"
|
value: "cname"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "HTTP验证",
|
||||||
|
value: "http"
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue?: DomainsVerifyPlanInput;
|
modelValue?: DomainsVerifyPlanInput;
|
||||||
domains?: string[];
|
domains?: string[];
|
||||||
|
defaultType?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -132,6 +138,16 @@ function showError(error: string) {
|
||||||
|
|
||||||
type DomainGroup = Record<string, Record<string, CnameRecord>>;
|
type DomainGroup = Record<string, Record<string, CnameRecord>>;
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => {
|
||||||
|
return props.defaultType;
|
||||||
|
},
|
||||||
|
(value: string) => {
|
||||||
|
planRef.value = {};
|
||||||
|
onDomainsChanged(props.domains);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
function onDomainsChanged(domains: string[]) {
|
function onDomainsChanged(domains: string[]) {
|
||||||
console.log("域名变化", domains);
|
console.log("域名变化", domains);
|
||||||
if (domains == null) {
|
if (domains == null) {
|
||||||
|
@ -155,9 +171,7 @@ function onDomainsChanged(domains: string[]) {
|
||||||
group = {};
|
group = {};
|
||||||
domainGroups[mainDomain] = group;
|
domainGroups[mainDomain] = group;
|
||||||
}
|
}
|
||||||
group[domain] = {
|
group[domain] = {};
|
||||||
id: 0
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const domain in domainGroups) {
|
for (const domain in domainGroups) {
|
||||||
|
@ -166,27 +180,43 @@ function onDomainsChanged(domains: string[]) {
|
||||||
if (!planItem) {
|
if (!planItem) {
|
||||||
planItem = {
|
planItem = {
|
||||||
domain,
|
domain,
|
||||||
type: "cname",
|
//@ts-ignore
|
||||||
|
type: props.defaultType || "cname",
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
cnameVerifyPlan: {
|
cnameVerifyPlan: {
|
||||||
...subDomains
|
...subDomains
|
||||||
|
},
|
||||||
|
//@ts-ignore
|
||||||
|
httpVerifyPlan: {
|
||||||
|
...subDomains
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
planRef.value[domain] = planItem;
|
planRef.value[domain] = planItem;
|
||||||
} else {
|
}
|
||||||
const cnamePlan = planItem.cnameVerifyPlan;
|
const cnamePlan = planItem.cnameVerifyPlan;
|
||||||
for (const subDomain in subDomains) {
|
for (const subDomain in subDomains) {
|
||||||
if (!cnamePlan[subDomain]) {
|
//@ts-ignore
|
||||||
//@ts-ignore
|
cnamePlan[subDomain] = {
|
||||||
cnamePlan[subDomain] = {
|
id: 0
|
||||||
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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import Validator from "async-validator";
|
import Validator from "async-validator";
|
||||||
import { DomainsVerifyPlanInput } from "./type";
|
import { DomainsVerifyPlanInput } from "./type";
|
||||||
|
|
||||||
function checkCnameVerifyPlan(rule, value: DomainsVerifyPlanInput) {
|
function checkDomainVerifyPlan(rule: any, value: DomainsVerifyPlanInput) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (const domain in value) {
|
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);
|
const subDomains = Object.keys(value[domain].cnameVerifyPlan);
|
||||||
if (subDomains.length > 0) {
|
if (subDomains.length > 0) {
|
||||||
for (const subDomain of subDomains) {
|
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) {
|
if (value[domain].dnsProviderType == null || value[domain].dnsProviderAccessId == null) {
|
||||||
throw new Error(`DNS模式下,域名${domain}的DNS类型和授权信息必须填写`);
|
throw new Error(`DNS模式下,域名${domain}的DNS类型和授权信息必须填写`);
|
||||||
}
|
}
|
||||||
|
@ -25,4 +40,4 @@ function checkCnameVerifyPlan(rule, value: DomainsVerifyPlanInput) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// 注册自定义验证器
|
// 注册自定义验证器
|
||||||
Validator.register("checkCnameVerifyPlan", checkCnameVerifyPlan);
|
Validator.register("checkDomainVerifyPlan", checkDomainVerifyPlan);
|
||||||
|
|
Loading…
Reference in New Issue