diff --git a/packages/core/acme-client/src/auto.js b/packages/core/acme-client/src/auto.js index 92791f13..86c82192 100644 --- a/packages/core/acme-client/src/auto.js +++ b/packages/core/acme-client/src/auto.js @@ -149,7 +149,7 @@ export default async (client, userOpts) => { } challengeCompleted = true; log(`[auto] [${d}] 等待返回valid状态`); - await client.waitForValidStatus(challenge); + await client.waitForValidStatus(challenge,d); }); @@ -242,6 +242,8 @@ export default async (client, userOpts) => { await wait(60 * 1000); } else { await runPromisePa(localVerifyTasks, 1000); + log("本地校验完成,等待30s") + await wait(30 * 1000) } log("开始向提供商请求挑战验证"); diff --git a/packages/core/acme-client/src/client.js b/packages/core/acme-client/src/client.js index 3b42aa79..9f8b81e0 100644 --- a/packages/core/acme-client/src/client.js +++ b/packages/core/acme-client/src/client.js @@ -554,9 +554,9 @@ class AcmeClient { * ``` */ - async waitForValidStatus(item) { + async waitForValidStatus(item,d) { if (!item.url) { - throw new Error('Unable to verify status of item, URL not found'); + throw new Error(`[${d}] Unable to verify status of item, URL not found`); } const verifyFn = async (abort) => { @@ -568,23 +568,23 @@ class AcmeClient { const resp = await this.api.apiRequest(item.url, null, [200]); /* Verify status */ - log(`Item has status(挑战状态): ${resp.data.status}`); + log(`[${d}] Item has status(挑战状态): ${resp.data.status}`); if (invalidStates.includes(resp.data.status)) { abort(); throw new Error(util.formatResponseError(resp)); } else if (pendingStates.includes(resp.data.status)) { - throw new Error('Operation is pending or processing(当前仍然在等待状态)'); + throw new Error(`[${d}] Operation is pending or processing(当前仍然在等待状态)`); } else if (validStates.includes(resp.data.status)) { return resp.data; } - throw new Error(`Unexpected item status: ${resp.data.status}`); + throw new Error(`[${d}] Unexpected item status: ${resp.data.status}`); }; - log(`Waiting for valid status (等待valid状态): ${item.url}`, this.backoffOpts); + log(`[${d}] Waiting for valid status (等待valid状态): ${item.url}`, this.backoffOpts); return util.retry(verifyFn, this.backoffOpts); } diff --git a/packages/core/acme-client/src/verify.js b/packages/core/acme-client/src/verify.js index e6790bff..2334331a 100644 --- a/packages/core/acme-client/src/verify.js +++ b/packages/core/acme-client/src/verify.js @@ -147,12 +147,12 @@ async function verifyDnsChallenge(authz, challenge, keyAuthorization, prefix = ' let recordValues = await walkTxtRecord(recordName); //去重 recordValues = [...new Set(recordValues)]; - log(`DNS查询成功, 找到 ${recordValues.length} 条TXT记录`); + log(`DNS查询成功, 找到 ${recordValues.length} 条TXT记录:${recordValues}`); if (!recordValues.length || !recordValues.includes(keyAuthorization)) { throw new Error(`没有找到需要的DNS TXT记录: ${recordName},期望:${keyAuthorization},结果:${recordValues}`); } - log(`关键授权匹配成功(${challenge.type}/${recordName}),校验成功, ACME challenge verified`); + log(`关键授权匹配成功(${challenge.type}/${recordName}):${keyAuthorization},校验成功, ACME challenge verified`); return true; } diff --git a/packages/ui/certd-client/src/style/antdv4.less b/packages/ui/certd-client/src/style/antdv4.less index 6eb93abc..40c79d3b 100644 --- a/packages/ui/certd-client/src/style/antdv4.less +++ b/packages/ui/certd-client/src/style/antdv4.less @@ -60,3 +60,9 @@ footer{ background-color: hsl(var(--card)) !important; } + + +.ant-select-multiple .ant-select-selection-item-remove{ + display: flex; + align-items: center; +} \ No newline at end of file diff --git a/packages/ui/certd-server/src/plugins/plugin-huawei/dns-provider/huawei-dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-huawei/dns-provider/huawei-dns-provider.ts index ddee06b5..e5d6404a 100644 --- a/packages/ui/certd-server/src/plugins/plugin-huawei/dns-provider/huawei-dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-huawei/dns-provider/huawei-dns-provider.ts @@ -1,37 +1,38 @@ -import * as _ from 'lodash-es'; -import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from '@certd/plugin-cert'; -import { Autowire } from '@certd/pipeline'; +import * as _ from "lodash-es"; +import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert"; +import { Autowire } from "@certd/pipeline"; -import { HuaweiAccess } from '../access/index.js'; -import { ApiRequestOptions, HuaweiYunClient } from '@certd/lib-huawei'; +import { HuaweiAccess } from "../access/index.js"; +import { ApiRequestOptions, HuaweiYunClient } from "@certd/lib-huawei"; export type SearchRecordOptions = { zoneId: string; } & CreateRecordOptions; @IsDnsProvider({ - name: 'huawei', - title: '华为云', - desc: '华为云DNS解析提供商', - accessType: 'huawei', - icon: 'svg:icon-huawei', + name: "huawei", + title: "华为云", + desc: "华为云DNS解析提供商", + accessType: "huawei", + icon: "svg:icon-huawei" }) export class HuaweiDnsProvider extends AbstractDnsProvider { client!: HuaweiYunClient; @Autowire() access!: HuaweiAccess; - domainEndpoint = 'https://domains-external.myhuaweicloud.com'; - dnsEndpoint = 'https://dns.cn-south-1.myhuaweicloud.com'; + domainEndpoint = "https://domains-external.myhuaweicloud.com"; + dnsEndpoint = "https://dns.cn-south-1.myhuaweicloud.com"; + async onInstance() { const access: any = this.access; - this.client = new HuaweiYunClient(access,this.logger); + this.client = new HuaweiYunClient(access, this.logger); } async getDomainList() { const url = `${this.dnsEndpoint}/v2/zones`; const ret = await this.client.request({ url, - method: 'GET', + method: "GET" }); return ret.zones; } @@ -40,21 +41,21 @@ export class HuaweiDnsProvider extends AbstractDnsProvider { const zoneList = await this.getDomainList(); let zoneRecord = null; for (const item of zoneList) { - if (_.endsWith(dnsRecord + '.', item.name)) { + if (_.endsWith(dnsRecord + ".", item.name)) { zoneRecord = item; break; } } if (!zoneRecord) { - throw new Error('can not find Domain ,' + dnsRecord); + throw new Error("can not find Domain ," + dnsRecord); } return zoneRecord; } async searchRecord(options: SearchRecordOptions): Promise { const req: ApiRequestOptions = { - url: `${this.dnsEndpoint}/v2/zones/${options.zoneId}/recordsets?name=${options.fullRecord}.`, - method: 'GET', + url: `${this.dnsEndpoint}/v2/zones/${options.zoneId}/recordsets?search_mode=equal&name=${options.fullRecord}.&type=${options.type}`, + method: "GET" }; const ret = await this.client.request(req); return ret.recordsets; @@ -62,59 +63,120 @@ export class HuaweiDnsProvider extends AbstractDnsProvider { async createRecord(options: CreateRecordOptions): Promise { const { fullRecord, value, type } = options; - this.logger.info('添加域名解析:', fullRecord, value); + this.logger.info("添加域名解析:", fullRecord, value); + this.logger.info("查询是否有重复记录"); const zoneRecord = await this.matchDomain(fullRecord); const zoneId = zoneRecord.id; const records: any = await this.searchRecord({ zoneId, - ...options, + ...options }); + this.logger.info(`查询${options.type}数量:${records.length}`); + let found = null; + const hwRecordValue = `"${value}"`; if (records && records.length > 0) { - for (const record of records) { - await this.removeRecord({ - recordRes: record, - recordReq: options, - }); + found = records[0]; + this.logger.info(`记录:${found.id},${found.records}`); + if (found.records.includes(hwRecordValue)) { + // this.logger.info(`删除重复记录:${record.id}`) + // await this.removeRecord({ + // recordRes: record, + // recordReq: options, + // }); + this.logger.info(`无需重复添加:${found.records}`); + return found; } } - try { + if (found) { + //修改 const req: ApiRequestOptions = { - url: `${this.dnsEndpoint}/v2/zones/${zoneId}/recordsets`, - method: 'POST', + url: `${this.dnsEndpoint}/v2/zones/${zoneId}/recordsets/${found.id}`, + method: "PUT", data: { - name: fullRecord + '.', + name: fullRecord + ".", type, - records: [`"${value}"`], - }, + records: [hwRecordValue, ...found.records] + } }; const ret = await this.client.request(req); - this.logger.info('添加域名解析成功:', value, ret); + this.logger.info("添加域名解析成功:", value, ret); return ret; - } catch (e: any) { - if (e.code === 'DNS.0312') { - return; + } else { + //创建 + try { + const req: ApiRequestOptions = { + url: `${this.dnsEndpoint}/v2/zones/${zoneId}/recordsets`, + method: "POST", + data: { + name: fullRecord + ".", + type, + records: [hwRecordValue] + } + }; + const ret = await this.client.request(req); + this.logger.info("添加域名解析成功:", value, ret); + return ret; + } catch (e: any) { + if (e.code === "DNS.0312") { + return; + } + this.logger.info("添加域名解析出错", e); + throw e; } - this.logger.info('添加域名解析出错', e); - throw e; } } + async removeRecord(options: RemoveRecordOptions): Promise { const { fullRecord, value } = options.recordReq; const record = options.recordRes; if (!record) { - this.logger.info('解析记录recordId为空,不执行删除', fullRecord, value); + this.logger.info("解析记录recordId为空,不执行删除", fullRecord, value); return; } - const req: ApiRequestOptions = { - url: `${this.dnsEndpoint}/v2/zones/${record.zone_id}/recordsets/${record.id}`, - method: 'DELETE', - }; + const zoneId = record.zone_id; - const ret = await this.client.request(req); - this.logger.info('删除域名解析成功:', fullRecord, value, ret.RecordId); - return ret.RecordId; + //查询原来的记录 + const records: any = await this.searchRecord({ + zoneId, + ...options.recordReq + }); + const hwRecordValue = `"${value}"`; + + if (records && records.length > 0) { + //找到记录 + const found = records[0]; + if (found.records.includes(hwRecordValue)) { + if (found.records.length > 1) { + //修改 + + const req: ApiRequestOptions = { + url: `${this.dnsEndpoint}/v2/zones/${zoneId}/recordsets/${found.id}`, + method: "PUT", + data: { + name: fullRecord + ".", + type: found.type, + records: found.records.filter((item: string) => item !== hwRecordValue) + } + }; + const ret = await this.client.request(req); + this.logger.info("修改域名解析成功[put]:", value, ret); + } else { + //删除 + const req: ApiRequestOptions = { + url: `${this.dnsEndpoint}/v2/zones/${zoneId}/recordsets/${found.id}`, + method: "DELETE" + }; + const ret = await this.client.request(req); + this.logger.info("删除域名解析成功[delete]:", fullRecord, value, ret.RecordId); + } + }else{ + this.logger.info("没有找到records无需删除", fullRecord, value,found); + } + }else{ + this.logger.info("删除域名解析失败,没有找到解析记录", fullRecord, value); + } } }