mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-11 00:37:55 +03:00
Dynamic Routing: Added migration for routes.yaml file (#9692)
refs #9601 - the home.hbs behaviour for the index collection (`/`) is hardcoded in Ghost - we would like to migrate all existing routes.yaml files - we only replace the file if the contents of the routes.yaml file equals the old routes.yaml format (with home.hbs as template) - updated README of settings folder - if we don't remove the home.hbs template from the default routes.yaml file, home.hbs will be rendered for any page of the index collection - the backwards compatible behaviour was different - only render home.hbs for page 1 - remember: the default routes.yaml file reflects how Ghost was working without dynamic routing
This commit is contained in:
parent
a1b55509df
commit
5a61f99467
@ -13,7 +13,6 @@ collections:
|
||||
/:
|
||||
permalink: '{globals.permalinks}'
|
||||
template:
|
||||
- home
|
||||
- index
|
||||
|
||||
taxonomies:
|
||||
|
@ -1,6 +1,20 @@
|
||||
const crypto = require('crypto');
|
||||
|
||||
module.exports.generateHash = function generateHash(options) {
|
||||
module.exports.generateFromContent = function generateFromContent(options) {
|
||||
options = options || {};
|
||||
|
||||
const hash = crypto.createHash('sha256'),
|
||||
content = options.content;
|
||||
|
||||
let text = '';
|
||||
|
||||
hash.update(content);
|
||||
|
||||
text += [content, hash.digest('base64')].join('|');
|
||||
return new Buffer(text).toString('base64');
|
||||
};
|
||||
|
||||
module.exports.generateFromEmail = function generateFromEmail(options) {
|
||||
options = options || {};
|
||||
|
||||
const hash = crypto.createHash('sha256'),
|
||||
|
@ -34,7 +34,7 @@ Invite = ghostBookshelf.Model.extend({
|
||||
}
|
||||
|
||||
data.expires = Date.now() + constants.ONE_WEEK_MS;
|
||||
data.token = security.tokens.generateHash({
|
||||
data.token = security.tokens.generateFromEmail({
|
||||
email: data.email,
|
||||
expires: data.expires,
|
||||
secret: settingsCache.get('db_hash')
|
||||
|
@ -4,7 +4,6 @@ collections:
|
||||
/:
|
||||
permalink: '{globals.permalinks}' # special 1.0 compatibility setting. See the docs for details.
|
||||
template:
|
||||
- home
|
||||
- index
|
||||
|
||||
taxonomies:
|
||||
|
@ -3,7 +3,11 @@ const fs = require('fs-extra'),
|
||||
path = require('path'),
|
||||
debug = require('ghost-ignition').debug('services:settings:ensure-settings'),
|
||||
common = require('../../lib/common'),
|
||||
config = require('../../config');
|
||||
security = require('../../lib/security'),
|
||||
config = require('../../config'),
|
||||
yamlMigrations = {
|
||||
homeTemplate: 'cm91dGVzOmNvbGxlY3Rpb25zOi86cGVybWFsaW5rOid7Z2xvYmFscy5wZXJtYWxpbmtzfScjc3BlY2lhbDEuMGNvbXBhdGliaWxpdHlzZXR0aW5nLlNlZXRoZWRvY3Nmb3JkZXRhaWxzLnRlbXBsYXRlOi1ob21lLWluZGV4dGF4b25vbWllczp0YWc6L3RhZy97c2x1Z30vYXV0aG9yOi9hdXRob3Ive3NsdWd9L3wwUUg4SHRFQWZvbHBBSmVTYWkyOEwwSGFNMGFIOU5SczhZWGhMcExmZ2c0PQ=='
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes sure that all supported settings files are in the
|
||||
@ -24,7 +28,22 @@ module.exports = function ensureSettingsFiles(knownSettings) {
|
||||
defaultFileName = `default-${fileName}`,
|
||||
filePath = path.join(contentPath, fileName);
|
||||
|
||||
return fs.access(filePath)
|
||||
return fs.readFile(filePath, 'utf8')
|
||||
.then((content) => {
|
||||
content = content.replace(/\s/g, '');
|
||||
|
||||
/**
|
||||
* routes.yaml migrations:
|
||||
*
|
||||
* 1. We have removed the "home.hbs" template from the default collection, because
|
||||
* this is a hardcoded behaviour in Ghost. If we don't remove the home.hbs every page of the
|
||||
* index collection get's rendered with the home template, but this is not the correct behaviour
|
||||
* < 1.24. The index collection is `/`.
|
||||
*/
|
||||
if (security.tokens.generateFromContent({content: content}) === yamlMigrations.homeTemplate) {
|
||||
throw new common.errors.ValidationError({code: 'ENOENT'});
|
||||
}
|
||||
})
|
||||
.catch({code: 'ENOENT'}, () => {
|
||||
// CASE: file doesn't exist, copy it from our defaults
|
||||
return fs.copy(
|
||||
|
@ -13,6 +13,8 @@ const sinon = require('sinon'),
|
||||
describe('UNIT > Settings Service:', function () {
|
||||
beforeEach(function () {
|
||||
configUtils.set('paths:contentPath', path.join(__dirname, '../../../utils/fixtures/'));
|
||||
sandbox.stub(fs, 'readFile');
|
||||
sandbox.stub(fs, 'copy');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
@ -22,10 +24,12 @@ describe('UNIT > Settings Service:', function () {
|
||||
|
||||
describe('Ensure settings files', function () {
|
||||
it('returns yaml file from settings folder if it exists', function () {
|
||||
const fsAccessSpy = sandbox.spy(fs, 'access');
|
||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/badroutes.yaml'), 'utf8').resolves('content');
|
||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/goodroutes.yaml'), 'utf8').resolves('content');
|
||||
|
||||
return ensureSettings(['goodroutes', 'badroutes']).then(() => {
|
||||
fsAccessSpy.callCount.should.be.eql(2);
|
||||
fs.readFile.callCount.should.be.eql(2);
|
||||
fs.copy.called.should.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
@ -34,20 +38,16 @@ describe('UNIT > Settings Service:', function () {
|
||||
const expectedContentPath = path.join(__dirname, '../../../utils/fixtures/settings/globals.yaml');
|
||||
const fsError = new Error('not found');
|
||||
fsError.code = 'ENOENT';
|
||||
const fsAccessStub = sandbox.stub(fs, 'access');
|
||||
const fsCopyStub = sandbox.stub(fs, 'copy').resolves();
|
||||
|
||||
fsAccessStub.onFirstCall().resolves();
|
||||
// route file in settings directotry is not found
|
||||
fsAccessStub.onSecondCall().rejects(fsError);
|
||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml'), 'utf8').resolves('content');
|
||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/globals.yaml'), 'utf8').rejects(fsError);
|
||||
fs.copy.withArgs(expectedDefaultSettingsPath, expectedContentPath).resolves();
|
||||
|
||||
return ensureSettings(['routes', 'globals'])
|
||||
.then(() => {
|
||||
fsAccessStub.calledTwice.should.be.true();
|
||||
}).then(() => {
|
||||
fsCopyStub.calledWith(expectedDefaultSettingsPath, expectedContentPath).should.be.true();
|
||||
fsCopyStub.calledOnce.should.be.true();
|
||||
});
|
||||
.then(() => {
|
||||
fs.readFile.calledTwice.should.be.true();
|
||||
fs.copy.calledOnce.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('copies default settings file if no file found', function () {
|
||||
@ -55,13 +55,13 @@ describe('UNIT > Settings Service:', function () {
|
||||
const expectedContentPath = path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml');
|
||||
const fsError = new Error('not found');
|
||||
fsError.code = 'ENOENT';
|
||||
const fsAccessStub = sandbox.stub(fs, 'access').rejects(fsError);
|
||||
const fsCopyStub = sandbox.stub(fs, 'copy').resolves();
|
||||
|
||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml'), 'utf8').rejects(fsError);
|
||||
fs.copy.withArgs(expectedDefaultSettingsPath, expectedContentPath).resolves();
|
||||
|
||||
return ensureSettings(['routes']).then(() => {
|
||||
fsAccessStub.calledOnce.should.be.true();
|
||||
fsCopyStub.calledWith(expectedDefaultSettingsPath, expectedContentPath).should.be.true();
|
||||
fsCopyStub.calledOnce.should.be.true();
|
||||
fs.readFile.calledOnce.should.be.true();
|
||||
fs.copy.calledOnce.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
@ -69,12 +69,120 @@ describe('UNIT > Settings Service:', function () {
|
||||
const expectedContentPath = path.join(__dirname, '../../../utils/fixtures/settings/');
|
||||
const fsError = new Error('no permission');
|
||||
fsError.code = 'EPERM';
|
||||
const fsAccessStub = sandbox.stub(fs, 'access').rejects(new Error('Oopsi!'));
|
||||
|
||||
return ensureSettings(['routes']).catch((error) => {
|
||||
should.exist(error);
|
||||
error.message.should.be.eql(`Error trying to access settings files in ${expectedContentPath}.`);
|
||||
fsAccessStub.calledOnce.should.be.true();
|
||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml'), 'utf8').rejects(fsError);
|
||||
|
||||
return ensureSettings(['routes'])
|
||||
.then(()=> {
|
||||
throw new Error('Expected test to fail');
|
||||
})
|
||||
.catch((error) => {
|
||||
should.exist(error);
|
||||
error.message.should.be.eql(`Error trying to access settings files in ${expectedContentPath}.`);
|
||||
fs.readFile.calledOnce.should.be.true();
|
||||
fs.copy.called.should.be.false();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Migrations: home.hbs', function () {
|
||||
it('routes.yaml has modifications', function () {
|
||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml'), 'utf8').resolves('' +
|
||||
'routes:\n' +
|
||||
'\n' +
|
||||
'collections:\n' +
|
||||
' /:\n' +
|
||||
' permalink: \'{globals.permalinks}\' # special 1.0 compatibility setting. See the docs for details.\n' +
|
||||
' template:\n' +
|
||||
' - index\n' +
|
||||
'\n' +
|
||||
'taxonomies:\n' +
|
||||
' tag: /tag/{slug}/\n' +
|
||||
' author: /author/{slug}/' + '' +
|
||||
'\n'
|
||||
);
|
||||
|
||||
return ensureSettings(['routes']).then(() => {
|
||||
fs.readFile.callCount.should.be.eql(1);
|
||||
fs.copy.called.should.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
it('routes.yaml is old routes.yaml', function () {
|
||||
const expectedDefaultSettingsPath = path.join(__dirname, '../../../../server/services/settings/default-routes.yaml');
|
||||
const expectedContentPath = path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml');
|
||||
|
||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml'), 'utf8').resolves('' +
|
||||
'routes:\n' +
|
||||
'\n' +
|
||||
'collections:\n' +
|
||||
' /:\n' +
|
||||
' permalink: \'{globals.permalinks}\' # special 1.0 compatibility setting. See the docs for details.\n' +
|
||||
' template:\n' +
|
||||
' - home\n' +
|
||||
' - index\n' +
|
||||
'\n' +
|
||||
'taxonomies:\n' +
|
||||
' tag: /tag/{slug}/\n' +
|
||||
' author: /author/{slug}/' +
|
||||
'\n'
|
||||
);
|
||||
|
||||
fs.copy.withArgs(expectedDefaultSettingsPath, expectedContentPath).resolves();
|
||||
|
||||
return ensureSettings(['routes']).then(() => {
|
||||
fs.readFile.callCount.should.be.eql(1);
|
||||
fs.copy.called.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('routes.yaml is old routes.yaml', function () {
|
||||
const expectedDefaultSettingsPath = path.join(__dirname, '../../../../server/services/settings/default-routes.yaml');
|
||||
const expectedContentPath = path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml');
|
||||
|
||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml'), 'utf8').resolves('' +
|
||||
'routes:\n' +
|
||||
'\n\n' +
|
||||
'collections:\n' +
|
||||
' /:\n' +
|
||||
' permalink: \'{globals.permalinks}\' # special 1.0 compatibility setting. See the docs for details.\n' +
|
||||
' template:\n' +
|
||||
' - home\n' +
|
||||
' - index\n' +
|
||||
'\n\r' +
|
||||
'taxonomies: \n' +
|
||||
' tag: /tag/{slug}/\n' +
|
||||
' author: /author/{slug}/' +
|
||||
'\t' +
|
||||
'\n'
|
||||
);
|
||||
|
||||
fs.copy.withArgs(expectedDefaultSettingsPath, expectedContentPath).resolves();
|
||||
|
||||
return ensureSettings(['routes']).then(() => {
|
||||
fs.readFile.callCount.should.be.eql(1);
|
||||
fs.copy.called.should.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
it('routes.yaml has modifications, do not replace', function () {
|
||||
fs.readFile.withArgs(path.join(__dirname, '../../../utils/fixtures/settings/routes.yaml'), 'utf8').resolves('' +
|
||||
'routes:\n' +
|
||||
' /about/: about' +
|
||||
'\n' +
|
||||
'collections:\n' +
|
||||
' /:\n' +
|
||||
' permalink: \'{globals.permalinks}\' # special 1.0 compatibility setting. See the docs for details.\n' +
|
||||
'\n' +
|
||||
'taxonomies:\n' +
|
||||
' tag: /categories/{slug}/\n' +
|
||||
' author: /author/{slug}/' + '' +
|
||||
'\n'
|
||||
);
|
||||
|
||||
return ensureSettings(['routes']).then(() => {
|
||||
fs.readFile.callCount.should.be.eql(1);
|
||||
fs.copy.called.should.be.false();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,7 +4,6 @@ collections:
|
||||
/
|
||||
permalink: '{globals.permalinks}'
|
||||
template:
|
||||
- home
|
||||
- index
|
||||
|
||||
taxonomies:
|
||||
|
@ -4,7 +4,6 @@ collections:
|
||||
/:
|
||||
permalink: '{globals.permalinks}'
|
||||
template:
|
||||
- home
|
||||
- index
|
||||
|
||||
taxonomies:
|
||||
|
@ -4,7 +4,6 @@ collections:
|
||||
/:
|
||||
permalink: '{globals.permalinks}'
|
||||
template:
|
||||
- home
|
||||
- index
|
||||
|
||||
taxonomies:
|
||||
|
Loading…
Reference in New Issue
Block a user