From 785bee2b3955493b60892677a6ba5a89044df531 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sat, 12 Jul 2025 23:00:04 +0800 Subject: [PATCH] chore: auto --- .../src/user/access/service/access-service.ts | 25 ++++- .../plugin-cert/src/dns-provider/api.ts | 29 ++++++ .../src/plugin/cert-plugin/index.ts | 76 ++++++++++++++- .../src/components/plugins/lib/dicts.ts | 6 +- .../src/views/certd/access/api.ts | 8 ++ .../src/views/certd/cert/domain/crud.tsx | 96 +++++++++++++++++-- .../user/pipeline/access-controller.ts | 6 ++ 7 files changed, 230 insertions(+), 16 deletions(-) diff --git a/packages/libs/lib-server/src/user/access/service/access-service.ts b/packages/libs/lib-server/src/user/access/service/access-service.ts index bc19f964..f99f6330 100644 --- a/packages/libs/lib-server/src/user/access/service/access-service.ts +++ b/packages/libs/lib-server/src/user/access/service/access-service.ts @@ -1,6 +1,6 @@ import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core'; import {InjectEntityModel} from '@midwayjs/typeorm'; -import {Repository} from 'typeorm'; +import { In, Repository } from "typeorm"; import {AccessGetter, BaseService, PageReq, PermissionException, ValidateException} from '../../../index.js'; import {AccessEntity} from '../entity/access.js'; import {AccessDefine, accessRegistry, newAccess} from '@certd/pipeline'; @@ -175,4 +175,27 @@ export class AccessService extends BaseService { getDefineByType(type: string) { return accessRegistry.getDefine(type); } + + + async getSimpleByIds(ids: number[], userId: any) { + if (ids.length === 0) { + return []; + } + if (!userId) { + return []; + } + return await this.repository.find({ + where: { + id: In(ids), + userId, + }, + select: { + id: true, + name: true, + type: true, + userId:true + }, + }); + + } } diff --git a/packages/plugins/plugin-cert/src/dns-provider/api.ts b/packages/plugins/plugin-cert/src/dns-provider/api.ts index 523159fe..a8817c21 100644 --- a/packages/plugins/plugin-cert/src/dns-provider/api.ts +++ b/packages/plugins/plugin-cert/src/dns-provider/api.ts @@ -59,3 +59,32 @@ export interface ISubDomainsGetter { export interface IDomainParser { parse(fullDomain: string): Promise; } + +export type DnsVerifier = { + // dns直接校验 + dnsProviderType: string; + dnsProviderAccessId: number; +}; + +export type CnameVerifier = { + cnameRecord: string; +}; + +export type HttpVerifier = { + // http校验 + httpUploaderType: string; + httpUploaderAccess: string; + httpUploadRootDir: string; +}; +export type DomainVerifier = { + domain: string; + mainDomain: string; + challengeType: string; + dns?: DnsVerifier; + cname?: CnameVerifier; + http?: HttpVerifier; +}; + +export interface IDomainVerifierGetter { + getVerifiers(domains: string[]): Promise; +} 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 6b6d864a..b08c74b7 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts @@ -4,7 +4,7 @@ import { utils } from "@certd/basic"; 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, ISubDomainsGetter } from "../../dns-provider/index.js"; +import { createDnsProvider, DnsProviderContext, DomainVerifier, IDnsProvider, IDomainVerifierGetter, ISubDomainsGetter } from "../../dns-provider/index.js"; import { CertReader } from "./cert-reader.js"; import { CertApplyBasePlugin } from "./base.js"; import { GoogleClient } from "../../libs/google.js"; @@ -66,6 +66,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin { { value: "cname", label: "CNAME代理验证" }, { value: "http", label: "HTTP文件验证" }, { value: "dnses", label: "多DNS提供商" }, + { value: "auto", label: "自动选择" }, ], }, required: true, @@ -73,6 +74,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin { 2. CNAME代理验证:支持任何注册商的域名,第一次需要手动添加CNAME记录(建议将DNS服务器修改为阿里云/腾讯云的,然后使用DNS直接验证) 3. HTTP文件验证:不支持泛域名,需要配置网站文件上传 4. 多DNS提供商:每个域名可以选择独立的DNS提供商 +5. 自动选择:需要在[域名管理](#/certd/cert/domain)中事先配置好校验方式 `, }) challengeType!: string; @@ -408,7 +410,11 @@ export class CertApplyPlugin extends CertApplyBasePlugin { let dnsProvider: IDnsProvider = null; let domainsVerifyPlan: DomainsVerifyPlan = null; if (this.challengeType === "cname" || this.challengeType === "http" || this.challengeType === "dnses") { - domainsVerifyPlan = await this.createDomainsVerifyPlan(); + domainsVerifyPlan = await this.createDomainsVerifyPlan(this.domainsVerifyPlan); + } + if (this.challengeType === "auto") { + const planInput = await this.buildVerifyPlanInputByAuto(); + domainsVerifyPlan = await this.createDomainsVerifyPlan(planInput); } else { const dnsProviderType = this.dnsProviderType; const access = await this.getAccess(this.dnsProviderAccess); @@ -451,10 +457,10 @@ export class CertApplyPlugin extends CertApplyBasePlugin { }); } - async createDomainsVerifyPlan(): Promise { + async createDomainsVerifyPlan(verifyPlanSetting: DomainsVerifyPlanInput): Promise { const plan: DomainsVerifyPlan = {}; - for (const domain in this.domainsVerifyPlan) { - const domainVerifyPlan = this.domainsVerifyPlan[domain]; + for (const domain in verifyPlanSetting) { + const domainVerifyPlan = verifyPlanSetting[domain]; let dnsProvider = null; const cnameVerifyPlan: Record = {}; const httpVerifyPlan: Record = {}; @@ -511,6 +517,66 @@ export class CertApplyPlugin extends CertApplyBasePlugin { } return plan; } + + private async buildVerifyPlanInputByAuto() { + //从数据库里面自动选择校验方式 + // domain list + const domainList = new Set(); + //整理域名 + for (let domain in this.domains) { + domain = domain.replaceAll("*.", ""); + domainList.add(domain); + } + const domainVerifierGetter: IDomainVerifierGetter = await this.ctx.serviceGetter.get("DomainVerifierGetter"); + + const verifiers = await domainVerifierGetter.getVerifiers([...domainList]); + + const verifyPlanInput: DomainsVerifyPlanInput = {}; + + for (const verifier of verifiers) { + const domain = verifier.domain; + const mainDomain = verifier.mainDomain; + let plan = verifyPlanInput[mainDomain]; + if (!plan) { + plan = { + domain: mainDomain, + type: "cname", + }; + verifyPlanInput[mainDomain] = plan; + } + if (verifier.challengeType === "cname") { + verifyPlanInput[domain] = { + type: "cname", + domain: domain, + cnameVerifyPlan: { + [domain]: { + id: 0, + status: "validate", + }, + }, + }; + } else if (verifier.challengeType === "http") { + //http + const http = verifier.http; + verifyPlanInput[domain] = { + type: "http", + domain: domain, + httpVerifyPlan: { + [domain]: { + domain: domain, + httpUploaderType: http.httpUploaderType, + httpUploaderAccess: http.httpUploaderAccess, + httpUploadRootDir: http.httpUploadRootDir, + }, + }, + }; + } else { + //dns + } + } + + return verifyPlanInput; + } } new CertApplyPlugin(); diff --git a/packages/ui/certd-client/src/components/plugins/lib/dicts.ts b/packages/ui/certd-client/src/components/plugins/lib/dicts.ts index aecdcb66..c01a974b 100644 --- a/packages/ui/certd-client/src/components/plugins/lib/dicts.ts +++ b/packages/ui/certd-client/src/components/plugins/lib/dicts.ts @@ -9,9 +9,9 @@ export const Dicts = { }), challengeTypeDict: dict({ data: [ - { value: "dns", label: "DNS校验" }, - { value: "cname", label: "CNAME代理校验" }, - { value: "http", label: "HTTP校验" }, + { value: "dns", label: "DNS校验", color: "green" }, + { value: "cname", label: "CNAME代理校验", color: "blue" }, + { value: "http", label: "HTTP校验", color: "yellow" }, ], }), dnsProviderTypeDict: dict({ diff --git a/packages/ui/certd-client/src/views/certd/access/api.ts b/packages/ui/certd-client/src/views/certd/access/api.ts index 180984fe..9d81c0c1 100644 --- a/packages/ui/certd-client/src/views/certd/access/api.ts +++ b/packages/ui/certd-client/src/views/certd/access/api.ts @@ -55,6 +55,14 @@ export function createAccessApi(from = "user") { }); }, + async GetDictByIds(ids: number[]) { + return await request({ + url: apiPrefix + "/getDictByIds", + method: "post", + data: { ids }, + }); + }, + async GetSecretPlain(id: number, key: string) { return await request({ url: apiPrefix + "/getSecretPlain", diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx b/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx index fd58711f..57a2688e 100644 --- a/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx @@ -5,8 +5,9 @@ import { useRouter } from "vue-router"; import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; import { useUserStore } from "/@/store/user"; import { useSettingStore } from "/@/store/settings"; -import { message } from "ant-design-vue"; import { Dicts } from "/@/components/plugins/lib/dicts"; +import { createAccessApi } from "/@/views/certd/access/api"; + export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const router = useRouter(); const { t } = useI18n(); @@ -32,6 +33,21 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat const selectedRowKeys: Ref = ref([]); context.selectedRowKeys = selectedRowKeys; + const accessApi = createAccessApi(); + const accessDict = dict({ + value: "id", + label: "name", + url: "accessDict", + async getNodesByValues(ids: number[]) { + return await accessApi.GetDictByIds(ids); + }, + }); + + const httpUploaderTypeDict = Dicts.uploaderTypeDict; + + const dnsProviderTypeDict = dict({ + url: "pi/dnsProvider/dnsProviderTypeDict", + }); return { crudOptions: { settings: { @@ -90,6 +106,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat disabled: false, }, }, + column: { + sorter: true, + }, }, challengeType: { title: t("certd.domain.challengeType"), @@ -98,6 +117,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat form: { required: true, }, + column: { + sorter: true, + }, }, /** * challengeType varchar(50), @@ -109,7 +131,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat */ dnsProviderType: { title: t("certd.domain.dnsProviderType"), - type: "text", + type: "dict-select", + dict: dnsProviderTypeDict, form: { component: { name: "DnsProviderSelector", @@ -119,10 +142,17 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }), required: true, }, + column: { + show: false, + component: { + color: "auto", + }, + }, }, dnsProviderAccess: { title: t("certd.domain.dnsProviderAccess"), - type: "text", + type: "dict-select", + dict: accessDict, form: { component: { name: "AccessSelector", @@ -136,10 +166,16 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }), required: true, }, + column: { + show: false, + component: { + color: "auto", + }, + }, }, httpUploaderType: { title: t("certd.domain.httpUploaderType"), - type: "dict-text", + type: "dict-select", dict: Dicts.uploaderTypeDict, form: { show: compute(({ form }) => { @@ -147,6 +183,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }), required: true, }, + column: { + show: false, + component: { + color: "auto", + }, + }, }, httpUploaderAccess: { title: t("certd.domain.httpUploaderAccess"), @@ -160,6 +202,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }), required: true, }, + column: { + show: false, + component: { + color: "auto", + }, + }, }, httpUploadRootDir: { title: t("certd.domain.httpUploadRootDir"), @@ -170,14 +218,47 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }), required: true, }, + column: { + show: false, + component: { + color: "auto", + }, + }, + }, + challengeSetting: { + title: "校验配置", + type: "text", + form: { show: false }, + column: { + width: 400, + conditionalRender: false, + cellRender({ row }) { + if (row.challengeType === "dns") { + return ( +
+ + +
+ ); + } else if (row.challengeType === "http") { + return ( +
+ + + {row.httpUploadRootDir} +
+ ); + } + }, + }, }, disabled: { title: t("certd.domain.disabled"), type: "dict-switch", dict: dict({ data: [ - { label: "启用", value: false }, - { label: "禁用", value: true }, + { label: "启用", value: false, color: "green" }, + { label: "禁用", value: true, color: "red" }, ], }), form: { @@ -185,7 +266,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat required: true, }, column: { - width: 80, + width: 100, + sorter: true, }, }, createTime: { diff --git a/packages/ui/certd-server/src/controller/user/pipeline/access-controller.ts b/packages/ui/certd-server/src/controller/user/pipeline/access-controller.ts index cfde0533..5be35bab 100644 --- a/packages/ui/certd-server/src/controller/user/pipeline/access-controller.ts +++ b/packages/ui/certd-server/src/controller/user/pipeline/access-controller.ts @@ -101,4 +101,10 @@ export class AccessController extends CrudController { const res = await this.service.getSimpleInfo(id); return this.ok(res); } + + @Post('/getDictByIds', { summary: Constants.per.authOnly }) + async getDictByIds(@Body('ids') ids: number[]) { + const res = await this.service.getSimpleByIds(ids, this.getUserId()); + return this.ok(res); + } }