Skip to content

Commit ad7cad3

Browse files
committed
Changed servePublicFile from middleware to router
ref https://linear.app/ghost/issue/BER-3095 - Extracted the public files code from the middleware to the router. This avoids route comparison on every requests Ghost receives.
1 parent 236f908 commit ad7cad3

File tree

5 files changed

+75
-31
lines changed

5 files changed

+75
-31
lines changed

ghost/core/content/themes/source

ghost/core/core/frontend/web/middleware/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@ module.exports = {
44
frontendCaching: require('./frontend-caching'),
55
handleImageSizes: require('./handle-image-sizes'),
66
redirectGhostToAdmin: require('./redirect-ghost-to-admin'),
7-
servePublicFile: require('./serve-public-file'),
87
staticTheme: require('./static-theme')
98
};

ghost/core/core/frontend/web/middleware/serve-public-file.js renamed to ghost/core/core/frontend/web/routers/serve-public-file.js

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ const errors = require('@tryghost/errors');
55
const config = require('../../../shared/config');
66
const urlUtils = require('../../../shared/url-utils');
77
const tpl = require('@tryghost/tpl');
8+
const {cardAssets} = require('../../services/assets-minification');
9+
const themeEngine = require('../../services/theme-engine');
10+
const settingsCache = require('../../../shared/settings-cache');
811

912
const messages = {
1013
imageNotFound: 'Image not found',
@@ -99,7 +102,6 @@ function createPublicFileMiddleware(location, file, mime, maxAge, options = {})
99102
};
100103
}
101104

102-
// ### servePublicFile Middleware
103105
// Handles requests to robots.txt and favicon.ico (and caches them)
104106
function servePublicFile(location, file, type, maxAge, options = {}) {
105107
const publicFileMiddleware = createPublicFileMiddleware(location, file, type, maxAge, options);
@@ -113,6 +115,72 @@ function servePublicFile(location, file, type, maxAge, options = {}) {
113115
};
114116
}
115117

116-
module.exports = servePublicFile;
118+
// Handles requests to public static files served by Ghost
119+
function servePublicFiles(siteApp) {
120+
// Serve sitemap.xsl file if not found in theme
121+
const defaultSitemapXslMiddleware = createPublicFileMiddleware('static', 'sitemap.xsl', 'text/xsl', config.get('caching:sitemapXSL:maxAge'));
122+
siteApp.get('/sitemap.xsl', function serveSitemapXsl(req, res, next) {
123+
const activeTheme = themeEngine.getActive();
124+
if (!activeTheme) {
125+
return defaultSitemapXslMiddleware(req, res, next);
126+
}
127+
128+
const themeSitemapXslPath = path.join(activeTheme.path, 'sitemap.xsl');
129+
fs.access(themeSitemapXslPath, fs.constants.R_OK, (err) => {
130+
if (err) {
131+
// Theme doesn't have sitemap.xsl, serve default
132+
return defaultSitemapXslMiddleware(req, res, next);
133+
}
134+
// Theme has sitemap.xsl, let staticTheme middleware handle it
135+
return next();
136+
});
137+
});
138+
139+
// Serve stylesheets for default templates
140+
siteApp.get('/public/ghost.css', createPublicFileMiddleware('static', 'public/ghost.css', 'text/css', config.get('caching:publicAssets:maxAge')));
141+
siteApp.get('/public/ghost.min.css', createPublicFileMiddleware('static', 'public/ghost.min.css', 'text/css', config.get('caching:publicAssets:maxAge')));
142+
143+
// Traffic analytics tracking script
144+
siteApp.get('/public/ghost-stats.min.js', createPublicFileMiddleware('static', 'public/ghost-stats.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
145+
146+
// Card assets (built on the fly)
147+
siteApp.get('/public/cards.min.css', cardAssets.serveMiddleware(), createPublicFileMiddleware('built', 'public/cards.min.css', 'text/css', config.get('caching:publicAssets:maxAge')));
148+
siteApp.get('/public/cards.min.js', cardAssets.serveMiddleware(), createPublicFileMiddleware('built', 'public/cards.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
149+
150+
// Comment counts
151+
siteApp.get('/public/comment-counts.min.js', createPublicFileMiddleware('static', 'public/comment-counts.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
152+
153+
// Member attribution
154+
siteApp.get('/public/member-attribution.min.js', createPublicFileMiddleware('static', 'public/member-attribution.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
155+
156+
// Recommendations well-known
157+
siteApp.get('/.well-known/recommendations.json', createPublicFileMiddleware('built', '.well-known/recommendations.json', 'application/json', config.get('caching:publicAssets:maxAge'), {disableServerCache: true}));
158+
159+
// Serve robots.txt if not found in theme (and blog is not private)
160+
const defaultRobotsTxtMiddleware = createPublicFileMiddleware('static', 'robots.txt', 'text/plain', config.get('caching:robotstxt:maxAge'));
161+
siteApp.get('/robots.txt', function serveRobotsTxt(req, res, next) {
162+
// If private blogging is enabled, let filterPrivateRoutes handle it
163+
if (settingsCache.get('is_private')) {
164+
return next();
165+
}
166+
167+
const activeTheme = themeEngine.getActive();
168+
if (!activeTheme) {
169+
return defaultRobotsTxtMiddleware(req, res, next);
170+
}
171+
172+
const themeRobotsTxtPath = path.join(activeTheme.path, 'robots.txt');
173+
fs.access(themeRobotsTxtPath, fs.constants.R_OK, (err) => {
174+
if (err) {
175+
// Theme doesn't have robots.txt, serve default
176+
return defaultRobotsTxtMiddleware(req, res, next);
177+
}
178+
// Theme has robots.txt, let staticTheme middleware handle it
179+
return next();
180+
});
181+
});
182+
}
183+
184+
module.exports = servePublicFiles;
117185
module.exports.servePublicFile = servePublicFile;
118186
module.exports.createPublicFileMiddleware = createPublicFileMiddleware;

ghost/core/core/frontend/web/site.js

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ const storage = require('../../server/adapters/storage');
1010
const urlUtils = require('../../shared/url-utils');
1111
const sitemapHandler = require('../services/sitemap/handler');
1212
const serveFavicon = require('./routers/serve-favicon');
13+
const servePublicFiles = require('./routers/serve-public-file');
1314
const themeEngine = require('../services/theme-engine');
1415
const themeMiddleware = themeEngine.middleware;
1516
const membersService = require('../../server/services/members');
1617
const offersService = require('../../server/services/offers');
1718
const customRedirects = require('../../server/services/custom-redirects');
1819
const linkRedirects = require('../../server/services/link-redirection');
19-
const {cardAssets} = require('../services/assets-minification');
2020
const siteRoutes = require('./routes');
2121
const shared = require('../../server/web/shared');
2222
const errorHandler = require('@tryghost/mw-error-handler');
@@ -65,25 +65,8 @@ module.exports = function setupSiteApp(routerConfig) {
6565
// Favicon
6666
serveFavicon(siteApp);
6767

68-
// Serve sitemap.xsl file
69-
siteApp.use(mw.servePublicFile('static', 'sitemap.xsl', 'text/xsl', config.get('caching:sitemapXSL:maxAge')));
70-
71-
// Serve stylesheets for default templates
72-
siteApp.use(mw.servePublicFile('static', 'public/ghost.css', 'text/css', config.get('caching:publicAssets:maxAge')));
73-
siteApp.use(mw.servePublicFile('static', 'public/ghost.min.css', 'text/css', config.get('caching:publicAssets:maxAge')));
74-
75-
// Traffic analytics tracking script
76-
siteApp.use(mw.servePublicFile('static', 'public/ghost-stats.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
77-
78-
// Card assets
79-
siteApp.use(cardAssets.serveMiddleware(), mw.servePublicFile('built', 'public/cards.min.css', 'text/css', config.get('caching:publicAssets:maxAge')));
80-
siteApp.use(cardAssets.serveMiddleware(), mw.servePublicFile('built', 'public/cards.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
81-
82-
// Comment counts
83-
siteApp.use(mw.servePublicFile('static', 'public/comment-counts.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
84-
85-
// Member attribution
86-
siteApp.use(mw.servePublicFile('static', 'public/member-attribution.min.js', 'application/javascript', config.get('caching:publicAssets:maxAge')));
68+
// Public files (sitemap.xsl, stylesheets, scripts, etc.)
69+
servePublicFiles(siteApp);
8770

8871
// Serve site images using the storage adapter
8972
siteApp.use(STATIC_IMAGE_URL_PREFIX, mw.handleImageSizes, storage.getStorage('images').serve());
@@ -101,9 +84,6 @@ module.exports = function setupSiteApp(routerConfig) {
10184
}
10285
);
10386

104-
// Recommendations well-known
105-
siteApp.use(mw.servePublicFile('built', '.well-known/recommendations.json', 'application/json', config.get('caching:publicAssets:maxAge'), {disableServerCache: true}));
106-
10787
// setup middleware for internal apps
10888
// @TODO: refactor this to be a proper app middleware hook for internal apps
10989
config.get('apps:internal').forEach((appName) => {
@@ -117,9 +97,6 @@ module.exports = function setupSiteApp(routerConfig) {
11797
// Theme static assets/files
11898
siteApp.use(mw.staticTheme());
11999

120-
// Serve robots.txt if not found in theme
121-
siteApp.use(mw.servePublicFile('static', 'robots.txt', 'text/plain', config.get('caching:robotstxt:maxAge')));
122-
123100
debug('Static content done');
124101

125102
// site map - this should probably be refactored to be an internal app

ghost/core/test/unit/frontend/web/middleware/serve-public-file.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const should = require('should');
22
const sinon = require('sinon');
33
const fs = require('fs-extra');
4-
const servePublicFile = require('../../../../../core/frontend/web/middleware/serve-public-file');
4+
const {servePublicFile} = require('../../../../../core/frontend/web/routers/serve-public-file');
55

66
describe('servePublicFile', function () {
77
let res;

0 commit comments

Comments
 (0)