diff --git a/core/server/api/app.js b/core/server/api/app.js new file mode 100644 index 0000000000..83bb4e7648 --- /dev/null +++ b/core/server/api/app.js @@ -0,0 +1,240 @@ +// # API routes +var express = require('express'), + tmpdir = require('os').tmpdir, + + // This essentially provides the controllers for the routes + api = require('../api'), + + // Include the middleware + + // API specific + auth = require('../auth'), + cors = require('../middleware/api/cors'), // routes only?! + spamPrevention = require('../middleware/api/spam-prevention'), // routes only + versionMatch = require('../middleware/api/version-match'), // global + + // Handling uploads & imports + upload = require('multer')({dest: tmpdir()}), // routes only + validation = require('../middleware/validation'), // routes only + + // Shared + bodyParser = require('body-parser'), // global, shared + cacheControl = require('../middleware/cache-control'), // global, shared + maintenance = require('../middleware/maintenance'), // global, shared + errorHandler = require('../middleware/error-handler'), // global, shared + + // Temporary + // @TODO find a more appy way to do this! + labs = require('../middleware/labs'), + + // @TODO find a better way to bundle these authentication packages + // Authentication for public endpoints + authenticatePublic = [ + auth.authenticate.authenticateClient, + auth.authenticate.authenticateUser, + auth.authorize.requiresAuthorizedUserPublicAPI, + // @TODO do we really need this multiple times or should it be global? + cors + ], + // Require user for private endpoints + authenticatePrivate = [ + auth.authenticate.authenticateClient, + auth.authenticate.authenticateUser, + auth.authorize.requiresAuthorizedUser, + // @TODO do we really need this multiple times or should it be global? + cors + ]; + +// @TODO refactor/clean this up - how do we want the routing to work long term? +function apiRoutes() { + var apiRouter = express.Router(); + + // alias delete with del + apiRouter.del = apiRouter.delete; + + // ## CORS pre-flight check + apiRouter.options('*', cors); + + // ## Configuration + apiRouter.get('/configuration', authenticatePrivate, api.http(api.configuration.read)); + apiRouter.get('/configuration/:key', authenticatePrivate, api.http(api.configuration.read)); + apiRouter.get('/configuration/timezones', authenticatePrivate, api.http(api.configuration.read)); + + // ## Posts + apiRouter.get('/posts', authenticatePublic, api.http(api.posts.browse)); + + apiRouter.post('/posts', authenticatePrivate, api.http(api.posts.add)); + apiRouter.get('/posts/:id', authenticatePublic, api.http(api.posts.read)); + apiRouter.get('/posts/slug/:slug', authenticatePublic, api.http(api.posts.read)); + apiRouter.put('/posts/:id', authenticatePrivate, api.http(api.posts.edit)); + apiRouter.del('/posts/:id', authenticatePrivate, api.http(api.posts.destroy)); + + // ## Schedules + apiRouter.put('/schedules/posts/:id', [ + auth.authenticate.authenticateClient, + auth.authenticate.authenticateUser + ], api.http(api.schedules.publishPost)); + + // ## Settings + apiRouter.get('/settings', authenticatePrivate, api.http(api.settings.browse)); + apiRouter.get('/settings/:key', authenticatePrivate, api.http(api.settings.read)); + apiRouter.put('/settings', authenticatePrivate, api.http(api.settings.edit)); + + // ## Users + apiRouter.get('/users', authenticatePublic, api.http(api.users.browse)); + apiRouter.get('/users/:id', authenticatePublic, api.http(api.users.read)); + apiRouter.get('/users/slug/:slug', authenticatePublic, api.http(api.users.read)); + apiRouter.get('/users/email/:email', authenticatePublic, api.http(api.users.read)); + + apiRouter.put('/users/password', authenticatePrivate, api.http(api.users.changePassword)); + apiRouter.put('/users/owner', authenticatePrivate, api.http(api.users.transferOwnership)); + apiRouter.put('/users/:id', authenticatePrivate, api.http(api.users.edit)); + + apiRouter.post('/users', authenticatePrivate, api.http(api.users.add)); + apiRouter.del('/users/:id', authenticatePrivate, api.http(api.users.destroy)); + + // ## Tags + apiRouter.get('/tags', authenticatePublic, api.http(api.tags.browse)); + apiRouter.get('/tags/:id', authenticatePublic, api.http(api.tags.read)); + apiRouter.get('/tags/slug/:slug', authenticatePublic, api.http(api.tags.read)); + apiRouter.post('/tags', authenticatePrivate, api.http(api.tags.add)); + apiRouter.put('/tags/:id', authenticatePrivate, api.http(api.tags.edit)); + apiRouter.del('/tags/:id', authenticatePrivate, api.http(api.tags.destroy)); + + // ## Subscribers + apiRouter.get('/subscribers', labs.subscribers, authenticatePrivate, api.http(api.subscribers.browse)); + apiRouter.get('/subscribers/csv', labs.subscribers, authenticatePrivate, api.http(api.subscribers.exportCSV)); + apiRouter.post('/subscribers/csv', + labs.subscribers, + authenticatePrivate, + upload.single('subscribersfile'), + validation.upload({type: 'subscribers'}), + api.http(api.subscribers.importCSV) + ); + apiRouter.get('/subscribers/:id', labs.subscribers, authenticatePrivate, api.http(api.subscribers.read)); + apiRouter.post('/subscribers', labs.subscribers, authenticatePublic, api.http(api.subscribers.add)); + apiRouter.put('/subscribers/:id', labs.subscribers, authenticatePrivate, api.http(api.subscribers.edit)); + apiRouter.del('/subscribers/:id', labs.subscribers, authenticatePrivate, api.http(api.subscribers.destroy)); + + // ## Roles + apiRouter.get('/roles/', authenticatePrivate, api.http(api.roles.browse)); + + // ## Clients + apiRouter.get('/clients/slug/:slug', api.http(api.clients.read)); + + // ## Slugs + apiRouter.get('/slugs/:type/:name', authenticatePrivate, api.http(api.slugs.generate)); + + // ## Themes + apiRouter.get('/themes/:name/download', + authenticatePrivate, + api.http(api.themes.download) + ); + + apiRouter.post('/themes/upload', + authenticatePrivate, + upload.single('theme'), + validation.upload({type: 'themes'}), + api.http(api.themes.upload) + ); + + apiRouter.del('/themes/:name', + authenticatePrivate, + api.http(api.themes.destroy) + ); + + // ## Notifications + apiRouter.get('/notifications', authenticatePrivate, api.http(api.notifications.browse)); + apiRouter.post('/notifications', authenticatePrivate, api.http(api.notifications.add)); + apiRouter.del('/notifications/:id', authenticatePrivate, api.http(api.notifications.destroy)); + + // ## DB + apiRouter.get('/db', authenticatePrivate, api.http(api.db.exportContent)); + apiRouter.post('/db', + authenticatePrivate, + upload.single('importfile'), + validation.upload({type: 'db'}), + api.http(api.db.importContent) + ); + apiRouter.del('/db', authenticatePrivate, api.http(api.db.deleteAllContent)); + + // ## Mail + apiRouter.post('/mail', authenticatePrivate, api.http(api.mail.send)); + apiRouter.post('/mail/test', authenticatePrivate, api.http(api.mail.sendTest)); + + // ## Slack + apiRouter.post('/slack/test', authenticatePrivate, api.http(api.slack.sendTest)); + + // ## Authentication + apiRouter.post('/authentication/passwordreset', + spamPrevention.forgotten, + api.http(api.authentication.generateResetToken) + ); + apiRouter.put('/authentication/passwordreset', api.http(api.authentication.resetPassword)); + apiRouter.post('/authentication/invitation', api.http(api.authentication.acceptInvitation)); + apiRouter.get('/authentication/invitation', api.http(api.authentication.isInvitation)); + apiRouter.post('/authentication/setup', api.http(api.authentication.setup)); + apiRouter.put('/authentication/setup', authenticatePrivate, api.http(api.authentication.updateSetup)); + apiRouter.get('/authentication/setup', api.http(api.authentication.isSetup)); + apiRouter.post('/authentication/token', + spamPrevention.signin, + auth.authenticate.authenticateClient, + auth.oauth.generateAccessToken + ); + + apiRouter.post('/authentication/ghost', [ + auth.authenticate.authenticateClient, + auth.authenticate.authenticateGhostUser, + api.http(api.authentication.createTokens) + ]); + + apiRouter.post('/authentication/revoke', authenticatePrivate, api.http(api.authentication.revoke)); + + // ## Uploads + // @TODO: rename endpoint to /images/upload (or similar) + apiRouter.post('/uploads', + authenticatePrivate, + upload.single('uploadimage'), + validation.upload({type: 'images'}), + api.http(api.uploads.add) + ); + + // ## Invites + apiRouter.get('/invites', authenticatePrivate, api.http(api.invites.browse)); + apiRouter.get('/invites/:id', authenticatePrivate, api.http(api.invites.read)); + apiRouter.post('/invites', authenticatePrivate, api.http(api.invites.add)); + apiRouter.del('/invites/:id', authenticatePrivate, api.http(api.invites.destroy)); + + return apiRouter; +} + +module.exports = function setupApiApp() { + var apiApp = express(); + + // API middleware + + // Body parsing + apiApp.use(bodyParser.json({limit: '1mb'})); + apiApp.use(bodyParser.urlencoded({extended: true, limit: '1mb'})); + + // send 503 json response in case of maintenance + apiApp.use(maintenance); + + // @TODO check SSL and pretty URLS is needed here too, once we've finished refactoring + + // Check version matches for API requests, depends on res.locals.safeVersion being set + // Therefore must come after themeHandler.ghostLocals, for now + apiApp.use(versionMatch); + + // API shouldn't be cached + apiApp.use(cacheControl('private')); + + // Routing + apiApp.use(apiRoutes()); + + // API error handling + // @TODO: split the API error handling into its own thing? + apiApp.use(errorHandler); + + return apiApp; +}; diff --git a/core/server/apps/amp/lib/router.js b/core/server/apps/amp/lib/router.js index 636f8a3fde..095741c103 100644 --- a/core/server/apps/amp/lib/router.js +++ b/core/server/apps/amp/lib/router.js @@ -12,7 +12,7 @@ var path = require('path'), function controller(req, res, next) { var defaultView = path.resolve(__dirname, 'views', 'amp.hbs'), paths = templates.getActiveThemePaths(req.app.get('activeTheme')), - data = req.body; + data = req.amp; if (res.error) { data.error = res.error; @@ -33,10 +33,12 @@ function controller(req, res, next) { } function getPostData(req, res, next) { + // Create a req property where we can store our data + req.amp = {}; postLookup(res.locals.relativeUrl) .then(function (result) { if (result && result.post) { - req.body.post = result.post; + req.amp.post = result.post; } next(); diff --git a/core/server/apps/amp/tests/router_spec.js b/core/server/apps/amp/tests/router_spec.js index 79184fb7fb..5902332d23 100644 --- a/core/server/apps/amp/tests/router_spec.js +++ b/core/server/apps/amp/tests/router_spec.js @@ -37,7 +37,7 @@ describe('AMP Controller', function () { route: {path: '/'}, query: {r: ''}, params: {}, - body: {} + amp: {} }; defaultPath = path.join(configUtils.config.get('paths').appRoot, '/core/server/apps/amp/lib/views/amp.hbs'); @@ -148,7 +148,7 @@ describe('AMP getPostData', function () { }; req = { - body: { + amp: { post: {} } }; @@ -173,7 +173,7 @@ describe('AMP getPostData', function () { ampController.__set__('postLookup', postLookupStub); ampController.getPostData(req, res, function () { - req.body.post.should.be.eql({ + req.amp.post.should.be.eql({ id: '1', slug: 'welcome-to-ghost', isAmpURL: true @@ -197,7 +197,7 @@ describe('AMP getPostData', function () { err.message.should.be.eql('not found'); err.statusCode.should.be.eql(404); err.errorType.should.be.eql('NotFoundError'); - req.body.post.should.be.eql({}); + req.amp.should.be.eql({}); done(); }); }); @@ -210,7 +210,7 @@ describe('AMP getPostData', function () { ampController.getPostData(req, res, function (err) { should.exist(err); err.should.be.eql('not found'); - req.body.post.should.be.eql({}); + req.amp.should.be.eql({}); done(); }); }); diff --git a/core/server/apps/private-blogging/lib/router.js b/core/server/apps/private-blogging/lib/router.js index e5789f2547..fdcce5d9ae 100644 --- a/core/server/apps/private-blogging/lib/router.js +++ b/core/server/apps/private-blogging/lib/router.js @@ -1,6 +1,7 @@ var path = require('path'), express = require('express'), middleware = require('./middleware'), + bodyParser = require('body-parser'), templates = require('../../../controllers/frontend/templates'), setResponseContext = require('../../../controllers/frontend/context'), privateRouter = express.Router(); @@ -29,6 +30,7 @@ privateRouter.route('/') controller ) .post( + bodyParser.urlencoded({extended: true}), middleware.isPrivateSessionAuth, middleware.spamPrevention, middleware.authenticateProtection, diff --git a/core/server/apps/subscribers/lib/router.js b/core/server/apps/subscribers/lib/router.js index 352710f74b..de783cf8f4 100644 --- a/core/server/apps/subscribers/lib/router.js +++ b/core/server/apps/subscribers/lib/router.js @@ -2,6 +2,7 @@ var path = require('path'), express = require('express'), _ = require('lodash'), subscribeRouter = express.Router(), + bodyParser = require('body-parser'), // Dirty requires api = require('../../../api'), @@ -92,6 +93,7 @@ subscribeRouter.route('/') controller ) .post( + bodyParser.urlencoded({extended: true}), honeyPot, handleSource, storeSubscriber, diff --git a/core/server/auth/oauth.js b/core/server/auth/oauth.js index f5702bde2c..bc045a0c90 100644 --- a/core/server/auth/oauth.js +++ b/core/server/auth/oauth.js @@ -3,7 +3,7 @@ var oauth2orize = require('oauth2orize'), utils = require('../utils'), errors = require('../errors'), authenticationAPI = require('../api/authentication'), - spamPrevention = require('../middleware/spam-prevention'), + spamPrevention = require('../middleware/api/spam-prevention'), i18n = require('../i18n'), oauthServer, oauth; diff --git a/core/server/middleware/cors.js b/core/server/middleware/api/cors.js similarity index 98% rename from core/server/middleware/cors.js rename to core/server/middleware/api/cors.js index f38fb5ad45..8c166eb96a 100644 --- a/core/server/middleware/cors.js +++ b/core/server/middleware/api/cors.js @@ -2,7 +2,7 @@ var cors = require('cors'), _ = require('lodash'), url = require('url'), os = require('os'), - config = require('../config'), + config = require('../../config'), whitelist = [], ENABLE_CORS = {origin: true, maxAge: 86400}, DISABLE_CORS = {origin: false}; diff --git a/core/server/middleware/spam-prevention.js b/core/server/middleware/api/spam-prevention.js similarity index 97% rename from core/server/middleware/spam-prevention.js rename to core/server/middleware/api/spam-prevention.js index e2d94ecea8..823e8ea0c5 100644 --- a/core/server/middleware/spam-prevention.js +++ b/core/server/middleware/api/spam-prevention.js @@ -7,9 +7,9 @@ // Helpers to handle spam detection on signin, forgot password, and protected pages. var _ = require('lodash'), - errors = require('../errors'), - config = require('../config'), - i18n = require('../i18n'), + errors = require('../../errors'), + config = require('../../config'), + i18n = require('../../i18n'), loginSecurity = [], forgottenSecurity = [], spamPrevention; diff --git a/core/server/middleware/index.js b/core/server/middleware/index.js index 975be5d4fa..8cdf0ed904 100644 --- a/core/server/middleware/index.js +++ b/core/server/middleware/index.js @@ -1,56 +1,39 @@ var debug = require('debug')('ghost:middleware'), - bodyParser = require('body-parser'), - compress = require('compression'), express = require('express'), hbs = require('express-hbs'), path = require('path'), - netjet = require('netjet'), - multer = require('multer'), - tmpdir = require('os').tmpdir, - serveStatic = require('express').static, - routes = require('../routes'), + + // app requires config = require('../config'), - storage = require('../storage'), - logging = require('../logging'), errors = require('../errors'), + helpers = require('../helpers'), i18n = require('../i18n'), + logging = require('../logging'), + routes = require('../routes'), + storage = require('../storage'), utils = require('../utils'), + + // This should probably be an internal app sitemapHandler = require('../data/xml/sitemap/handler'), - cacheControl = require('./cache-control'), - checkSSL = require('./check-ssl'), - decideIsAdmin = require('./decide-is-admin'), - redirectToSetup = require('./redirect-to-setup'), + + // middleware + compress = require('compression'), + netjet = require('netjet'), + serveStatic = require('express').static, + // local middleware + cacheControl = require('./cache-control'), + checkSSL = require('./check-ssl'), + decideIsAdmin = require('./decide-is-admin'), + errorHandler = require('./error-handler'), ghostLocals = require('./ghost-locals'), + maintenance = require('./maintenance'), prettyURLs = require('./pretty-urls'), - serveSharedFile = require('./serve-shared-file'), - spamPrevention = require('./spam-prevention'), - staticTheme = require('./static-theme'), - themeHandler = require('./theme-handler'), - maintenance = require('./maintenance'), - errorHandler = require('./error-handler'), - versionMatch = require('./api/version-match'), - cors = require('./cors'), - validation = require('./validation'), - labs = require('./labs'), - helpers = require('../helpers'), - middleware, - setupMiddleware; + redirectToSetup = require('./redirect-to-setup'), + serveSharedFile = require('./serve-shared-file'), + staticTheme = require('./static-theme'), + themeHandler = require('./theme-handler'); -middleware = { - upload: multer({dest: tmpdir()}), - validation: validation, - cacheControl: cacheControl, - spamPrevention: spamPrevention, - api: { - errorHandler: errorHandler, - cors: cors, - labs: labs, - versionMatch: versionMatch, - maintenance: maintenance - } -}; - -setupMiddleware = function setupMiddleware(blogApp) { +module.exports = function setupMiddleware(blogApp) { debug('Middleware start'); var adminApp = express(), @@ -171,23 +154,18 @@ setupMiddleware = function setupMiddleware(blogApp) { // must happen AFTER asset loading and BEFORE routing blogApp.use(prettyURLs); - // Body parsing - blogApp.use(bodyParser.json({limit: '1mb'})); - blogApp.use(bodyParser.urlencoded({extended: true, limit: '1mb'})); - // ### Caching // Blog frontend is cacheable blogApp.use(cacheControl('public')); // Admin shouldn't be cached adminApp.use(cacheControl('private')); - // API shouldn't be cached - blogApp.use(routes.apiBaseUri, cacheControl('private')); debug('General middleware done'); - // ### Routing - // Set up API routes - blogApp.use(routes.apiBaseUri, routes.api(middleware)); + // Load the API + // @TODO: finish refactoring the API app + // @TODO: decide what to do with these paths - config defaults? config overrides? + blogApp.use('/ghost/api/v0.1/', require('../api/app')()); // Mount admin express app to /ghost and set up routes adminApp.use(redirectToSetup); @@ -210,7 +188,3 @@ setupMiddleware = function setupMiddleware(blogApp) { blogApp.use(errorHandler); debug('Middleware end'); }; - -module.exports = setupMiddleware; -// Export middleware functions directly -module.exports.middleware = middleware; diff --git a/core/server/routes/api.js b/core/server/routes/api.js deleted file mode 100644 index e1da7ad3d1..0000000000 --- a/core/server/routes/api.js +++ /dev/null @@ -1,193 +0,0 @@ -// # API routes -var express = require('express'), - api = require('../api'), - auth = require('../auth'), - apiRoutes; - -apiRoutes = function apiRoutes(middleware) { - var router = express.Router(), - // Authentication for public endpoints - authenticatePublic = [ - auth.authenticate.authenticateClient, - auth.authenticate.authenticateUser, - auth.authorize.requiresAuthorizedUserPublicAPI, - middleware.api.cors - ], - // Require user for private endpoints - authenticatePrivate = [ - auth.authenticate.authenticateClient, - auth.authenticate.authenticateUser, - auth.authorize.requiresAuthorizedUser, - middleware.api.cors - ]; - - // alias delete with del - router.del = router.delete; - - // send 503 json response in case of maintenance - router.use(middleware.api.maintenance); - - // Check version matches for API requests, depends on res.locals.safeVersion being set - // Therefore must come after themeHandler.ghostLocals, for now - router.use(middleware.api.versionMatch); - - // ## CORS pre-flight check - router.options('*', middleware.api.cors); - - // ## Configuration - router.get('/configuration', authenticatePrivate, api.http(api.configuration.read)); - router.get('/configuration/:key', authenticatePrivate, api.http(api.configuration.read)); - router.get('/configuration/timezones', authenticatePrivate, api.http(api.configuration.read)); - - // ## Posts - router.get('/posts', authenticatePublic, api.http(api.posts.browse)); - - router.post('/posts', authenticatePrivate, api.http(api.posts.add)); - router.get('/posts/:id', authenticatePublic, api.http(api.posts.read)); - router.get('/posts/slug/:slug', authenticatePublic, api.http(api.posts.read)); - router.put('/posts/:id', authenticatePrivate, api.http(api.posts.edit)); - router.del('/posts/:id', authenticatePrivate, api.http(api.posts.destroy)); - - // ## Schedules - router.put('/schedules/posts/:id', [ - auth.authenticate.authenticateClient, - auth.authenticate.authenticateUser - ], api.http(api.schedules.publishPost)); - - // ## Settings - router.get('/settings', authenticatePrivate, api.http(api.settings.browse)); - router.get('/settings/:key', authenticatePrivate, api.http(api.settings.read)); - router.put('/settings', authenticatePrivate, api.http(api.settings.edit)); - - // ## Users - router.get('/users', authenticatePublic, api.http(api.users.browse)); - router.get('/users/:id', authenticatePublic, api.http(api.users.read)); - router.get('/users/slug/:slug', authenticatePublic, api.http(api.users.read)); - router.get('/users/email/:email', authenticatePublic, api.http(api.users.read)); - - router.put('/users/password', authenticatePrivate, api.http(api.users.changePassword)); - router.put('/users/owner', authenticatePrivate, api.http(api.users.transferOwnership)); - router.put('/users/:id', authenticatePrivate, api.http(api.users.edit)); - - router.post('/users', authenticatePrivate, api.http(api.users.add)); - router.del('/users/:id', authenticatePrivate, api.http(api.users.destroy)); - - // ## Tags - router.get('/tags', authenticatePublic, api.http(api.tags.browse)); - router.get('/tags/:id', authenticatePublic, api.http(api.tags.read)); - router.get('/tags/slug/:slug', authenticatePublic, api.http(api.tags.read)); - router.post('/tags', authenticatePrivate, api.http(api.tags.add)); - router.put('/tags/:id', authenticatePrivate, api.http(api.tags.edit)); - router.del('/tags/:id', authenticatePrivate, api.http(api.tags.destroy)); - - // ## Subscribers - router.get('/subscribers', middleware.api.labs.subscribers, authenticatePrivate, api.http(api.subscribers.browse)); - router.get('/subscribers/csv', middleware.api.labs.subscribers, authenticatePrivate, api.http(api.subscribers.exportCSV)); - router.post('/subscribers/csv', - middleware.api.labs.subscribers, - authenticatePrivate, - middleware.upload.single('subscribersfile'), - middleware.validation.upload({type: 'subscribers'}), - api.http(api.subscribers.importCSV) - ); - router.get('/subscribers/:id', middleware.api.labs.subscribers, authenticatePrivate, api.http(api.subscribers.read)); - router.post('/subscribers', middleware.api.labs.subscribers, authenticatePublic, api.http(api.subscribers.add)); - router.put('/subscribers/:id', middleware.api.labs.subscribers, authenticatePrivate, api.http(api.subscribers.edit)); - router.del('/subscribers/:id', middleware.api.labs.subscribers, authenticatePrivate, api.http(api.subscribers.destroy)); - - // ## Roles - router.get('/roles/', authenticatePrivate, api.http(api.roles.browse)); - - // ## Clients - router.get('/clients/slug/:slug', api.http(api.clients.read)); - - // ## Slugs - router.get('/slugs/:type/:name', authenticatePrivate, api.http(api.slugs.generate)); - - // ## Themes - router.get('/themes/:name/download', - authenticatePrivate, - api.http(api.themes.download) - ); - - router.post('/themes/upload', - authenticatePrivate, - middleware.upload.single('theme'), - middleware.validation.upload({type: 'themes'}), - api.http(api.themes.upload) - ); - - router.del('/themes/:name', - authenticatePrivate, - api.http(api.themes.destroy) - ); - - // ## Notifications - router.get('/notifications', authenticatePrivate, api.http(api.notifications.browse)); - router.post('/notifications', authenticatePrivate, api.http(api.notifications.add)); - router.del('/notifications/:id', authenticatePrivate, api.http(api.notifications.destroy)); - - // ## DB - router.get('/db', authenticatePrivate, api.http(api.db.exportContent)); - router.post('/db', - authenticatePrivate, - middleware.upload.single('importfile'), - middleware.validation.upload({type: 'db'}), - api.http(api.db.importContent) - ); - router.del('/db', authenticatePrivate, api.http(api.db.deleteAllContent)); - - // ## Mail - router.post('/mail', authenticatePrivate, api.http(api.mail.send)); - router.post('/mail/test', authenticatePrivate, api.http(api.mail.sendTest)); - - // ## Slack - router.post('/slack/test', authenticatePrivate, api.http(api.slack.sendTest)); - - // ## Authentication - router.post('/authentication/passwordreset', - middleware.spamPrevention.forgotten, - api.http(api.authentication.generateResetToken) - ); - router.put('/authentication/passwordreset', api.http(api.authentication.resetPassword)); - router.post('/authentication/invitation', api.http(api.authentication.acceptInvitation)); - router.get('/authentication/invitation', api.http(api.authentication.isInvitation)); - router.post('/authentication/setup', api.http(api.authentication.setup)); - router.put('/authentication/setup', authenticatePrivate, api.http(api.authentication.updateSetup)); - router.get('/authentication/setup', api.http(api.authentication.isSetup)); - router.post('/authentication/token', - middleware.spamPrevention.signin, - auth.authenticate.authenticateClient, - auth.oauth.generateAccessToken - ); - - router.post('/authentication/ghost', [ - auth.authenticate.authenticateClient, - auth.authenticate.authenticateGhostUser, - api.http(api.authentication.createTokens) - ]); - - router.post('/authentication/revoke', authenticatePrivate, api.http(api.authentication.revoke)); - - // ## Uploads - // @TODO: rename endpoint to /images/upload (or similar) - router.post('/uploads', - authenticatePrivate, - middleware.upload.single('uploadimage'), - middleware.validation.upload({type: 'images'}), - api.http(api.uploads.add) - ); - - // ## Invites - router.get('/invites', authenticatePrivate, api.http(api.invites.browse)); - router.get('/invites/:id', authenticatePrivate, api.http(api.invites.read)); - router.post('/invites', authenticatePrivate, api.http(api.invites.add)); - router.del('/invites/:id', authenticatePrivate, api.http(api.invites.destroy)); - - // API Router middleware - router.use(middleware.api.errorHandler); - - return router; -}; - -module.exports = apiRoutes; diff --git a/core/server/routes/index.js b/core/server/routes/index.js index 0c2d4346a4..f5fb6f0da2 100644 --- a/core/server/routes/index.js +++ b/core/server/routes/index.js @@ -1,10 +1,4 @@ -var api = require('./api'), - admin = require('./admin'), - frontend = require('./frontend'); - module.exports = { - apiBaseUri: '/ghost/api/v0.1/', - api: api, - admin: admin, - frontend: frontend + admin: require('./admin'), + frontend: require('./frontend') }; diff --git a/core/test/unit/middleware/cors_spec.js b/core/test/unit/middleware/api/cors_spec.js similarity index 96% rename from core/test/unit/middleware/cors_spec.js rename to core/test/unit/middleware/api/cors_spec.js index 5975b95f7e..2fd9edd959 100644 --- a/core/test/unit/middleware/cors_spec.js +++ b/core/test/unit/middleware/api/cors_spec.js @@ -1,8 +1,8 @@ var sinon = require('sinon'), should = require('should'), rewire = require('rewire'), - configUtils = require('../../utils/configUtils'), - cors = rewire('../../../server/middleware/cors'); + configUtils = require('../../../utils/configUtils'), + cors = rewire('../../../../server/middleware/api/cors'); describe('cors', function () { var res, req, next, sandbox; @@ -33,7 +33,7 @@ describe('cors', function () { afterEach(function () { sandbox.restore(); configUtils.restore(); - cors = rewire('../../../server/middleware/cors'); + cors = rewire('../../../../server/middleware/api/cors'); }); it('should not be enabled without a request origin header', function (done) { diff --git a/core/test/unit/middleware/spam-prevention_spec.js b/core/test/unit/middleware/api/spam-prevention_spec.js similarity index 78% rename from core/test/unit/middleware/spam-prevention_spec.js rename to core/test/unit/middleware/api/spam-prevention_spec.js index 1f6e6525f6..81a34c4d47 100644 --- a/core/test/unit/middleware/spam-prevention_spec.js +++ b/core/test/unit/middleware/api/spam-prevention_spec.js @@ -1,7 +1,7 @@ var should = require('should'), sinon = require('sinon'), rewire = require('rewire'), - middleware = require('../../../server/middleware').middleware; + spamPrevention = require('../../../../server/middleware/api/spam-prevention'); describe('Middleware: spamPrevention', function () { var sandbox, @@ -19,7 +19,7 @@ describe('Middleware: spamPrevention', function () { spyNext = sinon.spy(function (param) { error = param; }); - middleware.spamPrevention = rewire('../../../server/middleware/spam-prevention'); + spamPrevention = rewire('../../../../server/middleware/api/spam-prevention'); }); afterEach(function () { @@ -41,7 +41,7 @@ describe('Middleware: spamPrevention', function () { it('calls next if refreshing the token', function (done) { req.body.grant_type = 'refresh_token'; - middleware.spamPrevention.signin(req, null, next); + spamPrevention.signin(req, null, next); next.calledOnce.should.be.true(); done(); @@ -50,7 +50,7 @@ describe('Middleware: spamPrevention', function () { it ('creates a BadRequestError when there\'s no username', function (done) { req.body = {}; - middleware.spamPrevention.signin(req, null, spyNext); + spamPrevention.signin(req, null, spyNext); should.exist(error); error.errorType.should.eql('BadRequestError'); @@ -59,10 +59,10 @@ describe('Middleware: spamPrevention', function () { it ('rate limits after 10 attempts', function (done) { for (var ndx = 0; ndx < 10; ndx = ndx + 1) { - middleware.spamPrevention.signin(req, null, spyNext); + spamPrevention.signin(req, null, spyNext); } - middleware.spamPrevention.signin(req, null, spyNext); + spamPrevention.signin(req, null, spyNext); should.exist(error); error.errorType.should.eql('TooManyRequestsError'); @@ -76,10 +76,10 @@ describe('Middleware: spamPrevention', function () { }); for (ndx = 0; ndx < 10; ndx = ndx + 1) { - middleware.spamPrevention.signin(req, null, spyNext); + spamPrevention.signin(req, null, spyNext); } - middleware.spamPrevention.signin(req, null, spyNext); + spamPrevention.signin(req, null, spyNext); error.errorType.should.eql('TooManyRequestsError'); error = null; @@ -89,7 +89,7 @@ describe('Middleware: spamPrevention', function () { return [3610, 10]; }); - middleware.spamPrevention.signin(req, null, spyNext); + spamPrevention.signin(req, null, spyNext); should(error).equal(undefined); spyNext.called.should.be.true(); @@ -117,7 +117,7 @@ describe('Middleware: spamPrevention', function () { passwordreset: [{}] }; - middleware.spamPrevention.forgotten(req, null, spyNext); + spamPrevention.forgotten(req, null, spyNext); error.errorType.should.eql('BadRequestError'); done(); @@ -125,10 +125,10 @@ describe('Middleware: spamPrevention', function () { it ('creates an unauthorized error after 5 attempts with same email', function (done) { for (var ndx = 0; ndx < 6; ndx = ndx + 1) { - middleware.spamPrevention.forgotten(req, null, spyNext); + spamPrevention.forgotten(req, null, spyNext); } - middleware.spamPrevention.forgotten(req, null, spyNext); + spamPrevention.forgotten(req, null, spyNext); error.errorType.should.eql('TooManyRequestsError'); done(); @@ -143,10 +143,10 @@ describe('Middleware: spamPrevention', function () { {email: email} ]; - middleware.spamPrevention.forgotten(req, null, spyNext); + spamPrevention.forgotten(req, null, spyNext); } - middleware.spamPrevention.forgotten(req, null, spyNext); + spamPrevention.forgotten(req, null, spyNext); error.errorType.should.eql('TooManyRequestsError'); done(); diff --git a/core/test/unit/middleware/cache-control_spec.js b/core/test/unit/middleware/cache-control_spec.js index c218a135a8..9c4e6a3f31 100644 --- a/core/test/unit/middleware/cache-control_spec.js +++ b/core/test/unit/middleware/cache-control_spec.js @@ -1,6 +1,6 @@ var should = require('should'), sinon = require('sinon'), - middleware = require('../../../server/middleware').middleware; + cacheControl = require('../../../server/middleware/cache-control'); describe('Middleware: cacheControl', function () { var sandbox, @@ -19,7 +19,7 @@ describe('Middleware: cacheControl', function () { }); it('correctly sets the public profile headers', function (done) { - middleware.cacheControl('public')(null, res, function (a) { + cacheControl('public')(null, res, function (a) { should.not.exist(a); res.set.calledOnce.should.be.true(); res.set.calledWith({'Cache-Control': 'public, max-age=0'}); @@ -28,7 +28,7 @@ describe('Middleware: cacheControl', function () { }); it('correctly sets the private profile headers', function (done) { - middleware.cacheControl('private')(null, res, function (a) { + cacheControl('private')(null, res, function (a) { should.not.exist(a); res.set.calledOnce.should.be.true(); res.set.calledWith({ @@ -40,7 +40,7 @@ describe('Middleware: cacheControl', function () { }); it('will not set headers without a profile', function (done) { - middleware.cacheControl()(null, res, function (a) { + cacheControl()(null, res, function (a) { should.not.exist(a); res.set.called.should.be.false(); done(); @@ -48,8 +48,8 @@ describe('Middleware: cacheControl', function () { }); it('will not get confused between serving public and private', function (done) { - var publicCC = middleware.cacheControl('public'), - privateCC = middleware.cacheControl('private'); + var publicCC = cacheControl('public'), + privateCC = cacheControl('private'); publicCC(null, res, function () { res.set.calledOnce.should.be.true(); @@ -79,7 +79,7 @@ describe('Middleware: cacheControl', function () { it('will override public with private for private blogs', function (done) { res.isPrivateBlog = true; - middleware.cacheControl('public')(null, res, function (a) { + cacheControl('public')(null, res, function (a) { should.not.exist(a); res.set.calledOnce.should.be.true(); res.set.calledWith({