theophilusx / ssh2-sftp-client

a client for SSH2 SFTP
Apache License 2.0
808 stars 199 forks source link

pm2 restart 0 无返回数据 #530

Closed wjg55555 closed 6 months ago

wjg55555 commented 6 months ago

this.client.exec(‘pm2 restart 0’, (err, stream) => { if (err) return reject(err);

  let bf = new Buffer([]);

  stream.on('data', (buffer) => {

this is no data 无数据 bf = Buffer.concat([bf, buffer]); fn && fn(buffer.toString('utf8')); }); stream.on('end', (_) => { resolve(bf.toString('utf8')); }); });

theophilusx commented 6 months ago

Sorry, but I don't understand what this is about. There is no 'exec' support in the ssh2-sftp-client library. Did you mean this issue to go to the ssh2 library instead?

wjg55555 commented 6 months ago

监听服务器调用命令返回的结果 pm2 restart 0 没有返回结果 node -v 会返回版本号 服务器上直接执行上面的命令,都是成功的

theophilusx commented 6 months ago

Sorry, I cannot read/understand this text. I have translated it using Google, but not sure how correct that translation is.

The sftp protocol does not have support for executing a command on a remote server. You would need to use ssh2 for that. This library is specific to the sftp (FTP) protocol. There is no support for calling exec within the FTP protocol or this library.

I'm not sure what you mean by "Monitoring the result returned by the server call command." My guess is you need to be using ssh2 and not ssh2-sftp-client. The ssh2-sftp-client is not the right library for what you want to do.

wjg55555 commented 6 months ago

const fs = require('fs'); const glob = require('glob'); const path = require('path'); const chalk = require('chalk'); const inquirer = require('inquirer'); const Client = require('ssh2-sftp-client'); const Moment = require('moment'); let pm2Conf = require('./ecosystem.config'); pm2Conf = pm2Conf.apps[0];

const sftp = new Client(); const projectName = 'http://www.yinghou.vip/'; const backupDirName = 'backup_' + pm2Conf.name;

const os = require('os');

// 适配win32 if (os.platform() == 'win32') { path._join = path.join; path.join = function () { return path._join.apply(null, arguments).replace(/\/g, '/'); }; }

const bundleDir = { client: ['./.nuxt/', './package.json'], server: [ './app/', './middleware/', './modules/', './plugins/', './server/', './serverMiddleware/', './utils/', './nuxt.config.js', './app.html', './Robots.txt', './ecosystem.config.js', ], static: ['./static/**'], };

bundleDir.both = [...bundleDir.client, ...bundleDir.server, ...bundleDir.static];

const remoteDir = '/www/' + pm2Conf.name; const config = { host: '', port: '', username: '', password: '', };

String.prototype.tranCode = function () { return this.split('') .map((char) => char.charCodeAt()) .join('-'); };

String.prototype.tranString = function () { return this.split('-') .map((code) => String.fromCharCode(code)) .join(''); };

Client.prototype.exec = function (exec, fn) { return new Promise((resolve, reject) => { this.client.exec(exec, (err, stream) => { if (err) return reject(err);

  let bf = new Buffer([]);

  stream.on('data', (buffer) => {
    bf = Buffer.concat([bf, buffer]);
    fn && fn(buffer.toString('utf8'));
  });
  stream.on('end', (_) => {
    resolve(bf.toString('utf8'));
  });
});

}); };

const resolvePaths = async (dirList) => { const dirs = []; const files = [];

await Promise.all( dirList.map( (dir) => new Promise((resolve) => { glob(dir, (err, paths) => { paths.map((p) => { if (fs.statSync(p).isDirectory()) dirs.push(p); else files.push(p); }); resolve(); }); }), ), );

return { dirs, files }; };

const getRemotePath = (localPath) => { return path.join(remoteDir, localPath); };

const connect = async () => { const _config = { ...config }; _config.username = _config.username.tranString(); _config.password = _config.password.tranString();

await sftp.connect(_config); console.log(chalk.blueBright(connect ${projectName} success !)); };

const backup = async () => { // 备份文件夹 const backupDir = path.join(remoteDir, ../${backupDirName}); // 待备份的项目文件夹 const projectDir = path.join(remoteDir); // 忽略的模块 const ignoreDir = path.join(remoteDir, 'node_modules/*'); // 创建文件夹 const mkdir = mkdir ${backupDir}; // zip name const zipName = path.join(backupDir, Moment().format('YYYY-MM-DD_HH_mm_ss.\zip')); // 执行备份 const zip = zip -r ${zipName} ${projectDir} -x ${ignoreDir};

await sftp.exec(mkdir); await sftp.exec(zip, (msg) => console.log(msg));

console.log(chalk.blueBright('backup complete.')); };

