mirror of https://github.com/certd/certd
refactor: dnspod支持
parent
49b96592a0
commit
71f6d5769a
|
@ -12,9 +12,12 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alicloud/pop-core": "^1.7.10",
|
"@alicloud/pop-core": "^1.7.10",
|
||||||
"@types/node": "^14.14.13",
|
"@types/node": "^14.14.13",
|
||||||
|
"axios": "^0.21.1",
|
||||||
"dayjs": "^1.9.7",
|
"dayjs": "^1.9.7",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"log4js": "^6.3.0",
|
"log4js": "^6.3.0",
|
||||||
|
"node-forge": "^0.10.0",
|
||||||
|
"qs": "^6.9.4",
|
||||||
"@certd/acme-client": "^0.0.1"
|
"@certd/acme-client": "^0.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,47 +1,58 @@
|
||||||
import log from './utils/util.log.js'
|
import log from './utils/util.log.js'
|
||||||
import acme from '@certd/acme-client'
|
import acme from '@certd/acme-client'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import path from 'path'
|
||||||
import sleep from './utils/util.sleep.js'
|
import sleep from './utils/util.sleep.js'
|
||||||
export class AcmeService {
|
export class AcmeService {
|
||||||
constructor (store) {
|
constructor (store) {
|
||||||
this.store = store
|
this.store = store
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAccountKey (email) {
|
async getAccountConfig (email) {
|
||||||
let key = this.store.get(this.buildAccountKeyPath(email))
|
let conf = this.store.get(this.buildAccountPath(email))
|
||||||
if (key == null) {
|
if (conf == null) {
|
||||||
key = await this.createNewKey({ email })
|
conf = {}
|
||||||
|
} else {
|
||||||
|
conf = JSON.parse(conf)
|
||||||
}
|
}
|
||||||
return key
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
buildAccountKeyPath (email) {
|
buildAccountPath (email) {
|
||||||
return email + '/acme/account.key'
|
return path.join(email, '/account.json')
|
||||||
}
|
}
|
||||||
|
|
||||||
setAccountKey (email, privateKey) {
|
saveAccountConfig (email, conf) {
|
||||||
this.store.set(this.buildAccountKeyPath(email), privateKey)
|
this.store.set(this.buildAccountPath(email), JSON.stringify(conf))
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAcmeClient (email) {
|
async getAcmeClient (email) {
|
||||||
const key = await this.getAccountKey(email)
|
const conf = await this.getAccountConfig(email)
|
||||||
|
if (conf.key == null) {
|
||||||
|
conf.key = await this.createNewKey()
|
||||||
|
this.saveAccountConfig(email, conf)
|
||||||
|
}
|
||||||
const client = new acme.Client({
|
const client = new acme.Client({
|
||||||
directoryUrl: acme.directory.letsencrypt.staging,
|
directoryUrl: acme.directory.letsencrypt.staging,
|
||||||
accountKey: key,
|
accountKey: conf.key,
|
||||||
backoffAttempts: 10,
|
accountUrl: conf.accountUrl,
|
||||||
|
backoffAttempts: 20,
|
||||||
backoffMin: 5000,
|
backoffMin: 5000,
|
||||||
backoffMax: 10000
|
backoffMax: 10000
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (conf.accountUrl == null) {
|
||||||
|
const accountPayload = { termsOfServiceAgreed: true, contact: [`mailto:${email}`] }
|
||||||
|
await client.createAccount(accountPayload)
|
||||||
|
conf.accountUrl = client.getAccountUrl()
|
||||||
|
this.saveAccountConfig(email, conf)
|
||||||
|
}
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
async createNewKey ({ email }) {
|
async createNewKey () {
|
||||||
const privateKey = await acme.forge.createPrivateKey()
|
const key = await acme.forge.createPrivateKey()
|
||||||
this.setAccountKey(email, privateKey)
|
return key.toString()
|
||||||
}
|
|
||||||
|
|
||||||
async loggerin () {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async challengeCreateFn (authz, challenge, keyAuthorization, dnsProvider) {
|
async challengeCreateFn (authz, challenge, keyAuthorization, dnsProvider) {
|
||||||
|
@ -67,7 +78,11 @@ export class AcmeService {
|
||||||
/* Replace this */
|
/* Replace this */
|
||||||
log.info(`Would create TXT record "${dnsRecord}" with value "${recordValue}"`)
|
log.info(`Would create TXT record "${dnsRecord}" with value "${recordValue}"`)
|
||||||
|
|
||||||
return await dnsProvider.createRecord(dnsRecord, 'TXT', recordValue)
|
return await dnsProvider.createRecord({
|
||||||
|
fullRecord: dnsRecord,
|
||||||
|
type: 'TXT',
|
||||||
|
value: recordValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,10 +92,12 @@ export class AcmeService {
|
||||||
* @param {object} authz Authorization object
|
* @param {object} authz Authorization object
|
||||||
* @param {object} challenge Selected challenge
|
* @param {object} challenge Selected challenge
|
||||||
* @param {string} keyAuthorization Authorization key
|
* @param {string} keyAuthorization Authorization key
|
||||||
|
* @param recordItem challengeCreateFn create record item
|
||||||
|
* @param dnsProvider dnsProvider
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async challengeRemoveFn (authz, challenge, keyAuthorization, dnsProvider) {
|
async challengeRemoveFn (authz, challenge, keyAuthorization, recordItem, dnsProvider) {
|
||||||
log.info('Triggered challengeRemoveFn()')
|
log.info('Triggered challengeRemoveFn()')
|
||||||
|
|
||||||
/* http-01 */
|
/* http-01 */
|
||||||
|
@ -100,12 +117,24 @@ export class AcmeService {
|
||||||
|
|
||||||
/* Replace this */
|
/* Replace this */
|
||||||
log.info(`Would remove TXT record "${dnsRecord}" with value "${recordValue}"`)
|
log.info(`Would remove TXT record "${dnsRecord}" with value "${recordValue}"`)
|
||||||
await dnsProvider.removeRecord(dnsRecord, 'TXT', keyAuthorization)
|
await dnsProvider.removeRecord({
|
||||||
|
fullRecord: dnsRecord,
|
||||||
|
type: 'TXT',
|
||||||
|
value: keyAuthorization,
|
||||||
|
record: recordItem
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async order ({ email, domains, dnsProvider, dnsProviderCreator, csrInfo }) {
|
async order ({ email, domains, dnsProvider, dnsProviderCreator, csrInfo }) {
|
||||||
const client = await this.getAcmeClient(email)
|
const client = await this.getAcmeClient(email)
|
||||||
|
|
||||||
|
let accountUrl
|
||||||
|
try {
|
||||||
|
accountUrl = client.getAccountUrl()
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
|
||||||
/* Create CSR */
|
/* Create CSR */
|
||||||
const { commonName, altNames } = this.buildCommonNameByDomains(domains)
|
const { commonName, altNames } = this.buildCommonNameByDomains(domains)
|
||||||
|
|
||||||
|
@ -120,7 +149,7 @@ export class AcmeService {
|
||||||
if (dnsProvider == null) {
|
if (dnsProvider == null) {
|
||||||
throw new Error('dnsProvider 不能为空')
|
throw new Error('dnsProvider 不能为空')
|
||||||
}
|
}
|
||||||
/* Certificate */
|
/* 自动申请证书 */
|
||||||
const crt = await client.auto({
|
const crt = await client.auto({
|
||||||
csr,
|
csr,
|
||||||
email: email,
|
email: email,
|
||||||
|
@ -129,11 +158,20 @@ export class AcmeService {
|
||||||
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
|
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
|
||||||
return await this.challengeCreateFn(authz, challenge, keyAuthorization, dnsProvider)
|
return await this.challengeCreateFn(authz, challenge, keyAuthorization, dnsProvider)
|
||||||
},
|
},
|
||||||
challengeRemoveFn: async (authz, challenge, keyAuthorization) => {
|
challengeRemoveFn: async (authz, challenge, keyAuthorization, recordItem) => {
|
||||||
return await this.challengeRemoveFn(authz, challenge, keyAuthorization, dnsProvider)
|
return await this.challengeRemoveFn(authz, challenge, keyAuthorization, recordItem, dnsProvider)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 保存账号url
|
||||||
|
if (!accountUrl) {
|
||||||
|
try {
|
||||||
|
accountUrl = client.getAccountUrl()
|
||||||
|
this.setAccountUrl(email, accountUrl)
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('保存accountUrl出错', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
/* Done */
|
/* Done */
|
||||||
log.debug(`CSR:\n${csr.toString()}`)
|
log.debug(`CSR:\n${csr.toString()}`)
|
||||||
log.debug(`Certificate:\n${crt.toString()}`)
|
log.debug(`Certificate:\n${crt.toString()}`)
|
||||||
|
|
|
@ -1,9 +1,29 @@
|
||||||
|
import _ from 'lodash'
|
||||||
export class DnsProvider {
|
export class DnsProvider {
|
||||||
createRecord (dnsRecord, type, recordValue) {
|
async createRecord ({ fullRecord, type, value }) {
|
||||||
|
throw new Error('请实现 createRecord 方法')
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRecord (dnsRecord, type, recordValue) {
|
async removeRecord ({ fullRecord, type, value, record }) {
|
||||||
|
throw new Error('请实现 removeRecord 方法')
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDomainList () {
|
||||||
|
throw new Error('请实现 getDomainList 方法')
|
||||||
|
}
|
||||||
|
|
||||||
|
async matchDomain (dnsRecord, domainPropName) {
|
||||||
|
const list = await this.getDomainList()
|
||||||
|
let domain = null
|
||||||
|
for (const item of list) {
|
||||||
|
if (_.endsWith(dnsRecord, item[domainPropName])) {
|
||||||
|
domain = item
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!domain) {
|
||||||
|
throw new Error('找不到域名,请检查域名是否正确:' + dnsRecord)
|
||||||
|
}
|
||||||
|
return domain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,17 +59,18 @@ export default class AliyunDnsProvider extends DnsProvider {
|
||||||
return ret.DomainRecords.Record
|
return ret.DomainRecords.Record
|
||||||
}
|
}
|
||||||
|
|
||||||
async createRecord (dnsRecord, type, recordValue) {
|
async createRecord ({ fullRecord, type, value }) {
|
||||||
log.info('添加域名解析:', dnsRecord, recordValue)
|
log.info('添加域名解析:', fullRecord, value)
|
||||||
const domain = await this.matchDomain(dnsRecord)
|
const domain = await this.matchDomain(fullRecord)
|
||||||
const rr = dnsRecord.replace('.' + domain, '')
|
const rr = fullRecord.replace('.' + domain, '')
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
RegionId: 'cn-hangzhou',
|
RegionId: 'cn-hangzhou',
|
||||||
DomainName: domain,
|
DomainName: domain,
|
||||||
RR: rr,
|
RR: rr,
|
||||||
Type: type,
|
Type: type,
|
||||||
Value: recordValue
|
Value: value,
|
||||||
|
Line: 'oversea' // 海外
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestOption = {
|
const requestOption = {
|
||||||
|
@ -78,7 +79,7 @@ export default class AliyunDnsProvider extends DnsProvider {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const ret = await this.client.request('AddDomainRecord', params, requestOption)
|
const ret = await this.client.request('AddDomainRecord', params, requestOption)
|
||||||
console.log('添加域名解析成功:', dnsRecord, recordValue, ret.RecordId)
|
console.log('添加域名解析成功:', value, value, ret.RecordId)
|
||||||
return ret.RecordId
|
return ret.RecordId
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// e.code === 'DomainRecordDuplicate'
|
// e.code === 'DomainRecordDuplicate'
|
||||||
|
@ -87,15 +88,10 @@ export default class AliyunDnsProvider extends DnsProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeRecord (dnsRecord, type, value) {
|
async removeRecord ({ fullRecord, type, value, record }) {
|
||||||
const domain = await this.matchDomain(dnsRecord)
|
|
||||||
const rr = dnsRecord.replace('.' + domain, '')
|
|
||||||
|
|
||||||
const record = await this.getRecords(domain, rr, value)
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
RegionId: 'cn-hangzhou',
|
RegionId: 'cn-hangzhou',
|
||||||
RecordId: record[0].RecordId
|
RecordId: record
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestOption = {
|
const requestOption = {
|
||||||
|
@ -103,7 +99,7 @@ export default class AliyunDnsProvider extends DnsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ret = await this.client.request('DeleteDomainRecord', params, requestOption)
|
const ret = await this.client.request('DeleteDomainRecord', params, requestOption)
|
||||||
log.info('删除域名解析成功:', dnsRecord, value, ret.RecordId)
|
log.info('删除域名解析成功:', fullRecord, value, ret.RecordId)
|
||||||
return ret.RecordId
|
return ret.RecordId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
import { DnsProvider } from '../dns-provider.js'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import log from '../../utils/util.log.js'
|
||||||
|
import { request } from '../../utils/util.request.js'
|
||||||
|
export default class DnspodDnsProvider extends DnsProvider {
|
||||||
|
constructor (dnsProviderConfig) {
|
||||||
|
super()
|
||||||
|
if (!dnsProviderConfig.id || !dnsProviderConfig.token) {
|
||||||
|
throw new Error('请正确配置dnspod的 id 和 token')
|
||||||
|
}
|
||||||
|
this.loginToken = dnsProviderConfig.id + ',' + dnsProviderConfig.token
|
||||||
|
}
|
||||||
|
|
||||||
|
async doRequest (options) {
|
||||||
|
const config = {
|
||||||
|
method: 'post',
|
||||||
|
formData: {
|
||||||
|
login_token: this.loginToken,
|
||||||
|
format: 'json',
|
||||||
|
lang: 'cn',
|
||||||
|
error_on_empty: 'no'
|
||||||
|
},
|
||||||
|
timeout: 5000
|
||||||
|
}
|
||||||
|
_.merge(config, options)
|
||||||
|
|
||||||
|
const ret = await request(config)
|
||||||
|
if (ret?.status?.code !== '1') {
|
||||||
|
throw new Error('请求失败:' + ret.status.message + ',api=' + config.url)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDomainList () {
|
||||||
|
const ret = await this.doRequest({
|
||||||
|
url: 'https://dnsapi.cn/Domain.List'
|
||||||
|
})
|
||||||
|
log.debug('dnspod 域名列表:', ret.domains)
|
||||||
|
return ret.domains
|
||||||
|
}
|
||||||
|
|
||||||
|
async createRecord ({ fullRecord, type, value }) {
|
||||||
|
log.info('添加域名解析:', fullRecord, value)
|
||||||
|
const domainItem = await this.matchDomain(fullRecord, 'name')
|
||||||
|
const domain = domainItem.name
|
||||||
|
const rr = fullRecord.replace('.' + domain, '')
|
||||||
|
|
||||||
|
const ret = await this.doRequest({
|
||||||
|
url: 'https://dnsapi.cn/Record.Create',
|
||||||
|
formData: {
|
||||||
|
domain,
|
||||||
|
sub_domain: rr,
|
||||||
|
record_type: type,
|
||||||
|
record_line: '默认',
|
||||||
|
value: value,
|
||||||
|
mx: 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log('添加域名解析成功:', fullRecord, value, JSON.stringify(ret.record))
|
||||||
|
return ret.record
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeRecord ({ fullRecord, type, value, record }) {
|
||||||
|
const domain = await this.matchDomain(fullRecord, 'name')
|
||||||
|
|
||||||
|
const ret = await this.doRequest({
|
||||||
|
url: 'https://dnsapi.cn/Record.Remove',
|
||||||
|
formData: {
|
||||||
|
domain,
|
||||||
|
record_id: record.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
log.info('删除域名解析成功:', fullRecord, value)
|
||||||
|
return ret.RecordId
|
||||||
|
}
|
||||||
|
}
|
|
@ -95,7 +95,7 @@ export class Certd {
|
||||||
|
|
||||||
async createDnsProvider (options) {
|
async createDnsProvider (options) {
|
||||||
const accessProviders = options.accessProviders
|
const accessProviders = options.accessProviders
|
||||||
const providerOptions = accessProviders[options.cert.challenge.dnsProvider]
|
const providerOptions = accessProviders[options.cert.dnsProvider]
|
||||||
return await DnsProviderFactory.createByType(providerOptions.providerType, providerOptions)
|
return await DnsProviderFactory.createByType(providerOptions.providerType, providerOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import axios from 'axios'
|
||||||
|
import log from './util.log.js'
|
||||||
|
import qs from 'qs'
|
||||||
|
/**
|
||||||
|
* @description 创建请求实例
|
||||||
|
*/
|
||||||
|
function createService () {
|
||||||
|
// 创建一个 axios 实例
|
||||||
|
const service = axios.create()
|
||||||
|
// 请求拦截
|
||||||
|
service.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
if (config.formData) {
|
||||||
|
config.data = qs.stringify(config.formData, {
|
||||||
|
arrayFormat: 'indices',
|
||||||
|
allowDots: true
|
||||||
|
}) // 序列化请求参数
|
||||||
|
delete config.formData
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
// 发送失败
|
||||||
|
log.error(error)
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 响应拦截
|
||||||
|
service.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
return response.data
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
// const status = _.get(error, 'response.status')
|
||||||
|
// switch (status) {
|
||||||
|
// case 400: error.message = '请求错误'; break
|
||||||
|
// case 401: error.message = '未授权,请登录'; break
|
||||||
|
// case 403: error.message = '拒绝访问'; break
|
||||||
|
// case 404: error.message = `请求地址出错: ${error.response.config.url}`; break
|
||||||
|
// case 408: error.message = '请求超时'; break
|
||||||
|
// case 500: error.message = '服务器内部错误'; break
|
||||||
|
// case 501: error.message = '服务未实现'; break
|
||||||
|
// case 502: error.message = '网关错误'; break
|
||||||
|
// case 503: error.message = '服务不可用'; break
|
||||||
|
// case 504: error.message = '网关超时'; break
|
||||||
|
// case 505: error.message = 'HTTP版本不受支持'; break
|
||||||
|
// default: break
|
||||||
|
// }
|
||||||
|
log.error('请求出错:', error.response.config.url, error)
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return service
|
||||||
|
}
|
||||||
|
|
||||||
|
export const request = createService()
|
|
@ -1,6 +1,7 @@
|
||||||
import pkg from 'chai'
|
import pkg from 'chai'
|
||||||
import options from '../options.js'
|
import options from '../options.js'
|
||||||
import AliyunDnsProvider from '../../src/dns-provider/impl/aliyun.js'
|
import AliyunDnsProvider from '../../src/dns-provider/impl/aliyun.js'
|
||||||
|
import { Certd } from '../../src/index.js'
|
||||||
const { expect } = pkg
|
const { expect } = pkg
|
||||||
describe('AliyunDnsProvider', function () {
|
describe('AliyunDnsProvider', function () {
|
||||||
it('#getDomainList', async function () {
|
it('#getDomainList', async function () {
|
||||||
|
@ -17,17 +18,26 @@ describe('AliyunDnsProvider', function () {
|
||||||
expect(recordList.length).gt(0)
|
expect(recordList.length).gt(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('#createRecord', async function () {
|
it('#createAndRemoveRecord', async function () {
|
||||||
const aliyunDnsProvider = new AliyunDnsProvider(options.accessProviders.aliyun)
|
const aliyunDnsProvider = new AliyunDnsProvider(options.accessProviders.aliyun)
|
||||||
const recordId = await aliyunDnsProvider.createRecord('___certd___.__test__.docmirror.cn', 'TXT', 'aaaa')
|
const record = await aliyunDnsProvider.createRecord({ fullRecord: '___certd___.__test__.docmirror.cn', type: 'TXT', value: 'aaaa' })
|
||||||
|
console.log('recordId', record)
|
||||||
|
expect(record != null).ok
|
||||||
|
|
||||||
|
const recordId = await aliyunDnsProvider.removeRecord({ fullRecord: '___certd___.__test__.docmirror.cn', type: 'TXT', value: 'aaaa', record })
|
||||||
console.log('recordId', recordId)
|
console.log('recordId', recordId)
|
||||||
expect(recordId != null).ok
|
expect(recordId != null).ok
|
||||||
})
|
})
|
||||||
|
|
||||||
it('#removeRecord', async function () {
|
it('#申请证书-aliyun', async function () {
|
||||||
const aliyunDnsProvider = new AliyunDnsProvider(options.accessProviders.aliyun)
|
this.timeout(300000)
|
||||||
const recordId = await aliyunDnsProvider.removeRecord('___certd___.__test__.docmirror.cn', 'TXT', 'aaaa')
|
options.args = { forceCert: true }
|
||||||
console.log('recordId', recordId)
|
const certd = new Certd()
|
||||||
expect(recordId != null).ok
|
const cert = await certd.certApply(options)
|
||||||
|
expect(cert).ok
|
||||||
|
expect(cert.crt).ok
|
||||||
|
expect(cert.key).ok
|
||||||
|
expect(cert.detail).ok
|
||||||
|
expect(cert.expires).ok
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import pkg from 'chai'
|
||||||
|
import options from '../options.js'
|
||||||
|
import DnspodDnsProvider from '../../src/dns-provider/impl/dnspod.js'
|
||||||
|
import { Certd } from '../../src/index.js'
|
||||||
|
const { expect } = pkg
|
||||||
|
describe('DnspodDnsProvider', function () {
|
||||||
|
it('#getDomainList', async function () {
|
||||||
|
const dnsProvider = new DnspodDnsProvider(options.accessProviders.dnspod)
|
||||||
|
const domainList = await dnsProvider.getDomainList()
|
||||||
|
console.log('domainList', domainList)
|
||||||
|
expect(domainList.length).gt(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('#createRecord&removeRecord', async function () {
|
||||||
|
const dnsProvider = new DnspodDnsProvider(options.accessProviders.dnspod)
|
||||||
|
const record = await dnsProvider.createRecord({ fullRecord: '___certd___.__test__.certd.xyz', type: 'TXT', value: 'aaaa' })
|
||||||
|
console.log('recordId', record.id)
|
||||||
|
expect(record.id != null).ok
|
||||||
|
|
||||||
|
await dnsProvider.removeRecord({ fullRecord: '___certd___.__test__.certd.xyz', type: 'TXT', value: 'aaaa', record })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('#申请证书', async function () {
|
||||||
|
this.timeout(300000)
|
||||||
|
options.cert.domains = ['*.certd.xyz', 'certd.xyz']
|
||||||
|
options.cert.dnsProvider = 'dnspod'
|
||||||
|
options.args = { forceCert: true }
|
||||||
|
const certd = new Certd()
|
||||||
|
const cert = await certd.certApply(options)
|
||||||
|
expect(cert).ok
|
||||||
|
expect(cert.crt).ok
|
||||||
|
expect(cert.key).ok
|
||||||
|
expect(cert.detail).ok
|
||||||
|
expect(cert.expires).ok
|
||||||
|
})
|
||||||
|
})
|
|
@ -14,20 +14,9 @@ describe('Certd', function () {
|
||||||
const certd = new Certd()
|
const certd = new Certd()
|
||||||
certd.writeCert('xiaojunnuo@qq.com', ['*.domain.cn'], { csr: 'csr', crt: 'aaa', key: 'bbb' })
|
certd.writeCert('xiaojunnuo@qq.com', ['*.domain.cn'], { csr: 'csr', crt: 'aaa', key: 'bbb' })
|
||||||
})
|
})
|
||||||
it('#申请证书-aliyun', async function () {
|
|
||||||
this.timeout(300000)
|
|
||||||
options.args = { forceCert: true }
|
|
||||||
const certd = new Certd()
|
|
||||||
const cert = await certd.certApply(options)
|
|
||||||
expect(cert).ok
|
|
||||||
expect(cert.crt).ok
|
|
||||||
expect(cert.key).ok
|
|
||||||
expect(cert.detail).ok
|
|
||||||
expect(cert.expires).ok
|
|
||||||
})
|
|
||||||
it('#readCurrentCert', async function () {
|
it('#readCurrentCert', async function () {
|
||||||
const certd = new Certd()
|
const certd = new Certd()
|
||||||
const cert = certd.readCurrentCert('xiaojunnuo@qq.com', ['*.docmirror.cn'])
|
const cert = certd.readCurrentCert('xiaojunnuo@qq.com', ['*.domain.cn'])
|
||||||
expect(cert).to.be.ok
|
expect(cert).to.be.ok
|
||||||
expect(cert.crt).ok
|
expect(cert.crt).ok
|
||||||
expect(cert.key).to.be.ok
|
expect(cert.key).to.be.ok
|
||||||
|
|
|
@ -19,10 +19,7 @@ const defaultOptions = {
|
||||||
cert: {
|
cert: {
|
||||||
domains: ['*.docmirror.club', 'docmirror.club'],
|
domains: ['*.docmirror.club', 'docmirror.club'],
|
||||||
email: 'xiaojunnuo@qq.com',
|
email: 'xiaojunnuo@qq.com',
|
||||||
challenge: {
|
dnsProvider: 'aliyun',
|
||||||
challengeType: 'dns',
|
|
||||||
dnsProvider: 'aliyun'
|
|
||||||
},
|
|
||||||
csrInfo: {
|
csrInfo: {
|
||||||
country: 'CN',
|
country: 'CN',
|
||||||
state: 'GuangDong',
|
state: 'GuangDong',
|
||||||
|
|
|
@ -65,17 +65,6 @@
|
||||||
resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
|
resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
|
||||||
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
|
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
|
||||||
|
|
||||||
acme-client@^4.1.2:
|
|
||||||
version "4.1.2"
|
|
||||||
resolved "https://registry.npmjs.org/acme-client/-/acme-client-4.1.2.tgz#6072e98cc2e5aaf0a3f769a823b6bc68d034e8da"
|
|
||||||
integrity sha512-3GlqDVWHgm0xpfnwOME/OpEBwEgO2vOplGEN8miWS7n7A28U9C7MtuTg6AuPYo8Lmqu4SADllnZrMLNasVNLEQ==
|
|
||||||
dependencies:
|
|
||||||
axios "0.21.0"
|
|
||||||
backo2 "^1.0.0"
|
|
||||||
bluebird "^3.5.0"
|
|
||||||
debug "^4.1.1"
|
|
||||||
node-forge "^0.10.0"
|
|
||||||
|
|
||||||
acorn-jsx@^5.3.1:
|
acorn-jsx@^5.3.1:
|
||||||
version "5.3.1"
|
version "5.3.1"
|
||||||
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
|
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
|
||||||
|
@ -175,18 +164,13 @@ astral-regex@^1.0.0:
|
||||||
resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
|
resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
|
||||||
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
|
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
|
||||||
|
|
||||||
axios@0.21.0:
|
axios@^0.21.1:
|
||||||
version "0.21.0"
|
version "0.21.1"
|
||||||
resolved "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz#26df088803a2350dff2c27f96fef99fe49442aca"
|
resolved "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||||
integrity sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==
|
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.10.0"
|
follow-redirects "^1.10.0"
|
||||||
|
|
||||||
backo2@^1.0.0:
|
|
||||||
version "1.0.2"
|
|
||||||
resolved "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
|
|
||||||
integrity sha1-MasayLEpNjRj41s+u2n038+6eUc=
|
|
||||||
|
|
||||||
balanced-match@^1.0.0:
|
balanced-match@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
|
@ -202,11 +186,6 @@ binary-extensions@^2.0.0:
|
||||||
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
||||||
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
||||||
|
|
||||||
bluebird@^3.5.0:
|
|
||||||
version "3.7.2"
|
|
||||||
resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
|
||||||
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
|
||||||
|
|
||||||
brace-expansion@^1.1.7:
|
brace-expansion@^1.1.7:
|
||||||
version "1.1.11"
|
version "1.1.11"
|
||||||
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||||
|
@ -750,9 +729,9 @@ flatted@^3.1.0:
|
||||||
integrity sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==
|
integrity sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==
|
||||||
|
|
||||||
follow-redirects@^1.10.0:
|
follow-redirects@^1.10.0:
|
||||||
version "1.13.0"
|
version "1.13.1"
|
||||||
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
|
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
|
||||||
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
|
integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
|
||||||
|
|
||||||
fs-extra@^8.1.0:
|
fs-extra@^8.1.0:
|
||||||
version "8.1.0"
|
version "8.1.0"
|
||||||
|
@ -1404,6 +1383,11 @@ punycode@^2.1.0:
|
||||||
resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||||
|
|
||||||
|
qs@^6.9.4:
|
||||||
|
version "6.9.4"
|
||||||
|
resolved "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687"
|
||||||
|
integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==
|
||||||
|
|
||||||
randombytes@^2.1.0:
|
randombytes@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 5a3f7446222fa123b645e51e954ccfd981f459bd
|
Subproject commit 334a73743d03ca401258dce7ed479a969383689e
|
Loading…
Reference in New Issue