Netlify Node

base64

declaration
 base64 

var base64 = require('base64-js'),
    http   = require('http'),
    https  = require('https');

var Client = function(options) {
  options = options || {};
  this.access_token  = options.access_token;
  this.client_id     = options.client_id;
  this.client_secret = options.client_secret;
  this.redirect_uri  = options.redirect_uri;
  this.ENDPOINT      = options.endpoint || 'https://api.netlify.com';
  this.VERSION       = options.version  || 'v1';
  this.hostname = this.ENDPOINT.match(/^https?:\/\/([^:]+)/)[1];
  this.ssl = this.ENDPOINT.match(/^https:\/\//)
  this.port = this.ssl ? 443 : (this.ENDPOINT.match(/:(\d+)$/) || [])[1] || 80;
  this.http = options.http || (this.ssl ? https : http);
};

Client.models = {
  Site: require("./site").Site,
  Form: require("./form").Form,
  Submission: require("./submission").Submission,
  User: require("./user").User,
  Snippet: require("./snippet").Snippet,
  Deploy: require("./deploy").Deploy,
  DeployKey: require("./deploy_key").DeployKey,
  DnsZone: require("./dns-zone").DnsZone,
  AccessToken: require("./access-token").AccessToken,
  Ticket: require("./ticket").Ticket
};

var stringToByteArray = function(str) {
  return Array.prototype.map.call(str, function (char) { return char.charCodeAt(0); });
};

var prepareBody = function(body, headers) {
  return typeof(body) == "string" ? body : (headers['Content-Type'] == "application/json" ? JSON.stringify(body) : body);
};

Client.prototype = {
  isAuthorized: function() { return !(this.access_token == null); },

  authorizeFromCredentials: function(cb) {
    var client = this;

    if (!(this.client_id && this.client_secret)) {
      return cb("Instantiate the client with a client_id and client_secret to authorize from credentials");
    }

    this.request({
      type: "post",
      url: "/oauth/token",
      raw_path: true,
      contentType: "application/x-www-form-urlencoded",
      auth: {
        user: this.client_id,
        password: this.client_secret
      },
      body: "grant_type=client_credentials"
    }, function(err, data) {
      if (err) return cb(err);
      client.access_token = data.access_token;
      cb(null, data.access_token);
    });
  },

  authorizeFromCode: function(code, cb) {
    var client = this;

    if (!(this.client_id && this.client_secret && this.redirect_uri)) {
      return cb("Instantiate the client with a client_id, client_secret and redirect_uri to authorize from code");
    }

    this.request({
      type: "post",
      url: "/oath/token",
      raw_path: true,
      contentType: "application/x-www-form-urlencoded",
      auth: {
        user: this.client_id,
        password: this.client_secret
      },
      body: [
        "grant_type=authorization_code",
        "code=" + code,
        "redirect_uri=" + encodeURIComponent(this.redirect_uri)
      ].join("&")
    }, function(err, data) {
      if (err) return cb(err);
      client.access_token = data.access_token;
      cb(null, data.access_token);
    });
  },

  authorizeUrl: function(options) {
    if (!(this.client_id && this.redirect_uri)) {
      throw("Instantiate the client with a client_id and a redirect_uri to generate an authorizeUrl");
    }
    return this.ENDPOINT + "/oauth/authorize?" + [
      "response_type=code",
      "client_id=" + this.client_id,
      "redirect_uri=" + encodeURIComponent(this.redirect_uri)
    ].join("&");
  },

  sites: function(options, cb) { this.collection({model: Client.models.Site}, options, cb); },

  site: function(id, cb) { this.element({model: Client.models.Site, id: id}, cb); },

  createSite: function(options, cb) {
    this.withAuthorization(cb, function() {
      if (options.dir) {
        Client.models.Site.createFromDir(this, options.dir, cb);
      } else if (options.zip) {
        Client.models.Site.createFromZip(this, options.zip, cb);
      } else {
        Client.models.Site.create(this, options, cb);
      }
    });
  },

  forms: function(cb) { this.collection({model: Client.models.Form}, cb); },

  form: function(id, cb) { this.element({model: Client.models.Form, id: id}, cb); },

  submissions: function(options, cb) { this.collection({model: Client.models.Submission}, options, cb); },

  submission: function(id, cb) { this.element({model: Client.models.Submission, id: id}, cb); },

  users: function(options, cb) { this.collection({model: Client.models.User}, options, cb); },

  user: function(id, cb) { this.element({model: Client.models.User, id: id}, cb); },

  createUser: function(attributes, cb) {
    this.create({model: Client.models.User, attributes: attributes}, cb);
  },

  dnsZones: function(options, cb) { this.collection({model: Client.models.DnsZone}, options, cb); },

  dnsZone: function(id, cb) { this.element({model: Client.models.DnsZone, id: id}, cb); },

  createDnsZone: function(attributes, cb) {
    this.create({model: Client.models.DnsZone, attributes: attributes}, cb);
  },

  accessToken: function(id, cb) { this.element({model: Client.models.AccessToken, id: id}, cb); },

  createAccessToken: function(attributes, cb) {
    this.create({model: Client.models.AccessToken, attributes: attributes}, cb);
  },

  createDeployKey: function(attributes, cb) {
    this.create({model: Client.models.DeployKey, attributes: attributes}, cb);
  },

  ticket: function(id, cb) { this.element({model: Client.models.Ticket, id: id}, cb); },

  createTicket: function(cb) {
    this.request({
      url: Client.models.Ticket.path,
      type: "post",
      body: {client_id: this.client_id}
    }, function(err, data, client) {
      if (err) return cb(err);
      cb(null, new Client.models.Ticket(client, data));
    });
  },

  collection: function(options, pagination, cb) {
    if (cb === undefined) { cb = pagination; pagination = {}}
    var params = [];
    if (pagination.page) { params.push(["page", pagination.page].join("=")) }
    if (pagination.per_page) { params.push(["per_page", pagination.per_page].join("=")) }
    this.withAuthorization(cb, function() {
      this.request({
        url: (options.prefix || "") + options.model.path + (params.length ? "?" + params.join("&") : "")
      }, function(err, collection, client, meta) {
        if (err) return cb(err);
        var result = collection.map(function(data) { return new options.model(client, data); });
        result.meta = meta
        cb(null, result);
      })
    });
  },

  element: function(options, cb) {
    this.withAuthorization(cb, function() {
      this.request({
        url: (options.prefix || "" ) + options.model.path + "/" + options.id
      }, function(err, data, client) {
        if (err) return cb(err);
        cb(null, new options.model(client, data));
      });
    });
  },

  create: function(options, cb) {
    this.withAuthorization(cb, function() {
      this.request({
        url: (options.prefix || "") + options.model.path,
        type: "post",
        body: options.attributes
      }, function(err, data, client) {
        if (err) return cb(err);
        cb(null, new options.model(client, data));
      });
    });
  },

  update: function(options, cb) {
    this.withAuthorization(cb, function() {
      this.request({
        url: (options.prefix || "") + options.element.apiPath,
        type: "put",
        body: options.attributes
      }, function(err, data, client) {
        if (err) return cb(err);
        options.model.call(options.element, client, data);
        cb(null, options.element);
      });
    });
  },

  destroy: function(options, cb) {
    this.withAuthorization(cb, function() {
      this.request({
        url: (options.prefix || "") + options.element.apiPath,
        type: "delete",
        ignoreResponse: true
      }, function(err) {
        return cb(err);
      });
    });
  },

  request: function(options, cb) {
    var client  = this,
        http    = this.http,
        path    = options.raw_path ? options.url : "/api/" + this.VERSION + options.url,
        headers = options.headers || {},
        retries = options.retries ? options.retries : options.retries === 0 ? 0 : 3,
        body    = null;

    headers['Content-Type'] = options.contentType || "application/json";

    if (options.body) {
      body = prepareBody(options.body, headers);
    }

    headers['Content-Length'] = body ? (typeof body == "string" ? Buffer.byteLength(body) : body.length) : 0;

    if (this.access_token && !options.auth) {
      headers['Authorization'] = "Bearer " + this.access_token
    }

    var requestOptions = {
      method: (options.type || "get").toLowerCase(),
      hostname: this.hostname,
      path: path,
      port: this.port,
      headers: headers,
      auth: options.auth ? options.auth.user + ":" + options.auth.password : null,
    }

    var request = http.request(requestOptions, function(res) {
      var body = "",
          data = null;

      res.on("data", function(chunk) {
        body += chunk;
      });
      res.on("end", function() {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          if (body && !options.ignoreResponse) {
            data = JSON.parse(body);
          }
          cb(null, data, client, client.metadata(res));
        } else if (res.statusCode == 401 || res.statusCode == 403) {
          cb("Authentication failed", null, client, client.metadata(res));
        } else {
          if ((requestOptions.method === "get" ||
               requestOptions.method === "put" ||
               requestOptions.method === "delete") &&
              (retries > 0 && res.statusCode !== 422 && res.statusCode !== 404)) {
            options.retries = retries - 1;
            setTimeout(function() { client.request(options, cb); }, 500);
          } else {
            cb(body, null, client, client.metadata(res));
          }
        }
      });
    });

    request.setTimeout(300000, function() {
      request.abort();
    });

    request.on("error", function(err) {
      if ((requestOptions.method == "get" ||
           requestOptions.method == "put" ||
           requestOptions.method == "delete") &&
           retries > 0) {
        options.retries = retries - 1;
        setTimeout(function() { client.request(options, cb); }, 500);
      } else {
        cb(err, null, client);
      }
    });

    if (body) {
      request.write(body);
    }
    request.end();
  },

  withAuthorization: function(cb, fn) {
    if (!this.isAuthorized()) return cb("Not authorized: Instantiate client with access_token");
    fn.call(this);
  },

  metadata: function(res) {
    var meta = {},
        links     = res.getHeader && res.getHeader("Link"),
        limit     = res.getHeader && res.getHeader("X-Ratelimit-Limit"),
        remaining = res.getHeader && res.getHeader("X-Ratelimit-Remaining"),
        reset     = res.getHeader && res.getHeader("X-Ratelimit-Reset");

    if (links) {
      meta.pagination = {}
      for (var part in links.split(",")) {
        var link = part.replace(/(^\s*|\s*$)/, '');
        var segments = link.split(";");
        var m = segments[0].match(/page=(\d+)/);
        var page = m && parseInt(m[1],10);
        m = segments[0].match(/^\<(.+)\>\s*$/);

        if (segments[1].match(/last/)) {
          meta.pagination.last = page;
        } else if (segments[1].match(/next/)) {
          meta.pagination.next = page;
        } else if (segments[1].match(/prev/)) {
          meta.pagination.prev = page;
        } else if (segments[1].match(/first/)) {
          meta.pagination.first = page;
        }
      }
    }
    if (limit) {
      meta.rateLimit = limit;
    }
    if (remaining) {
      meta.rateRemaining = remaining;
    }
    if (reset) {
      meta.rateReset = reset;
    }
    return meta;
  }
};

exports.Client = Client;