mirror of https://github.com/certd/certd
perf: cname 域名映射记录可读性优化
parent
7ad4b55ee0
commit
b1117ed54a
|
@ -9,7 +9,7 @@ export * from './util.merge.js';
|
|||
export * from './util.cache.js';
|
||||
import sleep from './util.sleep.js';
|
||||
import { http } from './util.request.js';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
import { mergeUtils } from './util.merge.js';
|
||||
import { sp } from './util.sp.js';
|
||||
import { hashUtils } from './util.hash.js';
|
||||
|
@ -20,7 +20,8 @@ import { cache } from './util.cache.js';
|
|||
import dayjs from 'dayjs';
|
||||
import { domainUtils } from './util.domain.js';
|
||||
import { optionsUtils } from './util.options.js';
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import * as id from './util.id.js';
|
||||
export const utils = {
|
||||
sleep,
|
||||
http,
|
||||
|
@ -32,6 +33,7 @@ export const utils = {
|
|||
mergeUtils,
|
||||
cache,
|
||||
nanoid,
|
||||
id,
|
||||
dayjs,
|
||||
domain: domainUtils,
|
||||
options: optionsUtils,
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import { customAlphabet } from 'nanoid';
|
||||
|
||||
export const simpleNanoId = customAlphabet('1234567890abcdefghijklmopqrstuvwxyz', 10);
|
|
@ -12,13 +12,18 @@ export class PlusService {
|
|||
plusServerBaseUrls: string[];
|
||||
|
||||
async getPlusRequestService() {
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
const subjectId = await this.getSubjectId();
|
||||
return new PlusRequestService({
|
||||
plusServerBaseUrls: this.plusServerBaseUrls,
|
||||
subjectId: installInfo.siteId,
|
||||
subjectId,
|
||||
});
|
||||
}
|
||||
|
||||
async getSubjectId() {
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
return installInfo.siteId;
|
||||
}
|
||||
|
||||
async requestWithoutSign(config: any) {
|
||||
const plusRequestService = await this.getPlusRequestService();
|
||||
return await plusRequestService.requestWithoutSign(config);
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.2",
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"@fast-crud/fast-crud": "^1.22.2",
|
||||
"@fast-crud/fast-extends": "^1.22.2",
|
||||
"@fast-crud/ui-antdv4": "^1.22.2",
|
||||
"@fast-crud/ui-interface": "^1.22.2",
|
||||
"@fast-crud/fast-crud": "^1.22.3",
|
||||
"@fast-crud/fast-extends": "^1.22.3",
|
||||
"@fast-crud/ui-antdv4": "^1.22.3",
|
||||
"@fast-crud/ui-interface": "^1.22.3",
|
||||
"@iconify/vue": "^4.1.1",
|
||||
"@soerenmartius/vue3-clipboard": "^0.1.2",
|
||||
"@vue-js-cron/light": "^4.0.5",
|
||||
|
|
|
@ -15,7 +15,11 @@
|
|||
<fs-values-format v-model="cnameRecord.status" :dict="statusDict" />
|
||||
</td>
|
||||
<td class="center">
|
||||
<a-button v-if="cnameRecord.status !== 'valid'" type="primary" size="small" :loading="loading" @click="doVerify">点击验证</a-button>
|
||||
<template v-if="cnameRecord.status !== 'valid'">
|
||||
<a-button type="primary" size="small" :loading="loading" @click="doVerify">点击验证</a-button>
|
||||
<cname-tip :record="cnameRecord"></cname-tip>
|
||||
</template>
|
||||
|
||||
<div v-else class="helper">不要删除CNAME</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -27,6 +31,7 @@ import { CnameRecord, GetByDomain } from "/@/components/plugins/cert/domains-ver
|
|||
import { ref, watch } from "vue";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
import * as api from "./api.js";
|
||||
import CnameTip from "./cname-tip.vue";
|
||||
const statusDict = dict({
|
||||
data: [
|
||||
{ label: "待设置CNAME", value: "cname", color: "warning" },
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<a-tooltip :overlay-style="{ maxWidth: '400px' }">
|
||||
<template #title>
|
||||
<div>
|
||||
<div>多试几次,如果仍然无法验证通过,请按如下步骤排查问题:</div>
|
||||
<div>1. 解析记录应该添加在{{ record.domain }}域名下</div>
|
||||
<div>2. 要添加的是CNAME类型的记录,不是TXT</div>
|
||||
<div>3. 核对记录值是否是:{{ record.recordValue }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<fs-icon class="ml-5 pointer" icon="mingcute:question-line"></fs-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps<{
|
||||
record: any;
|
||||
}>();
|
||||
</script>
|
|
@ -64,18 +64,22 @@ const steps = ref<Step[]>([
|
|||
{
|
||||
image: "/static/doc/images/3-add-success.png",
|
||||
title: "流水线创建成功",
|
||||
descriptions: ["此时证书申请任务已经建好", "点击手动触发即可测试证书申请", "接下来演示如何添加部署任务"]
|
||||
descriptions: ["点击手动触发即可申请证书"]
|
||||
},
|
||||
{
|
||||
title: "接下来演示如何自动部署证书",
|
||||
descriptions: ["如果您只需要申请证书,那么到这一步就可以了"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "添加部署证书任务",
|
||||
description: "演示部署到主机上的Nginx",
|
||||
description: "这里演示部署证书到Nginx",
|
||||
items: [
|
||||
{
|
||||
image: "/static/doc/images/5-1-add-host.png",
|
||||
title: "添加nginx部署任务",
|
||||
descriptions: ["演示第一个部署任务,部署到nginx"]
|
||||
title: "添加证书部署任务",
|
||||
descriptions: ["这里演示自动部署证书到nginx", "Certd提供茫茫多的部署插件,满足您的各种部署需求"]
|
||||
},
|
||||
{
|
||||
image: "/static/doc/images/5-2-add-host.png",
|
||||
|
@ -94,8 +98,8 @@ const steps = ref<Step[]>([
|
|||
},
|
||||
{
|
||||
image: "/static/doc/images/5-5-plugin-list.png",
|
||||
title: "还可以添加其他更多部署任务",
|
||||
descriptions: ["插件列表"]
|
||||
title: "本系统提供茫茫多的部署插件",
|
||||
descriptions: ["您可以根据自身需求将证书部署到各种应用和平台"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -155,7 +155,7 @@ function openUpgrade() {
|
|||
title = "续期专业版/升级商业版";
|
||||
}
|
||||
|
||||
modal.confirm({
|
||||
const modalRef = modal.confirm({
|
||||
title,
|
||||
async onOk() {
|
||||
return await doActive();
|
||||
|
@ -206,6 +206,10 @@ function openUpgrade() {
|
|||
</a-col>
|
||||
);
|
||||
}
|
||||
function goAccount() {
|
||||
router.push("/sys/account");
|
||||
modalRef.destroy();
|
||||
}
|
||||
return (
|
||||
<div class="mt-10 mb-10 vip-active-modal">
|
||||
<div class="vip-type-vs">
|
||||
|
@ -226,6 +230,9 @@ function openUpgrade() {
|
|||
没有激活码?
|
||||
{activationCodeGetWay}
|
||||
</div>
|
||||
<div class="mt-10">
|
||||
激活码使用过一次之后,不可再次使用,如果要更换站点,请<a onClick={goAccount}>绑定账号</a>,然后"转移VIP"即可
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -165,7 +165,7 @@ onMounted(async () => {
|
|||
await settingStore.checkUrlBound();
|
||||
});
|
||||
|
||||
function menuClick(menu) {
|
||||
function menuClick(menu: any) {
|
||||
routerUtils.open(menu.path);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, Edi
|
|||
import { useUserStore } from "/@/store/modules/user";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { message } from "ant-design-vue";
|
||||
|
||||
import CnameTip from "/@/components/plugins/cert/domains-verify-plan-editor/cname-tip.vue";
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
|
@ -126,11 +126,18 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
component: {
|
||||
onDictChange: ({ form, dict }: any) => {
|
||||
if (!form.cnameProviderId) {
|
||||
const item = dict.data.find((item: any) => item.isDefault);
|
||||
const item = dict.data.find((item: any) => item.isDefault && !item.disabled);
|
||||
if (item) {
|
||||
form.cnameProviderId = item.id;
|
||||
}
|
||||
}
|
||||
},
|
||||
renderLabel(item: any) {
|
||||
if (item.title) {
|
||||
return `${item.domain}<${item.title}>`;
|
||||
} else {
|
||||
return item.domain;
|
||||
}
|
||||
}
|
||||
},
|
||||
helper: {
|
||||
|
@ -147,7 +154,13 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
}
|
||||
},
|
||||
column: {
|
||||
show: false
|
||||
cellRender({ value }) {
|
||||
if (value < 0) {
|
||||
return <a-tag color={"green"}>公共CNAME</a-tag>;
|
||||
} else {
|
||||
return <a-tag color={"blue"}>自定义CNAME</a-tag>;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
status: {
|
||||
|
@ -183,6 +196,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
if (row.status === "valid") {
|
||||
return "-";
|
||||
}
|
||||
|
||||
async function doVerify() {
|
||||
row._validating_ = true;
|
||||
try {
|
||||
|
@ -199,9 +213,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||
}
|
||||
}
|
||||
return (
|
||||
<a-button onClick={doVerify} loading={row._validating_} size={"small"} type={"primary"}>
|
||||
点击验证
|
||||
</a-button>
|
||||
<div>
|
||||
<a-button onClick={doVerify} loading={row._validating_} size={"small"} type={"primary"}>
|
||||
点击验证
|
||||
</a-button>
|
||||
<CnameTip record={row} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ export class FileController extends BaseController {
|
|||
}
|
||||
const filePath = this.fileService.getFile(key, userId);
|
||||
this.ctx.response.attachment(filePath);
|
||||
this.ctx.response.set('Cache-Control', 'public,max-age=2592000');
|
||||
await send(this.ctx, filePath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export class CnameProviderController extends BaseController {
|
|||
@Post('/list', { summary: Constants.per.authOnly })
|
||||
async list(@Body(ALL) body: any) {
|
||||
body.userId = this.getUserId();
|
||||
const res = await this.providerService.find({});
|
||||
const res = await this.providerService.list({});
|
||||
return this.ok(res);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
|
|||
@Post('/list', { summary: Constants.per.authOnly })
|
||||
async list(@Body(ALL) body: any) {
|
||||
body.userId = this.getUserId();
|
||||
return super.list(body);
|
||||
const list = await this.getService().list(body);
|
||||
return this.ok(list);
|
||||
}
|
||||
|
||||
@Post('/add', { summary: Constants.per.authOnly })
|
||||
|
|
|
@ -34,4 +34,6 @@ export class CnameProviderEntity {
|
|||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
|
||||
title: string;
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService, ValidateException } from '@certd/lib-server';
|
||||
import { CnameProviderEntity } from '../entity/cname_provider.js';
|
||||
import { BaseService, ListReq, ValidateException } from '@certd/lib-server';
|
||||
import { CnameProviderEntity } from '../entity/cname-provider.js';
|
||||
import { CommonProviders } from './common-provider.js';
|
||||
|
||||
/**
|
||||
* 授权
|
||||
|
@ -19,7 +20,7 @@ export class CnameProviderService extends BaseService<CnameProviderEntity> {
|
|||
}
|
||||
|
||||
async getDefault() {
|
||||
return await this.repository.findOne({ where: { isDefault: true } });
|
||||
return await this.repository.findOne({ where: { isDefault: true, disabled: false } });
|
||||
}
|
||||
/**
|
||||
* 新增
|
||||
|
@ -80,10 +81,24 @@ export class CnameProviderService extends BaseService<CnameProviderEntity> {
|
|||
if (def) {
|
||||
return def;
|
||||
}
|
||||
const founds = await this.repository.find({ take: 1, order: { createTime: 'DESC' } });
|
||||
const founds = await this.repository.find({ take: 1, order: { createTime: 'DESC' }, where: { disabled: false } });
|
||||
if (founds && founds.length > 0) {
|
||||
return founds[0];
|
||||
}
|
||||
return null;
|
||||
return CommonProviders[0] as CnameProviderEntity;
|
||||
}
|
||||
|
||||
async list(req: ListReq): Promise<any[]> {
|
||||
const list = await super.list(req);
|
||||
|
||||
return [...list, ...CommonProviders];
|
||||
}
|
||||
|
||||
async info(id: any, infoIgnoreProperty?: any): Promise<any | null> {
|
||||
if (id < 0) {
|
||||
//使用公共provider
|
||||
return CommonProviders.find(p => p.id === id);
|
||||
}
|
||||
return await super.info(id, infoIgnoreProperty);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService, ValidateException } from '@certd/lib-server';
|
||||
import { BaseService, PlusService, ValidateException } from '@certd/lib-server';
|
||||
import { CnameRecordEntity, CnameRecordStatusType } from '../entity/cname-record.js';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { createDnsProvider, IDnsProvider, parseDomain } from '@certd/plugin-cert';
|
||||
import { cache, CnameProvider, http, logger, utils } from '@certd/pipeline';
|
||||
import { AccessService } from '../../pipeline/service/access-service.js';
|
||||
import { isDev } from '../../../utils/env.js';
|
||||
import { walkTxtRecord } from '@certd/acme-client';
|
||||
import { CnameProviderService } from './cname-provider-service.js';
|
||||
import { CnameProviderEntity } from '../entity/cname_provider.js';
|
||||
import { CnameProviderEntity } from '../entity/cname-provider.js';
|
||||
import { CommonDnsProvider } from './common-provider.js';
|
||||
|
||||
type CnameCheckCacheValue = {
|
||||
validating: boolean;
|
||||
|
@ -34,6 +34,10 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||
|
||||
@Inject()
|
||||
accessService: AccessService;
|
||||
|
||||
@Inject()
|
||||
plusService: PlusService;
|
||||
|
||||
//@ts-ignore
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
|
@ -85,8 +89,8 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||
}
|
||||
param.hostRecord = hostRecord;
|
||||
|
||||
const cnameKey = uuidv4().replaceAll('-', '');
|
||||
param.recordValue = `${cnameKey}.${cnameProvider.domain}`;
|
||||
const cnameKey = utils.id.simpleNanoId();
|
||||
param.recordValue = `${param.domain}.${cnameKey}.${cnameProvider.domain}`;
|
||||
}
|
||||
|
||||
async update(param: any) {
|
||||
|
@ -189,6 +193,15 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||
if (cnameProvider == null) {
|
||||
throw new ValidateException(`CNAME服务:${bean.cnameProviderId} 已被删除,请修改CNAME记录,重新选择CNAME服务`);
|
||||
}
|
||||
|
||||
if (cnameProvider.id < 0) {
|
||||
//公共CNAME
|
||||
return new CommonDnsProvider({
|
||||
config: cnameProvider,
|
||||
plusService: this.plusService,
|
||||
});
|
||||
}
|
||||
|
||||
const access = await this.accessService.getById(cnameProvider.accessId, cnameProvider.userId);
|
||||
const context = { access, logger, http, utils };
|
||||
const dnsProvider: IDnsProvider = await createDnsProvider({
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import { CreateRecordOptions, DnsProviderContext, IDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
|
||||
import { PlusService } from '@certd/lib-server';
|
||||
|
||||
export type CnameProvider = {
|
||||
id: number;
|
||||
domain: string;
|
||||
title: string;
|
||||
};
|
||||
export const CommonProviders = [
|
||||
{
|
||||
id: -1,
|
||||
domain: 'cname.certd.com.cn',
|
||||
title: '公共CNAME服务',
|
||||
},
|
||||
];
|
||||
|
||||
export class CommonDnsProvider implements IDnsProvider {
|
||||
ctx: DnsProviderContext;
|
||||
config: CnameProvider;
|
||||
plusService: PlusService;
|
||||
|
||||
constructor(opts: { config: CnameProvider; plusService: PlusService }) {
|
||||
this.config = opts.config;
|
||||
this.plusService = opts.plusService;
|
||||
}
|
||||
|
||||
async onInstance() {}
|
||||
async createRecord(options: CreateRecordOptions) {
|
||||
if (!this.config.domain.endsWith(options.domain)) {
|
||||
throw new Error('cname服务域名不匹配');
|
||||
}
|
||||
const res = await this.plusService.requestWithoutSign({
|
||||
url: '/activation/certd/cname/recordCreate',
|
||||
data: {
|
||||
subjectId: this.plusService.getSubjectId(),
|
||||
domain: options.domain,
|
||||
hostRecord: options.hostRecord,
|
||||
recordValue: options.value,
|
||||
providerId: this.config.id,
|
||||
},
|
||||
});
|
||||
return res;
|
||||
}
|
||||
async removeRecord(options: RemoveRecordOptions<any>) {
|
||||
const res = await this.plusService.requestWithoutSign({
|
||||
url: '/activation/certd/cname/recordRemove',
|
||||
data: {
|
||||
subjectId: this.plusService.getSubjectId(),
|
||||
domain: options.recordReq.domain,
|
||||
hostRecord: options.recordReq.hostRecord,
|
||||
recordValue: options.recordReq.value,
|
||||
recordId: options.recordRes.id,
|
||||
providerId: this.config.id,
|
||||
},
|
||||
});
|
||||
return res;
|
||||
}
|
||||
setCtx(ctx: DnsProviderContext): void {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue