Source: index.js

'use strict';

/**
 * Linerate module
 */

var request = require('request');
var url = require('url');
var config = require('../config.json');

/**
 * @class
 */
function Linerate() {
  this.jar;

  this.rest_url = '/lrs/api/v1.0';

  this.defaults = {
    rest_url: '/lrs/api/v1.0',
    headers: {
      'content-type': 'application/json'
    }
  };

  // wrap request with defaults
  this.request = request.defaults({
    rejectUnauthorized: false
  });

}

/**
 * Create a session with LineRate
 * @function
 * @param {Object} opts - The connection options object.  Supported object properties:
 * @param {string} opts.host - Hostname or IP address
 * @param {integer} opts.port - Port number
 * @param {string} opts.username - Username
 * @param {string} opts.password - Password
 * @param {function} callback
 *
 * @example
 * connect({
 *    host: '127.0.0.1',
 *    port: 8443,
 *    username: 'admin',
 *    password: 'changeme'
 *  }, function() {
 *    // code here
 *  });
 *
 */
Linerate.prototype.connect = function(opts, callback) {

  var self = this;

  this.base_url = 'https://' + opts.host + ':' + opts.port;

  this.jar = request.jar();
  
  var options = {
    url: this.base_url + '/login',
    headers: {
      'content-type': 'application/x-www-form-urlencoded'
    },
    body: 'username=' + opts.username + '&password=' + opts.password,
    jar: self.jar
  };

  this.request.post(options, function(error, response) {
    if (error) {
      //throw new Error('connection error: ' + error);
      callback(error);
    }

    // successful login always returns a 302
    if (response.statusCode !== 302) {
      //throw new Error('Login failure: ' + response.statusCode);
      callback(error);
    }

    // and, conveniently, a failed login attempt returns a 302.
    // check for 302 and redirect back to login page for failed login
    if (response.statusCode === 302 && 
        url.parse(response.headers.location).pathname === '/login') {
      callback('Login failed');
    }


    //console.log(self.jar.getCookies(options.url));
    callback(null, 'connected');
  });

};

/**
 * Initiate a GET request
 * @param {Object} opts - The connection options object.  Supported object properties:
 * @param {string} opts.host - Hostname or IP address
 * @param {integer} opts.port - Port number
 * @param {string} opts.username - Username
 * @param {string} opts.password - Password
 * @param {function} callback
 *
 * @example
 * connect({
 *    host: '127.0.0.1',
 *    port: 8443,
 *    username: 'admin',
 *    password: 'changeme'
 *  }, function() {
 *    // code here
 *  });
 *
 */
Linerate.prototype.get = function(node, opts, callback) {

  // arguments fixup
  if (typeof opts === 'function') {
    callback = opts;
    opts = null;
  }

  var path = this.rest_url + node;

  if (opts) {
    if (opts.query) {
      path += '?' + opts.query;
    }
  }

  this.request({
      url: this.base_url + path,
      jar: this.jar,
      headers: this.defaults.headers,
      json: true
    },
    function(error, response) {
      if (error) { 
        throw new Error('err');
      }
      // pass callback in case of error, we can return
      // directly from check_response
      check_response(response, callback);
      callback(null, format_response(response.body));
    });
};


// opts should be:
// opts = {
//    'data': data
//    'type: type  - default to string
//    'default': default - default to false
//    'query': k:v pair that will get added to uri - some 
//     ... nodes accept a query string, like /exec/system/util/backup (key)
//     ... should maybe consider using an object so we can just use request.qs
// }
Linerate.prototype.put = function(node, opts, callback) {

  var path = this.rest_url + node;

  // arguments fixup
  // TODO: do we need this here?  I think opts will always be required
  // for PUT
  if (typeof opts === 'function') {
    callback = opts;
  }

  if (opts) {

    // if opts is a string, assuming all defaults except 'data'
    if (typeof opts === 'string') {
      opts = {
        data: opts,
        type: 'string',
        default: false
      };
    }
    else if (typeof opts === 'object') {
      if (!opts.data) {
        callback('missing data');
      }
      if (opts.query) {
        path += '?' + opts.query;
      }

      opts.type = opts.type || 'string';
      opts.default = opts.default || false;

    }
    else {
      callback('unrecognized opts argument');
    }
  }


  // note that json: true does 3 things:
  // formats body data as json
  // sets content-type header to application/json
  // parses response body as JSON
  this.request.put(
      {
        url: this.base_url + path,
        jar: this.jar,
        headers: this.defaults.headers,
        json: true,
        body: opts
      },
      function(error, response) {
        if (error) { 
          throw new Error('err');
        }
        check_response(response);
        callback(null, format_response(response.body));
      });
};

function check_response(response) {

  if (response.statusCode !== 200) {
    // TODO: change to callback error
    throw new Error('Non-200 response: ' +
        response.statusCode +
        ', ' +
        JSON.stringify(response.body)
        );
  }
  if (response.body.httpResponseCode !== 200) {
    // TODO: change to callback error
    throw new Error('Non-200 response 2: ' +
        response.httpResponseCode +
        ', ' +
        JSON.stringify(response.body)
        );
  }

}

function format_response(response) {
  // remember, response is already parsed JSON
  // let's just return the full output for now
  return response;
  //return response[response.requestPath].data;
}

Linerate.prototype.util_delete = function(file, callback) {
  var node = '/exec/system/util/delete';
  this.put(node, file, callback);
};

Linerate.prototype.backup_list = function(callback) {
  var node = '/status/system/util/backup/list';
  this.get(node, callback);
};

Linerate.prototype.backup_home = function(callback) {
  var node = '/exec/system/util/backup/home';
  this.put(node, callback);
};

module.exports = Linerate;