mirror of
https://github.com/certd/certd.git
synced 2026-04-12 11:20:57 +08:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2942d39dfe | ||
|
|
df65b0509e | ||
|
|
fbde35483b | ||
|
|
7370f8b83b | ||
|
|
8b0ca1da2e | ||
|
|
466f2b1a02 | ||
|
|
f1d6cce88c | ||
|
|
ad7ababb4c | ||
|
|
72fa623674 | ||
|
|
e5d117c134 | ||
|
|
f8944a1331 | ||
|
|
e850855154 | ||
|
|
06eacee90c | ||
|
|
b1e100982e | ||
|
|
8f30158b00 | ||
|
|
576f7db978 | ||
|
|
137e043dfe | ||
|
|
15467fc233 | ||
|
|
eec0fcdcf1 | ||
|
|
a41dee015e |
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Greper
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
196
README.md
196
README.md
@@ -0,0 +1,196 @@
|
||||
# CertD
|
||||
|
||||
CertD 是一个帮助你全自动申请和部署SSL证书的工具。
|
||||
后缀D取自linux守护进程的命名风格,意为证书守护进程。
|
||||
|
||||
## 特性
|
||||
本项目不仅支持证书申请过程自动化,还可以自动化部署证书,让你的证书永不过期。
|
||||
|
||||
* 全自动申请证书
|
||||
* 全自动部署证书(目前支持服务器上传部署、阿里云、腾讯云等)
|
||||
* 可与CI/DI工具结合使用
|
||||
|
||||
## 免费证书申请说明
|
||||
* 本项目ssl证书提供商为letencrypt
|
||||
* 申请过程遵循acme协议
|
||||
* 需要验证域名所有权,一般有两种方式(目前本项目仅支持dns-01)
|
||||
* http-01: 在网站根目录下放置一份txt文件
|
||||
* dns-01: 需要给域名添加txt解析记录,泛域名只能用这种方式
|
||||
* 证书续期:
|
||||
* 实际上acme并没有续期概念。
|
||||
* 我们所说的续期,其实就是按照全套流程重新申请一份新证书。
|
||||
* 免费证书过期时间90天,以后可能还会缩短,所以自动化部署必不可少
|
||||
|
||||
|
||||
|
||||
## 快速开始
|
||||
本案例演示,如何配置自动申请证书,并部署到阿里云CDN,然后快要到期前自动更新证书并重新部署
|
||||
|
||||
1. 环境准备
|
||||
安装[nodejs](https://nodejs.org/zh-cn/)
|
||||
|
||||
2. 创建任务项目
|
||||
```
|
||||
mkdir certd-run # 项目名称可以任意命名
|
||||
cd certd-run -y
|
||||
npm install @certd/executor -s --production
|
||||
```
|
||||
|
||||
3. 创建index.js
|
||||
|
||||
参数配置分几个部分
|
||||
args: 运行时参数
|
||||
accessProviders: 授权提供者,提供dns验证与部署任务的授权
|
||||
cert: 证书申请的配置
|
||||
deploy: 证书部署流程
|
||||
|
||||
```js
|
||||
import { Executor } from '@certd/executor'
|
||||
const options = {
|
||||
args: { // 运行时参数
|
||||
forceDeploy: true,
|
||||
},
|
||||
accessProviders: { //授权提供者
|
||||
aliyun: { // 阿里云accessKey,用于dns验证和上传证书到阿里云,并部署到cdn
|
||||
providerType: 'aliyun',
|
||||
accessKeyId: 'Your accessKeyId',
|
||||
accessKeySecret: 'Your accessKeySecret'
|
||||
},
|
||||
},
|
||||
cert: { //免费证书申请配置
|
||||
domains: [ //可以在一张证书上绑定多个域名(前提是他们的验证方式要一样,目前仅支持dns验证)
|
||||
'*.yourdomain.com',
|
||||
'*.test.yourdomain.com',
|
||||
'yourdomain.com'
|
||||
],
|
||||
email: 'Your email',
|
||||
dnsProvider: 'aliyun', //上方accessProviders里面配置的
|
||||
csrInfo: { //证书csr信息
|
||||
country: 'CN',
|
||||
state: 'GuangDong',
|
||||
locality: 'ShengZhen',
|
||||
organization: 'Your company Org.',
|
||||
organizationUnit: 'IT Department',
|
||||
emailAddress: 'Your email'
|
||||
}
|
||||
},
|
||||
deploy: [ //部署流程配置,数组,可以配置多条流程
|
||||
{
|
||||
deployName: '流程1-部署到阿里云CDN',
|
||||
tasks: [ //流程任务,一个流程下可以包含多个部署任务,并且将按顺序执行
|
||||
{ //任务1
|
||||
taskName: '上传到阿里云', //任务名称
|
||||
type: 'uploadCertToAliyun', //任务插件名称
|
||||
props: { //任务插件参数
|
||||
accessProvider: 'aliyun'
|
||||
}
|
||||
},
|
||||
{ // 任务2
|
||||
taskName: '部署证书到CDN',
|
||||
type: 'deployCertToAliyunCDN', //任务插件名称
|
||||
props:{
|
||||
domainName: 'your cdn domain 全称', //cdn域名全称
|
||||
certName: 'certd自动部署',//证书名称前缀
|
||||
accessProvider: 'aliyun'
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
const executor = new Executor()
|
||||
await executor.run(options)
|
||||
```
|
||||
|
||||
4. 运行
|
||||
```
|
||||
node index.js
|
||||
```
|
||||
5. 执行效果
|
||||
生成的证书默认会存储在 `${home}/.certd/${email}/certs/${domain}/current`目录下
|
||||
```
|
||||
[2021-01-08T16:15:04.681] [INFO] certd - 任务完成
|
||||
[2021-01-08T16:15:04.681] [INFO] certd - ---------------------------任务结果总览--------------------------
|
||||
[2021-01-08T16:15:04.682] [INFO] certd - 【更新证书】--------------------------------------- [success]
|
||||
证书申请成功
|
||||
[2021-01-08T16:15:04.682] [INFO] certd - 【流程1-部署到阿里云CDN】---------------------------- [success] 执行成功
|
||||
[2021-01-08T16:15:04.682] [INFO] certd - └【上传到阿里云】-------------------------------- [success] 执行成功
|
||||
[2021-01-08T16:15:04.682] [INFO] certd - └【部署证书到CDN】------------------------------- [success] 执行成功
|
||||
```
|
||||
6. 证书续期
|
||||
实际上没有证书续期的概念,只有重新生成一份新的证书,然后重新部署证书
|
||||
所以每天定时运行即可,当证书过期日前20天时,会重新申请新的证书,然后执行部署任务。
|
||||
|
||||
7. 其他说明
|
||||
证书的部署任务执行后会记录执行结果,已经成功过的不会重复执行
|
||||
所以当你临时需要将证书部署到其他地方时,直接追加部署任务,然后再次运行即可
|
||||
|
||||
## CI/DI集成与自动续期重新部署
|
||||
集成前,将以上代码提交到内网git仓库,或者私有git仓库(由于包含敏感信息,不要提交到公开git仓库)
|
||||
|
||||
### jenkins任务
|
||||
1. 创建任务
|
||||
选择构建自由风格的任务
|
||||
|
||||
2. 配置git
|
||||
配置cert-run的git地址
|
||||
|
||||
3. 构建触发器
|
||||
配置 `H 3 * * *` ,每天凌晨3点-4点执行一次
|
||||
|
||||
4. 构建环境
|
||||
勾选 `Provide Node & npm bin/ folder to PATH`,提供nodejs运行环境
|
||||
如果没有此选项,需要jenkins安装`nodejs`插件
|
||||
|
||||
5. 构建
|
||||
执行shell
|
||||
```
|
||||
npm install --production #执行过一次之后,就可以注释掉,加快执行速度
|
||||
npm run post
|
||||
```
|
||||
6. 构建后操作
|
||||
邮件通知
|
||||
配置你的邮箱地址,可以在执行失败时收到邮件通知。
|
||||
|
||||
|
||||
## API
|
||||
先列个提纲,待完善
|
||||
|
||||
参数示例参考:https://gitee.com/certd/certd/blob/master/test/options.js
|
||||
|
||||
### 授权提供者
|
||||
用于dns验证接口调用
|
||||
#### aliyun
|
||||
|
||||
#### dnspod
|
||||
|
||||
### deploy插件
|
||||
部署任务插件
|
||||
#### 阿里云
|
||||
##### 上传到阿里云
|
||||
type = uploadCertToAliyun
|
||||
##### 部署到阿里云DNS
|
||||
type = deployCertToAliyunCDN
|
||||
|
||||
##### 部署到阿里云CLB
|
||||
type = deployCertToAliyunCLB
|
||||
|
||||
#### 腾讯云
|
||||
##### 上传到腾讯云
|
||||
type = uploadCertToTencent
|
||||
|
||||
##### 部署到腾讯云DNS
|
||||
type = deployCertToTencentDNS
|
||||
|
||||
##### 部署到腾讯云CLB
|
||||
type = deployCertToTencentCLB
|
||||
|
||||
##### 部署到腾讯云TKE-ingress
|
||||
type = deployCertToTencentTKEIngress
|
||||
|
||||
|
||||
### 更多部署插件
|
||||
等你来提需求
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "0.1.9"
|
||||
"version": "0.1.11"
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
"lerna": "^3.18.4"
|
||||
},
|
||||
"scripts": {
|
||||
"submodule": "git submodule update --init --recursive"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.20"
|
||||
"lodash-es": "^4.17.20"
|
||||
}
|
||||
}
|
||||
|
||||
2
packages/api/package-lock.json
generated
2
packages/api/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/api",
|
||||
"version": "0.1.7",
|
||||
"version": "0.1.11",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
{
|
||||
"name": "@certd/api",
|
||||
"version": "0.1.7",
|
||||
"description": "",
|
||||
"main": "./src/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"type": "module",
|
||||
"author": "Greper",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"dayjs": "^1.9.7",
|
||||
"lodash-es": "^4.17.20",
|
||||
"log4js": "^6.3.0",
|
||||
"qs": "^6.9.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"mocha": "^8.2.1"
|
||||
}
|
||||
"name": "@certd/api",
|
||||
"version": "0.1.11",
|
||||
"description": "",
|
||||
"main": "./src/index.js",
|
||||
"type": "module",
|
||||
"author": "Greper",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"dayjs": "^1.9.7",
|
||||
"lodash-es": "^4.17.20",
|
||||
"log4js": "^6.3.0",
|
||||
"qs": "^6.9.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"mocha": "^8.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
699
packages/certd/package-lock.json
generated
699
packages/certd/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/certd",
|
||||
"version": "0.1.7",
|
||||
"version": "0.1.11",
|
||||
"description": "",
|
||||
"main": "./src/index.js",
|
||||
"scripts": {
|
||||
@@ -11,8 +11,8 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^0.1.6",
|
||||
"@certd/api": "^0.1.7",
|
||||
"@certd/providers": "^0.1.7",
|
||||
"@certd/api": "^0.1.11",
|
||||
"@certd/providers": "^0.1.11",
|
||||
"dayjs": "^1.9.7",
|
||||
"lodash-es": "^4.17.20",
|
||||
"node-forge": "^0.10.0"
|
||||
|
||||
@@ -2,16 +2,30 @@ import { util, Store } from '@certd/api'
|
||||
import { AcmeService } from './acme.js'
|
||||
import { FileStore } from './store/file-store.js'
|
||||
import { CertStore } from './store/cert-store.js'
|
||||
import { DnsProviderFactory } from '@certd/providers'
|
||||
import dayjs from 'dayjs'
|
||||
import forge from 'node-forge'
|
||||
import DefaultProviders from '@certd/providers'
|
||||
import _ from 'lodash-es'
|
||||
const logger = util.logger
|
||||
|
||||
const AccessProviderClasses = {}
|
||||
function install (providerClass) {
|
||||
AccessProviderClasses[providerClass.name()] = providerClass
|
||||
}
|
||||
logger.info('use')
|
||||
_.forEach(DefaultProviders, item => {
|
||||
logger.info('use:', item.name())
|
||||
install(item)
|
||||
})
|
||||
export class Certd {
|
||||
static use (providerClass) {
|
||||
install(providerClass)
|
||||
}
|
||||
|
||||
constructor (options) {
|
||||
this.options = options
|
||||
this.email = options.cert.email
|
||||
this.domains = options.cert.domains
|
||||
this.domain = this.getMainDomain(options.cert.domains)
|
||||
|
||||
if (!(options.store instanceof Store)) {
|
||||
this.store = new FileStore(options.store || {})
|
||||
@@ -19,33 +33,11 @@ export class Certd {
|
||||
this.certStore = new CertStore({
|
||||
store: this.store,
|
||||
email: options.cert.email,
|
||||
domain: this.domain
|
||||
domains: this.domains
|
||||
})
|
||||
this.acme = new AcmeService(this.store)
|
||||
}
|
||||
|
||||
getMainDomain (domains) {
|
||||
if (domains == null) {
|
||||
return null
|
||||
}
|
||||
if (typeof domains === 'string') {
|
||||
return domains
|
||||
}
|
||||
if (domains.length > 0) {
|
||||
return domains[0]
|
||||
}
|
||||
}
|
||||
//
|
||||
// buildDomainFileName (domains) {
|
||||
// const domain = this.getMainDomain(domains)
|
||||
// return domain.replace(/\*/g, '_')
|
||||
// }
|
||||
//
|
||||
// buildCertDir (email, domains) {
|
||||
// const domainFileName = this.buildDomainFileName(domains)
|
||||
// return path.join(email, '/certs/', domainFileName)
|
||||
// }
|
||||
|
||||
async certApply () {
|
||||
let oldCert
|
||||
try {
|
||||
@@ -97,7 +89,7 @@ export class Certd {
|
||||
createDnsProvider (options) {
|
||||
const accessProviders = options.accessProviders
|
||||
const providerOptions = accessProviders[options.cert.dnsProvider]
|
||||
return DnsProviderFactory.createByType(providerOptions.providerType, providerOptions)
|
||||
return this.createProviderByType(providerOptions.providerType, providerOptions)
|
||||
}
|
||||
|
||||
async writeCert (cert) {
|
||||
@@ -114,7 +106,7 @@ export class Certd {
|
||||
return null
|
||||
}
|
||||
const { detail, expires } = this.getCrtDetail(cert.crt)
|
||||
const domain = this.getMainDomain(this.options.cert.domains)
|
||||
const domain = this.certStore.getMainDomain(this.options.cert.domains)
|
||||
return {
|
||||
...cert, detail, expires, domain, domains: this.domains, email: this.email
|
||||
}
|
||||
@@ -144,4 +136,13 @@ export class Certd {
|
||||
leftDays
|
||||
}
|
||||
}
|
||||
|
||||
createProviderByType (type, options) {
|
||||
try {
|
||||
const Provider = AccessProviderClasses[type]
|
||||
return new Provider(options)
|
||||
} catch (e) {
|
||||
throw new Error('暂不支持此dnsProvider,请先use该provider:' + type, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,48 @@
|
||||
import dayjs from 'dayjs'
|
||||
import crypto from 'crypto'
|
||||
function md5 (content) {
|
||||
return crypto.createHash('md5').update(content).digest('hex')
|
||||
}
|
||||
export class CertStore {
|
||||
constructor ({ store, email, domain }) {
|
||||
constructor ({ store, email, domains }) {
|
||||
this.store = store
|
||||
this.email = email
|
||||
this.domain = domain
|
||||
this.domains = domains
|
||||
this.domain = this.getMainDomain(this.domains)
|
||||
this.safetyDomain = this.getSafetyDomain(this.domain)
|
||||
|
||||
this.domainDir = this.safetyDomain + '-' + md5(this.getDomainStr(this.domains))
|
||||
this.certsRootPath = this.store.buildKey(this.email, 'certs')
|
||||
|
||||
this.currentRootPath = this.store.buildKey(this.certsRootPath, this.safetyDomain, 'current')
|
||||
this.currentRootPath = this.store.buildKey(this.certsRootPath, this.domainDir, 'current')
|
||||
}
|
||||
|
||||
// getAccountConfig () {
|
||||
// return this.store.get(this.accountConfigKey)
|
||||
// }
|
||||
//
|
||||
// setAccountConfig (email, account) {
|
||||
// return this.store.set(this.accountConfigKey, account)
|
||||
// }
|
||||
getMainDomain (domains) {
|
||||
if (domains == null) {
|
||||
return null
|
||||
}
|
||||
if (typeof domains === 'string') {
|
||||
return domains
|
||||
}
|
||||
if (domains.length > 0) {
|
||||
return domains[0]
|
||||
}
|
||||
}
|
||||
|
||||
getDomainStr (domains) {
|
||||
if (domains == null) {
|
||||
return null
|
||||
}
|
||||
if (typeof domains === 'string') {
|
||||
return domains
|
||||
}
|
||||
return domains.join(',')
|
||||
}
|
||||
|
||||
buildNewCertRootPath (dir) {
|
||||
if (dir == null) {
|
||||
dir = dayjs().format('YYYY.MM.DD.HHmmss')
|
||||
}
|
||||
return this.store.buildKey(this.certsRootPath, this.safetyDomain, dir)
|
||||
return this.store.buildKey(this.certsRootPath, this.domainDir, dir)
|
||||
}
|
||||
|
||||
formatCert (pem) {
|
||||
|
||||
@@ -68,7 +68,7 @@ describe('Certd', function () {
|
||||
const certd = new Certd(options)
|
||||
const currentRootPath = certd.certStore.currentRootPath
|
||||
console.log('rootDir', currentRootPath)
|
||||
expect(currentRootPath).match(/xiaojunnuo@qq.com\\certs\\_.docmirror.club\\current/)
|
||||
expect(currentRootPath).match(/xiaojunnuo@qq.com\\certs\\_.docmirror.club-\w+\\current/)
|
||||
})
|
||||
it('#writeAndReadCert', async function () {
|
||||
const options = createOptions()
|
||||
|
||||
1571
packages/executor/package-lock.json
generated
1571
packages/executor/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/executor",
|
||||
"version": "0.1.9",
|
||||
"version": "0.1.11",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
@@ -10,9 +10,9 @@
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@certd/api": "^0.1.7",
|
||||
"@certd/certd": "^0.1.7",
|
||||
"@certd/plugins": "^0.1.9",
|
||||
"@certd/api": "^0.1.11",
|
||||
"@certd/certd": "^0.1.11",
|
||||
"@certd/plugins": "^0.1.11",
|
||||
"dayjs": "^1.9.7",
|
||||
"lodash-es": "^4.17.20"
|
||||
},
|
||||
|
||||
@@ -11,7 +11,8 @@ function createDefaultOptions () {
|
||||
args: {
|
||||
forceCert: false,
|
||||
forceDeploy: true,
|
||||
forceRedeploy: false
|
||||
forceRedeploy: false,
|
||||
doNotThrowError: false // 部署流程执行有错误时,不抛异常,此时整个任务执行完毕后,可以返回结果,你可以在返回结果中处理
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,10 +22,25 @@ export class Executor {
|
||||
this.trace = new Trace()
|
||||
}
|
||||
|
||||
use (plugin) {
|
||||
useProviders (providers) {
|
||||
if (providers) {
|
||||
_.forEach(item => {
|
||||
Certd.use(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useProvider (provider) {
|
||||
Certd.use(provider)
|
||||
}
|
||||
|
||||
usePlugin (plugin) {
|
||||
if (plugin == null) {
|
||||
return
|
||||
}
|
||||
if (this.plugins == null) {
|
||||
this.plugins = {}
|
||||
}
|
||||
this.plugins[plugin.name] = plugin
|
||||
if (plugin.define) {
|
||||
const define = plugin.define()
|
||||
@@ -34,9 +50,9 @@ export class Executor {
|
||||
|
||||
usePlugins (plugins) {
|
||||
if (plugins) {
|
||||
for (const plugin of plugins) {
|
||||
this.use(plugin)
|
||||
}
|
||||
_.forEach(plugins, item => {
|
||||
this.usePlugin(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +70,7 @@ export class Executor {
|
||||
async doRun (options) {
|
||||
// 申请证书
|
||||
logger.info('任务开始')
|
||||
const certd = new Certd(options)
|
||||
const certd = new Certd(options, this.providers)
|
||||
const cert = await this.runCertd(certd)
|
||||
if (cert == null) {
|
||||
throw new Error('申请证书失败')
|
||||
@@ -64,7 +80,7 @@ export class Executor {
|
||||
logger.info('----------------------')
|
||||
if (!cert.isNew) {
|
||||
// 如果没有更新
|
||||
if (!options.args?.forceDeploy && !options.args?.forceRedeploy) {
|
||||
if (!options.args.forceDeploy && !options.args.forceRedeploy) {
|
||||
// 且不需要强制运行deploy
|
||||
logger.info('证书无更新,无需重新部署')
|
||||
logger.info('任务完成')
|
||||
@@ -81,24 +97,23 @@ export class Executor {
|
||||
context.certIsNew = !!cert.isNew
|
||||
|
||||
const trace = new Trace(context)
|
||||
const resultTrace = trace.getInstance({ type: 'result' })
|
||||
// 运行部署任务
|
||||
try {
|
||||
await this.runDeploys({ options, cert, context, trace })
|
||||
} finally {
|
||||
await certd.certStore.setCurrentFile('context.json', JSON.stringify(context))
|
||||
}
|
||||
|
||||
logger.info('任务完成')
|
||||
trace.print()
|
||||
const result = trace.get({ })
|
||||
const result = resultTrace.get({ })
|
||||
const returnData = {
|
||||
cert,
|
||||
context,
|
||||
result
|
||||
}
|
||||
if (result.status === 'error') {
|
||||
const err = new Error(result.remark)
|
||||
err.data = returnData
|
||||
if (result.status === 'error' && options.args.doNotThrowError === false) {
|
||||
throw new Error(result.remark)
|
||||
}
|
||||
return returnData
|
||||
}
|
||||
@@ -119,10 +134,12 @@ export class Executor {
|
||||
for (const deploy of options.deploy) {
|
||||
const deployName = deploy.deployName
|
||||
logger.info(`------------【${deployName}】-----------`)
|
||||
|
||||
const deployTrace = trace.getInstance({ type: 'deploy', deployName })
|
||||
if (deploy.disabled === true) {
|
||||
logger.info('此流程已被禁用,跳过')
|
||||
logger.info('')
|
||||
trace.set({ deployName, value: { current: 'skip', status: 'disabled', remark: '流程禁用' } })
|
||||
deployTrace.set({ value: { current: 'skip', status: 'disabled', remark: '流程禁用' } })
|
||||
continue
|
||||
}
|
||||
try {
|
||||
@@ -131,13 +148,14 @@ export class Executor {
|
||||
context[deployName] = {}
|
||||
}
|
||||
const taskContext = context[deployName]
|
||||
// 开始执行任务列表
|
||||
await this.runTask({ options, cert, task, context: taskContext, deploy, trace })
|
||||
}
|
||||
|
||||
trace.set({ deployName, value: { status: 'success', remark: '执行成功' } })
|
||||
deployTrace.set({ value: { status: 'success', remark: '执行成功' } })
|
||||
} catch (e) {
|
||||
trace.set({ deployName, value: { status: 'error', remark: '执行失败:' + e.message } })
|
||||
trace.set({ value: { status: 'error', remark: deployName + '执行失败:' + e.message } })
|
||||
deployTrace.set({ value: { status: 'error', remark: '执行失败:' + e.message } })
|
||||
trace.set({ type: 'result', value: { status: 'error', remark: deployName + '执行失败:' + e.message } })
|
||||
logger.error('流程执行失败', e)
|
||||
}
|
||||
|
||||
@@ -158,18 +176,20 @@ export class Executor {
|
||||
if (Plugin instanceof Function) {
|
||||
instance = new Plugin({ accessProviders: options.accessProviders })
|
||||
}
|
||||
const traceStatus = trace.get({ deployName: deploy.deployName, taskName: taskName })
|
||||
if (traceStatus?.status === 'success' && !options?.args?.forceRedeploy) {
|
||||
const taskTrace = trace.getInstance({ type: 'deploy', deployName, taskName })
|
||||
const traceStatus = taskTrace.get({})
|
||||
if (traceStatus && traceStatus.status === 'success' && !options.args.forceRedeploy) {
|
||||
logger.info(`----【${taskName}】已经执行完成,跳过此任务`)
|
||||
trace.set({ deployName, taskName, value: { current: 'skip', status: 'success', remark: '已执行成功过,本次跳过' } })
|
||||
taskTrace.set({ value: { current: 'skip', status: 'success', remark: '已执行成功过,本次跳过' } })
|
||||
return
|
||||
}
|
||||
logger.info(`----【${taskName}】开始执行`)
|
||||
try {
|
||||
// 执行任务
|
||||
await instance.execute({ cert, props: task.props, context })
|
||||
trace.set({ deployName, taskName, value: { current: 'success', status: 'success', remark: '执行成功', time: dayjs().format() } })
|
||||
taskTrace.set({ value: { current: 'success', status: 'success', remark: '执行成功', time: dayjs().format() } })
|
||||
} catch (e) {
|
||||
trace.set({ deployName, taskName, value: { current: 'error', status: 'error', remark: e.message, time: dayjs().format() } })
|
||||
taskTrace.set({ value: { current: 'error', status: 'error', remark: e.message, time: dayjs().format() } })
|
||||
throw e
|
||||
}
|
||||
logger.info(`----任务【${taskName}】执行完成`)
|
||||
|
||||
@@ -6,19 +6,30 @@ export class Trace {
|
||||
this.context = context
|
||||
}
|
||||
|
||||
set ({ deployName, taskName, prop, value }) {
|
||||
const key = this.buildTraceKey({ deployName, taskName, prop })
|
||||
getInstance ({ type, deployName, taskName }) {
|
||||
return {
|
||||
get: ({ prop }) => {
|
||||
return this.get({ type, deployName, taskName, prop })
|
||||
},
|
||||
set: ({ prop, value }) => {
|
||||
this.set({ type, deployName, taskName, prop, value })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set ({ type, deployName, taskName, prop, value }) {
|
||||
const key = this.buildTraceKey({ type, deployName, taskName, prop })
|
||||
const oldValue = _.get(this.context, key) || {}
|
||||
_.merge(oldValue, value)
|
||||
_.set(this.context, key, oldValue)
|
||||
}
|
||||
|
||||
get ({ deployName, taskName, prop }) {
|
||||
return _.get(this.context, this.buildTraceKey({ deployName, taskName, prop }))
|
||||
get ({ type, deployName, taskName, prop }) {
|
||||
return _.get(this.context, this.buildTraceKey({ type, deployName, taskName, prop }))
|
||||
}
|
||||
|
||||
buildTraceKey ({ deployName, taskName, prop }) {
|
||||
let key = '__trace__'
|
||||
buildTraceKey ({ type = 'default', deployName, taskName, prop }) {
|
||||
let key = '__trace__.' + type
|
||||
if (deployName) {
|
||||
key += '.'
|
||||
key += deployName.replace(/\./g, '_')
|
||||
@@ -46,7 +57,7 @@ export class Trace {
|
||||
} else {
|
||||
this.printTraceLine({ current: 'skip', remark: '还未到过期时间,跳过' }, '更新证书')
|
||||
}
|
||||
const trace = this.get({ })
|
||||
const trace = this.get({ type: 'deploy' })
|
||||
// logger.info('trace', trace)
|
||||
for (const deployName in trace) {
|
||||
if (trace[deployName] == null) {
|
||||
@@ -64,11 +75,12 @@ export class Trace {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = this.get({ type: 'result' })
|
||||
this.printTraceLine(result, 'result', '')
|
||||
const mainContext = {}
|
||||
_.merge(mainContext, context)
|
||||
delete mainContext.__trace__
|
||||
logger.info('context:', JSON.stringify(mainContext))
|
||||
logger.info('【context】', JSON.stringify(mainContext))
|
||||
}
|
||||
|
||||
printTraceLine (traceStatus, name, prefix = '') {
|
||||
|
||||
@@ -5,7 +5,7 @@ const { expect } = pkg
|
||||
|
||||
describe('AutoDeploy', function () {
|
||||
it('#run', async function () {
|
||||
this.timeout(20000)
|
||||
this.timeout(120000)
|
||||
const options = createOptions()
|
||||
const executor = new Executor()
|
||||
const ret = await executor.run(options)
|
||||
@@ -13,15 +13,18 @@ describe('AutoDeploy', function () {
|
||||
expect(ret.cert).ok
|
||||
})
|
||||
it('#forceCert', async function () {
|
||||
this.timeout(20000)
|
||||
this.timeout(120000)
|
||||
const executor = new Executor()
|
||||
const options = createOptions()
|
||||
const ret = await executor.run(options, { forceCert: true, forceDeploy: false })
|
||||
options.args.forceCert = true
|
||||
options.args.forceDeploy = true
|
||||
|
||||
const ret = await executor.run(options)
|
||||
expect(ret).ok
|
||||
expect(ret.cert).ok
|
||||
})
|
||||
it('#forceDeploy', async function () {
|
||||
this.timeout(20000)
|
||||
this.timeout(120000)
|
||||
const executor = new Executor()
|
||||
const options = createOptions()
|
||||
const ret = await executor.run(options, { forceCert: false, forceDeploy: true, forceRedeploy: true })
|
||||
|
||||
69
packages/plugins/package-lock.json
generated
69
packages/plugins/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/plugins",
|
||||
"version": "0.1.9",
|
||||
"version": "0.1.11",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -131,34 +131,6 @@
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@certd/acme-client": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@certd/acme-client/-/acme-client-0.1.6.tgz",
|
||||
"integrity": "sha512-XffzB/QHRj61gUHXor1B8R2TVt7HnklJpjAbwQi8vHjBGloU8I3occJxIunoh1AShhc4wsxnc+h/D5yRIUp17A==",
|
||||
"requires": {
|
||||
"axios": "0.21.1",
|
||||
"backo2": "^1.0.0",
|
||||
"bluebird": "^3.5.0",
|
||||
"debug": "^4.1.1",
|
||||
"log4js": "^6.3.0",
|
||||
"node-forge": "^0.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@certd/api": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@certd/api/-/api-0.1.7.tgz",
|
||||
@@ -171,30 +143,6 @@
|
||||
"qs": "^6.9.4"
|
||||
}
|
||||
},
|
||||
"@certd/certd": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@certd/certd/-/certd-0.1.7.tgz",
|
||||
"integrity": "sha512-h+YWlcXzdQk3dbhK7u0guwyzotKRtsIA9zTaHdViWlMDlT9/oO9QflFoo8x8wA1Xx/Vd90APT6EEBnizgOXxsQ==",
|
||||
"requires": {
|
||||
"@certd/acme-client": "^0.1.6",
|
||||
"@certd/api": "^0.1.7",
|
||||
"@certd/providers": "^0.1.7",
|
||||
"dayjs": "^1.9.7",
|
||||
"lodash-es": "^4.17.20",
|
||||
"node-forge": "^0.10.0"
|
||||
}
|
||||
},
|
||||
"@certd/providers": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@certd/providers/-/providers-0.1.7.tgz",
|
||||
"integrity": "sha512-ACiFNhRBVWB5Nyui3RTuAX0oTVkuWi0zopO1qMzbA/2iOrtFAoNs3I5DSLpZxS3d1erKcOq+Lx7Rm0rZxiL6wg==",
|
||||
"requires": {
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@certd/api": "^0.1.7",
|
||||
"lodash-es": "^4.17.20",
|
||||
"tencentcloud-sdk-nodejs": "^4.0.44"
|
||||
}
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npm.taobao.org/@eslint/eslintrc/download/@eslint/eslintrc-0.2.2.tgz?cache=0&sync_timestamp=1607145629875&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40eslint%2Feslintrc%2Fdownload%2F%40eslint%2Feslintrc-0.2.2.tgz",
|
||||
@@ -561,11 +509,6 @@
|
||||
"resolve": "^1.12.0"
|
||||
}
|
||||
},
|
||||
"backo2": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz",
|
||||
@@ -595,11 +538,6 @@
|
||||
"integrity": "sha1-MPpAyef+B9vIlWeM0ocCTeokHdk=",
|
||||
"dev": true
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz?cache=0&sync_timestamp=1601898189928&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbrace-expansion%2Fdownload%2Fbrace-expansion-1.1.11.tgz",
|
||||
@@ -2428,11 +2366,6 @@
|
||||
"resolved": "https://registry.npm.taobao.org/node-fetch/download/node-fetch-2.6.1.tgz?cache=0&sync_timestamp=1599309179354&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-fetch%2Fdownload%2Fnode-fetch-2.6.1.tgz",
|
||||
"integrity": "sha1-BFvTI2Mfdu0uK1VXM5RBa2OaAFI="
|
||||
},
|
||||
"node-forge": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
|
||||
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
|
||||
},
|
||||
"normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npm.taobao.org/normalize-package-data/download/normalize-package-data-2.5.0.tgz?cache=0&sync_timestamp=1602547447569&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnormalize-package-data%2Fdownload%2Fnormalize-package-data-2.5.0.tgz",
|
||||
|
||||
@@ -1,31 +1,27 @@
|
||||
{
|
||||
"name": "@certd/plugins",
|
||||
"version": "0.1.9",
|
||||
"description": "",
|
||||
"main": "./src/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@certd/api": "^0.1.7",
|
||||
"@certd/certd": "^0.1.7",
|
||||
"dayjs": "^1.9.7",
|
||||
"kubernetes-client": "^9.0.0",
|
||||
"lodash-es": "^4.17.20",
|
||||
"ssh2": "^0.8.9",
|
||||
"tencentcloud-sdk-nodejs": "^4.0.44"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"mocha": "^8.2.1"
|
||||
},
|
||||
"author": "Greper",
|
||||
"license": "MIT"
|
||||
"name": "@certd/plugins",
|
||||
"version": "0.1.11",
|
||||
"description": "",
|
||||
"main": "./src/index.js",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@certd/api": "^0.1.11",
|
||||
"dayjs": "^1.9.7",
|
||||
"kubernetes-client": "^9.0.0",
|
||||
"lodash-es": "^4.17.20",
|
||||
"ssh2": "^0.8.9",
|
||||
"tencentcloud-sdk-nodejs": "^4.0.44"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"mocha": "^8.2.1"
|
||||
},
|
||||
"author": "Greper",
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ import { DeployCertToTencentCLB } from './tencent/deploy-to-clb/index.js'
|
||||
|
||||
import { DeployCertToTencentTKEIngress } from './tencent/deploy-to-tke-ingress/index.js'
|
||||
|
||||
export default [
|
||||
export default {
|
||||
UploadCertToAliyun,
|
||||
DeployCertToAliyunCDN,
|
||||
UploadCertToTencent,
|
||||
DeployCertToTencentTKEIngress,
|
||||
DeployCertToTencentCDN,
|
||||
DeployCertToTencentCLB
|
||||
]
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class K8sClient {
|
||||
* @returns secretsList
|
||||
*/
|
||||
async getSecret (opts) {
|
||||
const namespace = opts?.namespace || 'default'
|
||||
const namespace = opts.namespace || 'default'
|
||||
const secrets = await this.client.api.v1.namespaces(namespace).secrets.get()
|
||||
return secrets
|
||||
}
|
||||
@@ -57,7 +57,7 @@ export class K8sClient {
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
async createSecret (opts) {
|
||||
const namespace = opts?.namespace || 'default'
|
||||
const namespace = opts.namespace || 'default'
|
||||
const created = await this.client.api.v1.namespaces(namespace).secrets.post({
|
||||
body: opts.body
|
||||
})
|
||||
@@ -66,8 +66,8 @@ export class K8sClient {
|
||||
}
|
||||
|
||||
async updateSecret (opts) {
|
||||
const namespace = opts?.namespace || 'default'
|
||||
const secretName = opts?.secretName
|
||||
const namespace = opts.namespace || 'default'
|
||||
const secretName = opts.secretName
|
||||
if (secretName == null) {
|
||||
throw new Error('secretName 不能为空')
|
||||
}
|
||||
@@ -77,8 +77,8 @@ export class K8sClient {
|
||||
}
|
||||
|
||||
async patchSecret (opts) {
|
||||
const namespace = opts?.namespace || 'default'
|
||||
const secretName = opts?.secretName
|
||||
const namespace = opts.namespace || 'default'
|
||||
const secretName = opts.secretName
|
||||
if (secretName == null) {
|
||||
throw new Error('secretName 不能为空')
|
||||
}
|
||||
@@ -88,8 +88,8 @@ export class K8sClient {
|
||||
}
|
||||
|
||||
async getIngress (opts) {
|
||||
const namespace = opts?.namespace || 'default'
|
||||
const ingressName = opts?.ingressName
|
||||
const namespace = opts.namespace || 'default'
|
||||
const ingressName = opts.ingressName
|
||||
if (!ingressName) {
|
||||
throw new Error('ingressName 不能为空')
|
||||
}
|
||||
@@ -97,8 +97,8 @@ export class K8sClient {
|
||||
}
|
||||
|
||||
async patchIngress (opts) {
|
||||
const namespace = opts?.namespace || 'default'
|
||||
const ingressName = opts?.ingressName
|
||||
const namespace = opts.namespace || 'default'
|
||||
const ingressName = opts.ingressName
|
||||
if (!ingressName) {
|
||||
throw new Error('ingressName 不能为空')
|
||||
}
|
||||
|
||||
2
packages/providers/package-lock.json
generated
2
packages/providers/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/providers",
|
||||
"version": "0.1.7",
|
||||
"version": "0.1.11",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/providers",
|
||||
"version": "0.1.7",
|
||||
"version": "0.1.11",
|
||||
"description": "",
|
||||
"main": "./src/index.js",
|
||||
"scripts": {
|
||||
@@ -11,7 +11,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@certd/api": "^0.1.7",
|
||||
"@certd/api": "^0.1.11",
|
||||
"lodash-es": "^4.17.20",
|
||||
"tencentcloud-sdk-nodejs": "^4.0.44"
|
||||
},
|
||||
|
||||
@@ -85,7 +85,9 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
|
||||
this.logger.info('添加域名解析成功:', value, value, ret.RecordId)
|
||||
return ret.RecordId
|
||||
} catch (e) {
|
||||
// e.code === 'DomainRecordDuplicate'
|
||||
if (e.code === 'DomainRecordDuplicate') {
|
||||
return
|
||||
}
|
||||
this.logger.info('添加域名解析出错', e)
|
||||
throw e
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import dnsProviders from './index.js'
|
||||
|
||||
export class DnsProviderFactory {
|
||||
static createByType (type, options) {
|
||||
try {
|
||||
const Provider = dnsProviders[type]
|
||||
return new Provider(options)
|
||||
} catch (e) {
|
||||
throw new Error('暂不支持此dnsProvider:' + type, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ export class DnspodDnsProvider extends AbstractDnsProvider {
|
||||
_.merge(config, options)
|
||||
|
||||
const ret = await request(config)
|
||||
if (ret?.status?.code !== '1') {
|
||||
if (!ret || !ret.status || ret.status.code !== '1') {
|
||||
throw new Error('请求失败:' + ret.status.message + ',api=' + config.url)
|
||||
}
|
||||
return ret
|
||||
@@ -1,6 +0,0 @@
|
||||
import { AliyunDnsProvider } from './impl/aliyun.js'
|
||||
import { DnspodDnsProvider } from './impl/dnspod.js'
|
||||
export default {
|
||||
[AliyunDnsProvider.name()]: AliyunDnsProvider,
|
||||
[DnspodDnsProvider.name()]: DnspodDnsProvider
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
import dnsProviders from './dns-provider/index.js'
|
||||
export { DnsProviderFactory } from './dns-provider/dns-provider-factory.js'
|
||||
import { AliyunDnsProvider } from './dns-provider/aliyun.js'
|
||||
import { DnspodDnsProvider } from './dns-provider/dnspod.js'
|
||||
|
||||
export default dnsProviders
|
||||
export default {
|
||||
AliyunDnsProvider,
|
||||
DnspodDnsProvider
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import pkg from 'chai'
|
||||
import AliyunDnsProvider from '../../src/dns-provider/impl/aliyun.js'
|
||||
import AliyunDnsProvider from '../../src/dns-provider/aliyun.js'
|
||||
import { createOptions } from '../../../../test/options.js'
|
||||
import { Certd } from '../../src/index.js'
|
||||
const { expect } = pkg
|
||||
describe('AliyunDnsProvider', function () {
|
||||
it('#getDomainList', async function () {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import pkg from 'chai'
|
||||
import DnspodDnsProvider from '../../src/dns-provider/impl/dnspod.js'
|
||||
import DnspodDnsProvider from '../../src/dns-provider/dnspod.js'
|
||||
import { Certd } from '../../src/index.js'
|
||||
import { createOptions } from '../../../../test/options.js'
|
||||
const { expect } = pkg
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import _ from 'lodash'
|
||||
import _ from 'lodash-es'
|
||||
import optionsPrivate from './options.private.js'
|
||||
const defaultOptions = {
|
||||
args: {
|
||||
forceCert: false, // 强制更新证书
|
||||
skipCert: false, // 是否跳过证书申请环节
|
||||
forceDeploy: false,
|
||||
test: true
|
||||
forceDeploy: true,
|
||||
test: false
|
||||
},
|
||||
accessProviders: {
|
||||
aliyun: {
|
||||
|
||||
Reference in New Issue
Block a user