perf: 检查cname是否正确配置

This commit is contained in:
xiaojunnuo
2024-10-09 02:34:28 +08:00
parent 9498d189e4
commit b5d8935159
36 changed files with 321 additions and 190 deletions
@@ -58,4 +58,12 @@ export class CnameRecordController extends CrudController<CnameRecordService> {
const res = await this.service.getByDomain(body.domain, userId, body.createOnNotFound);
return this.ok(res);
}
@Post('/verify', { summary: Constants.per.authOnly })
async verify(@Body(ALL) body: { id: string }) {
const userId = this.ctx.user.id;
await this.service.checkUserId(body.id, userId);
const res = await this.service.verify(body.id);
return this.ok(res);
}
}
@@ -1,5 +1,5 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
export type CnameRecordStatusType = 'cname' | 'validating' | 'valid' | 'error';
/**
* cname record配置
*/
@@ -2,11 +2,14 @@ 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 { CnameRecordEntity } from '../entity/cname-record.js';
import { CnameRecordEntity, CnameRecordStatusType } from '../entity/cname-record.js';
import { v4 as uuidv4 } from 'uuid';
import { CnameProviderService } from '../../sys/cname/service/cname-provider-service.js';
import { CnameProviderEntity } from '../../sys/cname/entity/cname_provider.js';
import { parseDomain } from '@certd/plugin-cert';
import { createDnsProvider, IDnsProvider, parseDomain } from '@certd/plugin-cert';
import { cache, http, logger, utils } from '@certd/pipeline';
import dns from 'dns';
import { AccessService } from '../../pipeline/service/access-service.js';
/**
* 授权
@@ -19,6 +22,10 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
@Inject()
cnameProviderService: CnameProviderService;
@Inject()
accessService: AccessService;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -130,4 +137,81 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
cnameProvider: provider,
};
}
/**
* 验证是否配置好cname
* @param id
*/
async verify(id: string) {
const bean = await this.info(id);
if (!bean) {
throw new ValidateException(`CnameRecord:${id} 不存在`);
}
const cacheKey = `cname.record.verify.${bean.id}`;
type CacheValue = {
ready: boolean;
pass: boolean;
};
let value: CacheValue = cache.get(cacheKey);
if (!value) {
value = {
ready: false,
pass: false,
};
}
const originDomain = parseDomain(bean.domain);
const fullDomain = `${bean.hostRecord}.${originDomain}`;
const recordValue = bean.recordValue.substring(0, bean.recordValue.indexOf('.'));
const checkRecordValue = async () => {
logger.info(`检查CNAME配置 ${fullDomain} ${recordValue}`);
const txtRecords = await dns.promises.resolveTxt(fullDomain);
let records: string[] = [];
if (txtRecords.length) {
records = [].concat(...txtRecords);
}
logger.info(`检查到TXT记录 ${JSON.stringify(records)}`);
const success = records.includes(recordValue);
if (success) {
logger.info(`检测到CNAME配置,修改状态 ${fullDomain} ${recordValue}`);
await this.updateStatus(bean.id, 'valid');
value.pass = true;
}
};
if (value.ready) {
// lookup recordValue in dns
return await checkRecordValue();
}
const ttl = 60 * 60 * 30;
cache.set(cacheKey, value, {
ttl: ttl,
});
const cnameProvider = await this.cnameProviderService.info(bean.cnameProviderId);
const access = await this.accessService.getById(cnameProvider.accessId, bean.userId);
const context = { access, logger, http, utils };
const dnsProvider: IDnsProvider = await createDnsProvider({
dnsProviderType: cnameProvider.dnsProviderType,
context,
});
const domain = parseDomain(bean.recordValue);
const fullRecord = bean.recordValue;
const hostRecord = fullRecord.replace(`.${domain}`, '');
const req = {
domain: domain,
fullRecord: fullRecord,
hostRecord: hostRecord,
type: 'TXT',
value: recordValue,
};
await dnsProvider.createRecord(req);
value.ready = true;
}
async updateStatus(id: number, status: CnameRecordStatusType) {
await this.getRepository().update(id, { status });
}
}
@@ -13,6 +13,7 @@ export class UserSettingsService extends BaseService<UserSettingsEntity> {
@InjectEntityModel(UserSettingsEntity)
repository: Repository<UserSettingsEntity>;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -15,6 +15,7 @@ import {
import { BaseController } from '@certd/lib-server';
import { AccessService } from '../service/access-service.js';
import { EmailService } from '../../basic/service/email-service.js';
import { AccessGetter } from '../service/access-getter.js';
@Provide()
@Controller('/api/pi/handle')
@@ -49,6 +50,7 @@ export class HandleController extends BaseController {
@Post('/plugin', { summary: Constants.per.authOnly })
async pluginRequest(@Body(ALL) body: PluginRequestHandleReq) {
const userId = this.getUserId();
const pluginDefine = pluginRegistry.get(body.typeName);
const pluginCls = pluginDefine.target;
if (pluginCls == null) {
@@ -59,6 +61,9 @@ export class HandleController extends BaseController {
const plugin: PluginRequestHandler = new pluginCls();
//@ts-ignore
const instance = plugin as ITaskPlugin;
const accessGetter = new AccessGetter(userId, this.accessService.getById.bind(this.accessService));
//@ts-ignore
const taskCtx: TaskInstanceContext = {
pipeline: undefined,
@@ -67,7 +72,7 @@ export class HandleController extends BaseController {
http,
logger: logger,
inputChanged: true,
accessService: this.accessService,
accessService: accessGetter,
emailService: this.emailService,
pipelineContext: undefined,
userContext: undefined,
@@ -1,11 +1,9 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
import { CrudController } from '@certd/lib-server';
import { Constants, CrudController, SysSettingsService } from '@certd/lib-server';
import { PipelineService } from '../service/pipeline-service.js';
import { PipelineEntity } from '../entity/pipeline.js';
import { Constants } from '@certd/lib-server';
import { HistoryService } from '../service/history-service.js';
import { AuthService } from '../../sys/authority/service/auth-service.js';
import { SysSettingsService } from '@certd/lib-server';
/**
* 证书
@@ -18,6 +18,7 @@ export class AccessService extends BaseService<AccessEntity> {
@Inject()
encryptService: EncryptService;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -101,13 +102,13 @@ export class AccessService extends BaseService<AccessEntity> {
return await super.update(param);
}
async getById(id: any, userId?: number): Promise<any> {
async getById(id: any, userId: number): Promise<any> {
const entity = await this.info(id);
if (entity == null) {
throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`);
}
if (userId !== entity.userId) {
throw new PermissionException('您对该授权无访问权限');
throw new PermissionException('您对该Access授权无访问权限');
}
// const access = accessRegistry.get(entity.type);
const setting = this.decryptAccessEntity(entity);
@@ -13,6 +13,7 @@ export class HistoryLogService extends BaseService<HistoryLogEntity> {
@InjectEntityModel(HistoryLogEntity)
repository: Repository<HistoryLogEntity>;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -27,6 +27,7 @@ export class HistoryService extends BaseService<HistoryEntity> {
@Config('certd')
private certdConfig: any;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -54,6 +54,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
@Config('certd')
private certdConfig: any;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -12,6 +12,7 @@ export class StorageService extends BaseService<StorageEntity> {
@InjectEntityModel(StorageEntity)
repository: Repository<StorageEntity>;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -1,6 +1,5 @@
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { RoleService } from './role-service.js';
import { BaseService } from '@certd/lib-server';
/**
* 权限校验
@@ -28,7 +27,7 @@ export class AuthService {
}
}
async checkEntityUserId(ctx: any, service: BaseService<any>, id: any = 0, userKey = 'userId') {
async checkEntityUserId(ctx: any, service: any, id: any = 0, userKey = 'userId') {
const isAdmin = await this.isAdmin(ctx);
if (isAdmin) {
return true;
@@ -13,6 +13,7 @@ export class PermissionService extends BaseService<PermissionEntity> {
@InjectEntityModel(PermissionEntity)
repository: Repository<PermissionEntity>;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -13,6 +13,7 @@ export class RolePermissionService extends BaseService<RolePermissionEntity> {
@InjectEntityModel(RolePermissionEntity)
repository: Repository<RolePermissionEntity>;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -29,7 +29,8 @@ export class RoleService extends BaseService<RoleEntity> {
ttl: 1000 * 60 * 10,
});
getRepository() {
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -13,6 +13,7 @@ export class UserRoleService extends BaseService<UserRoleEntity> {
@InjectEntityModel(UserRoleEntity)
repository: Repository<UserRoleEntity>;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -34,6 +34,7 @@ export class UserService extends BaseService<UserEntity> {
@Inject()
sysSettingsService: SysSettingsService;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -13,6 +13,7 @@ export class CnameProviderService extends BaseService<CnameProviderEntity> {
@InjectEntityModel(CnameProviderEntity)
repository: Repository<CnameProviderEntity>;
//@ts-ignore
getRepository() {
return this.repository;
}
@@ -1,10 +1,6 @@
import { ALL, Body, Controller, Get, Inject, Post, Provide } from '@midwayjs/core';
import { Constants, SysSettingsService } from '@certd/lib-server';
import { BaseController } from '@certd/lib-server';
import { AppKey, http, PlusRequestService, verify } from '@certd/pipeline';
import { SysInstallInfo } from '@certd/lib-server';
import { logger } from '@certd/pipeline';
import { PlusService } from '@certd/lib-server';
import { BaseController, Constants, PlusService, SysInstallInfo, SysSettingsService } from '@certd/lib-server';
import { AppKey, logger, PlusRequestService, verify } from '@certd/pipeline';
/**
*/
@@ -63,8 +59,6 @@ export class SysPlusController extends BaseController {
const timestamps = 1728365013899;
const bindUrl = 'http://89.21.0.171:7001/';
const service = new PlusRequestService({
logger: logger,
http: http,
subjectId: subjectId,
plusServerBaseUrls: ['https://api.ai.handsfree.work'],
});
@@ -107,16 +107,23 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
try {
const ret = await this.client.request('AddDomainRecord', params, requestOption);
this.logger.info('添加域名解析成功:', value, value, ret.RecordId);
this.logger.info('添加域名解析成功:', JSON.stringify(options), ret.RecordId);
return ret.RecordId;
} catch (e: any) {
if (e.code === 'DomainRecordDuplicate') {
return;
}
this.logger.info('添加域名解析出错', e);
throw e;
this.resolveError(e, options);
}
}
resolveError(e: any, req: CreateRecordOptions) {
if (e.Message.indexOf('The specified domain name does not exist') > -1) {
throw new Error(`阿里云账号中不存在此域名:${req.domain}`);
}
throw e;
}
async removeRecord(options: RemoveRecordOptions<any>): Promise<any> {
const { fullRecord, value } = options.recordReq;
const record = options.recordRes;