Using ssh2 in React and Redux Application

We create open-source because we love it, and we share our finding so everyone else can benefit as well.

react redux

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!

 

No Comments

Add your comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.