const upload = async (localDir) => { const { dirs, files } = await resolvePaths(localDir); console.log(dirs, files);

// dirs for (let i = 0; i < dirs.length; i++) { let p = dirs[i]; await sftp .mkdir(getRemotePath(p), true) .then((res) => console.log([${(((i + 1) / dirs.length) * 100).toFixed(2)}%], res)) .catch((err) => {}); }

// files for (let i = 0; i < files.length; i++) { let p = files[i]; await sftp .put(p, getRemotePath(p)) .then((res) => console.log([${(((i + 1) / files.length) * 100).toFixed(2)}%], res)) .catch((err) => console.log(err)); }

sftp.on('end', (_) => { console.log(chalk.blueBright('jobs done!')); }); };

const recover = async (_) => { await connect();

const backupDir = path.join(remoteDir, ../${backupDirName});

const list = await sftp.exec(ls ${backupDir});

let choices = list .split('\n') .filter((str) => str) .map((str) => ({ name: str, value: str, }));

let cfg = [ { type: 'list', name: 'zipName', message: 'select backup file to recover..', choices, }, ];

const { zipName } = await inquirer.prompt(cfg);

const unzip = unzip ${path.join(backupDir, zipName)} -d /;

console.log(unzip);

const msg = await sftp.exec(unzip, (msg) => console.log(msg));

console.log(msg);

sftp.end();

console.log(chalk.blueBright('Connection closed.')); };

const restartProject = async (_) => { console.log('restart project...');

await sftp.exec(pm2 restart ${pm2Conf.name}).then((res) => console.log(res)); };

const uploadPrompts = [ { type: 'list', name: 'uploadType', message: 'Select the bundles to be uploaded:', choices: [ { name: 'both server and client and static', value: 'both', }, { name: 'only client', value: 'client', }, { name: 'only server', value: 'server', }, { name: 'only static', value: 'static', }, ], }, ];

const backupPrompts = [ { type: 'list', name: 'action', message: Whether to back up ${projectName} before uploading??, choices: [ // { // name: 'Yes!!', // value: true // }, { name: 'no', value: false, }, ], }, ];

const restartPrompts = [ { type: 'list', name: 'willRestart', message: Do you want to restart the ${projectName}?, choices: [ { name: 'Yes!!', value: true, }, { name: 'no', value: false, }, ], }, ];

const setup = async (_) => { await connect();

const { action } = await inquirer.prompt(backupPrompts);

if (action) { await backup(); }

const { uploadType } = await inquirer.prompt(uploadPrompts);

await upload(bundleDir[uploadType]);

const { willRestart } = await inquirer.prompt(restartPrompts);

if (willRestart) { await restartProject(); }

sftp.end(); };

const cmdModel = async () => { await connect(); let exit = false; while (!exit) { const { cmd } = await inquirer.prompt([ { type: 'input', name: 'cmd', message: 'input cmd', }, ]);

const msg = await sftp.exec(cmd);

console.log(msg);

} };

const { NODE_ENV } = process.env;

const task = { recover: recover, cmd: cmdModel, }[NODE_ENV];

task ? task() : setup();

theophilusx commented 6 months ago

You cannot do this with ssh2-sftp-client. This will not work.

The ssh2-sftp-client module is a wrapper giving a promise based API to the ftp layer of the ssh2 library. This layer does not include exec support from the main ssh2 layer and only support a subset of the FTP protocol.

The exec support you are after is only available in the ssh2 library.

To do what you are trying to do you need to use ssh2 not ssh2-sftp-client.

You need

const client = require('ssh2')

and use the API at https://github.com/mscdex/ssh2

wjg55555 commented 6 months ago

However, when I deploy this project on another server, it will automatically restart, and the current server will not automatically restart. Why is that?

theophilusx commented 6 months ago

I still have no idea what your trying to do. The library ssh2-sftp-client is a library for building a CLIENT and has nothing to do with servers or executing servers or running server processes.

PM2 is a module to manage (run/start/restart) node processes. ssh2-sftp-client doe snot implement a node process.

Your earlier code indicated you were trying to start a process on a remote server using ssh2-sftp-client. You CANNOT DO THIS. The ssh2-sftp-client is specific to the FTP protocol and has not support for executing remote processes.

If you want to execute a process on a remote server, you need to use the ssh2 library and not this library.

wjg55555 commented 6 months ago

The pm2 command cannot be used remotely. The node command is successful

wjg55555 commented 6 months ago

Is there any configuration on the server that I didn't set?

wjg55555 commented 6 months ago

What are the configurations of the pm2 remote deployment server?