mirror of
https://github.com/certd/certd.git
synced 2026-04-14 20:40:53 +08:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a421d5b14 | ||
|
|
7b9825eb40 | ||
|
|
5cde165f0b | ||
|
|
305824ff1a | ||
|
|
86ddb72227 | ||
|
|
cca33478e4 | ||
|
|
a8f41d3c48 | ||
|
|
a25a15ca6e | ||
|
|
a39dac4dbd | ||
|
|
eab0c3be60 | ||
|
|
b4ee3d0dfc | ||
|
|
2f03e18c59 | ||
|
|
232cd7215e | ||
|
|
86b1e9959b | ||
|
|
fd130f86fd | ||
|
|
2669f509e1 | ||
|
|
d3619ad60f | ||
|
|
c26417d769 | ||
|
|
2942d39dfe | ||
|
|
df65b0509e | ||
|
|
fbde35483b | ||
|
|
7370f8b83b | ||
|
|
8b0ca1da2e | ||
|
|
466f2b1a02 | ||
|
|
f1d6cce88c | ||
|
|
ad7ababb4c | ||
|
|
72fa623674 | ||
|
|
e5d117c134 | ||
|
|
f8944a1331 | ||
|
|
e850855154 | ||
|
|
06eacee90c | ||
|
|
b1e100982e | ||
|
|
8f30158b00 | ||
|
|
576f7db978 | ||
|
|
137e043dfe |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -5,7 +5,12 @@ out
|
|||||||
gen
|
gen
|
||||||
node_modules/
|
node_modules/
|
||||||
/test/*.private.*
|
/test/*.private.*
|
||||||
/other/node-acme-client/.idea/
|
|
||||||
/*.log
|
/*.log
|
||||||
/other/certd-run
|
|
||||||
/other/node-acme-client
|
/ui/*/.idea
|
||||||
|
|
||||||
|
/ui/*/node_modules
|
||||||
|
|
||||||
|
/packages/*/node_modules
|
||||||
|
/ui/certd-server/tmp/
|
||||||
|
|||||||
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
|
||||||
|
|
||||||
|
|
||||||
|
### 更多部署插件
|
||||||
|
等你来提需求
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*",
|
||||||
|
"ui/*"
|
||||||
],
|
],
|
||||||
"version": "0.1.10"
|
"version": "0.1.12"
|
||||||
}
|
}
|
||||||
|
|||||||
7073
package-lock.json
generated
Normal file
7073
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,11 +5,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"lerna": "^3.18.4"
|
"lerna": "^3.18.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {},
|
||||||
"submodule": "git submodule update --init --recursive"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "^4.17.20"
|
"lodash-es": "^4.17.20"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
"env": {
|
"env": {
|
||||||
"mocha": true
|
"mocha": true
|
||||||
},
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaVersion": 2020
|
||||||
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["*.test.js", "*.spec.js"],
|
"files": ["*.test.js", "*.spec.js"],
|
||||||
1599
packages/access-providers/package-lock.json
generated
Normal file
1599
packages/access-providers/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
packages/access-providers/package.json
Normal file
23
packages/access-providers/package.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "@certd/access-providers",
|
||||||
|
"version": "0.1.12",
|
||||||
|
"description": "",
|
||||||
|
"main": "./src/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"author": "Greper",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@certd/api": "^0.1.12",
|
||||||
|
"lodash-es": "^4.17.20"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
18
packages/access-providers/src/index.js
Normal file
18
packages/access-providers/src/index.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import _ from 'lodash-es'
|
||||||
|
import { AliyunAccessProvider } from './providers/aliyun.js'
|
||||||
|
import { DnspodAccessProvider } from './providers/dnspod.js'
|
||||||
|
import { TencentAccessProvider } from './providers/tencent.js'
|
||||||
|
import { accessProviderRegistry } from '@certd/api'
|
||||||
|
|
||||||
|
export const DefaultAccessProviders = {
|
||||||
|
AliyunAccessProvider,
|
||||||
|
DnspodAccessProvider,
|
||||||
|
TencentAccessProvider,
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
install () {
|
||||||
|
_.forEach(DefaultAccessProviders, item => {
|
||||||
|
accessProviderRegistry.install(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
34
packages/access-providers/src/providers/aliyun.js
Normal file
34
packages/access-providers/src/providers/aliyun.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import _ from 'lodash-es'
|
||||||
|
export class AliyunAccessProvider{
|
||||||
|
static define () {
|
||||||
|
return {
|
||||||
|
name: 'aliyun',
|
||||||
|
label: '阿里云',
|
||||||
|
desc: '',
|
||||||
|
input: {
|
||||||
|
accessKeyId: {
|
||||||
|
type: String,
|
||||||
|
component: {
|
||||||
|
placeholder: 'accessKeyId',
|
||||||
|
rules: [{ required: true, message: '必填项' }]
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
accessKeySecret: {
|
||||||
|
type: String,
|
||||||
|
component: {
|
||||||
|
placeholder: 'accessKeySecret',
|
||||||
|
rules: [{ required: true, message: '必填项' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
}
|
||||||
|
}
|
||||||
30
packages/access-providers/src/providers/dnspod.js
Normal file
30
packages/access-providers/src/providers/dnspod.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
export class DnspodAccessProvider {
|
||||||
|
static define () {
|
||||||
|
return {
|
||||||
|
name: 'dnspod',
|
||||||
|
label: 'dnspod',
|
||||||
|
desc: '腾讯云的域名解析接口已迁移到dnspod',
|
||||||
|
input: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
component: {
|
||||||
|
placeholder: 'dnspod接口账户id',
|
||||||
|
rules: [{ required: true, message: '该项必填' }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
token: {
|
||||||
|
type: String,
|
||||||
|
label: 'token',
|
||||||
|
component: {
|
||||||
|
placeholder: '开放接口token',
|
||||||
|
rules: [{ required: true, message: '该项必填' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
30
packages/access-providers/src/providers/tencent.js
Normal file
30
packages/access-providers/src/providers/tencent.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
export class TencentAccessProvider {
|
||||||
|
static define () {
|
||||||
|
return {
|
||||||
|
name: 'tencent',
|
||||||
|
label: '腾讯云',
|
||||||
|
input: {
|
||||||
|
secretId: {
|
||||||
|
type: String,
|
||||||
|
label:'secretId',
|
||||||
|
component: {
|
||||||
|
placeholder: 'secretId',
|
||||||
|
rules: [{ required: true, message: '该项必填' }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
secretKey: {
|
||||||
|
type: String,
|
||||||
|
label: 'secretKey',
|
||||||
|
component: {
|
||||||
|
placeholder: 'secretKey',
|
||||||
|
rules: [{ required: true, message: '该项必填' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,9 @@
|
|||||||
"env": {
|
"env": {
|
||||||
"mocha": true
|
"mocha": true
|
||||||
},
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2020
|
||||||
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["*.test.js", "*.spec.js"],
|
"files": ["*.test.js", "*.spec.js"],
|
||||||
|
|||||||
2
packages/api/package-lock.json
generated
2
packages/api/package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/api",
|
"name": "@certd/api",
|
||||||
"version": "0.1.7",
|
"version": "0.1.12",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,28 +1,26 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/api",
|
"name": "@certd/api",
|
||||||
"version": "0.1.7",
|
"version": "0.1.12",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "./src/index.js",
|
"main": "./src/index.js",
|
||||||
"scripts": {
|
"type": "module",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"author": "Greper",
|
||||||
},
|
"license": "MIT",
|
||||||
"type": "module",
|
"dependencies": {
|
||||||
"author": "Greper",
|
"axios": "^0.21.1",
|
||||||
"license": "MIT",
|
"dayjs": "^1.9.7",
|
||||||
"dependencies": {
|
"lodash-es": "^4.17.20",
|
||||||
"axios": "^0.21.1",
|
"log4js": "^6.3.0",
|
||||||
"dayjs": "^1.9.7",
|
"qs": "^6.9.4"
|
||||||
"lodash-es": "^4.17.20",
|
},
|
||||||
"log4js": "^6.3.0",
|
"devDependencies": {
|
||||||
"qs": "^6.9.4"
|
"chai": "^4.2.0",
|
||||||
},
|
"eslint": "^7.15.0",
|
||||||
"devDependencies": {
|
"eslint-config-standard": "^16.0.2",
|
||||||
"chai": "^4.2.0",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"eslint": "^7.15.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-config-standard": "^16.0.2",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"mocha": "^8.2.1"
|
||||||
"eslint-plugin-node": "^11.1.0",
|
},
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"gitHead": "2942d39dfe64d60ce7dc0e0b2c0887866ca67f18"
|
||||||
"mocha": "^8.2.1"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
2
packages/api/src/access-provider/index.js
Normal file
2
packages/api/src/access-provider/index.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import { Registry } from '../registry/registry.js'
|
||||||
|
export const accessProviderRegistry = new Registry()
|
||||||
42
packages/api/src/dns-provider/abstract-dns-provider.js
Normal file
42
packages/api/src/dns-provider/abstract-dns-provider.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import _ from 'lodash-es'
|
||||||
|
import logger from '../utils/util.log.js'
|
||||||
|
export class AbstractDnsProvider {
|
||||||
|
constructor ({ accessProviders }) {
|
||||||
|
this.logger = logger
|
||||||
|
this.accessProviders = accessProviders
|
||||||
|
}
|
||||||
|
|
||||||
|
async createRecord ({ fullRecord, type, value }) {
|
||||||
|
throw new Error('请实现 createRecord 方法')
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
getAccessProvider (accessProvider, accessProviders = this.accessProviders) {
|
||||||
|
if (typeof accessProvider === 'string' && accessProviders) {
|
||||||
|
accessProvider = accessProviders[accessProvider]
|
||||||
|
}
|
||||||
|
return accessProvider
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,34 +1,3 @@
|
|||||||
import _ from 'lodash-es'
|
import { Registry } from '../registry/registry.js'
|
||||||
import logger from '../utils/util.log.js'
|
export { AbstractDnsProvider } from './abstract-dns-provider.js'
|
||||||
export class AbstractDnsProvider {
|
export const dnsProviderRegistry = new Registry()
|
||||||
constructor () {
|
|
||||||
this.logger = logger
|
|
||||||
}
|
|
||||||
|
|
||||||
async createRecord ({ fullRecord, type, value }) {
|
|
||||||
throw new Error('请实现 createRecord 方法')
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export { AbstractDnsProvider } from './dns-provider/index.js'
|
export * from './dns-provider/index.js'
|
||||||
|
export * from './plugin/index.js'
|
||||||
|
export * from './access-provider/index.js'
|
||||||
export { Store } from './store/store.js'
|
export { Store } from './store/store.js'
|
||||||
export { util } from './utils/index.js'
|
export { util } from './utils/index.js'
|
||||||
export { AbstractPlugin } from './plugin/index.js'
|
|
||||||
|
|||||||
73
packages/api/src/plugin/abstract-plugin.js
Normal file
73
packages/api/src/plugin/abstract-plugin.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import logger from '../utils/util.log.js'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import Sleep from '../utils/util.sleep.js'
|
||||||
|
|
||||||
|
export class AbstractPlugin {
|
||||||
|
constructor ({ accessProviders }) {
|
||||||
|
this.logger = logger
|
||||||
|
this.accessProviders = accessProviders
|
||||||
|
}
|
||||||
|
|
||||||
|
appendTimeSuffix (name) {
|
||||||
|
if (name == null) {
|
||||||
|
name = 'certd'
|
||||||
|
}
|
||||||
|
return name + '-' + dayjs().format('YYYYMMDD-HHmmss')
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeFromContextFile (options = {}) {
|
||||||
|
const { contextPath } = options
|
||||||
|
const contextJson = fs.readFileSync(contextPath)
|
||||||
|
const context = JSON.parse(contextJson)
|
||||||
|
options.context = context
|
||||||
|
await this.doExecute(options)
|
||||||
|
fs.writeFileSync(JSON.stringify(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
async doExecute (options) {
|
||||||
|
try {
|
||||||
|
return await this.execute(options)
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('插件执行出错:', e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行
|
||||||
|
* @param options
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async execute (options) {
|
||||||
|
console.error('请实现此方法,context:', options.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
async doRollback (options) {
|
||||||
|
try {
|
||||||
|
return await this.rollback(options)
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('插件rollback出错:', e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回退,用于单元测试
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
async rollback (options) {
|
||||||
|
console.error('请实现此方法,rollback:', options.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
getAccessProvider (accessProvider, accessProviders = this.accessProviders) {
|
||||||
|
if (typeof accessProvider === 'string' && accessProviders) {
|
||||||
|
accessProvider = accessProviders[accessProvider]
|
||||||
|
}
|
||||||
|
return accessProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
async sleep (time) {
|
||||||
|
await Sleep(time)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,72 +1,3 @@
|
|||||||
import fs from 'fs'
|
import { Registry } from '../registry/registry.js'
|
||||||
import logger from '../utils/util.log.js'
|
export { AbstractPlugin } from './abstract-plugin.js'
|
||||||
import dayjs from 'dayjs'
|
export const pluginRegistry = new Registry()
|
||||||
import Sleep from '../utils/util.sleep.js'
|
|
||||||
export class AbstractPlugin {
|
|
||||||
constructor ({ accessProviders }) {
|
|
||||||
this.logger = logger
|
|
||||||
this.accessProviders = accessProviders
|
|
||||||
}
|
|
||||||
|
|
||||||
appendTimeSuffix (name) {
|
|
||||||
if (name == null) {
|
|
||||||
name = 'certd'
|
|
||||||
}
|
|
||||||
return name + '-' + dayjs().format('YYYYMMDD-HHmmss')
|
|
||||||
}
|
|
||||||
|
|
||||||
async executeFromContextFile (options = {}) {
|
|
||||||
const { contextPath } = options
|
|
||||||
const contextJson = fs.readFileSync(contextPath)
|
|
||||||
const context = JSON.parse(contextJson)
|
|
||||||
options.context = context
|
|
||||||
await this.doExecute(options)
|
|
||||||
fs.writeFileSync(JSON.stringify(context))
|
|
||||||
}
|
|
||||||
|
|
||||||
async doExecute (options) {
|
|
||||||
try {
|
|
||||||
return await this.execute(options)
|
|
||||||
} catch (e) {
|
|
||||||
logger.error('插件执行出错:', e)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行
|
|
||||||
* @param options
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
async execute (options) {
|
|
||||||
console.error('请实现此方法,context:', options.context)
|
|
||||||
}
|
|
||||||
|
|
||||||
async doRollback (options) {
|
|
||||||
try {
|
|
||||||
return await this.rollback(options)
|
|
||||||
} catch (e) {
|
|
||||||
logger.error('插件rollback出错:', e)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 回退,如有必要
|
|
||||||
* @param options
|
|
||||||
*/
|
|
||||||
async rollback (options) {
|
|
||||||
console.error('请实现此方法,rollback:', options.context)
|
|
||||||
}
|
|
||||||
|
|
||||||
getAccessProvider (accessProvider, accessProviders = this.accessProviders) {
|
|
||||||
if (typeof accessProvider === 'string' && accessProviders) {
|
|
||||||
accessProvider = accessProviders[accessProvider]
|
|
||||||
}
|
|
||||||
return accessProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
async sleep (time) {
|
|
||||||
await Sleep(time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
32
packages/api/src/registry/registry.js
Normal file
32
packages/api/src/registry/registry.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
export class Registry {
|
||||||
|
constructor () {
|
||||||
|
this.collection = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
install (target) {
|
||||||
|
if (target == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.collection == null) {
|
||||||
|
this.collection = {}
|
||||||
|
}
|
||||||
|
const defineName = (target.define && target.define().name) || target.name
|
||||||
|
this.register(defineName, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
register (key, value) {
|
||||||
|
if (!key || value == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.collection[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
get (name) {
|
||||||
|
if (name) {
|
||||||
|
return this.collection[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`${name} not found`)
|
||||||
|
}
|
||||||
|
}
|
||||||
121
packages/certd/package-lock.json
generated
121
packages/certd/package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/certd",
|
"name": "@certd/certd",
|
||||||
"version": "0.1.7",
|
"version": "0.1.12",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -30,6 +30,19 @@
|
|||||||
"js-tokens": "^4.0.0"
|
"js-tokens": "^4.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"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@eslint/eslintrc": {
|
"@eslint/eslintrc": {
|
||||||
"version": "0.2.2",
|
"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",
|
"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",
|
||||||
@@ -186,6 +199,19 @@
|
|||||||
"integrity": "sha1-SDFDxWeu7UeFdZwIZXhtx319LjE=",
|
"integrity": "sha1-SDFDxWeu7UeFdZwIZXhtx319LjE=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"axios": {
|
||||||
|
"version": "0.21.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||||
|
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backo2": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
|
||||||
|
},
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz",
|
||||||
@@ -198,6 +224,11 @@
|
|||||||
"integrity": "sha1-MPpAyef+B9vIlWeM0ocCTeokHdk=",
|
"integrity": "sha1-MPpAyef+B9vIlWeM0ocCTeokHdk=",
|
||||||
"dev": true
|
"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": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"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",
|
"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",
|
||||||
@@ -381,11 +412,24 @@
|
|||||||
"which": "^2.0.1"
|
"which": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"date-format": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w=="
|
||||||
|
},
|
||||||
"dayjs": {
|
"dayjs": {
|
||||||
"version": "1.10.2",
|
"version": "1.10.2",
|
||||||
"resolved": "https://registry.npm.taobao.org/dayjs/download/dayjs-1.10.2.tgz?cache=0&sync_timestamp=1609889274763&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdayjs%2Fdownload%2Fdayjs-1.10.2.tgz",
|
"resolved": "https://registry.npm.taobao.org/dayjs/download/dayjs-1.10.2.tgz?cache=0&sync_timestamp=1609889274763&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdayjs%2Fdownload%2Fdayjs-1.10.2.tgz",
|
||||||
"integrity": "sha1-jzpCTOuUSoGTUGgEsARadz0tBnI="
|
"integrity": "sha1-jzpCTOuUSoGTUGgEsARadz0tBnI="
|
||||||
},
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "4.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
||||||
|
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"decamelize": {
|
"decamelize": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz",
|
||||||
@@ -927,6 +971,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flatted": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA=="
|
||||||
|
},
|
||||||
|
"follow-redirects": {
|
||||||
|
"version": "1.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
|
||||||
|
"integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg=="
|
||||||
|
},
|
||||||
|
"fs-extra": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
||||||
|
"requires": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^4.0.0",
|
||||||
|
"universalify": "^0.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz",
|
||||||
@@ -1001,8 +1065,7 @@
|
|||||||
"graceful-fs": {
|
"graceful-fs": {
|
||||||
"version": "4.2.4",
|
"version": "4.2.4",
|
||||||
"resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz",
|
"resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz",
|
||||||
"integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs=",
|
"integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"growl": {
|
"growl": {
|
||||||
"version": "1.10.5",
|
"version": "1.10.5",
|
||||||
@@ -1229,6 +1292,14 @@
|
|||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jsonfile": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||||
|
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||||
|
"requires": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"levn": {
|
"levn": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npm.taobao.org/levn/download/levn-0.4.1.tgz",
|
"resolved": "https://registry.npm.taobao.org/levn/download/levn-0.4.1.tgz",
|
||||||
@@ -1332,6 +1403,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"log4js": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==",
|
||||||
|
"requires": {
|
||||||
|
"date-format": "^3.0.0",
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"flatted": "^2.0.1",
|
||||||
|
"rfdc": "^1.1.4",
|
||||||
|
"streamroller": "^2.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lru-cache": {
|
"lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-6.0.0.tgz?cache=0&sync_timestamp=1599054167787&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flru-cache%2Fdownload%2Flru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-6.0.0.tgz?cache=0&sync_timestamp=1599054167787&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flru-cache%2Fdownload%2Flru-cache-6.0.0.tgz",
|
||||||
@@ -1480,6 +1563,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
"nanoid": {
|
"nanoid": {
|
||||||
"version": "3.1.12",
|
"version": "3.1.12",
|
||||||
"resolved": "https://registry.npm.taobao.org/nanoid/download/nanoid-3.1.12.tgz?cache=0&sync_timestamp=1606833958647&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnanoid%2Fdownload%2Fnanoid-3.1.12.tgz",
|
"resolved": "https://registry.npm.taobao.org/nanoid/download/nanoid-3.1.12.tgz?cache=0&sync_timestamp=1606833958647&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnanoid%2Fdownload%2Fnanoid-3.1.12.tgz",
|
||||||
@@ -1781,6 +1869,11 @@
|
|||||||
"integrity": "sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=",
|
"integrity": "sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"rfdc": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug=="
|
||||||
|
},
|
||||||
"rimraf": {
|
"rimraf": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npm.taobao.org/rimraf/download/rimraf-3.0.2.tgz?cache=0&sync_timestamp=1599054104695&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frimraf%2Fdownload%2Frimraf-3.0.2.tgz",
|
"resolved": "https://registry.npm.taobao.org/rimraf/download/rimraf-3.0.2.tgz?cache=0&sync_timestamp=1599054104695&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frimraf%2Fdownload%2Frimraf-3.0.2.tgz",
|
||||||
@@ -1910,6 +2003,23 @@
|
|||||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"streamroller": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz",
|
||||||
|
"integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==",
|
||||||
|
"requires": {
|
||||||
|
"date-format": "^2.1.0",
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"fs-extra": "^8.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"date-format": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/string-width/download/string-width-4.2.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/string-width/download/string-width-4.2.0.tgz",
|
||||||
@@ -2051,6 +2161,11 @@
|
|||||||
"integrity": "sha1-CeJJ696FHTseSNJ8EFREZn8XuD0=",
|
"integrity": "sha1-CeJJ696FHTseSNJ8EFREZn8XuD0=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"universalify": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
||||||
|
},
|
||||||
"uri-js": {
|
"uri-js": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/uri-js/download/uri-js-4.4.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/uri-js/download/uri-js-4.4.0.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/certd",
|
"name": "@certd/certd",
|
||||||
"version": "0.1.7",
|
"version": "0.1.12",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "./src/index.js",
|
"main": "./src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -11,8 +11,8 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^0.1.6",
|
"@certd/acme-client": "^0.1.6",
|
||||||
"@certd/api": "^0.1.7",
|
"@certd/api": "^0.1.12",
|
||||||
"@certd/providers": "^0.1.7",
|
"@certd/dns-providers": "^0.1.12",
|
||||||
"dayjs": "^1.9.7",
|
"dayjs": "^1.9.7",
|
||||||
"lodash-es": "^4.17.20",
|
"lodash-es": "^4.17.20",
|
||||||
"node-forge": "^0.10.0"
|
"node-forge": "^0.10.0"
|
||||||
@@ -25,5 +25,6 @@
|
|||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"mocha": "^8.2.1"
|
"mocha": "^8.2.1"
|
||||||
}
|
},
|
||||||
|
"gitHead": "2942d39dfe64d60ce7dc0e0b2c0887866ca67f18"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import { util, Store } from '@certd/api'
|
import { util, Store, dnsProviderRegistry } from '@certd/api'
|
||||||
import { AcmeService } from './acme.js'
|
import { AcmeService } from './acme.js'
|
||||||
import { FileStore } from './store/file-store.js'
|
import { FileStore } from './store/file-store.js'
|
||||||
import { CertStore } from './store/cert-store.js'
|
import { CertStore } from './store/cert-store.js'
|
||||||
import { DnsProviderFactory } from '@certd/providers'
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import forge from 'node-forge'
|
import forge from 'node-forge'
|
||||||
const logger = util.logger
|
const logger = util.logger
|
||||||
|
|
||||||
export class Certd {
|
export class Certd {
|
||||||
constructor (options) {
|
constructor (options) {
|
||||||
this.options = options
|
this.options = options
|
||||||
this.email = options.cert.email
|
this.email = options.cert.email
|
||||||
this.domains = options.cert.domains
|
this.domains = options.cert.domains
|
||||||
this.domain = this.getMainDomain(options.cert.domains)
|
|
||||||
|
|
||||||
if (!(options.store instanceof Store)) {
|
if (!(options.store instanceof Store)) {
|
||||||
this.store = new FileStore(options.store || {})
|
this.store = new FileStore(options.store || {})
|
||||||
@@ -19,33 +18,11 @@ export class Certd {
|
|||||||
this.certStore = new CertStore({
|
this.certStore = new CertStore({
|
||||||
store: this.store,
|
store: this.store,
|
||||||
email: options.cert.email,
|
email: options.cert.email,
|
||||||
domain: this.domain
|
domains: this.domains
|
||||||
})
|
})
|
||||||
this.acme = new AcmeService(this.store)
|
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 () {
|
async certApply () {
|
||||||
let oldCert
|
let oldCert
|
||||||
try {
|
try {
|
||||||
@@ -95,16 +72,14 @@ export class Certd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createDnsProvider (options) {
|
createDnsProvider (options) {
|
||||||
const accessProviders = options.accessProviders
|
return this.createProviderByType(options.cert.dnsProvider, options.accessProviders)
|
||||||
const providerOptions = accessProviders[options.cert.dnsProvider]
|
|
||||||
return DnsProviderFactory.createByType(providerOptions.providerType, providerOptions)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async writeCert (cert) {
|
async writeCert (cert) {
|
||||||
const newPath = await this.certStore.writeCert(cert)
|
const newPath = await this.certStore.writeCert(cert)
|
||||||
return {
|
return {
|
||||||
realPath: this.certStore.store.getActualKey(newPath),
|
realPath: this.certStore.store.getActualKey(newPath),
|
||||||
currentPath: this.certStore.store.getActualKey(this.certStore.currentRootPath)
|
currentPath: this.certStore.store.getActualKey(this.certStore.currentMarkPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +89,7 @@ export class Certd {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const { detail, expires } = this.getCrtDetail(cert.crt)
|
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 {
|
return {
|
||||||
...cert, detail, expires, domain, domains: this.domains, email: this.email
|
...cert, detail, expires, domain, domains: this.domains, email: this.email
|
||||||
}
|
}
|
||||||
@@ -144,4 +119,14 @@ export class Certd {
|
|||||||
leftDays
|
leftDays
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createProviderByType (props, accessProviders) {
|
||||||
|
const { type } = props
|
||||||
|
try {
|
||||||
|
const Provider = dnsProviderRegistry.get(type)
|
||||||
|
return new Provider({ accessProviders, props })
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('暂不支持此dnsProvider,请先注册该provider:' + type, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,50 @@
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import crypto from 'crypto'
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
function md5 (content) {
|
||||||
|
return crypto.createHash('md5').update(content).digest('hex')
|
||||||
|
}
|
||||||
export class CertStore {
|
export class CertStore {
|
||||||
constructor ({ store, email, domain }) {
|
constructor ({ store, email, domains }) {
|
||||||
this.store = store
|
this.store = store
|
||||||
this.email = email
|
this.email = email
|
||||||
this.domain = domain
|
this.domains = domains
|
||||||
|
this.domain = this.getMainDomain(this.domains)
|
||||||
this.safetyDomain = this.getSafetyDomain(this.domain)
|
this.safetyDomain = this.getSafetyDomain(this.domain)
|
||||||
|
// this.domainDir = this.safetyDomain + '-' + md5(this.getDomainStr(this.domains))
|
||||||
|
this.domainDir = this.safetyDomain
|
||||||
this.certsRootPath = this.store.buildKey(this.email, 'certs')
|
this.certsRootPath = this.store.buildKey(this.email, 'certs')
|
||||||
|
|
||||||
this.currentRootPath = this.store.buildKey(this.certsRootPath, this.safetyDomain, 'current')
|
this.currentMarkPath = this.store.buildKey(this.certsRootPath, this.domainDir, 'current.json')
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAccountConfig () {
|
getMainDomain (domains) {
|
||||||
// return this.store.get(this.accountConfigKey)
|
if (domains == null) {
|
||||||
// }
|
return null
|
||||||
//
|
}
|
||||||
// setAccountConfig (email, account) {
|
if (typeof domains === 'string') {
|
||||||
// return this.store.set(this.accountConfigKey, account)
|
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) {
|
buildNewCertRootPath (dir) {
|
||||||
if (dir == null) {
|
if (dir == null) {
|
||||||
dir = dayjs().format('YYYY.MM.DD.HHmmss')
|
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) {
|
formatCert (pem) {
|
||||||
@@ -43,15 +64,19 @@ export class CertStore {
|
|||||||
await this.store.set(priKey, this.formatCert(cert.key.toString()))
|
await this.store.set(priKey, this.formatCert(cert.key.toString()))
|
||||||
await this.store.set(csrKey, cert.csr.toString())
|
await this.store.set(csrKey, cert.csr.toString())
|
||||||
|
|
||||||
await this.store.link(newDir, this.currentRootPath)
|
await this.store.set(this.currentMarkPath, JSON.stringify({ latest: newDir }))
|
||||||
|
|
||||||
return newDir
|
return newDir
|
||||||
}
|
}
|
||||||
|
|
||||||
async readCert (dir) {
|
async readCert (dir) {
|
||||||
if (dir == null) {
|
if (dir == null) {
|
||||||
dir = this.currentRootPath
|
dir = await this.getCurrentDir()
|
||||||
}
|
}
|
||||||
|
if (dir == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const crtKey = this.buildKey(dir, this.safetyDomain + '.crt')
|
const crtKey = this.buildKey(dir, this.safetyDomain + '.crt')
|
||||||
const priKey = this.buildKey(dir, this.safetyDomain + '.key')
|
const priKey = this.buildKey(dir, this.safetyDomain + '.key')
|
||||||
const csrKey = this.buildKey(dir, this.safetyDomain + '.csr')
|
const csrKey = this.buildKey(dir, this.safetyDomain + '.csr')
|
||||||
@@ -80,13 +105,23 @@ export class CertStore {
|
|||||||
return domain.replace(/\*/g, '_')
|
return domain.replace(/\*/g, '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentFile (file) {
|
async getCurrentDir () {
|
||||||
const key = this.buildKey(this.currentRootPath, file)
|
const current = await this.store.get(this.currentMarkPath)
|
||||||
|
if (current == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return JSON.parse(current).latest
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCurrentFile (file) {
|
||||||
|
const currentDir = await this.getCurrentDir()
|
||||||
|
const key = this.buildKey(currentDir, file)
|
||||||
return this.store.get(key)
|
return this.store.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentFile (file, value) {
|
async setCurrentFile (file, value) {
|
||||||
const key = this.buildKey(this.currentRootPath, file)
|
const currentDir = await this.getCurrentDir()
|
||||||
|
const key = this.buildKey(currentDir, file)
|
||||||
return this.store.set(key, value)
|
return this.store.set(key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ describe('Certd', function () {
|
|||||||
options.cert.email = 'xiaojunnuo@qq.com'
|
options.cert.email = 'xiaojunnuo@qq.com'
|
||||||
options.cert.domains = ['*.docmirror.club']
|
options.cert.domains = ['*.docmirror.club']
|
||||||
const certd = new Certd(options)
|
const certd = new Certd(options)
|
||||||
const currentRootPath = certd.certStore.currentRootPath
|
const currentRootPath = certd.certStore.currentMarkPath
|
||||||
console.log('rootDir', 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.json/)
|
||||||
})
|
})
|
||||||
it('#writeAndReadCert', async function () {
|
it('#writeAndReadCert', async function () {
|
||||||
const options = createOptions()
|
const options = createOptions()
|
||||||
@@ -83,6 +83,6 @@ describe('Certd', function () {
|
|||||||
expect(cert.key).to.be.ok
|
expect(cert.key).to.be.ok
|
||||||
expect(cert.detail).to.be.ok
|
expect(cert.detail).to.be.ok
|
||||||
expect(cert.expires).to.be.ok
|
expect(cert.expires).to.be.ok
|
||||||
console.log('expires:', cert.expires)
|
console.log('cert:', JSON.stringify(cert))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
18
packages/dns-providers/.eslintrc
Normal file
18
packages/dns-providers/.eslintrc
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": "standard",
|
||||||
|
"env": {
|
||||||
|
"mocha": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaVersion": 2020
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.test.js", "*.spec.js"],
|
||||||
|
"rules": {
|
||||||
|
"no-unused-expressions": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/providers",
|
"name": "@certd/dns-providers",
|
||||||
"version": "0.1.7",
|
"version": "0.1.12",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/providers",
|
"name": "@certd/dns-providers",
|
||||||
"version": "0.1.7",
|
"version": "0.1.12",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "./src/index.js",
|
"main": "./src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alicloud/pop-core": "^1.7.10",
|
"@alicloud/pop-core": "^1.7.10",
|
||||||
"@certd/api": "^0.1.7",
|
"@certd/api": "^0.1.12",
|
||||||
"lodash-es": "^4.17.20",
|
"lodash-es": "^4.17.20",
|
||||||
"tencentcloud-sdk-nodejs": "^4.0.44"
|
"tencentcloud-sdk-nodejs": "^4.0.44"
|
||||||
},
|
},
|
||||||
@@ -23,5 +23,6 @@
|
|||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"mocha": "^8.2.1"
|
"mocha": "^8.2.1"
|
||||||
}
|
},
|
||||||
|
"gitHead": "2942d39dfe64d60ce7dc0e0b2c0887866ca67f18"
|
||||||
}
|
}
|
||||||
16
packages/dns-providers/src/index.js
Normal file
16
packages/dns-providers/src/index.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import _ from 'lodash-es'
|
||||||
|
import { AliyunDnsProvider } from './providers/aliyun.js'
|
||||||
|
import { DnspodDnsProvider } from './providers/dnspod.js'
|
||||||
|
import { dnsProviderRegistry } from '@certd/api'
|
||||||
|
|
||||||
|
export const DefaultDnsProviders = {
|
||||||
|
AliyunDnsProvider,
|
||||||
|
DnspodDnsProvider
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
install () {
|
||||||
|
_.forEach(DefaultDnsProviders, item => {
|
||||||
|
dnsProviderRegistry.install(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,20 +2,41 @@ import { AbstractDnsProvider } from '@certd/api'
|
|||||||
import Core from '@alicloud/pop-core'
|
import Core from '@alicloud/pop-core'
|
||||||
import _ from 'lodash-es'
|
import _ from 'lodash-es'
|
||||||
export class AliyunDnsProvider extends AbstractDnsProvider {
|
export class AliyunDnsProvider extends AbstractDnsProvider {
|
||||||
constructor (dnsProviderConfig) {
|
static define () {
|
||||||
super()
|
return {
|
||||||
|
name: 'aliyun',
|
||||||
|
label: '阿里云',
|
||||||
|
desc: '',
|
||||||
|
input: {
|
||||||
|
accessProvider: {
|
||||||
|
label: '授权',
|
||||||
|
type: [String, Object],
|
||||||
|
desc: '需要aliyun类型的授权',
|
||||||
|
component: {
|
||||||
|
name: 'access-provider-selector',
|
||||||
|
filter: 'aliyun'
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor (args) {
|
||||||
|
super(args)
|
||||||
|
const { props } = args
|
||||||
|
const accessProvider = this.getAccessProvider(props.accessProvider)
|
||||||
this.client = new Core({
|
this.client = new Core({
|
||||||
accessKeyId: dnsProviderConfig.accessKeyId,
|
accessKeyId: accessProvider.accessKeyId,
|
||||||
accessKeySecret: dnsProviderConfig.accessKeySecret,
|
accessKeySecret: accessProvider.accessKeySecret,
|
||||||
endpoint: 'https://alidns.aliyuncs.com',
|
endpoint: 'https://alidns.aliyuncs.com',
|
||||||
apiVersion: '2015-01-09'
|
apiVersion: '2015-01-09'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static name () {
|
|
||||||
return 'aliyun'
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDomainList () {
|
async getDomainList () {
|
||||||
const params = {
|
const params = {
|
||||||
RegionId: 'cn-hangzhou'
|
RegionId: 'cn-hangzhou'
|
||||||
@@ -85,7 +106,9 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
|
|||||||
this.logger.info('添加域名解析成功:', value, value, ret.RecordId)
|
this.logger.info('添加域名解析成功:', value, value, ret.RecordId)
|
||||||
return ret.RecordId
|
return ret.RecordId
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// e.code === 'DomainRecordDuplicate'
|
if (e.code === 'DomainRecordDuplicate') {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.logger.info('添加域名解析出错', e)
|
this.logger.info('添加域名解析出错', e)
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
@@ -2,16 +2,31 @@ import { AbstractDnsProvider, util } from '@certd/api'
|
|||||||
import _ from 'lodash-es'
|
import _ from 'lodash-es'
|
||||||
const request = util.request
|
const request = util.request
|
||||||
export class DnspodDnsProvider extends AbstractDnsProvider {
|
export class DnspodDnsProvider extends AbstractDnsProvider {
|
||||||
static name () {
|
static define () {
|
||||||
return 'dnspod'
|
return {
|
||||||
|
name: 'dnspod',
|
||||||
|
label: 'dnspod(腾讯云)',
|
||||||
|
desc: '腾讯云的域名解析接口已迁移到dnspod',
|
||||||
|
input: {
|
||||||
|
accessProvider: {
|
||||||
|
label: '授权',
|
||||||
|
type: [String, Object],
|
||||||
|
desc: '需要dnspod类型的授权',
|
||||||
|
component: {
|
||||||
|
name: 'access-provider-selector',
|
||||||
|
filter: 'dnspod'
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor (dnsProviderConfig) {
|
constructor (args) {
|
||||||
super()
|
super(args)
|
||||||
if (!dnsProviderConfig.id || !dnsProviderConfig.token) {
|
const { props } = args
|
||||||
throw new Error('请正确配置dnspod的 id 和 token')
|
const accessProvider = this.getAccessProvider(props.accessProvider)
|
||||||
}
|
this.loginToken = accessProvider.id + ',' + accessProvider.token
|
||||||
this.loginToken = dnsProviderConfig.id + ',' + dnsProviderConfig.token
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async doRequest (options) {
|
async doRequest (options) {
|
||||||
@@ -28,7 +43,7 @@ export class DnspodDnsProvider extends AbstractDnsProvider {
|
|||||||
_.merge(config, options)
|
_.merge(config, options)
|
||||||
|
|
||||||
const ret = await request(config)
|
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)
|
throw new Error('请求失败:' + ret.status.message + ',api=' + config.url)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import pkg from 'chai'
|
import pkg from 'chai'
|
||||||
import AliyunDnsProvider from '../../src/dns-provider/impl/aliyun.js'
|
import AliyunDnsProvider from '../../src/providers/aliyun.js'
|
||||||
import { createOptions } from '../../../../test/options.js'
|
import { createOptions } from '../../../../test/options.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 () {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import pkg from 'chai'
|
import pkg from 'chai'
|
||||||
import DnspodDnsProvider from '../../src/dns-provider/impl/dnspod.js'
|
import DnspodDnsProvider from '../../src/providers/dnspod.js'
|
||||||
import { Certd } from '../../src/index.js'
|
import { Certd } from '../../src/index.js'
|
||||||
import { createOptions } from '../../../../test/options.js'
|
import { createOptions } from '../../../../test/options.js'
|
||||||
const { expect } = pkg
|
const { expect } = pkg
|
||||||
1563
packages/executor/package-lock.json
generated
1563
packages/executor/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/executor",
|
"name": "@certd/executor",
|
||||||
"version": "0.1.10",
|
"version": "0.1.12",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -10,9 +10,10 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/api": "^0.1.7",
|
"@certd/api": "^0.1.12",
|
||||||
"@certd/certd": "^0.1.7",
|
"@certd/certd": "^0.1.12",
|
||||||
"@certd/plugins": "^0.1.9",
|
"@certd/dns-providers": "^0.1.12",
|
||||||
|
"@certd/plugins": "^0.1.12",
|
||||||
"dayjs": "^1.9.7",
|
"dayjs": "^1.9.7",
|
||||||
"lodash-es": "^4.17.20"
|
"lodash-es": "^4.17.20"
|
||||||
},
|
},
|
||||||
@@ -32,5 +33,6 @@
|
|||||||
},
|
},
|
||||||
"author": "Greper",
|
"author": "Greper",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"sideEffects": false
|
"sideEffects": false,
|
||||||
|
"gitHead": "2942d39dfe64d60ce7dc0e0b2c0887866ca67f18"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,32 @@
|
|||||||
import { Certd } from '@certd/certd'
|
import { Certd } from '@certd/certd'
|
||||||
import DefaultPlugins from '@certd/plugins'
|
import { pluginRegistry, util } from '@certd/api'
|
||||||
import { util } from '@certd/api'
|
|
||||||
import _ from 'lodash-es'
|
import _ from 'lodash-es'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { Trace } from './trace.js'
|
import { Trace } from './trace.js'
|
||||||
|
import DefaultPlugins from '@certd/plugins'
|
||||||
|
import DefaultDnsProviders from '@certd/dns-providers'
|
||||||
|
|
||||||
const logger = util.logger
|
const logger = util.logger
|
||||||
|
|
||||||
|
// 安装默认插件和授权提供者
|
||||||
|
DefaultPlugins.install()
|
||||||
|
DefaultDnsProviders.install()
|
||||||
|
|
||||||
function createDefaultOptions () {
|
function createDefaultOptions () {
|
||||||
return {
|
return {
|
||||||
args: {
|
args: {
|
||||||
forceCert: false,
|
forceCert: false,
|
||||||
forceDeploy: true,
|
forceDeploy: true,
|
||||||
forceRedeploy: false
|
forceRedeploy: false,
|
||||||
|
doNotThrowError: false // 部署流程执行有错误时,不抛异常,此时整个任务执行完毕后,可以返回结果,你可以在返回结果中处理
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export class Executor {
|
export class Executor {
|
||||||
constructor () {
|
constructor () {
|
||||||
this.usePlugins(DefaultPlugins)
|
|
||||||
this.trace = new Trace()
|
this.trace = new Trace()
|
||||||
}
|
}
|
||||||
|
|
||||||
use (plugin) {
|
|
||||||
if (plugin == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (this.plugins == null) {
|
|
||||||
this.plugins = {}
|
|
||||||
}
|
|
||||||
this.plugins[plugin.name] = plugin
|
|
||||||
if (plugin.define) {
|
|
||||||
const define = plugin.define()
|
|
||||||
this.plugins[define.name] = plugin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
usePlugins (plugins) {
|
|
||||||
if (plugins) {
|
|
||||||
for (const plugin of plugins) {
|
|
||||||
this.use(plugin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async run (options) {
|
async run (options) {
|
||||||
logger.info('------------------- Cert-D ---------------------')
|
logger.info('------------------- Cert-D ---------------------')
|
||||||
try {
|
try {
|
||||||
@@ -67,7 +51,7 @@ export class Executor {
|
|||||||
logger.info('----------------------')
|
logger.info('----------------------')
|
||||||
if (!cert.isNew) {
|
if (!cert.isNew) {
|
||||||
// 如果没有更新
|
// 如果没有更新
|
||||||
if (!options.args?.forceDeploy && !options.args?.forceRedeploy) {
|
if (!options.args.forceDeploy && !options.args.forceRedeploy) {
|
||||||
// 且不需要强制运行deploy
|
// 且不需要强制运行deploy
|
||||||
logger.info('证书无更新,无需重新部署')
|
logger.info('证书无更新,无需重新部署')
|
||||||
logger.info('任务完成')
|
logger.info('任务完成')
|
||||||
@@ -84,24 +68,23 @@ export class Executor {
|
|||||||
context.certIsNew = !!cert.isNew
|
context.certIsNew = !!cert.isNew
|
||||||
|
|
||||||
const trace = new Trace(context)
|
const trace = new Trace(context)
|
||||||
|
const resultTrace = trace.getInstance({ type: 'result' })
|
||||||
// 运行部署任务
|
// 运行部署任务
|
||||||
try {
|
try {
|
||||||
await this.runDeploys({ options, cert, context, trace })
|
await this.runDeploys({ options, cert, context, trace })
|
||||||
} finally {
|
} finally {
|
||||||
await certd.certStore.setCurrentFile('context.json', JSON.stringify(context))
|
await certd.certStore.setCurrentFile('context.json', JSON.stringify(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('任务完成')
|
logger.info('任务完成')
|
||||||
trace.print()
|
trace.print()
|
||||||
const result = trace.get({ })
|
const result = resultTrace.get({ })
|
||||||
const returnData = {
|
const returnData = {
|
||||||
cert,
|
cert,
|
||||||
context,
|
context,
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
if (result.status === 'error') {
|
if (result.status === 'error' && options.args.doNotThrowError === false) {
|
||||||
const err = new Error(result.remark)
|
throw new Error(result.remark)
|
||||||
err.data = returnData
|
|
||||||
}
|
}
|
||||||
return returnData
|
return returnData
|
||||||
}
|
}
|
||||||
@@ -122,10 +105,12 @@ export class Executor {
|
|||||||
for (const deploy of options.deploy) {
|
for (const deploy of options.deploy) {
|
||||||
const deployName = deploy.deployName
|
const deployName = deploy.deployName
|
||||||
logger.info(`------------【${deployName}】-----------`)
|
logger.info(`------------【${deployName}】-----------`)
|
||||||
|
|
||||||
|
const deployTrace = trace.getInstance({ type: 'deploy', deployName })
|
||||||
if (deploy.disabled === true) {
|
if (deploy.disabled === true) {
|
||||||
logger.info('此流程已被禁用,跳过')
|
logger.info('此流程已被禁用,跳过')
|
||||||
logger.info('')
|
logger.info('')
|
||||||
trace.set({ deployName, value: { current: 'skip', status: 'disabled', remark: '流程禁用' } })
|
deployTrace.set({ value: { current: 'skip', status: 'disabled', remark: '流程禁用' } })
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -134,13 +119,14 @@ export class Executor {
|
|||||||
context[deployName] = {}
|
context[deployName] = {}
|
||||||
}
|
}
|
||||||
const taskContext = context[deployName]
|
const taskContext = context[deployName]
|
||||||
|
// 开始执行任务列表
|
||||||
await this.runTask({ options, cert, task, context: taskContext, deploy, trace })
|
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) {
|
} catch (e) {
|
||||||
trace.set({ deployName, value: { status: 'error', remark: '执行失败:' + e.message } })
|
deployTrace.set({ value: { status: 'error', remark: '执行失败:' + e.message } })
|
||||||
trace.set({ value: { status: 'error', remark: deployName + '执行失败:' + e.message } })
|
trace.set({ type: 'result', value: { status: 'error', remark: deployName + '执行失败:' + e.message } })
|
||||||
logger.error('流程执行失败', e)
|
logger.error('流程执行失败', e)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +136,7 @@ export class Executor {
|
|||||||
|
|
||||||
async runTask ({ options, task, cert, context, deploy, trace }) {
|
async runTask ({ options, task, cert, context, deploy, trace }) {
|
||||||
const taskType = task.type
|
const taskType = task.type
|
||||||
const Plugin = this.plugins[taskType]
|
const Plugin = pluginRegistry.get(taskType)
|
||||||
const deployName = deploy.deployName
|
const deployName = deploy.deployName
|
||||||
const taskName = task.taskName
|
const taskName = task.taskName
|
||||||
if (Plugin == null) {
|
if (Plugin == null) {
|
||||||
@@ -161,18 +147,20 @@ export class Executor {
|
|||||||
if (Plugin instanceof Function) {
|
if (Plugin instanceof Function) {
|
||||||
instance = new Plugin({ accessProviders: options.accessProviders })
|
instance = new Plugin({ accessProviders: options.accessProviders })
|
||||||
}
|
}
|
||||||
const traceStatus = trace.get({ deployName: deploy.deployName, taskName: taskName })
|
const taskTrace = trace.getInstance({ type: 'deploy', deployName, taskName })
|
||||||
if (traceStatus?.status === 'success' && !options?.args?.forceRedeploy) {
|
const traceStatus = taskTrace.get({})
|
||||||
|
if (traceStatus && traceStatus.status === 'success' && !options.args.forceRedeploy) {
|
||||||
logger.info(`----【${taskName}】已经执行完成,跳过此任务`)
|
logger.info(`----【${taskName}】已经执行完成,跳过此任务`)
|
||||||
trace.set({ deployName, taskName, value: { current: 'skip', status: 'success', remark: '已执行成功过,本次跳过' } })
|
taskTrace.set({ value: { current: 'skip', status: 'success', remark: '已执行成功过,本次跳过' } })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.info(`----【${taskName}】开始执行`)
|
logger.info(`----【${taskName}】开始执行`)
|
||||||
try {
|
try {
|
||||||
|
// 执行任务
|
||||||
await instance.execute({ cert, props: task.props, context })
|
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) {
|
} 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
|
throw e
|
||||||
}
|
}
|
||||||
logger.info(`----任务【${taskName}】执行完成`)
|
logger.info(`----任务【${taskName}】执行完成`)
|
||||||
|
|||||||
@@ -6,19 +6,30 @@ export class Trace {
|
|||||||
this.context = context
|
this.context = context
|
||||||
}
|
}
|
||||||
|
|
||||||
set ({ deployName, taskName, prop, value }) {
|
getInstance ({ type, deployName, taskName }) {
|
||||||
const key = this.buildTraceKey({ deployName, taskName, prop })
|
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) || {}
|
const oldValue = _.get(this.context, key) || {}
|
||||||
_.merge(oldValue, value)
|
_.merge(oldValue, value)
|
||||||
_.set(this.context, key, oldValue)
|
_.set(this.context, key, oldValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
get ({ deployName, taskName, prop }) {
|
get ({ type, deployName, taskName, prop }) {
|
||||||
return _.get(this.context, this.buildTraceKey({ deployName, taskName, prop }))
|
return _.get(this.context, this.buildTraceKey({ type, deployName, taskName, prop }))
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTraceKey ({ deployName, taskName, prop }) {
|
buildTraceKey ({ type = 'default', deployName, taskName, prop }) {
|
||||||
let key = '__trace__'
|
let key = '__trace__.' + type
|
||||||
if (deployName) {
|
if (deployName) {
|
||||||
key += '.'
|
key += '.'
|
||||||
key += deployName.replace(/\./g, '_')
|
key += deployName.replace(/\./g, '_')
|
||||||
@@ -46,7 +57,7 @@ export class Trace {
|
|||||||
} else {
|
} else {
|
||||||
this.printTraceLine({ current: 'skip', remark: '还未到过期时间,跳过' }, '更新证书')
|
this.printTraceLine({ current: 'skip', remark: '还未到过期时间,跳过' }, '更新证书')
|
||||||
}
|
}
|
||||||
const trace = this.get({ })
|
const trace = this.get({ type: 'deploy' })
|
||||||
// logger.info('trace', trace)
|
// logger.info('trace', trace)
|
||||||
for (const deployName in trace) {
|
for (const deployName in trace) {
|
||||||
if (trace[deployName] == null) {
|
if (trace[deployName] == null) {
|
||||||
@@ -64,11 +75,12 @@ export class Trace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const result = this.get({ type: 'result' })
|
||||||
|
this.printTraceLine(result, 'result', '')
|
||||||
const mainContext = {}
|
const mainContext = {}
|
||||||
_.merge(mainContext, context)
|
_.merge(mainContext, context)
|
||||||
delete mainContext.__trace__
|
delete mainContext.__trace__
|
||||||
logger.info('context:', JSON.stringify(mainContext))
|
logger.info('【context】', JSON.stringify(mainContext))
|
||||||
}
|
}
|
||||||
|
|
||||||
printTraceLine (traceStatus, name, prefix = '') {
|
printTraceLine (traceStatus, name, prefix = '') {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const { expect } = pkg
|
|||||||
|
|
||||||
describe('AutoDeploy', function () {
|
describe('AutoDeploy', function () {
|
||||||
it('#run', async function () {
|
it('#run', async function () {
|
||||||
this.timeout(20000)
|
this.timeout(120000)
|
||||||
const options = createOptions()
|
const options = createOptions()
|
||||||
const executor = new Executor()
|
const executor = new Executor()
|
||||||
const ret = await executor.run(options)
|
const ret = await executor.run(options)
|
||||||
@@ -13,15 +13,18 @@ describe('AutoDeploy', function () {
|
|||||||
expect(ret.cert).ok
|
expect(ret.cert).ok
|
||||||
})
|
})
|
||||||
it('#forceCert', async function () {
|
it('#forceCert', async function () {
|
||||||
this.timeout(20000)
|
this.timeout(120000)
|
||||||
const executor = new Executor()
|
const executor = new Executor()
|
||||||
const options = createOptions()
|
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).ok
|
||||||
expect(ret.cert).ok
|
expect(ret.cert).ok
|
||||||
})
|
})
|
||||||
it('#forceDeploy', async function () {
|
it('#forceDeploy', async function () {
|
||||||
this.timeout(20000)
|
this.timeout(120000)
|
||||||
const executor = new Executor()
|
const executor = new Executor()
|
||||||
const options = createOptions()
|
const options = createOptions()
|
||||||
const ret = await executor.run(options, { forceCert: false, forceDeploy: true, forceRedeploy: true })
|
const ret = await executor.run(options, { forceCert: false, forceDeploy: true, forceRedeploy: true })
|
||||||
|
|||||||
192
packages/plugins/package-lock.json
generated
192
packages/plugins/package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugins",
|
"name": "@certd/plugins",
|
||||||
"version": "0.1.9",
|
"version": "0.1.12",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -131,70 +131,6 @@
|
|||||||
"to-fast-properties": "^2.0.0"
|
"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",
|
|
||||||
"integrity": "sha512-2spEdF9A6Tpe5KwkbWV1zE9Zwql04DrG5KlWRUsn8kLvwY6qfdDsdCsNoFf1+XDytn4OYH5nfrMGZBi8TpG84w==",
|
|
||||||
"requires": {
|
|
||||||
"axios": "^0.21.1",
|
|
||||||
"dayjs": "^1.9.7",
|
|
||||||
"lodash-es": "^4.17.20",
|
|
||||||
"log4js": "^6.3.0",
|
|
||||||
"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": {
|
"@eslint/eslintrc": {
|
||||||
"version": "0.2.2",
|
"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",
|
"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",
|
||||||
@@ -540,14 +476,6 @@
|
|||||||
"resolved": "https://registry.npm.taobao.org/aws4/download/aws4-1.11.0.tgz?cache=0&sync_timestamp=1604101166484&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faws4%2Fdownload%2Faws4-1.11.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/aws4/download/aws4-1.11.0.tgz?cache=0&sync_timestamp=1604101166484&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faws4%2Fdownload%2Faws4-1.11.0.tgz",
|
||||||
"integrity": "sha1-1h9G2DslGSUOJ4Ta9bCUeai0HFk="
|
"integrity": "sha1-1h9G2DslGSUOJ4Ta9bCUeai0HFk="
|
||||||
},
|
},
|
||||||
"axios": {
|
|
||||||
"version": "0.21.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
|
||||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
|
||||||
"requires": {
|
|
||||||
"follow-redirects": "^1.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"babel-eslint": {
|
"babel-eslint": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/babel-eslint/download/babel-eslint-10.1.0.tgz?cache=0&sync_timestamp=1599054223324&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-eslint%2Fdownload%2Fbabel-eslint-10.1.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/babel-eslint/download/babel-eslint-10.1.0.tgz?cache=0&sync_timestamp=1599054223324&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-eslint%2Fdownload%2Fbabel-eslint-10.1.0.tgz",
|
||||||
@@ -561,11 +489,6 @@
|
|||||||
"resolve": "^1.12.0"
|
"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": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz",
|
||||||
@@ -595,11 +518,6 @@
|
|||||||
"integrity": "sha1-MPpAyef+B9vIlWeM0ocCTeokHdk=",
|
"integrity": "sha1-MPpAyef+B9vIlWeM0ocCTeokHdk=",
|
||||||
"dev": true
|
"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": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"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",
|
"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",
|
||||||
@@ -844,11 +762,6 @@
|
|||||||
"assert-plus": "^1.0.0"
|
"assert-plus": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"date-format": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w=="
|
|
||||||
},
|
|
||||||
"dayjs": {
|
"dayjs": {
|
||||||
"version": "1.10.2",
|
"version": "1.10.2",
|
||||||
"resolved": "https://registry.npm.taobao.org/dayjs/download/dayjs-1.10.2.tgz?cache=0&sync_timestamp=1609889274763&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdayjs%2Fdownload%2Fdayjs-1.10.2.tgz",
|
"resolved": "https://registry.npm.taobao.org/dayjs/download/dayjs-1.10.2.tgz?cache=0&sync_timestamp=1609889274763&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdayjs%2Fdownload%2Fdayjs-1.10.2.tgz",
|
||||||
@@ -1493,16 +1406,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flatted": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA=="
|
|
||||||
},
|
|
||||||
"follow-redirects": {
|
|
||||||
"version": "1.13.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
|
|
||||||
"integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg=="
|
|
||||||
},
|
|
||||||
"forever-agent": {
|
"forever-agent": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz",
|
"resolved": "https://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz",
|
||||||
@@ -1518,16 +1421,6 @@
|
|||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs-extra": {
|
|
||||||
"version": "8.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
|
||||||
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
|
|
||||||
"requires": {
|
|
||||||
"graceful-fs": "^4.2.0",
|
|
||||||
"jsonfile": "^4.0.0",
|
|
||||||
"universalify": "^0.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz",
|
||||||
@@ -1644,7 +1537,8 @@
|
|||||||
"graceful-fs": {
|
"graceful-fs": {
|
||||||
"version": "4.2.4",
|
"version": "4.2.4",
|
||||||
"resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz",
|
"resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz",
|
||||||
"integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs="
|
"integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"growl": {
|
"growl": {
|
||||||
"version": "1.10.5",
|
"version": "1.10.5",
|
||||||
@@ -2029,14 +1923,6 @@
|
|||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jsonfile": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
|
||||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
|
||||||
"requires": {
|
|
||||||
"graceful-fs": "^4.1.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jsonpath-plus": {
|
"jsonpath-plus": {
|
||||||
"version": "0.19.0",
|
"version": "0.19.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/jsonpath-plus/download/jsonpath-plus-0.19.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/jsonpath-plus/download/jsonpath-plus-0.19.0.tgz",
|
||||||
@@ -2197,33 +2083,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"log4js": {
|
|
||||||
"version": "6.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz",
|
|
||||||
"integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==",
|
|
||||||
"requires": {
|
|
||||||
"date-format": "^3.0.0",
|
|
||||||
"debug": "^4.1.1",
|
|
||||||
"flatted": "^2.0.1",
|
|
||||||
"rfdc": "^1.1.4",
|
|
||||||
"streamroller": "^2.2.4"
|
|
||||||
},
|
|
||||||
"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=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"loose-envify": {
|
"loose-envify": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/loose-envify/download/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/loose-envify/download/loose-envify-1.4.0.tgz",
|
||||||
@@ -2428,11 +2287,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",
|
"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="
|
"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": {
|
"normalize-package-data": {
|
||||||
"version": "2.5.0",
|
"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",
|
"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",
|
||||||
@@ -2941,11 +2795,6 @@
|
|||||||
"lowercase-keys": "^1.0.0"
|
"lowercase-keys": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rfdc": {
|
|
||||||
"version": "1.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
|
|
||||||
"integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug=="
|
|
||||||
},
|
|
||||||
"rimraf": {
|
"rimraf": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npm.taobao.org/rimraf/download/rimraf-3.0.2.tgz?cache=0&sync_timestamp=1599054104695&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frimraf%2Fdownload%2Frimraf-3.0.2.tgz",
|
"resolved": "https://registry.npm.taobao.org/rimraf/download/rimraf-3.0.2.tgz?cache=0&sync_timestamp=1599054104695&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frimraf%2Fdownload%2Frimraf-3.0.2.tgz",
|
||||||
@@ -3142,36 +2991,6 @@
|
|||||||
"tweetnacl": "~0.14.0"
|
"tweetnacl": "~0.14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"streamroller": {
|
|
||||||
"version": "2.2.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz",
|
|
||||||
"integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==",
|
|
||||||
"requires": {
|
|
||||||
"date-format": "^2.1.0",
|
|
||||||
"debug": "^4.1.1",
|
|
||||||
"fs-extra": "^8.1.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"date-format": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA=="
|
|
||||||
},
|
|
||||||
"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=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"streamsearch": {
|
"streamsearch": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npm.taobao.org/streamsearch/download/streamsearch-0.1.2.tgz",
|
"resolved": "https://registry.npm.taobao.org/streamsearch/download/streamsearch-0.1.2.tgz",
|
||||||
@@ -3396,11 +3215,6 @@
|
|||||||
"resolved": "https://registry.npm.taobao.org/underscore/download/underscore-1.12.0.tgz?cache=0&sync_timestamp=1606179462980&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Funderscore%2Fdownload%2Funderscore-1.12.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/underscore/download/underscore-1.12.0.tgz?cache=0&sync_timestamp=1606179462980&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Funderscore%2Fdownload%2Funderscore-1.12.0.tgz",
|
||||||
"integrity": "sha1-SBSUBVH8gFh873hA0euw8WRTvpc="
|
"integrity": "sha1-SBSUBVH8gFh873hA0euw8WRTvpc="
|
||||||
},
|
},
|
||||||
"universalify": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
|
||||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
|
|
||||||
},
|
|
||||||
"uri-js": {
|
"uri-js": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/uri-js/download/uri-js-4.4.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/uri-js/download/uri-js-4.4.0.tgz",
|
||||||
|
|||||||
@@ -1,31 +1,28 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugins",
|
"name": "@certd/plugins",
|
||||||
"version": "0.1.9",
|
"version": "0.1.12",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "./src/index.js",
|
"main": "./src/index.js",
|
||||||
"scripts": {
|
"type": "module",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"dependencies": {
|
||||||
},
|
"@alicloud/pop-core": "^1.7.10",
|
||||||
"type": "module",
|
"@certd/api": "^0.1.12",
|
||||||
"dependencies": {
|
"dayjs": "^1.9.7",
|
||||||
"@alicloud/pop-core": "^1.7.10",
|
"kubernetes-client": "^9.0.0",
|
||||||
"@certd/api": "^0.1.7",
|
"lodash-es": "^4.17.20",
|
||||||
"@certd/certd": "^0.1.7",
|
"ssh2": "^0.8.9",
|
||||||
"dayjs": "^1.9.7",
|
"tencentcloud-sdk-nodejs": "^4.0.44"
|
||||||
"kubernetes-client": "^9.0.0",
|
},
|
||||||
"lodash-es": "^4.17.20",
|
"devDependencies": {
|
||||||
"ssh2": "^0.8.9",
|
"chai": "^4.2.0",
|
||||||
"tencentcloud-sdk-nodejs": "^4.0.44"
|
"eslint": "^7.15.0",
|
||||||
},
|
"eslint-config-standard": "^16.0.2",
|
||||||
"devDependencies": {
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"chai": "^4.2.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint": "^7.15.0",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-config-standard": "^16.0.2",
|
"mocha": "^8.2.1"
|
||||||
"eslint-plugin-import": "^2.22.1",
|
},
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"author": "Greper",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"license": "MIT",
|
||||||
"mocha": "^8.2.1"
|
"gitHead": "2942d39dfe64d60ce7dc0e0b2c0887866ca67f18"
|
||||||
},
|
|
||||||
"author": "Greper",
|
|
||||||
"license": "MIT"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,67 @@
|
|||||||
import { AbstractAliyunPlugin } from '../../aliyun/abstract-aliyun.js'
|
import { AbstractAliyunPlugin } from '../../aliyun/abstract-aliyun.js'
|
||||||
import Core from '@alicloud/pop-core'
|
import Core from '@alicloud/pop-core'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
export class DeployCertToAliyunCDN extends AbstractAliyunPlugin {
|
|
||||||
/**
|
|
||||||
* 插件定义
|
|
||||||
* 名称
|
|
||||||
* 入参
|
|
||||||
* 出参
|
|
||||||
*/
|
|
||||||
static define () {
|
|
||||||
return {
|
|
||||||
name: 'deployCertToAliyunCDN',
|
|
||||||
label: '部署到阿里云CDN',
|
|
||||||
input: {
|
|
||||||
domainName: {
|
|
||||||
label: 'cdn加速域名',
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
certName: {
|
|
||||||
label: '证书名称'
|
|
||||||
},
|
|
||||||
from: {
|
|
||||||
value: 'upload',
|
|
||||||
label: '证书来源',
|
|
||||||
options: [
|
|
||||||
{ value: 'upload', label: '直接上传' },
|
|
||||||
{ value: 'cas', label: '从证书库', desc: '需要uploadCertToAliyun作为前置任务' }
|
|
||||||
],
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
// serverCertificateStatus: {
|
|
||||||
// label: '启用https',
|
|
||||||
// options: [
|
|
||||||
// { value: 'on', label: '开启HTTPS,并更新证书' },
|
|
||||||
// { value: 'auto', label: '若HTTPS开启则更新,未开启不更新' }
|
|
||||||
// ],
|
|
||||||
// required:true
|
|
||||||
// },
|
|
||||||
accessProvider: {
|
|
||||||
label: 'Access提供者',
|
|
||||||
type: [String, Object],
|
|
||||||
desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象',
|
|
||||||
options: 'accessProviders[type=aliyun]',
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
|
|
||||||
|
const define = {
|
||||||
|
name: 'deployCertToAliyunCDN',
|
||||||
|
label: '部署到阿里云CDN',
|
||||||
|
input: {
|
||||||
|
domainName: {
|
||||||
|
label: 'cdn加速域名',
|
||||||
|
component: {
|
||||||
|
placeholder: 'cdn加速域名'
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
certName: {
|
||||||
|
label: '证书名称',
|
||||||
|
component: {
|
||||||
|
placeholder: '上传后将以此名称作为前缀'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
default: 'upload',
|
||||||
|
label: '证书来源',
|
||||||
|
required: true,
|
||||||
|
component: {
|
||||||
|
required: true,
|
||||||
|
placeholder: '证书来源',
|
||||||
|
name: 'a-select',
|
||||||
|
options: [
|
||||||
|
{ value: 'upload', label: '直接上传' },
|
||||||
|
{ value: 'cas', label: '从证书库', title: '需要uploadCertToAliyun作为前置任务' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
desc: '如果选择‘从证书库’类型,则需要以《上传证书到阿里云》作为前置任务'
|
||||||
|
|
||||||
|
},
|
||||||
|
// serverCertificateStatus: {
|
||||||
|
// label: '启用https',
|
||||||
|
// options: [
|
||||||
|
// { value: 'on', label: '开启HTTPS,并更新证书' },
|
||||||
|
// { value: 'auto', label: '若HTTPS开启则更新,未开启不更新' }
|
||||||
|
// ],
|
||||||
|
// required:true
|
||||||
|
// },
|
||||||
|
accessProvider: {
|
||||||
|
label: 'Access提供者',
|
||||||
|
type: [String, Object],
|
||||||
|
desc: 'access授权',
|
||||||
|
component: {
|
||||||
|
name: 'access-provider-selector',
|
||||||
|
filter: 'aliyun'
|
||||||
|
},
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DeployCertToAliyunCDN extends AbstractAliyunPlugin {
|
||||||
|
static define () {
|
||||||
|
return define
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute ({ cert, props, context }) {
|
async execute ({ cert, props, context }) {
|
||||||
|
|||||||
@@ -1,38 +1,41 @@
|
|||||||
import Core from '@alicloud/pop-core'
|
import Core from '@alicloud/pop-core'
|
||||||
import { AbstractAliyunPlugin } from '../abstract-aliyun.js'
|
import { AbstractAliyunPlugin } from '../abstract-aliyun.js'
|
||||||
export class UploadCertToAliyun extends AbstractAliyunPlugin {
|
|
||||||
/**
|
const define = {
|
||||||
* 插件定义
|
name: 'uploadCertToAliyun',
|
||||||
* 名称
|
label: '上传证书到阿里云',
|
||||||
* 入参
|
input: {
|
||||||
* 出参
|
name: {
|
||||||
*/
|
label: '证书名称',
|
||||||
static define () {
|
desc: '证书上传后将以此参数作为名称前缀'
|
||||||
return {
|
},
|
||||||
name: 'uploadCertToAliyun',
|
regionId: {
|
||||||
label: '上传证书到阿里云',
|
label: '大区',
|
||||||
input: {
|
default: 'cn-hangzhou',
|
||||||
name: {
|
required: true
|
||||||
label: '证书名称'
|
},
|
||||||
},
|
accessProvider: {
|
||||||
regionId: {
|
label: 'Access提供者',
|
||||||
label: '大区',
|
type: [String, Object],
|
||||||
value: 'cn-hangzhou'
|
desc: 'access授权',
|
||||||
},
|
component: {
|
||||||
accessProvider: {
|
name: 'access-provider-selector',
|
||||||
label: 'Access提供者',
|
filter: 'aliyun'
|
||||||
type: [String, Object],
|
|
||||||
desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象',
|
|
||||||
options: 'accessProviders[type=aliyun]'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
output: {
|
required: true
|
||||||
aliyunCertId: {
|
|
||||||
type: String,
|
|
||||||
desc: '上传成功后的阿里云CertId'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
aliyunCertId: {
|
||||||
|
type: String,
|
||||||
|
desc: '上传成功后的阿里云CertId'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UploadCertToAliyun extends AbstractAliyunPlugin {
|
||||||
|
static define () {
|
||||||
|
return define
|
||||||
}
|
}
|
||||||
|
|
||||||
getClient (aliyunProvider) {
|
getClient (aliyunProvider) {
|
||||||
|
|||||||
@@ -18,8 +18,12 @@ export class HostShellExecute extends AbstractHostPlugin {
|
|||||||
accessProvider: {
|
accessProvider: {
|
||||||
label: '主机登录配置',
|
label: '主机登录配置',
|
||||||
type: [String, Object],
|
type: [String, Object],
|
||||||
desc: 'AccessProviders的key 或 一个包含用户名密码的对象',
|
desc: '登录',
|
||||||
options: 'accessProviders[type=ssh]'
|
component: {
|
||||||
|
name: 'access-provider-selector',
|
||||||
|
filter: 'host'
|
||||||
|
},
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
|
|||||||
@@ -21,8 +21,12 @@ export class UploadCertToHost extends AbstractHostPlugin {
|
|||||||
accessProvider: {
|
accessProvider: {
|
||||||
label: '主机登录配置',
|
label: '主机登录配置',
|
||||||
type: [String, Object],
|
type: [String, Object],
|
||||||
desc: 'AccessProviders的key 或 一个包含用户名密码的对象',
|
desc: 'access授权',
|
||||||
options: 'accessProviders[type=ssh]'
|
component: {
|
||||||
|
name: 'access-provider-selector',
|
||||||
|
filter: 'host'
|
||||||
|
},
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import _ from 'lodash-es'
|
||||||
import { UploadCertToAliyun } from './aliyun/upload-to-aliyun/index.js'
|
import { UploadCertToAliyun } from './aliyun/upload-to-aliyun/index.js'
|
||||||
import { DeployCertToAliyunCDN } from './aliyun/deploy-to-cdn/index.js'
|
import { DeployCertToAliyunCDN } from './aliyun/deploy-to-cdn/index.js'
|
||||||
|
|
||||||
@@ -8,12 +9,20 @@ import { DeployCertToTencentCDN } from './tencent/deploy-to-cdn/index.js'
|
|||||||
import { DeployCertToTencentCLB } from './tencent/deploy-to-clb/index.js'
|
import { DeployCertToTencentCLB } from './tencent/deploy-to-clb/index.js'
|
||||||
|
|
||||||
import { DeployCertToTencentTKEIngress } from './tencent/deploy-to-tke-ingress/index.js'
|
import { DeployCertToTencentTKEIngress } from './tencent/deploy-to-tke-ingress/index.js'
|
||||||
|
import { pluginRegistry } from '@certd/api'
|
||||||
|
|
||||||
export default [
|
export const DefaultPlugins = {
|
||||||
UploadCertToAliyun,
|
UploadCertToAliyun,
|
||||||
DeployCertToAliyunCDN,
|
DeployCertToAliyunCDN,
|
||||||
UploadCertToTencent,
|
UploadCertToTencent,
|
||||||
DeployCertToTencentTKEIngress,
|
DeployCertToTencentTKEIngress,
|
||||||
DeployCertToTencentCDN,
|
DeployCertToTencentCDN,
|
||||||
DeployCertToTencentCLB
|
DeployCertToTencentCLB
|
||||||
]
|
}
|
||||||
|
export default {
|
||||||
|
install () {
|
||||||
|
_.forEach(DefaultPlugins, item => {
|
||||||
|
pluginRegistry.install(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,30 +19,27 @@ export class DeployCertToTencentCDN extends AbstractTencentPlugin {
|
|||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
certName: {
|
certName: {
|
||||||
label: '证书名称'
|
label: '证书名称',
|
||||||
|
desc: '证书上传后将以此参数作为名称前缀'
|
||||||
},
|
},
|
||||||
certType: {
|
certType: {
|
||||||
value: 'upload',
|
default: 'upload',
|
||||||
label: '证书来源',
|
label: '证书来源',
|
||||||
options: [
|
options: [
|
||||||
{ value: 'upload', label: '直接上传' },
|
{ value: 'upload', label: '直接上传' },
|
||||||
{ value: 'cloud', label: '从证书库', desc: '需要uploadCertToTencent作为前置任务' }
|
{ value: 'cloud', label: '从证书库', desc: '需要uploadCertToTencent作为前置任务' }
|
||||||
],
|
],
|
||||||
|
desc: '如果选择‘从证书库’类型,则需要以《上传证书到腾讯云》作为前置任务',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
// serverCertificateStatus: {
|
|
||||||
// label: '启用https',
|
|
||||||
// options: [
|
|
||||||
// { value: 'on', label: '开启HTTPS,并更新证书' },
|
|
||||||
// { value: 'auto', label: '若HTTPS开启则更新,未开启不更新' }
|
|
||||||
// ],
|
|
||||||
// required:true
|
|
||||||
// },
|
|
||||||
accessProvider: {
|
accessProvider: {
|
||||||
label: 'Access提供者',
|
label: 'Access提供者',
|
||||||
type: [String, Object],
|
type: [String, Object],
|
||||||
desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象',
|
desc: 'access 授权',
|
||||||
options: 'accessProviders[type=aliyun]',
|
component: {
|
||||||
|
name: 'access-provider-selector',
|
||||||
|
filter: 'tencent'
|
||||||
|
},
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,16 +15,19 @@ export class DeployCertToTencentCLB extends AbstractTencentPlugin {
|
|||||||
input: {
|
input: {
|
||||||
region: {
|
region: {
|
||||||
label: '大区',
|
label: '大区',
|
||||||
value: 'ap-guangzhou'
|
default: 'ap-guangzhou',
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
domain: {
|
domain: {
|
||||||
label: '域名',
|
label: '域名',
|
||||||
type: [String, Array],
|
type: [String, Array],
|
||||||
|
required: true,
|
||||||
desc: '要更新的支持https的负载均衡的域名'
|
desc: '要更新的支持https的负载均衡的域名'
|
||||||
},
|
},
|
||||||
loadBalancerId: {
|
loadBalancerId: {
|
||||||
label: '负载均衡ID',
|
label: '负载均衡ID',
|
||||||
desc: '如果没有配置,则根据域名匹配负载均衡下的监听器(根据域名匹配时暂时只支持前100个)'
|
desc: '如果没有配置,则根据域名匹配负载均衡下的监听器(根据域名匹配时暂时只支持前100个)',
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
listenerId: {
|
listenerId: {
|
||||||
label: '监听器ID',
|
label: '监听器ID',
|
||||||
@@ -37,8 +40,11 @@ export class DeployCertToTencentCLB extends AbstractTencentPlugin {
|
|||||||
accessProvider: {
|
accessProvider: {
|
||||||
label: 'Access提供者',
|
label: 'Access提供者',
|
||||||
type: [String, Object],
|
type: [String, Object],
|
||||||
desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象',
|
desc: 'access授权',
|
||||||
options: 'accessProviders[type=tencent]',
|
component: {
|
||||||
|
name: 'access-provider-selector',
|
||||||
|
filter: 'tencent'
|
||||||
|
},
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,21 +16,25 @@ export class DeployCertToTencentTKEIngress extends AbstractTencentPlugin {
|
|||||||
input: {
|
input: {
|
||||||
region: {
|
region: {
|
||||||
label: '大区',
|
label: '大区',
|
||||||
value: 'ap-guangzhou'
|
default: 'ap-guangzhou',
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
clusterId: {
|
clusterId: {
|
||||||
label: '集群ID',
|
label: '集群ID',
|
||||||
required: true,
|
required: true,
|
||||||
desc: '例如:cls-6lbj1vee'
|
desc: '例如:cls-6lbj1vee',
|
||||||
|
request: true
|
||||||
},
|
},
|
||||||
namespace: {
|
namespace: {
|
||||||
label: '集群的namespace',
|
label: '集群namespace',
|
||||||
value: 'default'
|
default: 'default',
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
secreteName: {
|
secreteName: {
|
||||||
type: [String, Array],
|
type: [String, Array],
|
||||||
label: '证书的secret名称',
|
label: '证书的secret名称',
|
||||||
desc: '支持多个(传入数组)'
|
desc: '支持多个(传入数组)',
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
ingressName: {
|
ingressName: {
|
||||||
type: [String, Array],
|
type: [String, Array],
|
||||||
@@ -44,20 +48,19 @@ export class DeployCertToTencentTKEIngress extends AbstractTencentPlugin {
|
|||||||
},
|
},
|
||||||
clusterDomain: {
|
clusterDomain: {
|
||||||
type: String,
|
type: String,
|
||||||
label: '集群域名,可不填,默认为:[clusterId].ccs.tencent-cloud.com'
|
label: '集群域名',
|
||||||
|
desc: '可不填,默认为:[clusterId].ccs.tencent-cloud.com'
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* AccessProvider的key,或者一个包含access的具体的对象
|
* AccessProvider的key,或者一个包含access的具体的对象
|
||||||
*/
|
*/
|
||||||
accessProvider: {
|
accessProvider: {
|
||||||
label: 'Access提供者',
|
label: 'Access授权',
|
||||||
type: [String, Object],
|
type: [String, Object],
|
||||||
desc: '请选择access提供者',
|
desc: 'access授权',
|
||||||
component: {
|
component: {
|
||||||
name: 'accessProviderSelect',
|
name: 'access-provider-selector',
|
||||||
props: {
|
filter: 'tencent'
|
||||||
filterType: 'tencent'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,14 @@ export class UploadCertToTencent extends AbstractTencentPlugin {
|
|||||||
label: '证书名称'
|
label: '证书名称'
|
||||||
},
|
},
|
||||||
accessProvider: {
|
accessProvider: {
|
||||||
label: 'Access提供者',
|
label: 'Access授权',
|
||||||
type: [String, Object],
|
type: [String, Object],
|
||||||
desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象',
|
desc: 'access授权',
|
||||||
options: 'accessProviders[type=tencent]'
|
component: {
|
||||||
|
name: 'access-provider-selector',
|
||||||
|
filter: 'tencent'
|
||||||
|
},
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export class K8sClient {
|
|||||||
* @returns secretsList
|
* @returns secretsList
|
||||||
*/
|
*/
|
||||||
async getSecret (opts) {
|
async getSecret (opts) {
|
||||||
const namespace = opts?.namespace || 'default'
|
const namespace = opts.namespace || 'default'
|
||||||
const secrets = await this.client.api.v1.namespaces(namespace).secrets.get()
|
const secrets = await this.client.api.v1.namespaces(namespace).secrets.get()
|
||||||
return secrets
|
return secrets
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ export class K8sClient {
|
|||||||
* @returns {Promise<*>}
|
* @returns {Promise<*>}
|
||||||
*/
|
*/
|
||||||
async createSecret (opts) {
|
async createSecret (opts) {
|
||||||
const namespace = opts?.namespace || 'default'
|
const namespace = opts.namespace || 'default'
|
||||||
const created = await this.client.api.v1.namespaces(namespace).secrets.post({
|
const created = await this.client.api.v1.namespaces(namespace).secrets.post({
|
||||||
body: opts.body
|
body: opts.body
|
||||||
})
|
})
|
||||||
@@ -66,8 +66,8 @@ export class K8sClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateSecret (opts) {
|
async updateSecret (opts) {
|
||||||
const namespace = opts?.namespace || 'default'
|
const namespace = opts.namespace || 'default'
|
||||||
const secretName = opts?.secretName
|
const secretName = opts.secretName
|
||||||
if (secretName == null) {
|
if (secretName == null) {
|
||||||
throw new Error('secretName 不能为空')
|
throw new Error('secretName 不能为空')
|
||||||
}
|
}
|
||||||
@@ -77,8 +77,8 @@ export class K8sClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async patchSecret (opts) {
|
async patchSecret (opts) {
|
||||||
const namespace = opts?.namespace || 'default'
|
const namespace = opts.namespace || 'default'
|
||||||
const secretName = opts?.secretName
|
const secretName = opts.secretName
|
||||||
if (secretName == null) {
|
if (secretName == null) {
|
||||||
throw new Error('secretName 不能为空')
|
throw new Error('secretName 不能为空')
|
||||||
}
|
}
|
||||||
@@ -88,8 +88,8 @@ export class K8sClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getIngress (opts) {
|
async getIngress (opts) {
|
||||||
const namespace = opts?.namespace || 'default'
|
const namespace = opts.namespace || 'default'
|
||||||
const ingressName = opts?.ingressName
|
const ingressName = opts.ingressName
|
||||||
if (!ingressName) {
|
if (!ingressName) {
|
||||||
throw new Error('ingressName 不能为空')
|
throw new Error('ingressName 不能为空')
|
||||||
}
|
}
|
||||||
@@ -97,8 +97,8 @@ export class K8sClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async patchIngress (opts) {
|
async patchIngress (opts) {
|
||||||
const namespace = opts?.namespace || 'default'
|
const namespace = opts.namespace || 'default'
|
||||||
const ingressName = opts?.ingressName
|
const ingressName = opts.ingressName
|
||||||
if (!ingressName) {
|
if (!ingressName) {
|
||||||
throw new Error('ingressName 不能为空')
|
throw new Error('ingressName 不能为空')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 +0,0 @@
|
|||||||
import dnsProviders from './dns-provider/index.js'
|
|
||||||
export { DnsProviderFactory } from './dns-provider/dns-provider-factory.js'
|
|
||||||
|
|
||||||
export default dnsProviders
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash-es'
|
||||||
import optionsPrivate from './options.private.js'
|
import optionsPrivate from './options.private.js'
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
args: {
|
args: {
|
||||||
forceCert: false, // 强制更新证书
|
forceCert: false, // 强制更新证书
|
||||||
skipCert: false, // 是否跳过证书申请环节
|
skipCert: false, // 是否跳过证书申请环节
|
||||||
forceDeploy: false,
|
forceDeploy: true,
|
||||||
test: true
|
test: false
|
||||||
},
|
},
|
||||||
accessProviders: {
|
accessProviders: {
|
||||||
aliyun: {
|
aliyun: {
|
||||||
@@ -35,7 +35,10 @@ const defaultOptions = {
|
|||||||
cert: {
|
cert: {
|
||||||
domains: ['*.docmirror.cn'],
|
domains: ['*.docmirror.cn'],
|
||||||
email: 'xiaojunnuo@qq.com',
|
email: 'xiaojunnuo@qq.com',
|
||||||
dnsProvider: 'aliyun',
|
dnsProvider: {
|
||||||
|
type:'aliyun',
|
||||||
|
accessProvider:'aliyun'
|
||||||
|
},
|
||||||
csrInfo: {
|
csrInfo: {
|
||||||
country: 'CN',
|
country: 'CN',
|
||||||
state: 'GuangDong',
|
state: 'GuangDong',
|
||||||
@@ -48,7 +51,7 @@ const defaultOptions = {
|
|||||||
deploy: [
|
deploy: [
|
||||||
{
|
{
|
||||||
deployName: '流程1-部署到阿里云系列产品',
|
deployName: '流程1-部署到阿里云系列产品',
|
||||||
disabled: true,
|
disabled: false,
|
||||||
tasks: [
|
tasks: [
|
||||||
{
|
{
|
||||||
taskName: '上传证书到云',
|
taskName: '上传证书到云',
|
||||||
@@ -94,7 +97,7 @@ const defaultOptions = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
deployName: '流程3-触发jenkins任务',
|
deployName: '流程3-触发jenkins任务',
|
||||||
disabled: true,
|
disabled: false,
|
||||||
tasks: [
|
tasks: [
|
||||||
{
|
{
|
||||||
taskName: '触发jenkins任务',
|
taskName: '触发jenkins任务',
|
||||||
@@ -108,6 +111,7 @@ const defaultOptions = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
deployName: '流程4-部署到腾讯云ingress',
|
deployName: '流程4-部署到腾讯云ingress',
|
||||||
|
disabled: true,
|
||||||
tasks: [
|
tasks: [
|
||||||
{
|
{
|
||||||
taskName: '上传到腾讯云',
|
taskName: '上传到腾讯云',
|
||||||
|
|||||||
16
ui/certd-server/.eslintrc.cjs
Normal file
16
ui/certd-server/.eslintrc.cjs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaVersion: '2020'
|
||||||
|
},
|
||||||
|
parser: 'babel-eslint',
|
||||||
|
extends: ['standard'],
|
||||||
|
env: {
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
53
ui/certd-server/app.js
Normal file
53
ui/certd-server/app.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import Koa from 'koa'
|
||||||
|
import json from 'koa-json'
|
||||||
|
import onerror from 'koa-onerror'
|
||||||
|
import bodyparser from 'koa-bodyparser'
|
||||||
|
import logger from 'koa-logger'
|
||||||
|
import Static from 'koa-static'
|
||||||
|
import fs from 'fs'
|
||||||
|
import _ from 'lodash-es'
|
||||||
|
|
||||||
|
const app = new Koa()
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
onerror(app)
|
||||||
|
|
||||||
|
// middlewares
|
||||||
|
app.use(bodyparser({
|
||||||
|
enableTypes: ['json', 'form', 'text']
|
||||||
|
}))
|
||||||
|
app.use(json())
|
||||||
|
app.use(logger())
|
||||||
|
|
||||||
|
app.use(Static(new URL('public', import.meta.url).pathname))
|
||||||
|
|
||||||
|
// logger
|
||||||
|
app.use(async (ctx, next) => {
|
||||||
|
const start = new Date()
|
||||||
|
await next()
|
||||||
|
const ms = new Date() - start
|
||||||
|
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('url', import.meta.url)
|
||||||
|
|
||||||
|
// routes
|
||||||
|
const files = fs.readdirSync(new URL('controllers/', import.meta.url))
|
||||||
|
// 过滤出.js文件:
|
||||||
|
const jsFiles = files.filter((f) => {
|
||||||
|
return f.endsWith('.js')
|
||||||
|
})
|
||||||
|
|
||||||
|
_.forEach(jsFiles, async item => {
|
||||||
|
let mapping = await import(new URL('controllers/' + item, import.meta.url))
|
||||||
|
mapping = mapping.default
|
||||||
|
app.use(mapping.routes(), mapping.allowedMethods())
|
||||||
|
})
|
||||||
|
|
||||||
|
// error-handling
|
||||||
|
app.on('error', (err, ctx) => {
|
||||||
|
console.error('server error', err, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('http://localhost:3000/')
|
||||||
|
export default app
|
||||||
93
ui/certd-server/bin/www.js
Normal file
93
ui/certd-server/bin/www.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import app from '../app.js';
|
||||||
|
import debuger from 'debug'
|
||||||
|
const debug = debuger('demo:serer')
|
||||||
|
// require('debug')('demo:server');
|
||||||
|
import http from 'http';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port from environment and store in Express.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var port = normalizePort(process.env.PORT || '3000');
|
||||||
|
// app.set('port', port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTP server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var server = http.createServer(app.callback());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
server.on('error', onError);
|
||||||
|
server.on('listening', onListening);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize a port into a number, string, or false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalizePort(val) {
|
||||||
|
var port = parseInt(val, 10);
|
||||||
|
|
||||||
|
if (isNaN(port)) {
|
||||||
|
// named pipe
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port >= 0) {
|
||||||
|
// port number
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error.syscall !== 'listen') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bind = typeof port === 'string'
|
||||||
|
? 'Pipe ' + port
|
||||||
|
: 'Port ' + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case 'EACCES':
|
||||||
|
console.error(bind + ' requires elevated privileges');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case 'EADDRINUSE':
|
||||||
|
console.error(bind + ' is already in use');
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening() {
|
||||||
|
var addr = server.address();
|
||||||
|
var bind = typeof addr === 'string'
|
||||||
|
? 'pipe ' + addr
|
||||||
|
: 'port ' + addr.port;
|
||||||
|
debug('Listening on ' + bind);
|
||||||
|
}
|
||||||
|
|
||||||
19
ui/certd-server/controllers/access-providers.js
Normal file
19
ui/certd-server/controllers/access-providers.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Router from 'koa-router'
|
||||||
|
import { accessProviderRegistry } from '@certd/api'
|
||||||
|
import DefaultAccessProviders from '@certd/access-providers'
|
||||||
|
import _ from 'lodash-es'
|
||||||
|
import { Ret } from '../models/Ret.js'
|
||||||
|
const router = Router()
|
||||||
|
router.prefix('/access-providers')
|
||||||
|
|
||||||
|
DefaultAccessProviders.install()
|
||||||
|
|
||||||
|
router.get('/list', function (ctx, next) {
|
||||||
|
const list = []
|
||||||
|
_.forEach(accessProviderRegistry.collection, item => {
|
||||||
|
list.push(item.define())
|
||||||
|
})
|
||||||
|
ctx.body = Ret.success(list)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
19
ui/certd-server/controllers/dns-providers.js
Normal file
19
ui/certd-server/controllers/dns-providers.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Router from 'koa-router'
|
||||||
|
import { dnsProviderRegistry } from '@certd/api'
|
||||||
|
import DefaultDnsProviders from '@certd/dns-providers'
|
||||||
|
import _ from 'lodash-es'
|
||||||
|
import { Ret } from '../models/Ret.js'
|
||||||
|
const router = Router()
|
||||||
|
router.prefix('/dns-providers')
|
||||||
|
|
||||||
|
DefaultDnsProviders.install()
|
||||||
|
|
||||||
|
router.get('/list', function (ctx, next) {
|
||||||
|
const list = []
|
||||||
|
_.forEach(dnsProviderRegistry.collection, item => {
|
||||||
|
list.push(item.define())
|
||||||
|
})
|
||||||
|
ctx.body = Ret.success(list)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
23
ui/certd-server/controllers/exports.js
Normal file
23
ui/certd-server/controllers/exports.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import Router from 'koa-router'
|
||||||
|
import fs from 'fs'
|
||||||
|
import exportsService from '../service/exports-service.js'
|
||||||
|
// import executorPkg from '@certd/executor/package.json'
|
||||||
|
const router = Router()
|
||||||
|
router.prefix('/exports')
|
||||||
|
|
||||||
|
router.post('/toZip', async function (ctx, next) {
|
||||||
|
// const request = ctx.request
|
||||||
|
// const query = request.query
|
||||||
|
const body = ctx.request.body
|
||||||
|
// const req_queryString = request.queryString
|
||||||
|
const { zipPath, fileName } = await exportsService.exportsToZip(body, 'certd-run')
|
||||||
|
|
||||||
|
console.log('zipFile', zipPath)
|
||||||
|
ctx.set('Content-disposition', 'attachment;filename=' + fileName)
|
||||||
|
ctx.set('Content-Type', 'application/zip')
|
||||||
|
ctx.body = fs.createReadStream(zipPath)
|
||||||
|
//
|
||||||
|
// // ctx.body = Ret.success(zipPath)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
10
ui/certd-server/controllers/index.js
Normal file
10
ui/certd-server/controllers/index.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import Router from 'koa-router'
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
router.get('/', async (ctx, next) => {
|
||||||
|
await ctx.render('index', {
|
||||||
|
title: 'Hello CertD!'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
19
ui/certd-server/controllers/plugins.js
Normal file
19
ui/certd-server/controllers/plugins.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Router from 'koa-router'
|
||||||
|
import { pluginRegistry } from '@certd/api'
|
||||||
|
import DefaultPlugins from '@certd/plugins'
|
||||||
|
import _ from 'lodash-es'
|
||||||
|
import { Ret } from '../models/Ret.js'
|
||||||
|
const router = Router()
|
||||||
|
router.prefix('/plugins')
|
||||||
|
|
||||||
|
DefaultPlugins.install()
|
||||||
|
|
||||||
|
router.get('/list', function (ctx, next) {
|
||||||
|
const list = []
|
||||||
|
_.forEach(pluginRegistry.collection, item => {
|
||||||
|
list.push(item.define())
|
||||||
|
})
|
||||||
|
ctx.body = Ret.success(list)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
15
ui/certd-server/models/Ret.js
Normal file
15
ui/certd-server/models/Ret.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
export class Ret {
|
||||||
|
constructor (code = 0, msg, data) {
|
||||||
|
this.code = code
|
||||||
|
this.msg = msg
|
||||||
|
this.data = data
|
||||||
|
}
|
||||||
|
|
||||||
|
static success (data) {
|
||||||
|
return new Ret(0, '', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
static error (msg) {
|
||||||
|
return new Ret(1, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
5684
ui/certd-server/package-lock.json
generated
Normal file
5684
ui/certd-server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
41
ui/certd-server/package.json
Normal file
41
ui/certd-server/package.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "@certd/server",
|
||||||
|
"version": "0.1.12",
|
||||||
|
"private": false,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node bin/www.js",
|
||||||
|
"dev": "./node_modules/.bin/nodemon bin/www.js",
|
||||||
|
"prd": "pm2 start bin/www.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@certd/access-providers": "^0.1.12",
|
||||||
|
"@certd/api": "^0.1.12",
|
||||||
|
"@certd/dns-providers": "^0.1.12",
|
||||||
|
"@certd/executor": "^0.1.12",
|
||||||
|
"@certd/plugins": "^0.1.12",
|
||||||
|
"compressing": "^1.5.1",
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"fs-extra": "^9.1.0",
|
||||||
|
"koa": "^2.7.0",
|
||||||
|
"koa-bodyparser": "^4.2.1",
|
||||||
|
"koa-convert": "^1.2.0",
|
||||||
|
"koa-json": "^2.0.2",
|
||||||
|
"koa-logger": "^3.2.0",
|
||||||
|
"koa-onerror": "^4.1.0",
|
||||||
|
"koa-router": "^7.4.0",
|
||||||
|
"koa-static": "^5.0.0",
|
||||||
|
"koa-views": "^6.2.0",
|
||||||
|
"lodash-es": "^4.17.20"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"eslint": "^7.18.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",
|
||||||
|
"nodemon": "^1.19.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
ui/certd-server/public/stylesheets/style.css
Normal file
8
ui/certd-server/public/stylesheets/style.css
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
body {
|
||||||
|
padding: 50px;
|
||||||
|
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #00B7FF;
|
||||||
|
}
|
||||||
39
ui/certd-server/service/exports-service.js
Normal file
39
ui/certd-server/service/exports-service.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import os from 'os'
|
||||||
|
import fs from 'fs-extra'
|
||||||
|
import pathUtil from '../utils/util.path.js'
|
||||||
|
import cryptoRandomString from 'crypto-random-string'
|
||||||
|
import zipUtil from '../utils/util.zip.js'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async exportsToZip (options, dirName) {
|
||||||
|
const tempDir = os.tmpdir()
|
||||||
|
const targetDir = path.join(tempDir, 'certd-server', cryptoRandomString(10))
|
||||||
|
const projectName = dirName
|
||||||
|
const targetProjectDir = path.join(targetDir, projectName)
|
||||||
|
const templateDir = pathUtil.join('templates/' + projectName)
|
||||||
|
|
||||||
|
console.log('targetDir', targetDir)
|
||||||
|
console.log('projectName', projectName)
|
||||||
|
console.log('tempalteDir', templateDir)
|
||||||
|
console.log('targetProjectDir', targetProjectDir)
|
||||||
|
fs.copySync(templateDir, targetProjectDir)
|
||||||
|
|
||||||
|
// const packageFilePath = path.join(targetProjectDir, 'package.json')
|
||||||
|
const optionsFilePath = path.join(targetProjectDir, 'options.json')
|
||||||
|
|
||||||
|
fs.writeJsonSync(optionsFilePath, options)
|
||||||
|
|
||||||
|
const zipName = dirName + '.zip'
|
||||||
|
const outputFilePath = path.join(targetDir, zipName)
|
||||||
|
|
||||||
|
console.log('outputFilePath', outputFilePath)
|
||||||
|
await zipUtil.compress({ dir: targetProjectDir, output: outputFilePath })
|
||||||
|
return {
|
||||||
|
dir: targetDir,
|
||||||
|
fileName: zipName,
|
||||||
|
zipPath: outputFilePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
4
ui/certd-server/templates/certd-run/index.js
Normal file
4
ui/certd-server/templates/certd-run/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { Executor } from '@certd/executor'
|
||||||
|
import options from './options.json'
|
||||||
|
const executor = new Executor()
|
||||||
|
executor.run(options)
|
||||||
1
ui/certd-server/templates/certd-run/options.json
Normal file
1
ui/certd-server/templates/certd-run/options.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
19
ui/certd-server/templates/certd-run/package.json
Normal file
19
ui/certd-server/templates/certd-run/package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "certd-run",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "certd run",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"certd": "node index.js"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/certd/certd"
|
||||||
|
},
|
||||||
|
"author": "greper",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@certd/executor": "^0.1.11"
|
||||||
|
}
|
||||||
|
}
|
||||||
12
ui/certd-server/utils/util.path.js
Normal file
12
ui/certd-server/utils/util.path.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import os from 'os'
|
||||||
|
export default {
|
||||||
|
join (...dirs) {
|
||||||
|
const url = new URL('../' + dirs.join('/'), import.meta.url)
|
||||||
|
console.log('url', url)
|
||||||
|
let path = url.pathname
|
||||||
|
if (os.type() === 'Windows_NT') {
|
||||||
|
path = path.substring(1)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
8
ui/certd-server/utils/util.zip.js
Normal file
8
ui/certd-server/utils/util.zip.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import compressing from 'compressing'
|
||||||
|
export default {
|
||||||
|
compress ({
|
||||||
|
dir, output
|
||||||
|
}) {
|
||||||
|
return compressing.zip.compressDir(dir, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
3
ui/certd-ui/.browserslistrc
Normal file
3
ui/certd-ui/.browserslistrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
> 1%
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
||||||
5
ui/certd-ui/.editorconfig
Normal file
5
ui/certd-ui/.editorconfig
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[*.{js,jsx,ts,tsx,vue}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
28
ui/certd-ui/.eslintrc.js
Normal file
28
ui/certd-ui/.eslintrc.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/vue3-essential',
|
||||||
|
'@vue/standard'
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
parser: 'babel-eslint'
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'**/__tests__/*.{j,t}s?(x)',
|
||||||
|
'**/tests/unit/**/*.spec.{j,t}s?(x)'
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
mocha: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
29
ui/certd-ui/README.md
Normal file
29
ui/certd-ui/README.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# certd-ui
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and hot-reloads for development
|
||||||
|
```
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and minifies for production
|
||||||
|
```
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run your unit tests
|
||||||
|
```
|
||||||
|
npm run test:unit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lints and fixes files
|
||||||
|
```
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||||
5
ui/certd-ui/babel.config.js
Normal file
5
ui/certd-ui/babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
||||||
19016
ui/certd-ui/package-lock.json
generated
Normal file
19016
ui/certd-ui/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
60
ui/certd-ui/package.json
Normal file
60
ui/certd-ui/package.json
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"name": "@certd/certd-ui",
|
||||||
|
"version": "0.1.12",
|
||||||
|
"private": false,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"test:unit": "vue-cli-service test:unit",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@certd/dns-providers": "^0.1.12",
|
||||||
|
"@certd/plugins": "^0.1.12",
|
||||||
|
"ant-design-vue": "^2.0.0-rc.8",
|
||||||
|
"core-js": "^3.8.1",
|
||||||
|
"lodash-es": "^4.17.20",
|
||||||
|
"vue": "^3.0.4",
|
||||||
|
"vue-i18n": "^9.0.0-rc.2",
|
||||||
|
"vue-json-editor": "^1.4.2",
|
||||||
|
"vue-router": "^4.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.12.10",
|
||||||
|
"@babel/eslint-parser": "^7.12.1",
|
||||||
|
"@vue/cli-plugin-babel": "~5.0.0-alpha.3",
|
||||||
|
"@vue/cli-plugin-eslint": "~5.0.0-alpha.3",
|
||||||
|
"@vue/cli-plugin-router": "~5.0.0-alpha.3",
|
||||||
|
"@vue/cli-plugin-unit-mocha": "~5.0.0-alpha.3",
|
||||||
|
"@vue/cli-plugin-webpack-4": "^5.0.0-alpha.3",
|
||||||
|
"@vue/cli-service": "~5.0.0-alpha.3",
|
||||||
|
"@vue/compiler-sfc": "^3.0.4",
|
||||||
|
"@vue/eslint-config-standard": "^6.0.0",
|
||||||
|
"@vue/test-utils": "^2.0.0-0",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"chai": "^4.2.0",
|
||||||
|
"eslint": "^7.15.0",
|
||||||
|
"eslint-plugin-import": "^2.20.2",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"eslint-plugin-standard": "^5.0.0",
|
||||||
|
"eslint-plugin-vue": "^7.2.0",
|
||||||
|
"less": "^3.0.4",
|
||||||
|
"less-loader": "^5.0.0",
|
||||||
|
"lint-staged": "^9.5.0",
|
||||||
|
"postcss": "^8.2.4",
|
||||||
|
"webpack": "^4.0.0"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"@vue/cli-*/webpack": "^4.0.0"
|
||||||
|
},
|
||||||
|
"gitHooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,jsx,vue}": [
|
||||||
|
"vue-cli-service lint",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
ui/certd-ui/public/favicon.ico
Normal file
BIN
ui/certd-ui/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
ui/certd-ui/public/images/plugin.png
Normal file
BIN
ui/certd-ui/public/images/plugin.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
BIN
ui/certd-ui/public/images/provider.png
Normal file
BIN
ui/certd-ui/public/images/provider.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.5 KiB |
17
ui/certd-ui/public/index.html
Normal file
17
ui/certd-ui/public/index.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
41
ui/certd-ui/src/App.vue
Normal file
41
ui/certd-ui/src/App.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<a-config-provider :locale="locale">
|
||||||
|
|
||||||
|
<a-layout class="page-layout">
|
||||||
|
<a-layout-header>Cert-D</a-layout-header>
|
||||||
|
<a-layout style="flex:1">
|
||||||
|
<router-view/>
|
||||||
|
</a-layout>
|
||||||
|
<a-layout-footer>
|
||||||
|
by greper
|
||||||
|
</a-layout-footer>
|
||||||
|
|
||||||
|
</a-layout>
|
||||||
|
|
||||||
|
</a-config-provider>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import zhCN from 'ant-design-vue/es/locale/zh_CN'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
locale: zhCN
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup () {
|
||||||
|
const { t } = useI18n() // call `useI18n`, and spread `t` from `useI18n` returning
|
||||||
|
return { t } // return render context that included `t`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.page-layout{
|
||||||
|
height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
.ant-layout-header{
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
14
ui/certd-ui/src/api/api.access-providers.js
Normal file
14
ui/certd-ui/src/api/api.access-providers.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { request } from './service'
|
||||||
|
import inputHandler from '@/api/util.input.handler'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async list () {
|
||||||
|
const ret = await request({
|
||||||
|
url: '/access-providers/list'
|
||||||
|
})
|
||||||
|
|
||||||
|
inputHandler.handle(ret)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
14
ui/certd-ui/src/api/api.dns-providers.js
Normal file
14
ui/certd-ui/src/api/api.dns-providers.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { request } from './service'
|
||||||
|
import inputHandler from '@/api/util.input.handler'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async list () {
|
||||||
|
const ret = await request({
|
||||||
|
url: '/dns-providers/list'
|
||||||
|
})
|
||||||
|
|
||||||
|
inputHandler.handle(ret)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
25
ui/certd-ui/src/api/api.exports.js
Normal file
25
ui/certd-ui/src/api/api.exports.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { request } from './service'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
exportsToZip (options) {
|
||||||
|
return request({
|
||||||
|
url: '/exports/toZip',
|
||||||
|
data: { options },
|
||||||
|
method: 'post',
|
||||||
|
responseType: 'blob' // 重点在于配置responseType: 'blob'
|
||||||
|
}).then(res => {
|
||||||
|
console.log('res', res)
|
||||||
|
const filename = decodeURI(res.headers['content-disposition'].replace('attachment;filename=', '')) // 由后端设置下载文件名
|
||||||
|
const blob = new Blob([res.data], { type: 'application/zip' })
|
||||||
|
const a = document.createElement('a')
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
a.href = url
|
||||||
|
a.download = filename
|
||||||
|
const body = document.getElementsByTagName('body')[0]
|
||||||
|
body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
body.removeChild(a)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
14
ui/certd-ui/src/api/api.plugins.js
Normal file
14
ui/certd-ui/src/api/api.plugins.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { request } from './service'
|
||||||
|
import inputHandler from './util.input.handler'
|
||||||
|
export default {
|
||||||
|
async list () {
|
||||||
|
const ret = await request({
|
||||||
|
url: '/plugins/list'
|
||||||
|
})
|
||||||
|
|
||||||
|
inputHandler.handle(ret)
|
||||||
|
|
||||||
|
console.log('plugins', ret)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
10
ui/certd-ui/src/api/index.js
Normal file
10
ui/certd-ui/src/api/index.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { assign, map } from 'lodash'
|
||||||
|
import { service, request } from './service'
|
||||||
|
|
||||||
|
const files = require.context('./modules', false, /\.js$/)
|
||||||
|
const generators = files.keys().map(key => files(key).default)
|
||||||
|
|
||||||
|
export default assign({}, ...map(generators, generator => generator({
|
||||||
|
service,
|
||||||
|
request
|
||||||
|
})))
|
||||||
94
ui/certd-ui/src/api/service.js
Normal file
94
ui/certd-ui/src/api/service.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import { get } from 'lodash-es'
|
||||||
|
import { errorLog, errorCreate } from './tools'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 创建请求实例
|
||||||
|
*/
|
||||||
|
function createService () {
|
||||||
|
// 创建一个 axios 实例
|
||||||
|
const service = axios.create()
|
||||||
|
// 请求拦截
|
||||||
|
service.interceptors.request.use(
|
||||||
|
config => config,
|
||||||
|
error => {
|
||||||
|
// 发送失败
|
||||||
|
console.log(error)
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 响应拦截
|
||||||
|
service.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
console.log('response.config', response.config)
|
||||||
|
if (response.config.responseType === 'blob') {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
// dataAxios 是 axios 返回数据中的 data
|
||||||
|
const dataAxios = response.data
|
||||||
|
// 这个状态码是和后端约定的
|
||||||
|
const { code } = dataAxios
|
||||||
|
// 根据 code 进行判断
|
||||||
|
if (code === undefined) {
|
||||||
|
// 如果没有 code 代表这不是项目后端开发的接口 比如可能是 D2Admin 请求最新版本
|
||||||
|
if (response.config.unpack) {
|
||||||
|
return dataAxios
|
||||||
|
}
|
||||||
|
return dataAxios.data
|
||||||
|
} else {
|
||||||
|
// 有 code 代表这是一个后端接口 可以进行进一步的判断
|
||||||
|
switch (code) {
|
||||||
|
case 0:
|
||||||
|
// [ 示例 ] code === 0 代表没有错误
|
||||||
|
return dataAxios.data
|
||||||
|
default:
|
||||||
|
// 不是正确的 code
|
||||||
|
errorCreate(`${dataAxios.msg}: ${response.config.url}`)
|
||||||
|
return dataAxios
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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
|
||||||
|
}
|
||||||
|
errorLog(error)
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return service
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 创建请求方法
|
||||||
|
* @param {Object} service axios 实例
|
||||||
|
*/
|
||||||
|
function createRequestFunction (service) {
|
||||||
|
return function (config) {
|
||||||
|
const configDefault = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': get(config, 'headers.Content-Type', 'application/json')
|
||||||
|
},
|
||||||
|
timeout: 5000,
|
||||||
|
baseURL: process.env.VUE_APP_API,
|
||||||
|
data: {}
|
||||||
|
}
|
||||||
|
return service(Object.assign(configDefault, config))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用于真实网络请求的实例和请求方法
|
||||||
|
export const service = createService()
|
||||||
|
export const request = createRequestFunction(service)
|
||||||
73
ui/certd-ui/src/api/tools.js
Normal file
73
ui/certd-ui/src/api/tools.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { notification } from 'ant-design-vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 安全地解析 json 字符串
|
||||||
|
* @param {String} jsonString 需要解析的 json 字符串
|
||||||
|
* @param {String} defaultValue 默认值
|
||||||
|
*/
|
||||||
|
export function parse (jsonString = '{}', defaultValue = {}) {
|
||||||
|
let result = defaultValue
|
||||||
|
try {
|
||||||
|
result = JSON.parse(jsonString)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 接口请求返回
|
||||||
|
* @param {Any} data 返回值
|
||||||
|
* @param {String} msg 状态信息
|
||||||
|
* @param {Number} code 状态码
|
||||||
|
*/
|
||||||
|
export function response (data = {}, msg = '', code = 0) {
|
||||||
|
return [
|
||||||
|
200,
|
||||||
|
{ code, msg, data }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 接口请求返回 正确返回
|
||||||
|
* @param {Any} data 返回值
|
||||||
|
* @param {String} msg 状态信息
|
||||||
|
*/
|
||||||
|
export function responseSuccess (data = {}, msg = '成功') {
|
||||||
|
return response(data, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 接口请求返回 错误返回
|
||||||
|
* @param {Any} data 返回值
|
||||||
|
* @param {String} msg 状态信息
|
||||||
|
* @param {Number} code 状态码
|
||||||
|
*/
|
||||||
|
export function responseError (data = {}, msg = '请求失败', code = 500) {
|
||||||
|
return response(data, msg, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 记录和显示错误
|
||||||
|
* @param {Error} error 错误对象
|
||||||
|
*/
|
||||||
|
export function errorLog (error) {
|
||||||
|
// 打印到控制台
|
||||||
|
console.log(error)
|
||||||
|
// 显示提示
|
||||||
|
notification({
|
||||||
|
message: error.message,
|
||||||
|
type: 'error',
|
||||||
|
duration: 5 * 1000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 创建一个错误
|
||||||
|
* @param {String} msg 错误信息
|
||||||
|
*/
|
||||||
|
export function errorCreate (msg) {
|
||||||
|
const error = new Error(msg)
|
||||||
|
errorLog(error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
37
ui/certd-ui/src/api/util.input.handler.js
Normal file
37
ui/certd-ui/src/api/util.input.handler.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import _ from 'lodash-es'
|
||||||
|
|
||||||
|
function handleInputs (inputs) {
|
||||||
|
if (inputs == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_.forEach(inputs, (item, key) => {
|
||||||
|
if (item.required === true) {
|
||||||
|
if (item.component == null) {
|
||||||
|
item.component = {}
|
||||||
|
}
|
||||||
|
let rules = item.component.rules
|
||||||
|
if (rules == null) {
|
||||||
|
item.component.rules = rules = []
|
||||||
|
}
|
||||||
|
if (rules.length > 0) {
|
||||||
|
const hasRequired = rules.filter(rule => {
|
||||||
|
return rule.required === true
|
||||||
|
})
|
||||||
|
if (hasRequired.length > 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rules.push({ required: true, message: '该项必填' })
|
||||||
|
delete item.required
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
|
||||||
|
handle (list) {
|
||||||
|
_.forEach(list, item => {
|
||||||
|
handleInputs(item.input)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
BIN
ui/certd-ui/src/assets/logo.png
Normal file
BIN
ui/certd-ui/src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,287 @@
|
|||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
title="授权管理"
|
||||||
|
placement="right"
|
||||||
|
:closable="true"
|
||||||
|
width="500px"
|
||||||
|
v-model:visible="visible"
|
||||||
|
:after-visible-change="onAfterVisibleChange"
|
||||||
|
>
|
||||||
|
<div class="d-container access-provider-manager">
|
||||||
|
<a-button @click="add">
|
||||||
|
添加授权
|
||||||
|
</a-button>
|
||||||
|
<a-list
|
||||||
|
class="list"
|
||||||
|
item-layout="horizontal"
|
||||||
|
:data-source="getProviders()"
|
||||||
|
>
|
||||||
|
<template #renderItem="{ item ,index }">
|
||||||
|
<a-list-item>
|
||||||
|
<template #actions>
|
||||||
|
<a-button type="primary" @click="openEdit(item,index)"><template #icon><EditOutlined /></template></a-button>
|
||||||
|
<a-button type="danger" @click="remove(item,index)"><template #icon ><DeleteOutlined /></template></a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-radio :disabled="isDisabled(item)" :checked="item.key===selectedKey" @update:checked="selectedKey = item.key" >
|
||||||
|
{{ item.name }} ({{item.type}})
|
||||||
|
</a-radio>
|
||||||
|
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a-button @click="onProviderSelectSubmit">确定</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
|
|
||||||
|
<a-modal v-model:visible="editVisible" dialogClass="d-dialog" width="700px" title="编辑授权" @ok="onSubmit">
|
||||||
|
|
||||||
|
<a-alert v-if="currentProvider?.desc" :message="currentProvider.desc" type="success" />
|
||||||
|
|
||||||
|
<a-form ref="formRef" class="domain-form" :model="formData" labelWidth="150px" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||||
|
<a-form-item label="类型" name="type" :rules="rules.type">
|
||||||
|
<a-radio-group :disabled="editIndex!=null" v-model:value="formData.type" @change="onTypeChanged" >
|
||||||
|
<a-radio-button v-for="(option) of providerDefineList" :disabled="isDisabled(option,'name')" :key="option.name" :value="option.name">
|
||||||
|
{{option.label}}
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<template v-if="formData.type && currentProvider">
|
||||||
|
<a-form-item label="名称" name="name" :rules="rules.name">
|
||||||
|
<a-input v-model:value="formData.name"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item v-for="(item,key,index) in currentProvider.input"
|
||||||
|
:key="index"
|
||||||
|
v-bind="item.component||{}"
|
||||||
|
:label="item.label || key"
|
||||||
|
:name="key">
|
||||||
|
<component-render v-model:value="formData[key]" v-bind="item.component || {}"></component-render>
|
||||||
|
<template #extra >
|
||||||
|
<div v-if="item.desc" class="helper">{{item.desc}}</div>
|
||||||
|
</template>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, reactive, nextTick, watch, inject } from 'vue'
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
import { useForm } from '@ant-design-vue/use'
|
||||||
|
import _ from 'lodash-es'
|
||||||
|
import providerApi from '@/api/api.access-providers'
|
||||||
|
function useEdit (props, context, onEditSave) {
|
||||||
|
const formData = reactive({
|
||||||
|
key: '',
|
||||||
|
name: '',
|
||||||
|
type: undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = ref({
|
||||||
|
type: [{
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
message: '请选择类型'
|
||||||
|
}],
|
||||||
|
name: [{
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
// const { resetFields, validate, validateInfos } = useForm(formData, rules)
|
||||||
|
const onSubmit = async e => {
|
||||||
|
e.preventDefault()
|
||||||
|
const res = await formRef.value.validate()
|
||||||
|
console.log('validation:', res)
|
||||||
|
const newProvider = _.cloneDeep(formData)
|
||||||
|
onEditSave(newProvider, editIndex.value)
|
||||||
|
closeEdit()
|
||||||
|
}
|
||||||
|
|
||||||
|
const editVisible = ref(false)
|
||||||
|
const editIndex = ref(null)
|
||||||
|
const openEdit = (item, index) => {
|
||||||
|
if (item) {
|
||||||
|
editIndex.value = index
|
||||||
|
_.forEach(formData, (value, key) => {
|
||||||
|
formData[key] = null
|
||||||
|
})
|
||||||
|
_.merge(formData, item)
|
||||||
|
changeType(item.type)
|
||||||
|
} else {
|
||||||
|
editIndex.value = null
|
||||||
|
formData.type = null
|
||||||
|
}
|
||||||
|
editVisible.value = true
|
||||||
|
}
|
||||||
|
const add = () => {
|
||||||
|
openEdit()
|
||||||
|
}
|
||||||
|
const closeEdit = () => {
|
||||||
|
editVisible.value = false
|
||||||
|
}
|
||||||
|
const providerDefineList = ref([])
|
||||||
|
const onCreated = async () => {
|
||||||
|
providerDefineList.value = await providerApi.list()
|
||||||
|
}
|
||||||
|
onCreated()
|
||||||
|
const currentProvider = ref(null)
|
||||||
|
const onTypeChanged = (e) => {
|
||||||
|
const value = e.target.value
|
||||||
|
changeType(value)
|
||||||
|
// 遍历input 设置到form rules
|
||||||
|
}
|
||||||
|
const changeType = (type) => {
|
||||||
|
if (providerDefineList.value == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (const item of providerDefineList.value) {
|
||||||
|
if (item.name === type) {
|
||||||
|
currentProvider.value = item
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editIndex.value == null) {
|
||||||
|
formData.key = currentProvider.value.name
|
||||||
|
formData.name = currentProvider.value.label || currentProvider.value.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDisabled = (item, keyName = 'type') => {
|
||||||
|
if (!props.filter) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return item[keyName
|
||||||
|
] !== props.filter
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
labelCol: { span: 6 },
|
||||||
|
wrapperCol: { span: 16 },
|
||||||
|
formData,
|
||||||
|
onSubmit,
|
||||||
|
rules,
|
||||||
|
editVisible,
|
||||||
|
formRef,
|
||||||
|
currentProvider,
|
||||||
|
providerDefineList,
|
||||||
|
editIndex,
|
||||||
|
openEdit,
|
||||||
|
onTypeChanged,
|
||||||
|
add,
|
||||||
|
isDisabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = 0
|
||||||
|
const keyPrefix = 'provider_'
|
||||||
|
function generateNewKey (list) {
|
||||||
|
index++
|
||||||
|
let exists = false
|
||||||
|
for (const item of list) {
|
||||||
|
if (item.key === keyPrefix + index) {
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exists) {
|
||||||
|
return generateNewKey(list)
|
||||||
|
}
|
||||||
|
return keyPrefix + index
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
name: 'provider-manager',
|
||||||
|
props: {
|
||||||
|
value: {},
|
||||||
|
filter: {}
|
||||||
|
},
|
||||||
|
emits: ['update:value'],
|
||||||
|
setup (props, context) {
|
||||||
|
const visible = ref(false)
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const onAfterVisibleChange = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProviders = inject('get:accessProviders')
|
||||||
|
// const providerList = ref([])
|
||||||
|
const selectedKey = ref(null)
|
||||||
|
|
||||||
|
watch(() => props.value, () => {
|
||||||
|
selectedKey.value = props.value
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
const onEditSave = (newProvider, editIndex) => {
|
||||||
|
const providerList = getProviders()
|
||||||
|
if (editIndex == null) {
|
||||||
|
// add 生成一个key
|
||||||
|
newProvider.key = generateNewKey(providerList)
|
||||||
|
providerList.push(newProvider)
|
||||||
|
} else {
|
||||||
|
_.merge(providerList[editIndex], newProvider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const editModule = useEdit(props, context, onEditSave)
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
visible.value = true
|
||||||
|
const providerList = getProviders()
|
||||||
|
if (providerList.length === 0) {
|
||||||
|
nextTick(() => {
|
||||||
|
editModule.add()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const remove = (item, index) => {
|
||||||
|
const providerList = getProviders()
|
||||||
|
providerList.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateProviders = inject('update:accessProviders')
|
||||||
|
|
||||||
|
// watch(() => providers, () => {
|
||||||
|
// providerList.value = _.cloneDeep(props.providers || [])
|
||||||
|
// }, { immediate: true })
|
||||||
|
|
||||||
|
const onProviderSelectSubmit = () => {
|
||||||
|
const providerList = getProviders()
|
||||||
|
updateProviders(providerList)
|
||||||
|
context.emit('update:value', selectedKey.value)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
visible,
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
onAfterVisibleChange,
|
||||||
|
remove,
|
||||||
|
selectedKey,
|
||||||
|
onProviderSelectSubmit,
|
||||||
|
getProviders,
|
||||||
|
...editModule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.access-provider-manager{
|
||||||
|
padding:10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<div class="access-provider-selector">
|
||||||
|
<a-select
|
||||||
|
:value="value"
|
||||||
|
@update:value="valueUpdate"
|
||||||
|
placeholder="没有可选时请点右边按钮添加"
|
||||||
|
>
|
||||||
|
<a-select-option v-for="item of getProviders()" :key="item.key" :value="item.key" :disabled="isDisabled(item)">
|
||||||
|
{{ item.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-button class="suffix" @click="providerManagerOpen">
|
||||||
|
添加授权
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<access-provider-manager ref="providerManagerRef"
|
||||||
|
:value="value"
|
||||||
|
:filter="filter"
|
||||||
|
@update:value="valueUpdate"
|
||||||
|
></access-provider-manager>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ref, inject } from 'vue'
|
||||||
|
import AccessProviderManager from './access-provider-manager'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'access-provider-selector',
|
||||||
|
components: { AccessProviderManager },
|
||||||
|
emits: ['update:providers', 'update:value'],
|
||||||
|
// 属性定义
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
filter: {}
|
||||||
|
},
|
||||||
|
setup (props, context) {
|
||||||
|
const providerManagerRef = ref(null)
|
||||||
|
const providerManagerOpen = () => {
|
||||||
|
console.log('providerManagerRef', providerManagerRef)
|
||||||
|
if (providerManagerRef.value) {
|
||||||
|
providerManagerRef.value.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const providersUpdate = (val) => {
|
||||||
|
console.log('accessUpdate', val)
|
||||||
|
context.emit('update:providers', val)
|
||||||
|
}
|
||||||
|
const valueUpdate = (val) => {
|
||||||
|
context.emit('update:value', val)
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDisabled = (item) => {
|
||||||
|
if (!props.filter) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return item.type !== props.filter
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProviders = inject('get:accessProviders')
|
||||||
|
|
||||||
|
return {
|
||||||
|
providersUpdate,
|
||||||
|
valueUpdate,
|
||||||
|
providerManagerOpen,
|
||||||
|
providerManagerRef,
|
||||||
|
isDisabled,
|
||||||
|
getProviders
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less">
|
||||||
|
.access-provider-selector{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.ant-select{
|
||||||
|
flex:1;
|
||||||
|
}
|
||||||
|
.suffix{
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left:5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
33
ui/certd-ui/src/components/component-render.vue
Normal file
33
ui/certd-ui/src/components/component-render.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<script>
|
||||||
|
import { h, resolveComponent } from 'vue'
|
||||||
|
import _ from 'lodash-es'
|
||||||
|
export default {
|
||||||
|
name: 'component-render',
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: 'a-input'
|
||||||
|
},
|
||||||
|
children: {
|
||||||
|
type: Array
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
type: Object
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup (props, context) {
|
||||||
|
const attrs = {
|
||||||
|
...context.$attrs
|
||||||
|
}
|
||||||
|
_.forEach(props.on, (value, key) => {
|
||||||
|
attrs[key] = value
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
// eslint-disable-next-line no-eval
|
||||||
|
attrs[key] = eval(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const comp = resolveComponent(props.name)
|
||||||
|
return () => h(comp, context.$attrs, props.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
56
ui/certd-ui/src/components/d-container.vue
Normal file
56
ui/certd-ui/src/components/d-container.vue
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<div class="d-container">
|
||||||
|
<div class="box">
|
||||||
|
<div class="inner">
|
||||||
|
<div class="header">
|
||||||
|
<slot name="header"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="body">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'd-container'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.d-container{
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
.box {
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
.inner{
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.header{
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.body{
|
||||||
|
overflow-y: auto;
|
||||||
|
flex:1
|
||||||
|
}
|
||||||
|
.footer{
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user