refactor: move

This commit is contained in:
xiaojunnuo
2021-02-08 00:21:36 +08:00
parent cfb1034450
commit 82f86d9556
150 changed files with 14691 additions and 2059 deletions

View File

@@ -0,0 +1,14 @@
{
"extends": "standard",
"env": {
"mocha": true
},
"overrides": [
{
"files": ["*.test.js", "*.spec.js"],
"rules": {
"no-unused-expressions": "off"
}
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
{
"name": "@certd/plugins",
"version": "0.1.13",
"description": "",
"main": "src/index.js",
"type": "module",
"dependencies": {
"@certd/api": "^0.1.13",
"dayjs": "^1.9.7",
"lodash-es": "^4.17.20",
"ssh2": "^0.8.9"
},
"devDependencies": {
"chai": "^4.2.0",
"eslint": "^7.15.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"mocha": "^8.2.1"
},
"author": "Greper",
"license": "MIT",
"gitHead": "4a421d5b142d453203c68ce6d1036e168ea2455b"
}

View File

@@ -0,0 +1,26 @@
export class SSHAccessProvider {
static define () {
return {
name: 'ssh',
label: '主机',
desc: '',
input: {
host: { required: true },
port: {
label: '端口',
type: Number,
default: '22',
required: true
},
username: {
default: 'root',
required: true
},
password: { desc: '登录密码' },
publicKey: {
desc: '密钥,密码或此项必填一项'
}
}
}
}
}

View File

@@ -0,0 +1,22 @@
import _ from 'lodash-es'
import { SSHAccessProvider } from './access-providers/ssh'
import { UploadCertToHost } from './host/upload-to-host/index.js'
import { HostShellExecute } from './host/host-shell-execute/index.js'
import { pluginRegistry, accessProviderRegistry } from '@certd/api'
export const DefaultPlugins = {
UploadCertToHost,
HostShellExecute
}
export default {
install () {
_.forEach(DefaultPlugins, item => {
pluginRegistry.install(item)
})
accessProviderRegistry.install(SSHAccessProvider)
}
}

View File

@@ -0,0 +1,9 @@
import { AbstractPlugin } from '@certd/api'
export class AbstractHostPlugin extends AbstractPlugin {
checkRet (ret) {
if (ret.code != null) {
throw new Error('执行失败:', ret.Message)
}
}
}

View File

@@ -0,0 +1,58 @@
import { AbstractHostPlugin } from '../abstract-host.js'
import { SshClient } from '../ssh.js'
export class HostShellExecute extends AbstractHostPlugin {
/**
* 插件定义
* 名称
* 入参
* 出参
*/
static define () {
return {
name: 'hostShellExecute',
label: '执行远程主机脚本命令',
input: {
script: {
label: 'shell脚本命令',
component: {
name: 'a-textarea'
}
},
accessProvider: {
label: '主机登录配置',
type: [String, Object],
desc: '登录',
component: {
name: 'access-provider-selector',
filter: 'ssh'
},
required: true
}
},
output: {
}
}
}
async execute ({ cert, props, context }) {
const { script, accessProvider } = props
const connectConf = this.getAccessProvider(accessProvider)
const sshClient = new SshClient()
const ret = await sshClient.shell({
connectConf,
script
})
return ret
}
/**
* @param cert
* @param props
* @param context
* @returns {Promise<void>}
*/
async rollback ({ cert, props, context }) {
}
}

View File

@@ -0,0 +1,111 @@
import ssh2 from 'ssh2'
import path from 'path'
import { util } from '@certd/api'
const logger = util.logger
export class SshClient {
/**
*
* @param connectConf
{
host: '192.168.100.100',
port: 22,
username: 'frylock',
password: 'nodejsrules'
}
* @param transports
*/
uploadFiles ({ connectConf, transports }) {
const conn = new ssh2.Client()
return new Promise((resolve, reject) => {
conn.on('ready', () => {
logger.info('连接服务器成功')
conn.sftp(async (err, sftp) => {
if (err) {
throw err
}
try {
for (const transport of transports) {
logger.info('上传文件:', JSON.stringify(transport))
await this.exec({ conn, cmd: 'mkdir ' + path.dirname(transport.remotePath) })
await this.fastPut({ sftp, ...transport })
}
resolve()
} catch (e) {
reject(e)
} finally {
conn.end()
}
})
}).connect(connectConf)
})
}
shell ({ connectConf, script }) {
return new Promise((resolve, reject) => {
this.connect({
connectConf,
onReady: (conn) => {
conn.shell((err, stream) => {
if (err) {
reject(err)
return
}
const output = []
stream.on('close', () => {
logger.info('Stream :: close')
conn.end()
resolve(output)
}).on('data', (data) => {
logger.info('' + data)
output.push('' + data)
})
stream.end(script + '\nexit\n')
})
}
})
})
}
connect ({ connectConf, onReady }) {
const conn = new ssh2.Client()
conn.on('ready', () => {
console.log('Client :: ready')
onReady(conn)
}).connect(connectConf)
return conn
}
fastPut ({ sftp, localPath, remotePath }) {
return new Promise((resolve, reject) => {
sftp.fastPut(localPath, remotePath, (err) => {
if (err) {
reject(err)
return
}
resolve()
})
})
}
exec ({ conn, cmd }) {
return new Promise((resolve, reject) => {
conn.exec(cmd, (err, stream) => {
if (err) {
logger.error('执行命令出错', err)
reject(err)
// return conn.end()
}
stream.on('close', (code, signal) => {
// logger.info('Stream :: close :: code: ' + code + ', signal: ' + signal)
// conn.end()
resolve()
}).on('data', (data) => {
logger.info('data', data.toString())
})
})
})
}
}

View File

@@ -0,0 +1,81 @@
import { AbstractHostPlugin } from '../abstract-host.js'
import { SshClient } from '../ssh.js'
export class UploadCertToHost extends AbstractHostPlugin {
/**
* 插件定义
* 名称
* 入参
* 出参
*/
static define () {
return {
name: 'uploadCertToHost',
label: '上传证书到主机',
input: {
crtPath: {
label: '证书保存路径'
},
keyPath: {
label: '私钥保存路径'
},
accessProvider: {
label: '主机登录配置',
type: [String, Object],
desc: 'access授权',
component: {
name: 'access-provider-selector',
filter: 'ssh'
},
required: true
}
},
output: {
hostCrtPath: {
type: String,
desc: '上传成功后的证书路径'
},
hostKeyPath: {
type: String,
desc: '上传成功后的私钥路径'
}
}
}
}
async execute ({ cert, props, context }) {
const { crtPath, keyPath, accessProvider } = props
const connectConf = this.getAccessProvider(accessProvider)
const sshClient = new SshClient()
await sshClient.uploadFiles({
connectConf,
transports: [
{
localPath: cert.crtPath,
remotePath: crtPath
},
{
localPath: cert.keyPath,
remotePath: keyPath
}
]
})
this.logger.info('证书上传成功crtPath=', crtPath, ',keyPath=', keyPath)
context.hostCrtPath = crtPath
context.hostKeyPath = keyPath
return {
hostCrtPath: crtPath,
hostKeyPath: keyPath
}
}
/**
* @param cert
* @param props
* @param context
* @returns {Promise<void>}
*/
async rollback ({ cert, props, context }) {
}
}

View File

@@ -0,0 +1,42 @@
import _ from 'lodash-es'
import optionsPrivate from '../../../test/options.private.mjs'
const defaultOptions = {
version: '1.0.0',
args: {
directory: 'test',
dry: false
},
accessProviders: {
aliyun: {
providerType: 'aliyun',
accessKeyId: '',
accessKeySecret: ''
},
myLinux: {
providerType: 'SSH',
username: 'xxx',
password: 'xxx',
host: '1111.com',
port: 22,
publicKey: ''
}
},
cert: {
domains: ['*.docmirror.club', 'docmirror.club'],
email: 'xiaojunnuo@qq.com',
dnsProvider: 'aliyun',
certProvider: 'letsencrypt',
csrInfo: {
country: 'CN',
state: 'GuangDong',
locality: 'ShengZhen',
organization: 'CertD Org.',
organizationUnit: 'IT Department',
emailAddress: 'xiaojunnuo@qq.com'
}
}
}
_.merge(defaultOptions, optionsPrivate)
export default defaultOptions

View File

@@ -0,0 +1,29 @@
import pkg from 'chai'
import { HostShellExecute } from '../../src/plugins/host-shell-execute/index.js'
import { Certd } from '@certd/certd'
import { createOptions } from '../../../../../test/options.js'
const { expect } = pkg
describe('HostShellExecute', function () {
it('#execute', async function () {
this.timeout(10000)
const options = createOptions()
options.args = { test: false }
options.cert.email = 'xiaojunnuo@qq.com'
options.cert.domains = ['*.docmirror.cn']
const plugin = new HostShellExecute(options)
const certd = new Certd(options)
const cert = await certd.readCurrentCert()
const context = {}
const uploadOpts = {
cert,
props: { script: 'ls ', accessProvider: 'aliyun-ssh' },
context
}
const ret = await plugin.doExecute(uploadOpts)
for (const retElement of ret) {
console.log('-----' + retElement)
}
await plugin.doRollback(uploadOpts)
})
})

View File

@@ -0,0 +1,27 @@
import pkg from 'chai'
import { UploadCertToHost } from '../../src/plugins/upload-to-host/index.js'
import { Certd } from '@certd/certd'
import { createOptions } from '../../../../../test/options.js'
const { expect } = pkg
describe('PluginUploadToHost', function () {
it('#execute', async function () {
this.timeout(10000)
const options = createOptions()
options.args = { test: false }
options.cert.email = 'xiaojunnuo@qq.com'
options.cert.domains = ['*.docmirror.cn']
const plugin = new UploadCertToHost(options)
const certd = new Certd(options)
const cert = await certd.readCurrentCert()
const context = {}
const uploadOpts = {
cert,
props: { crtPath: '/root/certd/test/test.crt', keyPath: '/root/certd/test/test.key', accessProvider: 'aliyun-ssh' },
context
}
await plugin.doExecute(uploadOpts)
console.log('context:', context)
await plugin.doRollback(uploadOpts)
})
})

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff