From 14159b31e1b4dd9ef87209db5d4ab75422241d53 Mon Sep 17 00:00:00 2001 From: Paola Morales Date: Wed, 3 Feb 2021 17:33:04 +0100 Subject: [PATCH] ref: refactor utils methods (#2076) * ref: Relocate utils functions to web pkg * ref: Refactor utils functions * ref: Relocate utils functions to api pkg * ref: Relocate utils functions to config pkg * ref: Relocate utils functions to middleware pkg * ref: Relocate utils functions to storage pkg * ref: relocate utils functions to proxy pkg * ref: relocate utils functions to middleware pkg * ref: refactor utils functions * Fix compilation errors --- packages/api/src/publish.ts | 10 +- packages/api/src/utils.ts | 10 ++ packages/auth/src/auth.ts | 3 +- packages/auth/src/utils.ts | 18 ++- packages/config/src/config-path.ts | 2 +- packages/config/src/config-utils.ts | 29 ++++ packages/middleware/src/middleware-utils.ts | 10 ++ packages/middleware/src/middleware.ts | 2 +- .../middleware/test/middleware-utils.spec.ts | 18 +++ packages/proxy/src/proxy-utils.ts | 41 +++++ packages/proxy/src/up-storage.ts | 3 +- .../test/proxy-utils.spec.ts} | 3 +- packages/store/src/local-storage.ts | 3 +- packages/store/src/storage-utils.ts | 20 ++- packages/store/test/storage-utils.spec.ts | 46 ++++++ packages/utils/src/auth-utils.ts | 15 +- packages/utils/src/crypto-utils.ts | 3 - packages/utils/src/utils.ts | 146 +----------------- packages/utils/test/tag.version.spec.ts | 46 ------ packages/utils/test/utils.spec.ts | 117 +++----------- packages/web/src/endpoint/package.ts | 4 +- packages/web/src/render-web.ts | 3 +- packages/web/src/web-utils2.ts | 39 +++++ packages/web/test/web-utils.spec.ts | 65 ++++++++ 24 files changed, 329 insertions(+), 327 deletions(-) create mode 100644 packages/config/src/config-utils.ts create mode 100644 packages/middleware/src/middleware-utils.ts create mode 100644 packages/middleware/test/middleware-utils.spec.ts create mode 100644 packages/proxy/src/proxy-utils.ts rename packages/{utils/test/parseInterval.spec.ts => proxy/test/proxy-utils.spec.ts} (89%) delete mode 100644 packages/utils/test/tag.version.spec.ts create mode 100644 packages/web/src/web-utils2.ts create mode 100644 packages/web/test/web-utils.spec.ts diff --git a/packages/api/src/publish.ts b/packages/api/src/publish.ts index 48cb23959..bc2da4d67 100644 --- a/packages/api/src/publish.ts +++ b/packages/api/src/publish.ts @@ -5,13 +5,7 @@ import { Router } from 'express'; import buildDebug from 'debug'; import { API_MESSAGE, HEADERS, DIST_TAGS, API_ERROR, HTTP_STATUS } from '@verdaccio/commons-api'; -import { - validateMetadata, - isObject, - ErrorCode, - hasDiffOneKey, - isRelatedToDeprecation, -} from '@verdaccio/utils'; +import { validateMetadata, isObject, ErrorCode, hasDiffOneKey } from '@verdaccio/utils'; import { media, expectJson, allow } from '@verdaccio/middleware'; import { notify } from '@verdaccio/hooks'; import { Config, Callback, MergeTags, Version, Package } from '@verdaccio/types'; @@ -21,7 +15,7 @@ import { IStorageHandler } from '@verdaccio/store'; import { $RequestExtend, $ResponseExtend, $NextFunctionVer } from '../types/custom'; import star from './star'; -import { isPublishablePackage } from './utils'; +import { isPublishablePackage, isRelatedToDeprecation } from './utils'; const debug = buildDebug('verdaccio:api:publish'); diff --git a/packages/api/src/utils.ts b/packages/api/src/utils.ts index 76ad05747..546c213ad 100644 --- a/packages/api/src/utils.ts +++ b/packages/api/src/utils.ts @@ -11,3 +11,13 @@ export function isPublishablePackage(pkg: Package): boolean { return _.includes(keys, 'versions'); } + +export function isRelatedToDeprecation(pkgInfo: Package): boolean { + const { versions } = pkgInfo; + for (const version in versions) { + if (Object.prototype.hasOwnProperty.call(versions[version], 'deprecated')) { + return true; + } + } + return false; +} diff --git a/packages/auth/src/auth.ts b/packages/auth/src/auth.ts index d37df62e4..7f362a2b2 100644 --- a/packages/auth/src/auth.ts +++ b/packages/auth/src/auth.ts @@ -29,7 +29,7 @@ import { PluginOptions, } from '@verdaccio/types'; -import { isNil, isFunction, convertPayloadToBase64 } from '@verdaccio/utils'; +import { isNil, isFunction } from '@verdaccio/utils'; import { getMatchedPackagesSpec, createAnonymousRemoteUser, @@ -43,6 +43,7 @@ import { parseAuthTokenHeader, isAuthHeaderValid, isAESLegacy, + convertPayloadToBase64, } from './utils'; import { signPayload } from './jwt-token'; diff --git a/packages/auth/src/utils.ts b/packages/auth/src/utils.ts index 19e4ab980..050a7eb7a 100644 --- a/packages/auth/src/utils.ts +++ b/packages/auth/src/utils.ts @@ -18,8 +18,9 @@ import { getConflict, getCode, } from '@verdaccio/commons-api'; +import { VerdaccioError } from '@verdaccio/commons-api'; + import { createAnonymousRemoteUser } from '@verdaccio/config'; -import { AllowAction, AllowActionCallback, convertPayloadToBase64 } from '@verdaccio/utils'; import { TokenEncryption, AESPayload } from './auth'; import { aesDecrypt } from './legacy-token'; import { verifyPayload } from './jwt-token'; @@ -34,6 +35,17 @@ export interface AuthTokenHeader { scheme: string; token: string; } +export type AllowActionCallbackResponse = boolean | undefined; +export type AllowActionCallback = ( + error: VerdaccioError | null, + allowed?: AllowActionCallbackResponse +) => void; + +export type AllowAction = ( + user: RemoteUser, + pkg: AuthPackageAllow, + callback: AllowActionCallback +) => void; /** * Split authentication header eg: Bearer [secret_token] @@ -229,3 +241,7 @@ export function handleSpecialUnpublish(logger): any { export function buildUser(name: string, password: string): string { return String(`${name}:${password}`); } + +export function convertPayloadToBase64(payload: string): Buffer { + return Buffer.from(payload, 'base64'); +} diff --git a/packages/config/src/config-path.ts b/packages/config/src/config-path.ts index be949cdeb..f87f2dded 100644 --- a/packages/config/src/config-path.ts +++ b/packages/config/src/config-path.ts @@ -5,8 +5,8 @@ import _ from 'lodash'; import mkdirp from 'mkdirp'; import buildDebug from 'debug'; -import { folderExists, fileExists } from '@verdaccio/utils'; import { CHARACTER_ENCODING } from '@verdaccio/commons-api'; +import { folderExists, fileExists } from './config-utils'; const CONFIG_FILE = 'config.yaml'; const XDG = 'xdg'; diff --git a/packages/config/src/config-utils.ts b/packages/config/src/config-utils.ts new file mode 100644 index 000000000..cf268bbd2 --- /dev/null +++ b/packages/config/src/config-utils.ts @@ -0,0 +1,29 @@ +import fs from 'fs'; + +/** + * Check whether the path already exist. + * @param {String} path + * @return {Boolean} + */ +export function folderExists(path: string): boolean { + try { + const stat = fs.statSync(path); + return stat.isDirectory(); + } catch (_) { + return false; + } +} + +/** + * Check whether the file already exist. + * @param {String} path + * @return {Boolean} + */ +export function fileExists(path: string): boolean { + try { + const stat = fs.statSync(path); + return stat.isFile(); + } catch (_) { + return false; + } +} diff --git a/packages/middleware/src/middleware-utils.ts b/packages/middleware/src/middleware-utils.ts new file mode 100644 index 000000000..ad9dc6960 --- /dev/null +++ b/packages/middleware/src/middleware-utils.ts @@ -0,0 +1,10 @@ +/** + * return package version from tarball name + * @param {String} name + * @returns {String} + */ +export function getVersionFromTarball(name: string): string | void { + // FIXME: we know the regex is valid, but we should improve this part as ts suggest + // @ts-ignore + return /.+-(\d.+)\.tgz/.test(name) ? name.match(/.+-(\d.+)\.tgz/)[1] : undefined; +} diff --git a/packages/middleware/src/middleware.ts b/packages/middleware/src/middleware.ts index abef3961d..1f9d52b1e 100644 --- a/packages/middleware/src/middleware.ts +++ b/packages/middleware/src/middleware.ts @@ -3,7 +3,6 @@ import _ from 'lodash'; import { validateName as utilValidateName, validatePackage as utilValidatePackage, - getVersionFromTarball, isObject, stringToMD5, ErrorCode, @@ -24,6 +23,7 @@ import { VerdaccioError, } from '@verdaccio/commons-api'; import { HttpError } from 'http-errors'; +import { getVersionFromTarball } from './middleware-utils'; export type $RequestExtend = Request & { remote_user?: RemoteUser; log: Logger }; export type $ResponseExtend = Response & { cookies?: any }; diff --git a/packages/middleware/test/middleware-utils.spec.ts b/packages/middleware/test/middleware-utils.spec.ts new file mode 100644 index 000000000..f6ff29f66 --- /dev/null +++ b/packages/middleware/test/middleware-utils.spec.ts @@ -0,0 +1,18 @@ +import { getVersionFromTarball } from '../src/middleware-utils'; + +describe('Utilities', () => { + describe('getVersionFromTarball', () => { + test('should get the right version', () => { + const simpleName = 'test-name-4.2.12.tgz'; + const complexName = 'test-5.6.4-beta.2.tgz'; + const otherComplexName = 'test-3.5.0-6.tgz'; + expect(getVersionFromTarball(simpleName)).toEqual('4.2.12'); + expect(getVersionFromTarball(complexName)).toEqual('5.6.4-beta.2'); + expect(getVersionFromTarball(otherComplexName)).toEqual('3.5.0-6'); + }); + + test("should don'n fall at incorrect tarball name", () => { + expect(getVersionFromTarball('incorrectName')).toBeUndefined(); + }); + }); +}); diff --git a/packages/proxy/src/proxy-utils.ts b/packages/proxy/src/proxy-utils.ts new file mode 100644 index 000000000..75e988077 --- /dev/null +++ b/packages/proxy/src/proxy-utils.ts @@ -0,0 +1,41 @@ +const parseIntervalTable = { + '': 1000, + ms: 1, + s: 1000, + m: 60 * 1000, + h: 60 * 60 * 1000, + d: 86400000, + w: 7 * 86400000, + M: 30 * 86400000, + y: 365 * 86400000, +}; + +/** + * Parse an internal string to number + * @param {*} interval + * @return {Number} + * @deprecated + */ +export function parseInterval(interval: any): number { + if (typeof interval === 'number') { + return interval * 1000; + } + let result = 0; + let last_suffix = Infinity; + interval.split(/\s+/).forEach(function (x): void { + if (!x) { + return; + } + const m = x.match(/^((0|[1-9][0-9]*)(\.[0-9]+)?)(ms|s|m|h|d|w|M|y|)$/); + if ( + !m || + parseIntervalTable[m[4]] >= last_suffix || + (m[4] === '' && last_suffix !== Infinity) + ) { + throw Error('invalid interval: ' + interval); + } + last_suffix = parseIntervalTable[m[4]]; + result += Number(m[1]) * parseIntervalTable[m[4]]; + }); + return result; +} diff --git a/packages/proxy/src/up-storage.ts b/packages/proxy/src/up-storage.ts index 567c9c0b6..a612f2adf 100644 --- a/packages/proxy/src/up-storage.ts +++ b/packages/proxy/src/up-storage.ts @@ -4,7 +4,7 @@ import URL, { UrlWithStringQuery } from 'url'; import JSONStream from 'JSONStream'; import _ from 'lodash'; import request from 'request'; -import { parseInterval, isObject, ErrorCode, buildToken } from '@verdaccio/utils'; +import { isObject, ErrorCode, buildToken } from '@verdaccio/utils'; import { ReadTarball } from '@verdaccio/streams'; import { ERROR_CODE, @@ -25,6 +25,7 @@ import { Package, IReadTarball, } from '@verdaccio/types'; +import { parseInterval } from './proxy-utils'; const LoggerApi = require('@verdaccio/logger'); const encode = function (thing): string { diff --git a/packages/utils/test/parseInterval.spec.ts b/packages/proxy/test/proxy-utils.spec.ts similarity index 89% rename from packages/utils/test/parseInterval.spec.ts rename to packages/proxy/test/proxy-utils.spec.ts index 9d93fab5b..820ef190a 100644 --- a/packages/utils/test/parseInterval.spec.ts +++ b/packages/proxy/test/proxy-utils.spec.ts @@ -1,11 +1,12 @@ import assert from 'assert'; -import { parseInterval } from '../src/utils'; +import { parseInterval } from '../src/proxy-utils'; describe('Parse interval', () => { function addTest(str, res) { test('parse ' + str, () => { if (res === null) { assert.throws(function () { + // eslint-disable-next-line no-console console.log(parseInterval(str)); }); } else { diff --git a/packages/store/src/local-storage.ts b/packages/store/src/local-storage.ts index b8dea47bc..10b4cf58a 100644 --- a/packages/store/src/local-storage.ts +++ b/packages/store/src/local-storage.ts @@ -3,7 +3,7 @@ import UrlNode from 'url'; import _ from 'lodash'; import buildDebug from 'debug'; -import { ErrorCode, isObject, getLatestVersion, tagVersion, validateName } from '@verdaccio/utils'; +import { ErrorCode, isObject, getLatestVersion, validateName } from '@verdaccio/utils'; import { API_ERROR, DIST_TAGS, HTTP_STATUS, SUPPORT_ERRORS, USERS } from '@verdaccio/commons-api'; import { createTarballHash } from '@verdaccio/utils'; import { loadPlugin } from '@verdaccio/loaders'; @@ -42,6 +42,7 @@ import { cleanUpReadme, normalizeContributors, STORAGE, + tagVersion, } from './storage-utils'; const debug = buildDebug('verdaccio:storage:local'); diff --git a/packages/store/src/storage-utils.ts b/packages/store/src/storage-utils.ts index 8fc241785..d59ed71f6 100644 --- a/packages/store/src/storage-utils.ts +++ b/packages/store/src/storage-utils.ts @@ -1,5 +1,5 @@ import _ from 'lodash'; - +import semver from 'semver'; import { ErrorCode, isObject, @@ -9,7 +9,7 @@ import { isNil, } from '@verdaccio/utils'; -import { Package, Version, Author } from '@verdaccio/types'; +import { Package, Version, Author, StringValue } from '@verdaccio/types'; import { API_ERROR, HTTP_STATUS, DIST_TAGS, USERS } from '@verdaccio/commons-api'; import { SearchInstance } from './search'; import { IStorage } from './storage'; @@ -254,3 +254,19 @@ export function prepareSearchPackage(data: Package, time: unknown): any { return pkg; } } + +/** + * Create a tag for a package + * @param {*} data + * @param {*} version + * @param {*} tag + * @return {Boolean} whether a package has been tagged + */ +export function tagVersion(data: Package, version: string, tag: StringValue): boolean { + if (tag && data[DIST_TAGS][tag] !== version && semver.parse(version, true)) { + // valid version - store + data[DIST_TAGS][tag] = version; + return true; + } + return false; +} diff --git a/packages/store/test/storage-utils.spec.ts b/packages/store/test/storage-utils.spec.ts index 81a840ec5..cffbb4c81 100644 --- a/packages/store/test/storage-utils.spec.ts +++ b/packages/store/test/storage-utils.spec.ts @@ -1,7 +1,9 @@ +import assert from 'assert'; import { Package } from '@verdaccio/types'; import { DIST_TAGS } from '@verdaccio/commons-api'; import { normalizePackage, mergeUplinkTimeIntoLocal, STORAGE } from '../src/storage-utils'; +import { tagVersion } from '../src/storage-utils'; import { readFile } from './fixtures/test.utils'; describe('Storage Utils', () => { @@ -125,4 +127,48 @@ describe('Storage Utils', () => { expect(Object.keys(mergedPkg)).toEqual(['modified', 'created', ...Object.keys(vGroup1)]); }); }); + + describe('tagVersion', () => { + test('add new one', () => { + let pkg = { + versions: {}, + 'dist-tags': {}, + }; + + // @ts-ignore + assert(tagVersion(pkg, '1.1.1', 'foo', {})); + assert.deepEqual(pkg, { + versions: {}, + 'dist-tags': { foo: '1.1.1' }, + }); + }); + + test('add (compat)', () => { + const x = { + versions: {}, + 'dist-tags': { foo: '1.1.0' }, + }; + + // @ts-ignore + assert(tagVersion(x, '1.1.1', 'foo')); + assert.deepEqual(x, { + versions: {}, + 'dist-tags': { foo: '1.1.1' }, + }); + }); + + test('add fresh tag', () => { + let x = { + versions: {}, + 'dist-tags': { foo: '1.1.0' }, + }; + + // @ts-ignore + assert(tagVersion(x, '1.1.1', 'foo')); + assert.deepEqual(x, { + versions: {}, + 'dist-tags': { foo: '1.1.1' }, + }); + }); + }); }); diff --git a/packages/utils/src/auth-utils.ts b/packages/utils/src/auth-utils.ts index 9e423c099..97fd42d34 100644 --- a/packages/utils/src/auth-utils.ts +++ b/packages/utils/src/auth-utils.ts @@ -1,5 +1,4 @@ -import { VerdaccioError, DEFAULT_MIN_LIMIT_PASSWORD } from '@verdaccio/commons-api'; -import { RemoteUser, AuthPackageAllow } from '@verdaccio/types'; +import { DEFAULT_MIN_LIMIT_PASSWORD } from '@verdaccio/commons-api'; export interface CookieSessionToken { expires: Date; @@ -12,18 +11,6 @@ export function validatePassword( return typeof password === 'string' && password.length >= minLength; } -export type AllowActionCallbackResponse = boolean | undefined; -export type AllowActionCallback = ( - error: VerdaccioError | null, - allowed?: AllowActionCallbackResponse -) => void; - -export type AllowAction = ( - user: RemoteUser, - pkg: AuthPackageAllow, - callback: AllowActionCallback -) => void; - export function createSessionToken(): CookieSessionToken { const tenHoursTime = 10 * 60 * 60 * 1000; diff --git a/packages/utils/src/crypto-utils.ts b/packages/utils/src/crypto-utils.ts index 561cb1ef4..2caca41b7 100644 --- a/packages/utils/src/crypto-utils.ts +++ b/packages/utils/src/crypto-utils.ts @@ -2,7 +2,6 @@ import { createHash, pseudoRandomBytes, Hash } from 'crypto'; export const defaultTarballHashAlgorithm = 'sha1'; -// podria moverse a storage donde se usa export function createTarballHash(): Hash { return createHash(defaultTarballHashAlgorithm); } @@ -14,12 +13,10 @@ export function createTarballHash(): Hash { * @param {Object} data * @return {String} */ -// se usa en api, middleware, web export function stringToMD5(data: Buffer | string): string { return createHash('md5').update(data).digest('hex'); } -// se usa en config export function generateRandomHexString(length = 8): string { return pseudoRandomBytes(length).toString('hex'); } diff --git a/packages/utils/src/utils.ts b/packages/utils/src/utils.ts index 2a94ba2b0..e231208d6 100644 --- a/packages/utils/src/utils.ts +++ b/packages/utils/src/utils.ts @@ -1,13 +1,10 @@ -import fs from 'fs'; import assert from 'assert'; import URL from 'url'; import { IncomingHttpHeaders } from 'http'; import _ from 'lodash'; import semver from 'semver'; import { Request } from 'express'; - -import { Package, Version, Author, StringValue } from '@verdaccio/types'; - +import { Package, Version, Author } from '@verdaccio/types'; import { HEADERS, DIST_TAGS, @@ -23,10 +20,6 @@ import { getCode, } from '@verdaccio/commons-api'; -export function convertPayloadToBase64(payload: string): Buffer { - return Buffer.from(payload, 'base64'); -} - /** * From normalize-package-data/lib/fixer.js * @param {*} name the package name @@ -186,22 +179,6 @@ export function getLocalRegistryTarballUri( return `${domainRegistry}/${encodeScopedUri(pkgName)}/-/${tarballName}`; } -/** - * Create a tag for a package - * @param {*} data - * @param {*} version - * @param {*} tag - * @return {Boolean} whether a package has been tagged - */ -export function tagVersion(data: Package, version: string, tag: StringValue): boolean { - if (tag && data[DIST_TAGS][tag] !== version && semver.parse(version, true)) { - // valid version - store - data[DIST_TAGS][tag] = version; - return true; - } - return false; -} - /** * Gets version from a package object taking into account semver weirdness. * @return {String} return the semantic version of a package @@ -283,48 +260,6 @@ export function normalizeDistTags(pkg: Package): void { } } -const parseIntervalTable = { - '': 1000, - ms: 1, - s: 1000, - m: 60 * 1000, - h: 60 * 60 * 1000, - d: 86400000, - w: 7 * 86400000, - M: 30 * 86400000, - y: 365 * 86400000, -}; - -/** - * Parse an internal string to number - * @param {*} interval - * @return {Number} - * @deprecated - */ -export function parseInterval(interval: any): number { - if (typeof interval === 'number') { - return interval * 1000; - } - let result = 0; - let last_suffix = Infinity; - interval.split(/\s+/).forEach(function (x): void { - if (!x) { - return; - } - const m = x.match(/^((0|[1-9][0-9]*)(\.[0-9]+)?)(ms|s|m|h|d|w|M|y|)$/); - if ( - !m || - parseIntervalTable[m[4]] >= last_suffix || - (m[4] === '' && last_suffix !== Infinity) - ) { - throw Error('invalid interval: ' + interval); - } - last_suffix = parseIntervalTable[m[4]]; - result += Number(m[1]) * parseIntervalTable[m[4]]; - }); - return result; -} - /** * Detect running protocol (http or https) */ @@ -353,70 +288,11 @@ export const ErrorCode = { getCode, }; -/** - * Check whether the path already exist. - * @param {String} path - * @return {Boolean} - */ -export function folderExists(path: string): boolean { - try { - const stat = fs.statSync(path); - return stat.isDirectory(); - } catch (_) { - return false; - } -} - -/** - * Check whether the file already exist. - * @param {String} path - * @return {Boolean} - */ -export function fileExists(path: string): boolean { - try { - const stat = fs.statSync(path); - return stat.isFile(); - } catch (_) { - return false; - } -} - -export function sortByName(packages: any[], orderAscending: boolean | void = true): string[] { - return packages.slice().sort(function (a, b): number { - const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase(); - - return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1; - }); -} - -export function addScope(scope: string, packageName: string): string { - return `@${scope}/${packageName}`; -} - -export function deleteProperties(propertiesToDelete: string[], objectItem: any): any { - _.forEach(propertiesToDelete, (property): any => { - delete objectItem[property]; - }); - - return objectItem; -} - export function buildToken(type: string, token: string): string { return `${_.capitalize(type)} ${token}`; } -/** - * return package version from tarball name - * @param {String} name - * @returns {String} - */ -export function getVersionFromTarball(name: string): string | void { - // FIXME: we know the regex is valid, but we should improve this part as ts suggest - // @ts-ignore - return /.+-(\d.+)\.tgz/.test(name) ? name.match(/.+-(\d.+)\.tgz/)[1] : undefined; -} - -export type AuthorFormat = Author | string | null | object | void; +export type AuthorFormat = Author | string | null | void; /** * Formats author field for webui. @@ -451,14 +327,6 @@ export function formatAuthor(author: AuthorFormat): 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); -} - /** * Apply whitespaces based on the length * @param {*} str the log message @@ -498,13 +366,3 @@ export function isVersionValid(packageMeta, packageVersion): boolean { const hasMatchVersion = Object.keys(packageMeta.versions).includes(packageVersion); return hasMatchVersion; } - -export function isRelatedToDeprecation(pkgInfo: Package): boolean { - const { versions } = pkgInfo; - for (const version in versions) { - if (Object.prototype.hasOwnProperty.call(versions[version], 'deprecated')) { - return true; - } - } - return false; -} diff --git a/packages/utils/test/tag.version.spec.ts b/packages/utils/test/tag.version.spec.ts deleted file mode 100644 index 82c4ac925..000000000 --- a/packages/utils/test/tag.version.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import assert from 'assert'; -import { tagVersion } from '../src/utils'; - -describe('tagVersion', () => { - test('add new one', () => { - let pkg = { - versions: {}, - 'dist-tags': {}, - }; - - // @ts-ignore - assert(tagVersion(pkg, '1.1.1', 'foo', {})); - assert.deepEqual(pkg, { - versions: {}, - 'dist-tags': { foo: '1.1.1' }, - }); - }); - - test('add (compat)', () => { - const x = { - versions: {}, - 'dist-tags': { foo: '1.1.0' }, - }; - - // @ts-ignore - assert(tagVersion(x, '1.1.1', 'foo')); - assert.deepEqual(x, { - versions: {}, - 'dist-tags': { foo: '1.1.1' }, - }); - }); - - test('add fresh tag', () => { - let x = { - versions: {}, - 'dist-tags': { foo: '1.1.0' }, - }; - - // @ts-ignore - assert(tagVersion(x, '1.1.1', 'foo')); - assert.deepEqual(x, { - versions: {}, - 'dist-tags': { foo: '1.1.1' }, - }); - }); -}); diff --git a/packages/utils/test/utils.spec.ts b/packages/utils/test/utils.spec.ts index 3d87124c4..f71637aca 100644 --- a/packages/utils/test/utils.spec.ts +++ b/packages/utils/test/utils.spec.ts @@ -8,10 +8,7 @@ import { getVersion, normalizeDistTags, getWebProtocol, - getVersionFromTarball, - sortByName, formatAuthor, - isHTTPProtocol, } from '../src/index'; describe('Utilities', () => { @@ -36,47 +33,6 @@ describe('Utilities', () => { const cloneMetadata = (pkg = metadata) => Object.assign({}, pkg); describe('API utilities', () => { - describe('Sort packages', () => { - const packages = [ - { - name: 'ghc', - }, - { - name: 'abc', - }, - { - name: 'zxy', - }, - ]; - test('should order ascending', () => { - expect(sortByName(packages)).toEqual([ - { - name: 'abc', - }, - { - name: 'ghc', - }, - { - name: 'zxy', - }, - ]); - }); - - test('should order descending', () => { - expect(sortByName(packages, false)).toEqual([ - { - name: 'zxy', - }, - { - name: 'ghc', - }, - { - name: 'abc', - }, - ]); - }); - }); - describe('getWebProtocol', () => { test('should handle undefined header', () => { expect(getWebProtocol(undefined, 'http')).toBe('http'); @@ -300,63 +256,26 @@ describe('Utilities', () => { }); }); - describe('getVersionFromTarball', () => { - test('should get the right version', () => { - const simpleName = 'test-name-4.2.12.tgz'; - const complexName = 'test-5.6.4-beta.2.tgz'; - const otherComplexName = 'test-3.5.0-6.tgz'; - expect(getVersionFromTarball(simpleName)).toEqual('4.2.12'); - expect(getVersionFromTarball(complexName)).toEqual('5.6.4-beta.2'); - expect(getVersionFromTarball(otherComplexName)).toEqual('3.5.0-6'); + describe('formatAuthor', () => { + test('should check author field different values', () => { + const author = 'verdaccioNpm'; + expect(formatAuthor(author).name).toEqual(author); }); - - test("should don'n fall at incorrect tarball name", () => { - expect(getVersionFromTarball('incorrectName')).toBeUndefined(); + test('should check author field for object value', () => { + const user = { + name: 'Verdaccion NPM', + email: 'verdaccio@verdaccio.org', + url: 'https://verdaccio.org', + }; + expect(formatAuthor(user).url).toEqual(user.url); + expect(formatAuthor(user).email).toEqual(user.email); + expect(formatAuthor(user).name).toEqual(user.name); + }); + test('should check author field for other value', () => { + expect(formatAuthor(null).name).toEqual(DEFAULT_USER); + expect(formatAuthor({}).name).toEqual(DEFAULT_USER); + expect(formatAuthor([]).name).toEqual(DEFAULT_USER); }); - }); - }); - - describe('String utilities', () => { - 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('formatAuthor', () => { - test('should check author field different values', () => { - const author = 'verdaccioNpm'; - expect(formatAuthor(author).name).toEqual(author); - }); - test('should check author field for object value', () => { - const user = { - name: 'Verdaccion NPM', - email: 'verdaccio@verdaccio.org', - url: 'https://verdaccio.org', - }; - expect(formatAuthor(user).url).toEqual(user.url); - expect(formatAuthor(user).email).toEqual(user.email); - expect(formatAuthor(user).name).toEqual(user.name); - }); - test('should check author field for other value', () => { - expect(formatAuthor(null).name).toEqual(DEFAULT_USER); - expect(formatAuthor({}).name).toEqual(DEFAULT_USER); - expect(formatAuthor([]).name).toEqual(DEFAULT_USER); }); }); }); diff --git a/packages/web/src/endpoint/package.ts b/packages/web/src/endpoint/package.ts index 59b9d1d40..4d7d09e54 100644 --- a/packages/web/src/endpoint/package.ts +++ b/packages/web/src/endpoint/package.ts @@ -1,8 +1,5 @@ import _ from 'lodash'; import { - addScope, - deleteProperties, - sortByName, formatAuthor, convertDistRemoteToLocalTarballUrls, getLocalRegistryTarballUri, @@ -20,6 +17,7 @@ import { Config, Package, RemoteUser, Version } from '@verdaccio/types'; import { addGravatarSupport, AuthorAvatar, parseReadme } from '../web-utils'; import { generateGravatarUrl } from '../user'; +import { deleteProperties, addScope, sortByName } from '../web-utils2'; export type $SidebarPackage = Package & { latest: Version }; export { $RequestExtend, $ResponseExtend, $NextFunctionVer }; // Was required by other packages diff --git a/packages/web/src/render-web.ts b/packages/web/src/render-web.ts index 143fbd914..6ec7b0af2 100644 --- a/packages/web/src/render-web.ts +++ b/packages/web/src/render-web.ts @@ -4,12 +4,13 @@ import path from 'path'; import _ from 'lodash'; import express from 'express'; -import { combineBaseUrl, getWebProtocol, isHTTPProtocol } from '@verdaccio/utils'; +import { combineBaseUrl, getWebProtocol } from '@verdaccio/utils'; import { SearchInstance } from '@verdaccio/store'; import { WEB_TITLE } from '@verdaccio/config'; import { HEADERS, HTTP_STATUS } from '@verdaccio/commons-api'; import { loadPlugin } from '@verdaccio/loaders'; import { setSecurityWebHeaders } from '@verdaccio/middleware'; +import { isHTTPProtocol } from './web-utils2'; const pkgJSON = require('../package.json'); diff --git a/packages/web/src/web-utils2.ts b/packages/web/src/web-utils2.ts new file mode 100644 index 000000000..682239aa7 --- /dev/null +++ b/packages/web/src/web-utils2.ts @@ -0,0 +1,39 @@ +import _ from 'lodash'; + +/** + * Check if URI is starting with "http://", "https://" or "//" + * @param {string} uri + */ +export function isHTTPProtocol(uri: string): boolean { + return /^(https?:)?\/\//.test(uri); +} + +export function deleteProperties(propertiesToDelete: string[], objectItem: any): any { + _.forEach(propertiesToDelete, (property): any => { + delete objectItem[property]; + }); + + return objectItem; +} + +export function addScope(scope: string, packageName: string): string { + return `@${scope}/${packageName}`; +} + +export function sortByName(packages: any[], orderAscending: boolean | void = true): string[] { + return packages.slice().sort(function (a, b): number { + const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase(); + return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1; + }); +} + +/** + * Detect running protocol (http or https) + */ +export function getWebProtocol(headerProtocol: string | void, protocol: string): string { + if (typeof headerProtocol === 'string' && headerProtocol !== '') { + const commaIndex = headerProtocol.indexOf(','); + return commaIndex > 0 ? headerProtocol.substr(0, commaIndex) : headerProtocol; + } + return protocol; +} diff --git a/packages/web/test/web-utils.spec.ts b/packages/web/test/web-utils.spec.ts new file mode 100644 index 000000000..c50c02bd7 --- /dev/null +++ b/packages/web/test/web-utils.spec.ts @@ -0,0 +1,65 @@ +import { isHTTPProtocol, sortByName } from '../src/web-utils2'; + +describe('Utilities', () => { + describe('String utilities', () => { + 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('Sort packages', () => { + const packages = [ + { + name: 'ghc', + }, + { + name: 'abc', + }, + { + name: 'zxy', + }, + ]; + test('should order ascending', () => { + expect(sortByName(packages)).toEqual([ + { + name: 'abc', + }, + { + name: 'ghc', + }, + { + name: 'zxy', + }, + ]); + }); + + test('should order descending', () => { + expect(sortByName(packages, false)).toEqual([ + { + name: 'zxy', + }, + { + name: 'ghc', + }, + { + name: 'abc', + }, + ]); + }); + }); +});