1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-11-08 23:25:51 +01:00

fix: missing logo on header (#3636)

This commit is contained in:
Juan Picado 2023-02-19 11:43:28 +01:00 committed by GitHub
parent 3dc0fd41f4
commit 4fc21146ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 128 additions and 40 deletions

@ -0,0 +1,8 @@
---
'@verdaccio/types': patch
'@verdaccio/middleware': patch
'@verdaccio/ui-theme': patch
'@verdaccio/web': patch
---
fix: missing logo on header

@ -128,7 +128,6 @@ export type TemplateUIOptions = {
base: string; base: string;
primaryColor: string; primaryColor: string;
version?: string; version?: string;
logoURI?: string;
flags: FlagsConfig; flags: FlagsConfig;
} & CommonWebConf; } & CommonWebConf;

@ -7,7 +7,7 @@ import { HTTP_STATUS } from '@verdaccio/core';
import { isURLhasValidProtocol } from '@verdaccio/url'; import { isURLhasValidProtocol } from '@verdaccio/url';
import { setSecurityWebHeaders } from './security'; import { setSecurityWebHeaders } from './security';
import renderHTML, { isHTTPProtocol } from './utils/renderHTML'; import renderHTML from './utils/renderHTML';
const debug = buildDebug('verdaccio:web:render'); const debug = buildDebug('verdaccio:web:render');
@ -31,23 +31,10 @@ export function renderWebMiddleware(config, tokenMiddleware, pluginOptions) {
if (typeof tokenMiddleware === 'function') { if (typeof tokenMiddleware === 'function') {
router.use(tokenMiddleware); router.use(tokenMiddleware);
} }
router.use(setSecurityWebHeaders); router.use(setSecurityWebHeaders);
// Logo // any match within the static is routed to the file system
let logoURI = config?.web?.logo ?? '';
if (logoURI && !isURLhasValidProtocol(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));
debug('render static');
});
}
// Static
router.get('/-/static/*', function (req, res, next) { router.get('/-/static/*', function (req, res, next) {
const filename = req.params[0]; const filename = req.params[0];
const file = `${staticPath}/${filename}`; const file = `${staticPath}/${filename}`;
@ -55,13 +42,13 @@ export function renderWebMiddleware(config, tokenMiddleware, pluginOptions) {
res.sendFile(file, sendFileCallback(next)); res.sendFile(file, sendFileCallback(next));
}); });
// logo // check the origin of the logo
if (config?.web?.logo && !isHTTPProtocol(config?.web?.logo)) { if (config?.web?.logo && !isURLhasValidProtocol(config?.web?.logo)) {
// URI related to a local file // URI related to a local file
const absoluteLocalFile = path.posix.resolve(config.web.logo); const absoluteLocalFile = path.posix.resolve(config.web.logo);
debug('serve local logo %s', absoluteLocalFile); debug('serve local logo %s', absoluteLocalFile);
try { try {
// TODO: remove existsSync by async alternative // TODO: replace existsSync by async alternative
if ( if (
fs.existsSync(absoluteLocalFile) && fs.existsSync(absoluteLocalFile) &&
typeof fs.accessSync(absoluteLocalFile, fs.constants.R_OK) === 'undefined' typeof fs.accessSync(absoluteLocalFile, fs.constants.R_OK) === 'undefined'

@ -6,6 +6,7 @@ import { URL } from 'url';
import { WEB_TITLE } from '@verdaccio/config'; import { WEB_TITLE } from '@verdaccio/config';
import { HEADERS } from '@verdaccio/core'; import { HEADERS } from '@verdaccio/core';
import { TemplateUIOptions } from '@verdaccio/types'; import { TemplateUIOptions } from '@verdaccio/types';
import { isURLhasValidProtocol } from '@verdaccio/url';
import { getPublicUrl } from '@verdaccio/url'; import { getPublicUrl } from '@verdaccio/url';
import renderTemplate from './template'; import renderTemplate from './template';
@ -21,20 +22,12 @@ const defaultManifestFiles = {
ico: 'favicon.ico', ico: 'favicon.ico',
}; };
/**
* Check if URI is starting with "http://", "https://" or "//"
* @param {string} uri
*/
export function isHTTPProtocol(uri: string): boolean {
return /^(https?:)?\/\//.test(uri);
}
export function resolveLogo(config, req) { export function resolveLogo(config, req) {
const isLocalFile = config?.web?.logo && !isHTTPProtocol(config?.web?.logo); const isLocalFile = config?.web?.logo && !isURLhasValidProtocol(config?.web?.logo);
if (isLocalFile) { if (isLocalFile) {
return `${getPublicUrl(config?.url_prefix, req)}-/static/${path.basename(config?.web?.logo)}`; return `${getPublicUrl(config?.url_prefix, req)}-/static/${path.basename(config?.web?.logo)}`;
} else if (isHTTPProtocol(config?.web?.logo)) { } else if (isURLhasValidProtocol(config?.web?.logo)) {
return config?.web?.logo; return config?.web?.logo;
} else { } else {
return ''; return '';
@ -53,7 +46,7 @@ export default function renderHTML(config, manifest, manifestFiles, req, res) {
const title = config?.web?.title ?? WEB_TITLE; const title = config?.web?.title ?? WEB_TITLE;
const login = hasLogin(config); const login = hasLogin(config);
const scope = config?.web?.scope ?? ''; const scope = config?.web?.scope ?? '';
const logoURI = resolveLogo(config, req); const logo = resolveLogo(config, req);
const pkgManagers = config?.web?.pkgManagers ?? ['yarn', 'pnpm', 'npm']; const pkgManagers = config?.web?.pkgManagers ?? ['yarn', 'pnpm', 'npm'];
const version = config?.web?.version; const version = config?.web?.version;
const flags = { const flags = {
@ -94,7 +87,7 @@ export default function renderHTML(config, manifest, manifestFiles, req, res) {
base, base,
primaryColor, primaryColor,
version, version,
logoURI, logo,
flags, flags,
login, login,
pkgManagers, pkgManagers,

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

@ -0,0 +1,24 @@
web:
title: verdaccio web
login: true
scope: '@scope'
pkgManagers:
- pnpm
- yarn
showInfo: true
showSettings: true
showSearch: true
showFooter: true
showThemeSwitch: true
showDownloadTarball: true
showRaw: true
primary_color: '#ffffff'
logo: './test/config/dark-logo.png'
html_cache: false
url_prefix: /prefix
log: { type: stdout, format: pretty, level: trace }
flags:
changePassword: true

@ -8,6 +8,7 @@ auth:
web: web:
title: verdaccio title: verdaccio
login: false login: false
html_cache: false
publish: publish:
allow_offline: false allow_offline: false

@ -0,0 +1,24 @@
web:
title: verdaccio web
login: true
scope: '@scope'
pkgManagers:
- pnpm
- yarn
showInfo: true
showSettings: true
showSearch: true
showFooter: true
showThemeSwitch: true
showDownloadTarball: true
showRaw: true
primary_color: '#ffffff'
logo:
html_cache: false
url_prefix: /prefix
log: { type: stdout, format: pretty, level: trace }
flags:
changePassword: true

@ -13,7 +13,8 @@ web:
showDownloadTarball: true showDownloadTarball: true
showRaw: true showRaw: true
primary_color: '#ffffff' primary_color: '#ffffff'
logoURI: 'http://logo.org/logo.png' logo: 'http://logo.org/logo.png'
html_cache: false
url_prefix: /prefix url_prefix: /prefix

@ -0,0 +1,24 @@
web:
title: verdaccio web
login: true
scope: '@scope'
pkgManagers:
- pnpm
- yarn
showInfo: true
showSettings: true
showSearch: true
showFooter: true
showThemeSwitch: true
showDownloadTarball: true
showRaw: true
primary_color: '#ffffff'
logo: './does_not_exist/config/dark-logo.png'
html_cache: false
url_prefix: /prefix
log: { type: stdout, format: pretty, level: trace }
flags:
changePassword: true

@ -37,6 +37,10 @@ describe('test web server', () => {
return new JSDOM(response.text, { runScripts: 'dangerously' }); return new JSDOM(response.text, { runScripts: 'dangerously' });
}; };
const loadLogo = async (config = 'default-test.yaml', url) => {
return supertest(initializeServer(config)).get(url).expect(HTTP_STATUS.OK);
};
test('should match render set ui properties', async () => { test('should match render set ui properties', async () => {
const { const {
window: { __VERDACCIO_BASENAME_UI_OPTIONS }, window: { __VERDACCIO_BASENAME_UI_OPTIONS },
@ -56,7 +60,7 @@ describe('test web server', () => {
// FIXME: mock these values, avoid random // FIXME: mock these values, avoid random
// base: 'http://127.0.0.1:60864/prefix/', // base: 'http://127.0.0.1:60864/prefix/',
// version: '6.0.0-6-next.28', // version: '6.0.0-6-next.28',
logoURI: '', logo: 'http://logo.org/logo.png',
flags: { changePassword: true }, flags: { changePassword: true },
login: true, login: true,
pkgManagers: ['pnpm', 'yarn'], pkgManagers: ['pnpm', 'yarn'],
@ -67,6 +71,28 @@ describe('test web server', () => {
); );
}); });
test('should render logo as file', async () => {
const {
window: { __VERDACCIO_BASENAME_UI_OPTIONS },
} = await render('file-logo.yaml');
expect(__VERDACCIO_BASENAME_UI_OPTIONS.logo).toMatch('/prefix/-/static/dark-logo.png');
return loadLogo('file-logo.yaml', '/-/static/dark-logo.png');
});
test('should not render logo as absolute file is wrong', async () => {
const {
window: { __VERDACCIO_BASENAME_UI_OPTIONS },
} = await render('wrong-logo.yaml');
expect(__VERDACCIO_BASENAME_UI_OPTIONS.logo).toEqual('');
});
test('should render not render a logo', async () => {
const {
window: { __VERDACCIO_BASENAME_UI_OPTIONS },
} = await render('no-logo.yaml');
expect(__VERDACCIO_BASENAME_UI_OPTIONS.logo).toEqual('');
});
test.todo('should default title'); test.todo('should default title');
test.todo('should need html cache'); test.todo('should need html cache');
}); });

@ -109,9 +109,10 @@
"test": "cross-env TZ=UTC jest --config ./jest/jest.config.js", "test": "cross-env TZ=UTC jest --config ./jest/jest.config.js",
"test:update-snapshot": "yarn run test -- -u", "test:update-snapshot": "yarn run test -- -u",
"lint": "pnpm lint:js && pnpm lint:css", "lint": "pnpm lint:js && pnpm lint:css",
"clean": "rimraf ./static",
"lint:css": "yarn stylelint \"src/**/styles.ts\"", "lint:css": "yarn stylelint \"src/**/styles.ts\"",
"verdaccio:server": "node tools/verdaccio.js", "verdaccio:server": "node tools/verdaccio.js",
"build": "webpack --config tools/webpack.prod.config.babel.js", "build": "pnpm clean && webpack --config tools/webpack.prod.config.babel.js",
"build:stats": "webpack --config tools/webpack.prod.config.babel.js --json > stats.json", "build:stats": "webpack --config tools/webpack.prod.config.babel.js --json > stats.json",
"build:size": "webpack --config tools/webpack.prod.config.babel.js --json | webpack-bundle-size-analyzer" "build:size": "webpack --config tools/webpack.prod.config.babel.js --json | webpack-bundle-size-analyzer"
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

@ -20,7 +20,7 @@ web:
showDownloadTarball: true showDownloadTarball: true
showRaw: true showRaw: true
primary_color: '#ffffff' primary_color: '#ffffff'
logoURI: 'http://logo.org/logo.png' logo: 'http://logo.org/logo.png'
flags: flags:
- something: false - something: false

8
pnpm-lock.yaml generated

@ -10248,7 +10248,7 @@ packages:
/axios/0.21.3_debug@4.3.4: /axios/0.21.3_debug@4.3.4:
resolution: {integrity: sha512-JtoZ3Ndke/+Iwt5n+BgSli/3idTvpt5OjKyoCmz4LX5+lPiY5l7C1colYezhlxThjNa/NhngCUWZSZFypIFuaA==} resolution: {integrity: sha512-JtoZ3Ndke/+Iwt5n+BgSli/3idTvpt5OjKyoCmz4LX5+lPiY5l7C1colYezhlxThjNa/NhngCUWZSZFypIFuaA==}
dependencies: dependencies:
follow-redirects: 1.14.9_debug@4.3.4 follow-redirects: 1.14.9_debug@4.3.3
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
dev: false dev: false
@ -10256,7 +10256,7 @@ packages:
/axios/0.25.0_debug@4.3.4: /axios/0.25.0_debug@4.3.4:
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==} resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
dependencies: dependencies:
follow-redirects: 1.14.9_debug@4.3.4 follow-redirects: 1.14.9_debug@4.3.3
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
@ -25406,7 +25406,7 @@ packages:
resolution: {integrity: sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==} resolution: {integrity: sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w==}
/truncate-utf8-bytes/1.0.2: /truncate-utf8-bytes/1.0.2:
resolution: {integrity: sha1-QFkjkJWS1W94pYGENLC3hInKXys=} resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==}
dependencies: dependencies:
utf8-byte-length: 1.0.4 utf8-byte-length: 1.0.4
dev: false dev: false
@ -26157,7 +26157,7 @@ packages:
dev: false dev: false
/utf8-byte-length/1.0.4: /utf8-byte-length/1.0.4:
resolution: {integrity: sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=} resolution: {integrity: sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==}
dev: false dev: false
/util-deprecate/1.0.2: /util-deprecate/1.0.2: