0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00
ghost/core/server/middleware/index.js
Hannah Wolfe 59e2694acf Misc Middleware cleanup (#7526)
* 💄 Combine slashes & uncapitalise middleware

- these bits of middleware belong together
- ideally they should be optimised

* 🎨 Move ghostLocals out of themeHandler

GhostLocals sets several important values which are needed for every part of the application,
admin, api and theme. Therefore, it doesn't make sense for it to be bundled in the themeHandler.

* 🐛 Fix the uncapitalise middleware

- Updated to make correct use of req.baseUrl, req.path, req.url & req.originalUrl
- Updated the tests to actually cover our weird cases

* 🎨 Move ghostVersion logic out of config

* 💄 Group static / asset-related middleware together

* 🔥 Remove /shared/ asset handling

- The 5 files which are located in `/shared/` are all handled by individual calls to `serveSharedFile`
- Therefore this code is redundant
2016-10-10 21:14:32 +02:00

216 lines
7.1 KiB
JavaScript

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'),
config = require('../config'),
storage = require('../storage'),
logging = require('../logging'),
errors = require('../errors'),
i18n = require('../i18n'),
utils = require('../utils'),
sitemapHandler = require('../data/xml/sitemap/handler'),
cacheControl = require('./cache-control'),
checkSSL = require('./check-ssl'),
decideIsAdmin = require('./decide-is-admin'),
redirectToSetup = require('./redirect-to-setup'),
ghostLocals = require('./ghost-locals'),
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;
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) {
debug('Middleware start');
var adminApp = express(),
adminHbs = hbs.create();
// ##Configuration
// enabled gzip compression by default
if (config.get('server').compress !== false) {
blogApp.use(compress());
}
// ## View engine
// set the view engine
blogApp.set('view engine', 'hbs');
// Create a hbs instance for admin and init view engine
adminApp.set('view engine', 'hbs');
adminApp.engine('hbs', adminHbs.express3({}));
debug('Views done');
// Load helpers
helpers.loadCoreHelpers(adminHbs);
debug('Helpers done');
// Make sure 'req.secure' is valid for proxied requests
// (X-Forwarded-Proto header will be checked, if present)
blogApp.enable('trust proxy');
/**
* request logging
*/
blogApp.use(function expressLogging(req, res, next) {
res.once('finish', function () {
logging.request({req: req, res: res, err: req.err});
});
next();
});
if (debug.enabled) {
// debug keeps a timer, so this is super useful
blogApp.use((function () {
var reqDebug = require('debug')('ghost:req');
return function debugLog(req, res, next) {
reqDebug('Request', req.originalUrl);
next();
};
})());
}
// Preload link headers
if (config.get('preloadHeaders')) {
blogApp.use(netjet({
cache: {
max: config.get('preloadHeaders')
}
}));
}
// This sets global res.locals which are needed everywhere
blogApp.use(ghostLocals);
// First determine whether we're serving admin or theme content
// @TODO refactor this horror away!
blogApp.use(decideIsAdmin);
// Theme middleware
// rightly or wrongly currently comes before theme static assets
// @TODO revisit where and when these are needed
blogApp.use(themeHandler.updateActiveTheme);
blogApp.use(themeHandler.configHbsForContext);
debug('Themes done');
// Static content/assets
// Favicon
blogApp.use(serveSharedFile('favicon.ico', 'image/x-icon', utils.ONE_DAY_S));
// Ghost-Url
blogApp.use(serveSharedFile('shared/ghost-url.js', 'application/javascript', utils.ONE_HOUR_S));
blogApp.use(serveSharedFile('shared/ghost-url.min.js', 'application/javascript', utils.ONE_HOUR_S));
// Serve sitemap.xsl file
blogApp.use(serveSharedFile('sitemap.xsl', 'text/xsl', utils.ONE_DAY_S));
// Serve robots.txt if not found in theme
blogApp.use(serveSharedFile('robots.txt', 'text/plain', utils.ONE_HOUR_S));
// Serve blog images using the storage adapter
blogApp.use('/content/images', storage.getStorage().serve());
// Admin assets
// Admin only config
blogApp.use('/ghost/assets', serveStatic(
config.get('paths').clientAssets,
{maxAge: utils.ONE_YEAR_MS}
));
// Theme static assets/files
blogApp.use(staticTheme());
debug('Static content done');
// Force SSL
// must happen AFTER asset loading and BEFORE routing
blogApp.use(checkSSL);
adminApp.set('views', config.get('paths').adminViews);
// setup middleware for internal apps
// @TODO: refactor this to be a proper app middleware hook for internal & external apps
config.get('internalApps').forEach(function (appName) {
var app = require(path.join(config.get('paths').internalAppPath, appName));
if (app.hasOwnProperty('setupMiddleware')) {
app.setupMiddleware(blogApp);
}
});
// site map - this should probably be refactored to be an internal app
sitemapHandler(blogApp);
debug('Internal apps done');
// Add in all trailing slashes & remove uppercase
// 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));
// Mount admin express app to /ghost and set up routes
adminApp.use(redirectToSetup);
adminApp.use(maintenance);
adminApp.use(routes.admin());
blogApp.use('/ghost', adminApp);
debug('Admin app & api done');
// send 503 error page in case of maintenance
blogApp.use(maintenance);
// Set up Frontend routes (including private blogging routes)
blogApp.use(routes.frontend());
// ### Error handlers
blogApp.use(function pageNotFound(req, res, next) {
next(new errors.NotFoundError({message: i18n.t('errors.errors.pageNotFound')}));
});
blogApp.use(errorHandler);
debug('Middleware end');
};
module.exports = setupMiddleware;
// Export middleware functions directly
module.exports.middleware = middleware;