diff --git a/src/api/web/index.js b/src/api/web/index.js index dedea9b23..5dda2d63a 100644 --- a/src/api/web/index.js +++ b/src/api/web/index.js @@ -8,7 +8,7 @@ import fs from 'fs'; import path from 'path'; import express from 'express'; -import { combineBaseUrl, getWebProtocol } from '../../lib/utils'; +import { combineBaseUrl, getWebProtocol, isHTTPProtocol } from '../../lib/utils'; import Search from '../../lib/search'; import { HEADERS, HTTP_STATUS, WEB_TITLE } from '../../lib/constants'; import loadPlugin from '../../lib/plugin-loader'; @@ -32,6 +32,17 @@ export function loadTheme(config) { } } +const sendFileCallback = next => err => { + if (!err) { + return; + } + if (err.status === HTTP_STATUS.NOT_FOUND) { + next(); + } else { + next(err); + } +}; + module.exports = function(config, auth, storage) { Search.configureStorage(storage); /* eslint new-cap:off */ @@ -43,21 +54,24 @@ module.exports = function(config, auth, storage) { const indexTemplate = path.join(themePath, 'index.html'); const template = fs.readFileSync(indexTemplate).toString(); + // Logo + let logoURI = _.get(config, 'web.logo') ? config.web.logo : ''; + if (logoURI && !isHTTPProtocol(logoURI)) { + // URI related to a local file + + // Note: `path.join` will break on Windows, because it transforms `/` to `\` + // Use POSIX version `path.posix.join` instead. + logoURI = path.posix.join('/-/static/', path.basename(logoURI)); + router.get(logoURI, function(req, res, next) { + res.sendFile(path.resolve(config.web.logo), sendFileCallback(next)); + }); + } + // Static router.get('/-/static/*', function(req, res, next) { const filename = req.params[0]; - const file = `${themePath}/${filename}`; - res.sendFile(file, function(err) { - if (!err) { - return; - } - if (err.status === HTTP_STATUS.NOT_FOUND) { - next(); - } else { - next(err); - } - }); + res.sendFile(file, sendFileCallback(next)); }); function renderHTML(req, res) { @@ -66,7 +80,7 @@ module.exports = function(config, auth, storage) { .replace(/ToReplaceByVerdaccio/g, base) .replace(/ToReplaceByVersion/g, pkgJSON.version) .replace(/ToReplaceByTitle/g, _.get(config, 'web.title') ? config.web.title : WEB_TITLE) - .replace(/ToReplaceByLogo/g, _.get(config, 'web.logo') ? config.web.logo : '') + .replace(/ToReplaceByLogo/g, logoURI) .replace(/ToReplaceByPrimaryColor/g, _.get(config, 'web.primary_color') ? config.web.primary_color : '') .replace(/ToReplaceByScope/g, _.get(config, 'web.scope') ? config.web.scope : ''); diff --git a/src/lib/utils.js b/src/lib/utils.js index 202007aa8..ab4614d08 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -547,3 +547,11 @@ export function formatAuthor(author: any) { return authorDetails; } + +/** + * Check if URI is starting with "http://", "https://" or "//" + * @param {string} uri + */ +export function isHTTPProtocol(uri: string): boolean { + return /^(https?:)?\/\//.test(uri); +} diff --git a/test/unit/api/utils.spec.js b/test/unit/api/utils.spec.js index a4b3737cd..f8810ba2a 100644 --- a/test/unit/api/utils.spec.js +++ b/test/unit/api/utils.spec.js @@ -14,7 +14,8 @@ import { getWebProtocol, getVersionFromTarball, sortByName, - formatAuthor + formatAuthor, + isHTTPProtocol, } from '../../../src/lib/utils'; import { DIST_TAGS, DEFAULT_USER } from '../../../src/lib/constants'; import Logger, { setup } from '../../../src/lib/logger'; @@ -332,6 +333,26 @@ describe('Utilities', () => { expect(url).toMatch('/-/static/logo.png'); }); + + test('should check HTTP protocol correctly', () => { + expect(isHTTPProtocol('http://domain.com/-/static/logo.png')).toBeTruthy(); + expect(isHTTPProtocol('https://www.domain.com/-/static/logo.png')).toBeTruthy(); + expect(isHTTPProtocol('//domain.com/-/static/logo.png')).toBeTruthy(); + expect(isHTTPProtocol('file:///home/user/logo.png')).toBeFalsy(); + expect(isHTTPProtocol('file:///F:/home/user/logo.png')).toBeFalsy(); + // Note that uses ftp protocol in src was deprecated in modern browsers + expect(isHTTPProtocol('ftp://1.2.3.4/home/user/logo.png')).toBeFalsy(); + expect(isHTTPProtocol('./logo.png')).toBeFalsy(); + expect(isHTTPProtocol('.\\logo.png')).toBeFalsy(); + expect(isHTTPProtocol('../logo.png')).toBeFalsy(); + expect(isHTTPProtocol('..\\logo.png')).toBeFalsy(); + expect(isHTTPProtocol('../../static/logo.png')).toBeFalsy(); + expect(isHTTPProtocol('..\\..\\static\\logo.png')).toBeFalsy(); + expect(isHTTPProtocol('logo.png')).toBeFalsy(); + expect(isHTTPProtocol('.logo.png')).toBeFalsy(); + expect(isHTTPProtocol('/static/logo.png')).toBeFalsy(); + expect(isHTTPProtocol('F:\\static\\logo.png')).toBeFalsy(); + }); }); describe('User utilities', () => {