UNPKG

@yawetse/pkgcloud

Version:

An infrastructure-as-a-service agnostic cloud library for node.js

429 lines (369 loc) 12.4 kB
/* * cdn-containers.js: Instance methods for working with containers from Rackspace Cloudfiles * * (C) 2011 Charlie Robbins, Ken Perkins, Ross Kukulinski & the Contributors. * */ var async = require('async'), crypto = require('crypto'), _ = require('underscore'); /** * client.getFiles * * @description get the list of containers for an account * * @param {object|Function} options * @param {Number} [options.limit] the number of records to return * @param {String} [options.marker] the id of the first record to return in the current query * @param {Function} callback */ exports.getContainers = function (options, callback) { var self = this; if (typeof options === 'function') { callback = options; options = {}; } var getContainerOpts = { path: '', qs: _.extend({ format: 'json' }, _.pick(options, ['limit', 'marker', 'end_marker'])) }; this._request(getContainerOpts, function (err, body) { if (err) { return callback(err); } else if (!body || !(body instanceof Array)) { return new Error('Malformed API Response'); } if (!options.loadCDNAttributes) { return callback(null, body.map(function (container) { return new self.models.Container(self, container); })); } else { var containers = []; async.forEachLimit(body, 10, function (c, next) { var container = new self.models.Container(self, c); containers.push(container); container.refreshCdnDetails(function (err) { if (err) { return next(err); } next(); }); }, function (err) { callback(err, containers); }); } }); }; /** * client.getContainer * * @description get the details for a specific container * * @param {String|object} container the container or containerName * @param callback */ exports.getContainer = function (container, callback) { var containerName = container instanceof this.models.Container ? container.name : container, self = this; this._request({ method: 'HEAD', container: containerName }, function (err, body, res) { if (err) { return callback(err); } self._getCdnContainerDetails(containerName, function (err, details) { if (err) { return callback(err); } container = _.extend({}, details, { name: containerName, count: parseInt(res.headers['x-container-object-count'], 10), bytes: parseInt(res.headers['x-container-bytes-used'], 10) }); container.metadata = self.deserializeMetadata(self.CONTAINER_META_PREFIX, res.headers); callback(null, new self.models.Container(self, container)); }); }); }; /** * client.getCdnContainers * * @description get the list of cdn enabled containers * * @param {object|Function} options * @param {Number} [options.limit] the number of records to return * @param {String} [options.marker] the id of the first record to return in the current query * @param {String} [options.end_marker] the id of the last record to return in the current query * @param {boolean} [options.enabled_only] only get containers which are cdn enabled = true * @param {Function} callback */ exports.getCdnContainers = function (options, callback) { var self = this; if (typeof options === 'function') { callback = options; options = {}; } var getContainerOpts = { path: '', serviceType: this.cdnServiceType, qs: _.extend({ format: 'json' }, _.pick(options, ['limit', 'marker', 'end_marker'])) }; if (options.cdnOnly) { getContainerOpts.qs['enabled_only'] = options.cdnOnly; } this._request(getContainerOpts, function (err, body) { return err ? callback(err) : callback(null, body.map(function (container) { // // The cdn properties are normally set in response headers // when requesting single cdn containers // container.cdnEnabled = container.cdn_enabled == 'true'; container.logRetention = container.log_retention == 'true'; container.cdnUri = container.cdn_uri; container.cdnSslUri = container.cdn_ssl_uri; return new self.models.Container(self, container); })); }); }; /** * client.getCdnContainer * * @description get the details for a specific cdn enabled container * * @param {String|object} container the container or containerName * @param callback */ exports.getCdnContainer = function (container, callback) { var self = this; this._getCdnContainerDetails(container, function(err, details) { return err ? callback(err) : callback(null, new self.models.Container(self, details)); }); }; /** * client.setCdnEnabled * * @description enable or disable cdn capabilities on a storage container * * @param {String|object} container the container or containerName * @param {object|boolean} options an object with options, or boolean to just enable/disable * @param {boolean} options.enabled enable or disable the cdn capability * @param {number} options.ttl configure the CDN ttl for this container * @param callback */ exports.setCdnEnabled = function (container, options, callback) { var self = this, containerName = container instanceof self.models.Container ? container.name : container, enabled = typeof options === 'boolean' ? options : options.enabled; if (typeof options === 'function') { callback = options; options = {}; enabled = true; } var cdnOpts = { method: 'PUT', container: containerName, serviceType: this.cdnServiceType, headers: { 'x-cdn-enabled': enabled } }; if (options.ttl) { cdnOpts.headers['x-ttl'] = options.ttl; } self._request(cdnOpts, function(err) { if (err) { return callback(err); } self.getContainer(containerName, function(err, container) { return err ? callback(err) : callback(err, container); }); }); }; /** * client.updateCdnContainer * * @description update the settings for a cdn container * * @param {String|object} container the container or containerName * @param {object} options * @param {boolean} options.enabled enable or disable the cdn capability * @param {number} options.ttl configure the CDN ttl for this container * @param {boolean} options.logRetention enable log retention for this container * @param callback */ exports.updateCdnContainer = function (container, options, callback) { var self = this, containerName = container instanceof self.models.Container ? container.name : container; var cdnOpts = { method: 'POST', container: containerName, serviceType: this.cdnServiceType, headers: {} }; if (options.ttl) { cdnOpts.headers['x-ttl'] = options.ttl; } if (typeof options.enabled === 'boolean') { cdnOpts.headers['x-cdn-enabled'] = options.enabled; } if (typeof options.logRetention === 'boolean') { cdnOpts.headers['x-log-retention'] = options.logRetention; } self._request(cdnOpts, function (err) { if (err) { return callback(err); } self.getContainer(containerName, function (err, container) { return err ? callback(err) : callback(err, container); }); }); }; /** * client._getCdnContainerDetails * * @description Convenience function for getting CDN container details */ exports._getCdnContainerDetails = function(container, callback) { var containerName = container instanceof this.models.Container ? container.name : container, self = this; this._request({ method: 'HEAD', container: containerName, serviceType: this.cdnServiceType }, function (err, body, res) { if (err && !(err.statusCode === 403 || err.statusCode === 404)) { return callback(err); } else if (err) { return callback(null, {}); // return empty object } container = { name: containerName, count: parseInt(res.headers['x-container-object-count'], 10), bytes: parseInt(res.headers['x-container-bytes-used'], 10) }; container.cdnUri = res.headers['x-cdn-uri']; container.cdnSslUri = res.headers['x-cdn-ssl-uri']; container.cdnEnabled = res.headers['x-cdn-enabled'] == 'True'; container.cdnStreamingUri = res.headers['x-cdn-streaming-uri']; container.cdniOSUri = res.headers['x-cdn-ios-uri']; container.ttl = parseInt(res.headers['x-ttl'], 10); container.logRetention = res.headers['x-log-retention'] == 'True'; container.metadata = self.deserializeMetadata(self.CONTAINER_META_PREFIX, res.headers); callback(null, container); }); }; /** * client.setStaticWebsite * * @description set the static website index page on a storage container * * @param {String|object} container the container or containerName * @param {object} options an object with options * @param {String} options.indexFile configure the static website index file for this container * @param {String} options.errorFile configure the static website error file for this container * @param callback */ exports.setStaticWebsite = function (container, options, callback) { var indexFile = options.indexFile, errorFile = options.errorFile; if (typeof options === 'function') { callback = options; indexFile = 'index.html'; errorFile = 'error.html'; } container.metadata['web-index'] = indexFile; container.metadata['web-error'] = errorFile; this.updateContainerMetadata(container, callback); }; /** * client.removeStaticWebsite * * @description remove the static website index/error page on a storage container * * @param {String|object} container the container or containerName * @param callback */ exports.removeStaticWebsite = function (container, callback) { var indexFile = container.metadata['web-index'], errorFile = container.metadata['web-error'], metadata = { 'web-index': indexFile, 'web-error': errorFile }; this.removeContainerMetadata(container, metadata, callback); }; /** * client.setTemporaryUrlKey * * @description set a temporaryUrl key on the current account * * @param {String} key the secret key to be used in hmac signing temporary urls * @param callback */ exports.setTemporaryUrlKey = function(key, callback) { this._request({ method: 'POST', headers: { 'X-Account-Meta-Temp-Url-Key': key } }, function (err) { callback(err); }); }; /** * client.generateTempUrl * * @description create a temporary url for GET/PUT to a cloud files container * * @param {String|object} container the container or container name for the url * @param {String|object} file the file or fileName for the url * @param {String} method either GET or PUT * @param {Number} time expiry for the url in seconds (from now) * @param {String} key the secret key to be used for signing the url * @param callback */ exports.generateTempUrl = function(container, file, method, time, key, callback) { var containerName = container instanceof this.models.Container ? container.name : container, fileName = file instanceof this.models.File ? file.name : file, time = typeof time === 'number' ? time : parseInt(time), self = this, split = '/v1'; function createUrl() { // construct our hmac signature var expiry = parseInt(new Date().getTime() / 1000) + time, url = self._getUrl({ container: containerName, path: fileName }), hmac_body = method.toUpperCase() + '\n' + expiry + '\n' + split + url.split(split)[1]; var hash = crypto.createHmac('sha1', key).update(hmac_body).digest('hex'); callback(null, url + '?temp_url_sig=' + hash + '&temp_url_expires=' + expiry); } // We have to be authed to make sure we have the service catalog // this is required to validate the service url if (!this._isAuthorized()) { this.auth(function(err) { if (err) { callback(err); return; } createUrl(); }); return; } createUrl(); };