refactor: 重构优化

This commit is contained in:
xiaojunnuo
2021-02-04 18:44:16 +08:00
parent a39dac4dbd
commit a25a15ca6e
59 changed files with 3903 additions and 967 deletions

View File

@@ -1,5 +1,5 @@
{
"name": "@certd/providers",
"name": "@certd/dns-providers",
"version": "0.1.11",
"lockfileVersion": 1,
"requires": true,

View File

@@ -0,0 +1,23 @@
{
"name": "@certd/access-providers",
"version": "0.1.11",
"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.11",
"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"
}
}

View 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)
})
}
}

View 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 () {
}
}

View 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 () {
}
}

View 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 () {
}
}

View File

@@ -0,0 +1,2 @@
import { Registry } from '../registry/registry.js'
export const accessProviderRegistry = new Registry()

View File

@@ -1,8 +1,9 @@
import _ from 'lodash-es'
import logger from '../utils/util.log.js'
export class AbstractDnsProvider {
constructor () {
constructor ({ accessProviders }) {
this.logger = logger
this.accessProviders = accessProviders
}
async createRecord ({ fullRecord, type, value }) {
@@ -31,4 +32,11 @@ export class AbstractDnsProvider {
}
return domain
}
getAccessProvider (accessProvider, accessProviders = this.accessProviders) {
if (typeof accessProvider === 'string' && accessProviders) {
accessProvider = accessProviders[accessProvider]
}
return accessProvider
}
}

View File

@@ -1,2 +1,3 @@
export { providerRegistry } from './provider-registry.js'
export { AbstractDnsProvider } from './abstract-provider.js'
import { Registry } from '../registry/registry.js'
export { AbstractDnsProvider } from './abstract-dns-provider.js'
export const dnsProviderRegistry = new Registry()

View File

@@ -1,26 +0,0 @@
export class ProviderRegistry {
constructor () {
this.providers = {}
}
install (provider) {
if (provider == null) {
return
}
if (this.providers == null) {
this.providers = {}
}
const name = provider.name || (provider.define && provider.define.name)
this.providers[name] = provider
}
get (name) {
if (name) {
return this.providers[name]
}
throw new Error(`找不到授权提供者:${name}`)
}
}
export const providerRegistry = new ProviderRegistry()

View File

@@ -1,4 +1,5 @@
export * from './dns-provider/index.js'
export * from './plugin/index.js'
export * from './access-provider/index.js'
export { Store } from './store/store.js'
export { util } from './utils/index.js'

View File

@@ -3,8 +3,6 @@ import logger from '../utils/util.log.js'
import dayjs from 'dayjs'
import Sleep from '../utils/util.sleep.js'
import { pluginRegistry } from './plugin-registry.js'
export class AbstractPlugin {
constructor ({ accessProviders }) {
this.logger = logger
@@ -55,7 +53,7 @@ export class AbstractPlugin {
}
/**
* 回退,如有必要
* 回退,用于单元测试
* @param options
*/
async rollback (options) {

View File

@@ -1,2 +1,3 @@
export { pluginRegistry } from './plugin-registry.js'
import { Registry } from '../registry/registry.js'
export { AbstractPlugin } from './abstract-plugin.js'
export const pluginRegistry = new Registry()

View File

@@ -1,27 +0,0 @@
export class PluginRegistry {
constructor () {
this.plugins = {}
}
install (plugin) {
if (plugin == null) {
return
}
if (this.plugins == null) {
this.plugins = {}
}
const name = plugin.name || (plugin.define && plugin.define.name)
this.plugins[name] = plugin
}
get (name) {
if (name) {
return this.plugins[name]
}
throw new Error(`找不到${name}插件`)
}
}
export const pluginRegistry = new PluginRegistry()

View File

@@ -0,0 +1,34 @@
export class Registry {
constructor () {
this.collection = {}
}
install (target) {
if (target == null) {
return
}
if (this.collection == null) {
this.collection = {}
}
const className = target.name
this.register(className, target)
const defineName = target.define && target.define().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`)
}
}

View File

@@ -12,7 +12,7 @@
"dependencies": {
"@certd/acme-client": "^0.1.6",
"@certd/api": "^0.1.11",
"@certd/providers": "^0.1.11",
"@certd/dns-providers": "^0.1.11",
"dayjs": "^1.9.7",
"lodash-es": "^4.17.20",
"node-forge": "^0.10.0"

View File

@@ -1,4 +1,4 @@
import { util, Store, providerRegistry } from '@certd/api'
import { util, Store, dnsProviderRegistry } from '@certd/api'
import { AcmeService } from './acme.js'
import { FileStore } from './store/file-store.js'
import { CertStore } from './store/cert-store.js'
@@ -72,16 +72,14 @@ export class Certd {
}
createDnsProvider (options) {
const accessProviders = options.accessProviders
const providerOptions = accessProviders[options.cert.dnsProvider]
return this.createProviderByType(providerOptions.providerType, providerOptions)
return this.createProviderByType(options.cert.dnsProvider, options.accessProviders)
}
async writeCert (cert) {
const newPath = await this.certStore.writeCert(cert)
return {
realPath: this.certStore.store.getActualKey(newPath),
currentPath: this.certStore.store.getActualKey(this.certStore.currentRootPath)
currentPath: this.certStore.store.getActualKey(this.certStore.currentMarkPath)
}
}
@@ -122,12 +120,13 @@ export class Certd {
}
}
createProviderByType (type, options) {
createProviderByType (props, accessProviders) {
const { type } = props
try {
const Provider = providerRegistry.get(type)
return new Provider(options)
const Provider = dnsProviderRegistry.get(type)
return new Provider({ accessProviders, props })
} catch (e) {
throw new Error('暂不支持此dnsProvider,请先use该provider' + type, e)
throw new Error('暂不支持此dnsProvider,请先注册该provider' + type, e)
}
}
}

View File

@@ -1,5 +1,6 @@
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')
}
@@ -10,10 +11,11 @@ export class CertStore {
this.domains = domains
this.domain = this.getMainDomain(this.domains)
this.safetyDomain = this.getSafetyDomain(this.domain)
this.domainDir = this.safetyDomain + '-' + md5(this.getDomainStr(this.domains))
// this.domainDir = this.safetyDomain + '-' + md5(this.getDomainStr(this.domains))
this.domainDir = this.safetyDomain
this.certsRootPath = this.store.buildKey(this.email, 'certs')
this.currentRootPath = this.store.buildKey(this.certsRootPath, this.domainDir, 'current')
this.currentMarkPath = this.store.buildKey(this.certsRootPath, this.domainDir, 'current.json')
}
getMainDomain (domains) {
@@ -62,15 +64,19 @@ export class CertStore {
await this.store.set(priKey, this.formatCert(cert.key.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
}
async readCert (dir) {
if (dir == null) {
dir = this.currentRootPath
dir = await this.getCurrentDir()
}
if (dir == null) {
return
}
const crtKey = this.buildKey(dir, this.safetyDomain + '.crt')
const priKey = this.buildKey(dir, this.safetyDomain + '.key')
const csrKey = this.buildKey(dir, this.safetyDomain + '.csr')
@@ -99,13 +105,23 @@ export class CertStore {
return domain.replace(/\*/g, '_')
}
getCurrentFile (file) {
const key = this.buildKey(this.currentRootPath, file)
async getCurrentDir () {
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)
}
setCurrentFile (file, value) {
const key = this.buildKey(this.currentRootPath, file)
async setCurrentFile (file, value) {
const currentDir = await this.getCurrentDir()
const key = this.buildKey(currentDir, file)
return this.store.set(key, value)
}
}

View File

@@ -66,9 +66,9 @@ describe('Certd', function () {
options.cert.email = 'xiaojunnuo@qq.com'
options.cert.domains = ['*.docmirror.club']
const certd = new Certd(options)
const currentRootPath = certd.certStore.currentRootPath
const currentRootPath = certd.certStore.currentMarkPath
console.log('rootDir', currentRootPath)
expect(currentRootPath).match(/xiaojunnuo@qq.com\\certs\\_.docmirror.club-\w+\\current/)
expect(currentRootPath).match(/xiaojunnuo@qq.com\\certs\\_.docmirror.club\w*\\current.json/)
})
it('#writeAndReadCert', async function () {
const options = createOptions()
@@ -83,6 +83,6 @@ describe('Certd', function () {
expect(cert.key).to.be.ok
expect(cert.detail).to.be.ok
expect(cert.expires).to.be.ok
console.log('expires:', cert.expires)
console.log('cert:', JSON.stringify(cert))
})
})

View 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"
}
}
]
}

2814
packages/dns-providers/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{
"name": "@certd/providers",
"name": "@certd/dns-providers",
"version": "0.1.11",
"description": "",
"main": "./src/index.js",

View 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)
})
}
}

View File

@@ -8,21 +8,15 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
label: '阿里云',
desc: '',
input: {
accessKeyId: {
type: String,
accessProvider: {
label: 'Access提供者',
type: [String, Object],
desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象',
component: {
placeholder: 'accessKeyId',
rules: [{ required: true, message: '必填项' }]
name: 'provider-selector',
filter: 'aliyun'
},
required: true
},
accessKeySecret: {
type: String,
component: {
placeholder: 'accessKeySecret',
rules: [{ required: true, message: '必填项' }]
}
}
},
output: {
@@ -31,11 +25,13 @@ export class AliyunDnsProvider extends AbstractDnsProvider {
}
}
constructor (dnsProviderConfig) {
super()
constructor (args) {
super(args)
const { props } = args
const accessProvider = this.getAccessProvider(props.accessProvider)
this.client = new Core({
accessKeyId: dnsProviderConfig.accessKeyId,
accessKeySecret: dnsProviderConfig.accessKeySecret,
accessKeyId: accessProvider.accessKeyId,
accessKeySecret: accessProvider.accessKeySecret,
endpoint: 'https://alidns.aliyuncs.com',
apiVersion: '2015-01-09'
})

View File

@@ -8,31 +8,25 @@ export class DnspodDnsProvider extends AbstractDnsProvider {
label: 'dnspod(腾讯云)',
desc: '腾讯云的域名解析接口已迁移到dnspod',
input: {
id: {
type: String,
accessProvider: {
label: 'Access提供者',
type: [String, Object],
desc: 'AccessProviders的key',
component: {
placeholder: 'dnspod接口账户id',
rules: [{ required: true, message: '该项必填' }]
}
},
token: {
type: String,
label: 'token',
component: {
placeholder: '开放接口token',
rules: [{ required: true, message: '该项必填' }]
}
name: 'provider-selector',
filter: 'dnspod'
},
required: true
}
}
}
}
constructor (dnsProviderConfig) {
super()
if (!dnsProviderConfig.id || !dnsProviderConfig.token) {
throw new Error('请正确配置dnspod的 id 和 token')
}
this.loginToken = dnsProviderConfig.id + ',' + dnsProviderConfig.token
constructor (args) {
super(args)
const { props } = args
const accessProvider = this.getAccessProvider(props.accessProvider)
this.loginToken = accessProvider.id + ',' + accessProvider.token
}
async doRequest (options) {

View File

@@ -1,5 +1,5 @@
import pkg from 'chai'
import AliyunDnsProvider from '../../src/dns-provider/aliyun.js'
import AliyunDnsProvider from '../../src/providers/aliyun.js'
import { createOptions } from '../../../../test/options.js'
const { expect } = pkg
describe('AliyunDnsProvider', function () {

View File

@@ -1,5 +1,5 @@
import pkg from 'chai'
import DnspodDnsProvider from '../../src/dns-provider/dnspod.js'
import DnspodDnsProvider from '../../src/providers/dnspod.js'
import { Certd } from '../../src/index.js'
import { createOptions } from '../../../../test/options.js'
const { expect } = pkg

View File

@@ -13,6 +13,7 @@
"@certd/api": "^0.1.11",
"@certd/certd": "^0.1.11",
"@certd/plugins": "^0.1.11",
"@certd/dns-providers": "^0.1.11",
"dayjs": "^1.9.7",
"lodash-es": "^4.17.20"
},

View File

@@ -4,12 +4,13 @@ import _ from 'lodash-es'
import dayjs from 'dayjs'
import { Trace } from './trace.js'
import DefaultPlugins from '@certd/plugins'
import DefaultProviders from '@certd/providers'
import DefaultDnsProviders from '@certd/dns-providers'
const logger = util.logger
// 安装默认插件和授权提供者
DefaultPlugins.install()
DefaultProviders.install()
DefaultDnsProviders.install()
function createDefaultOptions () {
return {

View File

@@ -20,7 +20,7 @@ const define = {
}
},
from: {
value: 'upload',
default: 'upload',
label: '证书来源',
component: {
placeholder: '证书来源',
@@ -31,7 +31,7 @@ const define = {
{ value: 'cas', label: '从证书库', title: '需要uploadCertToAliyun作为前置任务' }
]
},
desc: '如果选择cas类型,则需要以《上传证书到阿里云》作为前置任务'
desc: '如果选择‘从证书库’类型,则需要以《上传证书到阿里云》作为前置任务'
},
// serverCertificateStatus: {

View File

@@ -6,17 +6,22 @@ const define = {
label: '上传证书到阿里云',
input: {
name: {
label: '证书名称'
label: '证书名称',
desc: '证书上传后将以此参数作为名称前缀'
},
regionId: {
label: '大区',
value: 'cn-hangzhou'
default: 'cn-hangzhou'
},
accessProvider: {
label: 'Access提供者',
type: [String, Object],
desc: 'AccessProviders的key 或 一个包含accessKeyId与accessKeySecret的对象',
options: 'accessProviders[type=aliyun]'
component: {
name: 'provider-selector',
filter: 'aliyun'
},
required: true
}
},
output: {

View File

@@ -19,30 +19,27 @@ export class DeployCertToTencentCDN extends AbstractTencentPlugin {
required: true
},
certName: {
label: '证书名称'
label: '证书名称',
desc: '证书上传后将以此参数作为名称前缀'
},
certType: {
value: 'upload',
default: 'upload',
label: '证书来源',
options: [
{ value: 'upload', label: '直接上传' },
{ value: 'cloud', label: '从证书库', desc: '需要uploadCertToTencent作为前置任务' }
],
desc: '如果选择‘从证书库’类型,则需要以《上传证书到腾讯云》作为前置任务',
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]',
component: {
name: 'provider-selector',
filter: 'tencent'
},
required: true
}
},

View File

@@ -15,7 +15,7 @@ export class DeployCertToTencentCLB extends AbstractTencentPlugin {
input: {
region: {
label: '大区',
value: 'ap-guangzhou'
default: 'ap-guangzhou'
},
domain: {
label: '域名',

View File

@@ -16,7 +16,7 @@ export class DeployCertToTencentTKEIngress extends AbstractTencentPlugin {
input: {
region: {
label: '大区',
value: 'ap-guangzhou'
default: 'ap-guangzhou'
},
clusterId: {
label: '集群ID',
@@ -25,7 +25,7 @@ export class DeployCertToTencentTKEIngress extends AbstractTencentPlugin {
},
namespace: {
label: '集群的namespace',
value: 'default'
default: 'default'
},
secreteName: {
type: [String, Array],

View File

@@ -1,16 +0,0 @@
import _ from 'lodash-es'
import { AliyunDnsProvider } from './dns-provider/aliyun.js'
import { DnspodDnsProvider } from './dns-provider/dnspod.js'
import { providerRegistry } from '@certd/api'
export const DefaultProviders = {
AliyunDnsProvider,
DnspodDnsProvider
}
export default {
install () {
_.forEach(DefaultProviders, item => {
providerRegistry.install(item)
})
}
}