perf: 支持公共cname服务

This commit is contained in:
xiaojunnuo
2024-11-08 01:31:20 +08:00
parent fdc6eef921
commit 3c919ee5d1
9 changed files with 90 additions and 34 deletions
@@ -1,5 +1,5 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
export type CnameRecordStatusType = 'cname' | 'validating' | 'valid' | 'error';
export type CnameRecordStatusType = 'cname' | 'validating' | 'valid' | 'error' | 'timeout';
/**
* cname record配置
*/
@@ -4,11 +4,11 @@ import { Repository } from 'typeorm';
import { BaseService, PlusService, ValidateException } from '@certd/lib-server';
import { CnameRecordEntity, CnameRecordStatusType } from '../entity/cname-record.js';
import { createDnsProvider, IDnsProvider, parseDomain } from '@certd/plugin-cert';
import { CnameProvider } from '@certd/pipeline';
import { CnameProvider, CnameRecord } from '@certd/pipeline';
import { cache, http, logger, utils } from '@certd/basic';
import { AccessService } from '../../pipeline/service/access-service.js';
import { isDev } from '../../../utils/env.js';
import { isDev } from '@certd/basic';
import { walkTxtRecord } from '@certd/acme-client';
import { CnameProviderService } from './cname-provider-service.js';
import { CnameProviderEntity } from '../entity/cname-provider.js';
@@ -128,8 +128,17 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
// }
async getWithAccessByDomain(domain: string, userId: number) {
const record = await this.getByDomain(domain, userId);
record.cnameProvider.access = await this.accessService.getAccessById(record.cnameProvider.accessId, false);
const record: CnameRecord = await this.getByDomain(domain, userId);
if (record.cnameProvider.id > 0) {
//自定义cname服务
record.cnameProvider.access = await this.accessService.getAccessById(record.cnameProvider.accessId, false);
} else {
record.commonDnsProvider = new CommonDnsProvider({
config: record.cnameProvider,
plusService: this.plusService,
});
}
return record;
}
@@ -158,7 +167,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
cnameProvider: {
...provider,
} as CnameProvider,
};
} as CnameRecord;
}
/**
@@ -184,17 +193,20 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
startTime: new Date().getTime(),
};
}
let ttl = 60 * 60 * 15 * 1000;
let ttl = 15 * 60 * 1000;
if (isDev()) {
ttl = 30 * 1000;
}
const recordValue = bean.recordValue.substring(0, bean.recordValue.indexOf('.'));
const testRecordValue = 'certd-cname-verify';
const buildDnsProvider = async () => {
const cnameProvider = await this.cnameProviderService.info(bean.cnameProviderId);
if (cnameProvider == null) {
throw new ValidateException(`CNAME服务:${bean.cnameProviderId} 已被删除,请修改CNAME记录,重新选择CNAME服务`);
}
if (cnameProvider.disabled === true) {
throw new Error(`CNAME服务:${bean.cnameProviderId} 已被禁用`);
}
if (cnameProvider.id < 0) {
//公共CNAME
@@ -218,16 +230,16 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
return true;
}
if (value.startTime + ttl < new Date().getTime()) {
logger.warn(`cname验证超时,停止检查,${bean.domain} ${recordValue}`);
logger.warn(`cname验证超时,停止检查,${bean.domain} ${testRecordValue}`);
clearInterval(value.intervalId);
await this.updateStatus(bean.id, 'cname');
await this.updateStatus(bean.id, 'timeout');
return false;
}
const originDomain = parseDomain(bean.domain);
const fullDomain = `${bean.hostRecord}.${originDomain}`;
logger.info(`检查CNAME配置 ${fullDomain} ${recordValue}`);
logger.info(`检查CNAME配置 ${fullDomain} ${testRecordValue}`);
// const txtRecords = await dns.promises.resolveTxt(fullDomain);
// if (txtRecords.length) {
@@ -240,10 +252,10 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
logger.error(`获取TXT记录失败,${e.message}`);
}
logger.info(`检查到TXT记录 ${JSON.stringify(records)}`);
const success = records.includes(recordValue);
const success = records.includes(testRecordValue);
if (success) {
clearInterval(value.intervalId);
logger.info(`检测到CNAME配置,修改状态 ${fullDomain} ${recordValue}`);
logger.info(`检测到CNAME配置,修改状态 ${fullDomain} ${testRecordValue}`);
await this.updateStatus(bean.id, 'valid');
value.pass = true;
cache.delete(cacheKey);
@@ -257,8 +269,8 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
} catch (e) {
logger.error(`删除CNAME的校验DNS记录失败, ${e.message}req:${JSON.stringify(value.recordReq)}recordRes:${JSON.stringify(value.recordRes)}`, e);
}
return success;
}
return success;
};
if (value.validating) {
@@ -278,7 +290,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
fullRecord: fullRecord,
hostRecord: hostRecord,
type: 'TXT',
value: recordValue,
value: testRecordValue,
};
const dnsProvider = await buildDnsProvider();
const recordRes = await dnsProvider.createRecord(req);
@@ -1,10 +1,10 @@
import { CreateRecordOptions, DnsProviderContext, IDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert';
import { PlusService } from '@certd/lib-server';
export type CnameProvider = {
export type CommonCnameProvider = {
id: number;
domain: string;
title: string;
title?: string;
};
export const CommonProviders = [
{
@@ -16,10 +16,10 @@ export const CommonProviders = [
export class CommonDnsProvider implements IDnsProvider {
ctx: DnsProviderContext;
config: CnameProvider;
config: CommonCnameProvider;
plusService: PlusService;
constructor(opts: { config: CnameProvider; plusService: PlusService }) {
constructor(opts: { config: CommonCnameProvider; plusService: PlusService }) {
this.config = opts.config;
this.plusService = opts.plusService;
}
@@ -34,8 +34,9 @@ export class CommonDnsProvider implements IDnsProvider {
const res = await this.plusService.requestWithToken({
url: '/activation/certd/cname/recordCreate',
method: 'post',
data: {
subjectId: this.plusService.getSubjectId(),
subjectId: await this.plusService.getSubjectId(),
domain: options.domain,
hostRecord: options.hostRecord,
recordValue: options.value,
@@ -47,12 +48,13 @@ export class CommonDnsProvider implements IDnsProvider {
async removeRecord(options: RemoveRecordOptions<any>) {
const res = await this.plusService.requestWithToken({
url: '/activation/certd/cname/recordRemove',
method: 'post',
data: {
subjectId: this.plusService.getSubjectId(),
subjectId: await this.plusService.getSubjectId(),
domain: options.recordReq.domain,
hostRecord: options.recordReq.hostRecord,
recordValue: options.recordReq.value,
recordId: options.recordRes.id,
recordId: options.recordRes.recordId,
providerId: this.config.id,
},
});