Ghost/core/server/api/schedules.js
Hannah Wolfe 4237446277 Misc cleanup & consistency amends (#9002)
no issue

- Consistent naming for postLookup
   - makes it easier to search and inspect the various usages
- Cleanup unneeded code
- Make res.render calls more consistent
- add some consistency to the calls to res.render
- Remove ancient reference to dataProvider
- Let's call it models everywhere now...
- Use consistent formatting across the API
- we're no longer using alignment in vars
- Misc other consistency changes in API
- always refer to local utils as apiUtils
- logical grouping of requires - dependencies, utils, "lib common" etc
- use xAPI to refer to API endpoints, e.g. mailAPI, settingsAPI for clarity
2017-09-12 17:31:14 +02:00

101 lines
3.9 KiB
JavaScript

var Promise = require('bluebird'),
_ = require('lodash'),
moment = require('moment'),
pipeline = require('../utils/pipeline'),
apiUtils = require('./utils'),
models = require('../models'),
config = require('../config'),
errors = require('../errors'),
i18n = require('../i18n'),
postsAPI = require('../api/posts');
/**
* Publish a scheduled post
*
* `apiPosts.read` and `apiPosts.edit` must happen in one transaction.
* We lock the target row on fetch by using the `forUpdate` option.
* Read more in models/post.js - `onFetching`
*
* object.force: you can force publishing a post in the past (for example if your service was down)
*/
exports.publishPost = function publishPost(object, options) {
if (_.isEmpty(options)) {
options = object || {};
object = {};
}
var post, publishedAtMoment,
publishAPostBySchedulerToleranceInMinutes = config.get('times').publishAPostBySchedulerToleranceInMinutes;
// CASE: only the scheduler client is allowed to publish (hardcoded because of missing client permission system)
if (!options.context || !options.context.client || options.context.client !== 'ghost-scheduler') {
return Promise.reject(new errors.NoPermissionError({message: i18n.t('errors.permissions.noPermissionToAction')}));
}
options.context = {internal: true};
return pipeline([
apiUtils.validate('posts', {opts: apiUtils.idDefaultOptions}),
function (cleanOptions) {
cleanOptions.status = 'scheduled';
return models.Base.transaction(function (transacting) {
cleanOptions.transacting = transacting;
cleanOptions.forUpdate = true;
// CASE: extend allowed options, see api/utils.js
cleanOptions.opts = ['forUpdate', 'transacting'];
return postsAPI.read(cleanOptions)
.then(function (result) {
post = result.posts[0];
publishedAtMoment = moment(post.published_at);
if (publishedAtMoment.diff(moment(), 'minutes') > publishAPostBySchedulerToleranceInMinutes) {
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.job.notFound')}));
}
if (publishedAtMoment.diff(moment(), 'minutes') < publishAPostBySchedulerToleranceInMinutes * -1 && object.force !== true) {
return Promise.reject(new errors.NotFoundError({message: i18n.t('errors.api.job.publishInThePast')}));
}
return postsAPI.edit({
posts: [{status: 'published'}]},
_.pick(cleanOptions, ['context', 'id', 'transacting', 'forUpdate', 'opts'])
);
});
});
}
], options);
};
/**
* get all scheduled posts/pages
* permission check not needed, because route is not exposed
*/
exports.getScheduledPosts = function readPosts(options) {
options = options || {};
options.context = {internal: true};
return pipeline([
apiUtils.validate('posts', {opts: ['from', 'to']}),
function (cleanOptions) {
cleanOptions.filter = 'status:scheduled';
cleanOptions.columns = ['id', 'published_at', 'created_at'];
if (cleanOptions.from) {
cleanOptions.filter += '+created_at:>=\'' + moment(cleanOptions.from).format('YYYY-MM-DD HH:mm:ss') + '\'';
}
if (cleanOptions.to) {
cleanOptions.filter += '+created_at:<=\'' + moment(cleanOptions.to).format('YYYY-MM-DD HH:mm:ss') + '\'';
}
return models.Post.findAll(cleanOptions)
.then(function (result) {
return Promise.resolve({posts: result.models});
});
}
], options);
};