Ghost/core/server/web/site/middleware/serve-favicon.js
Hannah Wolfe bd597db829
Moved settings/cache to shared/settings-cache
- This is part of the quest to separate the frontend and server & get rid of all the places where there are cross-requires
- At the moment the settings cache is one big shared cache used by the frontend and server liberally
- This change doesn't really solve the fundamental problems, as we still depend on events, and requires from inside frontend
- However it allows us to control the misuse slightly better by getting rid of restricted requires and turning on that eslint ruleset
2021-06-30 15:49:10 +01:00

88 lines
3.2 KiB
JavaScript

const fs = require('fs-extra');
const path = require('path');
const crypto = require('crypto');
const config = require('../../../../shared/config');
const {blogIcon} = require('../../../lib/image');
const storage = require('../../../adapters/storage');
const urlUtils = require('../../../../shared/url-utils');
const settingsCache = require('../../../../shared/settings-cache');
let content;
const buildContentResponse = (ext, buf) => {
content = {
headers: {
'Content-Type': `image/${ext}`,
'Content-Length': buf.length,
ETag: `"${crypto.createHash('md5').update(buf, 'utf8').digest('hex')}"`,
'Cache-Control': `public, max-age=${config.get('caching:favicon:maxAge')}`
},
body: buf
};
return content;
};
// ### serveFavicon Middleware
// Handles requests to favicon.png and favicon.ico
function serveFavicon() {
let iconType;
let filePath;
return function serveFaviconMiddleware(req, res, next) {
if (req.path.match(/^\/favicon\.(ico|png)/i)) {
// CASE: favicon is default
// confusing: if you upload an icon, it's same logic as storing images
// we store as /content/images, because this is the url path images get requested via the browser
// we are using an express route to skip /content/images and the result is a image path
// based on config.getContentPath('images') + req.path
// in this case we don't use path rewrite, that's why we have to make it manually
filePath = blogIcon.getIconPath();
let originalExtension = path.extname(filePath).toLowerCase();
const requestedExtension = path.extname(req.path).toLowerCase();
// CASE: custom favicon exists, load it from local file storage
if (settingsCache.get('icon')) {
// depends on the uploaded icon extension
if (originalExtension !== requestedExtension) {
return res.redirect(302, urlUtils.urlFor({relativeUrl: `/favicon${originalExtension}`}));
}
storage.getStorage()
.read({path: filePath})
.then((buf) => {
iconType = blogIcon.getIconType();
content = buildContentResponse(iconType, buf);
res.writeHead(200, content.headers);
res.end(content.body);
})
.catch(next);
} else {
originalExtension = path.extname(filePath).toLowerCase();
// CASE: always redirect to .ico for default icon
if (originalExtension !== requestedExtension) {
return res.redirect(302, urlUtils.urlFor({relativeUrl: '/favicon.ico'}));
}
fs.readFile(filePath, (err, buf) => {
if (err) {
return next(err);
}
content = buildContentResponse('x-icon', buf);
res.writeHead(200, content.headers);
res.end(content.body);
});
}
} else {
return next();
}
};
}
module.exports = serveFavicon;