0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

🙅🏽 Admin server split (#8142)

refs #8140

 Support new default-prod.hbs template for admin
 Redirect ghost admin urls without a #
 Update admin urls to include #
🎨 Move the admin templates
🔥 Remove redirect to setup middleware
🚨 Tests for new middleware
This commit is contained in:
Hannah Wolfe 2017-03-14 16:03:30 +00:00 committed by Katharina Irrgang
parent f4a68a2e52
commit 4a6f58d8d1
15 changed files with 102 additions and 175 deletions

2
.gitignore vendored
View file

@ -65,7 +65,7 @@ config.*.json
# Built asset files
/core/built
/core/server/views/default.hbs
/core/server/admin/views/default*.hbs
/core/shared/ghost-url.min.js
# Coverage reports

View file

@ -46,7 +46,7 @@ var overrides = require('./core/server/overrides'),
pkg: grunt.file.readJSON('package.json'),
clientFiles: [
'server/views/default.hbs',
'server/admin/views/default.hbs',
'built/assets/ghost.js',
'built/assets/ghost.css',
'built/assets/vendor.js',
@ -465,7 +465,7 @@ var overrides = require('./core/server/overrides'),
return grunt.task.run(['lint']);
}
grunt.task.run(['stubClientFiles', 'test-all']);
grunt.task.run(['test-all']);
});
// ### Test-All
@ -486,7 +486,7 @@ var overrides = require('./core/server/overrides'),
// ### test-setup *(utility)(
// `grunt test-setup` will run all the setup tasks required for running tests
grunt.registerTask('test-setup', 'Setup ready to run tests',
['knex-migrator', 'clean:test', 'setTestEnv']
['knex-migrator', 'clean:test', 'setTestEnv', 'stubClientFiles']
);
// ### Unit Tests *(sub task)*

View file

@ -4,7 +4,7 @@ var debug = require('debug')('ghost:admin'),
adminHbs = require('express-hbs').create(),
// Admin only middleware
redirectToSetup = require('../middleware/redirect-to-setup'),
adminMiddleware = require('./middleware'),
// Global/shared middleware?
cacheControl = require('../middleware/cache-control'),
@ -61,7 +61,7 @@ module.exports = function setupAdminApp() {
// Admin is currently set to not be cached at all
adminApp.use(cacheControl('private'));
// Special redirects for the admin (these should have their own cache-control headers)
adminApp.use(redirectToSetup);
adminApp.use(adminMiddleware);
// Finally, routing
adminApp.get('*', require('./controller'));

View file

@ -1,5 +1,6 @@
var debug = require('debug')('ghost:admin:controller'),
_ = require('lodash'),
config = require('../config'),
api = require('../api'),
updateCheck = require('../update-check'),
logging = require('../logging'),
@ -33,7 +34,9 @@ module.exports = function adminController(req, res) {
}
});
}).finally(function noMatterWhat() {
res.render('default');
var defaultTemplate = config.get('env') === 'production' ? 'default-prod' : 'default';
res.render(defaultTemplate);
}).catch(function (err) {
logging.error(err);
});

View file

@ -0,0 +1,14 @@
var utils = require('../utils');
function redirectAdminUrls(req, res, next) {
var ghostPathMatch = req.originalUrl.match(/^\/ghost\/(.+)$/);
if (ghostPathMatch) {
return res.redirect(utils.url.urlJoin(utils.url.urlFor('admin'), '#', ghostPathMatch[1]));
}
next();
}
module.exports = [
redirectAdminUrls
];

View file

View file

@ -13,10 +13,10 @@ frontendRoutes = function frontendRoutes() {
// ### Admin routes
router.get(/^\/(logout|signout)\/$/, function redirectToSignout(req, res) {
utils.redirect301(res, utils.url.urlJoin(utils.url.urlFor('admin'), 'signout/'));
utils.redirect301(res, utils.url.urlJoin(utils.url.urlFor('admin'), '#/signout/'));
});
router.get(/^\/signup\/$/, function redirectToSignup(req, res) {
utils.redirect301(res, utils.url.urlJoin(utils.url.urlFor('admin'), 'signup/'));
utils.redirect301(res, utils.url.urlJoin(utils.url.urlFor('admin'), '#/signup/'));
});
// redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc.

View file

@ -4,7 +4,7 @@
"corePath": "core/",
"clientAssets": "core/built/assets",
"helperTemplates": "core/server/helpers/tpl/",
"adminViews": "core/server/views/",
"adminViews": "core/server/admin/views/",
"defaultViews": "core/server/views/",
"internalAppPath": "core/server/apps/",
"internalStoragePath": "core/server/storage/",

View file

@ -24,7 +24,7 @@ channelConfig = function channelConfig() {
}
},
slugTemplate: true,
editRedirect: utils.url.urlJoin(utils.url.urlFor('admin'), '/settings/tags/:slug/')
editRedirect: utils.url.urlJoin(utils.url.urlFor('admin'), '#/settings/tags/:slug/')
},
author: {
name: 'author',
@ -40,7 +40,7 @@ channelConfig = function channelConfig() {
}
},
slugTemplate: true,
editRedirect: utils.url.urlJoin(utils.url.urlFor('admin'), '/team/:slug/')
editRedirect: utils.url.urlJoin(utils.url.urlFor('admin'), '#/team/:slug/')
}
};

