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

Added canary endpoint to parent app

no issue

Mounts new canary api endpoint on parent app
This commit is contained in:
Rish 2019-08-09 19:51:30 +05:30 committed by Rishabh Garg
parent 13a77363de
commit 9ab754a0c7
8 changed files with 453 additions and 0 deletions

View file

@ -0,0 +1,41 @@
const debug = require('ghost-ignition').debug('web:canary:admin:app');
const boolParser = require('express-query-boolean');
const express = require('express');
const bodyParser = require('body-parser');
const shared = require('../../../shared');
const routes = require('./routes');
module.exports = function setupApiApp() {
debug('Admin API canary setup start');
const apiApp = express();
// 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(shared.middlewares.maintenance);
// Check version matches for API requests, depends on res.locals.safeVersion being set
// Therefore must come after themeHandler.ghostLocals, for now
apiApp.use(shared.middlewares.api.versionMatch);
// Admin API shouldn't be cached
apiApp.use(shared.middlewares.cacheControl('private'));
// Routing
apiApp.use(routes());
// API error handling
apiApp.use(shared.middlewares.errorHandler.resourceNotFound);
apiApp.use(shared.middlewares.errorHandler.handleJSONResponseV2);
debug('Admin API canary setup end');
return apiApp;
};

View file

@ -0,0 +1,57 @@
const common = require('../../../../lib/common');
const auth = require('../../../../services/auth');
const shared = require('../../../shared');
const notImplemented = function (req, res, next) {
// CASE: user is logged in, allow
if (!req.api_key) {
return next();
}
// @NOTE: integrations have limited access for now
const whitelisted = {
// @NOTE: stable
site: ['GET'],
posts: ['GET', 'PUT', 'DELETE', 'POST'],
pages: ['GET', 'PUT', 'DELETE', 'POST'],
images: ['POST'],
// @NOTE: experimental
tags: ['GET', 'PUT', 'DELETE', 'POST'],
users: ['GET'],
themes: ['POST', 'PUT'],
subscribers: ['GET', 'PUT', 'DELETE', 'POST'],
config: ['GET'],
webhooks: ['POST', 'DELETE'],
schedules: ['PUT'],
db: ['POST']
};
const match = req.url.match(/^\/(\w+)\/?/);
if (match) {
const entity = match[1];
if (whitelisted[entity] && whitelisted[entity].includes(req.method)) {
return next();
}
}
next(new common.errors.GhostError({
errorType: 'NotImplementedError',
message: common.i18n.t('errors.api.common.notImplemented'),
statusCode: '501'
}));
};
/**
* Authentication for private endpoints
*/
module.exports.authAdminApi = [
auth.authenticate.authenticateAdminApi,
auth.authorize.authorizeAdminApi,
shared.middlewares.updateUserLastSeen,
shared.middlewares.api.cors,
shared.middlewares.urlRedirects.adminRedirect,
shared.middlewares.prettyUrls,
notImplemented
];

View file

