From c451823c2b7e4b8bd6faa29567bf8ee397171131 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 9 Jul 2025 16:00:55 +0800 Subject: [PATCH 01/10] chore: chore: auto domain entity --- .../src/modules/cert/entity/domain.ts | 50 +++++++++++++++++++ .../modules/cert/service/domain-service.ts | 25 ++++++++++ 2 files changed, 75 insertions(+) create mode 100644 packages/ui/certd-server/src/modules/cert/entity/domain.ts create mode 100644 packages/ui/certd-server/src/modules/cert/service/domain-service.ts diff --git a/packages/ui/certd-server/src/modules/cert/entity/domain.ts b/packages/ui/certd-server/src/modules/cert/entity/domain.ts new file mode 100644 index 000000000..5bfb8b5bd --- /dev/null +++ b/packages/ui/certd-server/src/modules/cert/entity/domain.ts @@ -0,0 +1,50 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +/** + * 域名管理 + */ +@Entity('cd_domain') +export class DomainEntity { + @PrimaryGeneratedColumn() + id: number; + + @Column({ comment: '用户ID', name: 'user_id' }) + userId: number; + + @Column({ comment: '主域名', length: 100 }) + domain: string; + + @Column({ comment: '校验类型', name: 'challenge_type', length: 100 }) + challengeType : string; + + @Column({ comment: 'DNS提供商', name: 'dns_provider_type', length: 100 }) + dnsProviderType: string; + + @Column({ comment: 'DNS提供商授权', name: 'dns_provider_access', length: 200 }) + dnsProviderAccess: number; + + @Column({ comment: '是否禁用', name: 'disabled' }) + disabled: boolean; + + + @Column({ comment: 'http上传类型', name: 'http_uploader_type', length: 200 }) + httpUploaderType: string; + + @Column({ comment: 'http上传授权', name: 'http_uploader_access', length: 200 }) + httpUploaderAccess: number; + + @Column({ comment: 'http上传根目录', name: 'http_upload_root_dir', length: 200 }) + httpUploadRootDir: string; + + @Column({ + comment: '创建时间', + name: 'create_time', + default: () => 'CURRENT_TIMESTAMP', + }) + createTime: Date; + @Column({ + comment: '修改时间', + name: 'update_time', + default: () => 'CURRENT_TIMESTAMP', + }) + updateTime: Date; +} diff --git a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts new file mode 100644 index 000000000..f6f6bbcee --- /dev/null +++ b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts @@ -0,0 +1,25 @@ +import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core'; +import {InjectEntityModel} from '@midwayjs/typeorm'; +import {Repository} from 'typeorm'; +import {AccessService, BaseService} from '@certd/lib-server'; +import {DomainEntity} from '../entity/domain.js'; + + +/** + * + */ +@Provide() +@Scope(ScopeEnum.Request, {allowDowngrade: true}) +export class DomainService extends BaseService { + @InjectEntityModel(DomainEntity) + repository: Repository; + + @Inject() + accessService: AccessService; + + //@ts-ignore + getRepository() { + return this.repository; + } + +} From f3002e4fb6d6fa1e694efb08c9b6f9cbb34292d9 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Thu, 10 Jul 2025 16:32:12 +0800 Subject: [PATCH 02/10] chore: domain manager --- .../http-verify-plan.vue | 13 +- .../src/components/plugins/lib/dicts.ts | 25 +++ .../src/locales/langs/en-US/certd.ts | 10 ++ .../src/locales/langs/zh-CN/certd.ts | 10 ++ .../src/router/source/modules/certd.ts | 11 ++ .../src/views/certd/cert/domain/api.ts | 60 +++++++ .../src/views/certd/cert/domain/crud.tsx | 166 ++++++++++++++++++ .../src/views/certd/cert/domain/index.vue | 60 +++++++ .../views/certd/pipeline/certd-form/dicts.ts | 14 -- .../db/migration/v10027__auto.sql | 19 ++ .../controller/user/cert/domain-controller.ts | 81 +++++++++ .../src/modules/cert/entity/domain.ts | 12 +- 12 files changed, 450 insertions(+), 31 deletions(-) create mode 100644 packages/ui/certd-client/src/components/plugins/lib/dicts.ts create mode 100644 packages/ui/certd-client/src/views/certd/cert/domain/api.ts create mode 100644 packages/ui/certd-client/src/views/certd/cert/domain/crud.tsx create mode 100644 packages/ui/certd-client/src/views/certd/cert/domain/index.vue delete mode 100644 packages/ui/certd-client/src/views/certd/pipeline/certd-form/dicts.ts create mode 100644 packages/ui/certd-server/db/migration/v10027__auto.sql create mode 100644 packages/ui/certd-server/src/controller/user/cert/domain-controller.ts 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 index f0c0bebd9..16c664973 100644 --- 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 @@ -33,6 +33,7 @@ import { Ref, ref, watch, nextTick } from "vue"; import { HttpRecord } from "/@/components/plugins/cert/domains-verify-plan-editor/type"; import { dict } from "@fast-crud/fast-crud"; +import { Dicts } from "/@/components/plugins/lib/dicts"; defineOptions({ name: "HttpVerifyPlan", @@ -68,17 +69,7 @@ async function onRecordChange() { emit("change", records.value); } -const uploaderTypeDict = dict({ - data: [ - { label: "SFTP", value: "sftp" }, - { label: "FTP", value: "ftp" }, - { label: "阿里云OSS", value: "alioss" }, - { label: "腾讯云COS", value: "tencentcos" }, - { label: "七牛OSS", value: "qiniuoss" }, - { label: "S3/Minio", value: "s3" }, - { label: "SSH(已废弃,请选择SFTP方式)", value: "ssh", disabled: true }, - ], -}); +const uploaderTypeDict = Dicts.uploaderTypeDict; diff --git a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/dicts.ts b/packages/ui/certd-client/src/views/certd/pipeline/certd-form/dicts.ts deleted file mode 100644 index d135544b6..000000000 --- a/packages/ui/certd-client/src/views/certd/pipeline/certd-form/dicts.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { dict } from "@fast-crud/fast-crud"; - -export const Dicts = { - sslProviderDict: dict({ - data: [ - { value: "letsencrypt", label: "Let‘s Encrypt" }, - { value: "zerossl", label: "ZeroSSL" }, - ], - }), - challengeTypeDict: dict({ data: [{ value: "dns", label: "DNS校验" }] }), - dnsProviderTypeDict: dict({ - url: "pi/dnsProvider/dnsProviderTypeDict", - }), -}; diff --git a/packages/ui/certd-server/db/migration/v10027__auto.sql b/packages/ui/certd-server/db/migration/v10027__auto.sql new file mode 100644 index 000000000..512cfaa26 --- /dev/null +++ b/packages/ui/certd-server/db/migration/v10027__auto.sql @@ -0,0 +1,19 @@ +CREATE TABLE "cd_domain" +( + "id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, + "user_id" integer, + "domain" varchar(1024), + challenge_type varchar(50), + dns_provider_type varchar(50), + dns_provider_access bigint, + http_uploader_type varchar(50), + http_uploader_access bigint, + http_upload_root_dir varchar(512), + "disabled" boolean NOT NULL DEFAULT (false), + "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), + "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP) +); + +CREATE INDEX "index_domain_user_id" ON "cd_domain" ("user_id"); +CREATE INDEX "index_domain_domain" ON "cd_domain" ("domain"); + diff --git a/packages/ui/certd-server/src/controller/user/cert/domain-controller.ts b/packages/ui/certd-server/src/controller/user/cert/domain-controller.ts new file mode 100644 index 000000000..ffcb0bd2a --- /dev/null +++ b/packages/ui/certd-server/src/controller/user/cert/domain-controller.ts @@ -0,0 +1,81 @@ +import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core'; +import { Constants, CrudController } from '@certd/lib-server'; +import {DomainService} from "../../../modules/cert/service/domain-service.js"; + +/** + * 授权 + */ +@Provide() +@Controller('/api/cert/domain') +export class DomainController extends CrudController { + @Inject() + service: DomainService; + + getService(): DomainService { + return this.service; + } + + @Post('/page', { summary: Constants.per.authOnly }) + async page(@Body(ALL) body: any) { + body.query = body.query ?? {}; + body.query.userId = this.getUserId(); + const domain = body.query.domain; + delete body.query.domain; + + const bq = qb => { + if (domain) { + qb.andWhere('domain like :domain', { domain: `%${domain}%` }); + } + }; + + const pageRet = await this.getService().page({ + query: body.query, + page: body.page, + sort: body.sort, + buildQuery: bq, + }); + return this.ok(pageRet); + } + + @Post('/list', { summary: Constants.per.authOnly }) + async list(@Body(ALL) body: any) { + body.query = body.query ?? {}; + body.query.userId = this.getUserId(); + const list = await this.getService().list(body); + return this.ok(list); + } + + @Post('/add', { summary: Constants.per.authOnly }) + async add(@Body(ALL) bean: any) { + bean.userId = this.getUserId(); + return super.add(bean); + } + + @Post('/update', { summary: Constants.per.authOnly }) + async update(@Body(ALL) bean: any) { + await this.service.checkUserId(bean.id, this.getUserId()); + delete bean.userId; + return super.update(bean); + } + + @Post('/info', { summary: Constants.per.authOnly }) + async info(@Query('id') id: number) { + await this.service.checkUserId(id, this.getUserId()); + return super.info(id); + } + + @Post('/delete', { summary: Constants.per.authOnly }) + async delete(@Query('id') id: number) { + await this.service.checkUserId(id, this.getUserId()); + return super.delete(id); + } + + @Post('/deleteByIds', { summary: Constants.per.authOnly }) + async deleteByIds(@Body(ALL) body: any) { + await this.service.delete(body.ids, { + userId: this.getUserId(), + }); + return this.ok(); + } + +} diff --git a/packages/ui/certd-server/src/modules/cert/entity/domain.ts b/packages/ui/certd-server/src/modules/cert/entity/domain.ts index 5bfb8b5bd..51c36fe1e 100644 --- a/packages/ui/certd-server/src/modules/cert/entity/domain.ts +++ b/packages/ui/certd-server/src/modules/cert/entity/domain.ts @@ -13,26 +13,26 @@ export class DomainEntity { @Column({ comment: '主域名', length: 100 }) domain: string; - @Column({ comment: '校验类型', name: 'challenge_type', length: 100 }) + @Column({ comment: '校验类型', name: 'challenge_type', length: 50 }) challengeType : string; - @Column({ comment: 'DNS提供商', name: 'dns_provider_type', length: 100 }) + @Column({ comment: 'DNS提供商', name: 'dns_provider_type', length: 50 }) dnsProviderType: string; - @Column({ comment: 'DNS提供商授权', name: 'dns_provider_access', length: 200 }) + @Column({ comment: 'DNS提供商授权', name: 'dns_provider_access' }) dnsProviderAccess: number; @Column({ comment: '是否禁用', name: 'disabled' }) disabled: boolean; - @Column({ comment: 'http上传类型', name: 'http_uploader_type', length: 200 }) + @Column({ comment: 'http上传类型', name: 'http_uploader_type', length: 50 }) httpUploaderType: string; - @Column({ comment: 'http上传授权', name: 'http_uploader_access', length: 200 }) + @Column({ comment: 'http上传授权', name: 'http_uploader_access' }) httpUploaderAccess: number; - @Column({ comment: 'http上传根目录', name: 'http_upload_root_dir', length: 200 }) + @Column({ comment: 'http上传根目录', name: 'http_upload_root_dir', length: 512 }) httpUploadRootDir: string; @Column({ From 39dc5c8160501c60e67a5685cd65a5bf2c3d2429 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Thu, 10 Jul 2025 17:00:47 +0800 Subject: [PATCH 03/10] chore: domain manager --- .../src/components/plugins/lib/dicts.ts | 8 ++- .../src/locales/langs/en-US/certd.ts | 1 + .../src/locales/langs/zh-CN/certd.ts | 1 + .../src/router/source/modules/certd.ts | 2 +- .../src/views/certd/cert/domain/crud.tsx | 58 +++++++++++++++++-- .../modules/cert/service/domain-service.ts | 47 ++++++++++++++- 6 files changed, 110 insertions(+), 7 deletions(-) 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 64fb77e5a..aecdcb664 100644 --- a/packages/ui/certd-client/src/components/plugins/lib/dicts.ts +++ b/packages/ui/certd-client/src/components/plugins/lib/dicts.ts @@ -7,7 +7,13 @@ export const Dicts = { { value: "zerossl", label: "ZeroSSL" }, ], }), - challengeTypeDict: dict({ data: [{ value: "dns", label: "DNS校验" }] }), + challengeTypeDict: dict({ + data: [ + { value: "dns", label: "DNS校验" }, + { value: "cname", label: "CNAME代理校验" }, + { value: "http", label: "HTTP校验" }, + ], + }), dnsProviderTypeDict: dict({ url: "pi/dnsProvider/dnsProviderTypeDict", }), diff --git a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts index 22c797eb4..7f9d1f5f5 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts @@ -717,5 +717,6 @@ export default { httpUploaderType: "HTTP Uploader Type", httpUploaderAccess: "HTTP Uploader Access", httpUploadRootDir: "HTTP Upload Root Dir", + disabled: "Disabled", }, }; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts index c4a0e3ab9..edbdd17cf 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts @@ -720,5 +720,6 @@ export default { httpUploaderType: "上传方式", httpUploaderAccess: "上传授权信息", httpUploadRootDir: "网站根路径", + disabled: "禁用/启用", }, }; diff --git a/packages/ui/certd-client/src/router/source/modules/certd.ts b/packages/ui/certd-client/src/router/source/modules/certd.ts index 620fc65f3..bba7fae8e 100644 --- a/packages/ui/certd-client/src/router/source/modules/certd.ts +++ b/packages/ui/certd-client/src/router/source/modules/certd.ts @@ -122,7 +122,7 @@ export const certdResources = [ path: "/certd/cert/domain", component: "/certd/cert/domain/index.vue", meta: { - icon: "material-symbols:approval-delegation-outline", + icon: "ion:globe-outline", auth: true, keepAlive: true, }, 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 e82725dc9..fd58711f9 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 @@ -82,17 +82,21 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat search: { show: true, }, + form: { + required: true, + }, editForm: { component: { - disabled: true, + disabled: false, }, }, }, challengeType: { title: t("certd.domain.challengeType"), type: "dict-select", + dict: Dicts.challengeTypeDict, form: { - show: false, + required: true, }, }, /** @@ -110,6 +114,10 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat component: { name: "DnsProviderSelector", }, + show: compute(({ form }) => { + return form.challengeType === "dns"; + }), + required: true, }, }, dnsProviderAccess: { @@ -118,16 +126,27 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat form: { component: { name: "AccessSelector", + vModel: "modelValue", type: compute(({ form }) => { return form.dnsProviderType; }), }, + show: compute(({ form }) => { + return form.challengeType === "dns"; + }), + required: true, }, }, httpUploaderType: { title: t("certd.domain.httpUploaderType"), type: "dict-text", dict: Dicts.uploaderTypeDict, + form: { + show: compute(({ form }) => { + return form.challengeType === "http"; + }), + required: true, + }, }, httpUploaderAccess: { title: t("certd.domain.httpUploaderAccess"), @@ -136,10 +155,41 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat component: { name: "AccessSelector", }, + show: compute(({ form }) => { + return form.challengeType === "http"; + }), + required: true, + }, + }, + httpUploadRootDir: { + title: t("certd.domain.httpUploadRootDir"), + type: "text", + form: { + show: compute(({ form }) => { + return form.challengeType === "http"; + }), + required: true, + }, + }, + disabled: { + title: t("certd.domain.disabled"), + type: "dict-switch", + dict: dict({ + data: [ + { label: "启用", value: false }, + { label: "禁用", value: true }, + ], + }), + form: { + value: false, + required: true, + }, + column: { + width: 80, }, }, createTime: { - title: t("certd.create_time"), + title: t("certd.createTime"), type: "datetime", form: { show: false, @@ -151,7 +201,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, }, updateTime: { - title: t("certd.update_time"), + title: t("certd.updateTime"), type: "datetime", form: { show: false, diff --git a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts index f6f6bbcee..ed99159a7 100644 --- a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts +++ b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts @@ -1,6 +1,6 @@ import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core'; import {InjectEntityModel} from '@midwayjs/typeorm'; -import {Repository} from 'typeorm'; +import {Not, Repository} from 'typeorm'; import {AccessService, BaseService} from '@certd/lib-server'; import {DomainEntity} from '../entity/domain.js'; @@ -22,4 +22,49 @@ export class DomainService extends BaseService { return this.repository; } + async add(param) { + if (param.userId == null ){ + throw new Error('userId 不能为空'); + } + if (!param.domain) { + throw new Error('domain 不能为空'); + } + const old = await this.repository.findOne({ + where: { + domain: param.domain, + userId: param.userId + } + }); + if (old) { + throw new Error(`域名(${param.domain})不能重复`); + } + return await super.add(param); + } + + async update(param) { + if (!param.id) { + throw new Error('id 不能为空'); + } + const old = await this.info(param.id) + if (!old) { + throw new Error('domain记录不存在'); + } + + const same = await this.repository.findOne({ + where: { + domain: param.domain, + userId: old.userId, + id: Not(param.id) + } + }); + + if (same) { + throw new Error(`域名(${param.domain})不能重复`); + } + delete param.userId + return await super.update(param); + + + } + } From 4b335db31ca24b00ddab3fd01a3f596f10015cee Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Fri, 11 Jul 2025 18:17:11 +0800 Subject: [PATCH 04/10] chore: --- packages/ui/certd-client/src/locales/langs/en-US/certd.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts index 0d917c332..d17346cc0 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts @@ -471,7 +471,7 @@ export default { statusError: "Error", actionImportBatch: "Batch Import", actionSyncIp: "Sync IP", - TitleSyncIp: "Sync IP", + modalTitleSyncIp: "Sync IP", modalContentSyncIp: "Are you sure to sync IP?", notificationSyncComplete: "Sync Complete", actionCheckAll: "Check All", From 785bee2b3955493b60892677a6ba5a89044df531 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sat, 12 Jul 2025 23:00:04 +0800 Subject: [PATCH 05/10] 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 bc19f9644..f99f6330d 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 523159fe5..a8817c214 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 6b6d864a1..b08c74b7a 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 aecdcb664..c01a974b0 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 180984fea..9d81c0c1d 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 fd58711f9..57a2688ed 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 cfde0533f..5be35bab7 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); + } } From 3f9943270cfb12946e38e6272bc5e8d95ad6ab9e Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sun, 13 Jul 2025 18:25:09 +0800 Subject: [PATCH 06/10] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E9=80=89=E6=8B=A9=E6=A0=A1=E9=AA=8C=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=E7=94=B3=E8=AF=B7=E8=AF=81=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin-cert/src/dns-provider/api.ts | 10 +- .../src/plugin/cert-plugin/acme.ts | 30 +-- .../src/plugin/cert-plugin/index.ts | 208 +++++++++--------- .../user/pipeline/sub-domain-controller.ts | 3 +- .../modules/cert/service/domain-service.ts | 101 ++++++++- .../{ => getter}/cname-proxy-service.ts | 0 .../service/getter/domain-verifier-getter.ts | 17 ++ .../{ => getter}/notification-getter.ts | 2 +- .../service/getter/sub-domain-getter.ts | 17 ++ .../service/getter/task-service-getter.ts | 81 +++++++ .../pipeline/service/pipeline-service.ts | 2 +- .../pipeline/service/sub-domain-service.ts | 19 -- .../pipeline/service/task-service-getter.ts | 63 ------ 13 files changed, 341 insertions(+), 212 deletions(-) rename packages/ui/certd-server/src/modules/pipeline/service/{ => getter}/cname-proxy-service.ts (100%) create mode 100644 packages/ui/certd-server/src/modules/pipeline/service/getter/domain-verifier-getter.ts rename packages/ui/certd-server/src/modules/pipeline/service/{ => getter}/notification-getter.ts (91%) create mode 100644 packages/ui/certd-server/src/modules/pipeline/service/getter/sub-domain-getter.ts create mode 100644 packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts delete mode 100644 packages/ui/certd-server/src/modules/pipeline/service/task-service-getter.ts diff --git a/packages/plugins/plugin-cert/src/dns-provider/api.ts b/packages/plugins/plugin-cert/src/dns-provider/api.ts index a8817c214..b3d87be39 100644 --- a/packages/plugins/plugin-cert/src/dns-provider/api.ts +++ b/packages/plugins/plugin-cert/src/dns-provider/api.ts @@ -73,18 +73,22 @@ export type CnameVerifier = { export type HttpVerifier = { // http校验 httpUploaderType: string; - httpUploaderAccess: string; + httpUploaderAccess: number; httpUploadRootDir: string; }; export type DomainVerifier = { domain: string; mainDomain: string; - challengeType: string; + type: string; dns?: DnsVerifier; cname?: CnameVerifier; http?: HttpVerifier; }; +export type DomainVerifiers = { + [key: string]: DomainVerifier; +}; + export interface IDomainVerifierGetter { - getVerifiers(domains: string[]): Promise; + getVerifiers(domains: string[]): Promise; } 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 c12a8d084..9e4c095c2 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts @@ -23,10 +23,11 @@ export type HttpVerifyPlan = { export type DomainVerifyPlan = { domain: string; + mainDomain: string; type: "cname" | "dns" | "http"; dnsProvider?: IDnsProvider; - cnameVerifyPlan?: Record; - httpVerifyPlan?: Record; + cnameVerifyPlan?: CnameVerifyPlan; + httpVerifyPlan?: HttpVerifyPlan; }; export type DomainsVerifyPlan = { [key: string]: DomainVerifyPlan; @@ -233,23 +234,20 @@ export class AcmeService { let dnsProvider = providers.dnsProvider; let fullRecord = `_acme-challenge.${fullDomain}`; - const origDomain = punycode.toUnicode(domain); + // const origDomain = punycode.toUnicode(domain); const origFullDomain = punycode.toUnicode(fullDomain); if (providers.domainsVerifyPlan) { //按照计划执行 - const domainVerifyPlan = providers.domainsVerifyPlan[origDomain]; + const domainVerifyPlan = providers.domainsVerifyPlan[origFullDomain]; if (domainVerifyPlan) { if (domainVerifyPlan.type === "dns") { dnsProvider = domainVerifyPlan.dnsProvider; } else if (domainVerifyPlan.type === "cname") { - const cnameVerifyPlan = domainVerifyPlan.cnameVerifyPlan; - if (cnameVerifyPlan) { - const cname = cnameVerifyPlan[origFullDomain]; - if (cname) { - dnsProvider = cname.dnsProvider; - domain = await this.options.domainParser.parse(cname.domain); - fullRecord = cname.fullRecord; - } + const cname: CnameVerifyPlan = domainVerifyPlan.cnameVerifyPlan; + if (cname) { + dnsProvider = cname.dnsProvider; + domain = await this.options.domainParser.parse(cname.domain); + fullRecord = cname.fullRecord; } else { this.logger.error(`未找到域名${fullDomain}的CNAME校验计划,请修改证书申请配置`); } @@ -257,13 +255,12 @@ export class AcmeService { throw new Error(`未找到域名${fullDomain}CNAME校验计划的DnsProvider,请修改证书申请配置`); } } else if (domainVerifyPlan.type === "http") { - const httpVerifyPlan = domainVerifyPlan.httpVerifyPlan; - if (httpVerifyPlan) { + const plan: HttpVerifyPlan = domainVerifyPlan.httpVerifyPlan; + if (plan) { const httpChallenge = getChallenge("http-01"); if (httpChallenge == null) { throw new Error("该域名不支持http-01方式校验"); } - const plan = httpVerifyPlan[fullDomain]; return await doHttpVerify(httpChallenge, plan.httpUploader); } else { throw new Error("未找到域名【" + fullDomain + "】的http校验配置"); @@ -275,6 +272,9 @@ export class AcmeService { this.logger.info("未找到域名校验计划,使用默认的dnsProvider"); } } + if (!dnsProvider) { + this.logger.error("dnsProvider不存在,无法申请证书"); + } const dnsChallenge = getChallenge("dns-01"); return await doDnsVerify(dnsChallenge, fullRecord, dnsProvider); 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 b08c74b7a..3c6543409 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts @@ -1,16 +1,16 @@ import { CancelError, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import { utils } from "@certd/basic"; -import type { CertInfo, CnameVerifyPlan, DomainsVerifyPlan, HttpVerifyPlan, PrivateKeyType, SSLProvider } from "./acme.js"; -import { AcmeService } from "./acme.js"; +import { AcmeService, CertInfo, DomainsVerifyPlan, DomainVerifyPlan, PrivateKeyType, SSLProvider } from "./acme.js"; import * as _ from "lodash-es"; -import { createDnsProvider, DnsProviderContext, DomainVerifier, IDnsProvider, IDomainVerifierGetter, ISubDomainsGetter } from "../../dns-provider/index.js"; +import { createDnsProvider, DnsProviderContext, DnsVerifier, DomainVerifiers, HttpVerifier, 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"; import { EabAccess } from "../../access"; import { DomainParser } from "../../dns-provider/domain-parser.js"; import { ossClientFactory } from "@certd/plugin-lib"; + export * from "./base.js"; export type { CertInfo }; export * from "./cert-reader.js"; @@ -335,6 +335,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin { acme!: AcmeService; eab!: EabAccess; + async onInit() { let eab: EabAccess = null; @@ -410,11 +411,9 @@ 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(this.domainsVerifyPlan); - } - if (this.challengeType === "auto") { - const planInput = await this.buildVerifyPlanInputByAuto(); - domainsVerifyPlan = await this.createDomainsVerifyPlan(planInput); + domainsVerifyPlan = await this.createDomainsVerifyPlan(domains, this.domainsVerifyPlan); + } else if (this.challengeType === "auto") { + domainsVerifyPlan = await this.createDomainsVerifyPlanByAuto(domains); } else { const dnsProviderType = this.dnsProviderType; const access = await this.getAccess(this.dnsProviderAccess); @@ -450,75 +449,39 @@ export class CertApplyPlugin extends CertApplyBasePlugin { async createDnsProvider(dnsProviderType: string, dnsProviderAccess: any): Promise { const domainParser = this.acme.options.domainParser; - const context: DnsProviderContext = { access: dnsProviderAccess, logger: this.logger, http: this.ctx.http, utils, domainParser }; + const context: DnsProviderContext = { + access: dnsProviderAccess, + logger: this.logger, + http: this.ctx.http, + utils, + domainParser, + }; return await createDnsProvider({ dnsProviderType, context, }); } - async createDomainsVerifyPlan(verifyPlanSetting: DomainsVerifyPlanInput): Promise { + async createDomainsVerifyPlan(domains: string[], verifyPlanSetting: DomainsVerifyPlanInput): Promise { const plan: DomainsVerifyPlan = {}; - for (const domain in verifyPlanSetting) { - const domainVerifyPlan = verifyPlanSetting[domain]; - let dnsProvider = null; - const cnameVerifyPlan: Record = {}; - const httpVerifyPlan: Record = {}; - if (domainVerifyPlan.type === "dns") { - const access = await this.getAccess(domainVerifyPlan.dnsProviderAccessId); - dnsProvider = await this.createDnsProvider(domainVerifyPlan.dnsProviderType, access); - } else if (domainVerifyPlan.type === "cname") { - for (const key in domainVerifyPlan.cnameVerifyPlan) { - const cnameRecord = await this.ctx.cnameProxyService.getByDomain(key); - let dnsProvider = cnameRecord.commonDnsProvider; - if (cnameRecord.cnameProvider.id > 0) { - 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.getAccess(httpRecord.httpUploaderAccess); - let rootDir = httpRecord.httpUploadRootDir; - if (!rootDir.endsWith("/") && !rootDir.endsWith("\\")) { - rootDir = rootDir + "/"; - } - this.logger.info("上传方式", httpRecord.httpUploaderType); - const httpUploader = await ossClientFactory.createOssClientByType(httpRecord.httpUploaderType, { - access, - rootDir: rootDir, - ctx: httpUploaderContext, - }); - httpVerifyPlan[key] = { - type: "http", - domain: key, - httpUploader, - }; - } + + const domainParser = this.acme.options.domainParser; + for (const fullDomain of domains) { + const domain = fullDomain.replaceAll("*.", ""); + const mainDomain = await domainParser.parse(domain); + const planSetting = verifyPlanSetting[mainDomain]; + if (planSetting.type === "dns") { + await this.createDnsDomainVerifyPlan(planSetting[mainDomain], domain, mainDomain); + } else if (planSetting.type === "cname") { + await this.createCnameDomainVerifyPlan(domain, mainDomain); + } else if (planSetting.type === "http") { + await this.createHttpDomainVerifyPlan(planSetting.httpVerifyPlan[domain], domain, mainDomain); } - plan[domain] = { - domain, - type: domainVerifyPlan.type, - dnsProvider, - cnameVerifyPlan, - httpVerifyPlan, - }; } return plan; } - private async buildVerifyPlanInputByAuto() { + private async createDomainsVerifyPlanByAuto(domains: string[]) { //从数据库里面自动选择校验方式 // domain list const domainList = new Set(); @@ -527,55 +490,84 @@ export class CertApplyPlugin extends CertApplyBasePlugin { domain = domain.replaceAll("*.", ""); domainList.add(domain); } - const domainVerifierGetter: IDomainVerifierGetter = await this.ctx.serviceGetter.get("DomainVerifierGetter"); + const domainVerifierGetter: IDomainVerifierGetter = await this.ctx.serviceGetter.get("domainVerifierGetter"); - const verifiers = await domainVerifierGetter.getVerifiers([...domainList]); + const verifiers: DomainVerifiers = await domainVerifierGetter.getVerifiers([...domainList]); - const verifyPlanInput: DomainsVerifyPlanInput = {}; + const plan: DomainsVerifyPlan = {}; - 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 + for (const domain in verifiers) { + const verifier = verifiers[domain]; + if (verifier.type === "dns") { + plan[domain] = await this.createDnsDomainVerifyPlan(verifier.dns, domain, verifier.mainDomain); + } else if (verifier.type === "cname") { + plan[domain] = await this.createCnameDomainVerifyPlan(domain, verifier.mainDomain); + } else if (verifier.type === "http") { + plan[domain] = await this.createHttpDomainVerifyPlan(verifier.http, domain, verifier.mainDomain); } } + return plan; + } - return verifyPlanInput; + private async createDnsDomainVerifyPlan(planSetting: DnsVerifier, domain: string, mainDomain: string): Promise { + const access = await this.getAccess(planSetting.dnsProviderAccessId); + return { + type: "dns", + mainDomain, + domain, + dnsProvider: await this.createDnsProvider(planSetting.dnsProviderType, access), + }; + } + + private async createHttpDomainVerifyPlan(httpSetting: HttpVerifier, domain: string, mainDomain: string): Promise { + const httpUploaderContext = { + accessService: this.ctx.accessService, + logger: this.logger, + utils, + }; + + const access = await this.getAccess(httpSetting.httpUploaderAccess); + let rootDir = httpSetting.httpUploadRootDir; + if (!rootDir.endsWith("/") && !rootDir.endsWith("\\")) { + rootDir = rootDir + "/"; + } + this.logger.info("上传方式", httpSetting.httpUploaderType); + const httpUploader = await ossClientFactory.createOssClientByType(httpSetting.httpUploaderType, { + access, + rootDir: rootDir, + ctx: httpUploaderContext, + }); + return { + type: "http", + domain, + mainDomain, + httpVerifyPlan: { + type: "http", + domain, + httpUploader, + }, + }; + } + + private async createCnameDomainVerifyPlan(domain: string, mainDomain: string): Promise { + const cnameRecord = await this.ctx.cnameProxyService.getByDomain(domain); + if (cnameRecord == null) { + throw new Error(`请先配置${domain}的CNAME记录,并通过校验`); + } + let dnsProvider = cnameRecord.commonDnsProvider; + if (cnameRecord.cnameProvider.id > 0) { + dnsProvider = await this.createDnsProvider(cnameRecord.cnameProvider.dnsProviderType, cnameRecord.cnameProvider.access); + } + return { + type: "cname", + domain, + mainDomain, + cnameVerifyPlan: { + domain, + fullRecord: cnameRecord.recordValue, + dnsProvider, + }, + }; } } diff --git a/packages/ui/certd-server/src/controller/user/pipeline/sub-domain-controller.ts b/packages/ui/certd-server/src/controller/user/pipeline/sub-domain-controller.ts index c94b0ecd6..76dae741d 100644 --- a/packages/ui/certd-server/src/controller/user/pipeline/sub-domain-controller.ts +++ b/packages/ui/certd-server/src/controller/user/pipeline/sub-domain-controller.ts @@ -1,7 +1,8 @@ import {ALL, Body, Controller, Inject, Post, Provide, Query} from '@midwayjs/core'; import {Constants, CrudController} from '@certd/lib-server'; -import {SubDomainService, SubDomainsGetter} from "../../../modules/pipeline/service/sub-domain-service.js"; +import {SubDomainService} from "../../../modules/pipeline/service/sub-domain-service.js"; import {DomainParser} from '@certd/plugin-cert/dist/dns-provider/domain-parser.js'; +import { SubDomainsGetter } from '../../../modules/pipeline/service/getter/sub-domain-getter.js'; /** * 子域名托管 diff --git a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts index ed99159a7..58c1146e7 100644 --- a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts +++ b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts @@ -1,8 +1,12 @@ import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core'; import {InjectEntityModel} from '@midwayjs/typeorm'; -import {Not, Repository} from 'typeorm'; +import {In, Not, Repository} from 'typeorm'; import {AccessService, BaseService} from '@certd/lib-server'; import {DomainEntity} from '../entity/domain.js'; +import {SubDomainService} from "../../pipeline/service/sub-domain-service.js"; +import {DomainParser} from "@certd/plugin-cert/dist/dns-provider/domain-parser.js"; +import {DomainVerifiers} from "@certd/plugin-cert"; +import { SubDomainsGetter } from '../../pipeline/service/getter/sub-domain-getter.js'; /** @@ -16,6 +20,8 @@ export class DomainService extends BaseService { @Inject() accessService: AccessService; + @Inject() + subDomainService: SubDomainService; //@ts-ignore getRepository() { @@ -67,4 +73,97 @@ export class DomainService extends BaseService { } + /** + * + * @param userId + * @param domains //去除* 且去重之后的域名列表 + */ + async getDomainVerifiers(userId: number, domains: string[]):Promise { + + const mainDomainMap:Record = {} + const subDomainGetter = new SubDomainsGetter(userId, this.subDomainService) + const domainParser = new DomainParser(subDomainGetter) + + const mainDomains = [] + for (const domain of domains) { + const mainDomain = await domainParser.parse(domain); + mainDomainMap[domain] = mainDomain; + mainDomains.push(mainDomain) + } + + //匹配DNS记录 + let allDomains = [...domains,...mainDomains] + //去重 + allDomains = [...new Set(allDomains)] + + const domainRecords = await this.find({ + where: { + domain: In(allDomains), + userId + } + }) + + const dnsMap = domainRecords.filter(item=>item.challengeType === 'dns').reduce((pre, item) => { + pre[item.domain] = item + 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) => { + pre[item.domain] = item + return pre + }, {}) + + + const domainVerifiers:DomainVerifiers = {} + + for (const domain of domains) { + const mainDomain = mainDomainMap[domain] + + const dnsRecord = dnsMap[mainDomain] + if (dnsRecord) { + domainVerifiers[domain] = { + domain, + mainDomain, + type: 'dns', + dns: { + dnsProviderType: dnsRecord.dnsProviderType, + dnsProviderAccessId: dnsRecord.dnsProviderAccessId + } + } + continue + } + const cnameRecord = cnameMap[mainDomain] + if (cnameRecord) { + domainVerifiers[domain] = { + domain, + mainDomain, + type: 'cname', + cname: { + cnameRecord: cnameRecord.cnameRecord + } + } + continue + } + const httpRecord = httpMap[mainDomain] + if (httpRecord) { + domainVerifiers[domain] = { + domain, + mainDomain, + type: 'http', + http: { + httpUploaderType: httpRecord.httpUploaderType, + httpUploaderAccess: httpRecord.httpUploaderAccess, + httpUploadRootDir: httpRecord.httpUploadRootDir + } + } + continue + } + domainVerifiers[domain] = null + } + + return domainVerifiers; + } } diff --git a/packages/ui/certd-server/src/modules/pipeline/service/cname-proxy-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/getter/cname-proxy-service.ts similarity index 100% rename from packages/ui/certd-server/src/modules/pipeline/service/cname-proxy-service.ts rename to packages/ui/certd-server/src/modules/pipeline/service/getter/cname-proxy-service.ts diff --git a/packages/ui/certd-server/src/modules/pipeline/service/getter/domain-verifier-getter.ts b/packages/ui/certd-server/src/modules/pipeline/service/getter/domain-verifier-getter.ts new file mode 100644 index 000000000..36744759d --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/getter/domain-verifier-getter.ts @@ -0,0 +1,17 @@ +import {DomainVerifiers, IDomainVerifierGetter} from "@certd/plugin-cert"; +import {DomainService} from "../../../cert/service/domain-service.js"; + +export class DomainVerifierGetter implements IDomainVerifierGetter { + private userId: number; + private domainService: DomainService; + + constructor(userId: number, domainService: DomainService) { + this.userId = userId; + this.domainService = domainService; + } + + async getVerifiers(domains: string[]): Promise{ + return await this.domainService.getDomainVerifiers(this.userId,domains); + } + +} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/notification-getter.ts b/packages/ui/certd-server/src/modules/pipeline/service/getter/notification-getter.ts similarity index 91% rename from packages/ui/certd-server/src/modules/pipeline/service/notification-getter.ts rename to packages/ui/certd-server/src/modules/pipeline/service/getter/notification-getter.ts index 609d536cd..691608ad3 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/notification-getter.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/getter/notification-getter.ts @@ -1,5 +1,5 @@ import { INotificationService, NotificationSendReq } from '@certd/pipeline'; -import { NotificationService } from './notification-service.js'; +import {NotificationService} from "../notification-service.js"; export class NotificationGetter implements INotificationService { userId: number; diff --git a/packages/ui/certd-server/src/modules/pipeline/service/getter/sub-domain-getter.ts b/packages/ui/certd-server/src/modules/pipeline/service/getter/sub-domain-getter.ts new file mode 100644 index 000000000..4878aed05 --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/getter/sub-domain-getter.ts @@ -0,0 +1,17 @@ +import {ISubDomainsGetter} from "@certd/plugin-cert"; +import {SubDomainService} from "../service/sub-domain-service.js"; + +export class SubDomainsGetter implements ISubDomainsGetter { + userId: number; + subDomainService: SubDomainService; + + constructor(userId: number, subDomainService: SubDomainService) { + this.userId = userId; + this.subDomainService = subDomainService; + } + + async getSubDomains() { + return await this.subDomainService.getListByUserId(this.userId) + } + +} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts b/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts new file mode 100644 index 000000000..006018dad --- /dev/null +++ b/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts @@ -0,0 +1,81 @@ +import {IServiceGetter} from "@certd/pipeline"; +import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core"; +import {SubDomainService} from "../sub-domain-service.js"; +import {AccessGetter, AccessService} from "@certd/lib-server"; +import {CnameProxyService} from "./cname-proxy-service.js"; +import {NotificationGetter} from "./notification-getter.js"; +import {NotificationService} from "../notification-service.js"; +import {CnameRecordService} from "../../../cname/service/cname-record-service.js"; +import {SubDomainsGetter} from './sub-domain-getter.js' +import {DomainVerifierGetter} from "./domain-verifier-getter.js"; +import {Context} from "@midwayjs/koa"; +import {DomainService} from "../../../cert/service/domain-service.js"; + +export class TaskServiceGetter implements IServiceGetter{ + private userId: number; + private ctx : Context; + constructor(userId:number,ctx:Context) { + this.userId = userId; + this.ctx = ctx + } + async get(serviceName: string): Promise { + + if(serviceName === 'subDomainsGetter'){ + return await this.getSubDomainsGetter() as T + } if (serviceName === 'accessService') { + return await this.getAccessService() as T + } else if (serviceName === 'cnameProxyService') { + return await this.getCnameProxyService() as T + } else if (serviceName === 'notificationService') { + return await this.getNotificationService() as T + } else if (serviceName === 'domainVerifierGetter') { + return await this.getDomainVerifierGetter() as T + }else{ + throw new Error(`service ${serviceName} not found`) + } + } + + async getSubDomainsGetter(): Promise { + const subDomainsService = await this.ctx.requestContext.getAsync("subDomainService") + return new SubDomainsGetter(this.userId, subDomainsService) + } + + async getAccessService(): Promise { + const accessService:AccessService = await this.ctx.requestContext.getAsync("accessService") + return new AccessGetter(this.userId, accessService.getById.bind(accessService)); + } + + async getCnameProxyService(): Promise { + const cnameRecordService:CnameRecordService = await this.ctx.requestContext.getAsync("cnameRecordService") + return new CnameProxyService(this.userId, cnameRecordService.getWithAccessByDomain.bind(cnameRecordService)); + } + + async getNotificationService(): Promise { + const notificationService:NotificationService = await this.ctx.requestContext.getAsync("notificationService") + return new NotificationGetter(this.userId, notificationService); + } + + async getDomainVerifierGetter(): Promise { + const domainService:DomainService = await this.ctx.requestContext.getAsync("domainService") + return new DomainVerifierGetter(this.userId, domainService); + } +} +export type TaskServiceCreateReq = { + userId: number; +} + +@Provide() +@Scope(ScopeEnum.Request, { allowDowngrade: true }) +export class TaskServiceBuilder { + @Inject() + ctx: Context; + + create(req:TaskServiceCreateReq){ + const userId = req.userId; + return new TaskServiceGetter(userId,this.ctx) + } +} + + + + diff --git a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts index 363d33e62..f9cc2b511 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/pipeline-service.ts @@ -44,7 +44,7 @@ import {UrlService} from "./url-service.js"; import {NotificationService} from "./notification-service.js"; import {UserSuiteEntity, UserSuiteService} from "@certd/commercial-core"; import {CertInfoService} from "../../monitor/service/cert-info-service.js"; -import {TaskServiceBuilder} from "./task-service-getter.js"; +import {TaskServiceBuilder} from "./getter/task-service-getter.js"; import {nanoid} from "nanoid"; import {set} from "lodash-es"; diff --git a/packages/ui/certd-server/src/modules/pipeline/service/sub-domain-service.ts b/packages/ui/certd-server/src/modules/pipeline/service/sub-domain-service.ts index 9df006be8..559ca8d1e 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/sub-domain-service.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/sub-domain-service.ts @@ -4,7 +4,6 @@ import {InjectEntityModel} from '@midwayjs/typeorm'; import {Repository} from 'typeorm'; import {SubDomainEntity} from '../entity/sub-domain.js'; import {EmailService} from '../../basic/service/email-service.js'; -import {ISubDomainsGetter} from "@certd/plugin-cert"; @Provide() @Scope(ScopeEnum.Request, { allowDowngrade: true }) @@ -38,21 +37,3 @@ export class SubDomainService extends BaseService { } } - - - - -export class SubDomainsGetter implements ISubDomainsGetter { - userId: number; - subDomainService: SubDomainService; - - constructor(userId: number, subDomainService: SubDomainService) { - this.userId = userId; - this.subDomainService = subDomainService; - } - - async getSubDomains() { - return await this.subDomainService.getListByUserId(this.userId) - } - -} diff --git a/packages/ui/certd-server/src/modules/pipeline/service/task-service-getter.ts b/packages/ui/certd-server/src/modules/pipeline/service/task-service-getter.ts deleted file mode 100644 index a54ea260c..000000000 --- a/packages/ui/certd-server/src/modules/pipeline/service/task-service-getter.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {IServiceGetter} from "@certd/pipeline"; -import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core"; -import {SubDomainService, SubDomainsGetter} from "./sub-domain-service.js"; -import {AccessGetter, AccessService} from "@certd/lib-server"; -import {CnameProxyService} from "./cname-proxy-service.js"; -import {NotificationGetter} from "./notification-getter.js"; -import {NotificationService} from "./notification-service.js"; -import {CnameRecordService} from "../../cname/service/cname-record-service.js"; - -export class TaskServiceGetter implements IServiceGetter{ - serviceContainer:Record; - constructor(serviceContainer:Record) { - this.serviceContainer = serviceContainer; - } - async get(serviceName: string): Promise { - const ret = this.serviceContainer[serviceName] as T; - if(!ret){ - throw new Error(`service ${serviceName} not found`) - } - return ret - } -} -export type TaskServiceCreateReq = { - userId: number; -} - -export type TaskServiceContainer = { - subDomainsGetter:SubDomainsGetter; - accessService: AccessGetter; - cnameProxyService: CnameProxyService; - notificationService: NotificationGetter; -} - -@Provide() -@Scope(ScopeEnum.Request, { allowDowngrade: true }) -export class TaskServiceBuilder { - - @Inject() - subDomainService: SubDomainService; - @Inject() - accessService: AccessService; - @Inject() - cnameRecordService: CnameRecordService; - @Inject() - notificationService: NotificationService; - - - create(req:TaskServiceCreateReq){ - - const userId = req.userId; - const accessGetter = new AccessGetter(userId, this.accessService.getById.bind(this.accessService)); - const cnameProxyService = new CnameProxyService(userId, this.cnameRecordService.getWithAccessByDomain.bind(this.cnameRecordService)); - const notificationGetter = new NotificationGetter(userId, this.notificationService); - - const serviceContainer:TaskServiceContainer = { - subDomainsGetter:new SubDomainsGetter(req.userId, this.subDomainService), - accessService: accessGetter, - cnameProxyService:cnameProxyService, - notificationService:notificationGetter - } - return new TaskServiceGetter(serviceContainer) - } -} From af5e1b805f07c4ffd21b5db044a19843f2a2c7bf Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sun, 13 Jul 2025 18:30:04 +0800 Subject: [PATCH 07/10] chore: --- .../src/controller/user/pipeline/handle-controller.ts | 2 +- .../src/modules/cname/service/cname-record-service.ts | 3 ++- .../src/modules/pipeline/service/getter/sub-domain-getter.ts | 2 +- .../modules/pipeline/service/getter/task-service-getter.ts | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/ui/certd-server/src/controller/user/pipeline/handle-controller.ts b/packages/ui/certd-server/src/controller/user/pipeline/handle-controller.ts index 06d3f45a4..536df2d4c 100644 --- a/packages/ui/certd-server/src/controller/user/pipeline/handle-controller.ts +++ b/packages/ui/certd-server/src/controller/user/pipeline/handle-controller.ts @@ -14,7 +14,7 @@ import { import {EmailService} from '../../../modules/basic/service/email-service.js'; import {http, HttpRequestConfig, logger, mergeUtils, utils} from '@certd/basic'; import {NotificationService} from '../../../modules/pipeline/service/notification-service.js'; -import {TaskServiceBuilder} from "../../../modules/pipeline/service/task-service-getter.js"; +import {TaskServiceBuilder} from "../../../modules/pipeline/service/getter/task-service-getter.js"; @Provide() @Controller('/api/pi/handle') diff --git a/packages/ui/certd-server/src/modules/cname/service/cname-record-service.ts b/packages/ui/certd-server/src/modules/cname/service/cname-record-service.ts index a94a5bbb6..10c5764fe 100644 --- a/packages/ui/certd-server/src/modules/cname/service/cname-record-service.ts +++ b/packages/ui/certd-server/src/modules/cname/service/cname-record-service.ts @@ -10,9 +10,10 @@ import {getAuthoritativeDnsResolver, walkTxtRecord} from '@certd/acme-client'; import {CnameProviderService} from './cname-provider-service.js'; import {CnameProviderEntity} from '../entity/cname-provider.js'; import {CommonDnsProvider} from './common-provider.js'; -import {SubDomainService, SubDomainsGetter} from "../../pipeline/service/sub-domain-service.js"; import {DomainParser} from "@certd/plugin-cert/dist/dns-provider/domain-parser.js"; import punycode from 'punycode.js' +import {SubDomainService} from "../../pipeline/service/sub-domain-service.js"; +import {SubDomainsGetter} from "../../pipeline/service/getter/sub-domain-getter.js"; type CnameCheckCacheValue = { validating: boolean; pass: boolean; diff --git a/packages/ui/certd-server/src/modules/pipeline/service/getter/sub-domain-getter.ts b/packages/ui/certd-server/src/modules/pipeline/service/getter/sub-domain-getter.ts index 4878aed05..bb0615cbd 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/getter/sub-domain-getter.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/getter/sub-domain-getter.ts @@ -1,5 +1,5 @@ import {ISubDomainsGetter} from "@certd/plugin-cert"; -import {SubDomainService} from "../service/sub-domain-service.js"; +import {SubDomainService} from "../sub-domain-service.js"; export class SubDomainsGetter implements ISubDomainsGetter { userId: number; diff --git a/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts b/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts index 006018dad..8943e45d1 100644 --- a/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts +++ b/packages/ui/certd-server/src/modules/pipeline/service/getter/task-service-getter.ts @@ -1,6 +1,5 @@ import {IServiceGetter} from "@certd/pipeline"; import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core"; -import {SubDomainService} from "../sub-domain-service.js"; import {AccessGetter, AccessService} from "@certd/lib-server"; import {CnameProxyService} from "./cname-proxy-service.js"; import {NotificationGetter} from "./notification-getter.js"; @@ -10,6 +9,7 @@ import {SubDomainsGetter} from './sub-domain-getter.js' import {DomainVerifierGetter} from "./domain-verifier-getter.js"; import {Context} from "@midwayjs/koa"; import {DomainService} from "../../../cert/service/domain-service.js"; +import {SubDomainService} from "../sub-domain-service.js"; export class TaskServiceGetter implements IServiceGetter{ private userId: number; @@ -36,7 +36,7 @@ export class TaskServiceGetter implements IServiceGetter{ } async getSubDomainsGetter(): Promise { - const subDomainsService = await this.ctx.requestContext.getAsync("subDomainService") + const subDomainsService:SubDomainService = await this.ctx.requestContext.getAsync("subDomainService") return new SubDomainsGetter(this.userId, subDomainsService) } From 896cd950e930f529c0b9a82251c6204e2dfa9816 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sun, 13 Jul 2025 23:08:00 +0800 Subject: [PATCH 08/10] chore: auto --- .../plugin-cert/src/dns-provider/api.ts | 8 ++-- .../src/plugin/cert-plugin/acme.ts | 4 +- .../src/plugin/cert-plugin/index.ts | 18 ++++---- .../src/locales/langs/en-US/certd.ts | 3 ++ .../src/locales/langs/en-US/common.ts | 2 + .../src/locales/langs/zh-CN/certd.ts | 3 ++ .../src/locales/langs/zh-CN/common.ts | 2 + .../src/views/certd/cert/domain/crud.tsx | 44 ++++++++++++++++--- .../src/views/certd/cert/domain/index.vue | 4 +- .../modules/cert/service/domain-service.ts | 37 ++++++++++++---- 10 files changed, 96 insertions(+), 29 deletions(-) diff --git a/packages/plugins/plugin-cert/src/dns-provider/api.ts b/packages/plugins/plugin-cert/src/dns-provider/api.ts index b3d87be39..0fd36b771 100644 --- a/packages/plugins/plugin-cert/src/dns-provider/api.ts +++ b/packages/plugins/plugin-cert/src/dns-provider/api.ts @@ -62,12 +62,14 @@ export interface IDomainParser { export type DnsVerifier = { // dns直接校验 - dnsProviderType: string; - dnsProviderAccessId: number; + dnsProviderType?: string; + dnsProviderAccessId?: number; }; export type CnameVerifier = { - cnameRecord: string; + hostRecord: string; + domain: string; + recordValue: string; }; export type HttpVerifier = { 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 9e4c095c2..2d1e4362d 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts @@ -269,11 +269,11 @@ export class AcmeService { throw new Error("不支持的校验类型", domainVerifyPlan.type); } } else { - this.logger.info("未找到域名校验计划,使用默认的dnsProvider"); + this.logger.warn(`未找到域名${fullDomain}的校验计划,使用默认的dnsProvider`); } } if (!dnsProvider) { - this.logger.error("dnsProvider不存在,无法申请证书"); + throw new Error(`域名${fullDomain}没有匹配到任何校验方式,证书申请失败`); } const dnsChallenge = getChallenge("dns-01"); 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 3c6543409..f41afe97b 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts @@ -66,15 +66,15 @@ export class CertApplyPlugin extends CertApplyBasePlugin { { value: "cname", label: "CNAME代理验证" }, { value: "http", label: "HTTP文件验证" }, { value: "dnses", label: "多DNS提供商" }, - { value: "auto", label: "自动选择" }, + { value: "auto", label: "自动匹配" }, ], }, required: true, helper: `1. DNS直接验证:域名dns解析是在阿里云/腾讯云/华为云/CF/NameSilo/西数/火山/dns.la/京东云/51dns的,选它 -2. CNAME代理验证:支持任何注册商的域名,第一次需要手动添加CNAME记录(建议将DNS服务器修改为阿里云/腾讯云的,然后使用DNS直接验证) +2. CNAME代理验证:支持任何注册商的域名,第一次需要手动添加[CNAME记录](#/certd/cname/record)(建议将DNS服务器修改为阿里云/腾讯云的,然后使用DNS直接验证) 3. HTTP文件验证:不支持泛域名,需要配置网站文件上传 4. 多DNS提供商:每个域名可以选择独立的DNS提供商 -5. 自动选择:需要在[域名管理](#/certd/cert/domain)中事先配置好校验方式 +5. 自动匹配:需要在[域名管理](#/certd/cert/domain)中事先配置好校验方式 `, }) challengeType!: string; @@ -469,13 +469,13 @@ export class CertApplyPlugin extends CertApplyBasePlugin { for (const fullDomain of domains) { const domain = fullDomain.replaceAll("*.", ""); const mainDomain = await domainParser.parse(domain); - const planSetting = verifyPlanSetting[mainDomain]; + const planSetting: DomainVerifyPlanInput = verifyPlanSetting[mainDomain]; if (planSetting.type === "dns") { - await this.createDnsDomainVerifyPlan(planSetting[mainDomain], domain, mainDomain); + plan[domain] = await this.createDnsDomainVerifyPlan(planSetting, domain, mainDomain); } else if (planSetting.type === "cname") { - await this.createCnameDomainVerifyPlan(domain, mainDomain); + plan[domain] = await this.createCnameDomainVerifyPlan(domain, mainDomain); } 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; @@ -486,7 +486,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin { // domain list const domainList = new Set(); //整理域名 - for (let domain in this.domains) { + for (let domain of domains) { domain = domain.replaceAll("*.", ""); domainList.add(domain); } @@ -563,7 +563,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin { domain, mainDomain, cnameVerifyPlan: { - domain, + domain: cnameRecord.cnameProvider.domain, fullRecord: cnameRecord.recordValue, dnsProvider, }, diff --git a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts index d17346cc0..351bb5e60 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts @@ -714,6 +714,7 @@ export default { }, domain: { domainManager: "Domain Manager", + domainDescription: "used to auto apply for certificate", //管理域名的校验方式,用于申请证书时自动选择验证方式 domain: "Domain", challengeType: "Challenge Type", dnsProviderType: "DNS Provider Type", @@ -722,5 +723,7 @@ export default { httpUploaderAccess: "HTTP Uploader Access", httpUploadRootDir: "HTTP Upload Root Dir", disabled: "Disabled", + challengeSetting: "Challenge Setting", + gotoCnameTip: "Please go to CNAME Record Page", }, }; diff --git a/packages/ui/certd-client/src/locales/langs/en-US/common.ts b/packages/ui/certd-client/src/locales/langs/en-US/common.ts index 34777d307..d0542e97a 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/common.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/common.ts @@ -14,6 +14,8 @@ export default { search: "Search", enabled: "Enabled", disabled: "Disabled", + enable: "Enable", + disable: "Disable", edit: "Edit", delete: "Delete", create: "Create", diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts index b5d8f37d3..c32ac8d74 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts @@ -717,6 +717,7 @@ export default { }, domain: { domainManager: "域名管理", + domainDescription: "管理域名的校验方式,用于申请证书时自动选择验证方式", domain: "域名", challengeType: "校验类型", dnsProviderType: "DNS提供商类型", @@ -725,5 +726,7 @@ export default { httpUploaderAccess: "上传授权信息", httpUploadRootDir: "网站根路径", disabled: "禁用/启用", + challengeSetting: "校验配置", + gotoCnameTip: "CNAME域名配置请前往CNAME记录页面添加", }, }; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/common.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/common.ts index 9cec01a92..1e990fb45 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/common.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/common.ts @@ -14,6 +14,8 @@ export default { search: "搜索", enabled: "已启用", disabled: "已禁用", + enable: "启用", + disable: "禁用", edit: "修改", delete: "删除", create: "新增", 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 57a2688ed..f6e10c24a 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 @@ -7,6 +7,7 @@ import { useUserStore } from "/@/store/user"; import { useSettingStore } from "/@/store/settings"; import { Dicts } from "/@/components/plugins/lib/dicts"; import { createAccessApi } from "/@/views/certd/access/api"; +import { Modal } from "ant-design-vue"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const router = useRouter(); @@ -73,13 +74,20 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat delRequest, }, tabs: { - name: "status", + name: "challengeType", show: true, }, rowHandle: { minWidth: 200, fixed: "right", }, + form: { + beforeSubmit({ form }) { + if (form.challengeType === "cname") { + throw new Error("CNAME方式请前往CNAME记录页面进行管理"); + } + }, + }, columns: { id: { title: "ID", @@ -114,11 +122,28 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat title: t("certd.domain.challengeType"), type: "dict-select", dict: Dicts.challengeTypeDict, + search: { + show: true, + }, form: { 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: { sorter: true, + show: false, }, }, /** @@ -196,6 +221,10 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat form: { component: { name: "AccessSelector", + vModel: "modelValue", + type: compute(({ form }) => { + return form.httpUploaderType; + }), }, show: compute(({ form }) => { return form.challengeType === "http"; @@ -226,16 +255,17 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, }, challengeSetting: { - title: "校验配置", + title: t("certd.domain.challengeSetting"), type: "text", form: { show: false }, column: { - width: 400, + width: 600, conditionalRender: false, cellRender({ row }) { if (row.challengeType === "dns") { return (
+
@@ -243,9 +273,10 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat } else if (row.challengeType === "http") { return (
+ - {row.httpUploadRootDir} + 路径:{row.httpUploadRootDir}
); } @@ -255,10 +286,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat disabled: { title: t("certd.domain.disabled"), type: "dict-switch", + search: { show: true }, dict: dict({ data: [ - { label: "启用", value: false, color: "green" }, - { label: "禁用", value: true, color: "red" }, + { label: t("common.enabled"), value: false, color: "green" }, + { label: t("common.disabled"), value: true, color: "red" }, ], }), form: { diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/index.vue b/packages/ui/certd-client/src/views/certd/cert/domain/index.vue index 8bf9ea635..b7c1f5b0f 100644 --- a/packages/ui/certd-client/src/views/certd/cert/domain/index.vue +++ b/packages/ui/certd-client/src/views/certd/cert/domain/index.vue @@ -3,7 +3,9 @@ diff --git a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts index 58c1146e7..1a3426bc6 100644 --- a/packages/ui/certd-server/src/modules/cert/service/domain-service.ts +++ b/packages/ui/certd-server/src/modules/cert/service/domain-service.ts @@ -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 {DomainVerifiers} from "@certd/plugin-cert"; 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 { @Inject() subDomainService: SubDomainService; + @Inject() + cnameRecordService: CnameRecordService; + //@ts-ignore getRepository() { return this.repository; @@ -96,10 +101,12 @@ export class DomainService extends BaseService { //去重 allDomains = [...new Set(allDomains)] + //从 domain 表中获取配置 const domainRecords = await this.find({ where: { domain: In(allDomains), - userId + userId, + disabled:false, } }) @@ -107,16 +114,28 @@ export class DomainService extends BaseService { pre[item.domain] = item 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) => { pre[item.domain] = item 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 = {} for (const domain of domains) { @@ -130,19 +149,21 @@ export class DomainService extends BaseService { type: 'dns', dns: { dnsProviderType: dnsRecord.dnsProviderType, - dnsProviderAccessId: dnsRecord.dnsProviderAccessId + dnsProviderAccessId: dnsRecord.dnsProviderAccess } } continue } - const cnameRecord = cnameMap[mainDomain] + const cnameRecord:CnameRecordEntity = cnameMap[mainDomain] if (cnameRecord) { domainVerifiers[domain] = { domain, mainDomain, type: 'cname', cname: { - cnameRecord: cnameRecord.cnameRecord + domain: cnameRecord.domain, + hostRecord: cnameRecord.hostRecord, + recordValue: cnameRecord.recordValue } } continue From 0b3158fdd5fe5bb0a98c4e65715dbc3de2c38047 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sun, 13 Jul 2025 23:14:28 +0800 Subject: [PATCH 09/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=9F=90?= =?UTF-8?q?=E4=BA=9B=E9=A1=B5=E9=9D=A2=E7=BF=BB=E8=AF=91=E4=B8=8D=E5=85=A8?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=94=99=E8=AF=AF=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../access/access-selector/access/crud.tsx | 2 +- .../src/views/certd/access/crud.tsx | 2 +- .../src/views/certd/access/index.vue | 2 +- .../src/views/certd/cert/domain/index.vue | 2 +- .../src/views/certd/cname/record/crud.tsx | 2 +- .../src/views/certd/cname/record/index.vue | 2 +- .../src/views/certd/history/crud.tsx | 2 +- .../src/views/certd/history/index.vue | 2 +- .../certd/mine/change-password-button.vue | 2 +- .../src/views/certd/mine/security/index.vue | 2 +- .../src/views/certd/mine/user-profile.vue | 2 +- .../src/views/certd/monitor/cert/crud.tsx | 2 +- .../src/views/certd/monitor/cert/index.vue | 2 +- .../src/views/certd/monitor/site/crud.tsx | 2 +- .../src/views/certd/monitor/site/index.vue | 2 +- .../src/views/certd/monitor/site/ip/crud.tsx | 2 +- .../certd/monitor/site/setting/index.vue | 2 +- .../src/views/certd/monitor/site/use.tsx | 2 +- .../notification-selector/index.vue | 2 +- .../src/views/certd/open/openkey/crud.tsx | 2 +- .../pipeline/components/change-trigger.vue | 2 +- .../src/views/certd/pipeline/index.vue | 2 +- .../component/notification-form/index.vue | 2 +- .../pi-notification-form-email.vue | 2 +- .../views/certd/pipeline/sub-domain/crud.tsx | 2 +- .../views/certd/pipeline/sub-domain/index.vue | 2 +- .../views/certd/pipeline/template/index.vue | 2 +- .../src/views/certd/suite/order-modal.vue | 22 +++++++++---------- .../src/views/certd/trade/crud.tsx | 2 +- .../views/framework/home/dashboard/index.vue | 2 +- .../src/views/framework/home/index.vue | 2 +- .../views/sys/authority/permission/crud.tsx | 2 +- .../permission/fs-permission-tree.vue | 2 +- .../views/sys/authority/permission/index.vue | 2 +- .../src/views/sys/authority/role/crud.tsx | 2 +- .../src/views/sys/authority/role/index.vue | 2 +- .../src/views/sys/authority/user/crud.tsx | 2 +- .../src/views/sys/cname/provider/crud.tsx | 2 +- .../src/views/sys/cname/provider/index.vue | 2 +- .../src/views/sys/plugin/crud.tsx | 2 +- .../src/views/sys/plugin/index.vue | 2 +- .../src/views/sys/settings/email/index.vue | 2 +- .../views/sys/settings/header-menus/crud.tsx | 2 +- .../src/views/sys/settings/tabs/base.vue | 2 +- .../src/views/sys/settings/tabs/register.vue | 2 +- .../src/views/sys/settings/tabs/safe.vue | 2 +- .../src/views/sys/suite/product/crud.tsx | 2 +- .../src/views/sys/suite/trade/crud.tsx | 2 +- 48 files changed, 58 insertions(+), 58 deletions(-) diff --git a/packages/ui/certd-client/src/views/certd/access/access-selector/access/crud.tsx b/packages/ui/certd-client/src/views/certd/access/access-selector/access/crud.tsx index 9093eeb58..23c54a129 100644 --- a/packages/ui/certd-client/src/views/certd/access/access-selector/access/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/access/access-selector/access/crud.tsx @@ -2,7 +2,7 @@ import { ref } from "vue"; import { getCommonColumnDefine } from "/@/views/certd/access/common"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; -import { useI18n } from "vue-i18n"; +import { useI18n } from "/src/locales"; export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { const { t } = useI18n(); diff --git a/packages/ui/certd-client/src/views/certd/access/crud.tsx b/packages/ui/certd-client/src/views/certd/access/crud.tsx index ff6ad138d..00323872e 100644 --- a/packages/ui/certd-client/src/views/certd/access/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/access/crud.tsx @@ -1,5 +1,5 @@ // @ts-ignore -import { useI18n } from "vue-i18n"; +import { useI18n } from "/src/locales"; import { ref } from "vue"; import { getCommonColumnDefine } from "/@/views/certd/access/common"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; diff --git a/packages/ui/certd-client/src/views/certd/access/index.vue b/packages/ui/certd-client/src/views/certd/access/index.vue index aeec1c9c3..25d312652 100644 --- a/packages/ui/certd-client/src/views/certd/access/index.vue +++ b/packages/ui/certd-client/src/views/certd/access/index.vue @@ -15,7 +15,7 @@ import { defineComponent, onActivated, onMounted } from "vue"; import { useFs } from "@fast-crud/fast-crud"; import createCrudOptions from "./crud"; import { createAccessApi } from "/@/views/certd/access/api"; -import { useI18n } from "vue-i18n"; +import { useI18n } from "/src/locales"; export default defineComponent({ name: "AccessManager", diff --git a/packages/ui/certd-client/src/views/certd/cert/domain/index.vue b/packages/ui/certd-client/src/views/certd/cert/domain/index.vue index b7c1f5b0f..2ffd486e4 100644 --- a/packages/ui/certd-client/src/views/certd/cert/domain/index.vue +++ b/packages/ui/certd-client/src/views/certd/cert/domain/index.vue @@ -24,7 +24,7 @@ import { useFs } from "@fast-crud/fast-crud"; import createCrudOptions from "./crud"; import { message, Modal } from "ant-design-vue"; import { DeleteBatch } from "./api"; -import { useI18n } from "vue-i18n"; +import { useI18n } from "/src/locales"; const { t } = useI18n(); diff --git a/packages/ui/certd-client/src/views/certd/cname/record/crud.tsx b/packages/ui/certd-client/src/views/certd/cname/record/crud.tsx index 769da2de5..ddad615df 100644 --- a/packages/ui/certd-client/src/views/certd/cname/record/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/cname/record/crud.tsx @@ -1,5 +1,5 @@ import * as api from "./api"; -import { useI18n } from "vue-i18n"; +import { useI18n } from "/src/locales"; import { Ref, ref } from "vue"; import { useRouter } from "vue-router"; import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; diff --git a/packages/ui/certd-client/src/views/certd/cname/record/index.vue b/packages/ui/certd-client/src/views/certd/cname/record/index.vue index 52eadb50b..4ed382d3f 100644 --- a/packages/ui/certd-client/src/views/certd/cname/record/index.vue +++ b/packages/ui/certd-client/src/views/certd/cname/record/index.vue @@ -26,7 +26,7 @@ import { useFs } from "@fast-crud/fast-crud"; import createCrudOptions from "./crud"; import { message, Modal } from "ant-design-vue"; import { DeleteBatch } from "./api"; -import { useI18n } from "vue-i18n"; +import { useI18n } from "/src/locales"; const { t } = useI18n(); diff --git a/packages/ui/certd-client/src/views/certd/history/crud.tsx b/packages/ui/certd-client/src/views/certd/history/crud.tsx index c745721c4..2c2b2a669 100644 --- a/packages/ui/certd-client/src/views/certd/history/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/history/crud.tsx @@ -1,5 +1,5 @@ import * as api from "./api"; -import { useI18n } from "vue-i18n"; +import { useI18n } from "/src/locales"; import { computed, Ref, ref } from "vue"; import { useRouter } from "vue-router"; import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, utils } from "@fast-crud/fast-crud"; diff --git a/packages/ui/certd-client/src/views/certd/history/index.vue b/packages/ui/certd-client/src/views/certd/history/index.vue index 17bfb9bcc..dea2c720d 100644 --- a/packages/ui/certd-client/src/views/certd/history/index.vue +++ b/packages/ui/certd-client/src/views/certd/history/index.vue @@ -19,7 +19,7 @@ import { useFs } from "@fast-crud/fast-crud"; import createCrudOptions from "./crud"; import { message, Modal } from "ant-design-vue"; import { DeleteBatch } from "./api"; -import { useI18n } from "vue-i18n"; +import { useI18n } from "/src/locales"; const { t } = useI18n(); diff --git a/packages/ui/certd-client/src/views/certd/mine/change-password-button.vue b/packages/ui/certd-client/src/views/certd/mine/change-password-button.vue index 1fc265e2a..b6615fbef 100644 --- a/packages/ui/certd-client/src/views/certd/mine/change-password-button.vue +++ b/packages/ui/certd-client/src/views/certd/mine/change-password-button.vue @@ -6,7 +6,7 @@