View file

@ -1,19 +0,0 @@
var api = require('../api'),
utils = require('../utils');
// Redirect to setup if no user exists
function redirectToSetup(req, res, next) {
var isSetupRequest = req.path.match(/\/setup\//),
isOauthAuthorization = req.path.match(/\/$/) && req.query && (req.query.code || req.query.error);
api.authentication.isSetup().then(function then(exists) {
if (!exists.setup[0].status && !isSetupRequest && !isOauthAuthorization) {
return res.redirect(utils.url.urlJoin(utils.url.urlFor('admin') + 'setup/'));
}
next();
}).catch(function handleError(err) {
return next(new Error(err));
});
}
module.exports = redirectToSetup;

View file

@ -82,25 +82,25 @@ describe('Admin Routing', function () {
});
describe('Legacy Redirects', function () {
it('should redirect /logout/ to /ghost/signout/', function (done) {
it('should redirect /logout/ to /ghost/#/signout/', function (done) {
request.get('/logout/')
.expect('Location', '/ghost/signout/')
.expect('Location', '/ghost/#/signout/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEndNoAuth(done));
});
it('should redirect /signout/ to /ghost/signout/', function (done) {
it('should redirect /signout/ to /ghost/#/signout/', function (done) {
request.get('/signout/')
.expect('Location', '/ghost/signout/')
.expect('Location', '/ghost/#/signout/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEndNoAuth(done));
});
it('should redirect /signup/ to /ghost/signup/', function (done) {
it('should redirect /signup/ to /ghost/#/signup/', function (done) {
request.get('/signup/')
.expect('Location', '/ghost/signup/')
.expect('Location', '/ghost/#/signup/')
.expect('Cache-Control', testUtils.cacheRules.year)
.expect(301)
.end(doEndNoAuth(done));
@ -130,32 +130,6 @@ describe('Admin Routing', function () {
.end(doEndNoAuth(done));
});
});
describe('Ghost Admin Setup', function () {
it('should redirect from /ghost/ to /ghost/setup/ when no user/not installed yet', function (done) {
request.get('/ghost/')
.expect('Location', /ghost\/setup/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(302)
.end(doEnd(done));
});
it('should redirect from /ghost/signin/ to /ghost/setup/ when no user', function (done) {
request.get('/ghost/signin/')
.expect('Location', /ghost\/setup/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(302)
.end(doEnd(done));
});
it('should respond with html for /ghost/setup/', function (done) {
request.get('/ghost/setup/')
.expect('Content-Type', /html/)
.expect('Cache-Control', testUtils.cacheRules.private)
.expect(200)
.end(doEnd(done));
});
});
});
describe('FORK', function () {
@ -194,7 +168,7 @@ describe('Admin Routing', function () {
});
it('should allow admin access over HTTPS', function (done) {
request.get('/ghost/setup/')
request.get('/ghost/')
.set('X-Forwarded-Proto', 'https')
.expect(200)
.end(doEnd(done));

View file

@ -380,7 +380,7 @@ describe('Channel Routes', function () {
it('should redirect to tag settings', function (done) {
request.get('/tag/getting-started/edit/')
.expect('Location', '/ghost/settings/tags/getting-started/')
.expect('Location', '/ghost/#/settings/tags/getting-started/')
.expect('Cache-Control', testUtils.cacheRules.public)
.expect(302)
.end(doEnd(done));
@ -549,7 +549,7 @@ describe('Channel Routes', function () {
it('should redirect to editor', function (done) {
request.get('/author/ghost-owner/edit/')
.expect('Location', '/ghost/team/ghost-owner/')
.expect('Location', '/ghost/#/team/ghost-owner/')
.expect('Cache-Control', testUtils.cacheRules.public)
.expect(302)
.end(doEnd(done));

View file

@ -0,0 +1,63 @@
var should = require('should'), // jshint ignore:line
sinon = require('sinon'),
// Thing we are testing
redirectAdminUrls = require('../../server/admin/middleware')[0],
sandbox = sinon.sandbox.create();
describe('Admin App', function () {
afterEach(function () {
sandbox.restore();
});
describe('middleware', function () {
describe('redirectAdminUrls', function () {
var req, res, next;
// Input: req.originalUrl
// Output: either next or res.redirect are called
beforeEach(function () {
req = {};
res = {};
next = sandbox.stub();
res.redirect = sandbox.stub();
});
it('should redirect a url which starts with ghost', function () {
req.originalUrl = '/ghost/x';
redirectAdminUrls(req, res, next);
next.called.should.be.false();
res.redirect.called.should.be.true();
res.redirect.calledWith('/ghost/#/x').should.be.true();
});
it('should not redirect /ghost/ on its owh', function () {
req.originalUrl = '/ghost/';
redirectAdminUrls(req, res, next);
next.called.should.be.true();
res.redirect.called.should.be.false();
});
it('should not redirect url that has no slash', function () {
req.originalUrl = 'ghost/x';
redirectAdminUrls(req, res, next);
next.called.should.be.true();
res.redirect.called.should.be.false();
});
it('should not redirect url that starts with something other than /ghost/', function () {
req.originalUrl = 'x/ghost/x';
redirectAdminUrls(req, res, next);
next.called.should.be.true();
res.redirect.called.should.be.false();
});
});
});
});

View file

@ -381,7 +381,7 @@ describe('Channels', function () {
describe('Edit', function () {
it('should redirect /edit/ to ghost admin', function (done) {
testChannelRedirect({url: '/tag/my-tag/edit/'}, function (path) {
path.should.eql('/ghost/settings/tags/my-tag/');
path.should.eql('/ghost/#/settings/tags/my-tag/');
postAPIStub.called.should.be.false();
}, done);
});

View file

@ -1,108 +0,0 @@
var sinon = require('sinon'),
should = require('should'),
Promise = require('bluebird'),
api = require('../../../server/api'),
redirectToSetup = require('../../../server/middleware/redirect-to-setup');
should.equal(true, true);
describe('redirectToSetup', function () {
var res, req, next, sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
res = sinon.spy();
req = sinon.spy();
next = sinon.spy();
});
afterEach(function () {
sandbox.restore();
});
it('should redirect to setup if not on setup', function (done) {
sandbox.stub(api.authentication, 'isSetup', function () {
return Promise.resolve({setup: [{status: false}]});
});
req.path = '/';
res.redirect = sinon.spy(function () {
next.called.should.be.false();
res.redirect.called.should.be.true();
done();
});
redirectToSetup(req, res, next);
});
it('should not redirect if setup is done', function (done) {
sandbox.stub(api.authentication, 'isSetup', function () {
return Promise.resolve({setup: [{status: true}]});
});
res = {redirect: sinon.spy()};
req.path = '/';
next = sinon.spy(function () {
next.called.should.be.true();
res.redirect.called.should.be.false();
done();
});
redirectToSetup(req, res, next);
});
it('should not redirect if already on setup', function (done) {
sandbox.stub(api.authentication, 'isSetup', function () {
return Promise.resolve({setup: [{status: false}]});
});
res = {redirect: sinon.spy()};
req.path = '/ghost/setup/';
next = sinon.spy(function () {
next.called.should.be.true();
res.redirect.called.should.be.false();
done();
});
redirectToSetup(req, res, next);
});
it('should not redirect successful oauth authorization requests', function (done) {
sandbox.stub(api.authentication, 'isSetup', function () {
return Promise.resolve({setup: [{status: false}]});
});
res = {redirect: sinon.spy()};
req.path = '/';
req.query = {code: 'authCode'};
next = sinon.spy(function () {
next.called.should.be.true();
res.redirect.called.should.be.false();
done();
});
redirectToSetup(req, res, next);
});
it('should not redirect failed oauth authorization requests', function (done) {
sandbox.stub(api.authentication, 'isSetup', function () {
return Promise.resolve({setup: [{status: false}]});
});
res = {redirect: sinon.spy()};
req.path = '/';
req.query = {error: 'access_denied', state: 'randomstring'};
next = sinon.spy(function () {
next.called.should.be.true();
res.redirect.called.should.be.false();
done();
});
redirectToSetup(req, res, next);
});
});