mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-29 07:09:48 +03:00
Added v2 api endpoints (#9874)
refs #9866 - Registered Content API under /ghost/api/v2/content/ - Registered Admin API under /ghost/api/v2/admin/ - Moved API v0.1 implementation to web/api/v0.1 - Created web/api/v2 for the new api endpoints - Started with reducing the implementation for the new Content API (the Content api does not serve admin api endpoints, that's why it was reducible) - Covered parent-app module with basic test checking correct applications/routes are being mounted - Added a readme file, which contains a warning using v2, because it's under active development! - This PR does only make the new endpoints available, we have not: - optimised the web folder (e.g. res.isAdmin) - started with different API controllers - reason: we want to do more preparation tasks before we copy the api controllers
This commit is contained in:
parent
6ae5c13de7
commit
57271127f4
7
core/server/web/api/README.md
Normal file
7
core/server/web/api/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Ghost APIs
|
||||
|
||||
Ghost is moving towards providing more robust APIs in the future. A plan and decisions can be found [here](https://github.com/TryGhost/Ghost/issues/9866).
|
||||
|
||||
## WARNING!
|
||||
|
||||
The v2 API (`/ghost/api/v2/*` endpoints) is to be considered under active development until this message is removed. Please use with caution and don't rely too heavy on it just yet :)
|
@ -9,16 +9,16 @@ const debug = require('ghost-ignition').debug('api'),
|
||||
// Include the middleware
|
||||
|
||||
// API specific
|
||||
versionMatch = require('../middleware/api/version-match'), // global
|
||||
versionMatch = require('../../middleware/api/version-match'), // global
|
||||
|
||||
// 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
|
||||
cacheControl = require('../../middleware/cache-control'), // global, shared
|
||||
maintenance = require('../../middleware/maintenance'), // global, shared
|
||||
errorHandler = require('../../middleware/error-handler'); // global, shared
|
||||
|
||||
module.exports = function setupApiApp() {
|
||||
debug('API setup start');
|
||||
debug('API v0.1 setup start');
|
||||
const apiApp = express();
|
||||
|
||||
// @TODO finish refactoring this away.
|
||||
@ -54,7 +54,7 @@ module.exports = function setupApiApp() {
|
||||
apiApp.use(errorHandler.resourceNotFound);
|
||||
apiApp.use(errorHandler.handleJSONResponse);
|
||||
|
||||
debug('API setup end');
|
||||
debug('API v0.1 setup end');
|
||||
|
||||
return apiApp;
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
const prettyURLs = require('../middleware/pretty-urls'),
|
||||
cors = require('../middleware/api/cors'),
|
||||
urlRedirects = require('../middleware/url-redirects'),
|
||||
auth = require('../../services/auth');
|
||||
const prettyURLs = require('../../middleware/pretty-urls'),
|
||||
cors = require('../../middleware/api/cors'),
|
||||
urlRedirects = require('../../middleware/url-redirects'),
|
||||
auth = require('../../../services/auth');
|
||||
|
||||
/**
|
||||
* Auth Middleware Packages
|
@ -1,26 +1,25 @@
|
||||
const express = require('express'),
|
||||
// This essentially provides the controllers for the routes
|
||||
api = require('../../api'),
|
||||
api = require('../../../api'),
|
||||
|
||||
// Middleware
|
||||
mw = require('./middleware'),
|
||||
|
||||
// API specific
|
||||
auth = require('../../services/auth'),
|
||||
cors = require('../middleware/api/cors'),
|
||||
brute = require('../middleware/brute'),
|
||||
auth = require('../../../services/auth'),
|
||||
cors = require('../../middleware/api/cors'),
|
||||
brute = require('../../middleware/brute'),
|
||||
|
||||
// Handling uploads & imports
|
||||
tmpdir = require('os').tmpdir,
|
||||
upload = require('multer')({dest: tmpdir()}),
|
||||
validation = require('../middleware/validation'),
|
||||
image = require('../middleware/image'),
|
||||
validation = require('../../middleware/validation'),
|
||||
image = require('../../middleware/image'),
|
||||
|
||||
// Temporary
|
||||
// @TODO find a more appy way to do this!
|
||||
labs = require('../middleware/labs');
|
||||
labs = require('../../middleware/labs');
|
||||
|
||||
// @TODO refactor/clean this up - how do we want the routing to work long term?
|
||||
module.exports = function apiRoutes() {
|
||||
const apiRouter = express.Router();
|
||||
|
60
core/server/web/api/v2/admin/app.js
Normal file
60
core/server/web/api/v2/admin/app.js
Normal file
@ -0,0 +1,60 @@
|
||||
// # API routes
|
||||
const debug = require('ghost-ignition').debug('api'),
|
||||
boolParser = require('express-query-boolean'),
|
||||
express = require('express'),
|
||||
|
||||
// routes
|
||||
routes = require('./routes'),
|
||||
|
||||
// Include the middleware
|
||||
|
||||
// API specific
|
||||
versionMatch = require('../../../middleware/api/version-match'), // global
|
||||
|
||||
// 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
|
||||
|
||||
module.exports = function setupApiApp() {
|
||||
debug('Admin API v2 setup start');
|
||||
const apiApp = express();
|
||||
|
||||
// @TODO finish refactoring this away.
|
||||
apiApp.use(function setIsAdmin(req, res, next) {
|
||||
// api === isAdmin
|
||||
res.isAdmin = true;
|
||||
next();
|
||||
});
|
||||
|
||||
// API middleware
|
||||
|
||||
// Body parsing
|
||||
apiApp.use(bodyParser.json({limit: '1mb'}));
|
||||
apiApp.use(bodyParser.urlencoded({extended: true, limit: '1mb'}));
|
||||
|
||||
// Query parsing
|
||||
apiApp.use(boolParser());
|
||||
|
||||
// send 503 json response in case of maintenance
|
||||
apiApp.use(maintenance);
|
||||
|
||||
// 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(routes());
|
||||
|
||||
// API error handling
|
||||
apiApp.use(errorHandler.resourceNotFound);
|
||||
apiApp.use(errorHandler.handleJSONResponse);
|
||||
|
||||
debug('Admin API v2 setup end');
|
||||
|
||||
return apiApp;
|
||||
};
|
30
core/server/web/api/v2/admin/middleware.js
Normal file
30
core/server/web/api/v2/admin/middleware.js
Normal file
@ -0,0 +1,30 @@
|
||||
const prettyURLs = require('../../../middleware/pretty-urls'),
|
||||
cors = require('../../../middleware/api/cors'),
|
||||
urlRedirects = require('../../../middleware/url-redirects'),
|
||||
auth = require('../../../../services/auth');
|
||||
|
||||
/**
|
||||
* Authentication for private endpoints
|
||||
*/
|
||||
module.exports.authenticatePrivate = [
|
||||
auth.authenticate.authenticateClient,
|
||||
auth.authenticate.authenticateUser,
|
||||
auth.authorize.requiresAuthorizedUser,
|
||||
cors,
|
||||
urlRedirects,
|
||||
prettyURLs
|
||||
];
|
||||
|
||||
/**
|
||||
* Authentication for client endpoints
|
||||
*/
|
||||
module.exports.authenticateClient = function authenticateClient(client) {
|
||||
return [
|
||||
auth.authenticate.authenticateClient,
|
||||
auth.authenticate.authenticateUser,
|
||||
auth.authorize.requiresAuthorizedClient(client),
|
||||
cors,
|
||||
urlRedirects,
|
||||
prettyURLs
|
||||
];
|
||||
};
|
222
core/server/web/api/v2/admin/routes.js
Normal file
222
core/server/web/api/v2/admin/routes.js
Normal file
@ -0,0 +1,222 @@
|
||||
const express = require('express'),
|
||||
// This essentially provides the controllers for the routes
|
||||
api = require('../../../../api'),
|
||||
|
||||
// Middleware
|
||||
mw = require('./middleware'),
|
||||
|
||||
// API specific
|
||||
auth = require('../../../../services/auth'),
|
||||
cors = require('../../../middleware/api/cors'),
|
||||
brute = require('../../../middleware/brute'),
|
||||
|
||||
// Handling uploads & imports
|
||||
tmpdir = require('os').tmpdir,
|
||||
upload = require('multer')({dest: tmpdir()}),
|
||||
validation = require('../../../middleware/validation'),
|
||||
image = require('../../../middleware/image'),
|
||||
|
||||
// Temporary
|
||||
// @TODO find a more appy way to do this!
|
||||
labs = require('../../../middleware/labs');
|
||||
|
||||
module.exports = function apiRoutes() {
|
||||
const router = express.Router();
|
||||
|
||||
// alias delete with del
|
||||
router.del = router.delete;
|
||||
|
||||
// ## CORS pre-flight check
|
||||
router.options('*', cors);
|
||||
|
||||
// ## Configuration
|
||||
router.get('/configuration', api.http(api.configuration.read));
|
||||
router.get('/configuration/:key', mw.authenticatePrivate, api.http(api.configuration.read));
|
||||
|
||||
// ## Posts
|
||||
router.get('/posts', mw.authenticatePrivate, api.http(api.posts.browse));
|
||||
|
||||
router.post('/posts', mw.authenticatePrivate, api.http(api.posts.add));
|
||||
router.get('/posts/:id', mw.authenticatePrivate, api.http(api.posts.read));
|
||||
router.get('/posts/slug/:slug', mw.authenticatePrivate, api.http(api.posts.read));
|
||||
router.put('/posts/:id', mw.authenticatePrivate, api.http(api.posts.edit));
|
||||
router.del('/posts/:id', mw.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/routes/yaml', mw.authenticatePrivate, api.http(api.settings.download));
|
||||
router.post('/settings/routes/yaml',
|
||||
mw.authenticatePrivate,
|
||||
upload.single('routes'),
|
||||
validation.upload({type: 'routes'}),
|
||||
api.http(api.settings.upload)
|
||||
);
|
||||
|
||||
router.get('/settings', mw.authenticatePrivate, api.http(api.settings.browse));
|
||||
router.get('/settings/:key', mw.authenticatePrivate, api.http(api.settings.read));
|
||||
router.put('/settings', mw.authenticatePrivate, api.http(api.settings.edit));
|
||||
|
||||
// ## Users
|
||||
router.get('/users', mw.authenticatePrivate, api.http(api.users.browse));
|
||||
router.get('/users/:id', mw.authenticatePrivate, api.http(api.users.read));
|
||||
router.get('/users/slug/:slug', mw.authenticatePrivate, api.http(api.users.read));
|
||||
// NOTE: We don't expose any email addresses via the public api.
|
||||
router.get('/users/email/:email', mw.authenticatePrivate, api.http(api.users.read));
|
||||
|
||||
router.put('/users/password', mw.authenticatePrivate, api.http(api.users.changePassword));
|
||||
router.put('/users/owner', mw.authenticatePrivate, api.http(api.users.transferOwnership));
|
||||
router.put('/users/:id', mw.authenticatePrivate, api.http(api.users.edit));
|
||||
router.del('/users/:id', mw.authenticatePrivate, api.http(api.users.destroy));
|
||||
|
||||
// ## Tags
|
||||
router.get('/tags', mw.authenticatePrivate, api.http(api.tags.browse));
|
||||
router.get('/tags/:id', mw.authenticatePrivate, api.http(api.tags.read));
|
||||
router.get('/tags/slug/:slug', mw.authenticatePrivate, api.http(api.tags.read));
|
||||
router.post('/tags', mw.authenticatePrivate, api.http(api.tags.add));
|
||||
router.put('/tags/:id', mw.authenticatePrivate, api.http(api.tags.edit));
|
||||
router.del('/tags/:id', mw.authenticatePrivate, api.http(api.tags.destroy));
|
||||
|
||||
// ## Subscribers
|
||||
router.get('/subscribers', labs.subscribers, mw.authenticatePrivate, api.http(api.subscribers.browse));
|
||||
router.get('/subscribers/csv', labs.subscribers, mw.authenticatePrivate, api.http(api.subscribers.exportCSV));
|
||||
router.post('/subscribers/csv',
|
||||
labs.subscribers,
|
||||
mw.authenticatePrivate,
|
||||
upload.single('subscribersfile'),
|
||||
validation.upload({type: 'subscribers'}),
|
||||
api.http(api.subscribers.importCSV)
|
||||
);
|
||||
router.get('/subscribers/:id', labs.subscribers, mw.authenticatePrivate, api.http(api.subscribers.read));
|
||||
router.get('/subscribers/email/:email', labs.subscribers, mw.authenticatePrivate, api.http(api.subscribers.read));
|
||||
router.post('/subscribers', labs.subscribers, mw.authenticatePrivate, api.http(api.subscribers.add));
|
||||
router.put('/subscribers/:id', labs.subscribers, mw.authenticatePrivate, api.http(api.subscribers.edit));
|
||||
router.del('/subscribers/:id', labs.subscribers, mw.authenticatePrivate, api.http(api.subscribers.destroy));
|
||||
router.del('/subscribers/email/:email', labs.subscribers, mw.authenticatePrivate, api.http(api.subscribers.destroy));
|
||||
|
||||
// ## Roles
|
||||
router.get('/roles/', mw.authenticatePrivate, api.http(api.roles.browse));
|
||||
|
||||
// ## Clients
|
||||
router.get('/clients/slug/:slug', api.http(api.clients.read));
|
||||
|
||||
// ## Slugs
|
||||
router.get('/slugs/:type/:name', mw.authenticatePrivate, api.http(api.slugs.generate));
|
||||
|
||||
// ## Themes
|
||||
router.get('/themes/', mw.authenticatePrivate, api.http(api.themes.browse));
|
||||
|
||||
router.get('/themes/:name/download',
|
||||
mw.authenticatePrivate,
|
||||
api.http(api.themes.download)
|
||||
);
|
||||
|
||||
router.post('/themes/upload',
|
||||
mw.authenticatePrivate,
|
||||
upload.single('theme'),
|
||||
validation.upload({type: 'themes'}),
|
||||
api.http(api.themes.upload)
|
||||
);
|
||||
|
||||
router.put('/themes/:name/activate',
|
||||
mw.authenticatePrivate,
|
||||
api.http(api.themes.activate)
|
||||
);
|
||||
|
||||
router.del('/themes/:name',
|
||||
mw.authenticatePrivate,
|
||||
api.http(api.themes.destroy)
|
||||
);
|
||||
|
||||
// ## Notifications
|
||||
router.get('/notifications', mw.authenticatePrivate, api.http(api.notifications.browse));
|
||||
router.post('/notifications', mw.authenticatePrivate, api.http(api.notifications.add));
|
||||
router.del('/notifications/:id', mw.authenticatePrivate, api.http(api.notifications.destroy));
|
||||
|
||||
// ## DB
|
||||
router.get('/db', mw.authenticatePrivate, api.http(api.db.exportContent));
|
||||
router.post('/db',
|
||||
mw.authenticatePrivate,
|
||||
upload.single('importfile'),
|
||||
validation.upload({type: 'db'}),
|
||||
api.http(api.db.importContent)
|
||||
);
|
||||
router.del('/db', mw.authenticatePrivate, api.http(api.db.deleteAllContent));
|
||||
|
||||
// ## Mail
|
||||
router.post('/mail', mw.authenticatePrivate, api.http(api.mail.send));
|
||||
router.post('/mail/test', mw.authenticatePrivate, api.http(api.mail.sendTest));
|
||||
|
||||
// ## Slack
|
||||
router.post('/slack/test', mw.authenticatePrivate, api.http(api.slack.sendTest));
|
||||
|
||||
// ## Authentication
|
||||
router.post('/authentication/passwordreset',
|
||||
brute.globalReset,
|
||||
brute.userReset,
|
||||
api.http(api.authentication.generateResetToken)
|
||||
);
|
||||
router.put('/authentication/passwordreset', brute.globalBlock, 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', mw.authenticatePrivate, api.http(api.authentication.updateSetup));
|
||||
router.get('/authentication/setup', api.http(api.authentication.isSetup));
|
||||
|
||||
router.post('/authentication/token',
|
||||
mw.authenticateClient(),
|
||||
brute.globalBlock,
|
||||
brute.userLogin,
|
||||
auth.oauth.generateAccessToken
|
||||
);
|
||||
|
||||
router.post('/authentication/revoke', mw.authenticatePrivate, api.http(api.authentication.revoke));
|
||||
|
||||
// ## Uploads
|
||||
// @TODO: rename endpoint to /images/upload (or similar)
|
||||
router.post('/uploads',
|
||||
mw.authenticatePrivate,
|
||||
upload.single('uploadimage'),
|
||||
validation.upload({type: 'images'}),
|
||||
image.normalize,
|
||||
api.http(api.uploads.add)
|
||||
);
|
||||
|
||||
router.post('/db/backup', mw.authenticateClient('Ghost Backup'), api.http(api.db.backupContent));
|
||||
|
||||
router.post('/uploads/icon',
|
||||
mw.authenticatePrivate,
|
||||
upload.single('uploadimage'),
|
||||
validation.upload({type: 'icons'}),
|
||||
validation.blogIcon(),
|
||||
api.http(api.uploads.add)
|
||||
);
|
||||
|
||||
// ## Invites
|
||||
router.get('/invites', mw.authenticatePrivate, api.http(api.invites.browse));
|
||||
router.get('/invites/:id', mw.authenticatePrivate, api.http(api.invites.read));
|
||||
router.post('/invites', mw.authenticatePrivate, api.http(api.invites.add));
|
||||
router.del('/invites/:id', mw.authenticatePrivate, api.http(api.invites.destroy));
|
||||
|
||||
// ## Redirects (JSON based)
|
||||
router.get('/redirects/json', mw.authenticatePrivate, api.http(api.redirects.download));
|
||||
router.post('/redirects/json',
|
||||
mw.authenticatePrivate,
|
||||
upload.single('redirects'),
|
||||
validation.upload({type: 'redirects'}),
|
||||
api.http(api.redirects.upload)
|
||||
);
|
||||
|
||||
// ## Webhooks (RESTHooks)
|
||||
router.post('/webhooks', mw.authenticatePrivate, api.http(api.webhooks.add));
|
||||
router.del('/webhooks/:id', mw.authenticatePrivate, api.http(api.webhooks.destroy));
|
||||
|
||||
// ## Oembed (fetch response from oembed provider)
|
||||
router.get('/oembed', mw.authenticatePrivate, api.http(api.oembed.read));
|
||||
|
||||
return router;
|
||||
};
|
48
core/server/web/api/v2/content/app.js
Normal file
48
core/server/web/api/v2/content/app.js
Normal file
@ -0,0 +1,48 @@
|
||||
// # API routes
|
||||
const debug = require('ghost-ignition').debug('api'),
|
||||
boolParser = require('express-query-boolean'),
|
||||
express = require('express'),
|
||||
|
||||
// routes
|
||||
routes = require('./routes'),
|
||||
|
||||
// Include the middleware
|
||||
|
||||
// Shared
|
||||
cacheControl = require('../../../middleware/cache-control'), // global, shared
|
||||
maintenance = require('../../../middleware/maintenance'), // global, shared
|
||||
errorHandler = require('../../../middleware/error-handler'); // global, shared
|
||||
|
||||
module.exports = function setupApiApp() {
|
||||
debug('Content API v2 setup start');
|
||||
const apiApp = express();
|
||||
|
||||
// @TODO finish refactoring this away.
|
||||
apiApp.use(function setIsAdmin(req, res, next) {
|
||||
// api === isAdmin
|
||||
res.isAdmin = true;
|
||||
next();
|
||||
});
|
||||
|
||||
// API middleware
|
||||
|
||||
// Query parsing
|
||||
apiApp.use(boolParser());
|
||||
|
||||
// send 503 json response in case of maintenance
|
||||
apiApp.use(maintenance);
|
||||
|
||||
// API shouldn't be cached
|
||||
apiApp.use(cacheControl('private'));
|
||||
|
||||
// Routing
|
||||
apiApp.use(routes());
|
||||
|
||||
// API error handling
|
||||
apiApp.use(errorHandler.resourceNotFound);
|
||||
apiApp.use(errorHandler.handleJSONResponse);
|
||||
|
||||
debug('Content API v2 setup end');
|
||||
|
||||
return apiApp;
|
||||
};
|
26
core/server/web/api/v2/content/middleware.js
Normal file
26
core/server/web/api/v2/content/middleware.js
Normal file
@ -0,0 +1,26 @@
|
||||
const prettyURLs = require('../../../middleware/pretty-urls'),
|
||||
cors = require('../../../middleware/api/cors'),
|
||||
urlRedirects = require('../../../middleware/url-redirects'),
|
||||
auth = require('../../../../services/auth');
|
||||
|
||||
/**
|
||||
* Auth Middleware Packages
|
||||
*
|
||||
* IMPORTANT
|
||||
* - cors middleware MUST happen before pretty urls, because otherwise cors header can get lost on redirect
|
||||
* - cors middleware MUST happen after authenticateClient, because authenticateClient reads the trusted domains
|
||||
* - url redirects MUST happen after cors, otherwise cors header can get lost on redirect
|
||||
*/
|
||||
|
||||
/**
|
||||
* Authentication for public endpoints
|
||||
*/
|
||||
module.exports.authenticatePublic = [
|
||||
auth.authenticate.authenticateClient,
|
||||
auth.authenticate.authenticateUser,
|
||||
// This is a labs-enabled middleware
|
||||
auth.authorize.requiresAuthorizedUserPublicAPI,
|
||||
cors,
|
||||
urlRedirects,
|
||||
prettyURLs
|
||||
];
|
49
core/server/web/api/v2/content/routes.js
Normal file
49
core/server/web/api/v2/content/routes.js
Normal file
@ -0,0 +1,49 @@
|
||||
const express = require('express'),
|
||||
// This essentially provides the controllers for the routes
|
||||
api = require('../../../../api'),
|
||||
|
||||
// Middleware
|
||||
mw = require('./middleware'),
|
||||
|
||||
// API specific
|
||||
cors = require('../../../middleware/api/cors'),
|
||||
|
||||
// Temporary
|
||||
// @TODO find a more appy way to do this!
|
||||
labs = require('../../../middleware/labs');
|
||||
|
||||
module.exports = function apiRoutes() {
|
||||
const router = express.Router();
|
||||
|
||||
// alias delete with del
|
||||
router.del = router.delete;
|
||||
|
||||
// ## CORS pre-flight check
|
||||
router.options('*', cors);
|
||||
|
||||
// ## Configuration
|
||||
router.get('/configuration', api.http(api.configuration.read));
|
||||
|
||||
// ## Posts
|
||||
router.get('/posts', mw.authenticatePublic, api.http(api.posts.browse));
|
||||
router.get('/posts/:id', mw.authenticatePublic, api.http(api.posts.read));
|
||||
router.get('/posts/slug/:slug', mw.authenticatePublic, api.http(api.posts.read));
|
||||
|
||||
// ## Users
|
||||
router.get('/users', mw.authenticatePublic, api.http(api.users.browse));
|
||||
router.get('/users/:id', mw.authenticatePublic, api.http(api.users.read));
|
||||
router.get('/users/slug/:slug', mw.authenticatePublic, api.http(api.users.read));
|
||||
|
||||
// ## Tags
|
||||
router.get('/tags', mw.authenticatePublic, api.http(api.tags.browse));
|
||||
router.get('/tags/:id', mw.authenticatePublic, api.http(api.tags.read));
|
||||
router.get('/tags/slug/:slug', mw.authenticatePublic, api.http(api.tags.read));
|
||||
|
||||
// ## Subscribers
|
||||
router.post('/subscribers', labs.subscribers, mw.authenticatePublic, api.http(api.subscribers.add));
|
||||
|
||||
// ## Clients
|
||||
router.get('/clients/slug/:slug', api.http(api.clients.read));
|
||||
|
||||
return router;
|
||||
};
|
@ -45,7 +45,9 @@ module.exports = function setupParentApp(options = {}) {
|
||||
// API
|
||||
// @TODO: finish refactoring the API app
|
||||
// @TODO: decide what to do with these paths - config defaults? config overrides?
|
||||
parentApp.use('/ghost/api/v0.1/', require('./api/app')());
|
||||
parentApp.use('/ghost/api/v0.1/', require('./api/v0.1/app')());
|
||||
parentApp.use('/ghost/api/v2/content/', require('./api/v2/content/app')());
|
||||
parentApp.use('/ghost/api/v2/admin/', require('./api/v2/admin/app')());
|
||||
|
||||
// ADMIN
|
||||
parentApp.use('/ghost', require('./admin')());
|
||||
|
57
core/test/unit/web/parent-app_spec.js
Normal file
57
core/test/unit/web/parent-app_spec.js
Normal file
@ -0,0 +1,57 @@
|
||||
var should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
proxyquire = require('proxyquire'),
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
describe('parent app', function () {
|
||||
let expressStub;
|
||||
let use;
|
||||
let apiV01Spy;
|
||||
let apiContentV2Spy;
|
||||
let apiAdminV2Spy;
|
||||
let parentApp;
|
||||
let adminSpy;
|
||||
let siteSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
use = sandbox.spy();
|
||||
expressStub = () => ({
|
||||
use,
|
||||
enable: () => {}
|
||||
});
|
||||
|
||||
apiV01Spy = sinon.spy();
|
||||
apiContentV2Spy = sinon.spy();
|
||||
apiAdminV2Spy = sinon.spy();
|
||||
adminSpy = sinon.spy();
|
||||
siteSpy = sinon.spy();
|
||||
|
||||
parentApp = proxyquire('../../../server/web/parent-app', {
|
||||
express: expressStub,
|
||||
'./api/v0.1/app': apiV01Spy,
|
||||
'./api/v2/content/app': apiContentV2Spy,
|
||||
'./api/v2/admin/app': apiAdminV2Spy,
|
||||
'./admin': adminSpy,
|
||||
'./site': siteSpy
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('should mount 5 apps and assign correct routes to them', function () {
|
||||
parentApp();
|
||||
|
||||
use.calledWith('/ghost/api/v0.1/').should.be.true();
|
||||
use.calledWith('/ghost/api/v2/content/').should.be.true();
|
||||
use.calledWith('/ghost/api/v2/admin/').should.be.true();
|
||||
use.calledWith('/ghost').should.be.true();
|
||||
|
||||
apiV01Spy.called.should.be.true();
|
||||
apiContentV2Spy.called.should.be.true();
|
||||
apiAdminV2Spy.called.should.be.true();
|
||||
adminSpy.called.should.be.true();
|
||||
siteSpy.called.should.be.true();
|
||||
});
|
||||
});
|
@ -128,6 +128,7 @@
|
||||
"minimist": "1.2.0",
|
||||
"mocha": "4.1.0",
|
||||
"nock": "9.4.0",
|
||||
"proxyquire": "2.1.0",
|
||||
"rewire": "3.0.2",
|
||||
"should": "13.2.1",
|
||||
"should-http": "0.1.1",
|
||||
|
29
yarn.lock
29
yarn.lock
@ -1934,6 +1934,13 @@ file-sync-cmp@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz#a5e7a8ffbfa493b43b923bbd4ca89a53b63b612b"
|
||||
|
||||
fill-keys@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20"
|
||||
dependencies:
|
||||
is-object "~1.0.1"
|
||||
merge-descriptors "~1.0.0"
|
||||
|
||||
fill-range@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
|
||||
@ -3065,7 +3072,7 @@ is-number@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
|
||||
|
||||
is-object@^1.0.1:
|
||||
is-object@^1.0.1, is-object@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470"
|
||||
|
||||
@ -3782,7 +3789,7 @@ meow@^3.1.0, meow@^3.3.0:
|
||||
redent "^1.0.0"
|
||||
trim-newlines "^1.0.0"
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
merge-descriptors@1.0.1, merge-descriptors@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
|
||||
@ -3971,6 +3978,10 @@ mocha@^3.1.2:
|
||||
mkdirp "0.5.1"
|
||||
supports-color "3.1.2"
|
||||
|
||||
module-not-found-error@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0"
|
||||
|
||||
moment-timezone@0.5.21:
|
||||
version "0.5.21"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.21.tgz#3cba247d84492174dbf71de2a9848fa13207b845"
|
||||
@ -4944,6 +4955,14 @@ proxy-addr@~2.0.3:
|
||||
forwarded "~0.1.2"
|
||||
ipaddr.js "1.6.0"
|
||||
|
||||
proxyquire@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.0.tgz#c2263a38bf0725f2ae950facc130e27510edce8d"
|
||||
dependencies:
|
||||
fill-keys "^1.0.2"
|
||||
module-not-found-error "^1.0.0"
|
||||
resolve "~1.8.1"
|
||||
|
||||
pseudomap@^1.0.1, pseudomap@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
||||
@ -5235,6 +5254,12 @@ resolve@1.7.1, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.4.0:
|
||||
dependencies:
|
||||
path-parse "^1.0.5"
|
||||
|
||||
resolve@~1.8.1:
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
|
||||
dependencies:
|
||||
path-parse "^1.0.5"
|
||||
|
||||
restore-cursor@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
|
||||
|
Loading…
Reference in New Issue
Block a user