123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- var path = Npm.require("path");
- HTTP.publishFormats({
- fileRecordFormat: function (input) {
- // Set the method scope content type to json
- this.setContentType('application/json');
- if (FS.Utility.isArray(input)) {
- return EJSON.stringify(FS.Utility.map(input, function (obj) {
- return FS.Utility.cloneFileRecord(obj);
- }));
- } else {
- return EJSON.stringify(FS.Utility.cloneFileRecord(input));
- }
- }
- });
- /**
- * @method FS.HTTP.setHeadersForGet
- * @public
- * @param {Array} headers - List of headers, where each is a two-item array in which item 1 is the header name and item 2 is the header value.
- * @param {Array|String} [collections] - Which collections the headers should be added for. Omit this argument to add the header for all collections.
- * @returns {undefined}
- */
- FS.HTTP.setHeadersForGet = function setHeadersForGet(headers, collections) {
- if (typeof collections === "string") {
- collections = [collections];
- }
- if (collections) {
- FS.Utility.each(collections, function(collectionName) {
- getHeadersByCollection[collectionName] = headers || [];
- });
- } else {
- getHeaders = headers || [];
- }
- };
- /**
- * @method FS.HTTP.publish
- * @public
- * @param {FS.Collection} collection
- * @param {Function} func - Publish function that returns a cursor.
- * @returns {undefined}
- *
- * Publishes all documents returned by the cursor at a GET URL
- * with the format baseUrl/record/collectionName. The publish
- * function `this` is similar to normal `Meteor.publish`.
- */
- FS.HTTP.publish = function fsHttpPublish(collection, func) {
- var name = baseUrl + '/record/' + collection.name;
- // Mount collection listing URL using http-publish package
- HTTP.publish({
- name: name,
- defaultFormat: 'fileRecordFormat',
- collection: collection,
- collectionGet: true,
- collectionPost: false,
- documentGet: true,
- documentPut: false,
- documentDelete: false
- }, func);
- FS.debug && console.log("Registered HTTP method GET URLs:\n\n" + name + '\n' + name + '/:id\n');
- };
- /**
- * @method FS.HTTP.unpublish
- * @public
- * @param {FS.Collection} collection
- * @returns {undefined}
- *
- * Unpublishes a restpoint created by a call to `FS.HTTP.publish`
- */
- FS.HTTP.unpublish = function fsHttpUnpublish(collection) {
- // Mount collection listing URL using http-publish package
- HTTP.unpublish(baseUrl + '/record/' + collection.name);
- };
- _existingMountPoints = {};
- /**
- * @method defaultSelectorFunction
- * @private
- * @returns { collection, file }
- *
- * This is the default selector function
- */
- var defaultSelectorFunction = function() {
- var self = this;
- // Selector function
- //
- // This function will have to return the collection and the
- // file. If file not found undefined is returned - if null is returned the
- // search was not possible
- var opts = FS.Utility.extend({}, self.query || {}, self.params || {});
- // Get the collection name from the url
- var collectionName = opts.collectionName;
- // Get the id from the url
- var id = opts.id;
- // Get the collection
- var collection = FS._collections[collectionName];
- //if Mongo ObjectIds are used, then we need to use that in find statement
- if(collection.options.idGeneration && collection.options.idGeneration === 'MONGO') {
- // Get the file if possible else return null
- var file = (id && collection)? collection.findOne({ _id: new Meteor.Collection.ObjectID(id)}): null;
- } else {
- var file = (id && collection)? collection.findOne({ _id: id }): null;
- }
- // Return the collection and the file
- return {
- collection: collection,
- file: file,
- storeName: opts.store,
- download: opts.download,
- filename: opts.filename
- };
- };
- /*
- * @method FS.HTTP.mount
- * @public
- * @param {array of string} mountPoints mount points to map rest functinality on
- * @param {function} selector_f [selector] function returns `{ collection, file }` for mount points to work with
- *
- */
- FS.HTTP.mount = function(mountPoints, selector_f) {
- // We take mount points as an array and we get a selector function
- var selectorFunction = selector_f || defaultSelectorFunction;
- var accessPoint = {
- 'stream': true,
- 'auth': expirationAuth,
- 'post': function(data) {
- // Use the selector for finding the collection and file reference
- var ref = selectorFunction.call(this);
- // We dont support post - this would be normal insert eg. of filerecord?
- throw new Meteor.Error(501, "Not implemented", "Post is not supported");
- },
- 'put': function(data) {
- // Use the selector for finding the collection and file reference
- var ref = selectorFunction.call(this);
- // Make sure we have a collection reference
- if (!ref.collection)
- throw new Meteor.Error(404, "Not Found", "No collection found");
- // Make sure we have a file reference
- if (ref.file === null) {
- // No id supplied so we will create a new FS.File instance and
- // insert the supplied data.
- return FS.HTTP.Handlers.PutInsert.apply(this, [ref]);
- } else {
- if (ref.file) {
- return FS.HTTP.Handlers.PutUpdate.apply(this, [ref]);
- } else {
- throw new Meteor.Error(404, "Not Found", 'No file found');
- }
- }
- },
- 'get': function(data) {
- // Use the selector for finding the collection and file reference
- var ref = selectorFunction.call(this);
- // Make sure we have a collection reference
- if (!ref.collection)
- throw new Meteor.Error(404, "Not Found", "No collection found");
- // Make sure we have a file reference
- if (ref.file === null) {
- // No id supplied so we will return the published list of files ala
- // http.publish in json format
- return FS.HTTP.Handlers.GetList.apply(this, [ref]);
- } else {
- if (ref.file) {
- return FS.HTTP.Handlers.Get.apply(this, [ref]);
- } else {
- throw new Meteor.Error(404, "Not Found", 'No file found');
- }
- }
- },
- 'delete': function(data) {
- // Use the selector for finding the collection and file reference
- var ref = selectorFunction.call(this);
- // Make sure we have a collection reference
- if (!ref.collection)
- throw new Meteor.Error(404, "Not Found", "No collection found");
- // Make sure we have a file reference
- if (ref.file) {
- return FS.HTTP.Handlers.Del.apply(this, [ref]);
- } else {
- throw new Meteor.Error(404, "Not Found", 'No file found');
- }
- }
- };
- var accessPoints = {};
- // Add debug message
- FS.debug && console.log('Registered HTTP method URLs:');
- FS.Utility.each(mountPoints, function(mountPoint) {
- // Couple mountpoint and accesspoint
- accessPoints[mountPoint] = accessPoint;
- // Remember our mountpoints
- _existingMountPoints[mountPoint] = mountPoint;
- // Add debug message
- FS.debug && console.log(mountPoint);
- });
- // XXX: HTTP:methods should unmount existing mounts in case of overwriting?
- HTTP.methods(accessPoints);
- };
- /**
- * @method FS.HTTP.unmount
- * @public
- * @param {string | array of string} [mountPoints] Optional, if not specified all mountpoints are unmounted
- *
- */
- FS.HTTP.unmount = function(mountPoints) {
- // The mountPoints is optional, can be string or array if undefined then
- // _existingMountPoints will be used
- var unmountList;
- // Container for the mount points to unmount
- var unmountPoints = {};
- if (typeof mountPoints === 'undefined') {
- // Use existing mount points - unmount all
- unmountList = _existingMountPoints;
- } else if (mountPoints === ''+mountPoints) {
- // Got a string
- unmountList = [mountPoints];
- } else if (mountPoints.length) {
- // Got an array
- unmountList = mountPoints;
- }
- // If we have a list to unmount
- if (unmountList) {
- // Iterate over each item
- FS.Utility.each(unmountList, function(mountPoint) {
- // Check _existingMountPoints to make sure the mount point exists in our
- // context / was created by the FS.HTTP.mount
- if (_existingMountPoints[mountPoint]) {
- // Mark as unmount
- unmountPoints[mountPoint] = false;
- // Release
- delete _existingMountPoints[mountPoint];
- }
- });
- FS.debug && console.log('FS.HTTP.unmount:');
- FS.debug && console.log(unmountPoints);
- // Complete unmount
- HTTP.methods(unmountPoints);
- }
- };
- // ### FS.Collection maps on HTTP pr. default on the following restpoints:
- // *
- // baseUrl + '/files/:collectionName/:id/:filename',
- // baseUrl + '/files/:collectionName/:id',
- // baseUrl + '/files/:collectionName'
- //
- // Change/ replace the existing mount point by:
- // ```js
- // // unmount all existing
- // FS.HTTP.unmount();
- // // Create new mount point
- // FS.HTTP.mount([
- // '/cfs/files/:collectionName/:id/:filename',
- // '/cfs/files/:collectionName/:id',
- // '/cfs/files/:collectionName'
- // ]);
- // ```
- //
- mountUrls = function mountUrls() {
- // We unmount first in case we are calling this a second time
- FS.HTTP.unmount();
- FS.HTTP.mount([
- baseUrl + '/files/:collectionName/:id/:filename',
- baseUrl + '/files/:collectionName/:id',
- baseUrl + '/files/:collectionName'
- ]);
- };
- // Returns the userId from URL token
- var expirationAuth = function expirationAuth() {
- var self = this;
- // Read the token from '/hello?token=base64'
- var encodedToken = self.query.token;
- FS.debug && console.log("token: "+encodedToken);
- if (!encodedToken || !Meteor.users) return false;
- // Check the userToken before adding it to the db query
- // Set the this.userId
- var tokenString = FS.Utility.atob(encodedToken);
- var tokenObject;
- try {
- tokenObject = JSON.parse(tokenString);
- } catch(err) {
- throw new Meteor.Error(400, 'Bad Request');
- }
- // XXX: Do some check here of the object
- var userToken = tokenObject.authToken;
- if (userToken !== ''+userToken) {
- throw new Meteor.Error(400, 'Bad Request');
- }
- // If we have an expiration token we should check that it's still valid
- if (tokenObject.expiration != null) {
- // check if its too old
- var now = Date.now();
- if (tokenObject.expiration < now) {
- FS.debug && console.log('Expired token: ' + tokenObject.expiration + ' is less than ' + now);
- throw new Meteor.Error(500, 'Expired token');
- }
- }
- // We are not on a secure line - so we have to look up the user...
- var user = Meteor.users.findOne({
- $or: [
- {'services.resume.loginTokens.hashedToken': Accounts._hashLoginToken(userToken)},
- {'services.resume.loginTokens.token': userToken}
- ]
- });
- // Set the userId in the scope
- return user && user._id;
- };
- HTTP.methods(
- {'/cfs/servertime': {
- get: function(data) {
- return Date.now().toString();
- }
- }
- });
- // Unify client / server api
- FS.HTTP.now = function() {
- return Date.now();
- };
- // Start up the basic mount points
- Meteor.startup(function () {
- mountUrls();
- });
|