mirror of
https://github.com/certd/certd.git
synced 2026-04-14 12:30:54 +08:00
refactor: move
This commit is contained in:
14
packages/plugins/plugin-host/.eslintrc
Normal file
14
packages/plugins/plugin-host/.eslintrc
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "standard",
|
||||
"env": {
|
||||
"mocha": true
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.test.js", "*.spec.js"],
|
||||
"rules": {
|
||||
"no-unused-expressions": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
3690
packages/plugins/plugin-host/package-lock.json
generated
Normal file
3690
packages/plugins/plugin-host/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
packages/plugins/plugin-host/package.json
Normal file
25
packages/plugins/plugin-host/package.json
Normal 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"
|
||||
}
|
||||
26
packages/plugins/plugin-host/src/access-providers/ssh.js
Normal file
26
packages/plugins/plugin-host/src/access-providers/ssh.js
Normal 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: '密钥,密码或此项必填一项'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
packages/plugins/plugin-host/src/index.js
Normal file
22
packages/plugins/plugin-host/src/index.js
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 }) {
|
||||
|
||||
}
|
||||
}
|
||||
111
packages/plugins/plugin-host/src/plugins/ssh.js
Normal file
111
packages/plugins/plugin-host/src/plugins/ssh.js
Normal 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())
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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 }) {
|
||||
|
||||
}
|
||||
}
|
||||
42
packages/plugins/plugin-host/test/options.js
Normal file
42
packages/plugins/plugin-host/test/options.js
Normal 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
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
1858
packages/plugins/plugin-host/yarn-error.log
Normal file
1858
packages/plugins/plugin-host/yarn-error.log
Normal file
File diff suppressed because it is too large
Load Diff
2922
packages/plugins/plugin-host/yarn.lock
Normal file
2922
packages/plugins/plugin-host/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user