2013-05-16 15:21:13 +04:00
|
|
|
// # Ghost Data API
|
2014-06-03 17:05:25 +04:00
|
|
|
// Provides access from anywhere to the Ghost data layer.
|
|
|
|
//
|
|
|
|
// Ghost's JSON API is integral to the workings of Ghost, regardless of whether you want to access data internally,
|
|
|
|
// from a theme, an app, or from an external app, you'll use the Ghost JSON API to do so.
|
2013-05-16 15:21:13 +04:00
|
|
|
|
2018-09-10 15:30:48 +03:00
|
|
|
const {isEmpty} = require('lodash'),
|
2017-09-12 18:31:14 +03:00
|
|
|
Promise = require('bluebird'),
|
|
|
|
models = require('../models'),
|
2017-12-11 21:14:05 +03:00
|
|
|
urlService = require('../services/url'),
|
2017-09-12 18:31:14 +03:00
|
|
|
configuration = require('./configuration'),
|
|
|
|
db = require('./db'),
|
|
|
|
mail = require('./mail'),
|
|
|
|
notifications = require('./notifications'),
|
|
|
|
posts = require('./posts'),
|
|
|
|
schedules = require('./schedules'),
|
|
|
|
roles = require('./roles'),
|
|
|
|
settings = require('./settings'),
|
|
|
|
tags = require('./tags'),
|
|
|
|
invites = require('./invites'),
|
2017-09-21 18:01:03 +03:00
|
|
|
redirects = require('./redirects'),
|
2017-09-12 18:31:14 +03:00
|
|
|
clients = require('./clients'),
|
|
|
|
users = require('./users'),
|
|
|
|
slugs = require('./slugs'),
|
|
|
|
themes = require('./themes'),
|
|
|
|
subscribers = require('./subscribers'),
|
2014-06-20 13:15:01 +04:00
|
|
|
authentication = require('./authentication'),
|
2017-09-12 18:31:14 +03:00
|
|
|
uploads = require('./upload'),
|
2018-07-20 16:59:54 +03:00
|
|
|
exporter = require('../data/exporter'),
|
2017-09-12 18:31:14 +03:00
|
|
|
slack = require('./slack'),
|
2017-11-21 18:43:14 +03:00
|
|
|
webhooks = require('./webhooks'),
|
2018-09-10 15:30:48 +03:00
|
|
|
oembed = require('./oembed');
|
2013-08-30 15:20:30 +04:00
|
|
|
|
2018-09-10 15:30:48 +03:00
|
|
|
let http,
|
2014-07-19 16:20:16 +04:00
|
|
|
addHeaders,
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
cacheInvalidationHeader,
|
|
|
|
locationHeader,
|
2016-04-14 23:44:05 +03:00
|
|
|
contentDispositionHeaderExport,
|
2017-09-21 18:01:03 +03:00
|
|
|
contentDispositionHeaderSubscribers,
|
2018-07-12 14:40:37 +03:00
|
|
|
contentDispositionHeaderRedirects,
|
|
|
|
contentDispositionHeaderRoutes;
|
2013-06-25 15:43:15 +04:00
|
|
|
|
2017-03-20 21:02:44 +03:00
|
|
|
function isActiveThemeUpdate(method, endpoint, result) {
|
|
|
|
if (endpoint === 'themes') {
|
|
|
|
if (method === 'PUT') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (method === 'POST' && result.themes && result.themes[0] && result.themes[0].active === true) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2016-09-14 13:18:52 +03:00
|
|
|
}
|
|
|
|
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
/**
|
|
|
|
* ### Cache Invalidation Header
|
|
|
|
* Calculate the header string for the X-Cache-Invalidate: header.
|
|
|
|
* The resulting string instructs any cache in front of the blog that request has occurred which invalidates any cached
|
|
|
|
* versions of the listed URIs.
|
|
|
|
*
|
|
|
|
* `/*` is used to mean the entire cache is invalid
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {Express.request} req Original HTTP Request
|
|
|
|
* @param {Object} result API method result
|
2014-12-28 08:27:29 +03:00
|
|
|
* @return {String} Resolves to header string
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
*/
|
2015-05-30 23:18:26 +03:00
|
|
|
cacheInvalidationHeader = function cacheInvalidationHeader(req, result) {
|
2018-09-10 15:30:48 +03:00
|
|
|
const parsedUrl = req._parsedUrl.pathname.replace(/^\/|\/$/g, '').split('/'),
|
2013-09-24 19:21:43 +04:00
|
|
|
method = req.method,
|
2014-07-09 02:28:31 +04:00
|
|
|
endpoint = parsedUrl[0],
|
2016-05-19 14:49:22 +03:00
|
|
|
subdir = parsedUrl[1],
|
2014-04-22 05:04:30 +04:00
|
|
|
jsonResult = result.toJSON ? result.toJSON() : result,
|
2018-09-10 15:30:48 +03:00
|
|
|
INVALIDATE_ALL = '/*';
|
|
|
|
|
|
|
|
let post,
|
2014-06-04 02:38:15 +04:00
|
|
|
hasStatusChanged,
|
|
|
|
wasPublishedUpdated;
|
2013-11-03 21:13:19 +04:00
|
|
|
|
2017-03-20 21:02:44 +03:00
|
|
|
if (isActiveThemeUpdate(method, endpoint, result)) {
|
2016-09-14 13:18:52 +03:00
|
|
|
// Special case for if we're overwriting an active theme
|
|
|
|
return INVALIDATE_ALL;
|
|
|
|
} else if (['POST', 'PUT', 'DELETE'].indexOf(method) > -1) {
|
2016-05-19 14:49:22 +03:00
|
|
|
if (endpoint === 'schedules' && subdir === 'posts') {
|
|
|
|
return INVALIDATE_ALL;
|
|
|
|
}
|
2017-09-27 20:58:33 +03:00
|
|
|
if (['settings', 'users', 'db', 'tags', 'redirects'].indexOf(endpoint) > -1) {
|
2016-03-20 20:26:42 +03:00
|
|
|
return INVALIDATE_ALL;
|
2013-09-24 19:21:43 +04:00
|
|
|
} else if (endpoint === 'posts') {
|
2016-03-20 20:26:42 +03:00
|
|
|
if (method === 'DELETE') {
|
|
|
|
return INVALIDATE_ALL;
|
|
|
|
}
|
|
|
|
|
2014-04-22 05:04:30 +04:00
|
|
|
post = jsonResult.posts[0];
|
2014-06-04 02:38:15 +04:00
|
|
|
hasStatusChanged = post.statusChanged;
|
|
|
|
// Invalidate cache when post was updated but not when post is draft
|
|
|
|
wasPublishedUpdated = method === 'PUT' && post.status === 'published';
|
2014-04-22 05:04:30 +04:00
|
|
|
|
|
|
|
// Remove the statusChanged value from the response
|
2014-06-04 02:38:15 +04:00
|
|
|
delete post.statusChanged;
|
2014-04-22 05:04:30 +04:00
|
|
|
|
|
|
|
// Don't set x-cache-invalidate header for drafts
|
2016-03-20 20:26:42 +03:00
|
|
|
if (hasStatusChanged || wasPublishedUpdated) {
|
|
|
|
return INVALIDATE_ALL;
|
2015-05-18 22:56:41 +03:00
|
|
|
} else {
|
2018-04-17 12:36:05 +03:00
|
|
|
// routeKeywords.preview: 'p'
|
|
|
|
return urlService.utils.urlFor({relativeUrl: urlService.utils.urlJoin('/p', post.uuid, '/')});
|
2013-09-24 19:21:43 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ### Location Header
|
|
|
|
*
|
|
|
|
* If the API request results in the creation of a new object, construct a Location: header which points to the new
|
|
|
|
* resource.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {Express.request} req Original HTTP Request
|
|
|
|
* @param {Object} result API method result
|
2014-12-28 08:27:29 +03:00
|
|
|
* @return {String} Resolves to header string
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
*/
|
2015-05-30 23:18:26 +03:00
|
|
|
locationHeader = function locationHeader(req, result) {
|
2018-09-10 15:30:48 +03:00
|
|
|
const apiRoot = urlService.utils.urlFor('api');
|
|
|
|
let location,
|
2016-11-14 17:38:55 +03:00
|
|
|
newObject,
|
|
|
|
statusQuery;
|
2014-05-02 03:42:23 +04:00
|
|
|
|
|
|
|
if (req.method === 'POST') {
|
|
|
|
if (result.hasOwnProperty('posts')) {
|
2014-07-23 10:13:20 +04:00
|
|
|
newObject = result.posts[0];
|
2018-09-10 15:30:48 +03:00
|
|
|
statusQuery = `/?status=${newObject.status}`;
|
2017-12-11 21:14:05 +03:00
|
|
|
location = urlService.utils.urlJoin(apiRoot, 'posts', newObject.id, statusQuery);
|
2014-07-23 10:13:20 +04:00
|
|
|
} else if (result.hasOwnProperty('notifications')) {
|
|
|
|
newObject = result.notifications[0];
|
2017-12-11 21:14:05 +03:00
|
|
|
location = urlService.utils.urlJoin(apiRoot, 'notifications', newObject.id, '/');
|
2014-07-23 10:13:20 +04:00
|
|
|
} else if (result.hasOwnProperty('users')) {
|
|
|
|
newObject = result.users[0];
|
2017-12-11 21:14:05 +03:00
|
|
|
location = urlService.utils.urlJoin(apiRoot, 'users', newObject.id, '/');
|
2014-12-28 08:27:29 +03:00
|
|
|
} else if (result.hasOwnProperty('tags')) {
|
|
|
|
newObject = result.tags[0];
|
2017-12-11 21:14:05 +03:00
|
|
|
location = urlService.utils.urlJoin(apiRoot, 'tags', newObject.id, '/');
|
2017-11-21 18:43:14 +03:00
|
|
|
} else if (result.hasOwnProperty('webhooks')) {
|
|
|
|
newObject = result.webhooks[0];
|
2017-12-11 21:14:05 +03:00
|
|
|
location = urlService.utils.urlJoin(apiRoot, 'webhooks', newObject.id, '/');
|
2014-05-02 03:42:23 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-28 08:27:29 +03:00
|
|
|
return location;
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ### Content Disposition Header
|
|
|
|
* create a header that invokes the 'Save As' dialog in the browser when exporting the database to file. The 'filename'
|
|
|
|
* parameter is governed by [RFC6266](http://tools.ietf.org/html/rfc6266#section-4.3).
|
|
|
|
*
|
|
|
|
* For encoding whitespace and non-ISO-8859-1 characters, you MUST use the "filename*=" attribute, NOT "filename=".
|
|
|
|
* Ideally, both. Examples: http://tools.ietf.org/html/rfc6266#section-5
|
|
|
|
*
|
|
|
|
* We'll use ISO-8859-1 characters here to keep it simple.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @see http://tools.ietf.org/html/rfc598
|
|
|
|
* @return {string}
|
|
|
|
*/
|
2016-04-14 23:44:05 +03:00
|
|
|
|
|
|
|
contentDispositionHeaderExport = function contentDispositionHeaderExport() {
|
2016-03-12 21:54:06 +03:00
|
|
|
return exporter.fileName().then(function then(filename) {
|
2018-09-10 15:30:48 +03:00
|
|
|
return `Attachment; filename="${filename}"`;
|
2014-07-19 16:20:16 +04:00
|
|
|
});
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
};
|
|
|
|
|
2016-04-14 23:44:05 +03:00
|
|
|
contentDispositionHeaderSubscribers = function contentDispositionHeaderSubscribers() {
|
2018-09-10 15:30:48 +03:00
|
|
|
const datetime = (new Date()).toJSON().substring(0, 10);
|
|
|
|
return Promise.resolve(`Attachment; filename="subscribers.${datetime}.csv"`);
|
2016-04-14 23:44:05 +03:00
|
|
|
};
|
|
|
|
|
2017-09-21 18:01:03 +03:00
|
|
|
contentDispositionHeaderRedirects = function contentDispositionHeaderRedirects() {
|
|
|
|
return Promise.resolve('Attachment; filename="redirects.json"');
|
|
|
|
};
|
|
|
|
|
2018-07-12 14:40:37 +03:00
|
|
|
contentDispositionHeaderRoutes = () => {
|
|
|
|
return Promise.resolve('Attachment; filename="routes.yaml"');
|
|
|
|
};
|
|
|
|
|
2015-05-30 23:18:26 +03:00
|
|
|
addHeaders = function addHeaders(apiMethod, req, res, result) {
|
2018-09-10 15:30:48 +03:00
|
|
|
let cacheInvalidation,
|
2014-07-19 16:20:16 +04:00
|
|
|
location,
|
|
|
|
contentDisposition;
|
|
|
|
|
2014-12-28 08:27:29 +03:00
|
|
|
cacheInvalidation = cacheInvalidationHeader(req, result);
|
|
|
|
if (cacheInvalidation) {
|
|
|
|
res.set({'X-Cache-Invalidate': cacheInvalidation});
|
|
|
|
}
|
2014-07-19 16:20:16 +04:00
|
|
|
|
|
|
|
if (req.method === 'POST') {
|
2014-12-28 08:27:29 +03:00
|
|
|
location = locationHeader(req, result);
|
|
|
|
if (location) {
|
|
|
|
res.set({Location: location});
|
|
|
|
// The location header indicates that a new object was created.
|
|
|
|
// In this case the status code should be 201 Created
|
|
|
|
res.status(201);
|
|
|
|
}
|
2014-07-19 16:20:16 +04:00
|
|
|
}
|
|
|
|
|
2016-04-14 23:44:05 +03:00
|
|
|
// Add Export Content-Disposition Header
|
2014-07-19 16:20:16 +04:00
|
|
|
if (apiMethod === db.exportContent) {
|
2016-04-14 23:44:05 +03:00
|
|
|
contentDisposition = contentDispositionHeaderExport()
|
|
|
|
.then(function addContentDispositionHeaderExport(header) {
|
|
|
|
res.set({
|
|
|
|
'Content-Disposition': header
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add Subscribers Content-Disposition Header
|
|
|
|
if (apiMethod === subscribers.exportCSV) {
|
|
|
|
contentDisposition = contentDispositionHeaderSubscribers()
|
|
|
|
.then(function addContentDispositionHeaderSubscribers(header) {
|
|
|
|
res.set({
|
|
|
|
'Content-Disposition': header,
|
|
|
|
'Content-Type': 'text/csv'
|
|
|
|
});
|
2014-07-19 16:20:16 +04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-09-21 18:01:03 +03:00
|
|
|
// Add Redirects Content-Disposition Header
|
|
|
|
if (apiMethod === redirects.download) {
|
|
|
|
contentDisposition = contentDispositionHeaderRedirects()
|
|
|
|
.then(function contentDispositionHeaderRedirects(header) {
|
|
|
|
res.set({
|
|
|
|
'Content-Disposition': header,
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Content-Length': JSON.stringify(result).length
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-07-12 14:40:37 +03:00
|
|
|
// Add Routes Content-Disposition Header
|
|
|
|
if (apiMethod === settings.download) {
|
|
|
|
contentDisposition = contentDispositionHeaderRoutes()
|
|
|
|
.then((header) => {
|
|
|
|
res.set({
|
|
|
|
'Content-Disposition': header,
|
|
|
|
'Content-Type': 'application/yaml',
|
|
|
|
'Content-Length': JSON.stringify(result).length
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-12-28 08:27:29 +03:00
|
|
|
return contentDisposition;
|
2014-07-19 16:20:16 +04:00
|
|
|
};
|
|
|
|
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
/**
|
|
|
|
* ### HTTP
|
|
|
|
*
|
|
|
|
* Decorator for API functions which are called via an HTTP request. Takes the API method and wraps it so that it gets
|
|
|
|
* data from the request and returns a sensible JSON response.
|
|
|
|
*
|
|
|
|
* @public
|
|
|
|
* @param {Function} apiMethod API method to call
|
|
|
|
* @return {Function} middleware format function to be called by the route when a matching request is made
|
|
|
|
*/
|
2015-05-30 23:18:26 +03:00
|
|
|
http = function http(apiMethod) {
|
2015-06-14 22:07:52 +03:00
|
|
|
return function apiHandler(req, res, next) {
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
// We define 2 properties for using as arguments in API calls:
|
2018-08-30 19:30:36 +03:00
|
|
|
let object = req.body,
|
2018-09-10 15:30:48 +03:00
|
|
|
options = Object.assign({}, req.file, {ip: req.ip}, req.query, req.params, {
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
context: {
|
2018-08-03 16:14:38 +03:00
|
|
|
// @TODO: forward the client and user obj (options.context.user.id)
|
2016-11-09 18:01:07 +03:00
|
|
|
user: ((req.user && req.user.id) || (req.user && models.User.isExternalUser(req.user.id))) ? req.user.id : null,
|
2016-09-30 14:45:59 +03:00
|
|
|
client: (req.client && req.client.slug) ? req.client.slug : null,
|
|
|
|
client_id: (req.client && req.client.id) ? req.client.id : null
|
2014-05-07 09:28:51 +04:00
|
|
|
}
|
2014-01-03 04:37:21 +04:00
|
|
|
});
|
2014-05-05 17:51:21 +04:00
|
|
|
|
2018-08-30 19:30:36 +03:00
|
|
|
if (req.files) {
|
|
|
|
options.files = req.files;
|
|
|
|
}
|
|
|
|
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
// If this is a GET, or a DELETE, req.body should be null, so we only have options (route and query params)
|
|
|
|
// If this is a PUT, POST, or PATCH, req.body is an object
|
2018-09-10 15:30:48 +03:00
|
|
|
if (isEmpty(object)) {
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
object = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-05-07 09:28:51 +04:00
|
|
|
|
2014-12-28 08:27:29 +03:00
|
|
|
return apiMethod(object, options).tap(function onSuccess(response) {
|
|
|
|
// Add X-Cache-Invalidate, Location, and Content-Disposition headers
|
2015-05-28 16:58:52 +03:00
|
|
|
return addHeaders(apiMethod, req, res, (response || {}));
|
2015-05-30 23:18:26 +03:00
|
|
|
}).then(function then(response) {
|
2016-03-20 20:26:42 +03:00
|
|
|
if (req.method === 'DELETE') {
|
|
|
|
return res.status(204).end();
|
|
|
|
}
|
2018-07-12 14:40:37 +03:00
|
|
|
|
|
|
|
// Keep CSV, yaml formatting
|
|
|
|
if (res.get('Content-Type') && res.get('Content-Type').indexOf('text/csv') === 0 ||
|
|
|
|
res.get('Content-Type') && res.get('Content-Type').indexOf('application/yaml') === 0) {
|
2016-04-14 23:44:05 +03:00
|
|
|
return res.status(200).send(response);
|
|
|
|
}
|
2016-08-23 15:07:25 +03:00
|
|
|
|
|
|
|
// CASE: api method response wants to handle the express response
|
|
|
|
// example: serve files (stream)
|
2018-09-10 15:30:48 +03:00
|
|
|
if (typeof response === 'function') {
|
2016-08-23 15:07:25 +03:00
|
|
|
return response(req, res, next);
|
|
|
|
}
|
|
|
|
|
2014-12-28 08:27:29 +03:00
|
|
|
// Send a properly formatting HTTP response containing the data with correct headers
|
|
|
|
res.json(response || {});
|
2015-06-14 22:07:52 +03:00
|
|
|
}).catch(function onAPIError(error) {
|
|
|
|
// To be handled by the API middleware
|
|
|
|
next(error);
|
2014-12-28 08:27:29 +03:00
|
|
|
});
|
2013-06-08 09:05:40 +04:00
|
|
|
};
|
2013-06-25 15:43:15 +04:00
|
|
|
};
|
|
|
|
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
/**
|
|
|
|
* ## Public API
|
|
|
|
*/
|
2013-12-06 12:51:35 +04:00
|
|
|
module.exports = {
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
http: http,
|
|
|
|
// API Endpoints
|
2014-08-21 01:42:34 +04:00
|
|
|
configuration: configuration,
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
db: db,
|
|
|
|
mail: mail,
|
|
|
|
notifications: notifications,
|
2013-12-06 12:51:35 +04:00
|
|
|
posts: posts,
|
2016-05-19 14:49:22 +03:00
|
|
|
schedules: schedules,
|
2014-07-15 19:22:06 +04:00
|
|
|
roles: roles,
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
settings: settings,
|
2013-12-06 12:51:35 +04:00
|
|
|
tags: tags,
|
2015-08-31 16:59:56 +03:00
|
|
|
clients: clients,
|
2014-05-06 02:38:05 +04:00
|
|
|
users: users,
|
2014-06-20 13:15:01 +04:00
|
|
|
slugs: slugs,
|
2016-04-14 23:44:05 +03:00
|
|
|
subscribers: subscribers,
|
2014-07-15 14:40:14 +04:00
|
|
|
authentication: authentication,
|
2016-03-29 11:40:44 +03:00
|
|
|
uploads: uploads,
|
2016-08-23 15:07:25 +03:00
|
|
|
slack: slack,
|
2016-09-21 17:48:14 +03:00
|
|
|
themes: themes,
|
2017-09-21 18:01:03 +03:00
|
|
|
invites: invites,
|
2017-11-21 18:43:14 +03:00
|
|
|
redirects: redirects,
|
2018-06-05 17:49:23 +03:00
|
|
|
webhooks: webhooks,
|
|
|
|
oembed: oembed
|
2013-12-06 12:51:35 +04:00
|
|
|
};
|
2014-06-03 17:05:25 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ## API Methods
|
|
|
|
*
|
|
|
|
* Most API methods follow the BREAD pattern, although not all BREAD methods are available for all resources.
|
|
|
|
* Most API methods have a similar signature, they either take just `options`, or both `object` and `options`.
|
|
|
|
* For RESTful resources `object` is always a model object of the correct type in the form `name: [{object}]`
|
|
|
|
* `options` is an object with several named properties, the possibilities are listed for each method.
|
|
|
|
*
|
|
|
|
* Read / Edit / Destroy routes expect some sort of identifier (id / slug / key) for which object they are handling
|
|
|
|
*
|
|
|
|
* All API methods take a context object as one of the options:
|
|
|
|
*
|
|
|
|
* @typedef context
|
|
|
|
* Context provides information for determining permissions. Usually a user, but sometimes an app, or the internal flag
|
|
|
|
* @param {Number} user (optional)
|
|
|
|
* @param {String} app (optional)
|
|
|
|
* @param {Boolean} internal (optional)
|
|
|
|
*/
|