Netlify Node

path

declaration
 path 

var path = require("path"),
    model = require("./model"),
    Form  = require("./form").Form,
    Submission = require("./submission").Submission,
    File = require("./file").File,
    Snippet = require("./snippet").Snippet,
    Deploy = require("./deploy").Deploy;

if (typeof(require) !== 'undefined') {
  var glob   = require("glob"),
      path   = require("path"),
      crypto = require("crypto"),
      fs     = require("graceful-fs");
}

var Site = model.constructor();
Site.path = "/sites";

var globFiles = function(dir, cb) {
  glob("**/*", {cwd: dir}, function(err, files) {
    if (err) return cb(err);

    var filtered = files.filter(function(file) {
      return file.match(/(\/__MACOSX|\/\.)/) ? false : true;
    }).map(function(file) { return {rel: file, abs: path.resolve(dir, file)}; });

    filterFiles(filtered, cb);
  });
};

var filterFiles = function(filesAndDirs, cb) {
  var processed = [],
      files     = [],
      cbCalled  = false;
  filesAndDirs.forEach(function(fileOrDir) {
    fs.lstat(fileOrDir.abs, function(err, stat) {
      if (cbCalled) return null;
      if (err) { cbCalled = true; return cb(err); }
      if (stat.isFile()) {
        files.push(fileOrDir);
      }
      processed.push(fileOrDir);
      if (processed.length == filesAndDirs.length) {
        cb(null, files);
      }
    });
  });
};

var calculateShas = function(files, cb) {
  var shas = {},
      cbCalled = false,
      processed = [];

  files.forEach(function(file) {
    fs.readFile(file.abs, function(err, data) {
      if (cbCalled) return null;
      if (err) { cbCalled = true; return cb(err); }

      var shasum = crypto.createHash('sha1');
      shasum.update(data);
      shas[file.rel] = shasum.digest('hex');
      processed.push(file);
      if (processed.length == files.length) {
        cb(null, shas);
      }
    });
  });
};

var deployFromDir = function(site, dir, draft, cb) {
  var fullDir = path.resolve(dir);

  fs.stat(fullDir, function(err, stat) {
    if (err || stat.isFile()) return cb("No such dir " + dir + " (" + fullDir + ")");

    globFiles(fullDir, function(err, files) {
      calculateShas(files, function(err, filesWithShas) {
        site.client.request({
          url: site.apiPath + "/deploys",
          type: "post",
          body: JSON.stringify({
            files: filesWithShas,
            draft: draft
          })
        }, function(err, data) {
          if (err) return cb(err);
          var deploy = new Deploy(site.client, data);
          var shas = {};
          data.required.forEach(function(sha) { shas[sha] = true; });
          var filtered = files.filter(function(file) { return shas[filesWithShas[file.rel]]; });
          deploy.uploadFiles(filtered, function(err, deploy) {
            cb(err, deploy);
          });
        });
      });
    });
  });
};

var deployFromZip = function(site, zip, draft, cb) {
  var fullPath = zip.match(/^\//) ? zip : process.cwd() + "/" + zip;

  fs.readFile(fullPath, function(err, zipData) {
    if (err) return cb(err);

    var path = site.apiPath + "/deploys";

    if (draft) { path += "?draft=true"; }

    site.client.request({
      url: path,
      type: "post",
      body: zipData,
      contentType: "application/zip"
    }, function(err, data) {
      if (err) return cb(err);

      return cb(null, new Deploy(site.client, data));
    });
  });
};

var attributesForUpdate = function(attributes) {
  var mapping = {
        name: "name",
        customDomain: "custom_domain",
        notificationEmail: "notification_email",
        password: "password",
        github: "github",
        repo: "repo"
      },
      result = {};

  for (var key in attributes) {
    if (mapping[key]) result[mapping[key]] = attributes[key];
  }

  return result;
};

Site.createFromDir = function(client, dir, cb) {
  Site.create(client, {}, function(err, site) {
    site.createDeploy({dir: dir}, function(err, deploy) {
      site.deploy_id = deploy.id;
      cb(null, site);
    })
  });
};

Site.createFromZip = function(client, zip, cb) {
  Site.create(client, {}, function(err, site) {
    site.createDeploy({zip: zip}, function(err, deploy) {
      site.deploy_id = deploy.id;
      cb(null, site);
    })
  });
};

Site.create = function(client, attributes, cb) {
  client.create({model: Site, attributes: attributesForUpdate(attributes)}, cb);
};

Site.prototype = {
  isReady: function() {
    return this.state == "current";
  },
  refresh: function(cb) {
    var self = this;
    this.client.request({
      url: "/sites/" + this.id
    }, function(err, data, client) {
      if (err) return cb(err);
      Site.call(self, client, data);
      cb(null, self);
    });
  },

  update: function(attributes, cb) {
    attributes = attributes || {};

    var self = this;

    if (attributes.dir) {
      createFromDir(this.client, attributes.dir, this.id, cb);
    } else if (attributes.zip) {
      createFromZip(this.client, attributes.zip, this.id, cb);
    } else {
      this.client.update({model: Site, element: this, attributes: attributesForUpdate(attributes)}, cb);
    }
  },

  destroy: function(cb) {
    this.client.destroy({element: this}, cb);
  },

  createDeploy: function(attributes, cb) {
    if (attributes.dir) {
      deployFromDir(this, attributes.dir, attributes.draft || false, cb);
    } else if (attributes.zip) {
      deployFromZip(this, attributes.zip, attributes.draft || false, cb);
    } else {
      cb("You must specify a 'dir' or a 'zip' to deploy");
    }
  },

  createDraftDeploy: function(attributes, cb) {
    attributes.draft = true;
    this.createDeploy(attributes, cb);
  },

  waitForReady: function(cb) {
    var self = this;

    if (this.isReady()) {
      process.nextTick(function() { cb(null, self); });
    } else {
      setTimeout(function() {
        self.refresh(function(err) {
          if (err) return cb(err);
          self.waitForReady(cb);
        });
      }, 1000);
    }
  },

  forms: function(options, cb) {
    this.client.collection({prefix: this.apiPath, model: Form}, options, cb);
  },

  submissions: function(options, cb) {
    this.client.collection({prefix: this.apiPath, model: Submission}, options, cb);
  },

  files: function(cb) {
    this.client.collection({prefix: this.apiPath, model: File}, cb);
  },

  file: function(path, cb) {
    this.client.element({prefix: this.apiPath, model: File, id: path}, cb);
  },

  snippets: function(cb) {
    this.client.collection({prefix: this.apiPath, model: Snippet}, cb);
  },

  snippet: function(id, cb) {
    this.client.element({prefix: this.apiPath, model: Snippet, id: id}, cb);
  },

  createSnippet: function(attributes, cb) {
    this.client.create({prefix: this.apiPath, model: Snippet, attributes: attributes}, cb);
  },

  deploys: function(options, cb) {
    this.client.collection({prefix: this.apiPath, model: Deploy}, options, cb);
  },

  deploy: function(id, cb) {
    this.client.element({prefix: this.apiPath, model: Deploy, id: id}, cb);
  }
};

exports.Site = Site;