mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-21 01:41:46 +03:00
00f95e7328
closes #10060
- Implemented scheduling for posts and pages
- Added cache invalidation when scheduling
- Refactored admin token eneration function to accept existing key as parameter in tests
- Added Ghost Scheduler Integration fixture
- Added fixture for permissions for post publish action
- Migrated getScheduled method to v2
- Did not add support for 'from' and 'to' parameters as they were not used by DefaultScheduler
- This method needs rethinking in a long run as it's an ugly hack and should rather become proper endpoint that returns JSON data instead of models
- Removed unused auth middleware from v2 routes
- Added internal scheduler role
- Implemetnted transactions in v2 frame
- This takes into account scenario mentioned in c93f03b87e
- Specifically:
>if two queries happening in a transaction we have to signalise
knex/mysql that we select for an update
otherwise the following case happens:
you fetch posts for an update
a user requests comes in and updates the post (e.g. sets title to "X")
you update the fetched posts, title would get overriden to the old one
121 lines
3.7 KiB
JavaScript
121 lines
3.7 KiB
JavaScript
const Promise = require('bluebird'),
|
|
moment = require('moment'),
|
|
localUtils = require('../utils'),
|
|
common = require('../../../lib/common'),
|
|
models = require('../../../models'),
|
|
urlUtils = require('../../../lib/url-utils'),
|
|
_private = {};
|
|
|
|
/**
|
|
* @description Normalize model data into scheduler notation.
|
|
* @param {Object} options
|
|
* @return {Object}
|
|
*/
|
|
_private.normalize = function normalize(options) {
|
|
const {model, apiUrl, client} = options;
|
|
|
|
return {
|
|
// NOTE: The scheduler expects a unix timestamp.
|
|
time: moment(model.get('published_at')).valueOf(),
|
|
// @TODO: We are still using API v0.1
|
|
url: `${urlUtils.urlJoin(apiUrl, 'schedules', 'posts', model.get('id'))}?client_id=${client.get('slug')}&client_secret=${client.get('secret')}`,
|
|
extra: {
|
|
httpMethod: 'PUT',
|
|
oldTime: model.previous('published_at') ? moment(model.previous('published_at')).valueOf() : null
|
|
}
|
|
};
|
|
};
|
|
|
|
/**
|
|
* @description Load the client credentials for v0.1 API.
|
|
*
|
|
* @TODO: Remove when we drop v0.1. API v2 uses integrations.
|
|
* @return {Promise}
|
|
*/
|
|
_private.loadClient = function loadClient() {
|
|
return models.Client.findOne({slug: 'ghost-scheduler'}, {columns: ['slug', 'secret']});
|
|
};
|
|
|
|
/**
|
|
* @description Load all scheduled posts from database.
|
|
* @return {Promise}
|
|
*/
|
|
_private.loadScheduledPosts = function () {
|
|
// TODO: make this version aware?
|
|
const api = require('../../../api');
|
|
return api.schedules.getScheduledPosts()
|
|
.then((result) => {
|
|
return result.posts || [];
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @description Initialise post scheduling.
|
|
* @param {Object} options
|
|
* @return {*}
|
|
*/
|
|
exports.init = function init(options = {}) {
|
|
const {apiUrl} = options;
|
|
let adapter = null,
|
|
client = null;
|
|
|
|
if (!Object.keys(options).length) {
|
|
return Promise.reject(new common.errors.IncorrectUsageError({message: 'post-scheduling: no config was provided'}));
|
|
}
|
|
|
|
if (!apiUrl) {
|
|
return Promise.reject(new common.errors.IncorrectUsageError({message: 'post-scheduling: no apiUrl was provided'}));
|
|
}
|
|
|
|
return _private.loadClient()
|
|
.then((_client) => {
|
|
client = _client;
|
|
return localUtils.createAdapter(options);
|
|
})
|
|
.then((_adapter) => {
|
|
adapter = _adapter;
|
|
|
|
if (!adapter.rescheduleOnBoot) {
|
|
return [];
|
|
}
|
|
|
|
return _private.loadScheduledPosts();
|
|
})
|
|
.then((scheduledPosts) => {
|
|
if (!scheduledPosts.length) {
|
|
return;
|
|
}
|
|
|
|
scheduledPosts.forEach((model) => {
|
|
// NOTE: We are using reschedule, because custom scheduling adapter could use a database, which needs to be updated
|
|
// and not an in-process implementation!
|
|
adapter.reschedule(_private.normalize({model, apiUrl, client}), {bootstrap: true});
|
|
});
|
|
})
|
|
.then(() => {
|
|
adapter.run();
|
|
})
|
|
.then(() => {
|
|
common.events.onMany([
|
|
'post.scheduled',
|
|
'page.scheduled'
|
|
], (model) => {
|
|
adapter.schedule(_private.normalize({model, apiUrl, client}));
|
|
});
|
|
|
|
common.events.onMany([
|
|
'post.rescheduled',
|
|
'page.rescheduled'
|
|
], (model) => {
|
|
adapter.reschedule(_private.normalize({model, apiUrl, client}));
|
|
});
|
|
|
|
common.events.onMany([
|
|
'post.unscheduled',
|
|
'page.unscheduled'
|
|
], (model) => {
|
|
adapter.unschedule(_private.normalize({model, apiUrl, client}));
|
|
});
|
|
});
|
|
};
|