Using ssh2 in React and Redux Application
When you need SSH connectivity within your JavaScript app, ssh2 is the best choice available. If you are using SSH2 with React and Redux, how can you make sure you always get a success, or a failure? Looking at the documentation, it is not that obvious. So let’s take a look at how to use this with a Redux setup.
Using SSH2
Even though it may not be that obvious now, remember that both the Client and Server use the exact same object.
let connectObject = {
host: ip,
user: user,
privateKey: fs.readFileSync(`${process.cwd()}/keys/${key}`)
}
let conn = new Client();
conn.on('ready', () => {
conn.shell((error, stream) => {
if(error) {
throw error;
}
stream.on('close', () => {
conn.end();
}).on('data', data => {
data
}).stderr.on('data', error => {
throw error;
});
stream.end(`${command}\n exit\n`);
}).connect(connectObject);
});
Similar to what is found in the documentation we have multiple events. If you are not very familar with Node.js streams, a way to receive a success/failure event may be foreign. We’ll add this as we prepare for React.
Preparing for a Declarative Structure
We need to create a custom promise around our stream, and use some extra events to receive the results. With this, you’ll know when the command succeeds, or fails.
let dataOut = '<p>'; let errOut; let connectObject = { host: ip, user: user, privateKey: fs.readFileSync(`${process.cwd()}/keys/${key}`) } let conn = new Client(); return new Promise((resolve, reject) => { conn.on('ready', () => { conn.shell((error, stream) => { if(error) { reject(error); // return errors when connecting to shell and reject }; stream.on('close', () => { conn.end(); }); stream.on('data', data => dataOut += data); stream.stderr.on('data', error => { reject(error); // return stderr data and fail the action }); stream.end(`${command}\n exit\n`); }); }).on('error', error => { reject(error); // sends reject to redux action when connect fails }).on('end', () => { resolve(dataOut); // send a collective string of data that was returned in the shell }).connect(connectObject); });
We reject any errors, including error events on the connection object. Data is passed to a lexical variable, and then returned with the resolve.
Using SSH2 with React Redux Actions
Redux is a great framework for your React apps. If you are still new to Redux, check out our Beginner’s Guide to Redux.
While we could use our promise in a redux action, it’s still very clunky. Instead, we can put the promise in its own class object method. We can then reference it in our actions, and observe the output. So to do this, we first create an event emitter to pass our data to the action.
SecureShell.js
export const sshEmitter = new EventEmitter
export default class SecureShell {
static sshCommand(command, ip, port, user, pass, key) {
let dataOut = '<p>';
let errOut;
let connectObject = {
host: ip,
user: user,
privateKey: fs.readFileSync(${process.cwd()}/keys/${key})
}
let conn = new Client();
return conn.on('ready', () => {
conn.shell((error, stream) => {
stream.on('close', () => {
conn.end()
}).on('data', data => {
sshEmitter.emit('data', data)
}).stderr.on('data', error => {
sshEmitter.emit('stderr', error)
})
stream.end(`${command}\nexit\n')
})
}).on('error', error => {
sshEmitter.emit('error', error)
}).on('end', () => {
sshEmitter.emit('end')
}).connect(connectObject);
}
}
With this in place, we can now add it into a Redux action easily:
Actions.js
export function callSshAction(command, ip, port, user, pass, key) {
return dispatch => {
SecureShell.sshCommand(command, ip, port, user, pass, key) {
SecureShell.sshEmitter.on('data', data => {
dispatch(updateDataStream(data));
}).on('stderr', data => {
console.log(`Execute ERR: ${data.toString()}`);
}).on('error', error => {
dispatch(errorAction(error));
reject(error);
})
}
}
}
Enjoy!
Comments: 1
is it possible to use this to create a react native app that can connects to raspberry pi?