mirror of
https://github.com/certd/certd.git
synced 2026-04-23 19:57:27 +08:00
refactor: move
This commit is contained in:
@@ -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: '密钥,密码或此项必填一项'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 }) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 }) {
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user