@ -0,0 +1,228 @@
const express = require('express');
const api = require('../../../../api');
const apiCanary = require('../../../../api/canary');
const mw = require('./middleware');
const shared = require('../../../shared');
// Handling uploads & imports
const upload = shared.middlewares.upload;
module.exports = function apiRoutes() {
const router = express.Router();
// alias delete with del
router.del = router.delete;
router.use(shared.middlewares.api.cors);
const http = apiCanary.http;
// ## Public
router.get('/site', http(apiCanary.site.read));
// ## Configuration
router.get('/config', mw.authAdminApi, http(apiCanary.config.read));
// ## Posts
router.get('/posts', mw.authAdminApi, http(apiCanary.posts.browse));
router.post('/posts', mw.authAdminApi, http(apiCanary.posts.add));
router.get('/posts/:id', mw.authAdminApi, http(apiCanary.posts.read));
router.get('/posts/slug/:slug', mw.authAdminApi, http(apiCanary.posts.read));
router.put('/posts/:id', mw.authAdminApi, http(apiCanary.posts.edit));
router.del('/posts/:id', mw.authAdminApi, http(apiCanary.posts.destroy));
// ## Pages
router.get('/pages', mw.authAdminApi, http(apiCanary.pages.browse));
router.post('/pages', mw.authAdminApi, http(apiCanary.pages.add));
router.get('/pages/:id', mw.authAdminApi, http(apiCanary.pages.read));
router.get('/pages/slug/:slug', mw.authAdminApi, http(apiCanary.pages.read));
router.put('/pages/:id', mw.authAdminApi, http(apiCanary.pages.edit));
router.del('/pages/:id', mw.authAdminApi, http(apiCanary.pages.destroy));
// # Integrations
router.get('/integrations', mw.authAdminApi, http(apiCanary.integrations.browse));
router.get('/integrations/:id', mw.authAdminApi, http(apiCanary.integrations.read));
router.post('/integrations', mw.authAdminApi, http(apiCanary.integrations.add));
router.put('/integrations/:id', mw.authAdminApi, http(apiCanary.integrations.edit));
router.del('/integrations/:id', mw.authAdminApi, http(apiCanary.integrations.destroy));
// ## Schedules
router.put('/schedules/:resource/:id', mw.authAdminApi, http(apiCanary.schedules.publish));
// ## Settings
router.get('/settings/routes/yaml', mw.authAdminApi, http(apiCanary.settings.download));
router.post('/settings/routes/yaml',
mw.authAdminApi,
upload.single('routes'),
shared.middlewares.validation.upload({type: 'routes'}),
http(apiCanary.settings.upload)
);
router.get('/settings', mw.authAdminApi, http(apiCanary.settings.browse));
router.get('/settings/:key', mw.authAdminApi, http(apiCanary.settings.read));
router.put('/settings', mw.authAdminApi, http(apiCanary.settings.edit));
// ## Users
router.get('/users', mw.authAdminApi, http(apiCanary.users.browse));
router.get('/users/:id', mw.authAdminApi, http(apiCanary.users.read));
router.get('/users/slug/:slug', mw.authAdminApi, http(apiCanary.users.read));
// NOTE: We don't expose any email addresses via the public api.
router.get('/users/email/:email', mw.authAdminApi, http(apiCanary.users.read));
router.put('/users/password', mw.authAdminApi, http(apiCanary.users.changePassword));
router.put('/users/owner', mw.authAdminApi, http(apiCanary.users.transferOwnership));
router.put('/users/:id', mw.authAdminApi, http(apiCanary.users.edit));
router.del('/users/:id', mw.authAdminApi, http(apiCanary.users.destroy));
// ## Tags
router.get('/tags', mw.authAdminApi, http(apiCanary.tags.browse));
router.get('/tags/:id', mw.authAdminApi, http(apiCanary.tags.read));
router.get('/tags/slug/:slug', mw.authAdminApi, http(apiCanary.tags.read));
router.post('/tags', mw.authAdminApi, http(apiCanary.tags.add));
router.put('/tags/:id', mw.authAdminApi, http(apiCanary.tags.edit));
router.del('/tags/:id', mw.authAdminApi, http(apiCanary.tags.destroy));
// ## Subscribers
router.get('/subscribers', shared.middlewares.labs.subscribers, mw.authAdminApi, http(apiCanary.subscribers.browse));
router.get('/subscribers/csv', shared.middlewares.labs.subscribers, mw.authAdminApi, http(apiCanary.subscribers.exportCSV));
router.post('/subscribers/csv',
shared.middlewares.labs.subscribers,
mw.authAdminApi,
upload.single('subscribersfile'),
shared.middlewares.validation.upload({type: 'subscribers'}),
http(apiCanary.subscribers.importCSV)
);
router.get('/subscribers/:id', shared.middlewares.labs.subscribers, mw.authAdminApi, http(apiCanary.subscribers.read));
router.get('/subscribers/email/:email', shared.middlewares.labs.subscribers, mw.authAdminApi, http(apiCanary.subscribers.read));
router.post('/subscribers', shared.middlewares.labs.subscribers, mw.authAdminApi, http(apiCanary.subscribers.add));
router.put('/subscribers/:id', shared.middlewares.labs.subscribers, mw.authAdminApi, http(apiCanary.subscribers.edit));
router.del('/subscribers/:id', shared.middlewares.labs.subscribers, mw.authAdminApi, http(apiCanary.subscribers.destroy));
router.del('/subscribers/email/:email', shared.middlewares.labs.subscribers, mw.authAdminApi, http(apiCanary.subscribers.destroy));
// ## Members
router.get('/members', shared.middlewares.labs.members, mw.authAdminApi, http(apiCanary.members.browse));
router.get('/members/:id', shared.middlewares.labs.members, mw.authAdminApi, http(apiCanary.members.read));
router.del('/members/:id', shared.middlewares.labs.members, mw.authAdminApi, http(apiCanary.members.destroy));
// ## Roles
router.get('/roles/', mw.authAdminApi, http(apiCanary.roles.browse));
// ## Clients
router.get('/clients/slug/:slug', api.http(api.clients.read));
// ## Slugs
router.get('/slugs/:type/:name', mw.authAdminApi, http(apiCanary.slugs.generate));
// ## Themes
router.get('/themes/', mw.authAdminApi, http(apiCanary.themes.browse));
router.get('/themes/:name/download',
mw.authAdminApi,
http(apiCanary.themes.download)
);
router.post('/themes/upload',
mw.authAdminApi,
upload.single('file'),
shared.middlewares.validation.upload({type: 'themes'}),
http(apiCanary.themes.upload)
);
router.put('/themes/:name/activate',
mw.authAdminApi,
http(apiCanary.themes.activate)
);
router.del('/themes/:name',
mw.authAdminApi,
http(apiCanary.themes.destroy)
);
// ## Notifications
router.get('/notifications', mw.authAdminApi, http(apiCanary.notifications.browse));
router.post('/notifications', mw.authAdminApi, http(apiCanary.notifications.add));
router.del('/notifications/:notification_id', mw.authAdminApi, http(apiCanary.notifications.destroy));
// ## DB
router.get('/db', mw.authAdminApi, http(apiCanary.db.exportContent));
router.post('/db',
mw.authAdminApi,
upload.single('importfile'),
shared.middlewares.validation.upload({type: 'db'}),
http(apiCanary.db.importContent)
);
router.del('/db', mw.authAdminApi, http(apiCanary.db.deleteAllContent));
router.post('/db/backup',
mw.authAdminApi,
http(apiCanary.db.backupContent)
);
// ## Mail
router.post('/mail', mw.authAdminApi, http(apiCanary.mail.send));
router.post('/mail/test', mw.authAdminApi, http(apiCanary.mail.sendTest));
// ## Slack
router.post('/slack/test', mw.authAdminApi, http(apiCanary.slack.sendTest));
// ## Sessions
router.get('/session', mw.authAdminApi, api.http(apiCanary.session.read));
// We don't need auth when creating a new session (logging in)
router.post('/session',
shared.middlewares.brute.globalBlock,
shared.middlewares.brute.userLogin,
api.http(apiCanary.session.add)
);
router.del('/session', mw.authAdminApi, api.http(apiCanary.session.delete));
// ## Authentication
router.post('/authentication/passwordreset',
shared.middlewares.brute.globalReset,
shared.middlewares.brute.userReset,
http(apiCanary.authentication.generateResetToken)
);
router.put('/authentication/passwordreset', shared.middlewares.brute.globalBlock, http(apiCanary.authentication.resetPassword));
router.post('/authentication/invitation', http(apiCanary.authentication.acceptInvitation));
router.get('/authentication/invitation', http(apiCanary.authentication.isInvitation));
router.post('/authentication/setup', http(apiCanary.authentication.setup));
router.put('/authentication/setup', mw.authAdminApi, http(apiCanary.authentication.updateSetup));
router.get('/authentication/setup', http(apiCanary.authentication.isSetup));
// ## Images
router.post('/images/upload',
mw.authAdminApi,
upload.single('file'),
shared.middlewares.validation.upload({type: 'images'}),
shared.middlewares.image.normalize,
http(apiCanary.images.upload)
);
// ## Invites
router.get('/invites', mw.authAdminApi, http(apiCanary.invites.browse));
router.get('/invites/:id', mw.authAdminApi, http(apiCanary.invites.read));
router.post('/invites', mw.authAdminApi, http(apiCanary.invites.add));
router.del('/invites/:id', mw.authAdminApi, http(apiCanary.invites.destroy));
// ## Redirects (JSON based)
router.get('/redirects/json', mw.authAdminApi, http(apiCanary.redirects.download));
router.post('/redirects/json',
mw.authAdminApi,
upload.single('redirects'),
shared.middlewares.validation.upload({type: 'redirects'}),
http(apiCanary.redirects.upload)
);
// ## Webhooks (RESTHooks)
router.post('/webhooks', mw.authAdminApi, http(apiCanary.webhooks.add));
router.put('/webhooks/:id', mw.authAdminApi, http(apiCanary.webhooks.edit));
router.del('/webhooks/:id', mw.authAdminApi, http(apiCanary.webhooks.destroy));
// ## Oembed (fetch response from oembed provider)
router.get('/oembed', mw.authAdminApi, http(apiCanary.oembed.read));
// ## Actions
router.get('/actions/:type/:id', mw.authAdminApi, http(apiCanary.actions.browse));
return router;
};

