diff --git a/core/server/api/v2/configuration.js b/core/server/api/v2/configuration.js new file mode 100644 index 000000000000..8e1e7c20f0dd --- /dev/null +++ b/core/server/api/v2/configuration.js @@ -0,0 +1,67 @@ +const Promise = require('bluebird'); +const {isPlainObject} = require('lodash'); +const urlService = require('../../services/url'); +const models = require('../../models'); +const config = require('../../config'); +const labs = require('../../services/labs'); +const settingsCache = require('../../services/settings/cache'); +const ghostVersion = require('../../lib/ghost-version'); + +function fetchAvailableTimezones() { + const timezones = require('../../data/timezones.json'); + return timezones; +} + +function getAboutConfig() { + return { + version: ghostVersion.full, + environment: config.get('env'), + database: config.get('database').client, + mail: isPlainObject(config.get('mail')) ? config.get('mail').transport : '' + }; +} + +function getBaseConfig() { + return { + useGravatar: !config.isPrivacyDisabled('useGravatar'), + publicAPI: labs.isSet('publicAPI'), + blogUrl: urlService.utils.urlFor('home', true), + blogTitle: settingsCache.get('title'), + clientExtensions: config.get('clientExtensions'), + enableDeveloperExperiments: config.get('enableDeveloperExperiments') + }; +} + +module.exports = { + docName: 'configuration', + read: { + permissions: false, + data: [ + 'key' + ], + query({data}) { + if (!data.key) { + return models.Client.findOne({slug: 'ghost-admin'}) + .then((ghostAdmin) => { + const configuration = getBaseConfig(); + + configuration.clientId = ghostAdmin.get('slug'); + configuration.clientSecret = ghostAdmin.get('secret'); + + return configuration; + }); + } + + if (data.key === 'about') { + return Promise.resolve(getAboutConfig()); + } + + // Timezone endpoint + if (data.key === 'timezones') { + return Promise.resolve(fetchAvailableTimezones()); + } + + return Promise.resolve(); + } + } +}; diff --git a/core/server/api/v2/index.js b/core/server/api/v2/index.js index 07aee414d6fe..02a246d4fb78 100644 --- a/core/server/api/v2/index.js +++ b/core/server/api/v2/index.js @@ -81,5 +81,9 @@ module.exports = { get authors() { return shared.pipeline(require('./authors'), localUtils); + }, + + get configuration() { + return shared.pipeline(require('./configuration'), localUtils); } }; diff --git a/core/server/api/v2/utils/serializers/output/configuration.js b/core/server/api/v2/utils/serializers/output/configuration.js new file mode 100644 index 000000000000..11d19a9170c6 --- /dev/null +++ b/core/server/api/v2/utils/serializers/output/configuration.js @@ -0,0 +1,11 @@ +const debug = require('ghost-ignition').debug('api:v2:utils:serializers:output:configuration'); + +module.exports = { + all(configuration, apiConfig, frame) { + frame.response = { + configuration: configuration ? [configuration] : [] + }; + + debug(frame.response); + } +}; diff --git a/core/server/api/v2/utils/serializers/output/index.js b/core/server/api/v2/utils/serializers/output/index.js index 6a6b954810a8..b33511df4ed3 100644 --- a/core/server/api/v2/utils/serializers/output/index.js +++ b/core/server/api/v2/utils/serializers/output/index.js @@ -65,5 +65,9 @@ module.exports = { get authors() { return require('./authors'); + }, + + get configuration() { + return require('./configuration'); } }; diff --git a/core/server/web/api/v2/admin/routes.js b/core/server/web/api/v2/admin/routes.js index 7b12fc5b47ee..10a3ef4d2dc7 100644 --- a/core/server/web/api/v2/admin/routes.js +++ b/core/server/web/api/v2/admin/routes.js @@ -22,8 +22,8 @@ module.exports = function apiRoutes() { router.options('*', shared.middlewares.api.cors); // ## Configuration - router.get('/configuration', api.http(api.configuration.read)); - router.get('/configuration/:key', mw.authAdminApi, api.http(api.configuration.read)); + router.get('/configuration', apiv2.http(apiv2.configuration.read)); + router.get('/configuration/:key', mw.authAdminApi, apiv2.http(apiv2.configuration.read)); // ## Posts router.get('/posts', mw.authAdminApi, apiv2.http(apiv2.posts.browse)); diff --git a/core/test/functional/api/v2/admin/configuration_spec.js b/core/test/functional/api/v2/admin/configuration_spec.js new file mode 100644 index 000000000000..130f69b21995 --- /dev/null +++ b/core/test/functional/api/v2/admin/configuration_spec.js @@ -0,0 +1,79 @@ +const should = require('should'); +const supertest = require('supertest'); +const testUtils = require('../../../../utils'); +const localUtils = require('./utils'); +const config = require('../../../../../../core/server/config'); +const ghost = testUtils.startGhost; + +let request; +describe('Configuration API', function () { + before(function () { + return ghost() + .then(function () { + request = supertest.agent(config.get('url')); + }) + .then(function () { + return localUtils.doAuth(request); + }); + }); + + describe('success', function () { + it('can retrieve public configuration and all expected properties', function (done) { + request.get(localUtils.API.getApiQuery('configuration/')) + .set('Origin', config.get('url')) + .expect('Content-Type', /json/) + .expect('Cache-Control', testUtils.cacheRules.private) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + + should.exist(res.body.configuration); + + res.body.configuration.should.be.an.Array().with.lengthOf(1); + const props = res.body.configuration[0]; + + props.blogUrl.should.eql('http://127.0.0.1:2369/'); + + props.useGravatar.should.eql(false); + props.publicAPI.should.eql(true); + props.clientId.should.eql('ghost-admin'); + props.clientSecret.should.eql('not_available'); + + // value not available, because settings API was not called yet + props.hasOwnProperty('blogTitle').should.eql(true); + done(); + }); + }); + + it('can read about config and get all expected properties', function (done) { + request.get(localUtils.API.getApiQuery('configuration/about/')) + .set('Origin', config.get('url')) + .expect('Content-Type', /json/) + .expect('Cache-Control', testUtils.cacheRules.private) + .expect(200) + .end(function (err, res) { + if (err) { + return done(err); + } + + should.exist(res.body.configuration); + + res.body.configuration.should.be.an.Array().with.lengthOf(1); + const props = res.body.configuration[0]; + + // Check the structure + props.should.have.property('version').which.is.a.String(); + props.should.have.property('environment').which.is.a.String(); + props.should.have.property('database').which.is.a.String(); + props.should.have.property('mail').which.is.a.String(); + + // Check a few values + props.environment.should.match(/^testing/); + props.version.should.eql(require('../../../../../../package.json').version); + done(); + }); + }); + }); +});