diff --git a/doc/tencent/dnspod-token.png b/doc/tencent/dnspod-token.png new file mode 100644 index 00000000..c656bc82 Binary files /dev/null and b/doc/tencent/dnspod-token.png differ diff --git a/doc/tencent/tencent-access.png b/doc/tencent/tencent-access.png new file mode 100644 index 00000000..09fda0dc Binary files /dev/null and b/doc/tencent/tencent-access.png differ diff --git a/doc/tencent/tencent.md b/doc/tencent/tencent.md new file mode 100644 index 00000000..e8cc9265 --- /dev/null +++ b/doc/tencent/tencent.md @@ -0,0 +1,16 @@ +# 腾讯云 + + +## DNSPOD 授权设置 +目前腾讯云管理的域名的dns暂时只支持从DNSPOD进行设置 +打开 https://console.dnspod.cn/account/token/apikey +然后按如下方式获取DNSPOD的授权 +![](./dnspod-token.png) + + +## 腾讯云API密钥设置 + +腾讯云其他部署需要API密钥,需要在腾讯云控制台进行设置 +打开 https://console.cloud.tencent.com/cam/capi +然后按如下方式获取腾讯云的API密钥 +![](./tencent-access.png) \ No newline at end of file diff --git a/packages/core/acme-client/src/auto.js b/packages/core/acme-client/src/auto.js index c15a47c1..6e888b3b 100644 --- a/packages/core/acme-client/src/auto.js +++ b/packages/core/acme-client/src/auto.js @@ -4,6 +4,7 @@ const { readCsrDomains } = require('./crypto'); const { log } = require('./logger'); +const { wait } = require('./wait'); const defaultOpts = { csr: null, @@ -118,7 +119,18 @@ module.exports = async function(client, userOpts) { let recordItem = null; try { recordItem = await opts.challengeCreateFn(authz, challenge, keyAuthorization); - + log(`[auto] [${d}] challengeCreateFn success`); + log(`[auto] [${d}] add challengeRemoveFn()`); + clearTasks.push(async () => { + /* Trigger challengeRemoveFn(), suppress errors */ + log(`[auto] [${d}] Trigger challengeRemoveFn()`); + try { + await opts.challengeRemoveFn(authz, challenge, keyAuthorization, recordItem); + } + catch (e) { + log(`[auto] [${d}] challengeRemoveFn threw error: ${e.message}`); + } + }); // throw new Error('测试异常'); /* Challenge verification */ if (opts.skipChallengeVerification === true) { @@ -140,19 +152,6 @@ module.exports = async function(client, userOpts) { log(`[auto] [${d}] challengeCreateFn threw error: ${e.message}`); throw e; } - finally { - log(`[auto] [${d}] add challengeRemoveFn()`); - clearTasks.push(async () => { - /* Trigger challengeRemoveFn(), suppress errors */ - log(`[auto] [${d}] Trigger challengeRemoveFn()`); - try { - await opts.challengeRemoveFn(authz, challenge, keyAuthorization, recordItem); - } - catch (e) { - log(`[auto] [${d}] challengeRemoveFn threw error: ${e.message}`); - } - }); - } } catch (e) { /* Deactivate pending authz when unable to complete challenge */ @@ -186,14 +185,21 @@ module.exports = async function(client, userOpts) { return promise; } - // function runPromisePa(tasks) { - // return Promise.all(tasks.map((task) => task())); - // } + async function runPromisePa(tasks) { + const results = []; + // eslint-disable-next-line no-await-in-loop,no-restricted-syntax + for (const task of tasks) { + results.push(task()); + // eslint-disable-next-line no-await-in-loop + await wait(30000); + } + return Promise.all(results); + } try { log('开始challenge'); - await runAllPromise(challengePromises); + await runPromisePa(challengePromises); log('challenge结束'); diff --git a/packages/core/acme-client/src/wait.js b/packages/core/acme-client/src/wait.js new file mode 100644 index 00000000..307d796c --- /dev/null +++ b/packages/core/acme-client/src/wait.js @@ -0,0 +1,9 @@ +async function wait(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +module.exports = { + wait +}; 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 788e999c..bbbd5bd4 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts @@ -45,7 +45,7 @@ export class AcmeService { directoryUrl: isTest ? acme.directory.letsencrypt.staging : acme.directory.letsencrypt.production, accountKey: conf.key, accountUrl: conf.accountUrl, - backoffAttempts: 20, + backoffAttempts: 60, backoffMin: 5000, backoffMax: 10000, }); diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index 13eb27c0..9a309318 100644 --- a/packages/ui/certd-server/package.json +++ b/packages/ui/certd-server/package.json @@ -62,6 +62,7 @@ "ssh2": "^0.8.9", "svg-captcha": "^1.4.0", "tencentcloud-sdk-nodejs": "^4.0.44", + "tencentcloud-sdk-nodejs-dnspod": "^4.0.866", "typeorm": "^0.3.11" }, "devDependencies": { diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/dns-provider/tencent-dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/dns-provider/tencent-dns-provider.ts new file mode 100644 index 00000000..30908b06 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/dns-provider/tencent-dns-provider.ts @@ -0,0 +1,84 @@ +import {Autowire, HttpClient, ILogger} from "@certd/pipeline"; +import {AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions} from "@certd/plugin-cert"; +import {TencentAccess} from "../access"; +import tencentcloud from 'tencentcloud-sdk-nodejs/index'; + +const DnspodClient = tencentcloud.dnspod.v20210323.Client; +@IsDnsProvider({ + name: 'tencent', + title: '腾讯云', + desc: '腾讯云域名DNS解析提供者', + accessType: 'tencent', +}) +export class TencentDnsProvider extends AbstractDnsProvider { + @Autowire() + http!: HttpClient; + + @Autowire() + access!: TencentAccess; + @Autowire() + logger!: ILogger; + + client!: any; + + endpoint = 'dnspod.tencentcloudapi.com'; + + async onInstance() { + + const clientConfig = { + credential: this.access, + region: "", + profile: { + httpProfile: { + endpoint: this.endpoint, + }, + }, + }; + +// 实例化要请求产品的client对象,clientProfile是可选的 + this.client = new DnspodClient(clientConfig); + } + + async createRecord(options: CreateRecordOptions): Promise { + const { fullRecord, value, type,domain } = options; + this.logger.info('添加域名解析:', fullRecord, value); + const rr = fullRecord.replace('.' + domain, ''); + + const params = { + "Domain": domain, + "RecordType": type, + "RecordLine": "默认", + "Value": value, + "SubDomain": rr + }; + + const ret = await this.client.CreateRecord(params) + /* + { + "RecordId": 162, + "RequestId": "ab4f1426-ea15-42ea-8183-dc1b44151166" + } + */ + this.logger.info( + '添加域名解析成功:', + fullRecord, + value, + JSON.stringify(ret) + ); + return ret; + } + + async removeRecord(options: RemoveRecordOptions) { + const { fullRecord, value, domain,record } = options; + + const params = { + "Domain": domain, + "RecordId": record.RecordId + }; + const ret = await this.client.DeleteRecord(params) + this.logger.info('删除域名解析成功:', fullRecord, value); + return ret; + } + +} +new TencentDnsProvider();