View file

@ -0,0 +1,36 @@
const debug = require('ghost-ignition').debug('web:api:canary:content:app');
const boolParser = require('express-query-boolean');
const bodyParser = require('body-parser');
const express = require('express');
const shared = require('../../../shared');
const routes = require('./routes');
module.exports = function setupApiApp() {
debug('Content API canary setup start');
const apiApp = express();
// API middleware
// @NOTE: req.body is undefined if we don't use this parser, this can trouble if components rely on req.body being present
apiApp.use(bodyParser.json({limit: '1mb'}));
// Query parsing
apiApp.use(boolParser());
// send 503 json response in case of maintenance
apiApp.use(shared.middlewares.maintenance);
// API shouldn't be cached
apiApp.use(shared.middlewares.cacheControl('private'));
// Routing
apiApp.use(routes());
// API error handling
apiApp.use(shared.middlewares.errorHandler.resourceNotFound);
apiApp.use(shared.middlewares.errorHandler.handleJSONResponse);
debug('Content API canary setup end');
return apiApp;
};

View file

@ -0,0 +1,23 @@
const cors = require('cors');
const auth = require('../../../../services/auth');
const shared = require('../../../shared');
/**
* Auth Middleware Packages
*
* IMPORTANT
* - cors middleware MUST happen before pretty urls, because otherwise cors header can get lost on redirect
* - url redirects MUST happen after cors, otherwise cors header can get lost on redirect
*/
/**
* Authentication for public endpoints
*/
module.exports.authenticatePublic = [
shared.middlewares.brute.contentApiKey,
auth.authenticate.authenticateContentApi,
auth.authorize.authorizeContentApi,
cors(),
shared.middlewares.urlRedirects.adminRedirect,
shared.middlewares.prettyUrls
];

