mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-15 11:34:24 +03:00
4056a6da4a
* 🎨 one token endpoint refs #7562 - delete /authentication/ghost - Ghost-Admin will use /authentication/token for all use cases (password, refresh token and ghost.org authorization code) - add new grant_type `authorization_code` * 🎨 update comment description and remove spamPrevention.resetCounter
254 lines
10 KiB
JavaScript
254 lines
10 KiB
JavaScript
// # API routes
|
|
var debug = require('debug')('ghost:api'),
|
|
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
|
|
checkSSL = require('../middleware/check-ssl'),
|
|
prettyURLs = require('../middleware/pretty-urls'),
|
|
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/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() {
|
|
debug('API setup start');
|
|
var apiApp = express();
|
|
|
|
// @TODO finish refactoring this away.
|
|
apiApp.use(function setIsAdmin(req, res, next) {
|
|
// Api === isAdmin for the purposes of the forceAdminSSL config option.
|
|
res.isAdmin = true;
|
|
next();
|
|
});
|
|
|
|
// 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);
|
|
|
|
// Force SSL if required
|
|
// must happen AFTER asset loading and BEFORE routing
|
|
apiApp.use(checkSSL);
|
|
|
|
// Add in all trailing slashes & remove uppercase
|
|
// must happen AFTER asset loading and BEFORE routing
|
|
apiApp.use(prettyURLs);
|
|
|
|
// 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
|
|
apiApp.use(errorHandler.resourceNotFound);
|
|
apiApp.use(errorHandler.handleJSONResponse);
|
|
|
|
debug('API setup end');
|
|
|
|
return apiApp;
|
|
};
|