2023-01-11 20:39:48 +08:00
|
|
|
// @ts-ignore
|
2024-06-19 00:21:13 +08:00
|
|
|
import ssh2, { ConnectConfig } from 'ssh2';
|
2024-05-27 18:38:41 +08:00
|
|
|
import path from 'path';
|
|
|
|
|
import _ from 'lodash';
|
|
|
|
|
import { ILogger } from '@certd/pipeline';
|
2022-11-07 23:31:20 +08:00
|
|
|
export class SshClient {
|
2023-01-11 20:39:48 +08:00
|
|
|
logger: ILogger;
|
|
|
|
|
constructor(logger: ILogger) {
|
2022-11-07 23:31:20 +08:00
|
|
|
this.logger = logger;
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param connectConf
|
|
|
|
|
{
|
|
|
|
|
host: '192.168.100.100',
|
|
|
|
|
port: 22,
|
|
|
|
|
username: 'frylock',
|
|
|
|
|
password: 'nodejsrules'
|
|
|
|
|
}
|
|
|
|
|
* @param options
|
|
|
|
|
*/
|
2024-06-19 00:21:13 +08:00
|
|
|
uploadFiles(options: { connectConf: ConnectConfig; transports: any }) {
|
2024-04-08 10:05:11 +08:00
|
|
|
const { connectConf, transports } = options;
|
2022-11-07 23:31:20 +08:00
|
|
|
const conn = new ssh2.Client();
|
|
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
conn
|
2024-05-27 18:38:41 +08:00
|
|
|
.on('ready', () => {
|
|
|
|
|
this.logger.info('连接服务器成功');
|
2023-05-09 09:49:42 +08:00
|
|
|
conn.sftp(async (err: any, sftp: any) => {
|
2022-11-07 23:31:20 +08:00
|
|
|
if (err) {
|
|
|
|
|
throw err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
for (const transport of transports) {
|
2024-05-27 18:38:41 +08:00
|
|
|
this.logger.info('上传文件:', JSON.stringify(transport));
|
|
|
|
|
await this.exec({
|
|
|
|
|
connectConf,
|
|
|
|
|
script: `mkdir -p ${path.dirname(transport.remotePath)} `,
|
|
|
|
|
});
|
2022-11-07 23:31:20 +08:00
|
|
|
await this.fastPut({ sftp, ...transport });
|
|
|
|
|
}
|
|
|
|
|
resolve({});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
reject(e);
|
|
|
|
|
} finally {
|
|
|
|
|
conn.end();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.connect(connectConf);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-19 00:21:13 +08:00
|
|
|
exec(options: {
|
|
|
|
|
connectConf: ConnectConfig;
|
|
|
|
|
script: string | Array<string>;
|
|
|
|
|
}) {
|
2022-11-07 23:31:20 +08:00
|
|
|
let { script } = options;
|
|
|
|
|
const { connectConf } = options;
|
|
|
|
|
if (_.isArray(script)) {
|
2024-05-27 18:38:41 +08:00
|
|
|
script = script as Array<string>;
|
|
|
|
|
script = script.join('\n');
|
2022-11-07 23:31:20 +08:00
|
|
|
}
|
2024-05-27 18:38:41 +08:00
|
|
|
this.logger.info('执行命令:', script);
|
2022-11-07 23:31:20 +08:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
this.connect({
|
|
|
|
|
connectConf,
|
2023-06-25 23:25:56 +08:00
|
|
|
onError(err: any) {
|
|
|
|
|
reject(err);
|
|
|
|
|
},
|
2022-11-07 23:31:20 +08:00
|
|
|
onReady: (conn: any) => {
|
|
|
|
|
conn.exec(script, (err: Error, stream: any) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
reject(err);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
let data: any = null;
|
|
|
|
|
stream
|
2024-05-27 18:38:41 +08:00
|
|
|
.on('close', (code: any, signal: any) => {
|
2022-11-07 23:31:20 +08:00
|
|
|
this.logger.info(`[${connectConf.host}][close]:code:${code}`);
|
|
|
|
|
data = data ? data.toString() : null;
|
|
|
|
|
if (code === 0) {
|
|
|
|
|
resolve(data);
|
|
|
|
|
} else {
|
|
|
|
|
reject(new Error(data));
|
|
|
|
|
}
|
|
|
|
|
conn.end();
|
|
|
|
|
})
|
2024-05-27 18:38:41 +08:00
|
|
|
.on('data', (ret: any) => {
|
2022-11-07 23:31:20 +08:00
|
|
|
this.logger.info(`[${connectConf.host}][info]: ` + ret);
|
|
|
|
|
data = ret;
|
|
|
|
|
})
|
2024-05-27 18:38:41 +08:00
|
|
|
.stderr.on('data', (err: Error) => {
|
2022-11-07 23:31:20 +08:00
|
|
|
this.logger.info(`[${connectConf.host}][error]: ` + err);
|
|
|
|
|
data = err;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-19 00:21:13 +08:00
|
|
|
shell(options: { connectConf: ConnectConfig; script: string }) {
|
2022-11-07 23:31:20 +08:00
|
|
|
const { connectConf, script } = options;
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
this.connect({
|
|
|
|
|
connectConf,
|
2023-06-25 23:25:56 +08:00
|
|
|
onError: (err: any) => {
|
|
|
|
|
this.logger.error(err);
|
|
|
|
|
reject(err);
|
|
|
|
|
},
|
2022-11-07 23:31:20 +08:00
|
|
|
onReady: (conn: any) => {
|
|
|
|
|
conn.shell((err: Error, stream: any) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
reject(err);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const output: any = [];
|
|
|
|
|
stream
|
2024-05-27 18:38:41 +08:00
|
|
|
.on('close', () => {
|
|
|
|
|
this.logger.info('Stream :: close');
|
2022-11-07 23:31:20 +08:00
|
|
|
conn.end();
|
|
|
|
|
resolve(output);
|
|
|
|
|
})
|
2024-05-27 18:38:41 +08:00
|
|
|
.on('data', (data: any) => {
|
|
|
|
|
this.logger.info('' + data);
|
|
|
|
|
output.push('' + data);
|
2022-11-07 23:31:20 +08:00
|
|
|
});
|
2024-05-27 18:38:41 +08:00
|
|
|
stream.end(script + '\nexit\n');
|
2022-11-07 23:31:20 +08:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-19 00:21:13 +08:00
|
|
|
connect(options: { connectConf: ConnectConfig; onReady: any; onError: any }) {
|
2023-06-25 23:25:56 +08:00
|
|
|
const { connectConf, onReady, onError } = options;
|
2022-11-07 23:31:20 +08:00
|
|
|
const conn = new ssh2.Client();
|
|
|
|
|
conn
|
2024-05-27 18:38:41 +08:00
|
|
|
.on('error', (err: any) => {
|
2023-06-25 23:25:56 +08:00
|
|
|
onError(err);
|
|
|
|
|
})
|
2024-05-27 18:38:41 +08:00
|
|
|
.on('ready', () => {
|
|
|
|
|
this.logger.info('Client :: ready');
|
2022-11-07 23:31:20 +08:00
|
|
|
onReady(conn);
|
|
|
|
|
})
|
|
|
|
|
.connect(connectConf);
|
|
|
|
|
return conn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fastPut(options: { sftp: any; localPath: string; remotePath: string }) {
|
|
|
|
|
const { sftp, localPath, remotePath } = options;
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
sftp.fastPut(localPath, remotePath, (err: Error) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
reject(err);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
resolve({});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|