View file

@ -0,0 +1,37 @@
const express = require('express');
const cors = require('cors');
const apiCanary = require('../../../../api/canary');
const mw = require('./middleware');
module.exports = function apiRoutes() {
const router = express.Router();
router.use(cors());
const http = apiCanary.http;
// ## Posts
router.get('/posts', mw.authenticatePublic, http(apiCanary.postsPublic.browse));
router.get('/posts/:id', mw.authenticatePublic, http(apiCanary.postsPublic.read));
router.get('/posts/slug/:slug', mw.authenticatePublic, http(apiCanary.postsPublic.read));
// ## Pages
router.get('/pages', mw.authenticatePublic, http(apiCanary.pagesPublic.browse));
router.get('/pages/:id', mw.authenticatePublic, http(apiCanary.pagesPublic.read));
router.get('/pages/slug/:slug', mw.authenticatePublic, http(apiCanary.pagesPublic.read));
// ## Users
router.get('/authors', mw.authenticatePublic, http(apiCanary.authorsPublic.browse));
router.get('/authors/:id', mw.authenticatePublic, http(apiCanary.authorsPublic.read));
router.get('/authors/slug/:slug', mw.authenticatePublic, http(apiCanary.authorsPublic.read));
// ## Tags
router.get('/tags', mw.authenticatePublic, http(apiCanary.tagsPublic.browse));
router.get('/tags/:id', mw.authenticatePublic, http(apiCanary.tagsPublic.read));
router.get('/tags/slug/:slug', mw.authenticatePublic, http(apiCanary.tagsPublic.read));
// ## Settings
router.get('/settings', mw.authenticatePublic, http(apiCanary.publicSettings.browse));
return router;
};

View file

@ -0,0 +1,28 @@
const debug = require('ghost-ignition').debug('web:canary:members:app');
const express = require('express');
const membersService = require('../../../../services/members');
const labs = require('../../../shared/middlewares/labs');
const shared = require('../../../shared');
module.exports = function setupMembersApiApp() {
debug('Members API canary setup start');
const apiApp = express();
// Entire app is behind labs flag
apiApp.use(labs.members);
// Set up the auth pages
apiApp.use('/static/auth', membersService.authPages);
// Set up the api endpoints and the gateway
// NOTE: this is wrapped in a function to ensure we always go via the getter
apiApp.use((req, res, next) => membersService.api(req, res, next));
// API error handling
apiApp.use(shared.middlewares.errorHandler.resourceNotFound);
apiApp.use(shared.middlewares.errorHandler.handleJSONResponseV2);
debug('Members API canary setup end');
return apiApp;
};

View file

@ -12,6 +12,9 @@ module.exports = function setupApiApp() {
apiApp.use(urlUtils.getVersionPath({version: 'v2', type: 'content'}), require('./v2/content/app')());
apiApp.use(urlUtils.getVersionPath({version: 'v2', type: 'admin'}), require('./v2/admin/app')());
apiApp.use(urlUtils.getVersionPath({version: 'v2', type: 'members'}), require('./v2/members/app')());
apiApp.use(urlUtils.getVersionPath({version: 'canary', type: 'content'}), require('./canary/content/app')());
apiApp.use(urlUtils.getVersionPath({version: 'canary', type: 'admin'}), require('./canary/admin/app')());
apiApp.use(urlUtils.getVersionPath({version: 'canary', type: 'members'}), require('./canary/members/app')());
// Error handling for requests to non-existent API versions
apiApp.use(errorHandler.resourceNotFound);