mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-11-08 23:25:51 +01:00
chore: lint prettier update (#1904)
* chore: update prettier@2.x * chore: prettier auto fix * chore: fake circleci we don't need it but I want make dissapear the error
This commit is contained in:
parent
a13e57586c
commit
e61bd6c78f
8
.circleci/config.yml
Normal file
8
.circleci/config.yml
Normal file
@ -0,0 +1,8 @@
|
||||
version: 2.1
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/node:12 # the primary container, where your job's commands are run
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: echo "hello world" # run the `echo` command
|
46
.eslintrc
46
.eslintrc
@ -2,21 +2,23 @@
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"google",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:jest/recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:import/typescript",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"prettier",
|
||||
"prettier/react",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"plugins": [
|
||||
"import",
|
||||
"jest",
|
||||
"prettier",
|
||||
"jsx-a11y",
|
||||
"react-hooks"
|
||||
"react-hooks",
|
||||
"prettier"
|
||||
],
|
||||
"env": {
|
||||
"es6": true,
|
||||
@ -30,7 +32,7 @@
|
||||
"parserOptions": {
|
||||
"allowImportExportEverywhere": true,
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 2019,
|
||||
"ecmaVersion": 11,
|
||||
"ecmaFeatures": {
|
||||
"impliedStrict": true,
|
||||
"jsx": true
|
||||
@ -52,18 +54,25 @@
|
||||
"error", "all"
|
||||
],
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
2,
|
||||
{
|
||||
"endOfLine": "auto",
|
||||
"printWidth": 120,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 160,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": true
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": true,
|
||||
"trailingComma": "es5",
|
||||
"semi": true,
|
||||
"parser": "typescript"
|
||||
}
|
||||
],
|
||||
"react/prop-types": 0,
|
||||
"jest/no-export": 0,
|
||||
"jest/no-test-callback": 0,
|
||||
"jest/expect-expect": 0,
|
||||
"jest/no-try-expect": 0,
|
||||
"jest/no-identical-title": 1,
|
||||
"keyword-spacing": "off",
|
||||
"no-tabs": "off",
|
||||
"no-useless-escape": "off",
|
||||
@ -71,16 +80,10 @@
|
||||
"require-jsdoc": "off",
|
||||
"valid-jsdoc": "off",
|
||||
"import/order": ["warn"],
|
||||
"react/prop-types": 0,
|
||||
"jest/no-export": 0,
|
||||
"jest/no-test-callback": 0,
|
||||
"jest/no-try-expect": 1,
|
||||
"jest/no-identical-title": 1,
|
||||
"eol-last": "warn",
|
||||
"no-irregular-whitespace": "warn",
|
||||
"no-mixed-spaces-and-tabs": ["warn", "smart-tabs"],
|
||||
"no-trailing-spaces": "warn",
|
||||
|
||||
"camelcase": "off",
|
||||
"guard-for-in": "error",
|
||||
"new-cap": "error",
|
||||
@ -99,7 +102,9 @@
|
||||
"one-var": "error",
|
||||
"prefer-rest-params": "warn",
|
||||
"prefer-spread": "warn",
|
||||
"semi": 0,
|
||||
"handle-callback-err": 0,
|
||||
"prefer-const": 0,
|
||||
"prefer-promise-reject-errors": 1,
|
||||
"@typescript-eslint/camelcase": 0,
|
||||
"@typescript-eslint/ban-ts-ignore": 0,
|
||||
"@typescript-eslint/no-var-requires": 0,
|
||||
@ -115,9 +120,6 @@
|
||||
"@typescript-eslint/ban-types": 0,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"@typescript-eslint/explicit-member-accessibility": ["warn"],
|
||||
"@typescript-eslint/prefer-optional-chain": ["warn"],
|
||||
"handle-callback-err": 0,
|
||||
"prefer-const": 0,
|
||||
"prefer-promise-reject-errors": 1
|
||||
"@typescript-eslint/prefer-optional-chain": ["warn"]
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@
|
||||
"kleur": "3.0.3",
|
||||
"lint-staged": "8.2.1",
|
||||
"nock": "12.0.3",
|
||||
"prettier": "1.19.1",
|
||||
"prettier": "2.0.5",
|
||||
"rimraf": "3.0.2",
|
||||
"selfsigned": "1.10.7",
|
||||
"standard-version": "8.0.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
import mime from 'mime';
|
||||
import _ from 'lodash';
|
||||
import{ Router } from 'express';
|
||||
import { Router } from 'express';
|
||||
|
||||
import { media, allow } from '@verdaccio/middleware';
|
||||
import { API_MESSAGE, HTTP_STATUS, DIST_TAGS } from '@verdaccio/dev-commons';
|
||||
@ -8,18 +8,18 @@ import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
import { Package } from '@verdaccio/types';
|
||||
|
||||
// @ts-ignore
|
||||
import{ IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler } from '@verdaccio/dev-types';
|
||||
import { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler } from '@verdaccio/dev-types';
|
||||
|
||||
export default function(route: Router, auth: IAuth, storage: IStorageHandler): void {
|
||||
export default function (route: Router, auth: IAuth, storage: IStorageHandler): void {
|
||||
const can = allow(auth);
|
||||
const tag_package_version = function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): $NextFunctionVer {
|
||||
const tag_package_version = function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): $NextFunctionVer {
|
||||
if (_.isString(req.body) === false) {
|
||||
return next('route');
|
||||
}
|
||||
|
||||
const tags = {};
|
||||
tags[req.params.tag] = req.body;
|
||||
storage.mergeTags(req.params.package, tags, function(err: Error): $NextFunctionVer {
|
||||
storage.mergeTags(req.params.package, tags, function (err: Error): $NextFunctionVer {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@ -35,10 +35,10 @@ export default function(route: Router, auth: IAuth, storage: IStorageHandler): v
|
||||
|
||||
route.put('/-/package/:package/dist-tags/:tag', can('publish'), media(mime.getType('json')), tag_package_version);
|
||||
|
||||
route.delete('/-/package/:package/dist-tags/:tag', can('publish'), function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
route.delete('/-/package/:package/dist-tags/:tag', can('publish'), function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
const tags = {};
|
||||
tags[req.params.tag] = null;
|
||||
storage.mergeTags(req.params.package, tags, function(err: VerdaccioError): $NextFunctionVer {
|
||||
storage.mergeTags(req.params.package, tags, function (err: VerdaccioError): $NextFunctionVer {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@ -49,12 +49,12 @@ export default function(route: Router, auth: IAuth, storage: IStorageHandler): v
|
||||
});
|
||||
});
|
||||
|
||||
route.get('/-/package/:package/dist-tags', can('access'), function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
route.get('/-/package/:package/dist-tags', can('access'), function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
storage.getPackage({
|
||||
name: req.params.package,
|
||||
uplinksLook: true,
|
||||
req,
|
||||
callback: function(err: VerdaccioError, info: Package): $NextFunctionVer {
|
||||
callback: function (err: VerdaccioError, info: Package): $NextFunctionVer {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@ -64,8 +64,8 @@ export default function(route: Router, auth: IAuth, storage: IStorageHandler): v
|
||||
});
|
||||
});
|
||||
|
||||
route.post('/-/package/:package/dist-tags', can('publish'), function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
storage.mergeTags(req.params.package, req.body, function(err: VerdaccioError): $NextFunctionVer {
|
||||
route.post('/-/package/:package/dist-tags', can('publish'), function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
storage.mergeTags(req.params.package, req.body, function (err: VerdaccioError): $NextFunctionVer {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import express, {Express} from 'express';
|
||||
import express, { Express } from 'express';
|
||||
|
||||
import { match, validateName, validatePackage, encodeScopePackage, antiLoop } from '@verdaccio/middleware';
|
||||
import { IAuth, IStorageHandler } from '@verdaccio/dev-types';
|
||||
@ -16,9 +16,9 @@ import pkg from './package';
|
||||
import stars from './stars';
|
||||
import profile from './v1/profile';
|
||||
import token from './v1/token';
|
||||
import v1Search from './v1/search'
|
||||
import v1Search from './v1/search';
|
||||
|
||||
export default function(config: Config, auth: IAuth, storage: IStorageHandler): Express.Application {
|
||||
export default function (config: Config, auth: IAuth, storage: IStorageHandler): Express.Application {
|
||||
/* eslint new-cap:off */
|
||||
const app = express.Router();
|
||||
/* eslint new-cap:off */
|
||||
@ -55,7 +55,7 @@ export default function(config: Config, auth: IAuth, storage: IStorageHandler):
|
||||
stars(app, storage);
|
||||
|
||||
if (_.get(config, 'experiments.search') === true) {
|
||||
v1Search(app, auth, storage)
|
||||
v1Search(app, auth, storage);
|
||||
}
|
||||
|
||||
if (_.get(config, 'experiments.token') === true) {
|
||||
|
@ -10,11 +10,11 @@ import { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandl
|
||||
const downloadStream = (packageName: string, filename: string, storage: any, req: $RequestExtend, res: $ResponseExtend): void => {
|
||||
const stream = storage.getTarball(packageName, filename);
|
||||
|
||||
stream.on('content-length', function(content): void {
|
||||
stream.on('content-length', function (content): void {
|
||||
res.header('Content-Length', content);
|
||||
});
|
||||
|
||||
stream.on('error', function(err): void {
|
||||
stream.on('error', function (err): void {
|
||||
return res.locals.report_error(err);
|
||||
});
|
||||
|
||||
@ -22,11 +22,11 @@ const downloadStream = (packageName: string, filename: string, storage: any, req
|
||||
stream.pipe(res);
|
||||
};
|
||||
|
||||
export default function(route: Router, auth: IAuth, storage: IStorageHandler, config: Config): void {
|
||||
export default function (route: Router, auth: IAuth, storage: IStorageHandler, config: Config): void {
|
||||
const can = allow(auth);
|
||||
// TODO: anonymous user?
|
||||
route.get('/:package/:version?', can('access'), function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
const getPackageMetaCallback = function(err, metadata: Package): void {
|
||||
route.get('/:package/:version?', can('access'), function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
const getPackageMetaCallback = function (err, metadata: Package): void {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@ -62,13 +62,13 @@ export default function(route: Router, auth: IAuth, storage: IStorageHandler, co
|
||||
});
|
||||
});
|
||||
|
||||
route.get('/:scopedPackage/-/:scope/:filename', can('access'), function(req: $RequestExtend, res: $ResponseExtend): void {
|
||||
route.get('/:scopedPackage/-/:scope/:filename', can('access'), function (req: $RequestExtend, res: $ResponseExtend): void {
|
||||
const { scopedPackage, filename } = req.params;
|
||||
|
||||
downloadStream(scopedPackage, filename, storage, req, res);
|
||||
});
|
||||
|
||||
route.get('/:package/-/:filename', can('access'), function(req: $RequestExtend, res: $ResponseExtend): void {
|
||||
route.get('/:package/-/:filename', can('access'), function (req: $RequestExtend, res: $ResponseExtend): void {
|
||||
downloadStream(req.params.package, req.params.filename, storage, req, res);
|
||||
});
|
||||
}
|
||||
|
@ -2,10 +2,7 @@ import { Router } from 'express';
|
||||
import { $RequestExtend, $ResponseExtend, $NextFunctionVer } from '@verdaccio/dev-types';
|
||||
|
||||
export default function (route: Router): void {
|
||||
route.get(
|
||||
'/-/ping',
|
||||
function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
next({});
|
||||
}
|
||||
);
|
||||
route.get('/-/ping', function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
next({});
|
||||
});
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ import { Router } from 'express';
|
||||
|
||||
import { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler } from '@verdaccio/dev-types';
|
||||
import { API_MESSAGE, HEADERS, DIST_TAGS, API_ERROR, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import {validateMetadata, isObject, ErrorCode, hasDiffOneKey, isRelatedToDeprecation} from '@verdaccio/utils';
|
||||
import { validateMetadata, isObject, ErrorCode, hasDiffOneKey, isRelatedToDeprecation } from '@verdaccio/utils';
|
||||
import { media, expectJson, allow } from '@verdaccio/middleware';
|
||||
import { notify } from '@verdaccio/hooks';
|
||||
import { Config, Callback, MergeTags, Version, Package } from '@verdaccio/types';
|
||||
import { logger } from '@verdaccio/logger';
|
||||
|
||||
import star from './star';
|
||||
import {isPublishablePackage} from "./utils";
|
||||
import { isPublishablePackage } from './utils';
|
||||
|
||||
export default function publish(router: Router, auth: IAuth, storage: IStorageHandler, config: Config): void {
|
||||
const can = allow(auth);
|
||||
@ -103,43 +103,43 @@ export default function publish(router: Router, auth: IAuth, storage: IStorageHa
|
||||
*/
|
||||
export function publishPackage(storage: IStorageHandler, config: Config, auth: IAuth): any {
|
||||
const starApi = star(storage);
|
||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
const packageName = req.params.package;
|
||||
|
||||
logger.debug({packageName} , `publishing or updating a new version for @{packageName}`);
|
||||
logger.debug({ packageName }, `publishing or updating a new version for @{packageName}`);
|
||||
|
||||
/**
|
||||
* Write tarball of stream data from package clients.
|
||||
*/
|
||||
const createTarball = function(filename: string, data, cb: Callback): void {
|
||||
const createTarball = function (filename: string, data, cb: Callback): void {
|
||||
const stream = storage.addTarball(packageName, filename);
|
||||
stream.on('error', function(err) {
|
||||
stream.on('error', function (err) {
|
||||
cb(err);
|
||||
});
|
||||
stream.on('success', function() {
|
||||
stream.on('success', function () {
|
||||
cb();
|
||||
});
|
||||
// this is dumb and memory-consuming, but what choices do we have?
|
||||
// flow: we need first refactor this file before decides which type use here
|
||||
stream.end(Buffer.from(data.data, 'base64'));
|
||||
stream.end(new Buffer(data.data, 'base64'));
|
||||
stream.done();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add new package version in storage
|
||||
*/
|
||||
const createVersion = function(version: string, metadata: Version, cb: Callback): void {
|
||||
const createVersion = function (version: string, metadata: Version, cb: Callback): void {
|
||||
storage.addVersion(packageName, version, metadata, null, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add new tags in storage
|
||||
*/
|
||||
const addTags = function(tags: MergeTags, cb: Callback): void {
|
||||
const addTags = function (tags: MergeTags, cb: Callback): void {
|
||||
storage.mergeTags(packageName, tags, cb);
|
||||
};
|
||||
|
||||
const afterChange = function(error, okMessage, metadata: Package): void {
|
||||
const afterChange = function (error, okMessage, metadata: Package): void {
|
||||
const metadataCopy: Package = { ...metadata };
|
||||
|
||||
const { _attachments, versions } = metadataCopy;
|
||||
@ -160,8 +160,7 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
||||
// npm-registry-client 0.3+ embeds tarball into the json upload
|
||||
// https://github.com/isaacs/npm-registry-client/commit/e9fbeb8b67f249394f735c74ef11fe4720d46ca0
|
||||
// issue https://github.com/rlidwka/sinopia/issues/31, dealing with it here:
|
||||
const isInvalidBodyFormat = isObject(_attachments) === false || hasDiffOneKey(_attachments) ||
|
||||
isObject(versions) === false || hasDiffOneKey(versions);
|
||||
const isInvalidBodyFormat = isObject(_attachments) === false || hasDiffOneKey(_attachments) || isObject(versions) === false || hasDiffOneKey(versions);
|
||||
|
||||
if (isInvalidBodyFormat) {
|
||||
// npm is doing something strange again
|
||||
@ -177,22 +176,21 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
||||
// at this point document is either created or existed before
|
||||
const [firstAttachmentKey] = Object.keys(_attachments);
|
||||
|
||||
createTarball(Path.basename(firstAttachmentKey), _attachments[firstAttachmentKey], function(error) {
|
||||
createTarball(Path.basename(firstAttachmentKey), _attachments[firstAttachmentKey], function (error) {
|
||||
if (error) {
|
||||
return next(error);
|
||||
}
|
||||
|
||||
const versionToPublish = Object.keys(versions)[0];
|
||||
const versionMetadataToPublish = versions[versionToPublish];
|
||||
|
||||
versionMetadataToPublish.readme = _.isNil(versionMetadataToPublish.readme) === false ? String(versionMetadataToPublish.readme) : '';
|
||||
versions[versionToPublish].readme = _.isNil(metadataCopy.readme) === false ? String(metadataCopy.readme) : '';
|
||||
|
||||
createVersion(versionToPublish, versionMetadataToPublish, function(error) {
|
||||
createVersion(versionToPublish, versions[versionToPublish], function (error) {
|
||||
if (error) {
|
||||
return next(error);
|
||||
}
|
||||
|
||||
addTags(metadataCopy[DIST_TAGS], async function(error) {
|
||||
addTags(metadataCopy[DIST_TAGS], async function (error) {
|
||||
if (error) {
|
||||
return next(error);
|
||||
}
|
||||
@ -218,27 +216,27 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
||||
const metadata = validateMetadata(req.body, packageName);
|
||||
// treating deprecation as updating a package
|
||||
if (req.params._rev || isRelatedToDeprecation(req.body)) {
|
||||
logger.debug({packageName} , `updating a new version for @{packageName}`);
|
||||
logger.debug({ packageName }, `updating a new version for @{packageName}`);
|
||||
// we check unpublish permissions, an update is basically remove versions
|
||||
const remote = req.remote_user;
|
||||
auth.allow_unpublish({packageName}, remote, (error) => {
|
||||
auth.allow_unpublish({ packageName }, remote, (error) => {
|
||||
if (error) {
|
||||
logger.debug({packageName} , `not allowed to unpublish a version for @{packageName}`);
|
||||
logger.debug({ packageName }, `not allowed to unpublish a version for @{packageName}`);
|
||||
return next(error);
|
||||
}
|
||||
|
||||
storage.changePackage(packageName, metadata, req.params.revision, function(error) {
|
||||
storage.changePackage(packageName, metadata, req.params.revision, function (error) {
|
||||
afterChange(error, API_MESSAGE.PKG_CHANGED, metadata);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
logger.debug({packageName} , `adding a new version for @{packageName}`);
|
||||
storage.addPackage(packageName, metadata, function(error) {
|
||||
logger.debug({ packageName }, `adding a new version for @{packageName}`);
|
||||
storage.addPackage(packageName, metadata, function (error) {
|
||||
afterChange(error, API_MESSAGE.PKG_CREATED, metadata);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error({packageName}, 'error on publish, bad package data for @{packageName}');
|
||||
logger.error({ packageName }, 'error on publish, bad package data for @{packageName}');
|
||||
return next(ErrorCode.getBadData(API_ERROR.BAD_PACKAGE_DATA));
|
||||
}
|
||||
};
|
||||
@ -248,11 +246,11 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
||||
* un-publish a package
|
||||
*/
|
||||
export function unPublishPackage(storage: IStorageHandler) {
|
||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
const packageName = req.params.package;
|
||||
|
||||
logger.debug({packageName} , `unpublishing @{packageName}`);
|
||||
storage.removePackage(packageName, function(err) {
|
||||
logger.debug({ packageName }, `unpublishing @{packageName}`);
|
||||
storage.removePackage(packageName, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@ -266,18 +264,18 @@ export function unPublishPackage(storage: IStorageHandler) {
|
||||
* Delete tarball
|
||||
*/
|
||||
export function removeTarball(storage: IStorageHandler) {
|
||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
const packageName = req.params.package;
|
||||
const {filename, revision} = req.params;
|
||||
const { filename, revision } = req.params;
|
||||
|
||||
logger.debug({packageName, filename, revision} , `removing a tarball for @{packageName}-@{tarballName}-@{revision}`);
|
||||
storage.removeTarball(packageName, filename, revision, function(err) {
|
||||
logger.debug({ packageName, filename, revision }, `removing a tarball for @{packageName}-@{tarballName}-@{revision}`);
|
||||
storage.removeTarball(packageName, filename, revision, function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.status(HTTP_STATUS.CREATED);
|
||||
|
||||
logger.debug({packageName, filename, revision} , `success remove tarball for @{packageName}-@{tarballName}-@{revision}`);
|
||||
logger.debug({ packageName, filename, revision }, `success remove tarball for @{packageName}-@{tarballName}-@{revision}`);
|
||||
return next({ ok: API_MESSAGE.TARBALL_REMOVED });
|
||||
});
|
||||
};
|
||||
@ -286,11 +284,11 @@ export function removeTarball(storage: IStorageHandler) {
|
||||
* Adds a new version
|
||||
*/
|
||||
export function addVersion(storage: IStorageHandler) {
|
||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
const { version, tag } = req.params;
|
||||
const packageName = req.params.package;
|
||||
|
||||
storage.addVersion(packageName, version, req.body, tag, function(error) {
|
||||
storage.addVersion(packageName, version, req.body, tag, function (error) {
|
||||
if (error) {
|
||||
return next(error);
|
||||
}
|
||||
@ -307,29 +305,29 @@ export function addVersion(storage: IStorageHandler) {
|
||||
* uploadPackageTarball
|
||||
*/
|
||||
export function uploadPackageTarball(storage: IStorageHandler) {
|
||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
const packageName = req.params.package;
|
||||
const stream = storage.addTarball(packageName, req.params.filename);
|
||||
req.pipe(stream);
|
||||
|
||||
// checking if end event came before closing
|
||||
let complete = false;
|
||||
req.on('end', function() {
|
||||
req.on('end', function () {
|
||||
complete = true;
|
||||
stream.done();
|
||||
});
|
||||
|
||||
req.on('close', function() {
|
||||
req.on('close', function () {
|
||||
if (!complete) {
|
||||
stream.abort();
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', function(err) {
|
||||
stream.on('error', function (err) {
|
||||
return res.locals.report_error(err);
|
||||
});
|
||||
|
||||
stream.on('success', function() {
|
||||
stream.on('success', function () {
|
||||
res.status(HTTP_STATUS.CREATED);
|
||||
return next({
|
||||
ok: API_MESSAGE.TARBALL_UPLOADED,
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { HEADERS } from '@verdaccio/dev-commons';
|
||||
|
||||
export default function(route, auth, storage): void {
|
||||
export default function (route, auth, storage): void {
|
||||
// searching packages
|
||||
route.get('/-/all(/since)?', function(req, res) {
|
||||
route.get('/-/all(/since)?', function (req, res) {
|
||||
let received_end = false;
|
||||
let response_finished = false;
|
||||
let processing_pkgs = 0;
|
||||
let firstPackage = true;
|
||||
|
||||
res.status(200);
|
||||
res.set(HEADERS.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
res.set(HEADERS.CONTENT_TYPE, HEADERS.JSON_CHARSET);
|
||||
|
||||
/*
|
||||
* Offical NPM registry (registry.npmjs.org) no longer return whole database,
|
||||
@ -33,7 +33,7 @@ export default function(route, auth, storage): void {
|
||||
if (!respShouldBeArray) {
|
||||
res.set('Date', 'Mon, 10 Oct 1983 00:12:48 GMT');
|
||||
}
|
||||
const check_finish = function(): void {
|
||||
const check_finish = function (): void {
|
||||
if (!received_end) {
|
||||
return;
|
||||
}
|
||||
@ -62,7 +62,7 @@ export default function(route, auth, storage): void {
|
||||
stream.on('data', function each(pkg) {
|
||||
processing_pkgs++;
|
||||
|
||||
auth.allow_access({ packageName: pkg.name }, req.remote_user, function(err, allowed) {
|
||||
auth.allow_access({ packageName: pkg.name }, req.remote_user, function (err, allowed) {
|
||||
processing_pkgs--;
|
||||
|
||||
if (err) {
|
||||
@ -90,11 +90,11 @@ export default function(route, auth, storage): void {
|
||||
});
|
||||
});
|
||||
|
||||
stream.on('error', function() {
|
||||
stream.on('error', function () {
|
||||
res.socket.destroy();
|
||||
});
|
||||
|
||||
stream.on('end', function() {
|
||||
stream.on('end', function () {
|
||||
received_end = true;
|
||||
check_finish();
|
||||
});
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { USERS, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import {Response} from 'express';
|
||||
import { Response } from 'express';
|
||||
import _ from 'lodash';
|
||||
import { logger } from '@verdaccio/logger';
|
||||
|
||||
import {$RequestExtend, $NextFunctionVer, IStorageHandler} from '@verdaccio/dev-types';
|
||||
import { $RequestExtend, $NextFunctionVer, IStorageHandler } from '@verdaccio/dev-types';
|
||||
|
||||
export default function(storage: IStorageHandler): (req: $RequestExtend, res: Response, next: $NextFunctionVer) => void {
|
||||
export default function (storage: IStorageHandler): (req: $RequestExtend, res: Response, next: $NextFunctionVer) => void {
|
||||
const validateInputs = (newUsers, localUsers, username, isStar): boolean => {
|
||||
const isExistlocalUsers = _.isNil(localUsers[username]) === false;
|
||||
if (isStar && isExistlocalUsers && localUsers[username]) {
|
||||
@ -20,8 +20,8 @@ export default function(storage: IStorageHandler): (req: $RequestExtend, res: Re
|
||||
|
||||
return (req: $RequestExtend, res: Response, next: $NextFunctionVer): void => {
|
||||
const name = req.params.package;
|
||||
logger.debug({name}, 'starring a package for @{name}');
|
||||
const afterChangePackage = function(err?: Error) {
|
||||
logger.debug({ name }, 'starring a package for @{name}');
|
||||
const afterChangePackage = function (err?: Error) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@ -34,7 +34,7 @@ export default function(storage: IStorageHandler): (req: $RequestExtend, res: Re
|
||||
storage.getPackage({
|
||||
name,
|
||||
req,
|
||||
callback: function(err, info) {
|
||||
callback: function (err, info) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@ -46,16 +46,22 @@ export default function(storage: IStorageHandler): (req: $RequestExtend, res: Re
|
||||
if (_.isNil(localStarUsers) === false && validateInputs(newStarUser, localStarUsers, remoteUsername, isStar)) {
|
||||
return afterChangePackage();
|
||||
}
|
||||
const users = isStar ? {
|
||||
...localStarUsers,
|
||||
[remoteUsername]: true,
|
||||
} : _.reduce(localStarUsers, (users, value, key) => {
|
||||
if (key !== remoteUsername) {
|
||||
users[key] = value;
|
||||
}
|
||||
return users;
|
||||
}, {});
|
||||
storage.changePackage(name, { ...info, users}, req.body._rev, function(err) {
|
||||
const users = isStar
|
||||
? {
|
||||
...localStarUsers,
|
||||
[remoteUsername]: true,
|
||||
}
|
||||
: _.reduce(
|
||||
localStarUsers,
|
||||
(users, value, key) => {
|
||||
if (key !== remoteUsername) {
|
||||
users[key] = value;
|
||||
}
|
||||
return users;
|
||||
},
|
||||
{}
|
||||
);
|
||||
storage.changePackage(name, { ...info, users }, req.body._rev, function (err) {
|
||||
afterChangePackage(err);
|
||||
});
|
||||
},
|
||||
|
@ -8,7 +8,7 @@ import { $RequestExtend, $NextFunctionVer, IStorageHandler } from '@verdaccio/de
|
||||
|
||||
type Packages = Package[];
|
||||
|
||||
export default function(route: Router, storage: IStorageHandler): void {
|
||||
export default function (route: Router, storage: IStorageHandler): void {
|
||||
route.get('/-/_view/starredByUser', (req: $RequestExtend, res: Response, next: $NextFunctionVer): void => {
|
||||
const remoteUsername = req.remote_user.name;
|
||||
|
||||
@ -17,9 +17,7 @@ export default function(route: Router, storage: IStorageHandler): void {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
const filteredPackages: Packages = localPackages.filter((localPackage: Package) =>
|
||||
_.keys(localPackage[USERS]).includes(remoteUsername)
|
||||
);
|
||||
const filteredPackages: Packages = localPackages.filter((localPackage: Package) => _.keys(localPackage[USERS]).includes(remoteUsername));
|
||||
|
||||
res.status(HTTP_STATUS.OK);
|
||||
next({
|
||||
|
@ -10,15 +10,15 @@ import { Config, RemoteUser } from '@verdaccio/types';
|
||||
import { $RequestExtend, $ResponseExtend, $NextFunctionVer, IAuth } from '@verdaccio/dev-types';
|
||||
import { API_ERROR, API_MESSAGE, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
|
||||
export default function(route: Router, auth: IAuth, config: Config): void {
|
||||
route.get('/-/user/:org_couchdb_user', function(req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
export default function (route: Router, auth: IAuth, config: Config): void {
|
||||
route.get('/-/user/:org_couchdb_user', function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
res.status(HTTP_STATUS.OK);
|
||||
next({
|
||||
ok: getAuthenticatedMessage(req.remote_user.name),
|
||||
});
|
||||
});
|
||||
|
||||
route.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
route.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
const { name, password } = req.body;
|
||||
const remoteName = req.remote_user.name;
|
||||
|
||||
@ -45,7 +45,7 @@ export default function(route: Router, auth: IAuth, config: Config): void {
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.PASSWORD_SHORT()));
|
||||
}
|
||||
|
||||
auth.add_user(name, password, async function(err, user): Promise<void> {
|
||||
auth.add_user(name, password, async function (err, user): Promise<void> {
|
||||
if (err) {
|
||||
if (err.status >= HTTP_STATUS.BAD_REQUEST && err.status < HTTP_STATUS.INTERNAL_ERROR) {
|
||||
// With npm registering is the same as logging in,
|
||||
@ -68,7 +68,7 @@ export default function(route: Router, auth: IAuth, config: Config): void {
|
||||
}
|
||||
});
|
||||
|
||||
route.delete('/-/user/token/*', function(req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
route.delete('/-/user/token/*', function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
res.status(HTTP_STATUS.OK);
|
||||
next({
|
||||
ok: API_MESSAGE.LOGGED_OUT,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Package} from '@verdaccio/types';
|
||||
import { Package } from '@verdaccio/types';
|
||||
import _ from 'lodash';
|
||||
|
||||
/**
|
||||
@ -7,7 +7,7 @@ import _ from 'lodash';
|
||||
*/
|
||||
|
||||
export function isPublishablePackage(pkg: Package): boolean {
|
||||
const keys: string[] = Object.keys(pkg);
|
||||
const keys: string[] = Object.keys(pkg);
|
||||
|
||||
return _.includes(keys, 'versions');
|
||||
return _.includes(keys, 'versions');
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ export interface Profile {
|
||||
fullname: string;
|
||||
}
|
||||
|
||||
export default function(route: Router, auth: IAuth): void {
|
||||
export default function (route: Router, auth: IAuth): void {
|
||||
function buildProfile(name: string): Profile {
|
||||
return {
|
||||
tfa: false,
|
||||
@ -30,7 +30,7 @@ export default function(route: Router, auth: IAuth): void {
|
||||
};
|
||||
}
|
||||
|
||||
route.get('/-/npm/v1/user', function(req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
route.get('/-/npm/v1/user', function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
if (_.isNil(req.remote_user.name) === false) {
|
||||
return next(buildProfile(req.remote_user.name));
|
||||
}
|
||||
@ -41,7 +41,7 @@ export default function(route: Router, auth: IAuth): void {
|
||||
});
|
||||
});
|
||||
|
||||
route.post('/-/npm/v1/user', function(req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
route.post('/-/npm/v1/user', function (req: $RequestExtend, res: Response, next: $NextFunctionVer): void {
|
||||
if (_.isNil(req.remote_user.name)) {
|
||||
res.status(HTTP_STATUS.UNAUTHORIZED);
|
||||
return next({
|
||||
|
@ -1,98 +1,101 @@
|
||||
import semver from 'semver'
|
||||
import semver from 'semver';
|
||||
import { Package } from '@verdaccio/types';
|
||||
|
||||
function compileTextSearch(textSearch: string): ((pkg: Package) => boolean) {
|
||||
const personMatch = (person, search) => {
|
||||
if(typeof person === 'string')
|
||||
{return person.includes(search);}
|
||||
function compileTextSearch(textSearch: string): (pkg: Package) => boolean {
|
||||
const personMatch = (person, search) => {
|
||||
if (typeof person === 'string') {
|
||||
return person.includes(search);
|
||||
}
|
||||
|
||||
if(typeof person === 'object')
|
||||
{for(const field of Object.values(person))
|
||||
{if(typeof field === 'string' && field.includes(search))
|
||||
{return true;}}}
|
||||
|
||||
return false;
|
||||
if (typeof person === 'object') {
|
||||
for (const field of Object.values(person)) {
|
||||
if (typeof field === 'string' && field.includes(search)) {
|
||||
return true;
|
||||
}
|
||||
const matcher = function(q) {
|
||||
const match = q.match(/author:(.*)/)
|
||||
if(match !== null)
|
||||
{return (pkg) => personMatch(pkg.author, match[1])}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: maintainer, keywords, not/is unstable insecure, boost-exact
|
||||
// TODO implement some scoring system for freetext
|
||||
return (pkg) => {
|
||||
return ['name', 'displayName', 'description']
|
||||
.map(k => pkg[k])
|
||||
.filter(x => x !== undefined)
|
||||
.some(txt => txt.includes(q))
|
||||
};
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const matcher = function (q) {
|
||||
const match = q.match(/author:(.*)/);
|
||||
if (match !== null) {
|
||||
return (pkg) => personMatch(pkg.author, match[1]);
|
||||
}
|
||||
|
||||
const textMatchers = (textSearch || '').split(' ').map(matcher);
|
||||
return (pkg) => textMatchers.every(m => m(pkg));
|
||||
// TODO: maintainer, keywords, not/is unstable insecure, boost-exact
|
||||
// TODO implement some scoring system for freetext
|
||||
return (pkg) => {
|
||||
return ['name', 'displayName', 'description']
|
||||
.map((k) => pkg[k])
|
||||
.filter((x) => x !== undefined)
|
||||
.some((txt) => txt.includes(q));
|
||||
};
|
||||
};
|
||||
|
||||
const textMatchers = (textSearch || '').split(' ').map(matcher);
|
||||
return (pkg) => textMatchers.every((m) => m(pkg));
|
||||
}
|
||||
|
||||
export default function(route, auth, storage): void {
|
||||
route.get('/-/v1/search', (req, res)=>{
|
||||
// TODO: implement proper result scoring weighted by quality, popularity and maintenance query parameters
|
||||
let [text, size, from /* , quality, popularity, maintenance */] =
|
||||
['text', 'size', 'from' /* , 'quality', 'popularity', 'maintenance' */]
|
||||
.map(k => req.query[k])
|
||||
export default function (route, auth, storage): void {
|
||||
route.get('/-/v1/search', (req, res) => {
|
||||
// TODO: implement proper result scoring weighted by quality, popularity and maintenance query parameters
|
||||
let [text, size, from /* , quality, popularity, maintenance */] = ['text', 'size', 'from' /* , 'quality', 'popularity', 'maintenance' */].map(
|
||||
(k) => req.query[k]
|
||||
);
|
||||
|
||||
size = parseInt(size) || 20;
|
||||
from = parseInt(from) || 0;
|
||||
size = parseInt(size) || 20;
|
||||
from = parseInt(from) || 0;
|
||||
|
||||
const isInteresting = compileTextSearch(text);
|
||||
const isInteresting = compileTextSearch(text);
|
||||
|
||||
const resultStream = storage.search(0, {req: {query: {local: true}}});
|
||||
const resultBuf = [] as any;
|
||||
let completed = false;
|
||||
const resultStream = storage.search(0, { req: { query: { local: true } } });
|
||||
const resultBuf = [] as any;
|
||||
let completed = false;
|
||||
|
||||
const sendResponse = (): void => {
|
||||
completed = true;
|
||||
resultStream.destroy()
|
||||
const sendResponse = (): void => {
|
||||
completed = true;
|
||||
resultStream.destroy();
|
||||
|
||||
const final = resultBuf.slice(from, size).map(pkg => {
|
||||
return {
|
||||
package: pkg,
|
||||
flags: {
|
||||
unstable:
|
||||
Object.keys(pkg.versions)
|
||||
.some(v => semver.satisfies(v, '^1.0.0'))
|
||||
? undefined
|
||||
: true
|
||||
},
|
||||
score: {
|
||||
final: 1,
|
||||
detail: {
|
||||
quality: 1,
|
||||
popularity: 1,
|
||||
maintenance: 0
|
||||
}
|
||||
},
|
||||
searchScore: 100000
|
||||
}
|
||||
})
|
||||
const response = {
|
||||
objects: final,
|
||||
total: final.length,
|
||||
time: new Date().toUTCString()
|
||||
}
|
||||
const final = resultBuf.slice(from, size).map((pkg) => {
|
||||
return {
|
||||
package: pkg,
|
||||
flags: {
|
||||
unstable: Object.keys(pkg.versions).some((v) => semver.satisfies(v, '^1.0.0')) ? undefined : true,
|
||||
},
|
||||
score: {
|
||||
final: 1,
|
||||
detail: {
|
||||
quality: 1,
|
||||
popularity: 1,
|
||||
maintenance: 0,
|
||||
},
|
||||
},
|
||||
searchScore: 100000,
|
||||
};
|
||||
});
|
||||
const response = {
|
||||
objects: final,
|
||||
total: final.length,
|
||||
time: new Date().toUTCString(),
|
||||
};
|
||||
|
||||
res.status(200)
|
||||
.json(response)
|
||||
}
|
||||
res.status(200).json(response);
|
||||
};
|
||||
|
||||
resultStream.on('data', (pkg)=>{
|
||||
if(!isInteresting(pkg))
|
||||
{return;}
|
||||
resultBuf.push(pkg)
|
||||
if(!completed && resultBuf.length >= size + from)
|
||||
{sendResponse();}
|
||||
})
|
||||
resultStream.on('end', ()=>{
|
||||
if(!completed)
|
||||
{sendResponse()}
|
||||
})
|
||||
})
|
||||
resultStream.on('data', (pkg) => {
|
||||
if (!isInteresting(pkg)) {
|
||||
return;
|
||||
}
|
||||
resultBuf.push(pkg);
|
||||
if (!completed && resultBuf.length >= size + from) {
|
||||
sendResponse();
|
||||
}
|
||||
});
|
||||
resultStream.on('end', () => {
|
||||
if (!completed) {
|
||||
sendResponse();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,122 +1,126 @@
|
||||
import _ from 'lodash';
|
||||
import { HTTP_STATUS, SUPPORT_ERRORS } from '@verdaccio/dev-commons';
|
||||
import {ErrorCode, stringToMD5, mask } from '@verdaccio/utils';
|
||||
import { ErrorCode, stringToMD5, mask } from '@verdaccio/utils';
|
||||
import { getApiToken } from '@verdaccio/auth';
|
||||
import { logger } from '@verdaccio/logger';
|
||||
import { Response, Router } from 'express';
|
||||
|
||||
import { Config, RemoteUser, Token } from '@verdaccio/types';
|
||||
import {$NextFunctionVer, $RequestExtend, IAuth, IStorageHandler} from '../../../types';
|
||||
import { $NextFunctionVer, $RequestExtend, IAuth, IStorageHandler } from '../../../types';
|
||||
|
||||
export type NormalizeToken = Token & {
|
||||
created: string;
|
||||
created: string;
|
||||
};
|
||||
|
||||
function normalizeToken(token: Token): NormalizeToken {
|
||||
return {
|
||||
...token,
|
||||
created: new Date(token.created).toISOString(),
|
||||
};
|
||||
};
|
||||
return {
|
||||
...token,
|
||||
created: new Date(token.created).toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/npm/npm-profile/blob/latest/lib/index.js
|
||||
export default function(route: Router, auth: IAuth, storage: IStorageHandler, config: Config): void {
|
||||
route.get('/-/npm/v1/tokens', async function(req: $RequestExtend, res: Response, next: $NextFunctionVer) {
|
||||
const { name } = req.remote_user;
|
||||
export default function (route: Router, auth: IAuth, storage: IStorageHandler, config: Config): void {
|
||||
route.get('/-/npm/v1/tokens', async function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {
|
||||
const { name } = req.remote_user;
|
||||
|
||||
if (_.isNil(name) === false) {
|
||||
try {
|
||||
const tokens = await storage.readTokens({user: name});
|
||||
const totalTokens = tokens.length;
|
||||
logger.debug({totalTokens}, 'token list retrieved: @{totalTokens}');
|
||||
if (_.isNil(name) === false) {
|
||||
try {
|
||||
const tokens = await storage.readTokens({ user: name });
|
||||
const totalTokens = tokens.length;
|
||||
logger.debug({ totalTokens }, 'token list retrieved: @{totalTokens}');
|
||||
|
||||
res.status(HTTP_STATUS.OK);
|
||||
return next({
|
||||
objects: tokens.map(normalizeToken),
|
||||
urls: {
|
||||
next: '', // TODO: pagination?
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error({ error: error.msg }, 'token list has failed: @{error}');
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
|
||||
}
|
||||
}
|
||||
return next(ErrorCode.getUnauthorized());
|
||||
});
|
||||
res.status(HTTP_STATUS.OK);
|
||||
return next({
|
||||
objects: tokens.map(normalizeToken),
|
||||
urls: {
|
||||
next: '', // TODO: pagination?
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error({ error: error.msg }, 'token list has failed: @{error}');
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
|
||||
}
|
||||
}
|
||||
return next(ErrorCode.getUnauthorized());
|
||||
});
|
||||
|
||||
route.post('/-/npm/v1/tokens', function(req: $RequestExtend, res: Response, next: $NextFunctionVer) {
|
||||
const { password, readonly, cidr_whitelist } = req.body;
|
||||
const { name } = req.remote_user;
|
||||
route.post('/-/npm/v1/tokens', function (req: $RequestExtend, res: Response, next: $NextFunctionVer) {
|
||||
const { password, readonly, cidr_whitelist } = req.body;
|
||||
const { name } = req.remote_user;
|
||||
|
||||
if (!_.isBoolean(readonly) || !_.isArray(cidr_whitelist)) {
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.BAD_DATA, SUPPORT_ERRORS.PARAMETERS_NOT_VALID));
|
||||
}
|
||||
if (!_.isBoolean(readonly) || !_.isArray(cidr_whitelist)) {
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.BAD_DATA, SUPPORT_ERRORS.PARAMETERS_NOT_VALID));
|
||||
}
|
||||
|
||||
auth.authenticate(name, password, async (err, user: RemoteUser) => {
|
||||
if (err) {
|
||||
const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;
|
||||
return next(ErrorCode.getCode(errorCode, err.message));
|
||||
}
|
||||
auth.authenticate(name, password, async (err, user: RemoteUser) => {
|
||||
if (err) {
|
||||
const errorCode = err.message ? HTTP_STATUS.UNAUTHORIZED : HTTP_STATUS.INTERNAL_ERROR;
|
||||
return next(ErrorCode.getCode(errorCode, err.message));
|
||||
}
|
||||
|
||||
req.remote_user = user;
|
||||
req.remote_user = user;
|
||||
|
||||
if (!_.isFunction(storage.saveToken)) {
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.NOT_IMPLEMENTED, SUPPORT_ERRORS.STORAGE_NOT_IMPLEMENT));
|
||||
}
|
||||
if (!_.isFunction(storage.saveToken)) {
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.NOT_IMPLEMENTED, SUPPORT_ERRORS.STORAGE_NOT_IMPLEMENT));
|
||||
}
|
||||
|
||||
try {
|
||||
const token = await getApiToken(auth, config, user, password);
|
||||
const key = stringToMD5(token);
|
||||
// TODO: use a utility here
|
||||
const maskedToken = mask(token, 5);
|
||||
const created = new Date().getTime();
|
||||
try {
|
||||
const token = await getApiToken(auth, config, user, password);
|
||||
const key = stringToMD5(token);
|
||||
// TODO: use a utility here
|
||||
const maskedToken = mask(token, 5);
|
||||
const created = new Date().getTime();
|
||||
|
||||
/**
|
||||
* cidr_whitelist: is not being used, we pass it through
|
||||
* token: we do not store the real token (it is generated once and retrieved to the user), just a mask of it.
|
||||
*/
|
||||
const saveToken: Token = {
|
||||
user: name,
|
||||
token: maskedToken,
|
||||
key,
|
||||
cidr: cidr_whitelist,
|
||||
readonly,
|
||||
created,
|
||||
};
|
||||
/**
|
||||
* cidr_whitelist: is not being used, we pass it through
|
||||
* token: we do not store the real token (it is generated once and retrieved to the user), just a mask of it.
|
||||
*/
|
||||
const saveToken: Token = {
|
||||
user: name,
|
||||
token: maskedToken,
|
||||
key,
|
||||
cidr: cidr_whitelist,
|
||||
readonly,
|
||||
created,
|
||||
};
|
||||
|
||||
await storage.saveToken(saveToken);
|
||||
logger.debug({ key, name }, 'token @{key} was created for user @{name}');
|
||||
return next(normalizeToken({
|
||||
token,
|
||||
user: name,
|
||||
key: saveToken.key,
|
||||
cidr: cidr_whitelist,
|
||||
readonly,
|
||||
created: saveToken.created,
|
||||
}));
|
||||
} catch (error) {
|
||||
logger.error({ error: error.msg }, 'token creation has failed: @{error}');
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
|
||||
}
|
||||
});
|
||||
});
|
||||
await storage.saveToken(saveToken);
|
||||
logger.debug({ key, name }, 'token @{key} was created for user @{name}');
|
||||
return next(
|
||||
normalizeToken({
|
||||
token,
|
||||
user: name,
|
||||
key: saveToken.key,
|
||||
cidr: cidr_whitelist,
|
||||
readonly,
|
||||
created: saveToken.created,
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error({ error: error.msg }, 'token creation has failed: @{error}');
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
route.delete('/-/npm/v1/tokens/token/:tokenKey', async (req: $RequestExtend, res: Response, next: $NextFunctionVer) => {
|
||||
const { params: { tokenKey }} = req;
|
||||
const { name } = req.remote_user;
|
||||
route.delete('/-/npm/v1/tokens/token/:tokenKey', async (req: $RequestExtend, res: Response, next: $NextFunctionVer) => {
|
||||
const {
|
||||
params: { tokenKey },
|
||||
} = req;
|
||||
const { name } = req.remote_user;
|
||||
|
||||
if (_.isNil(name) === false) {
|
||||
logger.debug({name}, '@{name} has requested remove a token');
|
||||
try {
|
||||
await storage.deleteToken(name, tokenKey);
|
||||
logger.info({ tokenKey, name }, 'token id @{tokenKey} was revoked for user @{name}');
|
||||
return next({});
|
||||
} catch(error) {
|
||||
logger.error({ error: error.msg }, 'token creation has failed: @{error}');
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
|
||||
}
|
||||
}
|
||||
return next(ErrorCode.getUnauthorized());
|
||||
});
|
||||
if (_.isNil(name) === false) {
|
||||
logger.debug({ name }, '@{name} has requested remove a token');
|
||||
try {
|
||||
await storage.deleteToken(name, tokenKey);
|
||||
logger.info({ tokenKey, name }, 'token id @{tokenKey} was revoked for user @{name}');
|
||||
return next({});
|
||||
} catch (error) {
|
||||
logger.error({ error: error.msg }, 'token creation has failed: @{error}');
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.INTERNAL_ERROR, error.message));
|
||||
}
|
||||
}
|
||||
return next(ErrorCode.getUnauthorized());
|
||||
});
|
||||
}
|
||||
|
@ -1,72 +1,72 @@
|
||||
import path from "path";
|
||||
import express, {Application} from 'express';
|
||||
import path from 'path';
|
||||
import express, { Application } from 'express';
|
||||
import supertest from 'supertest';
|
||||
import bodyParser from 'body-parser';
|
||||
|
||||
import {parseConfigFile} from '@verdaccio/utils';
|
||||
import { parseConfigFile } from '@verdaccio/utils';
|
||||
import { Config } from '@verdaccio/config';
|
||||
import { Storage } from '@verdaccio/store';
|
||||
import { final, handleError, errorReportingMiddleware } from '@verdaccio/middleware';
|
||||
import { Auth } from '@verdaccio/auth';
|
||||
import {IAuth} from "@verdaccio/dev-types";
|
||||
import {HEADER_TYPE, HTTP_STATUS, generatePackageMetadata} from "@verdaccio/dev-commons";
|
||||
import {HEADERS} from "@verdaccio/commons-api";
|
||||
import { IAuth } from '@verdaccio/dev-types';
|
||||
import { HEADER_TYPE, HTTP_STATUS, generatePackageMetadata } from '@verdaccio/dev-commons';
|
||||
import { HEADERS } from '@verdaccio/commons-api';
|
||||
import apiEndpoints from '../../src';
|
||||
|
||||
const getConf = (conf) => {
|
||||
const configPath = path.join(__dirname, 'config', conf);
|
||||
const configPath = path.join(__dirname, 'config', conf);
|
||||
|
||||
return parseConfigFile(configPath);
|
||||
return parseConfigFile(configPath);
|
||||
};
|
||||
|
||||
export async function initializeServer(configName): Promise<Application> {
|
||||
const app = express();
|
||||
const config = new Config(getConf(configName));
|
||||
const storage = new Storage(config);
|
||||
await storage.init(config, []);
|
||||
const auth: IAuth = new Auth(config);
|
||||
app.use(bodyParser.json({ strict: false, limit: '10mb' }));
|
||||
// @ts-ignore
|
||||
app.use(errorReportingMiddleware);
|
||||
// @ts-ignore
|
||||
app.use(apiEndpoints(config, auth, storage));
|
||||
// @ts-ignore
|
||||
app.use(handleError);
|
||||
// @ts-ignore
|
||||
app.use(final);
|
||||
const app = express();
|
||||
const config = new Config(getConf(configName));
|
||||
const storage = new Storage(config);
|
||||
await storage.init(config, []);
|
||||
const auth: IAuth = new Auth(config);
|
||||
app.use(bodyParser.json({ strict: false, limit: '10mb' }));
|
||||
// @ts-ignore
|
||||
app.use(errorReportingMiddleware);
|
||||
// @ts-ignore
|
||||
app.use(apiEndpoints(config, auth, storage));
|
||||
// @ts-ignore
|
||||
app.use(handleError);
|
||||
// @ts-ignore
|
||||
app.use(final);
|
||||
|
||||
app.use(function(request, response) {
|
||||
response.status(590);
|
||||
console.log('respo', response);
|
||||
response.json({error: 'cannot handle this'});
|
||||
});
|
||||
app.use(function (request, response) {
|
||||
response.status(590);
|
||||
console.log('respo', response);
|
||||
response.json({ error: 'cannot handle this' });
|
||||
});
|
||||
|
||||
return app;
|
||||
return app;
|
||||
}
|
||||
|
||||
export function publishVersion(app, configFile, pkgName, version): supertest.Test {
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, version);
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, version);
|
||||
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(pkgMetadata))
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(pkgMetadata))
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
||||
}
|
||||
|
||||
export async function publishTaggedVersion(app, configFile, pkgName: string, version: string, tag: string) {
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, version, {
|
||||
[tag]: version
|
||||
});
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, version, {
|
||||
[tag]: version,
|
||||
});
|
||||
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}/${encodeURIComponent(version)}/-tag/${encodeURIComponent(tag)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(pkgMetadata))
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}/${encodeURIComponent(version)}/-tag/${encodeURIComponent(tag)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(pkgMetadata))
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.set(HEADER_TYPE.ACCEPT_ENCODING, HEADERS.JSON)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
||||
}
|
||||
|
@ -1,83 +1,80 @@
|
||||
import supertest from 'supertest';
|
||||
|
||||
import { HTTP_STATUS } from '@verdaccio/commons-api';
|
||||
import {HEADER_TYPE, HEADERS} from '@verdaccio/dev-commons';
|
||||
import {$RequestExtend, $ResponseExtend} from "@verdaccio/dev-types";
|
||||
import {initializeServer, publishTaggedVersion, publishVersion} from './_helper';
|
||||
import { HEADER_TYPE, HEADERS } from '@verdaccio/dev-commons';
|
||||
import { $RequestExtend, $ResponseExtend } from '@verdaccio/dev-types';
|
||||
import { initializeServer, publishTaggedVersion, publishVersion } from './_helper';
|
||||
|
||||
const mockApiJWTmiddleware = jest.fn(() =>
|
||||
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: []}
|
||||
_next();
|
||||
}
|
||||
);
|
||||
const mockApiJWTmiddleware = jest.fn(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: [] };
|
||||
_next();
|
||||
});
|
||||
|
||||
jest.mock('@verdaccio/auth', () => ({
|
||||
Auth: class {
|
||||
apiJWTmiddleware() {
|
||||
return mockApiJWTmiddleware();
|
||||
}
|
||||
allow_access (_d, f_, cb) {
|
||||
cb(null, true)
|
||||
}
|
||||
allow_publish (_d, f_, cb) {
|
||||
cb(null, true)
|
||||
}
|
||||
}
|
||||
Auth: class {
|
||||
apiJWTmiddleware() {
|
||||
return mockApiJWTmiddleware();
|
||||
}
|
||||
allow_access(_d, f_, cb) {
|
||||
cb(null, true);
|
||||
}
|
||||
allow_publish(_d, f_, cb) {
|
||||
cb(null, true);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('package', () => {
|
||||
let app;
|
||||
beforeAll(async () => {
|
||||
app = await initializeServer('package.yaml');
|
||||
await publishVersion(app, 'package.yaml', 'foo', '1.0.0');
|
||||
});
|
||||
let app;
|
||||
beforeAll(async () => {
|
||||
app = await initializeServer('package.yaml');
|
||||
await publishVersion(app, 'package.yaml', 'foo', '1.0.0');
|
||||
});
|
||||
|
||||
test('should return a package', async (done) => {
|
||||
test('should return a package', async (done) => {
|
||||
return supertest(app)
|
||||
.get('/foo')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.then((response) => {
|
||||
expect(response.body.name).toEqual('foo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
return supertest(app)
|
||||
.get('/foo')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.then(response => {
|
||||
expect(response.body.name).toEqual('foo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
test('should return a package by version', async (done) => {
|
||||
return supertest(app)
|
||||
.get('/foo/1.0.0')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.then((response) => {
|
||||
expect(response.body.name).toEqual('foo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should return a package by version', async (done) => {
|
||||
return supertest(app)
|
||||
.get('/foo/1.0.0')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.then(response => {
|
||||
expect(response.body.name).toEqual('foo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
// TODO: investigate the 404
|
||||
test.skip('should return a package by dist-tag', async (done) => {
|
||||
await publishVersion(app, 'package.yaml', 'foo-tagged', '1.0.0');
|
||||
await publishTaggedVersion(app, 'package.yaml', 'foo-tagged', '1.0.1', 'test');
|
||||
return supertest(app)
|
||||
.get('/foo-tagged/1.0.0')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then((response) => {
|
||||
expect(response.body.name).toEqual('foo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: investigate the 404
|
||||
test.skip('should return a package by dist-tag', async (done) => {
|
||||
await publishVersion(app, 'package.yaml', 'foo-tagged', '1.0.0');
|
||||
await publishTaggedVersion(app, 'package.yaml', 'foo-tagged', '1.0.1', 'test');
|
||||
return supertest(app)
|
||||
.get('/foo-tagged/1.0.0')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then(response => {
|
||||
expect(response.body.name).toEqual('foo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should return 404', async () => {
|
||||
return supertest(app)
|
||||
.get('/404-not-found')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.NOT_FOUND);
|
||||
});
|
||||
test('should return 404', async () => {
|
||||
return supertest(app)
|
||||
.get('/404-not-found')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.NOT_FOUND);
|
||||
});
|
||||
});
|
||||
|
@ -1,16 +1,16 @@
|
||||
import supertest from 'supertest';
|
||||
|
||||
import { HTTP_STATUS } from '@verdaccio/commons-api';
|
||||
import {HEADER_TYPE, HEADERS} from '@verdaccio/dev-commons';
|
||||
import {initializeServer } from './_helper';
|
||||
import { HEADER_TYPE, HEADERS } from '@verdaccio/dev-commons';
|
||||
import { initializeServer } from './_helper';
|
||||
|
||||
describe('ping', () => {
|
||||
test('should return the reply the ping', async () => {
|
||||
return supertest(await initializeServer('ping.yaml'))
|
||||
.get('/-/ping')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.then(response => expect(response.body).toEqual({}));
|
||||
});
|
||||
test('should return the reply the ping', async () => {
|
||||
return supertest(await initializeServer('ping.yaml'))
|
||||
.get('/-/ping')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.then((response) => expect(response.body).toEqual({}));
|
||||
});
|
||||
});
|
||||
|
@ -1,34 +1,32 @@
|
||||
import { HTTP_STATUS } from '@verdaccio/commons-api';
|
||||
import {API_ERROR, API_MESSAGE, generatePackageMetadata, HEADER_TYPE, HEADERS} from '@verdaccio/dev-commons';
|
||||
import {$RequestExtend, $ResponseExtend} from '@verdaccio/dev-types';
|
||||
import supertest from "supertest";
|
||||
import {initializeServer, publishVersion} from './_helper';
|
||||
import { API_ERROR, API_MESSAGE, generatePackageMetadata, HEADER_TYPE, HEADERS } from '@verdaccio/dev-commons';
|
||||
import { $RequestExtend, $ResponseExtend } from '@verdaccio/dev-types';
|
||||
import supertest from 'supertest';
|
||||
import { initializeServer, publishVersion } from './_helper';
|
||||
|
||||
const mockApiJWTmiddleware = jest.fn(() =>
|
||||
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: []}
|
||||
_next();
|
||||
}
|
||||
);
|
||||
const mockApiJWTmiddleware = jest.fn(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: [] };
|
||||
_next();
|
||||
});
|
||||
|
||||
jest.setTimeout(50000000);
|
||||
|
||||
jest.mock('@verdaccio/auth', () => ({
|
||||
Auth: class {
|
||||
apiJWTmiddleware() {
|
||||
return mockApiJWTmiddleware();
|
||||
}
|
||||
allow_access (_d, f_, cb) {
|
||||
cb(null, true)
|
||||
}
|
||||
allow_publish (_d, f_, cb) {
|
||||
cb(null, true)
|
||||
}
|
||||
Auth: class {
|
||||
apiJWTmiddleware() {
|
||||
return mockApiJWTmiddleware();
|
||||
}
|
||||
allow_access(_d, f_, cb) {
|
||||
cb(null, true);
|
||||
}
|
||||
allow_publish(_d, f_, cb) {
|
||||
cb(null, true);
|
||||
}
|
||||
|
||||
allow_unpublish (_d, f_, cb) {
|
||||
cb(null, true)
|
||||
}
|
||||
}
|
||||
allow_unpublish(_d, f_, cb) {
|
||||
cb(null, true);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
// const mockStorage = jest.fn(() => {
|
||||
@ -55,125 +53,134 @@ jest.mock('@verdaccio/auth', () => ({
|
||||
// });
|
||||
|
||||
describe('publish', () => {
|
||||
describe('handle invalid publish formats', () => {
|
||||
const pkgName = 'test';
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
test.skip('should fail on publish a bad _attachments package', async (done) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(Object.assign({}, pkgMetadata, {
|
||||
_attachments: {}
|
||||
})))
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.BAD_REQUEST)
|
||||
.then(response => {
|
||||
console.log("response.body", response.body);
|
||||
expect(response.body.error).toEqual(API_ERROR.UNSUPORTED_REGISTRY_CALL);
|
||||
done();
|
||||
});
|
||||
});
|
||||
describe('handle invalid publish formats', () => {
|
||||
const pkgName = 'test';
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
test.skip('should fail on publish a bad _attachments package', async (done) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(
|
||||
JSON.stringify(
|
||||
Object.assign({}, pkgMetadata, {
|
||||
_attachments: {},
|
||||
})
|
||||
)
|
||||
)
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.BAD_REQUEST)
|
||||
.then((response) => {
|
||||
console.log('response.body', response.body);
|
||||
expect(response.body.error).toEqual(API_ERROR.UNSUPORTED_REGISTRY_CALL);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should fail on publish a bad versions package', async (done) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(Object.assign({}, pkgMetadata, {
|
||||
versions: ''
|
||||
})))
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.BAD_REQUEST)
|
||||
.then(response => {
|
||||
console.log("response.body", response.body);
|
||||
expect(response.body.error).toEqual(API_ERROR.UNSUPORTED_REGISTRY_CALL);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
test('should fail on publish a bad versions package', async (done) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(
|
||||
JSON.stringify(
|
||||
Object.assign({}, pkgMetadata, {
|
||||
versions: '',
|
||||
})
|
||||
)
|
||||
)
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.BAD_REQUEST)
|
||||
.then((response) => {
|
||||
console.log('response.body', response.body);
|
||||
expect(response.body.error).toEqual(API_ERROR.UNSUPORTED_REGISTRY_CALL);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('publish a package', () => {
|
||||
test('should publish a package', async (done) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return publishVersion(app, 'publish.yaml', 'foo', '1.0.0')
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then(response => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
describe('publish a package', () => {
|
||||
test('should publish a package', async (done) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return publishVersion(app, 'publish.yaml', 'foo', '1.0.0')
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then((response) => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should publish a new package', async (done) => {
|
||||
const pkgName = 'test';
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(Object.assign({}, pkgMetadata, {
|
||||
_attachments: null
|
||||
})))
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then(response => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
test('should publish a new package', async (done) => {
|
||||
const pkgName = 'test';
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(
|
||||
JSON.stringify(
|
||||
Object.assign({}, pkgMetadata, {
|
||||
_attachments: null,
|
||||
})
|
||||
)
|
||||
)
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then((response) => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should publish a new package with no readme', async (done) => {
|
||||
const pkgName = 'test';
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(Object.assign({}, pkgMetadata, {
|
||||
versions: {
|
||||
['1.0.0'] : {
|
||||
readme: null
|
||||
}
|
||||
}
|
||||
})))
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then(response => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
test('should publish a new package with no readme', async (done) => {
|
||||
const pkgName = 'test';
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(
|
||||
JSON.stringify(
|
||||
Object.assign({}, pkgMetadata, {
|
||||
versions: {
|
||||
['1.0.0']: {
|
||||
readme: null,
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
)
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then((response) => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
test('should fails on publish a duplicated package', async (done) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
await publishVersion(app, 'publish.yaml', 'foo', '1.0.0');
|
||||
return publishVersion(app, 'publish.yaml', 'foo', '1.0.0')
|
||||
.expect(HTTP_STATUS.CONFLICT)
|
||||
.then((response) => {
|
||||
console.log('response.body', response.body);
|
||||
expect(response.body.error).toEqual(API_ERROR.PACKAGE_EXIST);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('unpublish a package', () => {
|
||||
let app;
|
||||
|
||||
test('should fails on publish a duplicated package', async (done) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
await publishVersion(app, 'publish.yaml', 'foo', '1.0.0');
|
||||
return publishVersion(app, 'publish.yaml', 'foo', '1.0.0')
|
||||
.expect(HTTP_STATUS.CONFLICT)
|
||||
.then(response => {
|
||||
console.log("response.body", response.body);
|
||||
expect(response.body.error).toEqual(API_ERROR.PACKAGE_EXIST);
|
||||
done();
|
||||
});
|
||||
});
|
||||
beforeEach(async () => {
|
||||
app = await initializeServer('publish.yaml');
|
||||
await publishVersion(app, 'publish.yaml', 'foo', '1.0.0');
|
||||
});
|
||||
|
||||
describe('unpublish a package', () => {
|
||||
let app;
|
||||
|
||||
beforeEach(async () => {
|
||||
app = await initializeServer('publish.yaml');
|
||||
await publishVersion(app, 'publish.yaml', 'foo', '1.0.0');
|
||||
})
|
||||
|
||||
test('should unpublish a package', () => {
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
describe('star a package', () => {
|
||||
|
||||
});
|
||||
test('should unpublish a package', () => {});
|
||||
});
|
||||
|
||||
describe('star a package', () => {});
|
||||
});
|
||||
|
@ -1,237 +1,216 @@
|
||||
import supertest from 'supertest';
|
||||
|
||||
import { HTTP_STATUS, API_ERROR } from '@verdaccio/commons-api';
|
||||
import {HEADERS, HEADER_TYPE, API_MESSAGE} from '@verdaccio/dev-commons';
|
||||
import {$RequestExtend, $ResponseExtend} from "@verdaccio/dev-types";
|
||||
import {getBadRequest, getConflict, getUnauthorized} from "@verdaccio/commons-api/lib";
|
||||
import _ from "lodash";
|
||||
import { HEADERS, HEADER_TYPE, API_MESSAGE } from '@verdaccio/dev-commons';
|
||||
import { $RequestExtend, $ResponseExtend } from '@verdaccio/dev-types';
|
||||
import { getBadRequest, getConflict, getUnauthorized } from '@verdaccio/commons-api/lib';
|
||||
import _ from 'lodash';
|
||||
import { initializeServer } from './_helper';
|
||||
|
||||
const mockApiJWTmiddleware = jest.fn(() =>
|
||||
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test', groups: [], real_groups: []};
|
||||
_next();
|
||||
}
|
||||
);
|
||||
const mockApiJWTmiddleware = jest.fn(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test', groups: [], real_groups: [] };
|
||||
_next();
|
||||
});
|
||||
|
||||
const mockAuthenticate = jest.fn(() => (_name, _password, callback): void => {
|
||||
return callback(null, ['all']);
|
||||
}
|
||||
);
|
||||
return callback(null, ['all']);
|
||||
});
|
||||
|
||||
const mockAddUser = jest.fn(() => (_name, _password, callback): void => {
|
||||
return callback(getConflict(API_ERROR.USERNAME_ALREADY_REGISTERED));
|
||||
}
|
||||
);
|
||||
return callback(getConflict(API_ERROR.USERNAME_ALREADY_REGISTERED));
|
||||
});
|
||||
|
||||
jest.mock('@verdaccio/auth', () => ({
|
||||
getApiToken: () => 'token',
|
||||
Auth: class {
|
||||
apiJWTmiddleware() {
|
||||
return mockApiJWTmiddleware();
|
||||
}
|
||||
allow_access (_d, f_, cb) {
|
||||
cb(null, true);
|
||||
}
|
||||
add_user (name, password, callback) {
|
||||
mockAddUser()(name, password, callback);
|
||||
}
|
||||
authenticate (_name, _password, callback) {
|
||||
mockAuthenticate()(_name, _password, callback);
|
||||
}
|
||||
}
|
||||
getApiToken: () => 'token',
|
||||
Auth: class {
|
||||
apiJWTmiddleware() {
|
||||
return mockApiJWTmiddleware();
|
||||
}
|
||||
allow_access(_d, f_, cb) {
|
||||
cb(null, true);
|
||||
}
|
||||
add_user(name, password, callback) {
|
||||
mockAddUser()(name, password, callback);
|
||||
}
|
||||
authenticate(_name, _password, callback) {
|
||||
mockAuthenticate()(_name, _password, callback);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('user', () => {
|
||||
const credentials = { name: 'test', password: 'test' };
|
||||
const credentials = { name: 'test', password: 'test' };
|
||||
|
||||
test('should test add a new user', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() =>
|
||||
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined};
|
||||
_next();
|
||||
}
|
||||
);
|
||||
test('should test add a new user', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
});
|
||||
|
||||
mockAddUser.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(null, true);
|
||||
}
|
||||
);
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put(`/-/user/org.couchdb.user:newUser`)
|
||||
.send({
|
||||
name: 'newUser',
|
||||
password: 'newUser'
|
||||
})
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
expect(res.body.ok).toBeDefined();
|
||||
expect(res.body.token).toBeDefined();
|
||||
const token = res.body.token;
|
||||
expect(typeof token).toBe('string');
|
||||
expect(res.body.ok).toMatch(`user 'newUser' created`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
mockAddUser.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(null, true);
|
||||
});
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put(`/-/user/org.couchdb.user:newUser`)
|
||||
.send({
|
||||
name: 'newUser',
|
||||
password: 'newUser',
|
||||
})
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
expect(res.body.ok).toBeDefined();
|
||||
expect(res.body.token).toBeDefined();
|
||||
const token = res.body.token;
|
||||
expect(typeof token).toBe('string');
|
||||
expect(res.body.ok).toMatch(`user 'newUser' created`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should test fails on add a existing user with login', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() =>
|
||||
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined};
|
||||
_next();
|
||||
}
|
||||
);
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put('/-/user/org.couchdb.user:jotaNew')
|
||||
.send(credentials)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CONFLICT)
|
||||
.end(function(err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(API_ERROR.USERNAME_ALREADY_REGISTERED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
test('should test fails on add a existing user with login', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
});
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put('/-/user/org.couchdb.user:jotaNew')
|
||||
.send(credentials)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CONFLICT)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(API_ERROR.USERNAME_ALREADY_REGISTERED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should log in as existing user', async (done) => {
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put(`/-/user/org.couchdb.user:${credentials.name}`)
|
||||
.send(credentials)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end((err, res) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
test('should log in as existing user', async (done) => {
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put(`/-/user/org.couchdb.user:${credentials.name}`)
|
||||
.send(credentials)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end((err, res) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(res.body).toBeTruthy();
|
||||
expect(res.body.ok).toMatch(`you are authenticated as \'${credentials.name}\'`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
expect(res.body).toBeTruthy();
|
||||
expect(res.body.ok).toMatch(`you are authenticated as \'${credentials.name}\'`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should test fails add a new user with missing name', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() =>
|
||||
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
}
|
||||
);
|
||||
mockAddUser.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(getBadRequest(API_ERROR.USERNAME_PASSWORD_REQUIRED));
|
||||
}
|
||||
);
|
||||
const credentialsShort = _.cloneDeep(credentials);
|
||||
delete credentialsShort.name;
|
||||
test('should test fails add a new user with missing name', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
});
|
||||
mockAddUser.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(getBadRequest(API_ERROR.USERNAME_PASSWORD_REQUIRED));
|
||||
});
|
||||
const credentialsShort = _.cloneDeep(credentials);
|
||||
delete credentialsShort.name;
|
||||
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put(`/-/user/org.couchdb.user:${credentials.name}`)
|
||||
.send(credentialsShort)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.BAD_REQUEST)
|
||||
.end(function(err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put(`/-/user/org.couchdb.user:${credentials.name}`)
|
||||
.send(credentialsShort)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.BAD_REQUEST)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(API_ERROR.USERNAME_PASSWORD_REQUIRED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(API_ERROR.USERNAME_PASSWORD_REQUIRED);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should test fails add a new user with missing password', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() =>
|
||||
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined};
|
||||
_next();
|
||||
}
|
||||
);
|
||||
const credentialsShort = _.cloneDeep(credentials);
|
||||
delete credentialsShort.password;
|
||||
test('should test fails add a new user with missing password', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: undefined };
|
||||
_next();
|
||||
});
|
||||
const credentialsShort = _.cloneDeep(credentials);
|
||||
delete credentialsShort.password;
|
||||
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put(`/-/user/org.couchdb.user:${credentials.name}`)
|
||||
.send(credentialsShort)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.BAD_REQUEST)
|
||||
.end(function(err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put(`/-/user/org.couchdb.user:${credentials.name}`)
|
||||
.send(credentialsShort)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.BAD_REQUEST)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(res.body.error).toBeDefined();
|
||||
// FIXME: message is not 100% accurate
|
||||
// eslint-disable-next-line new-cap
|
||||
expect(res.body.error).toMatch(API_ERROR.PASSWORD_SHORT());
|
||||
done();
|
||||
});
|
||||
});
|
||||
expect(res.body.error).toBeDefined();
|
||||
// FIXME: message is not 100% accurate
|
||||
// eslint-disable-next-line new-cap
|
||||
expect(res.body.error).toMatch(API_ERROR.PASSWORD_SHORT());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should test fails add a new user with wrong password', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() =>
|
||||
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test'};
|
||||
_next();
|
||||
}
|
||||
);
|
||||
mockAuthenticate.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
|
||||
}
|
||||
);
|
||||
const credentialsShort = _.cloneDeep(credentials);
|
||||
credentialsShort.password = 'failPassword';
|
||||
test('should test fails add a new user with wrong password', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test' };
|
||||
_next();
|
||||
});
|
||||
mockAuthenticate.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
|
||||
});
|
||||
const credentialsShort = _.cloneDeep(credentials);
|
||||
credentialsShort.password = 'failPassword';
|
||||
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put('/-/user/org.couchdb.user:test')
|
||||
.send(credentialsShort)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.UNAUTHORIZED)
|
||||
.end(function(err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.put('/-/user/org.couchdb.user:test')
|
||||
.send(credentialsShort)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.UNAUTHORIZED)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(API_ERROR.BAD_USERNAME_PASSWORD);
|
||||
done();
|
||||
});
|
||||
});
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(API_ERROR.BAD_USERNAME_PASSWORD);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should be able to logout an user', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() =>
|
||||
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test'};
|
||||
_next();
|
||||
}
|
||||
);
|
||||
mockAuthenticate.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
|
||||
}
|
||||
);
|
||||
const credentialsShort = _.cloneDeep(credentials);
|
||||
credentialsShort.password = 'failPassword';
|
||||
test('should be able to logout an user', async (done) => {
|
||||
mockApiJWTmiddleware.mockImplementationOnce(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'test' };
|
||||
_next();
|
||||
});
|
||||
mockAuthenticate.mockImplementationOnce(() => (_name, _password, callback): void => {
|
||||
return callback(getUnauthorized(API_ERROR.BAD_USERNAME_PASSWORD));
|
||||
});
|
||||
const credentialsShort = _.cloneDeep(credentials);
|
||||
credentialsShort.password = 'failPassword';
|
||||
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.delete('/-/user/token/someSecretToken')
|
||||
.send(credentialsShort)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(res.body.ok).toMatch(API_MESSAGE.LOGGED_OUT);
|
||||
done();
|
||||
});
|
||||
});
|
||||
supertest(await initializeServer('user.yaml'))
|
||||
.delete('/-/user/token/someSecretToken')
|
||||
.send(credentialsShort)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(res.body.ok).toMatch(API_MESSAGE.LOGGED_OUT);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,53 +1,50 @@
|
||||
import supertest from 'supertest';
|
||||
|
||||
import { HTTP_STATUS } from '@verdaccio/commons-api';
|
||||
import { HEADERS} from '@verdaccio/dev-commons';
|
||||
import {$RequestExtend, $ResponseExtend} from "@verdaccio/dev-types";
|
||||
import {initializeServer } from './_helper';
|
||||
import { HEADERS } from '@verdaccio/dev-commons';
|
||||
import { $RequestExtend, $ResponseExtend } from '@verdaccio/dev-types';
|
||||
import { initializeServer } from './_helper';
|
||||
|
||||
const mockApiJWTmiddleware = jest.fn(() =>
|
||||
(req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: []}
|
||||
_next();
|
||||
}
|
||||
);
|
||||
const mockApiJWTmiddleware = jest.fn(() => (req: $RequestExtend, res: $ResponseExtend, _next): void => {
|
||||
req.remote_user = { name: 'foo', groups: [], real_groups: [] };
|
||||
_next();
|
||||
});
|
||||
|
||||
jest.mock('@verdaccio/auth', () => ({
|
||||
Auth: class {
|
||||
apiJWTmiddleware() {
|
||||
return mockApiJWTmiddleware();
|
||||
}
|
||||
allow_access (_d, f_, cb) {
|
||||
cb(null, true)
|
||||
}
|
||||
}
|
||||
Auth: class {
|
||||
apiJWTmiddleware() {
|
||||
return mockApiJWTmiddleware();
|
||||
}
|
||||
allow_access(_d, f_, cb) {
|
||||
cb(null, true);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('whoami', () => {
|
||||
test.skip('should test referer /whoami endpoint', async (done) => {
|
||||
return supertest(await initializeServer('whoami.yaml'))
|
||||
.get('/whoami')
|
||||
.set('referer', 'whoami')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(done);
|
||||
});
|
||||
test.skip('should test referer /whoami endpoint', async (done) => {
|
||||
return supertest(await initializeServer('whoami.yaml'))
|
||||
.get('/whoami')
|
||||
.set('referer', 'whoami')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(done);
|
||||
});
|
||||
|
||||
test.skip('should test no referer /whoami endpoint', async (done) => {
|
||||
return supertest(await initializeServer('whoami.yaml'))
|
||||
.get('/whoami')
|
||||
.expect(HTTP_STATUS.NOT_FOUND)
|
||||
.end(done);
|
||||
});
|
||||
test.skip('should test no referer /whoami endpoint', async (done) => {
|
||||
return supertest(await initializeServer('whoami.yaml'))
|
||||
.get('/whoami')
|
||||
.expect(HTTP_STATUS.NOT_FOUND)
|
||||
.end(done);
|
||||
});
|
||||
|
||||
|
||||
test('should return the logged username', async () => {
|
||||
return supertest(await initializeServer('whoami.yaml'))
|
||||
.get('/-/whoami')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect('Content-Type', HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.then(response => {
|
||||
expect(response.body.username).toEqual('foo');
|
||||
});
|
||||
});
|
||||
test('should return the logged username', async () => {
|
||||
return supertest(await initializeServer('whoami.yaml'))
|
||||
.get('/-/whoami')
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect('Content-Type', HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.then((response) => {
|
||||
expect(response.body.username).toEqual('foo');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -3,9 +3,7 @@ import { addVersion, uploadPackageTarball, removeTarball, unPublishPackage, publ
|
||||
|
||||
const REVISION_MOCK = '15-e53a77096b0ee33e';
|
||||
|
||||
require('@verdaccio/logger').setup([
|
||||
{ type: 'stdout', format: 'pretty', level: 'info' }
|
||||
]);
|
||||
require('@verdaccio/logger').setup([{ type: 'stdout', format: 'pretty', level: 'info' }]);
|
||||
|
||||
describe('Publish endpoints - add a tag', () => {
|
||||
let req;
|
||||
@ -28,7 +26,7 @@ describe('Publish endpoints - add a tag', () => {
|
||||
next = jest.fn();
|
||||
});
|
||||
|
||||
test('should add a version', done => {
|
||||
test('should add a version', (done) => {
|
||||
const storage = {
|
||||
addVersion: (packageName, version, body, tag, cb) => {
|
||||
expect(packageName).toEqual(req.params.package);
|
||||
@ -47,7 +45,7 @@ describe('Publish endpoints - add a tag', () => {
|
||||
expect(next).toHaveBeenLastCalledWith({ ok: 'package published' });
|
||||
});
|
||||
|
||||
test('when failed to add a version', done => {
|
||||
test('when failed to add a version', (done) => {
|
||||
const storage = {
|
||||
addVersion: (packageName, version, body, tag, cb) => {
|
||||
const error = {
|
||||
@ -82,7 +80,7 @@ describe('Publish endpoints - upload package tarball', () => {
|
||||
pipe: jest.fn(),
|
||||
on: jest.fn(),
|
||||
};
|
||||
res = { status: jest.fn(), locals: { report_error: jest.fn() }};
|
||||
res = { status: jest.fn(), locals: { report_error: jest.fn() } };
|
||||
next = jest.fn();
|
||||
});
|
||||
|
||||
@ -127,7 +125,7 @@ describe('Publish endpoints - delete tarball', () => {
|
||||
next = jest.fn();
|
||||
});
|
||||
|
||||
test('should delete tarball successfully', done => {
|
||||
test('should delete tarball successfully', (done) => {
|
||||
const storage = {
|
||||
removeTarball(packageName, filename, revision, cb) {
|
||||
expect(packageName).toEqual(req.params.package);
|
||||
@ -144,7 +142,7 @@ describe('Publish endpoints - delete tarball', () => {
|
||||
expect(next).toHaveBeenCalledWith({ ok: 'tarball removed' });
|
||||
});
|
||||
|
||||
test('failed while deleting the tarball', done => {
|
||||
test('failed while deleting the tarball', (done) => {
|
||||
const error = {
|
||||
message: 'deletion failed',
|
||||
};
|
||||
@ -179,7 +177,7 @@ describe('Publish endpoints - un-publish package', () => {
|
||||
next = jest.fn();
|
||||
});
|
||||
|
||||
test('should un-publish package successfully', done => {
|
||||
test('should un-publish package successfully', (done) => {
|
||||
const storage = {
|
||||
removePackage(packageName, cb) {
|
||||
expect(packageName).toEqual(req.params.package);
|
||||
@ -194,7 +192,7 @@ describe('Publish endpoints - un-publish package', () => {
|
||||
expect(next).toHaveBeenCalledWith({ ok: 'package removed' });
|
||||
});
|
||||
|
||||
test('un-publish failed', done => {
|
||||
test('un-publish failed', (done) => {
|
||||
const error = {
|
||||
message: 'un-publish failed',
|
||||
};
|
||||
|
@ -2,27 +2,15 @@ import _ from 'lodash';
|
||||
import { NextFunction } from 'express';
|
||||
|
||||
import { VerdaccioError, getBadRequest, getInternalError, getForbidden } from '@verdaccio/commons-api';
|
||||
import {API_ERROR, SUPPORT_ERRORS, TOKEN_BASIC, TOKEN_BEARER} from '@verdaccio/dev-commons';
|
||||
import { API_ERROR, SUPPORT_ERRORS, TOKEN_BASIC, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
import { loadPlugin } from '@verdaccio/loaders';
|
||||
import { aesEncrypt, signPayload } from '@verdaccio/utils';
|
||||
import {
|
||||
getDefaultPlugins,
|
||||
createAnonymousRemoteUser,
|
||||
convertPayloadToBase64,
|
||||
createRemoteUser,
|
||||
} from '@verdaccio/utils';
|
||||
import { getDefaultPlugins, createAnonymousRemoteUser, convertPayloadToBase64, createRemoteUser } from '@verdaccio/utils';
|
||||
|
||||
import { getMatchedPackagesSpec } from '@verdaccio/utils';
|
||||
import { Config, Logger, Callback, IPluginAuth, RemoteUser, JWTSignOptions, Security, AuthPluginPackage, AllowAccess, PackageAccess } from '@verdaccio/types';
|
||||
import { $RequestExtend, $ResponseExtend, IAuth, AESPayload } from '@verdaccio/dev-types';
|
||||
import {
|
||||
getMiddlewareCredentials,
|
||||
getSecurity,
|
||||
verifyJWTPayload,
|
||||
parseBasicPayload,
|
||||
parseAuthTokenHeader,
|
||||
isAuthHeaderValid,
|
||||
isAESLegacy } from "./utils";
|
||||
import { getMiddlewareCredentials, getSecurity, verifyJWTPayload, parseBasicPayload, parseAuthTokenHeader, isAuthHeaderValid, isAESLegacy } from './utils';
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const LoggerApi = require('@verdaccio/logger');
|
||||
@ -47,17 +35,12 @@ class Auth implements IAuth {
|
||||
logger: this.logger,
|
||||
};
|
||||
|
||||
return loadPlugin<IPluginAuth<Config>>(
|
||||
config,
|
||||
config.auth,
|
||||
pluginOptions,
|
||||
(plugin: IPluginAuth<Config>): boolean => {
|
||||
const { authenticate, allow_access, allow_publish } = plugin;
|
||||
return loadPlugin<IPluginAuth<Config>>(config, config.auth, pluginOptions, (plugin: IPluginAuth<Config>): boolean => {
|
||||
const { authenticate, allow_access, allow_publish } = plugin;
|
||||
|
||||
// @ts-ignore
|
||||
return authenticate || allow_access || allow_publish;
|
||||
}
|
||||
);
|
||||
// @ts-ignore
|
||||
return authenticate || allow_access || allow_publish;
|
||||
});
|
||||
}
|
||||
|
||||
private _applyDefaultPlugins(): void {
|
||||
@ -65,39 +48,33 @@ class Auth implements IAuth {
|
||||
}
|
||||
|
||||
public changePassword(username: string, password: string, newPassword: string, cb: Callback): void {
|
||||
const validPlugins = _.filter(this.plugins, plugin => _.isFunction(plugin.changePassword));
|
||||
const validPlugins = _.filter(this.plugins, (plugin) => _.isFunction(plugin.changePassword));
|
||||
|
||||
if (_.isEmpty(validPlugins)) {
|
||||
return cb(
|
||||
getInternalError(SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));
|
||||
}
|
||||
if (_.isEmpty(validPlugins)) {
|
||||
return cb(getInternalError(SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE));
|
||||
}
|
||||
|
||||
for (const plugin of validPlugins) {
|
||||
if (_.isNil(plugin) || _.isFunction(plugin.changePassword) === false) {
|
||||
this.logger.trace('auth plugin does not implement changePassword, trying next one');
|
||||
continue;
|
||||
} else {
|
||||
this.logger.trace({username}, 'updating password for @{username}');
|
||||
plugin.changePassword!(
|
||||
username,
|
||||
password,
|
||||
newPassword,
|
||||
(err, profile): void => {
|
||||
if (err) {
|
||||
this.logger.error(
|
||||
{username, err},
|
||||
`An error has been produced
|
||||
for (const plugin of validPlugins) {
|
||||
if (_.isNil(plugin) || _.isFunction(plugin.changePassword) === false) {
|
||||
this.logger.trace('auth plugin does not implement changePassword, trying next one');
|
||||
continue;
|
||||
} else {
|
||||
this.logger.trace({ username }, 'updating password for @{username}');
|
||||
plugin.changePassword!(username, password, newPassword, (err, profile): void => {
|
||||
if (err) {
|
||||
this.logger.error(
|
||||
{ username, err },
|
||||
`An error has been produced
|
||||
updating the password for @{username}. Error: @{err.message}`
|
||||
);
|
||||
return cb(err);
|
||||
}
|
||||
);
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
this.logger.trace({username}, 'updated password for @{username} was successful');
|
||||
return cb(null, profile);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
this.logger.trace({ username }, 'updated password for @{username} was successful');
|
||||
return cb(null, profile);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public authenticate(username: string, password: string, cb: Callback): void {
|
||||
@ -111,7 +88,7 @@ class Auth implements IAuth {
|
||||
}
|
||||
|
||||
self.logger.trace({ username }, 'authenticating @{username}');
|
||||
plugin.authenticate(username, password, function(err, groups): void {
|
||||
plugin.authenticate(username, password, function (err, groups): void {
|
||||
if (err) {
|
||||
self.logger.trace({ username, err }, 'authenticating for user @{username} failed. Error: @{err.message}');
|
||||
return cb(err);
|
||||
@ -159,7 +136,7 @@ class Auth implements IAuth {
|
||||
next();
|
||||
} else {
|
||||
// p.add_user() execution
|
||||
plugin[method](user, password, function(err, ok): void {
|
||||
plugin[method](user, password, function (err, ok): void {
|
||||
if (err) {
|
||||
self.logger.trace({ user, err: err.message }, 'the user @{user} could not being added. Error: @{err}');
|
||||
return cb(err);
|
||||
@ -191,7 +168,7 @@ class Auth implements IAuth {
|
||||
return next();
|
||||
}
|
||||
|
||||
plugin.allow_access!(user, pkg, function(err, ok: boolean): void {
|
||||
plugin.allow_access!(user, pkg, function (err, ok: boolean): void {
|
||||
if (err) {
|
||||
self.logger.trace({ packageName, err }, 'forbidden access for @{packageName}. Error: @{err.message}');
|
||||
return callback(err);
|
||||
@ -216,28 +193,24 @@ class Auth implements IAuth {
|
||||
this.logger.trace({ packageName }, 'allow unpublish for @{packageName} plugin does not implement allow_unpublish');
|
||||
continue;
|
||||
} else {
|
||||
plugin.allow_unpublish!(
|
||||
user,
|
||||
pkg,
|
||||
(err, ok: boolean): void => {
|
||||
if (err) {
|
||||
this.logger.trace({ packageName }, 'forbidden publish for @{packageName}, it will fallback on unpublish permissions');
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (_.isNil(ok) === true) {
|
||||
this.logger.trace({ packageName }, 'we bypass unpublish for @{packageName}, publish will handle the access');
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line
|
||||
return this.allow_publish(...arguments);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
this.logger.trace({ packageName }, 'allowed unpublish for @{packageName}');
|
||||
return callback(null, ok);
|
||||
}
|
||||
plugin.allow_unpublish!(user, pkg, (err, ok: boolean): void => {
|
||||
if (err) {
|
||||
this.logger.trace({ packageName }, 'forbidden publish for @{packageName}, it will fallback on unpublish permissions');
|
||||
return callback(err);
|
||||
}
|
||||
);
|
||||
|
||||
if (_.isNil(ok) === true) {
|
||||
this.logger.trace({ packageName }, 'we bypass unpublish for @{packageName}, publish will handle the access');
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line
|
||||
return this.allow_publish(...arguments);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
this.logger.trace({ packageName }, 'allowed unpublish for @{packageName}');
|
||||
return callback(null, ok);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -294,7 +267,7 @@ class Auth implements IAuth {
|
||||
return (req: $RequestExtend, res: $ResponseExtend, _next: NextFunction): void => {
|
||||
req.pause();
|
||||
|
||||
const next = function(err: VerdaccioError | void): void {
|
||||
const next = function (err: VerdaccioError | void): void {
|
||||
req.resume();
|
||||
// uncomment this to reject users with bad auth headers
|
||||
// return _next.apply(null, arguments)
|
||||
@ -342,19 +315,15 @@ class Auth implements IAuth {
|
||||
// this should happen when client tries to login with an existing user
|
||||
const credentials = convertPayloadToBase64(token).toString();
|
||||
const { user, password } = parseBasicPayload(credentials) as AESPayload;
|
||||
this.authenticate(
|
||||
user,
|
||||
password,
|
||||
(err, user): void => {
|
||||
if (!err) {
|
||||
req.remote_user = user;
|
||||
next();
|
||||
} else {
|
||||
req.remote_user = createAnonymousRemoteUser();
|
||||
next(err);
|
||||
}
|
||||
this.authenticate(user, password, (err, user): void => {
|
||||
if (!err) {
|
||||
req.remote_user = user;
|
||||
next();
|
||||
} else {
|
||||
req.remote_user = createAnonymousRemoteUser();
|
||||
next(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// jwt handler
|
||||
const credentials: any = getMiddlewareCredentials(security, secret, authorization);
|
||||
@ -373,19 +342,15 @@ class Auth implements IAuth {
|
||||
const credentials: any = getMiddlewareCredentials(security, secret, authorization);
|
||||
if (credentials) {
|
||||
const { user, password } = credentials;
|
||||
this.authenticate(
|
||||
user,
|
||||
password,
|
||||
(err, user): void => {
|
||||
if (!err) {
|
||||
req.remote_user = user;
|
||||
next();
|
||||
} else {
|
||||
req.remote_user = createAnonymousRemoteUser();
|
||||
next(err);
|
||||
}
|
||||
this.authenticate(user, password, (err, user): void => {
|
||||
if (!err) {
|
||||
req.remote_user = user;
|
||||
next();
|
||||
} else {
|
||||
req.remote_user = createAnonymousRemoteUser();
|
||||
next(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// we force npm client to ask again with basic authentication
|
||||
return next(getBadRequest(API_ERROR.BAD_AUTH_HEADER));
|
||||
|
@ -1,2 +1,2 @@
|
||||
export { Auth } from './auth'
|
||||
export { Auth } from './auth';
|
||||
export * from './utils';
|
||||
|
@ -1,127 +1,118 @@
|
||||
import {Config, RemoteUser, Security} from "@verdaccio/types";
|
||||
import {AuthMiddlewarePayload, AuthTokenHeader, BasicPayload, IAuthWebUI} from "@verdaccio/dev-types";
|
||||
import _ from "lodash";
|
||||
import {HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER} from "@verdaccio/dev-commons";
|
||||
import {
|
||||
aesDecrypt,
|
||||
buildUserBuffer,
|
||||
convertPayloadToBase64,
|
||||
createAnonymousRemoteUser,
|
||||
defaultSecurity,
|
||||
ErrorCode,
|
||||
verifyPayload
|
||||
} from "@verdaccio/utils";
|
||||
import { Config, RemoteUser, Security } from '@verdaccio/types';
|
||||
import { AuthMiddlewarePayload, AuthTokenHeader, BasicPayload, IAuthWebUI } from '@verdaccio/dev-types';
|
||||
import _ from 'lodash';
|
||||
import { HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
import { aesDecrypt, buildUserBuffer, convertPayloadToBase64, createAnonymousRemoteUser, defaultSecurity, ErrorCode, verifyPayload } from '@verdaccio/utils';
|
||||
|
||||
export function parseAuthTokenHeader(authorizationHeader: string): AuthTokenHeader {
|
||||
const parts = authorizationHeader.split(' ');
|
||||
const [scheme, token] = parts;
|
||||
const parts = authorizationHeader.split(' ');
|
||||
const [scheme, token] = parts;
|
||||
|
||||
return { scheme, token };
|
||||
return { scheme, token };
|
||||
}
|
||||
|
||||
export function parseAESCredentials(authorizationHeader: string, secret: string) {
|
||||
const { scheme, token } = parseAuthTokenHeader(authorizationHeader);
|
||||
const { scheme, token } = parseAuthTokenHeader(authorizationHeader);
|
||||
|
||||
// basic is deprecated and should not be enforced
|
||||
if (scheme.toUpperCase() === TOKEN_BASIC.toUpperCase()) {
|
||||
const credentials = convertPayloadToBase64(token).toString();
|
||||
// basic is deprecated and should not be enforced
|
||||
if (scheme.toUpperCase() === TOKEN_BASIC.toUpperCase()) {
|
||||
const credentials = convertPayloadToBase64(token).toString();
|
||||
|
||||
return credentials;
|
||||
} else if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
|
||||
const tokenAsBuffer = convertPayloadToBase64(token);
|
||||
const credentials = aesDecrypt(tokenAsBuffer, secret).toString('utf8');
|
||||
return credentials;
|
||||
} else if (scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
|
||||
const tokenAsBuffer = convertPayloadToBase64(token);
|
||||
const credentials = aesDecrypt(tokenAsBuffer, secret).toString('utf8');
|
||||
|
||||
return credentials;
|
||||
}
|
||||
return credentials;
|
||||
}
|
||||
}
|
||||
|
||||
export function getMiddlewareCredentials(security: Security, secret: string, authorizationHeader: string): AuthMiddlewarePayload {
|
||||
if (isAESLegacy(security)) {
|
||||
const credentials = parseAESCredentials(authorizationHeader, secret);
|
||||
if (!credentials) {
|
||||
return;
|
||||
}
|
||||
if (isAESLegacy(security)) {
|
||||
const credentials = parseAESCredentials(authorizationHeader, secret);
|
||||
if (!credentials) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedCredentials = parseBasicPayload(credentials);
|
||||
if (!parsedCredentials) {
|
||||
return;
|
||||
}
|
||||
const parsedCredentials = parseBasicPayload(credentials);
|
||||
if (!parsedCredentials) {
|
||||
return;
|
||||
}
|
||||
|
||||
return parsedCredentials;
|
||||
}
|
||||
const { scheme, token } = parseAuthTokenHeader(authorizationHeader);
|
||||
return parsedCredentials;
|
||||
}
|
||||
const { scheme, token } = parseAuthTokenHeader(authorizationHeader);
|
||||
|
||||
if (_.isString(token) && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
|
||||
return verifyJWTPayload(token, secret);
|
||||
}
|
||||
if (_.isString(token) && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
|
||||
return verifyJWTPayload(token, secret);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function isAESLegacy(security: Security): boolean {
|
||||
const { legacy, jwt } = security.api;
|
||||
const { legacy, jwt } = security.api;
|
||||
|
||||
return _.isNil(legacy) === false && _.isNil(jwt) && legacy === true;
|
||||
return _.isNil(legacy) === false && _.isNil(jwt) && legacy === true;
|
||||
}
|
||||
|
||||
export async function getApiToken(auth: IAuthWebUI, config: Config, remoteUser: RemoteUser, aesPassword: string): Promise<string> {
|
||||
const security: Security = getSecurity(config);
|
||||
const security: Security = getSecurity(config);
|
||||
|
||||
if (isAESLegacy(security)) {
|
||||
// fallback all goes to AES encryption
|
||||
return await new Promise((resolve): void => {
|
||||
resolve(auth.aesEncrypt(buildUserBuffer(remoteUser.name as string, aesPassword)).toString('base64'));
|
||||
});
|
||||
}
|
||||
// i am wiling to use here _.isNil but flow does not like it yet.
|
||||
const { jwt } = security.api;
|
||||
if (isAESLegacy(security)) {
|
||||
// fallback all goes to AES encryption
|
||||
return await new Promise((resolve): void => {
|
||||
resolve(auth.aesEncrypt(buildUserBuffer(remoteUser.name as string, aesPassword)).toString('base64'));
|
||||
});
|
||||
}
|
||||
// i am wiling to use here _.isNil but flow does not like it yet.
|
||||
const { jwt } = security.api;
|
||||
|
||||
if (jwt?.sign) {
|
||||
return await auth.jwtEncrypt(remoteUser, jwt.sign);
|
||||
}
|
||||
return await new Promise((resolve): void => {
|
||||
resolve(auth.aesEncrypt(buildUserBuffer(remoteUser.name as string, aesPassword)).toString('base64'));
|
||||
});
|
||||
if (jwt?.sign) {
|
||||
return await auth.jwtEncrypt(remoteUser, jwt.sign);
|
||||
}
|
||||
return await new Promise((resolve): void => {
|
||||
resolve(auth.aesEncrypt(buildUserBuffer(remoteUser.name as string, aesPassword)).toString('base64'));
|
||||
});
|
||||
}
|
||||
|
||||
export function getSecurity(config: Config): Security {
|
||||
if (_.isNil(config.security) === false) {
|
||||
return _.merge(defaultSecurity, config.security);
|
||||
}
|
||||
if (_.isNil(config.security) === false) {
|
||||
return _.merge(defaultSecurity, config.security);
|
||||
}
|
||||
|
||||
return defaultSecurity;
|
||||
return defaultSecurity;
|
||||
}
|
||||
|
||||
export const expireReasons: string[] = ['JsonWebTokenError', 'TokenExpiredError'];
|
||||
|
||||
export function verifyJWTPayload(token: string, secret: string): RemoteUser {
|
||||
try {
|
||||
const payload: RemoteUser = verifyPayload(token, secret);
|
||||
try {
|
||||
const payload: RemoteUser = verifyPayload(token, secret);
|
||||
|
||||
return payload;
|
||||
} catch (error) {
|
||||
// #168 this check should be removed as soon AES encrypt is removed.
|
||||
if (expireReasons.includes(error.name)) {
|
||||
// it might be possible the jwt configuration is enabled and
|
||||
// old tokens fails still remains in usage, thus
|
||||
// we return an anonymous user to force log in.
|
||||
return createAnonymousRemoteUser();
|
||||
}
|
||||
throw ErrorCode.getCode(HTTP_STATUS.UNAUTHORIZED, error.message);
|
||||
}
|
||||
return payload;
|
||||
} catch (error) {
|
||||
// #168 this check should be removed as soon AES encrypt is removed.
|
||||
if (expireReasons.includes(error.name)) {
|
||||
// it might be possible the jwt configuration is enabled and
|
||||
// old tokens fails still remains in usage, thus
|
||||
// we return an anonymous user to force log in.
|
||||
return createAnonymousRemoteUser();
|
||||
}
|
||||
throw ErrorCode.getCode(HTTP_STATUS.UNAUTHORIZED, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
export function isAuthHeaderValid(authorization: string): boolean {
|
||||
return authorization.split(' ').length === 2;
|
||||
return authorization.split(' ').length === 2;
|
||||
}
|
||||
|
||||
export function parseBasicPayload(credentials: string): BasicPayload {
|
||||
const index = credentials.indexOf(':');
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
const index = credentials.indexOf(':');
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user: string = credentials.slice(0, index);
|
||||
const password: string = credentials.slice(index + 1);
|
||||
const user: string = credentials.slice(0, index);
|
||||
const password: string = credentials.slice(index + 1);
|
||||
|
||||
return { user, password };
|
||||
return { user, password };
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
import path from "path";
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
import { Auth } from '../src';
|
||||
import {CHARACTER_ENCODING, TOKEN_BEARER} from '@verdaccio/dev-commons';
|
||||
import { CHARACTER_ENCODING, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
|
||||
import { configExample } from '@verdaccio/mock';
|
||||
import {Config as AppConfig } from '@verdaccio/config';
|
||||
import {setup} from '@verdaccio/logger';
|
||||
import { Config as AppConfig } from '@verdaccio/config';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
|
||||
import {
|
||||
buildUserBuffer,
|
||||
@ -17,17 +16,13 @@ import {
|
||||
parseConfigFile,
|
||||
createAnonymousRemoteUser,
|
||||
createRemoteUser,
|
||||
signPayload
|
||||
signPayload,
|
||||
} from '@verdaccio/utils';
|
||||
|
||||
|
||||
import { IAuth } from '@verdaccio/dev-types';
|
||||
import {Config, Security, RemoteUser} from '@verdaccio/types';
|
||||
import {
|
||||
getMiddlewareCredentials,
|
||||
getApiToken,
|
||||
verifyJWTPayload,
|
||||
getSecurity } from '../src'
|
||||
import { Config, Security, RemoteUser } from '@verdaccio/types';
|
||||
import { Auth } from '../src';
|
||||
import { getMiddlewareCredentials, getApiToken, verifyJWTPayload, getSecurity } from '../src';
|
||||
|
||||
setup([]);
|
||||
|
||||
@ -38,7 +33,6 @@ const parseConfigurationFile = (conf) => {
|
||||
return path.join(__dirname, `./partials/config/${format}/security/${name}.${format}`);
|
||||
};
|
||||
|
||||
|
||||
describe('Auth utilities', () => {
|
||||
jest.setTimeout(20000);
|
||||
|
||||
@ -49,7 +43,7 @@ describe('Auth utilities', () => {
|
||||
function getConfig(configFileName: string, secret: string) {
|
||||
const conf = parseConfigFile(parseConfigurationSecurityFile(configFileName));
|
||||
// @ts-ignore
|
||||
const secConf= _.merge(configExample(), conf);
|
||||
const secConf = _.merge(configExample(), conf);
|
||||
secConf.secret = secret;
|
||||
const config: Config = new AppConfig(secConf);
|
||||
|
||||
@ -62,7 +56,8 @@ describe('Auth utilities', () => {
|
||||
password: string,
|
||||
secret = '12345',
|
||||
methodToSpy: string,
|
||||
methodNotBeenCalled: string): Promise<string> {
|
||||
methodNotBeenCalled: string
|
||||
): Promise<string> {
|
||||
const config: Config = getConfig(configFileName, secret);
|
||||
const auth: IAuth = new Auth(config);
|
||||
// @ts-ignore
|
||||
@ -72,7 +67,7 @@ describe('Auth utilities', () => {
|
||||
const user: RemoteUser = {
|
||||
name: username,
|
||||
real_groups: [],
|
||||
groups: []
|
||||
groups: [],
|
||||
};
|
||||
const token = await getApiToken(auth, config, user, password);
|
||||
expect(spy).toHaveBeenCalled();
|
||||
@ -100,56 +95,49 @@ describe('Auth utilities', () => {
|
||||
|
||||
describe('getApiToken test', () => {
|
||||
test('should sign token with aes and security missing', async () => {
|
||||
const token = await signCredentials('security-missing',
|
||||
'test', 'test', '1234567', 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials('security-missing', 'test', 'test', '1234567', 'aesEncrypt', 'jwtEncrypt');
|
||||
|
||||
verifyAES(token, 'test', 'test', '1234567');
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should sign token with aes and security empty', async () => {
|
||||
const token = await signCredentials('security-empty',
|
||||
'test', 'test', '123456', 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials('security-empty', 'test', 'test', '123456', 'aesEncrypt', 'jwtEncrypt');
|
||||
|
||||
verifyAES(token, 'test', 'test', '123456');
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should sign token with aes', async () => {
|
||||
const token = await signCredentials('security-basic',
|
||||
'test', 'test', '123456', 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials('security-basic', 'test', 'test', '123456', 'aesEncrypt', 'jwtEncrypt');
|
||||
|
||||
verifyAES(token, 'test', 'test', '123456');
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should sign token with legacy and jwt disabled', async () => {
|
||||
const token = await signCredentials('security-no-legacy',
|
||||
'test', 'test', 'x8T#ZCx=2t', 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials('security-no-legacy', 'test', 'test', 'x8T#ZCx=2t', 'aesEncrypt', 'jwtEncrypt');
|
||||
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
verifyAES(token, 'test', 'test', 'x8T#ZCx=2t');
|
||||
});
|
||||
|
||||
test('should sign token with legacy enabled and jwt enabled', async () => {
|
||||
const token = await signCredentials('security-jwt-legacy-enabled',
|
||||
'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
||||
const token = await signCredentials('security-jwt-legacy-enabled', 'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
||||
|
||||
verifyJWT(token, 'test', 'test', 'secret');
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should sign token with jwt enabled', async () => {
|
||||
const token = await signCredentials('security-jwt',
|
||||
'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
||||
const token = await signCredentials('security-jwt', 'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
||||
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
verifyJWT(token, 'test', 'test', 'secret');
|
||||
});
|
||||
|
||||
test('should sign with jwt whether legacy is disabled', async () => {
|
||||
const token = await signCredentials('security-legacy-disabled',
|
||||
'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
||||
const token = await signCredentials('security-legacy-disabled', 'test', 'test', 'secret', 'jwtEncrypt', 'aesEncrypt');
|
||||
|
||||
expect(_.isString(token)).toBeTruthy();
|
||||
verifyJWT(token, 'test', 'test', 'secret');
|
||||
@ -158,7 +146,7 @@ describe('Auth utilities', () => {
|
||||
|
||||
describe('getAuthenticatedMessage test', () => {
|
||||
test('should sign token with jwt enabled', () => {
|
||||
expect(getAuthenticatedMessage('test')).toBe('you are authenticated as \'test\'');
|
||||
expect(getAuthenticatedMessage('test')).toBe("you are authenticated as 'test'");
|
||||
});
|
||||
});
|
||||
|
||||
@ -168,8 +156,7 @@ describe('Auth utilities', () => {
|
||||
const secret = 'secret';
|
||||
const user = 'test';
|
||||
const pass = 'test';
|
||||
const token = await signCredentials('security-legacy',
|
||||
user, pass, secret, 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials('security-legacy', user, pass, secret, 'aesEncrypt', 'jwtEncrypt');
|
||||
const config: Config = getConfig('security-legacy', secret);
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, secret, `Bearer ${token}`);
|
||||
@ -197,8 +184,7 @@ describe('Auth utilities', () => {
|
||||
|
||||
test.concurrent('should return empty credential wrong secret key', async () => {
|
||||
const secret = 'secret';
|
||||
const token = await signCredentials('security-legacy',
|
||||
'test', 'test', secret, 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials('security-legacy', 'test', 'test', secret, 'aesEncrypt', 'jwtEncrypt');
|
||||
const config: Config = getConfig('security-legacy', secret);
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, 'BAD_SECRET', buildToken(TOKEN_BEARER, token));
|
||||
@ -207,8 +193,7 @@ describe('Auth utilities', () => {
|
||||
|
||||
test.concurrent('should return empty credential wrong scheme', async () => {
|
||||
const secret = 'secret';
|
||||
const token = await signCredentials('security-legacy',
|
||||
'test', 'test', secret, 'aesEncrypt', 'jwtEncrypt');
|
||||
const token = await signCredentials('security-legacy', 'test', 'test', secret, 'aesEncrypt', 'jwtEncrypt');
|
||||
const config: Config = getConfig('security-legacy', secret);
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, secret, buildToken('BAD_SCHEME', token));
|
||||
@ -267,8 +252,7 @@ describe('Auth utilities', () => {
|
||||
const secret = 'secret';
|
||||
const user = 'test';
|
||||
const config: Config = getConfig('security-jwt', secret);
|
||||
const token = await signCredentials('security-jwt',
|
||||
user, 'secretTest', secret, 'jwtEncrypt', 'aesEncrypt');
|
||||
const token = await signCredentials('security-jwt', user, 'secretTest', secret, 'jwtEncrypt', 'aesEncrypt');
|
||||
const security: Security = getSecurity(config);
|
||||
const credentials = getMiddlewareCredentials(security, secret, buildToken(TOKEN_BEARER, token));
|
||||
expect(credentials).toBeDefined();
|
||||
|
@ -29,18 +29,16 @@ describe('AuthTest', () => {
|
||||
expect(auth).toBeDefined();
|
||||
|
||||
const callback = jest.fn();
|
||||
const groups = [ "test" ];
|
||||
const groups = ['test'];
|
||||
|
||||
auth.authenticate('foo', 'bar', callback);
|
||||
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
expect(callback).toHaveBeenCalledWith(null,
|
||||
{"groups":
|
||||
[
|
||||
"test", ROLES.$ALL, ROLES.$AUTH, ROLES.DEPRECATED_ALL, ROLES.DEPRECATED_AUTH, ROLES.ALL],
|
||||
"name": 'foo',
|
||||
"real_groups": groups
|
||||
});
|
||||
expect(callback).toHaveBeenCalledWith(null, {
|
||||
groups: ['test', ROLES.$ALL, ROLES.$AUTH, ROLES.DEPRECATED_ALL, ROLES.DEPRECATED_AUTH, ROLES.ALL],
|
||||
name: 'foo',
|
||||
real_groups: groups,
|
||||
});
|
||||
});
|
||||
|
||||
test('should be a fail on login', () => {
|
||||
@ -71,7 +69,7 @@ describe('AuthTest', () => {
|
||||
let index = 0;
|
||||
|
||||
// as defined by https://developer.mozilla.org/en-US/docs/Glossary/Falsy
|
||||
for (const value of [ false, 0, "", null, undefined, NaN ]) {
|
||||
for (const value of [false, 0, '', null, undefined, NaN]) {
|
||||
// @ts-ignore
|
||||
auth.authenticate(null, value, callback);
|
||||
const call = callback.mock.calls[index++];
|
||||
@ -88,8 +86,8 @@ describe('AuthTest', () => {
|
||||
|
||||
const callback = jest.fn();
|
||||
|
||||
for (const value of [ true, 1, "test", { } ]) {
|
||||
expect(function ( ) {
|
||||
for (const value of [true, 1, 'test', {}]) {
|
||||
expect(function () {
|
||||
// @ts-ignore
|
||||
auth.authenticate(null, value, callback);
|
||||
}).toThrow(TypeError);
|
||||
@ -104,7 +102,7 @@ describe('AuthTest', () => {
|
||||
expect(auth).toBeDefined();
|
||||
|
||||
const callback = jest.fn();
|
||||
const value = [ ];
|
||||
const value = [];
|
||||
|
||||
// @ts-ignore
|
||||
auth.authenticate(null, value, callback);
|
||||
@ -122,7 +120,7 @@ describe('AuthTest', () => {
|
||||
const callback = jest.fn();
|
||||
let index = 0;
|
||||
|
||||
for (const value of [ [ "" ], [ "1" ], [ "0" ], ["000"] ]) {
|
||||
for (const value of [[''], ['1'], ['0'], ['000']]) {
|
||||
// @ts-ignore
|
||||
auth.authenticate(null, value, callback);
|
||||
const call = callback.mock.calls[index++];
|
||||
@ -131,5 +129,5 @@ describe('AuthTest', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
@ -1,20 +1,20 @@
|
||||
import path from 'path';
|
||||
import { configExample as config }from '@verdaccio/mock';
|
||||
import { configExample as config } from '@verdaccio/mock';
|
||||
|
||||
export const authProfileConf = config({
|
||||
auth: {
|
||||
[`${path.join(__dirname, '../partials/plugin/authenticate.success')}`]: { }
|
||||
}
|
||||
[`${path.join(__dirname, '../partials/plugin/authenticate.success')}`]: {},
|
||||
},
|
||||
});
|
||||
|
||||
export const authPluginFailureConf = config({
|
||||
auth: {
|
||||
[`${path.join(__dirname, '../partials/plugin/authenticate.fail')}`]: { }
|
||||
}
|
||||
[`${path.join(__dirname, '../partials/plugin/authenticate.fail')}`]: {},
|
||||
},
|
||||
});
|
||||
|
||||
export const authPluginPassThrougConf = config({
|
||||
auth: {
|
||||
[`${path.join(__dirname, '../partials/plugin/authenticate.passthroug')}`]: { }
|
||||
}
|
||||
[`${path.join(__dirname, '../partials/plugin/authenticate.passthroug')}`]: {},
|
||||
},
|
||||
});
|
||||
|
@ -6,14 +6,14 @@ import { bgYellow, bgRed } from 'kleur';
|
||||
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
import infoCommand from "./commands/info";
|
||||
import initProgram from "./commands/init";
|
||||
import {isVersionValid, MIN_NODE_VERSION} from "./utils";
|
||||
import infoCommand from './commands/info';
|
||||
import initProgram from './commands/init';
|
||||
import { isVersionValid, MIN_NODE_VERSION } from './utils';
|
||||
|
||||
const isRootUser = process.getuid && process.getuid() === 0;
|
||||
|
||||
if (isRootUser) {
|
||||
global.console.warn(bgYellow().red('*** WARNING: Verdaccio doesn\'t need superuser privileges. Don\'t run it under root! ***'));
|
||||
global.console.warn(bgYellow().red("*** WARNING: Verdaccio doesn't need superuser privileges. Don't run it under root! ***"));
|
||||
}
|
||||
|
||||
if (isVersionValid()) {
|
||||
@ -38,20 +38,22 @@ const isHelp = commander.args.length !== 0;
|
||||
|
||||
if (commander.info) {
|
||||
infoCommand();
|
||||
} else if (fallbackConfig) {
|
||||
} else if (fallbackConfig) {
|
||||
// handling "verdaccio [config]" case if "-c" is missing in command line
|
||||
commander.config = commander.args.pop();
|
||||
initProgram(commander, pkgVersion, pkgName);
|
||||
initProgram(commander, pkgVersion, pkgName);
|
||||
} else if (isHelp) {
|
||||
commander.help();
|
||||
} else {
|
||||
initProgram(commander, pkgVersion, pkgName);
|
||||
initProgram(commander, pkgVersion, pkgName);
|
||||
}
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
logger.fatal( {
|
||||
err: err,
|
||||
},
|
||||
'uncaught exception, please report (https://github.com/verdaccio/verdaccio/issues) this: \n@{err.stack}' );
|
||||
process.on('uncaughtException', function (err) {
|
||||
logger.fatal(
|
||||
{
|
||||
err: err,
|
||||
},
|
||||
'uncaught exception, please report (https://github.com/verdaccio/verdaccio/issues) this: \n@{err.stack}'
|
||||
);
|
||||
process.exit(255);
|
||||
});
|
||||
|
@ -1,18 +1,18 @@
|
||||
import envinfo from 'envinfo';
|
||||
|
||||
export default function infoCommand() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('\nEnvironment Info:');
|
||||
(async () => {
|
||||
const data = await envinfo.run({
|
||||
System: ['OS', 'CPU'],
|
||||
Binaries: ['Node', 'Yarn', 'npm'],
|
||||
Virtualization: ['Docker'],
|
||||
Browsers: ['Chrome', 'Edge', 'Firefox', 'Safari'],
|
||||
npmGlobalPackages: ['verdaccio'],
|
||||
});
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(data);
|
||||
process.exit(0);
|
||||
})();
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('\nEnvironment Info:');
|
||||
(async () => {
|
||||
const data = await envinfo.run({
|
||||
System: ['OS', 'CPU'],
|
||||
Binaries: ['Node', 'Yarn', 'npm'],
|
||||
Virtualization: ['Docker'],
|
||||
Browsers: ['Chrome', 'Edge', 'Firefox', 'Safari'],
|
||||
npmGlobalPackages: ['verdaccio'],
|
||||
});
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(data);
|
||||
process.exit(0);
|
||||
})();
|
||||
}
|
||||
|
@ -1,44 +1,44 @@
|
||||
import path from "path";
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { parseConfigFile} from "@verdaccio/utils";
|
||||
import { findConfigFile } from "@verdaccio/config";
|
||||
import { parseConfigFile } from '@verdaccio/utils';
|
||||
import { findConfigFile } from '@verdaccio/config';
|
||||
import { logger, createLogger } from '@verdaccio/logger';
|
||||
import {startVerdaccio, listenDefaultCallback} from "@verdaccio/node-api";
|
||||
import { startVerdaccio, listenDefaultCallback } from '@verdaccio/node-api';
|
||||
|
||||
export const DEFAULT_PROCESS_NAME: string = 'verdaccio';
|
||||
|
||||
export default function initProgram(commander, pkgVersion, pkgName) {
|
||||
// FIXME: we need to log before the setup is being applied
|
||||
// const initLogger = createLogger();
|
||||
const cliListener = commander.listen;
|
||||
let configPathLocation;
|
||||
let verdaccioConfiguration;
|
||||
try {
|
||||
configPathLocation = findConfigFile(commander.config);
|
||||
verdaccioConfiguration = parseConfigFile(configPathLocation);
|
||||
const { web, https, self_path } = verdaccioConfiguration;
|
||||
export default function initProgram(commander, pkgVersion, pkgName) {
|
||||
// FIXME: we need to log before the setup is being applied
|
||||
// const initLogger = createLogger();
|
||||
const cliListener = commander.listen;
|
||||
let configPathLocation;
|
||||
let verdaccioConfiguration;
|
||||
try {
|
||||
configPathLocation = findConfigFile(commander.config);
|
||||
verdaccioConfiguration = parseConfigFile(configPathLocation);
|
||||
const { web, https, self_path } = verdaccioConfiguration;
|
||||
|
||||
process.title = web?.title || DEFAULT_PROCESS_NAME;
|
||||
process.title = web?.title || DEFAULT_PROCESS_NAME;
|
||||
|
||||
// note: self_path is only being used by @verdaccio/storage , not really useful and migth be removed soon
|
||||
if (!self_path) {
|
||||
verdaccioConfiguration = _.assign({}, verdaccioConfiguration, {
|
||||
self_path: path.resolve(configPathLocation)
|
||||
});
|
||||
}
|
||||
// note: self_path is only being used by @verdaccio/storage , not really useful and migth be removed soon
|
||||
if (!self_path) {
|
||||
verdaccioConfiguration = _.assign({}, verdaccioConfiguration, {
|
||||
self_path: path.resolve(configPathLocation),
|
||||
});
|
||||
}
|
||||
|
||||
if (!https) {
|
||||
verdaccioConfiguration = _.assign({}, verdaccioConfiguration, {
|
||||
https: {enable: false}
|
||||
});
|
||||
}
|
||||
if (!https) {
|
||||
verdaccioConfiguration = _.assign({}, verdaccioConfiguration, {
|
||||
https: { enable: false },
|
||||
});
|
||||
}
|
||||
|
||||
// initLogger.warn({file: configPathLocation}, 'config file - @{file}');
|
||||
// initLogger.warn({file: configPathLocation}, 'config file - @{file}');
|
||||
|
||||
startVerdaccio(verdaccioConfiguration, cliListener, configPathLocation, pkgVersion, pkgName, listenDefaultCallback);
|
||||
} catch (err) {
|
||||
// initLogger.fatal({file: configPathLocation, err: err}, 'cannot open config file @{file}: @{!err.message}');
|
||||
process.exit(1);
|
||||
}
|
||||
startVerdaccio(verdaccioConfiguration, cliListener, configPathLocation, pkgVersion, pkgName, listenDefaultCallback);
|
||||
} catch (err) {
|
||||
// initLogger.fatal({file: configPathLocation, err: err}, 'cannot open config file @{file}: @{!err.message}');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import semver from "semver";
|
||||
import semver from 'semver';
|
||||
|
||||
export const MIN_NODE_VERSION = '6.9.0';
|
||||
|
||||
export const isVersionValid = () => semver.satisfies(process.version, `>=${MIN_NODE_VERSION}`) === false
|
||||
export const isVersionValid = () => semver.satisfies(process.version, `>=${MIN_NODE_VERSION}`) === false;
|
||||
|
@ -1,3 +1,3 @@
|
||||
describe('cli test', () => {
|
||||
test.todo('write some test for this module');
|
||||
test.todo('write some test for this module');
|
||||
});
|
||||
|
@ -1,58 +1,57 @@
|
||||
import { Package } from "@verdaccio/types";
|
||||
import { Package } from '@verdaccio/types';
|
||||
|
||||
export interface DistTags {
|
||||
[key: string]: string;
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export function generatePackageMetadata(pkgName: string, version = '1.0.0', distTags: DistTags = {['latest']: version}): Package {
|
||||
// @ts-ignore
|
||||
return {
|
||||
"_id": pkgName,
|
||||
"name": pkgName,
|
||||
"dist-tags": {
|
||||
...distTags
|
||||
},
|
||||
"versions": {
|
||||
[version]: {
|
||||
"name": pkgName,
|
||||
"version": version,
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
],
|
||||
"author": {
|
||||
"name": "User NPM",
|
||||
"email": "user@domain.com"
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"verdaccio": "^2.7.2"
|
||||
},
|
||||
"readme": "# test",
|
||||
"readmeFilename": "README.md",
|
||||
"_id": `${pkgName}@${version}`,
|
||||
"_npmVersion": "5.5.1",
|
||||
"_npmUser": {
|
||||
'name': 'foo',
|
||||
},
|
||||
"dist": {
|
||||
"integrity": "sha512-6gHiERpiDgtb3hjqpQH5\/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==",
|
||||
"shasum": "2c03764f651a9f016ca0b7620421457b619151b9", // pragma: allowlist secret
|
||||
"tarball": `http:\/\/localhost:5555\/${pkgName}\/-\/${pkgName}-${version}.tgz`
|
||||
}
|
||||
}
|
||||
},
|
||||
"readme": "# test",
|
||||
"_attachments": {
|
||||
[`${pkgName}-${version}.tgz`]: {
|
||||
"content_type": "application\/octet-stream",
|
||||
"data": "H4sIAAAAAAAAE+2W32vbMBDH85y\/QnjQp9qxLEeBMsbGlocNBmN7bFdQ5WuqxJaEpGQdo\/\/79KPeQsnIw5KUDX\/9IOvurLuz\/DHSjK\/YAiY6jcXSKjk6sMqypHWNdtmD6hlBI0wqQmo8nVbVqMR4OsNoVB66kF1aW8eML+Vv10m9oF\/jP6IfY4QyyTrILlD2eqkcm+gVzpdrJrPz4NuAsULJ4MZFWdBkbcByI7R79CRjx0ScCdnAvf+SkjUFWu8IubzBgXUhDPidQlfZ3BhlLpBUKDiQ1cDFrYDmKkNnZwjuhUM4808+xNVW8P2bMk1Y7vJrtLC1u1MmLPjBF40+Cc4ahV6GDmI\/DWygVRpMwVX3KtXUCg7Sxp7ff3nbt6TBFy65gK1iffsN41yoEHtdFbOiisWMH8bPvXUH0SP3k+KG3UBr+DFy7OGfEJr4x5iWVeS\/pLQe+D+FIv\/agIWI6GX66kFuIhT+1gDjrp\/4d7WAvAwEJPh0u14IufWkM0zaW2W6nLfM2lybgJ4LTJ0\/jWiAK8OcMjt8MW3OlfQppcuhhQ6k+2OgkK2Q8DssFPi\/IHpU9fz3\/+xj5NjDf8QFE39VmE4JDfzPCBn4P4X6\/f88f\/Pu47zomiPk2Lv\/dOv8h+P\/34\/D\/p9CL+Kp67mrGDRo0KBBp9ZPsETQegASAAA=",
|
||||
"length": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
export function generatePackageMetadata(pkgName: string, version = '1.0.0', distTags: DistTags = { ['latest']: version }): Package {
|
||||
// @ts-ignore
|
||||
return {
|
||||
_id: pkgName,
|
||||
name: pkgName,
|
||||
'dist-tags': {
|
||||
...distTags,
|
||||
},
|
||||
versions: {
|
||||
[version]: {
|
||||
name: pkgName,
|
||||
version: version,
|
||||
description: '',
|
||||
main: 'index.js',
|
||||
scripts: {
|
||||
test: 'echo "Error: no test specified" && exit 1',
|
||||
},
|
||||
keywords: [],
|
||||
author: {
|
||||
name: 'User NPM',
|
||||
email: 'user@domain.com',
|
||||
},
|
||||
license: 'ISC',
|
||||
dependencies: {
|
||||
verdaccio: '^2.7.2',
|
||||
},
|
||||
readme: '# test',
|
||||
readmeFilename: 'README.md',
|
||||
_id: `${pkgName}@${version}`,
|
||||
_npmVersion: '5.5.1',
|
||||
_npmUser: {
|
||||
name: 'foo',
|
||||
},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
||||
tarball: `http:\/\/localhost:5555\/${pkgName}\/-\/${pkgName}-${version}.tgz`,
|
||||
},
|
||||
},
|
||||
},
|
||||
readme: '# test',
|
||||
_attachments: {
|
||||
[`${pkgName}-${version}.tgz`]: {
|
||||
content_type: 'application/octet-stream',
|
||||
data:
|
||||
'H4sIAAAAAAAAE+2W32vbMBDH85y/QnjQp9qxLEeBMsbGlocNBmN7bFdQ5WuqxJaEpGQdo//79KPeQsnIw5KUDX/9IOvurLuz/DHSjK/YAiY6jcXSKjk6sMqypHWNdtmD6hlBI0wqQmo8nVbVqMR4OsNoVB66kF1aW8eML+Vv10m9oF/jP6IfY4QyyTrILlD2eqkcm+gVzpdrJrPz4NuAsULJ4MZFWdBkbcByI7R79CRjx0ScCdnAvf+SkjUFWu8IubzBgXUhDPidQlfZ3BhlLpBUKDiQ1cDFrYDmKkNnZwjuhUM4808+xNVW8P2bMk1Y7vJrtLC1u1MmLPjBF40+Cc4ahV6GDmI/DWygVRpMwVX3KtXUCg7Sxp7ff3nbt6TBFy65gK1iffsN41yoEHtdFbOiisWMH8bPvXUH0SP3k+KG3UBr+DFy7OGfEJr4x5iWVeS/pLQe+D+FIv/agIWI6GX66kFuIhT+1gDjrp/4d7WAvAwEJPh0u14IufWkM0zaW2W6nLfM2lybgJ4LTJ0/jWiAK8OcMjt8MW3OlfQppcuhhQ6k+2OgkK2Q8DssFPi/IHpU9fz3/+xj5NjDf8QFE39VmE4JDfzPCBn4P4X6/f88f/Pu47zomiPk2Lv/dOv8h+P/34/D/p9CL+Kp67mrGDRo0KBBp9ZPsETQegASAAA=',
|
||||
length: 512,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -14,12 +14,12 @@ const WIN = 'win';
|
||||
const WIN32 = 'win32';
|
||||
// eslint-disable-next-line
|
||||
const pkgJSON = {
|
||||
name: 'verdaccio'
|
||||
name: 'verdaccio',
|
||||
};
|
||||
|
||||
export type SetupDirectory = {
|
||||
path: string;
|
||||
type: string
|
||||
type: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -84,13 +84,16 @@ function updateStorageLinks(configLocation, defaultConfig): string {
|
||||
}
|
||||
|
||||
function getConfigPaths(): SetupDirectory[] {
|
||||
const listPaths: SetupDirectory[] = [getXDGDirectory(), getWindowsDirectory(), getRelativeDefaultDirectory(), getOldDirectory()].reduce(
|
||||
function(acc, currentValue: any): SetupDirectory[] {
|
||||
if (_.isUndefined(currentValue) === false) {
|
||||
acc.push(currentValue);
|
||||
}
|
||||
return acc;
|
||||
}, [] as SetupDirectory[]);
|
||||
const listPaths: SetupDirectory[] = [getXDGDirectory(), getWindowsDirectory(), getRelativeDefaultDirectory(), getOldDirectory()].reduce(function (
|
||||
acc,
|
||||
currentValue: any
|
||||
): SetupDirectory[] {
|
||||
if (_.isUndefined(currentValue) === false) {
|
||||
acc.push(currentValue);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[] as SetupDirectory[]);
|
||||
|
||||
return listPaths;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
uplinkSanityCheck,
|
||||
generateRandomHexString,
|
||||
getUserAgent,
|
||||
isObject
|
||||
isObject,
|
||||
} from '@verdaccio/utils';
|
||||
import { APP_ERROR } from '@verdaccio/dev-commons';
|
||||
|
||||
@ -61,7 +61,7 @@ class Config implements AppConfig {
|
||||
assert(_.isObject(config), APP_ERROR.CONFIG_NOT_VALID);
|
||||
|
||||
// sanity check for strategic config properties
|
||||
strategicConfigProps.forEach(function(x): void {
|
||||
strategicConfigProps.forEach(function (x): void {
|
||||
if (self[x] == null) {
|
||||
self[x] = {};
|
||||
}
|
||||
@ -79,13 +79,11 @@ class Config implements AppConfig {
|
||||
this.packages = normalisePackageAccess(self.packages);
|
||||
|
||||
// loading these from ENV if aren't in config
|
||||
allowedEnvConfig.forEach(
|
||||
(envConf): void => {
|
||||
if (!(envConf in self)) {
|
||||
self[envConf] = process.env[envConf] || process.env[envConf.toUpperCase()];
|
||||
}
|
||||
allowedEnvConfig.forEach((envConf): void => {
|
||||
if (!(envConf in self)) {
|
||||
self[envConf] = process.env[envConf] || process.env[envConf.toUpperCase()];
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// unique identifier of self server (or a cluster), used to avoid loops
|
||||
// @ts-ignore
|
||||
|
@ -1,2 +1,2 @@
|
||||
export * from './config'
|
||||
export * from './config-path'
|
||||
export * from './config';
|
||||
export * from './config-path';
|
||||
|
@ -1,11 +1,11 @@
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { Config, readDefaultConfig } from '../src';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
import {DEFAULT_REGISTRY, DEFAULT_UPLINK, ROLES, WEB_TITLE} from '@verdaccio/dev-commons';
|
||||
import { DEFAULT_REGISTRY, DEFAULT_UPLINK, ROLES, WEB_TITLE } from '@verdaccio/dev-commons';
|
||||
|
||||
import {parseConfigFile} from '@verdaccio/utils';
|
||||
import { parseConfigFile } from '@verdaccio/utils';
|
||||
import { Config, readDefaultConfig } from '../src';
|
||||
|
||||
setup([]);
|
||||
|
||||
@ -45,7 +45,7 @@ const checkDefaultConfPackages = (config) => {
|
||||
expect(config.packages['**'].publish).toBeDefined();
|
||||
expect(config.packages['**'].publish).toContainEqual(ROLES.$AUTH);
|
||||
expect(config.packages['**'].proxy).toBeDefined();
|
||||
expect(config.packages['**'].proxy,).toContainEqual(DEFAULT_UPLINK);
|
||||
expect(config.packages['**'].proxy).toContainEqual(DEFAULT_UPLINK);
|
||||
// uplinks
|
||||
expect(config.uplinks[DEFAULT_UPLINK]).toBeDefined();
|
||||
expect(config.uplinks[DEFAULT_UPLINK].url).toEqual(DEFAULT_REGISTRY);
|
||||
@ -70,7 +70,7 @@ const checkDefaultConfPackages = (config) => {
|
||||
};
|
||||
|
||||
describe('Config file', () => {
|
||||
beforeAll(function() {
|
||||
beforeAll(function () {
|
||||
/* eslint no-invalid-this: 0 */
|
||||
// @ts-ignore
|
||||
this.config = new Config(parseConfigFile(resolveConf('default')));
|
||||
@ -96,9 +96,5 @@ describe('Config file', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Config file', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('Config file', () => {});
|
||||
});
|
||||
|
||||
|
@ -1 +1 @@
|
||||
export {handleNotify, notify, sendNotification } from './notify';
|
||||
export { handleNotify, notify, sendNotification } from './notify';
|
||||
|
@ -5,21 +5,19 @@ import { logger } from '@verdaccio/logger';
|
||||
import { HTTP_STATUS } from '@verdaccio/commons-api';
|
||||
|
||||
export function notifyRequest(options: RequiredUriUrl, content): Promise<any | Error> {
|
||||
return new Promise(
|
||||
(resolve, reject): void => {
|
||||
request(options, function(err, response, body): void {
|
||||
if (err || response.statusCode >= HTTP_STATUS.BAD_REQUEST) {
|
||||
const errorMessage = isNil(err) ? response.body : err.message;
|
||||
logger.error({ errorMessage }, 'notify service has thrown an error: @{errorMessage}');
|
||||
reject(errorMessage);
|
||||
}
|
||||
logger.info({ content }, 'A notification has been shipped: @{content}');
|
||||
if (isNil(body) === false) {
|
||||
logger.debug({ body }, ' body: @{body}');
|
||||
resolve(body);
|
||||
}
|
||||
reject(Error('body is missing'));
|
||||
});
|
||||
}
|
||||
);
|
||||
return new Promise((resolve, reject): void => {
|
||||
request(options, function (err, response, body): void {
|
||||
if (err || response.statusCode >= HTTP_STATUS.BAD_REQUEST) {
|
||||
const errorMessage = isNil(err) ? response.body : err.message;
|
||||
logger.error({ errorMessage }, 'notify service has thrown an error: @{errorMessage}');
|
||||
reject(errorMessage);
|
||||
}
|
||||
logger.info({ content }, 'A notification has been shipped: @{content}');
|
||||
if (isNil(body) === false) {
|
||||
logger.debug({ body }, ' body: @{body}');
|
||||
resolve(body);
|
||||
}
|
||||
reject(Error('body is missing'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -7,12 +7,7 @@ import { notifyRequest } from './notify-request';
|
||||
|
||||
type TemplateMetadata = Package & { publishedPackage: string };
|
||||
|
||||
export function handleNotify(
|
||||
metadata: Package,
|
||||
notifyEntry,
|
||||
remoteUser: RemoteUser,
|
||||
publishedPackage: string
|
||||
): Promise<any> | void {
|
||||
export function handleNotify(metadata: Package, notifyEntry, remoteUser: RemoteUser, publishedPackage: string): Promise<any> | void {
|
||||
let regex;
|
||||
if (metadata.name && notifyEntry.packagePattern) {
|
||||
regex = new RegExp(notifyEntry.packagePattern, notifyEntry.packagePatternFlags || '');
|
||||
@ -21,7 +16,7 @@ export function handleNotify(
|
||||
}
|
||||
}
|
||||
|
||||
const template = Handlebars.compile(notifyEntry.content);
|
||||
const template: HandlebarsTemplateDelegate = Handlebars.compile(notifyEntry.content);
|
||||
// don't override 'publisher' if package.json already has that
|
||||
/* eslint no-unused-vars: 0 */
|
||||
/* eslint @typescript-eslint/no-unused-vars: 0 */
|
||||
@ -35,7 +30,7 @@ export function handleNotify(
|
||||
|
||||
const options: OptionsWithUrl = {
|
||||
body: content,
|
||||
url: ''
|
||||
url: '',
|
||||
};
|
||||
|
||||
// provides fallback support, it's accept an Object {} and Array of {}
|
||||
@ -65,34 +60,17 @@ export function handleNotify(
|
||||
return notifyRequest(options, content);
|
||||
}
|
||||
|
||||
export function sendNotification(
|
||||
metadata: Package,
|
||||
notify: any,
|
||||
remoteUser: RemoteUser,
|
||||
publishedPackage: string
|
||||
): Promise<any> {
|
||||
export function sendNotification(metadata: Package, notify: Notification, remoteUser: RemoteUser, publishedPackage: string): Promise<any> {
|
||||
return handleNotify(metadata, notify, remoteUser, publishedPackage) as Promise<any>;
|
||||
}
|
||||
|
||||
export function notify(
|
||||
metadata: Package,
|
||||
config: Config,
|
||||
remoteUser: RemoteUser,
|
||||
publishedPackage: string
|
||||
): Promise<any> | void {
|
||||
export function notify(metadata: Package, config: Config, remoteUser: RemoteUser, publishedPackage: string): Promise<any> | void {
|
||||
if (config.notify) {
|
||||
if (config.notify.content) {
|
||||
return sendNotification(
|
||||
metadata,
|
||||
(config.notify as unknown) as any,
|
||||
remoteUser,
|
||||
publishedPackage
|
||||
);
|
||||
return sendNotification(metadata, (config.notify as unknown) as Notification, remoteUser, publishedPackage);
|
||||
}
|
||||
// multiple notifications endpoints PR #108
|
||||
return Promise.all(
|
||||
_.map(config.notify, (key) => sendNotification(metadata, key, remoteUser, publishedPackage))
|
||||
);
|
||||
return Promise.all(_.map(config.notify, (key) => sendNotification(metadata, key, remoteUser, publishedPackage)));
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
|
@ -1,14 +1,14 @@
|
||||
import {parseConfigFile} from '@verdaccio/utils';
|
||||
import { parseConfigFile } from '@verdaccio/utils';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
|
||||
import {notify} from '../src';
|
||||
import {notifyRequest} from '../src/notify-request';
|
||||
import {parseConfigurationFile} from './__helper';
|
||||
import { notify } from '../src';
|
||||
import { notifyRequest } from '../src/notify-request';
|
||||
import { parseConfigurationFile } from './__helper';
|
||||
|
||||
setup([]);
|
||||
|
||||
jest.mock('../src/notify-request', () => ({
|
||||
notifyRequest: jest.fn((options, content) => Promise.resolve([options, content]))
|
||||
notifyRequest: jest.fn((options, content) => Promise.resolve([options, content])),
|
||||
}));
|
||||
|
||||
const parseConfigurationNotifyFile = (name) => {
|
||||
@ -20,24 +20,23 @@ const packagePatternNotificationConfig = parseConfigFile(parseConfigurationNotif
|
||||
const multiNotificationConfig = parseConfigFile(parseConfigurationNotifyFile('multiple.notify'));
|
||||
|
||||
describe('Notifications:: Notify', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
// FUTURE: we should add some sort of health check of all props, (not implemented yet)
|
||||
|
||||
test("should not fails if config is not provided", async () => {
|
||||
test('should not fails if config is not provided', async () => {
|
||||
// @ts-ignore
|
||||
await notify({}, {});
|
||||
|
||||
expect(notifyRequest).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test("should send notification", async () => {
|
||||
test('should send notification', async () => {
|
||||
const name = 'package';
|
||||
// @ts-ignore
|
||||
const response = await notify({name}, singleNotificationConfig, { name: 'foo'}, 'bar');
|
||||
const response = await notify({ name }, singleNotificationConfig, { name: 'foo' }, 'bar');
|
||||
const [options, content] = response;
|
||||
|
||||
expect(options.headers).toBeDefined();
|
||||
@ -48,39 +47,36 @@ describe('Notifications:: Notify', () => {
|
||||
expect(notifyRequest).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("should send single header notification", async () => {
|
||||
test('should send single header notification', async () => {
|
||||
// @ts-ignore
|
||||
await notify({}, singleHeaderNotificationConfig, { name: 'foo'}, 'bar');
|
||||
await notify({}, singleHeaderNotificationConfig, { name: 'foo' }, 'bar');
|
||||
|
||||
expect(notifyRequest).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("should send multiple notification", async () => {
|
||||
test('should send multiple notification', async () => {
|
||||
// @ts-ignore
|
||||
await notify({name}, multiNotificationConfig, { name: 'foo'}, 'bar');
|
||||
await notify({ name }, multiNotificationConfig, { name: 'foo' }, 'bar');
|
||||
|
||||
expect(notifyRequest).toHaveBeenCalled();
|
||||
expect(notifyRequest).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
describe('packagePatternFlags', () => {
|
||||
test("should send single notification with packagePatternFlags", async () => {
|
||||
test('should send single notification with packagePatternFlags', async () => {
|
||||
const name = 'package';
|
||||
// @ts-ignore
|
||||
await notify({name}, packagePatternNotificationConfig, { name: 'foo'}, 'bar');
|
||||
|
||||
await notify({ name }, packagePatternNotificationConfig, { name: 'foo' }, 'bar');
|
||||
|
||||
expect(notifyRequest).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("should not match on send single notification with packagePatternFlags", async () => {
|
||||
test('should not match on send single notification with packagePatternFlags', async () => {
|
||||
const name = 'no-mach-name';
|
||||
// @ts-ignore
|
||||
await notify({name}, packagePatternNotificationConfig, { name: 'foo'}, 'bar');
|
||||
await notify({ name }, packagePatternNotificationConfig, { name: 'foo' }, 'bar');
|
||||
|
||||
expect(notifyRequest).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -46,13 +46,7 @@ function isES6(plugin): boolean {
|
||||
* @param {*} sanityCheck callback that check the shape that should fulfill the plugin
|
||||
* @return {Array} list of plugins
|
||||
*/
|
||||
export function loadPlugin<T extends IPlugin<T>>(
|
||||
config: Config,
|
||||
pluginConfigs: any = {},
|
||||
params: any,
|
||||
sanityCheck: any,
|
||||
prefix: string = 'verdaccio'
|
||||
): any[] {
|
||||
export function loadPlugin<T extends IPlugin<T>>(config: Config, pluginConfigs: any = {}, params: any, sanityCheck: any, prefix: string = 'verdaccio'): any[] {
|
||||
return Object.keys(pluginConfigs).map(
|
||||
(pluginId: string): IPlugin<T> => {
|
||||
let plugin;
|
||||
@ -102,17 +96,17 @@ export function loadPlugin<T extends IPlugin<T>>(
|
||||
}
|
||||
|
||||
if (!isValid(plugin)) {
|
||||
logger.error({ content: pluginId }, "@{prefix}-@{content} plugin does not have the right code structure");
|
||||
logger.error({ content: pluginId }, '@{prefix}-@{content} plugin does not have the right code structure');
|
||||
throw Error(`"${pluginId}" plugin does not have the right code structure`);
|
||||
}
|
||||
|
||||
/* eslint new-cap:off */
|
||||
try {
|
||||
plugin = isES6(plugin) ? new plugin.default(mergeConfig(config, pluginConfigs[pluginId]), params) : plugin(pluginConfigs[pluginId], params);
|
||||
} catch (error) {
|
||||
plugin = null;
|
||||
logger.error({ error, pluginId }, "error loading a plugin @{pluginId}: @{error}");
|
||||
}
|
||||
try {
|
||||
plugin = isES6(plugin) ? new plugin.default(mergeConfig(config, pluginConfigs[pluginId]), params) : plugin(pluginConfigs[pluginId], params);
|
||||
} catch (error) {
|
||||
plugin = null;
|
||||
logger.error({ error, pluginId }, 'error loading a plugin @{pluginId}: @{error}');
|
||||
}
|
||||
/* eslint new-cap:off */
|
||||
|
||||
if (plugin === null || !sanityCheck(plugin)) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import path from 'path';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
|
||||
import { loadPlugin } from '../src/plugin-loader';
|
||||
|
||||
@ -7,13 +7,13 @@ setup([]);
|
||||
|
||||
describe('plugin loader', () => {
|
||||
const relativePath = path.join(__dirname, './partials/test-plugin-storage');
|
||||
const buildConf = name => {
|
||||
const buildConf = (name) => {
|
||||
return {
|
||||
self_path: path.join(__dirname, './'),
|
||||
max_users: 0,
|
||||
auth: {
|
||||
[`${relativePath}/${name}`]: {}
|
||||
}
|
||||
[`${relativePath}/${name}`]: {},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@ -45,7 +45,7 @@ describe('plugin loader', () => {
|
||||
loadPlugin(_config, _config.auth, {}, function (p) {
|
||||
return p.authenticate || p.allow_access || p.allow_publish;
|
||||
});
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
expect(e.message).toEqual(`"${relativePath}/invalid-plugin" plugin does not have the right code structure`);
|
||||
}
|
||||
});
|
||||
@ -57,7 +57,7 @@ describe('plugin loader', () => {
|
||||
loadPlugin(_config, _config.auth, {}, function (plugin) {
|
||||
return plugin.authenticate || plugin.allow_access || plugin.allow_publish;
|
||||
});
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
expect(err.message).toEqual(`sanity check has failed, "${relativePath}/invalid-plugin-sanity" is not a valid plugin`);
|
||||
}
|
||||
});
|
||||
@ -69,7 +69,7 @@ describe('plugin loader', () => {
|
||||
loadPlugin(_config, _config.auth, {}, function (plugin) {
|
||||
return plugin.authenticate || plugin.allow_access || plugin.allow_publish;
|
||||
});
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
expect(e.message).toMatch('plugin not found');
|
||||
expect(e.message).toMatch('/partials/test-plugin-storage/invalid-package');
|
||||
}
|
||||
@ -78,5 +78,4 @@ describe('plugin loader', () => {
|
||||
test.todo('test middleware plugins');
|
||||
test.todo('test storage plugins');
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -3,9 +3,9 @@ import { inspect } from 'util';
|
||||
import { white, red, green } from 'kleur';
|
||||
import padLeft from 'pad-left';
|
||||
|
||||
import {calculateLevel, LevelCode, levelsColors, subSystemLevels} from "./levels";
|
||||
import {formatLoggingDate, isObject, pad} from './utils';
|
||||
import {PrettyOptionsExtended} from "./types";
|
||||
import { calculateLevel, LevelCode, levelsColors, subSystemLevels } from './levels';
|
||||
import { formatLoggingDate, isObject, pad } from './utils';
|
||||
import { PrettyOptionsExtended } from './types';
|
||||
|
||||
let LEVEL_VALUE_MAX = 0;
|
||||
for (const l in levelsColors) {
|
||||
@ -19,13 +19,12 @@ export interface ObjectTemplate {
|
||||
msg: string;
|
||||
sub?: string;
|
||||
[key: string]: string | number | object | null | void;
|
||||
};
|
||||
}
|
||||
|
||||
export function fillInMsgTemplate(msg, templateOptions: ObjectTemplate, colors): string {
|
||||
const templateRegex = /@{(!?[$A-Za-z_][$0-9A-Za-z\._]*)}/g;
|
||||
|
||||
return msg.replace(templateRegex, (_, name): string => {
|
||||
|
||||
let str = templateOptions;
|
||||
let isError;
|
||||
if (name[0] === ERROR_FLAG) {
|
||||
@ -50,7 +49,7 @@ export function fillInMsgTemplate(msg, templateOptions: ObjectTemplate, colors):
|
||||
}
|
||||
return green(str);
|
||||
}
|
||||
|
||||
|
||||
// object, showHidden, depth, colors
|
||||
return inspect(str, undefined, null, colors);
|
||||
});
|
||||
@ -65,17 +64,14 @@ function getMessage(debugLevel, msg, sub, templateObjects, hasColors) {
|
||||
if (hasColors) {
|
||||
const logString = `${levelsColors[debugLevel](pad(debugLevel, LEVEL_VALUE_MAX))}${white(`${subSystemType} ${finalMessage}`)}`;
|
||||
|
||||
return padLeft(logString, logString.length + CUSTOM_PAD_LENGTH , ' ');
|
||||
return padLeft(logString, logString.length + CUSTOM_PAD_LENGTH, ' ');
|
||||
}
|
||||
const logString = `${pad(debugLevel, LEVEL_VALUE_MAX)}${subSystemType} ${finalMessage}`;
|
||||
|
||||
return padLeft(logString, logString.length + CUSTOM_PAD_LENGTH , ' ');
|
||||
return padLeft(logString, logString.length + CUSTOM_PAD_LENGTH, ' ');
|
||||
}
|
||||
|
||||
export function printMessage(
|
||||
templateObjects: ObjectTemplate,
|
||||
options: PrettyOptionsExtended,
|
||||
hasColors = true): string {
|
||||
export function printMessage(templateObjects: ObjectTemplate, options: PrettyOptionsExtended, hasColors = true): string {
|
||||
const { prettyStamp } = options;
|
||||
const { level, msg, sub } = templateObjects;
|
||||
const debugLevel = calculateLevel(level);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { printMessage } from "./formatter";
|
||||
import {PrettyOptionsExtended} from "./types";
|
||||
import { printMessage } from './formatter';
|
||||
import { PrettyOptionsExtended } from './types';
|
||||
|
||||
export type PrettyFactory = (param) => string;
|
||||
|
||||
@ -8,11 +8,11 @@ export type PrettyFactory = (param) => string;
|
||||
{ messageKey: 'msg', levelFirst: true, prettyStamp: false }
|
||||
*/
|
||||
|
||||
module.exports = function prettyFactory (options: PrettyOptionsExtended): PrettyFactory {
|
||||
// the break line must happens in the prettify component
|
||||
const breakLike = '\n';
|
||||
return (inputData): string => {
|
||||
// FIXME: review colors by default is true
|
||||
return printMessage(inputData, options, true) + breakLike;
|
||||
};
|
||||
module.exports = function prettyFactory(options: PrettyOptionsExtended): PrettyFactory {
|
||||
// the break line must happens in the prettify component
|
||||
const breakLike = '\n';
|
||||
return (inputData): string => {
|
||||
// FIXME: review colors by default is true
|
||||
return printMessage(inputData, options, true) + breakLike;
|
||||
};
|
||||
};
|
||||
|
@ -5,52 +5,52 @@ export type LogLevel = 'trace' | 'debug' | 'info' | 'http' | 'warn' | 'error' |
|
||||
export type LevelCode = number;
|
||||
|
||||
export function calculateLevel(levelCode: LevelCode): LogLevel {
|
||||
switch (true) {
|
||||
case levelCode < 15:
|
||||
return 'trace';
|
||||
case levelCode < 25:
|
||||
return 'debug';
|
||||
case levelCode < 35:
|
||||
return 'info';
|
||||
case levelCode == 35:
|
||||
return 'http';
|
||||
case levelCode < 45:
|
||||
return 'warn';
|
||||
case levelCode < 55:
|
||||
return 'error';
|
||||
default:
|
||||
return 'fatal';
|
||||
}
|
||||
switch (true) {
|
||||
case levelCode < 15:
|
||||
return 'trace';
|
||||
case levelCode < 25:
|
||||
return 'debug';
|
||||
case levelCode < 35:
|
||||
return 'info';
|
||||
case levelCode == 35:
|
||||
return 'http';
|
||||
case levelCode < 45:
|
||||
return 'warn';
|
||||
case levelCode < 55:
|
||||
return 'error';
|
||||
default:
|
||||
return 'fatal';
|
||||
}
|
||||
}
|
||||
|
||||
export const levelsColors = {
|
||||
fatal: red,
|
||||
error: red,
|
||||
warn: yellow,
|
||||
http: magenta,
|
||||
info: cyan,
|
||||
debug: green,
|
||||
trace: white,
|
||||
fatal: red,
|
||||
error: red,
|
||||
warn: yellow,
|
||||
http: magenta,
|
||||
info: cyan,
|
||||
debug: green,
|
||||
trace: white,
|
||||
};
|
||||
|
||||
enum ARROWS {
|
||||
LEFT = '<--',
|
||||
RIGHT = '-->',
|
||||
EQUAL = '-=-',
|
||||
NEUTRAL = '---'
|
||||
LEFT = '<--',
|
||||
RIGHT = '-->',
|
||||
EQUAL = '-=-',
|
||||
NEUTRAL = '---',
|
||||
}
|
||||
|
||||
export const subSystemLevels = {
|
||||
color: {
|
||||
in: green(ARROWS.LEFT),
|
||||
out: yellow(ARROWS.RIGHT),
|
||||
fs: black(ARROWS.EQUAL),
|
||||
default: blue(ARROWS.NEUTRAL),
|
||||
},
|
||||
white: {
|
||||
in: ARROWS.LEFT,
|
||||
out: ARROWS.RIGHT,
|
||||
fs: ARROWS.EQUAL,
|
||||
default: ARROWS.NEUTRAL,
|
||||
},
|
||||
color: {
|
||||
in: green(ARROWS.LEFT),
|
||||
out: yellow(ARROWS.RIGHT),
|
||||
fs: black(ARROWS.EQUAL),
|
||||
default: blue(ARROWS.NEUTRAL),
|
||||
},
|
||||
white: {
|
||||
in: ARROWS.LEFT,
|
||||
out: ARROWS.RIGHT,
|
||||
fs: ARROWS.EQUAL,
|
||||
default: ARROWS.NEUTRAL,
|
||||
},
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {PrettyOptions} from "pino";
|
||||
import { PrettyOptions } from 'pino';
|
||||
|
||||
export interface PrettyOptionsExtended extends PrettyOptions {
|
||||
prettyStamp: boolean;
|
||||
export interface PrettyOptionsExtended extends PrettyOptions {
|
||||
prettyStamp: boolean;
|
||||
}
|
||||
|
@ -5,15 +5,15 @@ import dayjs from 'dayjs';
|
||||
export const FORMAT_DATE = 'YYYY-MM-DD HH:mm:ss';
|
||||
|
||||
export function isObject(obj: unknown): boolean {
|
||||
return _.isObject(obj) && _.isNull(obj) === false && _.isArray(obj) === false;
|
||||
return _.isObject(obj) && _.isNull(obj) === false && _.isArray(obj) === false;
|
||||
}
|
||||
|
||||
export function pad(str: string, max: number): string {
|
||||
return padRight(str, max, ' ');
|
||||
return padRight(str, max, ' ');
|
||||
}
|
||||
|
||||
export function formatLoggingDate(time: number, message): string {
|
||||
const timeFormatted = dayjs(time).format(FORMAT_DATE);
|
||||
const timeFormatted = dayjs(time).format(FORMAT_DATE);
|
||||
|
||||
return `[${timeFormatted}]${message}`;
|
||||
return `[${timeFormatted}]${message}`;
|
||||
}
|
||||
|
@ -1,213 +1,217 @@
|
||||
import {printMessage} from "../src/formatter";
|
||||
import {LevelCode} from "../src/levels";
|
||||
import { printMessage } from '../src/formatter';
|
||||
import { LevelCode } from '../src/levels';
|
||||
|
||||
jest.mock('dayjs', () => ({
|
||||
__esModule: true,
|
||||
default: () => ({
|
||||
format: () => 'formatted-date'
|
||||
})
|
||||
__esModule: true,
|
||||
default: () => ({
|
||||
format: () => 'formatted-date',
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('formatter', () => {
|
||||
const prettyfierOptions = {messageKey: 'msg', levelFirst: true, prettyStamp: false};
|
||||
describe('printMessage', () => {
|
||||
test('should display config file', () => {
|
||||
const log = {
|
||||
level: 40,
|
||||
time: 1585410824129,
|
||||
v: 1,
|
||||
pid: 27029,
|
||||
hostname: 'localhost',
|
||||
file: '/Users/user/.config/verdaccio/config/config.yaml',
|
||||
msg: 'config file - @{file}'
|
||||
};
|
||||
const prettyfierOptions = { messageKey: 'msg', levelFirst: true, prettyStamp: false };
|
||||
describe('printMessage', () => {
|
||||
test('should display config file', () => {
|
||||
const log = {
|
||||
level: 40,
|
||||
time: 1585410824129,
|
||||
v: 1,
|
||||
pid: 27029,
|
||||
hostname: 'localhost',
|
||||
file: '/Users/user/.config/verdaccio/config/config.yaml',
|
||||
msg: 'config file - @{file}',
|
||||
};
|
||||
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should display trace level', () => {
|
||||
const log = {
|
||||
level: 10,
|
||||
foo: 'foo',
|
||||
msg: '[trace] - @{foo}'
|
||||
};
|
||||
test('should display trace level', () => {
|
||||
const log = {
|
||||
level: 10,
|
||||
foo: 'foo',
|
||||
msg: '[trace] - @{foo}',
|
||||
};
|
||||
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should display trace level with pretty stamp', () => {
|
||||
const log = {
|
||||
level: 10,
|
||||
foo: 'foo',
|
||||
time: 1585411248203,
|
||||
msg: '[trace] - @{foo}'
|
||||
};
|
||||
test('should display trace level with pretty stamp', () => {
|
||||
const log = {
|
||||
level: 10,
|
||||
foo: 'foo',
|
||||
time: 1585411248203,
|
||||
msg: '[trace] - @{foo}',
|
||||
};
|
||||
|
||||
expect(printMessage(log, Object.assign({}, prettyfierOptions, {
|
||||
prettyStamp: true
|
||||
}))).toMatchSnapshot();
|
||||
});
|
||||
expect(
|
||||
printMessage(
|
||||
log,
|
||||
Object.assign({}, prettyfierOptions, {
|
||||
prettyStamp: true,
|
||||
})
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should display a bytes request', () => {
|
||||
const log = {
|
||||
level: 35,
|
||||
time: 1585411248203,
|
||||
v: 1,
|
||||
pid: 27029,
|
||||
hostname: 'macbook-touch',
|
||||
sub: 'in',
|
||||
request: {method: 'GET', url: '/verdaccio'},
|
||||
user: null,
|
||||
remoteIP: '127.0.0.1',
|
||||
status: 200,
|
||||
error: undefined,
|
||||
bytes: {in: 0, out: 150186},
|
||||
msg: "@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}', bytes: @{bytes.in}/@{bytes.out}"
|
||||
};
|
||||
test('should display a bytes request', () => {
|
||||
const log = {
|
||||
level: 35,
|
||||
time: 1585411248203,
|
||||
v: 1,
|
||||
pid: 27029,
|
||||
hostname: 'macbook-touch',
|
||||
sub: 'in',
|
||||
request: { method: 'GET', url: '/verdaccio' },
|
||||
user: null,
|
||||
remoteIP: '127.0.0.1',
|
||||
status: 200,
|
||||
error: undefined,
|
||||
bytes: { in: 0, out: 150186 },
|
||||
msg: "@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}', bytes: @{bytes.in}/@{bytes.out}",
|
||||
};
|
||||
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should display an error request', () => {
|
||||
const log = {
|
||||
level: 54,
|
||||
time: 1585416029123,
|
||||
v: 1,
|
||||
pid: 30032,
|
||||
hostname: 'macbook-touch',
|
||||
sub: 'out',
|
||||
err: {
|
||||
type: 'Error',
|
||||
message: 'getaddrinfo ENOTFOUND registry.fake.org',
|
||||
stack: 'Error: getaddrinfo ENOTFOUND registry.fake.org\n' +
|
||||
' at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:60:26)',
|
||||
errno: -3008,
|
||||
code: 'ENOTFOUND',
|
||||
syscall: 'getaddrinfo',
|
||||
hostname: 'registry.fake.org'
|
||||
},
|
||||
request: {method: 'GET', url: 'https://registry.fake.org/aaa'},
|
||||
status: 'ERR',
|
||||
error: 'getaddrinfo ENOTFOUND registry.fake.org',
|
||||
bytes: {in: 0, out: 0},
|
||||
msg: "@{!status}, req: '@{request.method} @{request.url}', error: @{!error}"
|
||||
};
|
||||
test('should display an error request', () => {
|
||||
const log = {
|
||||
level: 54,
|
||||
time: 1585416029123,
|
||||
v: 1,
|
||||
pid: 30032,
|
||||
hostname: 'macbook-touch',
|
||||
sub: 'out',
|
||||
err: {
|
||||
type: 'Error',
|
||||
message: 'getaddrinfo ENOTFOUND registry.fake.org',
|
||||
stack: 'Error: getaddrinfo ENOTFOUND registry.fake.org\n' + ' at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:60:26)',
|
||||
errno: -3008,
|
||||
code: 'ENOTFOUND',
|
||||
syscall: 'getaddrinfo',
|
||||
hostname: 'registry.fake.org',
|
||||
},
|
||||
request: { method: 'GET', url: 'https://registry.fake.org/aaa' },
|
||||
status: 'ERR',
|
||||
error: 'getaddrinfo ENOTFOUND registry.fake.org',
|
||||
bytes: { in: 0, out: 0 },
|
||||
msg: "@{!status}, req: '@{request.method} @{request.url}', error: @{!error}",
|
||||
};
|
||||
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should display an fatal request', () => {
|
||||
const log = {
|
||||
level: 60,
|
||||
time: 1585416029123,
|
||||
v: 1,
|
||||
pid: 30032,
|
||||
hostname: 'macbook-touch',
|
||||
sub: 'out',
|
||||
err: {
|
||||
type: 'Error',
|
||||
message: 'fatal error',
|
||||
stack: '....',
|
||||
errno: -3008,
|
||||
code: 'ENOTFOUND',
|
||||
},
|
||||
request: {method: 'GET', url: 'https://registry.fake.org/aaa'},
|
||||
status: 'ERR',
|
||||
error: 'fatal error',
|
||||
bytes: {in: 0, out: 0},
|
||||
msg: "@{!status}, req: '@{request.method} @{request.url}', error: @{!error}"
|
||||
};
|
||||
test('should display an fatal request', () => {
|
||||
const log = {
|
||||
level: 60,
|
||||
time: 1585416029123,
|
||||
v: 1,
|
||||
pid: 30032,
|
||||
hostname: 'macbook-touch',
|
||||
sub: 'out',
|
||||
err: {
|
||||
type: 'Error',
|
||||
message: 'fatal error',
|
||||
stack: '....',
|
||||
errno: -3008,
|
||||
code: 'ENOTFOUND',
|
||||
},
|
||||
request: { method: 'GET', url: 'https://registry.fake.org/aaa' },
|
||||
status: 'ERR',
|
||||
error: 'fatal error',
|
||||
bytes: { in: 0, out: 0 },
|
||||
msg: "@{!status}, req: '@{request.method} @{request.url}', error: @{!error}",
|
||||
};
|
||||
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should display a streaming request', () => {
|
||||
const log = {
|
||||
level: 35,
|
||||
time: 1585411247920,
|
||||
v: 1,
|
||||
pid: 27029,
|
||||
hostname: 'macbook-touch',
|
||||
sub: 'out',
|
||||
request: {method: 'GET', url: 'https://registry.npmjs.org/verdaccio'},
|
||||
status: 304,
|
||||
msg: "@{!status}, req: '@{request.method} @{request.url}' (streaming)"
|
||||
};
|
||||
test('should display a streaming request', () => {
|
||||
const log = {
|
||||
level: 35,
|
||||
time: 1585411247920,
|
||||
v: 1,
|
||||
pid: 27029,
|
||||
hostname: 'macbook-touch',
|
||||
sub: 'out',
|
||||
request: { method: 'GET', url: 'https://registry.npmjs.org/verdaccio' },
|
||||
status: 304,
|
||||
msg: "@{!status}, req: '@{request.method} @{request.url}' (streaming)",
|
||||
};
|
||||
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should display version and http address', () => {
|
||||
const log = {
|
||||
level: 40,
|
||||
time: 1585410824322,
|
||||
v: 1,
|
||||
pid: 27029,
|
||||
hostname: 'macbook-touch',
|
||||
addr: 'http://localhost:4873/',
|
||||
version: 'verdaccio/5.0.0',
|
||||
msg: 'http address - @{addr} - @{version}'
|
||||
};
|
||||
test('should display version and http address', () => {
|
||||
const log = {
|
||||
level: 40,
|
||||
time: 1585410824322,
|
||||
v: 1,
|
||||
pid: 27029,
|
||||
hostname: 'macbook-touch',
|
||||
addr: 'http://localhost:4873/',
|
||||
version: 'verdaccio/5.0.0',
|
||||
msg: 'http address - @{addr} - @{version}',
|
||||
};
|
||||
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should display custom log message', () => {
|
||||
const level: LevelCode = 15;
|
||||
const log = {
|
||||
level,
|
||||
something: 'foo',
|
||||
msg: 'custom - @{something} - @{missingParam}'
|
||||
};
|
||||
test('should display custom log message', () => {
|
||||
const level: LevelCode = 15;
|
||||
const log = {
|
||||
level,
|
||||
something: 'foo',
|
||||
msg: 'custom - @{something} - @{missingParam}',
|
||||
};
|
||||
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
expect(printMessage(log, prettyfierOptions)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// test('should handle undefined object', () => {
|
||||
// const log = {
|
||||
// level: 15,
|
||||
// something: 'foo',
|
||||
// msg: 'custom - @{something} - @{missingParam}'
|
||||
// };
|
||||
//
|
||||
// expect(printMessage(undefined, undefined, undefined)).toMatchSnapshot();
|
||||
// });
|
||||
// test('should handle undefined object', () => {
|
||||
// const log = {
|
||||
// level: 15,
|
||||
// something: 'foo',
|
||||
// msg: 'custom - @{something} - @{missingParam}'
|
||||
// };
|
||||
//
|
||||
// expect(printMessage(undefined, undefined, undefined)).toMatchSnapshot();
|
||||
// });
|
||||
|
||||
test('should display a resource request', () => {
|
||||
const log = {
|
||||
level: 30,
|
||||
time: 1585411247622,
|
||||
v: 1,
|
||||
pid: 27029,
|
||||
hostname: 'macbook-touch',
|
||||
sub: 'in',
|
||||
req: {
|
||||
id: undefined,
|
||||
method: 'GET',
|
||||
url: '/verdaccio',
|
||||
headers: {
|
||||
connection: 'keep-alive',
|
||||
'user-agent': 'npm/6.13.2 node/v13.1.0 darwin x64',
|
||||
'npm-in-ci': 'false',
|
||||
'npm-scope': '',
|
||||
'npm-session': 'afebb4748178bd4b',
|
||||
referer: 'view verdaccio',
|
||||
'pacote-req-type': 'packument',
|
||||
'pacote-pkg-id': 'registry:verdaccio',
|
||||
accept: 'application/json',
|
||||
authorization: '<Classified>',
|
||||
'if-none-match': '"fd6440ba2ad24681077664d8f969e5c3"',
|
||||
'accept-encoding': 'gzip,deflate',
|
||||
host: 'localhost:4873'
|
||||
},
|
||||
remoteAddress: '127.0.0.1',
|
||||
remotePort: 57968,
|
||||
},
|
||||
ip: '127.0.0.1',
|
||||
msg: "@{ip} requested '@{req.method} @{req.url}'"
|
||||
};
|
||||
test('should display a resource request', () => {
|
||||
const log = {
|
||||
level: 30,
|
||||
time: 1585411247622,
|
||||
v: 1,
|
||||
pid: 27029,
|
||||
hostname: 'macbook-touch',
|
||||
sub: 'in',
|
||||
req: {
|
||||
id: undefined,
|
||||
method: 'GET',
|
||||
url: '/verdaccio',
|
||||
headers: {
|
||||
connection: 'keep-alive',
|
||||
'user-agent': 'npm/6.13.2 node/v13.1.0 darwin x64',
|
||||
'npm-in-ci': 'false',
|
||||
'npm-scope': '',
|
||||
'npm-session': 'afebb4748178bd4b',
|
||||
referer: 'view verdaccio',
|
||||
'pacote-req-type': 'packument',
|
||||
'pacote-pkg-id': 'registry:verdaccio',
|
||||
accept: 'application/json',
|
||||
authorization: '<Classified>',
|
||||
'if-none-match': '"fd6440ba2ad24681077664d8f969e5c3"',
|
||||
'accept-encoding': 'gzip,deflate',
|
||||
host: 'localhost:4873',
|
||||
},
|
||||
remoteAddress: '127.0.0.1',
|
||||
remotePort: 57968,
|
||||
},
|
||||
ip: '127.0.0.1',
|
||||
msg: "@{ip} requested '@{req.method} @{req.url}'",
|
||||
};
|
||||
|
||||
expect(printMessage(log, prettyfierOptions, false)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
expect(printMessage(log, prettyfierOptions, false)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,18 +1,18 @@
|
||||
import * as factory from '../src';
|
||||
|
||||
describe('prettyFactory', () => {
|
||||
const prettyfierOptions = { messageKey: 'msg', levelFirst: true, prettyStamp: false };
|
||||
test('should return a function', () => {
|
||||
expect(typeof factory['default']({})).toEqual('function')
|
||||
});
|
||||
const prettyfierOptions = { messageKey: 'msg', levelFirst: true, prettyStamp: false };
|
||||
test('should return a function', () => {
|
||||
expect(typeof factory['default']({})).toEqual('function');
|
||||
});
|
||||
|
||||
test('should return a function', () => {
|
||||
const log = {
|
||||
level: 10,
|
||||
foo: 'foo',
|
||||
msg: '[trace] - @{foo}'
|
||||
};
|
||||
test('should return a function', () => {
|
||||
const log = {
|
||||
level: 10,
|
||||
foo: 'foo',
|
||||
msg: '[trace] - @{foo}',
|
||||
};
|
||||
|
||||
expect(factory['default'](prettyfierOptions)(log)).toMatchSnapshot();
|
||||
});
|
||||
expect(factory['default'](prettyfierOptions)(log)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -1,2 +1 @@
|
||||
export { setup, createLogger, logger } from './logger';
|
||||
|
||||
|
@ -6,112 +6,112 @@ const DEFAULT_LOG_FORMAT = 'pretty';
|
||||
export let logger;
|
||||
|
||||
function getPrettifier() {
|
||||
// TODO: this module can be loaded dynamically and allow custom formatting
|
||||
return require('@verdaccio/logger-prettify');
|
||||
// TODO: this module can be loaded dynamically and allow custom formatting
|
||||
return require('@verdaccio/logger-prettify');
|
||||
}
|
||||
|
||||
export type LogPlugin = {
|
||||
dest: string;
|
||||
options?: any[];
|
||||
dest: string;
|
||||
options?: any[];
|
||||
};
|
||||
|
||||
export type LogType = 'file' | 'stdout';
|
||||
export type LogFormat = 'json' | 'pretty-timestamped' | 'pretty';
|
||||
|
||||
export function createLogger(options = {},
|
||||
destination = pino.destination(1),
|
||||
format: LogFormat = DEFAULT_LOG_FORMAT,
|
||||
prettyPrintOptions = {}) {
|
||||
if (_.isNil(format)) {
|
||||
format = DEFAULT_LOG_FORMAT;
|
||||
}
|
||||
export function createLogger(options = {}, destination = pino.destination(1), format: LogFormat = DEFAULT_LOG_FORMAT, prettyPrintOptions = {}) {
|
||||
if (_.isNil(format)) {
|
||||
format = DEFAULT_LOG_FORMAT;
|
||||
}
|
||||
|
||||
let pinoConfig = {
|
||||
...options,
|
||||
customLevels: {
|
||||
http: 35
|
||||
},
|
||||
serializers: {
|
||||
err: pino.stdSerializers.err,
|
||||
req: pino.stdSerializers.req,
|
||||
res: pino.stdSerializers.res
|
||||
},
|
||||
};
|
||||
let pinoConfig = {
|
||||
...options,
|
||||
customLevels: {
|
||||
http: 35,
|
||||
},
|
||||
serializers: {
|
||||
err: pino.stdSerializers.err,
|
||||
req: pino.stdSerializers.req,
|
||||
res: pino.stdSerializers.res,
|
||||
},
|
||||
};
|
||||
|
||||
if (format === DEFAULT_LOG_FORMAT || format !== 'json') {
|
||||
pinoConfig = Object.assign({}, pinoConfig, {
|
||||
prettyPrint: {
|
||||
levelFirst: true,
|
||||
prettyStamp: format === 'pretty-timestamped',
|
||||
...prettyPrintOptions
|
||||
},
|
||||
prettifier: getPrettifier(),
|
||||
}
|
||||
)
|
||||
}
|
||||
if (format === DEFAULT_LOG_FORMAT || format !== 'json') {
|
||||
pinoConfig = Object.assign({}, pinoConfig, {
|
||||
prettyPrint: {
|
||||
levelFirst: true,
|
||||
prettyStamp: format === 'pretty-timestamped',
|
||||
...prettyPrintOptions,
|
||||
},
|
||||
prettifier: getPrettifier(),
|
||||
});
|
||||
}
|
||||
|
||||
return pino(pinoConfig, destination);
|
||||
return pino(pinoConfig, destination);
|
||||
}
|
||||
|
||||
export function getLogger() {
|
||||
if (_.isNil(logger)) {
|
||||
console.warn('logger is not defined');
|
||||
return;
|
||||
}
|
||||
if (_.isNil(logger)) {
|
||||
console.warn('logger is not defined');
|
||||
return;
|
||||
}
|
||||
|
||||
return logger;
|
||||
return logger;
|
||||
}
|
||||
|
||||
const DEFAULT_LOGGER_CONF: LoggerConfigItem = {
|
||||
type: 'stdout',
|
||||
format: 'pretty',
|
||||
level: 'http'
|
||||
type: 'stdout',
|
||||
format: 'pretty',
|
||||
level: 'http',
|
||||
};
|
||||
|
||||
export type LoggerConfigItem = {
|
||||
type?: LogType;
|
||||
plugin?: LogPlugin;
|
||||
format?: LogFormat;
|
||||
path?: string;
|
||||
level?: string;
|
||||
}
|
||||
type?: LogType;
|
||||
plugin?: LogPlugin;
|
||||
format?: LogFormat;
|
||||
path?: string;
|
||||
level?: string;
|
||||
};
|
||||
|
||||
export type LoggerConfig = LoggerConfigItem[];
|
||||
|
||||
export function setup(options: LoggerConfig | LoggerConfigItem = [DEFAULT_LOGGER_CONF]) {
|
||||
const isLegacyConf = _.isArray(options);
|
||||
if (isLegacyConf) {
|
||||
// FIXME: re-enable later
|
||||
// console.warn("DEPRECATE: logs does not have multi-stream support anymore, please upgrade your logger configuration");
|
||||
}
|
||||
const isLegacyConf = _.isArray(options);
|
||||
if (isLegacyConf) {
|
||||
// FIXME: re-enable later
|
||||
// console.warn("DEPRECATE: logs does not have multi-stream support anymore, please upgrade your logger configuration");
|
||||
}
|
||||
|
||||
// backward compatible, pick only the first option
|
||||
let loggerConfig = isLegacyConf ? options[0] : options;
|
||||
if (!loggerConfig?.level) {
|
||||
loggerConfig = Object.assign({}, loggerConfig, {
|
||||
level: 'http'
|
||||
})
|
||||
}
|
||||
// backward compatible, pick only the first option
|
||||
let loggerConfig = isLegacyConf ? options[0] : options;
|
||||
if (!loggerConfig?.level) {
|
||||
loggerConfig = Object.assign({}, loggerConfig, {
|
||||
level: 'http',
|
||||
});
|
||||
}
|
||||
|
||||
const pinoConfig = { level: loggerConfig.level };
|
||||
if (loggerConfig.type === 'file') {
|
||||
logger = createLogger(pinoConfig,
|
||||
pino.destination(loggerConfig.path),
|
||||
loggerConfig.format);
|
||||
} else if(loggerConfig.type === 'rotating-file') {
|
||||
throw new Error('rotating-file type is not longer supported, consider use [logrotate] instead');
|
||||
} else {
|
||||
logger = createLogger(pinoConfig, pino.destination(1), loggerConfig.format);
|
||||
}
|
||||
const pinoConfig = { level: loggerConfig.level };
|
||||
if (loggerConfig.type === 'file') {
|
||||
logger = createLogger(pinoConfig, pino.destination(loggerConfig.path), loggerConfig.format);
|
||||
} else if (loggerConfig.type === 'rotating-file') {
|
||||
throw new Error('rotating-file type is not longer supported, consider use [logrotate] instead');
|
||||
} else {
|
||||
logger = createLogger(pinoConfig, pino.destination(1), loggerConfig.format);
|
||||
}
|
||||
|
||||
process.on('uncaughtException', pino.final(logger, (err, finalLogger) => {
|
||||
finalLogger.fatal(err, 'uncaughtException');
|
||||
process.exit(1);
|
||||
}));
|
||||
process.on(
|
||||
'uncaughtException',
|
||||
pino.final(logger, (err, finalLogger) => {
|
||||
finalLogger.fatal(err, 'uncaughtException');
|
||||
process.exit(1);
|
||||
})
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
process.on('unhandledRejection', pino.final(logger, (err, finalLogger) => {
|
||||
finalLogger.fatal(err, 'uncaughtException');
|
||||
process.exit(1);
|
||||
}));
|
||||
// @ts-ignore
|
||||
process.on(
|
||||
'unhandledRejection',
|
||||
pino.final(logger, (err, finalLogger) => {
|
||||
finalLogger.fatal(err, 'uncaughtException');
|
||||
process.exit(1);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -1,15 +1,16 @@
|
||||
import { logger, setup } from "../src";
|
||||
|
||||
import { logger, setup } from '../src';
|
||||
|
||||
describe('logger', () => {
|
||||
test('dsadasd', () => {
|
||||
const spyOn = jest.spyOn( process.stdout, 'write');
|
||||
setup([{
|
||||
level: 'info'
|
||||
}]);
|
||||
test('dsadasd', () => {
|
||||
const spyOn = jest.spyOn(process.stdout, 'write');
|
||||
setup([
|
||||
{
|
||||
level: 'info',
|
||||
},
|
||||
]);
|
||||
|
||||
logger.info({packageName: 'test'} , `publishing or updating a new version for @{packageName}`);
|
||||
logger.info({ packageName: 'test' }, `publishing or updating a new version for @{packageName}`);
|
||||
|
||||
// expect(spyOn).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
// expect(spyOn).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import {
|
||||
@ -7,17 +6,17 @@ import {
|
||||
getVersionFromTarball,
|
||||
isObject,
|
||||
stringToMD5,
|
||||
ErrorCode
|
||||
ErrorCode,
|
||||
} from '@verdaccio/utils';
|
||||
import { API_ERROR, HEADER_TYPE, HEADERS, HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
import { $ResponseExtend, $RequestExtend, $NextFunctionVer, IAuth } from '@verdaccio/dev-types';
|
||||
import { Config, Package, RemoteUser } from '@verdaccio/types';
|
||||
import { logger } from '@verdaccio/logger';
|
||||
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
import {HttpError} from "http-errors";
|
||||
import { HttpError } from 'http-errors';
|
||||
|
||||
export function match(regexp: RegExp): any {
|
||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer, value: string): void {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer, value: string): void {
|
||||
if (regexp.exec(value)) {
|
||||
next();
|
||||
} else {
|
||||
@ -65,7 +64,7 @@ export function validatePackage(req: $RequestExtend, res: $ResponseExtend, next:
|
||||
}
|
||||
|
||||
export function media(expect: string | null): any {
|
||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
if (req.headers[HEADER_TYPE.CONTENT_TYPE] !== expect) {
|
||||
next(ErrorCode.getCode(HTTP_STATUS.UNSUPPORTED_MEDIA, 'wrong content-type, expect: ' + expect + ', got: ' + req.headers[HEADER_TYPE.CONTENT_TYPE]));
|
||||
} else {
|
||||
@ -90,7 +89,7 @@ export function expectJson(req: $RequestExtend, res: $ResponseExtend, next: $Nex
|
||||
}
|
||||
|
||||
export function antiLoop(config: Config): Function {
|
||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
if (req.headers.via != null) {
|
||||
const arr = req.headers.via.split(',');
|
||||
|
||||
@ -106,14 +105,14 @@ export function antiLoop(config: Config): Function {
|
||||
}
|
||||
|
||||
export function allow(auth: IAuth): Function {
|
||||
return function(action: string): Function {
|
||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
return function (action: string): Function {
|
||||
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
req.pause();
|
||||
const packageName = req.params.scope ? `@${req.params.scope}/${req.params.package}` : req.params.package;
|
||||
const packageVersion = req.params.filename ? getVersionFromTarball(req.params.filename) : undefined;
|
||||
const remote: RemoteUser = req.remote_user;
|
||||
logger.trace({ action, user: remote?.name }, `[middleware/allow][@{action}] allow for @{user}`);
|
||||
auth['allow_' + action]({ packageName, packageVersion }, remote, function(error, allowed): void {
|
||||
auth['allow_' + action]({ packageName, packageVersion }, remote, function (error, allowed): void {
|
||||
req.resume();
|
||||
if (error) {
|
||||
next(error);
|
||||
@ -210,7 +209,7 @@ export function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFuncti
|
||||
}
|
||||
|
||||
let bytesin = 0;
|
||||
req.on('data', function(chunk): void {
|
||||
req.on('data', function (chunk): void {
|
||||
bytesin += chunk.length;
|
||||
});
|
||||
|
||||
@ -218,14 +217,14 @@ export function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFuncti
|
||||
const _write = res.write;
|
||||
// FIXME: res.write should return boolean
|
||||
// @ts-ignore
|
||||
res.write = function(buf): boolean {
|
||||
res.write = function (buf): boolean {
|
||||
bytesout += buf.length;
|
||||
/* eslint prefer-rest-params: "off" */
|
||||
// @ts-ignore
|
||||
_write.apply(res, arguments);
|
||||
};
|
||||
|
||||
const log = function(): void {
|
||||
const log = function (): void {
|
||||
const forwardedFor = req.headers['x-forwarded-for'];
|
||||
const remoteAddress = req.connection.remoteAddress;
|
||||
const remoteIP = forwardedFor ? `${forwardedFor} via ${remoteAddress}` : remoteAddress;
|
||||
@ -258,12 +257,12 @@ export function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFuncti
|
||||
req.originalUrl = req.url;
|
||||
};
|
||||
|
||||
req.on('close', function(): void {
|
||||
req.on('close', function (): void {
|
||||
log();
|
||||
});
|
||||
|
||||
const _end = res.end;
|
||||
res.end = function(buf): void {
|
||||
res.end = function (buf): void {
|
||||
if (buf) {
|
||||
bytesout += buf.length;
|
||||
}
|
||||
@ -296,7 +295,7 @@ export function handleError(err: HttpError, req: $RequestExtend, res: $ResponseE
|
||||
export function errorReportingMiddleware(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
res.locals.report_error =
|
||||
res.locals.report_error ||
|
||||
function(err: VerdaccioError): void {
|
||||
function (err: VerdaccioError): void {
|
||||
if (err.status && err.status >= HTTP_STATUS.BAD_REQUEST && err.status < 600) {
|
||||
if (_.isNil(res.headersSent) === false) {
|
||||
res.status(err.status);
|
||||
|
@ -1,21 +1,16 @@
|
||||
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
|
||||
import {parseConfigFile} from '@verdaccio/utils';
|
||||
import { parseConfigFile } from '@verdaccio/utils';
|
||||
|
||||
/**
|
||||
* Override the default.yaml configuration file with any new config provided.
|
||||
*/
|
||||
function configExample(externalConfig, configFile: string = 'default.yaml', location: string = '') {
|
||||
const locationFile = location ? path.join(location, configFile) :
|
||||
path.join(__dirname, `./config/yaml/${configFile}`);
|
||||
const locationFile = location ? path.join(location, configFile) : path.join(__dirname, `./config/yaml/${configFile}`);
|
||||
const config = parseConfigFile(locationFile);
|
||||
|
||||
return _.assign({}, _.cloneDeep(config), externalConfig);
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
configExample
|
||||
}
|
||||
export { configExample };
|
||||
|
@ -1,7 +1,7 @@
|
||||
export const DOMAIN_SERVERS = '0.0.0.0';
|
||||
export const CREDENTIALS = {
|
||||
user: 'test',
|
||||
password: 'test'
|
||||
user: 'test',
|
||||
password: 'test',
|
||||
};
|
||||
|
||||
export const TARBALL = 'tarball-blahblah-file.name';
|
||||
|
@ -1,9 +1,9 @@
|
||||
import _ from 'lodash';
|
||||
import request from 'supertest';
|
||||
|
||||
import {DIST_TAGS, LATEST, HEADER_TYPE, HEADERS, HTTP_STATUS, TOKEN_BEARER} from '@verdaccio/dev-commons';
|
||||
import {buildToken, encodeScopedUri} from '@verdaccio/utils';
|
||||
import {generateRandomHexString} from "@verdaccio/utils";
|
||||
import { DIST_TAGS, LATEST, HEADER_TYPE, HEADERS, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
import { buildToken, encodeScopedUri } from '@verdaccio/utils';
|
||||
import { generateRandomHexString } from '@verdaccio/utils';
|
||||
import { Package } from '@verdaccio/types';
|
||||
|
||||
// API Helpers
|
||||
@ -29,57 +29,43 @@ export function getTaggedVersionFromPackage(pkg, pkgName, tag: string = LATEST,
|
||||
return latestPkg;
|
||||
}
|
||||
|
||||
|
||||
export function putPackage(
|
||||
request: any,
|
||||
pkgName: string,
|
||||
publishMetadata: Package,
|
||||
token?: string): Promise<any[]> {
|
||||
export function putPackage(request: any, pkgName: string, publishMetadata: Package, token?: string): Promise<any[]> {
|
||||
return new Promise((resolve) => {
|
||||
const put = request.put(pkgName)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(publishMetadata));
|
||||
const put = request.put(pkgName).set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON).send(JSON.stringify(publishMetadata));
|
||||
|
||||
if (_.isEmpty(token) === false ) {
|
||||
expect(token).toBeDefined();
|
||||
put.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token as string))
|
||||
if (_.isEmpty(token) === false) {
|
||||
expect(token).toBeDefined();
|
||||
put.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token as string));
|
||||
}
|
||||
|
||||
put.set('accept', 'gzip')
|
||||
.set('accept-encoding', HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
resolve([err, res]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function deletePackage(
|
||||
request: any,
|
||||
pkgName: string,
|
||||
token?: string
|
||||
): Promise<any[]> {
|
||||
return new Promise((resolve) => {
|
||||
const del = request.put(`/${encodeScopedUri(pkgName)}/-rev/${generateRandomHexString(8)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
||||
|
||||
if (_.isNil(token) === false ) {
|
||||
del.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token as string))
|
||||
}
|
||||
|
||||
del.set('accept-encoding', HEADERS.JSON)
|
||||
put
|
||||
.set('accept', 'gzip')
|
||||
.set('accept-encoding', HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
resolve([err, res]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getPackage(
|
||||
request: any,
|
||||
token: string,
|
||||
pkgName: string,
|
||||
statusCode: number = HTTP_STATUS.OK): Promise<any[]> {
|
||||
export function deletePackage(request: any, pkgName: string, token?: string): Promise<any[]> {
|
||||
return new Promise((resolve) => {
|
||||
const del = request.put(`/${encodeScopedUri(pkgName)}/-rev/${generateRandomHexString(8)}`).set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON);
|
||||
|
||||
if (_.isNil(token) === false) {
|
||||
del.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token as string));
|
||||
}
|
||||
|
||||
del
|
||||
.set('accept-encoding', HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function (err, res) {
|
||||
resolve([err, res]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getPackage(request: any, token: string, pkgName: string, statusCode: number = HTTP_STATUS.OK): Promise<any[]> {
|
||||
return new Promise((resolve) => {
|
||||
const getRequest = request.get(`/${pkgName}`);
|
||||
|
||||
@ -90,39 +76,36 @@ export function getPackage(
|
||||
getRequest
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(statusCode)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
resolve([err, res]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function loginUserToken(request: any,
|
||||
user: string,
|
||||
credentials: any,
|
||||
token: string,
|
||||
statusCode: number = HTTP_STATUS.CREATED): Promise<any[]> {
|
||||
export function loginUserToken(request: any, user: string, credentials: any, token: string, statusCode: number = HTTP_STATUS.CREATED): Promise<any[]> {
|
||||
// $FlowFixMe
|
||||
return new Promise((resolve) => {
|
||||
request.put(`/-/user/org.couchdb.user:${user}`)
|
||||
request
|
||||
.put(`/-/user/org.couchdb.user:${user}`)
|
||||
.send(credentials)
|
||||
.set('authorization', buildToken(TOKEN_BEARER, token))
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(statusCode)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
return resolve([err, res]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function addUser(request: any, user: string, credentials: any,
|
||||
statusCode: number = HTTP_STATUS.CREATED): Promise<any[]> {
|
||||
export function addUser(request: any, user: string, credentials: any, statusCode: number = HTTP_STATUS.CREATED): Promise<any[]> {
|
||||
// $FlowFixMe
|
||||
return new Promise((resolve) => {
|
||||
request.put(`/-/user/org.couchdb.user:${user}`)
|
||||
request
|
||||
.put(`/-/user/org.couchdb.user:${user}`)
|
||||
.send(credentials)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(statusCode)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
return resolve([err, res]);
|
||||
});
|
||||
});
|
||||
@ -133,7 +116,7 @@ export async function getNewToken(request: any, credentials: any): Promise<strin
|
||||
return new Promise(async (resolve) => {
|
||||
const [err, res] = await addUser(request, credentials.name, credentials);
|
||||
expect(err).toBeNull();
|
||||
const {token, ok} = res.body;
|
||||
const { token, ok } = res.body;
|
||||
expect(ok).toBeDefined();
|
||||
expect(token).toBeDefined();
|
||||
expect(typeof token).toBe('string');
|
||||
@ -141,28 +124,30 @@ export async function getNewToken(request: any, credentials: any): Promise<strin
|
||||
});
|
||||
}
|
||||
|
||||
export function getProfile(request: any, token: string, statusCode: number = HTTP_STATUS.OK): Promise<any[]> {
|
||||
export function getProfile(request: any, token: string, statusCode: number = HTTP_STATUS.OK): Promise<any[]> {
|
||||
// $FlowFixMe
|
||||
return new Promise((resolve) => {
|
||||
request.get(`/-/npm/v1/user`)
|
||||
request
|
||||
.get(`/-/npm/v1/user`)
|
||||
.set('authorization', buildToken(TOKEN_BEARER, token))
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(statusCode)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
return resolve([err, res]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function postProfile(request: any, body: any, token: string, statusCode: number = HTTP_STATUS.OK): Promise<any[]> {
|
||||
export function postProfile(request: any, body: any, token: string, statusCode: number = HTTP_STATUS.OK): Promise<any[]> {
|
||||
// $FlowFixMe
|
||||
return new Promise((resolve) => {
|
||||
request.post(`/-/npm/v1/user`)
|
||||
request
|
||||
.post(`/-/npm/v1/user`)
|
||||
.send(body)
|
||||
.set(HEADERS.AUTHORIZATION, `Bearer ${token}`)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(statusCode)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
return resolve([err, res]);
|
||||
});
|
||||
});
|
||||
@ -170,7 +155,7 @@ export function postProfile(request: any, body: any, token: string, statusCode:
|
||||
|
||||
export async function fetchPackageByVersionAndTag(app, encodedPkgName, pkgName, version, tag = 'latest') {
|
||||
// we retrieve the package to verify
|
||||
const [err, resp]= await getPackage(request(app), '', encodedPkgName);
|
||||
const [err, resp] = await getPackage(request(app), '', encodedPkgName);
|
||||
|
||||
expect(err).toBeNull();
|
||||
|
||||
@ -179,13 +164,13 @@ export async function fetchPackageByVersionAndTag(app, encodedPkgName, pkgName,
|
||||
}
|
||||
|
||||
export async function isExistPackage(app, packageName) {
|
||||
const [err]= await getPackage(request(app), '', encodeScopedUri(packageName), HTTP_STATUS.OK);
|
||||
const [err] = await getPackage(request(app), '', encodeScopedUri(packageName), HTTP_STATUS.OK);
|
||||
|
||||
return _.isNull(err);
|
||||
}
|
||||
|
||||
export async function verifyPackageVersionDoesExist(app, packageName, version, token?: string) {
|
||||
const [, res]= await getPackage(request(app), token as string, encodeScopedUri(packageName), HTTP_STATUS.OK);
|
||||
const [, res] = await getPackage(request(app), token as string, encodeScopedUri(packageName), HTTP_STATUS.OK);
|
||||
|
||||
const { versions } = res.body;
|
||||
const versionsKeys = Object.keys(versions);
|
||||
|
@ -2,12 +2,11 @@ import path from 'path';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import * as fsExtra from 'fs-extra';
|
||||
import {DOMAIN_SERVERS} from './constants';
|
||||
import { DOMAIN_SERVERS } from './constants';
|
||||
import VerdaccioProcess from './server_process';
|
||||
import {VerdaccioConfig} from './verdaccio-server';
|
||||
import { VerdaccioConfig } from './verdaccio-server';
|
||||
import Server from './server';
|
||||
import {IServerBridge} from './types';
|
||||
|
||||
import { IServerBridge } from './types';
|
||||
|
||||
/**
|
||||
* Fork a Verdaccio process with a custom configuration.
|
||||
@ -57,7 +56,7 @@ export function mockServer(port: number, options: MockRegistryOptions = {}) {
|
||||
// console.log("-->tempRoot", tempRoot);
|
||||
|
||||
// default locations
|
||||
const configPath = path.join(__dirname, './config/yaml', '/mock-server-test.yaml');
|
||||
const configPath = path.join(__dirname, './config/yaml', '/mock-server-test.yaml');
|
||||
const mockStorePath = path.join(__dirname, '/fixtures/mock-store');
|
||||
|
||||
// default options
|
||||
@ -67,7 +66,7 @@ export function mockServer(port: number, options: MockRegistryOptions = {}) {
|
||||
storePath: mockStorePath,
|
||||
rootFolder: tempRoot,
|
||||
silence: true,
|
||||
debug: false
|
||||
debug: false,
|
||||
};
|
||||
|
||||
// mix external options
|
||||
@ -93,6 +92,6 @@ export interface MockRegistryOptions {
|
||||
configPath?: string;
|
||||
port?: number;
|
||||
storePath?: string;
|
||||
silence?: boolean
|
||||
silence?: boolean;
|
||||
debug?: boolean;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import { IRequestPromise } from './types';
|
||||
const requestData = Symbol('smart_request_data');
|
||||
|
||||
export class PromiseAssert extends Promise<any> implements IRequestPromise {
|
||||
|
||||
public constructor(options: any) {
|
||||
super(options);
|
||||
}
|
||||
@ -14,56 +13,63 @@ export class PromiseAssert extends Promise<any> implements IRequestPromise {
|
||||
public status(expected: number) {
|
||||
const selfData = this[requestData];
|
||||
|
||||
return injectResponse(this, this.then(function(body) {
|
||||
try {
|
||||
assert.equal(selfData.response.statusCode, expected);
|
||||
} catch(err) {
|
||||
selfData.error.message = err.message;
|
||||
throw selfData.error;
|
||||
}
|
||||
return body;
|
||||
}));
|
||||
return injectResponse(
|
||||
this,
|
||||
this.then(function (body) {
|
||||
try {
|
||||
assert.equal(selfData.response.statusCode, expected);
|
||||
} catch (err) {
|
||||
selfData.error.message = err.message;
|
||||
throw selfData.error;
|
||||
}
|
||||
return body;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public body_ok(expected: any) {
|
||||
const selfData = this[requestData];
|
||||
|
||||
return injectResponse(this, this.then(function(body) {
|
||||
try {
|
||||
if (_.isRegExp(expected)) {
|
||||
assert(body.ok.match(expected), '\'' + body.ok + '\' doesn\'t match ' + expected);
|
||||
} else {
|
||||
assert.equal(body.ok, expected);
|
||||
return injectResponse(
|
||||
this,
|
||||
this.then(function (body) {
|
||||
try {
|
||||
if (_.isRegExp(expected)) {
|
||||
assert(body.ok.match(expected), "'" + body.ok + "' doesn't match " + expected);
|
||||
} else {
|
||||
assert.equal(body.ok, expected);
|
||||
}
|
||||
assert.equal(body.error, null);
|
||||
} catch (err) {
|
||||
selfData.error.message = err.message;
|
||||
throw selfData.error;
|
||||
}
|
||||
assert.equal(body.error, null);
|
||||
} catch(err) {
|
||||
selfData.error.message = err.message;
|
||||
throw selfData.error;
|
||||
}
|
||||
|
||||
return body;
|
||||
}));
|
||||
return body;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public body_error(expected: any) {
|
||||
|
||||
const selfData = this[requestData];
|
||||
|
||||
return injectResponse(this, this.then(function(body) {
|
||||
try {
|
||||
if (_.isRegExp(expected)) {
|
||||
assert(body.error.match(expected), body.error + ' doesn\'t match ' + expected);
|
||||
} else {
|
||||
assert.equal(body.error, expected);
|
||||
return injectResponse(
|
||||
this,
|
||||
this.then(function (body) {
|
||||
try {
|
||||
if (_.isRegExp(expected)) {
|
||||
assert(body.error.match(expected), body.error + " doesn't match " + expected);
|
||||
} else {
|
||||
assert.equal(body.error, expected);
|
||||
}
|
||||
assert.equal(body.ok, null);
|
||||
} catch (err) {
|
||||
selfData.error.message = err.message;
|
||||
throw selfData.error;
|
||||
}
|
||||
assert.equal(body.ok, null);
|
||||
} catch(err) {
|
||||
selfData.error.message = err.message;
|
||||
throw selfData.error;
|
||||
}
|
||||
return body;
|
||||
}));
|
||||
return body;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public request(callback: any) {
|
||||
@ -74,26 +80,26 @@ export class PromiseAssert extends Promise<any> implements IRequestPromise {
|
||||
public response(cb: any) {
|
||||
const selfData = this[requestData];
|
||||
|
||||
return injectResponse(this, this.then(function(body) {
|
||||
cb(selfData.response);
|
||||
return body;
|
||||
}));
|
||||
return injectResponse(
|
||||
this,
|
||||
this.then(function (body) {
|
||||
cb(selfData.response);
|
||||
return body;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public send(data: any) {
|
||||
this[requestData].request.end(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function injectResponse(smartObject: any, promise: Promise<any>): Promise<any> {
|
||||
|
||||
promise[requestData] = smartObject[requestData];
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
||||
function smartRequest(options: any): Promise<any> {
|
||||
const smartObject: any = {};
|
||||
|
||||
@ -101,9 +107,9 @@ function smartRequest(options: any): Promise<any> {
|
||||
smartObject[requestData].error = Error();
|
||||
Error.captureStackTrace(smartObject[requestData].error, smartRequest);
|
||||
|
||||
const promiseResult: Promise<any> = new PromiseAssert(function(resolve, reject) {
|
||||
const promiseResult: Promise<any> = new PromiseAssert(function (resolve, reject) {
|
||||
// store request reference on symbol
|
||||
smartObject[requestData].request = request(options, function(err, res, body) {
|
||||
smartObject[requestData].request = request(options, function (err, res, body) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
@ -118,4 +124,3 @@ function smartRequest(options: any): Promise<any> {
|
||||
}
|
||||
|
||||
export default smartRequest;
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
import assert from 'assert';
|
||||
import _ from 'lodash';
|
||||
import {API_MESSAGE, HEADERS, HTTP_STATUS, TOKEN_BASIC} from '@verdaccio/dev-commons';
|
||||
import {buildToken} from "@verdaccio/utils";
|
||||
import { API_MESSAGE, HEADERS, HTTP_STATUS, TOKEN_BASIC } from '@verdaccio/dev-commons';
|
||||
import { buildToken } from '@verdaccio/utils';
|
||||
import smartRequest from './request';
|
||||
import {IServerBridge} from './types';
|
||||
import { IServerBridge } from './types';
|
||||
|
||||
|
||||
import {CREDENTIALS} from "./constants"
|
||||
import { CREDENTIALS } from './constants';
|
||||
import getPackage from './fixtures/package';
|
||||
|
||||
const buildAuthHeader = (user, pass): string => {
|
||||
@ -65,7 +64,6 @@ export default class Server implements IServerBridge {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public getPackage(name: string) {
|
||||
return this.request({
|
||||
uri: `/${encodeURIComponent(name)}`,
|
||||
@ -139,7 +137,6 @@ export default class Server implements IServerBridge {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public addTag(name: string, tag: string, version: string) {
|
||||
return this.request({
|
||||
uri: `/${encodeURIComponent(name)}/${encodeURIComponent(tag)}`,
|
||||
@ -161,20 +158,20 @@ export default class Server implements IServerBridge {
|
||||
timeout: 1000,
|
||||
});
|
||||
|
||||
promise.request(function(req) {
|
||||
promise.request(function (req) {
|
||||
req.write(data);
|
||||
// it auto abort the request
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
req.req.abort();
|
||||
}, 20);
|
||||
});
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
promise
|
||||
.then(function() {
|
||||
.then(function () {
|
||||
reject(Error('no error'));
|
||||
})
|
||||
.catch(function(err) {
|
||||
.catch(function (err) {
|
||||
if (err.code === 'ECONNRESET') {
|
||||
// @ts-ignore
|
||||
resolve();
|
||||
@ -186,25 +183,25 @@ export default class Server implements IServerBridge {
|
||||
}
|
||||
|
||||
public addPackage(name: string) {
|
||||
return this.putPackage(name, getPackage(name))
|
||||
.status(HTTP_STATUS.CREATED)
|
||||
.body_ok(API_MESSAGE.PKG_CREATED);
|
||||
return this.putPackage(name, getPackage(name)).status(HTTP_STATUS.CREATED).body_ok(API_MESSAGE.PKG_CREATED);
|
||||
}
|
||||
|
||||
public whoami() {
|
||||
return this.request({
|
||||
uri: '/-/whoami'
|
||||
}).status(HTTP_STATUS.OK)
|
||||
.then(function(body) {
|
||||
uri: '/-/whoami',
|
||||
})
|
||||
.status(HTTP_STATUS.OK)
|
||||
.then(function (body) {
|
||||
return body.username;
|
||||
});
|
||||
}
|
||||
|
||||
public ping() {
|
||||
return this.request({
|
||||
uri: '/-/ping'
|
||||
}).status(HTTP_STATUS.OK)
|
||||
.then(function(body) {
|
||||
uri: '/-/ping',
|
||||
})
|
||||
.status(HTTP_STATUS.OK)
|
||||
.then(function (body) {
|
||||
return body;
|
||||
});
|
||||
}
|
||||
@ -216,6 +213,6 @@ export default class Server implements IServerBridge {
|
||||
headers: {
|
||||
[HEADERS.CONTENT_TYPE]: HEADERS.JSON,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,20 @@
|
||||
import assert from 'assert';
|
||||
import {fork} from 'child_process';
|
||||
import { fork } from 'child_process';
|
||||
|
||||
import {HTTP_STATUS} from '@verdaccio/dev-commons';
|
||||
import { HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
|
||||
import {CREDENTIALS} from './constants';
|
||||
import {IVerdaccioConfig, IServerBridge, IServerProcess} from './types';
|
||||
import { CREDENTIALS } from './constants';
|
||||
import { IVerdaccioConfig, IServerBridge, IServerProcess } from './types';
|
||||
const defaultBinPath = require.resolve('verdaccio/bin/verdaccio');
|
||||
|
||||
export default class VerdaccioProcess implements IServerProcess {
|
||||
|
||||
private bridge: IServerBridge;
|
||||
private config: IVerdaccioConfig;
|
||||
private childFork: any;
|
||||
private isDebug: boolean;
|
||||
private silence: boolean;
|
||||
|
||||
public constructor(config: IVerdaccioConfig,
|
||||
bridge: IServerBridge,
|
||||
silence = true,
|
||||
isDebug = false) {
|
||||
public constructor(config: IVerdaccioConfig, bridge: IServerBridge, silence = true, isDebug = false) {
|
||||
this.config = config;
|
||||
this.bridge = bridge;
|
||||
this.silence = silence;
|
||||
@ -34,7 +30,7 @@ export default class VerdaccioProcess implements IServerProcess {
|
||||
|
||||
private _start(verdaccioPath: string, resolve: Function, reject: Function) {
|
||||
let childOptions = {
|
||||
silent: this.silence
|
||||
silent: this.silence,
|
||||
};
|
||||
|
||||
if (this.isDebug) {
|
||||
@ -44,39 +40,42 @@ export default class VerdaccioProcess implements IServerProcess {
|
||||
childOptions = Object.assign({}, childOptions, {
|
||||
execArgv: [`--inspect=${debugPort}`],
|
||||
env: {
|
||||
"NODE_DEBUG": 'request'
|
||||
}
|
||||
NODE_DEBUG: 'request',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const {configPath, port} = this.config;
|
||||
const { configPath, port } = this.config;
|
||||
this.childFork = fork(verdaccioPath, ['-c', configPath, '-l', port as string], childOptions);
|
||||
|
||||
this.childFork.on('message', (msg) => {
|
||||
// verdaccio_started is a message that comes from verdaccio in debug mode that notify has been started
|
||||
if ('verdaccio_started' in msg) {
|
||||
this.bridge.debug().status(HTTP_STATUS.OK).then((body) => {
|
||||
this.bridge.auth(CREDENTIALS.user, CREDENTIALS.password)
|
||||
.status(HTTP_STATUS.CREATED)
|
||||
.body_ok(new RegExp(CREDENTIALS.user))
|
||||
.then(() => resolve([this, body.pid]), reject)
|
||||
}, reject);
|
||||
this.bridge
|
||||
.debug()
|
||||
.status(HTTP_STATUS.OK)
|
||||
.then((body) => {
|
||||
this.bridge
|
||||
.auth(CREDENTIALS.user, CREDENTIALS.password)
|
||||
.status(HTTP_STATUS.CREATED)
|
||||
.body_ok(new RegExp(CREDENTIALS.user))
|
||||
.then(() => resolve([this, body.pid]), reject);
|
||||
}, reject);
|
||||
}
|
||||
});
|
||||
|
||||
this.childFork.on('error', (err) => {
|
||||
reject([err, this])
|
||||
reject([err, this]);
|
||||
});
|
||||
this.childFork.on('disconnect', (err) => {
|
||||
reject([err, this])
|
||||
reject([err, this]);
|
||||
});
|
||||
this.childFork.on('exit', (err) => {
|
||||
reject([err, this])
|
||||
reject([err, this]);
|
||||
});
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
return this.childFork.kill('SIGINT');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { pseudoRandomBytes } from 'crypto';
|
||||
import { Version } from "@verdaccio/types";
|
||||
import { Version } from '@verdaccio/types';
|
||||
|
||||
export function generateRamdonStorage() {
|
||||
const tempStorage = pseudoRandomBytes(5).toString('hex');
|
||||
@ -11,31 +11,28 @@ export function generateRamdonStorage() {
|
||||
return path.join(tempRoot, tempStorage);
|
||||
}
|
||||
|
||||
export function generateNewVersion(
|
||||
pkgName: string,
|
||||
version: string,
|
||||
shashum = '238e7641e59508dc9c20eb4ad37a8aa57ab777b4'): Version {
|
||||
export function generateNewVersion(pkgName: string, version: string, shashum = '238e7641e59508dc9c20eb4ad37a8aa57ab777b4'): Version {
|
||||
// $FlowFixMe
|
||||
return {
|
||||
"name": pkgName,
|
||||
"version": version,
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"test": "^1.4.1"
|
||||
name: pkgName,
|
||||
version: version,
|
||||
description: '',
|
||||
main: 'index.js',
|
||||
dependencies: {
|
||||
test: '^1.4.1',
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"readme": "ERROR: No README data found!",
|
||||
"_id": `${pkgName}@${version}`,
|
||||
"_npmVersion": "5.5.1",
|
||||
"_npmUser": {
|
||||
"name": "Foo"
|
||||
author: '',
|
||||
license: 'ISC',
|
||||
readme: 'ERROR: No README data found!',
|
||||
_id: `${pkgName}@${version}`,
|
||||
_npmVersion: '5.5.1',
|
||||
_npmUser: {
|
||||
name: 'Foo',
|
||||
},
|
||||
"dist": {
|
||||
"integrity": "sha512-zVEqt1JUCOPsash9q4wMkJEDPD+QCx95TRhQII+JnoS31uBUKoZxhzvvUJCcLVy2CQG4QdwXARU7dYWPnrwhGg==",
|
||||
"shasum": shashum,
|
||||
"tarball": `http:\/\/localhost:4873\/${pkgName}\/-\/${pkgName}-${version}.tgz`
|
||||
}
|
||||
}
|
||||
dist: {
|
||||
integrity: 'sha512-zVEqt1JUCOPsash9q4wMkJEDPD+QCx95TRhQII+JnoS31uBUKoZxhzvvUJCcLVy2CQG4QdwXARU7dYWPnrwhGg==',
|
||||
shasum: shashum,
|
||||
tarball: `http:\/\/localhost:4873\/${pkgName}\/-\/${pkgName}-${version}.tgz`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {IVerdaccioConfig} from './types';
|
||||
import { IVerdaccioConfig } from './types';
|
||||
|
||||
export class VerdaccioConfig implements IVerdaccioConfig {
|
||||
|
||||
public storagePath: string;
|
||||
public configPath: string;
|
||||
public domainPath: string;
|
||||
|
@ -1,9 +1,9 @@
|
||||
import _ from 'lodash';
|
||||
import {HTTP_STATUS} from '@verdaccio/dev-commons';
|
||||
import { HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
|
||||
import smartRequest, {PromiseAssert} from '../src/request';
|
||||
import {mockServer} from '../src/mock';
|
||||
import smartRequest, { PromiseAssert } from '../src/request';
|
||||
import { mockServer } from '../src/mock';
|
||||
import { IRequestPromise } from '../src/types';
|
||||
|
||||
describe('Request Functional', () => {
|
||||
@ -19,7 +19,7 @@ describe('Request Functional', () => {
|
||||
});
|
||||
|
||||
test('basic resolve', (done) => {
|
||||
const requestPromise: IRequestPromise = new PromiseAssert(resolve => {
|
||||
const requestPromise: IRequestPromise = new PromiseAssert((resolve) => {
|
||||
resolve(1);
|
||||
});
|
||||
// @ts-ignore
|
||||
@ -36,7 +36,7 @@ describe('Request Functional', () => {
|
||||
mockRegistry = await mockServer(mockServerPort).init(binPath);
|
||||
});
|
||||
|
||||
afterAll(function(done) {
|
||||
afterAll(function (done) {
|
||||
const [registry, pid] = mockRegistry;
|
||||
registry.stop();
|
||||
console.log(`registry ${pid} has been stopped`);
|
||||
@ -47,53 +47,61 @@ describe('Request Functional', () => {
|
||||
test('basic rest', (done) => {
|
||||
const options: any = {
|
||||
url: restTest,
|
||||
method: 'GET'
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
smartRequest(options).then((result)=> {
|
||||
smartRequest(options).then((result) => {
|
||||
expect(_.isString(result)).toBeTruthy();
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('smartRequest Status', () => {
|
||||
|
||||
test('basic check status 200', (done) => {
|
||||
const options: any = {
|
||||
url: restTest,
|
||||
method: 'GET'
|
||||
method: 'GET',
|
||||
};
|
||||
// @ts-ignore
|
||||
smartRequest(options).status(HTTP_STATUS.OK).then((result)=> {
|
||||
expect(JSON.parse(result).name).toBe('jquery');
|
||||
done();
|
||||
})
|
||||
smartRequest(options)
|
||||
.status(HTTP_STATUS.OK)
|
||||
.then((result) => {
|
||||
expect(JSON.parse(result).name).toBe('jquery');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('basic ping status and empty response', (done) => {
|
||||
const options: any = {
|
||||
url: `${domainTest}/-/ping`,
|
||||
method: 'GET'
|
||||
method: 'GET',
|
||||
};
|
||||
// @ts-ignore
|
||||
smartRequest(options).status(HTTP_STATUS.OK).then((result)=> {
|
||||
expect(JSON.parse((result))).toEqual({});
|
||||
done();
|
||||
})
|
||||
smartRequest(options)
|
||||
.status(HTTP_STATUS.OK)
|
||||
.then((result) => {
|
||||
expect(JSON.parse(result)).toEqual({});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('basic check status 404', (done) => {
|
||||
const options: any = {
|
||||
url: 'http://www.google.fake',
|
||||
method: 'GET'
|
||||
method: 'GET',
|
||||
};
|
||||
// @ts-ignore
|
||||
smartRequest(options).status(HTTP_STATUS.NOT_FOUND).then(() => {
|
||||
// we do not intent to resolve this
|
||||
}, (error: VerdaccioError) => {
|
||||
expect(error.code).toBe('ENOTFOUND');
|
||||
done();
|
||||
})
|
||||
smartRequest(options)
|
||||
.status(HTTP_STATUS.NOT_FOUND)
|
||||
.then(
|
||||
() => {
|
||||
// we do not intent to resolve this
|
||||
},
|
||||
(error: VerdaccioError) => {
|
||||
expect(error.code).toBe('ENOTFOUND');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -6,13 +6,13 @@ import constants from 'constants';
|
||||
import { Application } from 'express';
|
||||
import { assign, isObject, isFunction } from 'lodash';
|
||||
|
||||
import { Callback, ConfigWithHttps, HttpsConfKeyCert, HttpsConfPfx } from '@verdaccio/types';
|
||||
import { Callback, ConfigWithHttps, HttpsConfKeyCert, HttpsConfPfx } from '@verdaccio/types';
|
||||
import { API_ERROR, certPem, csrPem, keyPem } from '@verdaccio/dev-commons';
|
||||
import server from '@verdaccio/server';
|
||||
import { logger} from '@verdaccio/logger';
|
||||
import { logger } from '@verdaccio/logger';
|
||||
|
||||
import { getListListenAddresses, resolveConfigPath } from './cli-utils';
|
||||
import {displayExperimentsInfoBox} from "./experiments";
|
||||
import { displayExperimentsInfoBox } from './experiments';
|
||||
|
||||
function launchServer(app, addr, config, configPath: string, pkgVersion: string, pkgName: string, callback: Callback): void {
|
||||
let webServer;
|
||||
@ -45,14 +45,13 @@ function startVerdaccio(config: any, cliListen: string, configPath: string, pkgV
|
||||
}
|
||||
|
||||
server(config).then((app): void => {
|
||||
const addresses = getListListenAddresses(cliListen, config.listen);
|
||||
if ('experiments' in config) {
|
||||
displayExperimentsInfoBox(config.experiments);
|
||||
}
|
||||
|
||||
addresses.forEach(addr =>launchServer(app, addr, config, configPath, pkgVersion, pkgName, callback));
|
||||
const addresses = getListListenAddresses(cliListen, config.listen);
|
||||
if ('experiments' in config) {
|
||||
displayExperimentsInfoBox(config.experiments);
|
||||
}
|
||||
);
|
||||
|
||||
addresses.forEach((addr) => launchServer(app, addr, config, configPath, pkgVersion, pkgName, callback));
|
||||
});
|
||||
}
|
||||
|
||||
function unlinkAddressPath(addr) {
|
||||
@ -129,19 +128,15 @@ function handleHTTPS(app: Application, configPath: string, config: ConfigWithHtt
|
||||
|
||||
function listenDefaultCallback(webServer: Application, addr: any, pkgName: string, pkgVersion: string): void {
|
||||
webServer
|
||||
.listen(
|
||||
addr.port || addr.path,
|
||||
addr.host,
|
||||
(): void => {
|
||||
// send a message for tests
|
||||
if (isFunction(process.send)) {
|
||||
process.send({
|
||||
verdaccio_started: true,
|
||||
});
|
||||
}
|
||||
.listen(addr.port || addr.path, addr.host, (): void => {
|
||||
// send a message for tests
|
||||
if (isFunction(process.send)) {
|
||||
process.send({
|
||||
verdaccio_started: true,
|
||||
});
|
||||
}
|
||||
)
|
||||
.on('error', function(err): void {
|
||||
})
|
||||
.on('error', function (err): void {
|
||||
logger.fatal({ err: err }, 'cannot create server: @{err.message}');
|
||||
process.exit(2);
|
||||
});
|
||||
|
@ -48,9 +48,7 @@ export function getListListenAddresses(argListen: string, configListen: any): an
|
||||
if (!parsedAddr) {
|
||||
logger.logger.warn(
|
||||
{ addr: addr },
|
||||
'invalid address - @{addr}, we expect a port (e.g. "4873"),' +
|
||||
' host:port (e.g. "localhost:4873") or full url' +
|
||||
' (e.g. "http://localhost:4873/")'
|
||||
'invalid address - @{addr}, we expect a port (e.g. "4873"),' + ' host:port (e.g. "localhost:4873") or full url' + ' (e.g. "http://localhost:4873/")'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
const logger = require('@verdaccio/logger');
|
||||
|
||||
export function displayExperimentsInfoBox(experiments) {
|
||||
const experimentList = Object.keys(experiments);
|
||||
if (experimentList.length >= 1) {
|
||||
logger.logger.warn('⚠️ experiments are enabled, we recommend do not use experiments in production, comment out this section to disable it');
|
||||
experimentList.forEach(experiment => {
|
||||
logger.logger.warn(` - support for ${experiment} ${experiments[experiment] ? 'is enabled' : ' is disabled'}`);
|
||||
});
|
||||
}
|
||||
const experimentList = Object.keys(experiments);
|
||||
if (experimentList.length >= 1) {
|
||||
logger.logger.warn('⚠️ experiments are enabled, we recommend do not use experiments in production, comment out this section to disable it');
|
||||
experimentList.forEach((experiment) => {
|
||||
logger.logger.warn(` - support for ${experiment} ${experiments[experiment] ? 'is enabled' : ' is disabled'}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,34 @@
|
||||
import { API_ERROR, certPem, csrPem, keyPem } from '@verdaccio/dev-commons';
|
||||
|
||||
import {resolveConfigPath} from "./cli-utils";
|
||||
import { resolveConfigPath } from './cli-utils';
|
||||
|
||||
const logger = require('@verdaccio/logger');
|
||||
|
||||
export function logHTTPSWarning(storageLocation) {
|
||||
logger.logger.fatal(
|
||||
[
|
||||
'You have enabled HTTPS and need to specify either ',
|
||||
' "https.key", "https.cert" and "https.ca" or ',
|
||||
' "https.pfx" and optionally "https.passphrase" ',
|
||||
'to run https server',
|
||||
'',
|
||||
// commands are borrowed from node.js docs
|
||||
'To quickly create self-signed certificate, use:',
|
||||
' $ openssl genrsa -out ' + resolveConfigPath(storageLocation, keyPem) + ' 2048',
|
||||
' $ openssl req -new -sha256 -key ' + resolveConfigPath(storageLocation, keyPem) + ' -out ' + resolveConfigPath(storageLocation, csrPem),
|
||||
' $ openssl x509 -req -in ' +
|
||||
resolveConfigPath(storageLocation, csrPem) +
|
||||
' -signkey ' +
|
||||
resolveConfigPath(storageLocation, keyPem) +
|
||||
' -out ' +
|
||||
resolveConfigPath(storageLocation, certPem),
|
||||
'',
|
||||
'And then add to config file (' + storageLocation + '):',
|
||||
' https:',
|
||||
` key: ${resolveConfigPath(storageLocation, keyPem)}`,
|
||||
` cert: ${resolveConfigPath(storageLocation, certPem)}`,
|
||||
` ca: ${resolveConfigPath(storageLocation, csrPem)}`,
|
||||
].join('\n')
|
||||
);
|
||||
process.exit(2);
|
||||
logger.logger.fatal(
|
||||
[
|
||||
'You have enabled HTTPS and need to specify either ',
|
||||
' "https.key", "https.cert" and "https.ca" or ',
|
||||
' "https.pfx" and optionally "https.passphrase" ',
|
||||
'to run https server',
|
||||
'',
|
||||
// commands are borrowed from node.js docs
|
||||
'To quickly create self-signed certificate, use:',
|
||||
' $ openssl genrsa -out ' + resolveConfigPath(storageLocation, keyPem) + ' 2048',
|
||||
' $ openssl req -new -sha256 -key ' + resolveConfigPath(storageLocation, keyPem) + ' -out ' + resolveConfigPath(storageLocation, csrPem),
|
||||
' $ openssl x509 -req -in ' +
|
||||
resolveConfigPath(storageLocation, csrPem) +
|
||||
' -signkey ' +
|
||||
resolveConfigPath(storageLocation, keyPem) +
|
||||
' -out ' +
|
||||
resolveConfigPath(storageLocation, certPem),
|
||||
'',
|
||||
'And then add to config file (' + storageLocation + '):',
|
||||
' https:',
|
||||
` key: ${resolveConfigPath(storageLocation, keyPem)}`,
|
||||
` cert: ${resolveConfigPath(storageLocation, certPem)}`,
|
||||
` ca: ${resolveConfigPath(storageLocation, csrPem)}`,
|
||||
].join('\n')
|
||||
);
|
||||
process.exit(2);
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ import fs from 'fs';
|
||||
import selfsigned from 'selfsigned';
|
||||
|
||||
import { configExample } from '@verdaccio/mock';
|
||||
import {DEFAULT_DOMAIN, DEFAULT_PROTOCOL} from '@verdaccio/dev-commons';
|
||||
import {parseConfigFile} from '@verdaccio/utils';
|
||||
import { DEFAULT_DOMAIN, DEFAULT_PROTOCOL } from '@verdaccio/dev-commons';
|
||||
import { parseConfigFile } from '@verdaccio/utils';
|
||||
|
||||
import { logger } from '@verdaccio/logger';
|
||||
|
||||
@ -19,12 +19,11 @@ jest.mock('@verdaccio/logger', () => ({
|
||||
trace: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
fatal: jest.fn()
|
||||
}
|
||||
fatal: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('startServer via API', () => {
|
||||
|
||||
const parseConfigurationFile = (name) => {
|
||||
return parseConfigFile(path.join(__dirname, `./partials/config/yaml/${name}.yaml`));
|
||||
};
|
||||
@ -36,19 +35,18 @@ describe('startServer via API', () => {
|
||||
const version = '1.0.0';
|
||||
const port = '6000';
|
||||
|
||||
await startVerdaccio(configExample(), port, store, version, serverName,
|
||||
(webServer, addrs, pkgName, pkgVersion) => {
|
||||
expect(webServer).toBeDefined();
|
||||
expect(addrs).toBeDefined();
|
||||
expect(addrs.proto).toBe(DEFAULT_PROTOCOL);
|
||||
expect(addrs.host).toBe(DEFAULT_DOMAIN);
|
||||
expect(addrs.port).toBe(port);
|
||||
expect(pkgName).toBeDefined();
|
||||
expect(pkgVersion).toBeDefined();
|
||||
expect(pkgVersion).toBe(version);
|
||||
expect(pkgName).toBe(serverName);
|
||||
done();
|
||||
});
|
||||
await startVerdaccio(configExample(), port, store, version, serverName, (webServer, addrs, pkgName, pkgVersion) => {
|
||||
expect(webServer).toBeDefined();
|
||||
expect(addrs).toBeDefined();
|
||||
expect(addrs.proto).toBe(DEFAULT_PROTOCOL);
|
||||
expect(addrs.host).toBe(DEFAULT_DOMAIN);
|
||||
expect(addrs.port).toBe(port);
|
||||
expect(pkgName).toBeDefined();
|
||||
expect(pkgVersion).toBeDefined();
|
||||
expect(pkgVersion).toBe(version);
|
||||
expect(pkgName).toBe(serverName);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should set keepAliveTimeout to 0 seconds', async (done) => {
|
||||
@ -57,7 +55,12 @@ describe('startServer via API', () => {
|
||||
const version = '1.0.0';
|
||||
const port = '6100';
|
||||
|
||||
await startVerdaccio(configExample(parseConfigurationFile('server/keepalivetimeout-0')), port, store, version, serverName,
|
||||
await startVerdaccio(
|
||||
configExample(parseConfigurationFile('server/keepalivetimeout-0')),
|
||||
port,
|
||||
store,
|
||||
version,
|
||||
serverName,
|
||||
(webServer, addrs, pkgName, pkgVersion) => {
|
||||
expect(webServer).toBeDefined();
|
||||
expect(webServer.keepAliveTimeout).toBeDefined();
|
||||
@ -71,7 +74,8 @@ describe('startServer via API', () => {
|
||||
expect(pkgVersion).toBe(version);
|
||||
expect(pkgName).toBe(serverName);
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('should set keepAliveTimeout to 60 seconds', async (done) => {
|
||||
@ -80,7 +84,12 @@ describe('startServer via API', () => {
|
||||
const version = '1.0.0';
|
||||
const port = '6200';
|
||||
|
||||
await startVerdaccio(configExample(parseConfigurationFile('server/keepalivetimeout-60')), port, store, version, serverName,
|
||||
await startVerdaccio(
|
||||
configExample(parseConfigurationFile('server/keepalivetimeout-60')),
|
||||
port,
|
||||
store,
|
||||
version,
|
||||
serverName,
|
||||
(webServer, addrs, pkgName, pkgVersion) => {
|
||||
expect(webServer).toBeDefined();
|
||||
expect(webServer.keepAliveTimeout).toBeDefined();
|
||||
@ -94,7 +103,8 @@ describe('startServer via API', () => {
|
||||
expect(pkgVersion).toBe(version);
|
||||
expect(pkgName).toBe(serverName);
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('should set keepAliveTimeout to 5 seconds per default', async (done) => {
|
||||
@ -103,7 +113,12 @@ describe('startServer via API', () => {
|
||||
const version = '1.0.0';
|
||||
const port = '6300';
|
||||
|
||||
await startVerdaccio(configExample(parseConfigurationFile('server/keepalivetimeout-undefined')), port, store, version, serverName,
|
||||
await startVerdaccio(
|
||||
configExample(parseConfigurationFile('server/keepalivetimeout-undefined')),
|
||||
port,
|
||||
store,
|
||||
version,
|
||||
serverName,
|
||||
(webServer, addrs, pkgName, pkgVersion) => {
|
||||
expect(webServer).toBeDefined();
|
||||
expect(webServer.keepAliveTimeout).toBeDefined();
|
||||
@ -117,7 +132,8 @@ describe('startServer via API', () => {
|
||||
expect(pkgVersion).toBe(version);
|
||||
expect(pkgName).toBe(serverName);
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('should provide all HTTPS server fails', async (done) => {
|
||||
@ -160,14 +176,13 @@ describe('startServer via API', () => {
|
||||
cert: certPath,
|
||||
};
|
||||
|
||||
await startVerdaccio(conf, address, store, version, serverName,
|
||||
(webServer, addrs) => {
|
||||
expect(webServer).toBeDefined();
|
||||
expect(addrs).toBeDefined();
|
||||
expect(addrs.proto).toBe('https');
|
||||
done();
|
||||
await startVerdaccio(conf, address, store, version, serverName, (webServer, addrs) => {
|
||||
expect(webServer).toBeDefined();
|
||||
expect(addrs).toBeDefined();
|
||||
expect(addrs.proto).toBe('https');
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
test('should fails if config is missing', async () => {
|
||||
try {
|
||||
@ -177,6 +192,5 @@ describe('startServer via API', () => {
|
||||
expect(e.message).toEqual('config file must be an object');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -1,76 +1,74 @@
|
||||
import _ from "lodash";
|
||||
import {DEFAULT_DOMAIN, DEFAULT_PORT, DEFAULT_PROTOCOL} from "@verdaccio/dev-commons";
|
||||
import {getListListenAddresses} from "../src/cli-utils";
|
||||
import _ from 'lodash';
|
||||
import { DEFAULT_DOMAIN, DEFAULT_PORT, DEFAULT_PROTOCOL } from '@verdaccio/dev-commons';
|
||||
import { getListListenAddresses } from '../src/cli-utils';
|
||||
|
||||
jest.mock('@verdaccio/logger', () => ({
|
||||
setup: jest.fn(),
|
||||
logger: {
|
||||
child: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
trace: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
fatal: jest.fn()
|
||||
}
|
||||
setup: jest.fn(),
|
||||
logger: {
|
||||
child: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
trace: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
fatal: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('getListListenAddresses test', () => {
|
||||
test('should return no address if a single address is wrong', () => {
|
||||
// @ts-ignore
|
||||
const addrs = getListListenAddresses('wrong');
|
||||
|
||||
test('should return no address if a single address is wrong', () => {
|
||||
// @ts-ignore
|
||||
const addrs = getListListenAddresses("wrong");
|
||||
expect(_.isArray(addrs)).toBeTruthy();
|
||||
expect(addrs).toHaveLength(0);
|
||||
});
|
||||
|
||||
expect(_.isArray(addrs)).toBeTruthy();
|
||||
expect(addrs).toHaveLength(0);
|
||||
});
|
||||
test('should return no address if a two address are wrong', () => {
|
||||
// @ts-ignore
|
||||
const addrs = getListListenAddresses(['wrong', 'same-wrong']);
|
||||
|
||||
test('should return no address if a two address are wrong', () => {
|
||||
// @ts-ignore
|
||||
const addrs = getListListenAddresses(["wrong", "same-wrong"]);
|
||||
expect(_.isArray(addrs)).toBeTruthy();
|
||||
expect(addrs).toHaveLength(0);
|
||||
});
|
||||
|
||||
expect(_.isArray(addrs)).toBeTruthy();
|
||||
expect(addrs).toHaveLength(0);
|
||||
});
|
||||
test('should return a list of 1 address provided', () => {
|
||||
// @ts-ignore
|
||||
const addrs = getListListenAddresses(null, '1000');
|
||||
|
||||
test('should return a list of 1 address provided', () => {
|
||||
// @ts-ignore
|
||||
const addrs = getListListenAddresses(null, '1000');
|
||||
expect(_.isArray(addrs)).toBeTruthy();
|
||||
expect(addrs).toHaveLength(1);
|
||||
});
|
||||
|
||||
expect(_.isArray(addrs)).toBeTruthy();
|
||||
expect(addrs).toHaveLength(1);
|
||||
});
|
||||
test('should return a list of 2 address provided', () => {
|
||||
// @ts-ignore
|
||||
const addrs = getListListenAddresses(null, ['1000', '2000']);
|
||||
|
||||
test('should return a list of 2 address provided', () => {
|
||||
// @ts-ignore
|
||||
const addrs = getListListenAddresses(null, ['1000', '2000']);
|
||||
expect(_.isArray(addrs)).toBeTruthy();
|
||||
expect(addrs).toHaveLength(2);
|
||||
});
|
||||
|
||||
expect(_.isArray(addrs)).toBeTruthy();
|
||||
expect(addrs).toHaveLength(2);
|
||||
});
|
||||
test(`should return by default ${DEFAULT_PORT}`, () => {
|
||||
// @ts-ignore
|
||||
const [addrs] = getListListenAddresses();
|
||||
|
||||
test(`should return by default ${DEFAULT_PORT}`, () => {
|
||||
// @ts-ignore
|
||||
const [addrs] = getListListenAddresses();
|
||||
// @ts-ignore
|
||||
expect(addrs.proto).toBe(DEFAULT_PROTOCOL);
|
||||
// @ts-ignore
|
||||
expect(addrs.host).toBe(DEFAULT_DOMAIN);
|
||||
// @ts-ignore
|
||||
expect(addrs.port).toBe(DEFAULT_PORT);
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
expect(addrs.proto).toBe(DEFAULT_PROTOCOL);
|
||||
// @ts-ignore
|
||||
expect(addrs.host).toBe(DEFAULT_DOMAIN);
|
||||
// @ts-ignore
|
||||
expect(addrs.port).toBe(DEFAULT_PORT);
|
||||
});
|
||||
|
||||
test('should return default proto, host and custom port', () => {
|
||||
const initPort = '1000';
|
||||
// @ts-ignore
|
||||
const [addrs] = getListListenAddresses(null, initPort);
|
||||
|
||||
// @ts-ignore
|
||||
expect(addrs.proto).toEqual(DEFAULT_PROTOCOL);
|
||||
// @ts-ignore
|
||||
expect(addrs.host).toEqual(DEFAULT_DOMAIN);
|
||||
// @ts-ignore
|
||||
expect(addrs.port).toEqual(initPort);
|
||||
});
|
||||
test('should return default proto, host and custom port', () => {
|
||||
const initPort = '1000';
|
||||
// @ts-ignore
|
||||
const [addrs] = getListListenAddresses(null, initPort);
|
||||
|
||||
// @ts-ignore
|
||||
expect(addrs.proto).toEqual(DEFAULT_PROTOCOL);
|
||||
// @ts-ignore
|
||||
expect(addrs.host).toEqual(DEFAULT_DOMAIN);
|
||||
// @ts-ignore
|
||||
expect(addrs.port).toEqual(initPort);
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import zlib from 'zlib';
|
||||
import Stream from 'stream';
|
||||
import URL, {UrlWithStringQuery} from 'url';
|
||||
import URL, { UrlWithStringQuery } from 'url';
|
||||
import JSONStream from 'JSONStream';
|
||||
import _ from 'lodash';
|
||||
import request from 'request';
|
||||
@ -11,7 +11,7 @@ import { Config, Callback, Headers, Logger, Package } from '@verdaccio/types';
|
||||
import { IProxy, UpLinkConfLocal } from '@verdaccio/dev-types';
|
||||
const LoggerApi = require('@verdaccio/logger');
|
||||
|
||||
const encode = function(thing): string {
|
||||
const encode = function (thing): string {
|
||||
return encodeURIComponent(thing).replace(/^%40/, '@');
|
||||
};
|
||||
|
||||
@ -107,16 +107,16 @@ class ProxyStorage implements IProxy {
|
||||
if (this._statusCheck() === false) {
|
||||
const streamRead = new Stream.Readable();
|
||||
|
||||
process.nextTick(function(): void {
|
||||
process.nextTick(function (): void {
|
||||
if (cb) {
|
||||
cb(ErrorCode.getInternalError(API_ERROR.UPLINK_OFFLINE));
|
||||
}
|
||||
streamRead.emit('error', ErrorCode.getInternalError(API_ERROR.UPLINK_OFFLINE));
|
||||
});
|
||||
// $FlowFixMe
|
||||
streamRead._read = function(): void {};
|
||||
streamRead._read = function (): void {};
|
||||
// preventing 'Uncaught, unspecified "error" event'
|
||||
streamRead.on('error', function(): void {});
|
||||
streamRead.on('error', function (): void {});
|
||||
return streamRead;
|
||||
}
|
||||
|
||||
@ -143,64 +143,66 @@ class ProxyStorage implements IProxy {
|
||||
headers['Content-Type'] = headers['Content-Type'] || HEADERS.JSON;
|
||||
}
|
||||
|
||||
const requestCallback = cb ? function(err, res, body): void {
|
||||
let error;
|
||||
const responseLength = err ? 0 : body.length;
|
||||
// $FlowFixMe
|
||||
processBody();
|
||||
logActivity();
|
||||
// $FlowFixMe
|
||||
cb(err, res, body);
|
||||
const requestCallback = cb
|
||||
? function (err, res, body): void {
|
||||
let error;
|
||||
const responseLength = err ? 0 : body.length;
|
||||
// $FlowFixMe
|
||||
processBody();
|
||||
logActivity();
|
||||
// $FlowFixMe
|
||||
cb(err, res, body);
|
||||
|
||||
/**
|
||||
* Perform a decode.
|
||||
*/
|
||||
function processBody(): void {
|
||||
if (err) {
|
||||
error = err.message;
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Perform a decode.
|
||||
*/
|
||||
function processBody(): void {
|
||||
if (err) {
|
||||
error = err.message;
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.json && res.statusCode < 300) {
|
||||
try {
|
||||
// $FlowFixMe
|
||||
body = JSON.parse(body.toString(CHARACTER_ENCODING.UTF8));
|
||||
} catch (_err) {
|
||||
body = {};
|
||||
err = _err;
|
||||
error = err.message;
|
||||
if (options.json && res.statusCode < 300) {
|
||||
try {
|
||||
// $FlowFixMe
|
||||
body = JSON.parse(body.toString(CHARACTER_ENCODING.UTF8));
|
||||
} catch (_err) {
|
||||
body = {};
|
||||
err = _err;
|
||||
error = err.message;
|
||||
}
|
||||
}
|
||||
|
||||
if (!err && isObject(body)) {
|
||||
if (_.isString(body.error)) {
|
||||
error = body.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Perform a log.
|
||||
*/
|
||||
function logActivity(): void {
|
||||
let message = "@{!status}, req: '@{request.method} @{request.url}'";
|
||||
// FIXME: use LOG_VERDACCIO_BYTES
|
||||
message += error ? ', error: @{!error}' : ', bytes: @{bytes.in}/@{bytes.out}';
|
||||
self.logger.warn(
|
||||
{
|
||||
err: err || undefined, // if error is null/false change this to undefined so it wont log
|
||||
request: { method: method, url: uri },
|
||||
level: 35, // http
|
||||
status: res != null ? res.statusCode : 'ERR',
|
||||
error: error,
|
||||
bytes: {
|
||||
in: json ? json.length : 0,
|
||||
out: responseLength || 0,
|
||||
},
|
||||
},
|
||||
message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!err && isObject(body)) {
|
||||
if (_.isString(body.error)) {
|
||||
error = body.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Perform a log.
|
||||
*/
|
||||
function logActivity(): void {
|
||||
let message = "@{!status}, req: '@{request.method} @{request.url}'";
|
||||
// FIXME: use LOG_VERDACCIO_BYTES
|
||||
message += error ? ', error: @{!error}' : ', bytes: @{bytes.in}/@{bytes.out}';
|
||||
self.logger.warn(
|
||||
{
|
||||
err: err || undefined, // if error is null/false change this to undefined so it wont log
|
||||
request: { method: method, url: uri },
|
||||
level: 35, // http
|
||||
status: res != null ? res.statusCode : 'ERR',
|
||||
error: error,
|
||||
bytes: {
|
||||
in: json ? json.length : 0,
|
||||
out: responseLength || 0,
|
||||
},
|
||||
},
|
||||
message
|
||||
);
|
||||
}
|
||||
} : undefined;
|
||||
: undefined;
|
||||
|
||||
let requestOptions = {
|
||||
url: uri,
|
||||
@ -217,14 +219,14 @@ class ProxyStorage implements IProxy {
|
||||
|
||||
if (this.ca) {
|
||||
requestOptions = Object.assign({}, requestOptions, {
|
||||
ca: this.ca
|
||||
ca: this.ca,
|
||||
});
|
||||
}
|
||||
|
||||
const req = request(requestOptions, requestCallback);
|
||||
|
||||
let statusCalled = false;
|
||||
req.on('response', function(res): void {
|
||||
req.on('response', function (res): void {
|
||||
// FIXME: _verdaccio_aborted seems not used
|
||||
// @ts-ignore
|
||||
if (!req._verdaccio_aborted && !statusCalled) {
|
||||
@ -249,7 +251,7 @@ class ProxyStorage implements IProxy {
|
||||
})();
|
||||
}
|
||||
});
|
||||
req.on('error', function(_err): void {
|
||||
req.on('error', function (_err): void {
|
||||
// FIXME: _verdaccio_aborted seems not used
|
||||
// @ts-ignore
|
||||
if (!req._verdaccio_aborted && !statusCalled) {
|
||||
@ -465,7 +467,7 @@ class ProxyStorage implements IProxy {
|
||||
},
|
||||
});
|
||||
|
||||
readStream.on('response', function(res: any) {
|
||||
readStream.on('response', function (res: any) {
|
||||
if (res.statusCode === HTTP_STATUS.NOT_FOUND) {
|
||||
return stream.emit('error', ErrorCode.getNotFound(API_ERROR.NOT_FILE_UPLINK));
|
||||
}
|
||||
@ -480,13 +482,13 @@ class ProxyStorage implements IProxy {
|
||||
readStream.pipe(stream);
|
||||
});
|
||||
|
||||
readStream.on('error', function(err) {
|
||||
readStream.on('error', function (err) {
|
||||
stream.emit('error', err);
|
||||
});
|
||||
readStream.on('data', function(data) {
|
||||
readStream.on('data', function (data) {
|
||||
current_length += data.length;
|
||||
});
|
||||
readStream.on('end', function(data) {
|
||||
readStream.on('end', function (data) {
|
||||
if (data) {
|
||||
current_length += data.length;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {buildToken} from "@verdaccio/utils";
|
||||
import { buildToken } from '@verdaccio/utils';
|
||||
|
||||
import {ERROR_CODE, TOKEN_BASIC, TOKEN_BEARER, DEFAULT_REGISTRY, HEADERS} from "@verdaccio/dev-commons";
|
||||
import {setup} from '@verdaccio/logger';
|
||||
import { ERROR_CODE, TOKEN_BASIC, TOKEN_BEARER, DEFAULT_REGISTRY, HEADERS } from '@verdaccio/dev-commons';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
|
||||
import { ProxyStorage } from '../src/up-storage';
|
||||
|
||||
@ -9,7 +9,7 @@ setup([]);
|
||||
|
||||
function createUplink(config) {
|
||||
const defaultConfig = {
|
||||
url: DEFAULT_REGISTRY
|
||||
url: DEFAULT_REGISTRY,
|
||||
};
|
||||
const mergeConfig = Object.assign({}, defaultConfig, config);
|
||||
// @ts-ignore
|
||||
@ -20,12 +20,11 @@ function setHeaders(config: unknown = {}, headers: unknown = {}) {
|
||||
const uplink = createUplink(config);
|
||||
// @ts-ignore
|
||||
return uplink._setHeaders({
|
||||
headers
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
describe('uplink headers auth test', () => {
|
||||
|
||||
test('if set headers empty should return default headers', () => {
|
||||
const headers = setHeaders();
|
||||
const keys = Object.keys(headers);
|
||||
@ -38,46 +37,49 @@ describe('uplink headers auth test', () => {
|
||||
test('if assigns value invalid to attribute auth', () => {
|
||||
const fnError = function () {
|
||||
setHeaders({
|
||||
auth: ''
|
||||
auth: '',
|
||||
});
|
||||
};
|
||||
|
||||
expect(function ( ) {
|
||||
expect(function () {
|
||||
fnError();
|
||||
}).toThrow(Error('Auth invalid'));
|
||||
});
|
||||
|
||||
test('if assigns the header authorization', () => {
|
||||
const headers = setHeaders({}, {
|
||||
[HEADERS.AUTHORIZATION]: buildToken(TOKEN_BASIC, 'Zm9vX2Jhcg==')
|
||||
});
|
||||
const headers = setHeaders(
|
||||
{},
|
||||
{
|
||||
[HEADERS.AUTHORIZATION]: buildToken(TOKEN_BASIC, 'Zm9vX2Jhcg=='),
|
||||
}
|
||||
);
|
||||
|
||||
expect(Object.keys(headers)).toHaveLength(4);
|
||||
expect(headers[HEADERS.AUTHORIZATION]).toEqual(buildToken(TOKEN_BASIC, 'Zm9vX2Jhcg=='));
|
||||
});
|
||||
|
||||
test(
|
||||
'if assigns headers authorization and token the header precedes',
|
||||
() => {
|
||||
const headers = setHeaders({
|
||||
test('if assigns headers authorization and token the header precedes', () => {
|
||||
const headers = setHeaders(
|
||||
{
|
||||
auth: {
|
||||
type: TOKEN_BEARER,
|
||||
token: 'tokenBearer'
|
||||
}
|
||||
}, {
|
||||
[HEADERS.AUTHORIZATION]: buildToken(TOKEN_BASIC, 'tokenBasic')
|
||||
});
|
||||
token: 'tokenBearer',
|
||||
},
|
||||
},
|
||||
{
|
||||
[HEADERS.AUTHORIZATION]: buildToken(TOKEN_BASIC, 'tokenBasic'),
|
||||
}
|
||||
);
|
||||
|
||||
expect(headers[HEADERS.AUTHORIZATION]).toEqual(buildToken(TOKEN_BASIC, 'tokenBasic'));
|
||||
}
|
||||
);
|
||||
expect(headers[HEADERS.AUTHORIZATION]).toEqual(buildToken(TOKEN_BASIC, 'tokenBasic'));
|
||||
});
|
||||
|
||||
test('set type auth basic', () => {
|
||||
const headers = setHeaders({
|
||||
auth: {
|
||||
type: TOKEN_BASIC,
|
||||
token: 'Zm9vX2Jhcg=='
|
||||
}
|
||||
token: 'Zm9vX2Jhcg==',
|
||||
},
|
||||
});
|
||||
|
||||
expect(Object.keys(headers)).toHaveLength(4);
|
||||
@ -88,8 +90,8 @@ describe('uplink headers auth test', () => {
|
||||
const headers = setHeaders({
|
||||
auth: {
|
||||
type: TOKEN_BEARER,
|
||||
token: 'Zm9vX2Jhcf==='
|
||||
}
|
||||
token: 'Zm9vX2Jhcf===',
|
||||
},
|
||||
});
|
||||
|
||||
expect(Object.keys(headers)).toHaveLength(4);
|
||||
@ -97,16 +99,16 @@ describe('uplink headers auth test', () => {
|
||||
});
|
||||
|
||||
test('set auth type invalid', () => {
|
||||
const fnError = function() {
|
||||
const fnError = function () {
|
||||
setHeaders({
|
||||
auth: {
|
||||
type: 'null',
|
||||
token: 'Zm9vX2Jhcf==='
|
||||
}
|
||||
})
|
||||
token: 'Zm9vX2Jhcf===',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
expect(function ( ) {
|
||||
expect(function () {
|
||||
fnError();
|
||||
}).toThrow(Error(`Auth type 'null' not allowed`));
|
||||
});
|
||||
@ -115,8 +117,8 @@ describe('uplink headers auth test', () => {
|
||||
process.env.NPM_TOKEN = 'myToken';
|
||||
const headers = setHeaders({
|
||||
auth: {
|
||||
type: TOKEN_BEARER
|
||||
}
|
||||
type: TOKEN_BEARER,
|
||||
},
|
||||
});
|
||||
|
||||
expect(headers[HEADERS.AUTHORIZATION]).toBe(buildToken(TOKEN_BEARER, 'myToken'));
|
||||
@ -128,25 +130,24 @@ describe('uplink headers auth test', () => {
|
||||
const headers = setHeaders({
|
||||
auth: {
|
||||
type: TOKEN_BASIC,
|
||||
token_env: 'NPM_TOKEN_TEST'
|
||||
}
|
||||
token_env: 'NPM_TOKEN_TEST',
|
||||
},
|
||||
});
|
||||
|
||||
expect(headers[HEADERS.AUTHORIZATION]).toBe(buildToken(TOKEN_BASIC, 'myTokenTest'));
|
||||
delete process.env.NPM_TOKEN_TEST;
|
||||
});
|
||||
|
||||
|
||||
test('if token not set', () => {
|
||||
const fnError = function() {
|
||||
const fnError = function () {
|
||||
setHeaders({
|
||||
auth: {
|
||||
type: TOKEN_BASIC
|
||||
}
|
||||
type: TOKEN_BASIC,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
expect(function( ) {
|
||||
expect(function () {
|
||||
fnError();
|
||||
}).toThrow(ERROR_CODE.token_required);
|
||||
});
|
||||
|
@ -16,81 +16,81 @@ describe('Use proxy', () => {
|
||||
});
|
||||
|
||||
test('local config should take priority', () => {
|
||||
const x = setupProxy('http://x/x', {http_proxy: '123'}, {http_proxy: '456'});
|
||||
const x = setupProxy('http://x/x', { http_proxy: '123' }, { http_proxy: '456' });
|
||||
expect(x.proxy).toEqual('123');
|
||||
});
|
||||
|
||||
test('no_proxy is invalid', () => {
|
||||
let x = setupProxy('http://x/x', {http_proxy: '123', no_proxy: false}, {});
|
||||
let x = setupProxy('http://x/x', { http_proxy: '123', no_proxy: false }, {});
|
||||
|
||||
expect(x.proxy).toEqual('123');
|
||||
x = setupProxy('http://x/x', {http_proxy: '123', no_proxy: null}, {});
|
||||
x = setupProxy('http://x/x', { http_proxy: '123', no_proxy: null }, {});
|
||||
expect(x.proxy).toEqual('123');
|
||||
x = setupProxy('http://x/x', {http_proxy: '123', no_proxy: []}, {});
|
||||
x = setupProxy('http://x/x', { http_proxy: '123', no_proxy: [] }, {});
|
||||
expect(x.proxy).toEqual('123');
|
||||
x = setupProxy('http://x/x', {http_proxy: '123', no_proxy: ''}, {});
|
||||
x = setupProxy('http://x/x', { http_proxy: '123', no_proxy: '' }, {});
|
||||
expect(x.proxy).toEqual('123');
|
||||
});
|
||||
|
||||
test('no_proxy - simple/include', () => {
|
||||
let x = setupProxy('http://localhost', {http_proxy: '123'}, {no_proxy: 'localhost'});
|
||||
let x = setupProxy('http://localhost', { http_proxy: '123' }, { no_proxy: 'localhost' });
|
||||
|
||||
expect(x.proxy).toEqual(undefined);
|
||||
});
|
||||
|
||||
test('no_proxy - simple/not', () => {
|
||||
let x = setupProxy('http://localhost', {http_proxy: '123'}, {no_proxy: 'blah'});
|
||||
let x = setupProxy('http://localhost', { http_proxy: '123' }, { no_proxy: 'blah' });
|
||||
|
||||
expect(x.proxy).toEqual('123');
|
||||
});
|
||||
|
||||
test('no_proxy - various, single string', () => {
|
||||
let x = setupProxy('http://blahblah', {http_proxy: '123'}, {no_proxy: 'blah'});
|
||||
let x = setupProxy('http://blahblah', { http_proxy: '123' }, { no_proxy: 'blah' });
|
||||
|
||||
expect(x.proxy).toEqual('123');
|
||||
x = setupProxy('http://blah.blah', {}, {http_proxy: '123', no_proxy: 'blah'});
|
||||
x = setupProxy('http://blah.blah', {}, { http_proxy: '123', no_proxy: 'blah' });
|
||||
expect(x.proxy).toEqual(undefined);
|
||||
x = setupProxy('http://blahblah', {}, {http_proxy: '123', no_proxy: '.blah'});
|
||||
x = setupProxy('http://blahblah', {}, { http_proxy: '123', no_proxy: '.blah' });
|
||||
expect(x.proxy).toEqual('123');
|
||||
x = setupProxy('http://blah.blah', {http_proxy: '123', no_proxy: '.blah'}, {});
|
||||
x = setupProxy('http://blah.blah', { http_proxy: '123', no_proxy: '.blah' }, {});
|
||||
expect(x.proxy).toEqual(undefined);
|
||||
x = setupProxy('http://blah', {http_proxy: '123', no_proxy: '.blah'}, {});
|
||||
x = setupProxy('http://blah', { http_proxy: '123', no_proxy: '.blah' }, {});
|
||||
expect(x.proxy).toEqual(undefined);
|
||||
x = setupProxy('http://blahh', {http_proxy: '123', no_proxy: 'blah'}, {});
|
||||
x = setupProxy('http://blahh', { http_proxy: '123', no_proxy: 'blah' }, {});
|
||||
expect(x.proxy).toEqual('123');
|
||||
});
|
||||
|
||||
test('no_proxy - various, array', () => {
|
||||
let x = setupProxy('http://blahblah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'});
|
||||
let x = setupProxy('http://blahblah', { http_proxy: '123' }, { no_proxy: 'foo,bar,blah' });
|
||||
|
||||
expect(x.proxy).toEqual('123');
|
||||
x = setupProxy('http://blah.blah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'});
|
||||
x = setupProxy('http://blah.blah', { http_proxy: '123' }, { no_proxy: 'foo,bar,blah' });
|
||||
expect(x.proxy).toEqual(undefined);
|
||||
x = setupProxy('http://blah.foo', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'});
|
||||
x = setupProxy('http://blah.foo', { http_proxy: '123' }, { no_proxy: 'foo,bar,blah' });
|
||||
expect(x.proxy).toEqual(undefined);
|
||||
x = setupProxy('http://foo.baz', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'});
|
||||
x = setupProxy('http://foo.baz', { http_proxy: '123' }, { no_proxy: 'foo,bar,blah' });
|
||||
expect(x.proxy).toEqual('123');
|
||||
x = setupProxy('http://blahblah', {http_proxy: '123'}, {no_proxy: ['foo', 'bar', 'blah']});
|
||||
x = setupProxy('http://blahblah', { http_proxy: '123' }, { no_proxy: ['foo', 'bar', 'blah'] });
|
||||
expect(x.proxy).toEqual('123');
|
||||
x = setupProxy('http://blah.blah', {http_proxy: '123'}, {no_proxy: ['foo', 'bar', 'blah']});
|
||||
x = setupProxy('http://blah.blah', { http_proxy: '123' }, { no_proxy: ['foo', 'bar', 'blah'] });
|
||||
expect(x.proxy).toEqual(undefined);
|
||||
});
|
||||
|
||||
test('no_proxy - hostport', () => {
|
||||
let x = setupProxy('http://localhost:80', {http_proxy: '123'}, {no_proxy: 'localhost'});
|
||||
let x = setupProxy('http://localhost:80', { http_proxy: '123' }, { no_proxy: 'localhost' });
|
||||
|
||||
expect(x.proxy).toEqual(undefined);
|
||||
x = setupProxy('http://localhost:8080', {http_proxy: '123'}, {no_proxy: 'localhost'});
|
||||
x = setupProxy('http://localhost:8080', { http_proxy: '123' }, { no_proxy: 'localhost' });
|
||||
expect(x.proxy).toEqual(undefined);
|
||||
});
|
||||
|
||||
test('no_proxy - secure', () => {
|
||||
let x = setupProxy('https://something', {http_proxy: '123'}, {});
|
||||
let x = setupProxy('https://something', { http_proxy: '123' }, {});
|
||||
|
||||
expect(x.proxy).toEqual(undefined);
|
||||
x = setupProxy('https://something', {https_proxy: '123'}, {});
|
||||
x = setupProxy('https://something', { https_proxy: '123' }, {});
|
||||
expect(x.proxy).toEqual('123');
|
||||
x = setupProxy('https://something', {http_proxy: '456', https_proxy: '123'}, {});
|
||||
x = setupProxy('https://something', { http_proxy: '456', https_proxy: '123' }, {});
|
||||
expect(x.proxy).toEqual('123');
|
||||
});
|
||||
});
|
||||
|
@ -4,23 +4,20 @@ import { $ResponseExtend, $RequestExtend, $NextFunctionVer } from '@verdaccio/de
|
||||
|
||||
export default (app: Application, selfPath: string): void => {
|
||||
// Hook for tests only
|
||||
app.get(
|
||||
'/-/_debug',
|
||||
function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
const doGarbabeCollector = _.isNil(global.gc) === false;
|
||||
app.get('/-/_debug', function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
const doGarbabeCollector = _.isNil(global.gc) === false;
|
||||
|
||||
if (doGarbabeCollector) {
|
||||
global.gc();
|
||||
}
|
||||
|
||||
next({
|
||||
pid: process.pid,
|
||||
// @ts-ignore
|
||||
main: process.mainModule.filename,
|
||||
conf: selfPath,
|
||||
mem: process.memoryUsage(),
|
||||
gc: doGarbabeCollector
|
||||
});
|
||||
if (doGarbabeCollector) {
|
||||
global.gc();
|
||||
}
|
||||
);
|
||||
|
||||
next({
|
||||
pid: process.pid,
|
||||
// @ts-ignore
|
||||
main: process.mainModule.filename,
|
||||
conf: selfPath,
|
||||
mem: process.memoryUsage(),
|
||||
gc: doGarbabeCollector,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ import { log, final, errorReportingMiddleware } from '@verdaccio/middleware';
|
||||
|
||||
import hookDebug from './debug';
|
||||
|
||||
const defineAPI = function(config: IConfig, storage: IStorageHandler): any {
|
||||
const defineAPI = function (config: IConfig, storage: IStorageHandler): any {
|
||||
const auth: IAuth = new Auth(config);
|
||||
const app: Application = express();
|
||||
// run in production mode by default, just in case
|
||||
@ -32,14 +32,14 @@ const defineAPI = function(config: IConfig, storage: IStorageHandler): any {
|
||||
// Router setup
|
||||
app.use(log(config));
|
||||
app.use(errorReportingMiddleware);
|
||||
app.use(function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
app.use(function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
res.setHeader('X-Powered-By', config.user_agent);
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(compression());
|
||||
|
||||
app.get('/favicon.ico', function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
app.get('/favicon.ico', function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
||||
req.url = '/-/static/favicon.png';
|
||||
next();
|
||||
});
|
||||
@ -55,7 +55,7 @@ const defineAPI = function(config: IConfig, storage: IStorageHandler): any {
|
||||
logger: logger,
|
||||
};
|
||||
|
||||
const plugins: IPluginMiddleware<IConfig>[] = loadPlugin(config, config.middlewares, plugin_params, function(plugin: IPluginMiddleware<IConfig>) {
|
||||
const plugins: IPluginMiddleware<IConfig>[] = loadPlugin(config, config.middlewares, plugin_params, function (plugin: IPluginMiddleware<IConfig>) {
|
||||
return plugin.register_middlewares;
|
||||
});
|
||||
plugins.forEach((plugin: IPluginMiddleware<IConfig>) => {
|
||||
@ -70,17 +70,17 @@ const defineAPI = function(config: IConfig, storage: IStorageHandler): any {
|
||||
app.use('/', renderWebMiddleware(config, auth, storage));
|
||||
app.use('/-/verdaccio/', webAPI(config, auth, storage));
|
||||
} else {
|
||||
app.get('/', function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
app.get('/', function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
next(ErrorCode.getNotFound(API_ERROR.WEB_DISABLED));
|
||||
});
|
||||
}
|
||||
|
||||
// Catch 404
|
||||
app.get('/*', function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
app.get('/*', function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
next(ErrorCode.getNotFound(API_ERROR.FILE_NOT_FOUND));
|
||||
});
|
||||
|
||||
app.use(function(err: HttpError, req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
app.use(function (err: HttpError, req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
if (_.isError(err)) {
|
||||
if (err.code === 'ECONNABORT' && res.statusCode === HTTP_STATUS.NOT_MODIFIED) {
|
||||
return next();
|
||||
@ -102,7 +102,7 @@ const defineAPI = function(config: IConfig, storage: IStorageHandler): any {
|
||||
return app;
|
||||
};
|
||||
|
||||
export default (async function(configHash: any): Promise<any> {
|
||||
export default (async function (configHash: any): Promise<any> {
|
||||
setup(configHash.logs);
|
||||
const config: IConfig = new AppConfig(_.cloneDeep(configHash));
|
||||
// register middleware plugins
|
||||
|
@ -1,53 +1,50 @@
|
||||
const json = {
|
||||
"_id": "@scope\/pk1-test",
|
||||
"name": "@scope\/pk1-test",
|
||||
"description": "",
|
||||
"dist-tags": {
|
||||
"latest": "1.0.6"
|
||||
_id: '@scope/pk1-test',
|
||||
name: '@scope/pk1-test',
|
||||
description: '',
|
||||
'dist-tags': {
|
||||
latest: '1.0.6',
|
||||
},
|
||||
"versions": {
|
||||
"1.0.6": {
|
||||
"name": "@scope\/pk1-test",
|
||||
"version": "1.0.6",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
versions: {
|
||||
'1.0.6': {
|
||||
name: '@scope/pk1-test',
|
||||
version: '1.0.6',
|
||||
description: '',
|
||||
main: 'index.js',
|
||||
scripts: {
|
||||
test: 'echo "Error: no test specified" && exit 1',
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
],
|
||||
"author": {
|
||||
"name": "User NPM",
|
||||
"email": "user@domain.com"
|
||||
keywords: [],
|
||||
author: {
|
||||
name: 'User NPM',
|
||||
email: 'user@domain.com',
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"verdaccio": "^2.7.2"
|
||||
license: 'ISC',
|
||||
dependencies: {
|
||||
verdaccio: '^2.7.2',
|
||||
},
|
||||
"readme": "# test",
|
||||
"readmeFilename": "README.md",
|
||||
"_id": "@scope\/pk1-test@1.0.6",
|
||||
"_npmVersion": "5.5.1",
|
||||
"_nodeVersion": "8.7.0",
|
||||
"_npmUser": {
|
||||
|
||||
readme: '# test',
|
||||
readmeFilename: 'README.md',
|
||||
_id: '@scope/pk1-test@1.0.6',
|
||||
_npmVersion: '5.5.1',
|
||||
_nodeVersion: '8.7.0',
|
||||
_npmUser: {},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9',
|
||||
tarball: 'http://localhost:5555/@scope/pk1-test/-/@scope/pk1-test-1.0.6.tgz',
|
||||
},
|
||||
"dist": {
|
||||
"integrity": "sha512-6gHiERpiDgtb3hjqpQH5\/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==",
|
||||
"shasum": "2c03764f651a9f016ca0b7620421457b619151b9",
|
||||
"tarball": "http:\/\/localhost:5555\/@scope\/pk1-test\/-\/@scope\/pk1-test-1.0.6.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"readme": "# test",
|
||||
"_attachments": {
|
||||
"@scope\/pk1-test-1.0.6.tgz": {
|
||||
"content_type": "application\/octet-stream",
|
||||
"data": "H4sIAAAAAAAAE+2W32vbMBDH85y\/QnjQp9qxLEeBMsbGlocNBmN7bFdQ5WuqxJaEpGQdo\/\/79KPeQsnIw5KUDX\/9IOvurLuz\/DHSjK\/YAiY6jcXSKjk6sMqypHWNdtmD6hlBI0wqQmo8nVbVqMR4OsNoVB66kF1aW8eML+Vv10m9oF\/jP6IfY4QyyTrILlD2eqkcm+gVzpdrJrPz4NuAsULJ4MZFWdBkbcByI7R79CRjx0ScCdnAvf+SkjUFWu8IubzBgXUhDPidQlfZ3BhlLpBUKDiQ1cDFrYDmKkNnZwjuhUM4808+xNVW8P2bMk1Y7vJrtLC1u1MmLPjBF40+Cc4ahV6GDmI\/DWygVRpMwVX3KtXUCg7Sxp7ff3nbt6TBFy65gK1iffsN41yoEHtdFbOiisWMH8bPvXUH0SP3k+KG3UBr+DFy7OGfEJr4x5iWVeS\/pLQe+D+FIv\/agIWI6GX66kFuIhT+1gDjrp\/4d7WAvAwEJPh0u14IufWkM0zaW2W6nLfM2lybgJ4LTJ0\/jWiAK8OcMjt8MW3OlfQppcuhhQ6k+2OgkK2Q8DssFPi\/IHpU9fz3\/+xj5NjDf8QFE39VmE4JDfzPCBn4P4X6\/f88f\/Pu47zomiPk2Lv\/dOv8h+P\/34\/D\/p9CL+Kp67mrGDRo0KBBp9ZPsETQegASAAA=",
|
||||
"length": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
readme: '# test',
|
||||
_attachments: {
|
||||
'@scope/pk1-test-1.0.6.tgz': {
|
||||
content_type: 'application/octet-stream',
|
||||
data:
|
||||
'H4sIAAAAAAAAE+2W32vbMBDH85y/QnjQp9qxLEeBMsbGlocNBmN7bFdQ5WuqxJaEpGQdo//79KPeQsnIw5KUDX/9IOvurLuz/DHSjK/YAiY6jcXSKjk6sMqypHWNdtmD6hlBI0wqQmo8nVbVqMR4OsNoVB66kF1aW8eML+Vv10m9oF/jP6IfY4QyyTrILlD2eqkcm+gVzpdrJrPz4NuAsULJ4MZFWdBkbcByI7R79CRjx0ScCdnAvf+SkjUFWu8IubzBgXUhDPidQlfZ3BhlLpBUKDiQ1cDFrYDmKkNnZwjuhUM4808+xNVW8P2bMk1Y7vJrtLC1u1MmLPjBF40+Cc4ahV6GDmI/DWygVRpMwVX3KtXUCg7Sxp7ff3nbt6TBFy65gK1iffsN41yoEHtdFbOiisWMH8bPvXUH0SP3k+KG3UBr+DFy7OGfEJr4x5iWVeS/pLQe+D+FIv/agIWI6GX66kFuIhT+1gDjrp/4d7WAvAwEJPh0u14IufWkM0zaW2W6nLfM2lybgJ4LTJ0/jWiAK8OcMjt8MW3OlfQppcuhhQ6k+2OgkK2Q8DssFPi/IHpU9fz3/+xj5NjDf8QFE39VmE4JDfzPCBn4P4X6/f88f/Pu47zomiPk2Lv/dOv8h+P/34/D/p9CL+Kp67mrGDRo0KBBp9ZPsETQegASAAA=',
|
||||
length: 512,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = json;
|
||||
|
@ -1,37 +1,36 @@
|
||||
import { Package } from "@verdaccio/types";
|
||||
|
||||
import { Package } from '@verdaccio/types';
|
||||
|
||||
export function generateVersion(pkgName, version) {
|
||||
return {
|
||||
"name": pkgName,
|
||||
"version": version,
|
||||
"description": "some foo dependency",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": {
|
||||
"name": "User NPM",
|
||||
"email": "user@domain.com"
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"verdaccio": "^4.0.0"
|
||||
},
|
||||
"readme": "# test",
|
||||
"readmeFilename": "README.md",
|
||||
"_id": `${pkgName}@${version}`,
|
||||
"_npmVersion": "5.5.1",
|
||||
"_npmUser": {
|
||||
'name': 'foo',
|
||||
},
|
||||
"dist": {
|
||||
"integrity": "sha512-6gHiERpiDgtb3hjqpQH5\/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==",
|
||||
"shasum": "2c03764f651a9f016ca0b7620421457b619151b9", // pragma: allowlist secret
|
||||
"tarball": `http:\/\/localhost:5555\/${pkgName}\/-\/${pkgName}-${version}.tgz`
|
||||
}
|
||||
}
|
||||
return {
|
||||
name: pkgName,
|
||||
version: version,
|
||||
description: 'some foo dependency',
|
||||
main: 'index.js',
|
||||
scripts: {
|
||||
test: 'echo "Error: no test specified" && exit 1',
|
||||
},
|
||||
keywords: [],
|
||||
author: {
|
||||
name: 'User NPM',
|
||||
email: 'user@domain.com',
|
||||
},
|
||||
license: 'ISC',
|
||||
dependencies: {
|
||||
verdaccio: '^4.0.0',
|
||||
},
|
||||
readme: '# test',
|
||||
readmeFilename: 'README.md',
|
||||
_id: `${pkgName}@${version}`,
|
||||
_npmVersion: '5.5.1',
|
||||
_npmUser: {
|
||||
name: 'foo',
|
||||
},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
||||
tarball: `http:\/\/localhost:5555\/${pkgName}\/-\/${pkgName}-${version}.tgz`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,88 +40,87 @@ export function generateVersion(pkgName, version) {
|
||||
* @param _versions
|
||||
*/
|
||||
export function generatePackageUnpublish(pkgName: string, _versions: string[] = ['1.0.0']): Package {
|
||||
const latest: string = _versions[_versions.length - 1];
|
||||
const versions = _versions.reduce((cat, version) => {
|
||||
cat[version] = generateVersion(pkgName, version);
|
||||
return cat;
|
||||
}, {});
|
||||
const latest: string = _versions[_versions.length - 1];
|
||||
const versions = _versions.reduce((cat, version) => {
|
||||
cat[version] = generateVersion(pkgName, version);
|
||||
return cat;
|
||||
}, {});
|
||||
|
||||
// @ts-ignore
|
||||
return {
|
||||
"_id": pkgName,
|
||||
"name": pkgName,
|
||||
"readme": "# test",
|
||||
// users usually is present when run npm star [pkg]
|
||||
"users": {},
|
||||
"dist-tags": {
|
||||
"latest": latest
|
||||
},
|
||||
"versions": versions,
|
||||
}
|
||||
// @ts-ignore
|
||||
return {
|
||||
_id: pkgName,
|
||||
name: pkgName,
|
||||
readme: '# test',
|
||||
// users usually is present when run npm star [pkg]
|
||||
users: {},
|
||||
'dist-tags': {
|
||||
latest: latest,
|
||||
},
|
||||
versions: versions,
|
||||
};
|
||||
}
|
||||
|
||||
export function generateStarMedatada(pkgName: string, users): any {
|
||||
return {
|
||||
"_id": pkgName,
|
||||
"_rev": "3-b0cdaefc9bdb77c8",
|
||||
"users": users
|
||||
}
|
||||
return {
|
||||
_id: pkgName,
|
||||
_rev: '3-b0cdaefc9bdb77c8',
|
||||
users: users,
|
||||
};
|
||||
}
|
||||
|
||||
export function generatePackageMetadata(pkgName: string, version = '1.0.0'): Package {
|
||||
// @ts-ignore
|
||||
return {
|
||||
"_id": pkgName,
|
||||
"name": pkgName,
|
||||
"dist-tags": {
|
||||
"latest": version
|
||||
},
|
||||
"versions": {
|
||||
[version]: {
|
||||
"name": pkgName,
|
||||
"version": version,
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
],
|
||||
"author": {
|
||||
"name": "User NPM",
|
||||
"email": "user@domain.com"
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"verdaccio": "^2.7.2"
|
||||
},
|
||||
"readme": "# test",
|
||||
"readmeFilename": "README.md",
|
||||
"_id": `${pkgName}@${version}`,
|
||||
"_npmVersion": "5.5.1",
|
||||
"_npmUser": {
|
||||
'name': 'foo',
|
||||
},
|
||||
"dist": {
|
||||
"integrity": "sha512-6gHiERpiDgtb3hjqpQH5\/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==",
|
||||
"shasum": "2c03764f651a9f016ca0b7620421457b619151b9", // pragma: allowlist secret
|
||||
"tarball": `http:\/\/localhost:5555\/${pkgName}\/-\/${pkgName}-${version}.tgz`
|
||||
}
|
||||
}
|
||||
},
|
||||
"readme": "# test",
|
||||
"_attachments": {
|
||||
[`${pkgName}-${version}.tgz`]: {
|
||||
"content_type": "application\/octet-stream",
|
||||
"data": "H4sIAAAAAAAAE+2W32vbMBDH85y\/QnjQp9qxLEeBMsbGlocNBmN7bFdQ5WuqxJaEpGQdo\/\/79KPeQsnIw5KUDX\/9IOvurLuz\/DHSjK\/YAiY6jcXSKjk6sMqypHWNdtmD6hlBI0wqQmo8nVbVqMR4OsNoVB66kF1aW8eML+Vv10m9oF\/jP6IfY4QyyTrILlD2eqkcm+gVzpdrJrPz4NuAsULJ4MZFWdBkbcByI7R79CRjx0ScCdnAvf+SkjUFWu8IubzBgXUhDPidQlfZ3BhlLpBUKDiQ1cDFrYDmKkNnZwjuhUM4808+xNVW8P2bMk1Y7vJrtLC1u1MmLPjBF40+Cc4ahV6GDmI\/DWygVRpMwVX3KtXUCg7Sxp7ff3nbt6TBFy65gK1iffsN41yoEHtdFbOiisWMH8bPvXUH0SP3k+KG3UBr+DFy7OGfEJr4x5iWVeS\/pLQe+D+FIv\/agIWI6GX66kFuIhT+1gDjrp\/4d7WAvAwEJPh0u14IufWkM0zaW2W6nLfM2lybgJ4LTJ0\/jWiAK8OcMjt8MW3OlfQppcuhhQ6k+2OgkK2Q8DssFPi\/IHpU9fz3\/+xj5NjDf8QFE39VmE4JDfzPCBn4P4X6\/f88f\/Pu47zomiPk2Lv\/dOv8h+P\/34\/D\/p9CL+Kp67mrGDRo0KBBp9ZPsETQegASAAA=",
|
||||
"length": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
return {
|
||||
_id: pkgName,
|
||||
name: pkgName,
|
||||
'dist-tags': {
|
||||
latest: version,
|
||||
},
|
||||
versions: {
|
||||
[version]: {
|
||||
name: pkgName,
|
||||
version: version,
|
||||
description: '',
|
||||
main: 'index.js',
|
||||
scripts: {
|
||||
test: 'echo "Error: no test specified" && exit 1',
|
||||
},
|
||||
keywords: [],
|
||||
author: {
|
||||
name: 'User NPM',
|
||||
email: 'user@domain.com',
|
||||
},
|
||||
license: 'ISC',
|
||||
dependencies: {
|
||||
verdaccio: '^2.7.2',
|
||||
},
|
||||
readme: '# test',
|
||||
readmeFilename: 'README.md',
|
||||
_id: `${pkgName}@${version}`,
|
||||
_npmVersion: '5.5.1',
|
||||
_npmUser: {
|
||||
name: 'foo',
|
||||
},
|
||||
dist: {
|
||||
integrity: 'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cmE6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
||||
tarball: `http:\/\/localhost:5555\/${pkgName}\/-\/${pkgName}-${version}.tgz`,
|
||||
},
|
||||
},
|
||||
},
|
||||
readme: '# test',
|
||||
_attachments: {
|
||||
[`${pkgName}-${version}.tgz`]: {
|
||||
content_type: 'application/octet-stream',
|
||||
data:
|
||||
'H4sIAAAAAAAAE+2W32vbMBDH85y/QnjQp9qxLEeBMsbGlocNBmN7bFdQ5WuqxJaEpGQdo//79KPeQsnIw5KUDX/9IOvurLuz/DHSjK/YAiY6jcXSKjk6sMqypHWNdtmD6hlBI0wqQmo8nVbVqMR4OsNoVB66kF1aW8eML+Vv10m9oF/jP6IfY4QyyTrILlD2eqkcm+gVzpdrJrPz4NuAsULJ4MZFWdBkbcByI7R79CRjx0ScCdnAvf+SkjUFWu8IubzBgXUhDPidQlfZ3BhlLpBUKDiQ1cDFrYDmKkNnZwjuhUM4808+xNVW8P2bMk1Y7vJrtLC1u1MmLPjBF40+Cc4ahV6GDmI/DWygVRpMwVX3KtXUCg7Sxp7ff3nbt6TBFy65gK1iffsN41yoEHtdFbOiisWMH8bPvXUH0SP3k+KG3UBr+DFy7OGfEJr4x5iWVeS/pLQe+D+FIv/agIWI6GX66kFuIhT+1gDjrp/4d7WAvAwEJPh0u14IufWkM0zaW2W6nLfM2lybgJ4LTJ0/jWiAK8OcMjt8MW3OlfQppcuhhQ6k+2OgkK2Q8DssFPi/IHpU9fz3/+xj5NjDf8QFE39VmE4JDfzPCBn4P4X6/f88f/Pu47zomiPk2Lv/dOv8h+P/34/D/p9CL+Kp67mrGDRo0KBBp9ZPsETQegASAAA=',
|
||||
length: 512,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function generateDeprecateMetadata(pkgName: string, version = '1.0.0', deprecated:string = ''): Package {
|
||||
export function generateDeprecateMetadata(pkgName: string, version = '1.0.0', deprecated: string = ''): Package {
|
||||
const res = {
|
||||
...generatePackageMetadata(pkgName, version),
|
||||
_attachments: {},
|
||||
|
@ -2,20 +2,15 @@ import path from 'path';
|
||||
import request from 'supertest';
|
||||
import _ from 'lodash';
|
||||
|
||||
import {
|
||||
HEADERS,
|
||||
HTTP_STATUS,
|
||||
HEADER_TYPE,
|
||||
API_MESSAGE,
|
||||
TOKEN_BEARER,
|
||||
} from '@verdaccio/dev-commons';
|
||||
import {buildToken, encodeScopedUri} from '@verdaccio/utils';
|
||||
import {setup, logger} from '@verdaccio/logger';
|
||||
import { HEADERS, HTTP_STATUS, HEADER_TYPE, API_MESSAGE, TOKEN_BEARER } from '@verdaccio/dev-commons';
|
||||
import { buildToken, encodeScopedUri } from '@verdaccio/utils';
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
import {mockServer} from '@verdaccio/mock';
|
||||
import { mockServer } from '@verdaccio/mock';
|
||||
|
||||
import {
|
||||
configExample, DOMAIN_SERVERS,
|
||||
configExample,
|
||||
DOMAIN_SERVERS,
|
||||
getNewToken,
|
||||
getPackage,
|
||||
putPackage,
|
||||
@ -24,15 +19,9 @@ import {
|
||||
generateUnPublishURI,
|
||||
} from '@verdaccio/mock';
|
||||
|
||||
import publishMetadata from './helpers/publish-api';
|
||||
import {
|
||||
generateDeprecateMetadata,
|
||||
generatePackageMetadata,
|
||||
generatePackageUnpublish,
|
||||
generateStarMedatada, generateVersion
|
||||
} from './helpers/utils';
|
||||
|
||||
import endPointAPI from '../../src';
|
||||
import publishMetadata from './helpers/publish-api';
|
||||
import { generateDeprecateMetadata, generatePackageMetadata, generatePackageUnpublish, generateStarMedatada, generateVersion } from './helpers/utils';
|
||||
|
||||
setup([]);
|
||||
|
||||
@ -53,24 +42,28 @@ describe('endpoint unit test', () => {
|
||||
let app;
|
||||
let mockRegistry;
|
||||
|
||||
beforeAll(async function(done) {
|
||||
beforeAll(async function (done) {
|
||||
const store = generateRamdonStorage();
|
||||
const mockServerPort = 55549;
|
||||
const configForTest = configExample({
|
||||
filters: {
|
||||
[path.join(__dirname, './plugin/filter')]: {
|
||||
pkg: 'npm_test',
|
||||
version: '2.0.0'
|
||||
}
|
||||
const configForTest = configExample(
|
||||
{
|
||||
filters: {
|
||||
[path.join(__dirname, './plugin/filter')]: {
|
||||
pkg: 'npm_test',
|
||||
version: '2.0.0',
|
||||
},
|
||||
},
|
||||
storage: store,
|
||||
self_path: store,
|
||||
uplinks: {
|
||||
npmjs: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
storage: store,
|
||||
self_path: store,
|
||||
uplinks: {
|
||||
npmjs: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`
|
||||
}
|
||||
}
|
||||
}, 'api.spec.yaml', __dirname);
|
||||
'api.spec.yaml',
|
||||
__dirname
|
||||
);
|
||||
|
||||
app = await endPointAPI(configForTest);
|
||||
const binPath = require.resolve('verdaccio/bin/verdaccio');
|
||||
@ -79,7 +72,7 @@ describe('endpoint unit test', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function(done) {
|
||||
afterAll(function (done) {
|
||||
const [registry, pid] = mockRegistry;
|
||||
registry.stop();
|
||||
logger.info(`registry ${pid} has been stopped`);
|
||||
@ -96,7 +89,7 @@ describe('endpoint unit test', () => {
|
||||
.set(HEADERS.AUTHORIZATION, 'FakeHader')
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.FORBIDDEN)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(/authorization required to access package auth-package/);
|
||||
done();
|
||||
@ -109,7 +102,7 @@ describe('endpoint unit test', () => {
|
||||
.set(HEADERS.AUTHORIZATION, TOKEN_BEARER)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.FORBIDDEN)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(/authorization required to access package auth-package/);
|
||||
done();
|
||||
@ -122,7 +115,7 @@ describe('endpoint unit test', () => {
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, '12345'))
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.FORBIDDEN)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(res.body.error).toBeDefined();
|
||||
expect(res.body.error).toMatch(/authorization required to access package auth-package/);
|
||||
done();
|
||||
@ -130,14 +123,13 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('should test add a new user', (done) => {
|
||||
request(app)
|
||||
.put(`/-/user/org.couchdb.user:${credentials.name}`)
|
||||
.send(credentials)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -154,7 +146,7 @@ describe('endpoint unit test', () => {
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(err).toBeNull();
|
||||
expect(res.body).toBeDefined();
|
||||
expect(res.body.name).toMatch(/vue/);
|
||||
@ -173,13 +165,12 @@ describe('endpoint unit test', () => {
|
||||
// proxy: npmjs
|
||||
|
||||
test('should fetch jquery package from remote uplink', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/jquery')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -191,13 +182,12 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should fetch jquery specific version package from remote uplink', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/jquery/1.5.1')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -209,13 +199,12 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should fetch jquery specific tag package from remote uplink', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/jquery/latest')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -227,13 +216,12 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should fails on fetch jquery specific tag package from remote uplink', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/jquery/never-will-exist-this-tag')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.NOT_FOUND)
|
||||
.end(function(err) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -242,13 +230,12 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should not found a unexisting remote package under scope', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/@verdaccio/not-found')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.NOT_FOUND)
|
||||
.end(function(err) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -263,7 +250,7 @@ describe('endpoint unit test', () => {
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -275,12 +262,12 @@ describe('endpoint unit test', () => {
|
||||
|
||||
test('should not found when a filter fails', (done) => {
|
||||
request(app)
|
||||
// Filter errors look like other uplink errors
|
||||
// Filter errors look like other uplink errors
|
||||
.get('/trigger-filter-failure')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.NOT_FOUND)
|
||||
.end(function(err) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -290,13 +277,12 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should forbid access to remote package', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/forbidden-place')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.UNAUTHORIZED)
|
||||
.end(function(err) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -305,12 +291,11 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should fetch a tarball from remote uplink', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/jquery/-/jquery-1.5.1.tgz')
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.OCTET_STREAM)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -321,12 +306,11 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should fetch a scoped tarball from remote uplink', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/@jquery/jquery/-/@jquery/jquery-1.5.1.tgz')
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.OCTET_STREAM)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -337,12 +321,11 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should fails fetch a tarball from remote uplink', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/jquery/-/jquery-not-found-tarball-0.0.1.tgz')
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.OCTET_STREAM)
|
||||
.expect(HTTP_STATUS.NOT_FOUND)
|
||||
.end(function(err) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
expect(err).not.toBeNull();
|
||||
return done(err);
|
||||
@ -351,20 +334,19 @@ describe('endpoint unit test', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('should test dist-tag api', () => {
|
||||
const jqueryVersion = '2.1.2';
|
||||
const jqueryUpdatedVersion = {
|
||||
'beta': '3.0.0',
|
||||
'jota': '1.6.3'
|
||||
beta: '3.0.0',
|
||||
jota: '1.6.3',
|
||||
};
|
||||
|
||||
test('should set a new tag on jquery', (done) => {
|
||||
putVersion(app, '/jquery/verdaccio-tag', jqueryVersion)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
@ -377,13 +359,12 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should fetch all tag for jquery', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/-/package/jquery/dist-tags')
|
||||
.set('accept-encoding', HEADERS.JSON)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
@ -396,13 +377,12 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should update a new tag on jquery', (done) => {
|
||||
|
||||
request(app)
|
||||
.post('/-/package/jquery/dist-tags')
|
||||
.send(JSON.stringify(jqueryUpdatedVersion))
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
@ -415,13 +395,12 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should fetch all tags for jquery and ccheck previous update', (done) => {
|
||||
|
||||
request(app)
|
||||
.get('/-/package/jquery/dist-tags')
|
||||
.set('accept-encoding', HEADERS.JSON)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
@ -434,14 +413,13 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
test('should set a remove a tag on jquery', (done) => {
|
||||
|
||||
request(app)
|
||||
.del('/-/package/jquery/dist-tags/verdaccio-tag')
|
||||
.set('accept-encoding', HEADERS.JSON)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
// .expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
@ -452,7 +430,6 @@ describe('endpoint unit test', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('should test search api', () => {
|
||||
@ -465,7 +442,7 @@ describe('endpoint unit test', () => {
|
||||
// .set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HEADERS.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
@ -475,7 +452,6 @@ describe('endpoint unit test', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('should test publish/unpublish api', () => {
|
||||
@ -494,14 +470,13 @@ describe('endpoint unit test', () => {
|
||||
}
|
||||
|
||||
const newVersion = '2.0.1';
|
||||
const [newErr] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`,
|
||||
generatePackageMetadata(pkgName, newVersion), token);
|
||||
const [newErr] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`, generatePackageMetadata(pkgName, newVersion), token);
|
||||
if (newErr) {
|
||||
expect(newErr).toBeNull();
|
||||
return done(newErr);
|
||||
}
|
||||
|
||||
const deletePayload = generatePackageUnpublish(pkgName, ['2.0.0']);
|
||||
const deletePayload = generatePackageUnpublish(pkgName, ['2.0.0']);
|
||||
const [err2, res2] = await putPackage(request(app), generateUnPublishURI(pkgName), deletePayload, token);
|
||||
|
||||
expect(err2).toBeNull();
|
||||
@ -551,14 +526,13 @@ describe('endpoint unit test', () => {
|
||||
const newVersion = '1.0.0';
|
||||
const token = await getNewToken(request(app), credentials);
|
||||
|
||||
const [newErr] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`,
|
||||
generatePackageMetadata(pkgName, newVersion), token);
|
||||
const [newErr] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`, generatePackageMetadata(pkgName, newVersion), token);
|
||||
if (newErr) {
|
||||
expect(newErr).toBeNull();
|
||||
return done(newErr);
|
||||
}
|
||||
|
||||
const deletePayload = generatePackageUnpublish(pkgName, ['2.0.0']);
|
||||
const deletePayload = generatePackageUnpublish(pkgName, ['2.0.0']);
|
||||
const [err2, res2] = await putPackage(request(app), generateUnPublishURI(pkgName), deletePayload, token);
|
||||
|
||||
expect(err2).not.toBeNull();
|
||||
@ -588,8 +562,7 @@ describe('endpoint unit test', () => {
|
||||
const newVersion = '1.0.0';
|
||||
const token = await getNewToken(request(app), credentials);
|
||||
|
||||
const [newErr, resp] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`,
|
||||
generatePackageMetadata(pkgName, newVersion), token);
|
||||
const [newErr, resp] = await putPackage(request(app), `/${encodeScopedUri(pkgName)}`, generatePackageMetadata(pkgName, newVersion), token);
|
||||
|
||||
expect(newErr).not.toBeNull();
|
||||
expect(resp.body.error).toMatch(/user jota_only_unpublish_fail is not allowed to publish package only-unpublish/);
|
||||
@ -604,11 +577,15 @@ describe('endpoint unit test', () => {
|
||||
.put('/super-admin-can-unpublish')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.send(JSON.stringify(_.assign({}, publishMetadata, {
|
||||
name: 'super-admin-can-unpublish'
|
||||
})))
|
||||
.send(
|
||||
JSON.stringify(
|
||||
_.assign({}, publishMetadata, {
|
||||
name: 'super-admin-can-unpublish',
|
||||
})
|
||||
)
|
||||
)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
@ -622,7 +599,7 @@ describe('endpoint unit test', () => {
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(err).toBeNull();
|
||||
expect(res.body.ok).toBeDefined();
|
||||
expect(res.body.ok).toMatch(API_MESSAGE.PKG_REMOVED);
|
||||
@ -638,11 +615,15 @@ describe('endpoint unit test', () => {
|
||||
.put('/all-can-unpublish')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.send(JSON.stringify(_.assign({}, publishMetadata, {
|
||||
name: 'all-can-unpublish'
|
||||
})))
|
||||
.send(
|
||||
JSON.stringify(
|
||||
_.assign({}, publishMetadata, {
|
||||
name: 'all-can-unpublish',
|
||||
})
|
||||
)
|
||||
)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
@ -656,7 +637,7 @@ describe('endpoint unit test', () => {
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(err).toBeNull();
|
||||
expect(res.body.ok).toBeDefined();
|
||||
expect(res.body.ok).toMatch(API_MESSAGE.PKG_REMOVED);
|
||||
@ -667,91 +648,98 @@ describe('endpoint unit test', () => {
|
||||
});
|
||||
|
||||
describe('should test star and stars api', () => {
|
||||
const pkgName = '@scope/starPackage';
|
||||
const credentials = { name: 'jota_star', password: 'secretPass' };
|
||||
let token = '';
|
||||
beforeAll(async (done) =>{
|
||||
token = await getNewToken(request(app), credentials);
|
||||
await putPackage(request(app), `/${pkgName}`, generatePackageMetadata(pkgName), token);
|
||||
const pkgName = '@scope/starPackage';
|
||||
const credentials = { name: 'jota_star', password: 'secretPass' };
|
||||
let token = '';
|
||||
beforeAll(async (done) => {
|
||||
token = await getNewToken(request(app), credentials);
|
||||
await putPackage(request(app), `/${pkgName}`, generatePackageMetadata(pkgName), token);
|
||||
done();
|
||||
});
|
||||
|
||||
test('should star a package', (done) => {
|
||||
request(app)
|
||||
.put(`/${pkgName}`)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(
|
||||
JSON.stringify(
|
||||
generateStarMedatada(pkgName, {
|
||||
[credentials.name]: true,
|
||||
})
|
||||
)
|
||||
)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
}
|
||||
expect(res.body.success).toBeDefined();
|
||||
expect(res.body.success).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should star a package', (done) => {
|
||||
request(app)
|
||||
.put(`/${pkgName}`)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(generateStarMedatada(pkgName, {
|
||||
[credentials.name]: true
|
||||
})))
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
}
|
||||
expect(res.body.success).toBeDefined();
|
||||
expect(res.body.success).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
test('should unstar a package', (done) => {
|
||||
request(app)
|
||||
.put(`/${pkgName}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.send(JSON.stringify(generateStarMedatada(pkgName, {})))
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
}
|
||||
expect(res.body.success).toBeDefined();
|
||||
expect(res.body.success).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should unstar a package', (done) => {
|
||||
test('should retrieve stars list with credentials', async (done) => {
|
||||
request(app)
|
||||
.put(`/${pkgName}`)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(generateStarMedatada(pkgName, { [credentials.name]: true }))
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
}
|
||||
request(app)
|
||||
.put(`/${pkgName}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.send(JSON.stringify(generateStarMedatada(pkgName, {})))
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
}
|
||||
expect(res.body.success).toBeDefined();
|
||||
expect(res.body.success).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should retrieve stars list with credentials', async (done) => {
|
||||
request(app)
|
||||
.put(`/${pkgName}`)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(generateStarMedatada(pkgName, {[credentials.name]: true}))
|
||||
.expect(HTTP_STATUS.OK).end(function(err) {
|
||||
.get('/-/_view/starredByUser')
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(
|
||||
JSON.stringify({
|
||||
key: [credentials.name],
|
||||
})
|
||||
)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
}
|
||||
request(app)
|
||||
.get('/-/_view/starredByUser')
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify({
|
||||
key: [credentials.name]
|
||||
}))
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
if (err) {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
}
|
||||
expect(res.body.rows).toBeDefined();
|
||||
expect(res.body.rows).toHaveLength(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
expect(res.body.rows).toBeDefined();
|
||||
expect(res.body.rows).toHaveLength(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('should test (un)deprecate api', () => {
|
||||
const pkgName = '@scope/deprecate';
|
||||
const credentials = { name: 'jota_deprecate', password: 'secretPass' };
|
||||
const version = '1.0.0'
|
||||
const version = '1.0.0';
|
||||
let token = '';
|
||||
beforeAll(async (done) =>{
|
||||
beforeAll(async (done) => {
|
||||
token = await getNewToken(request(app), credentials);
|
||||
await putPackage(request(app), `/${pkgName}`, generatePackageMetadata(pkgName, version), token);
|
||||
done();
|
||||
@ -764,7 +752,7 @@ describe('endpoint unit test', () => {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
}
|
||||
const [,res] = await getPackage(request(app), '', pkgName);
|
||||
const [, res] = await getPackage(request(app), '', pkgName);
|
||||
expect(res.body.versions[version].deprecated).toEqual('get deprecated');
|
||||
done();
|
||||
});
|
||||
@ -778,7 +766,7 @@ describe('endpoint unit test', () => {
|
||||
expect(err).toBeNull();
|
||||
return done(err);
|
||||
}
|
||||
const [,res] = await getPackage(request(app), '', pkgName);
|
||||
const [, res] = await getPackage(request(app), '', pkgName);
|
||||
expect(res.body.versions[version].deprecated).not.toBeDefined();
|
||||
done();
|
||||
});
|
||||
@ -797,7 +785,7 @@ describe('endpoint unit test', () => {
|
||||
expect(err2).not.toBeNull();
|
||||
expect(res2.body.error).toBeDefined();
|
||||
expect(res2.body.error).toMatch(/user only_unpublish is not allowed to publish package @scope\/deprecate/);
|
||||
})
|
||||
});
|
||||
|
||||
test('should deprecate multiple packages', async (done) => {
|
||||
await putPackage(request(app), `/${pkgName}`, generatePackageMetadata(pkgName, '1.0.1'), token);
|
||||
@ -807,11 +795,11 @@ describe('endpoint unit test', () => {
|
||||
deprecated: 'get deprecated',
|
||||
};
|
||||
await putPackage(request(app), `/${encodeScopedUri(pkgName)}`, pkg, token);
|
||||
const [,res] = await getPackage(request(app), '', pkgName);
|
||||
const [, res] = await getPackage(request(app), '', pkgName);
|
||||
expect(res.body.versions[version].deprecated).toEqual('get deprecated');
|
||||
expect(res.body.versions['1.0.1'].deprecated).toEqual('get deprecated');
|
||||
done()
|
||||
})
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,16 +1,14 @@
|
||||
import path from "path";
|
||||
import path from 'path';
|
||||
import express from 'express';
|
||||
import request from 'request';
|
||||
|
||||
import {API_ERROR} from '@verdaccio/dev-commons';
|
||||
import {parseConfigFile} from "@verdaccio/utils";
|
||||
import { API_ERROR } from '@verdaccio/dev-commons';
|
||||
import { parseConfigFile } from '@verdaccio/utils';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
|
||||
import endPointAPI from '../../src';
|
||||
|
||||
setup([
|
||||
{type: 'stdout', format: 'pretty', level: 'trace'}
|
||||
]);
|
||||
setup([{ type: 'stdout', format: 'pretty', level: 'trace' }]);
|
||||
|
||||
const app = express();
|
||||
const server = require('http').createServer(app);
|
||||
@ -23,10 +21,10 @@ describe('basic system test', () => {
|
||||
let port;
|
||||
jest.setTimeout(20000);
|
||||
|
||||
beforeAll(async function(done) {
|
||||
beforeAll(async function (done) {
|
||||
const config = parseConfigFile(parseConfigurationFile('basic.yaml'));
|
||||
app.use(await endPointAPI(config));
|
||||
server.listen(0, function() {
|
||||
server.listen(0, function () {
|
||||
port = server.address().port;
|
||||
done();
|
||||
});
|
||||
@ -36,24 +34,30 @@ describe('basic system test', () => {
|
||||
server.close(done);
|
||||
});
|
||||
|
||||
test('server should respond on /', done => {
|
||||
request({
|
||||
url: 'http://localhost:' + port + '/',
|
||||
}, function(err, res, body) {
|
||||
expect(err).toBeNull();
|
||||
expect(body).toMatch(/Verdaccio/);
|
||||
done();
|
||||
});
|
||||
test('server should respond on /', (done) => {
|
||||
request(
|
||||
{
|
||||
url: 'http://localhost:' + port + '/',
|
||||
},
|
||||
function (err, res, body) {
|
||||
expect(err).toBeNull();
|
||||
expect(body).toMatch(/Verdaccio/);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('server should respond on /___not_found_package', done => {
|
||||
request({
|
||||
json: true,
|
||||
url: `http://localhost:${port}/___not_found_package`,
|
||||
}, function(err, res, body) {
|
||||
expect(err).toBeNull();
|
||||
expect(body.error).toMatch(API_ERROR.NO_PACKAGE);
|
||||
done();
|
||||
});
|
||||
test('server should respond on /___not_found_package', (done) => {
|
||||
request(
|
||||
{
|
||||
json: true,
|
||||
url: `http://localhost:${port}/___not_found_package`,
|
||||
},
|
||||
function (err, res, body) {
|
||||
expect(err).toBeNull();
|
||||
expect(body.error).toMatch(API_ERROR.NO_PACKAGE);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -1,12 +1,12 @@
|
||||
import path from 'path';
|
||||
import request from 'supertest';
|
||||
|
||||
import {HEADERS, HTTP_STATUS, HEADER_TYPE, TOKEN_BEARER, TOKEN_BASIC, API_ERROR} from '@verdaccio/dev-commons';
|
||||
import {mockServer, generateRamdonStorage} from '@verdaccio/mock';
|
||||
import {buildUserBuffer, buildToken} from '@verdaccio/utils';
|
||||
import {configExample, DOMAIN_SERVERS, addUser, getPackage, loginUserToken} from '@verdaccio/mock';
|
||||
import { HEADERS, HTTP_STATUS, HEADER_TYPE, TOKEN_BEARER, TOKEN_BASIC, API_ERROR } from '@verdaccio/dev-commons';
|
||||
import { mockServer, generateRamdonStorage } from '@verdaccio/mock';
|
||||
import { buildUserBuffer, buildToken } from '@verdaccio/utils';
|
||||
import { configExample, DOMAIN_SERVERS, addUser, getPackage, loginUserToken } from '@verdaccio/mock';
|
||||
|
||||
import {setup, logger} from '@verdaccio/logger';
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
import endPointAPI from '../../src';
|
||||
|
||||
@ -22,18 +22,22 @@ describe('endpoint user auth JWT unit test', () => {
|
||||
let mockRegistry;
|
||||
const FAKE_TOKEN: string = buildToken(TOKEN_BEARER, 'fake');
|
||||
|
||||
beforeAll(async function(done) {
|
||||
beforeAll(async function (done) {
|
||||
const mockServerPort = 55546;
|
||||
const store = generateRamdonStorage();
|
||||
const configForTest = configExample({
|
||||
storage: store,
|
||||
uplinks: {
|
||||
remote: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`
|
||||
}
|
||||
const configForTest = configExample(
|
||||
{
|
||||
storage: store,
|
||||
uplinks: {
|
||||
remote: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
},
|
||||
},
|
||||
self_path: store,
|
||||
},
|
||||
self_path: store
|
||||
}, 'jwt.yaml', __dirname);
|
||||
'jwt.yaml',
|
||||
__dirname
|
||||
);
|
||||
|
||||
app = await endPointAPI(configForTest);
|
||||
const binPath = require.resolve('verdaccio/bin/verdaccio');
|
||||
@ -42,7 +46,7 @@ describe('endpoint user auth JWT unit test', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function(done) {
|
||||
afterAll(function (done) {
|
||||
const [registry, pid] = mockRegistry;
|
||||
registry.stop();
|
||||
logger.info(`registry ${pid} has been stopped`);
|
||||
@ -86,12 +90,13 @@ describe('endpoint user auth JWT unit test', () => {
|
||||
const token = buildUserBuffer(credentials.name, credentials.password).toString('base64');
|
||||
// put should exist in request
|
||||
// @ts-ignore
|
||||
request(app).put(`/-/user/org.couchdb.user:${credentials.name}/-rev/undefined`)
|
||||
request(app)
|
||||
.put(`/-/user/org.couchdb.user:${credentials.name}/-rev/undefined`)
|
||||
.send(credentials)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BASIC, token))
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(err).toBeNull();
|
||||
expect(res.body.ok).toBeDefined();
|
||||
expect(res.body.token).toBeDefined();
|
||||
|
@ -1,16 +1,14 @@
|
||||
import path from 'path';
|
||||
import request from 'supertest';
|
||||
|
||||
import {setup, logger} from '@verdaccio/logger'
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
setup([]);
|
||||
|
||||
import { HEADERS, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import { generateRamdonStorage, mockServer, configExample, DOMAIN_SERVERS } from '@verdaccio/mock';
|
||||
import endPointAPI from '../../src';
|
||||
|
||||
import {generateRamdonStorage, mockServer, configExample, DOMAIN_SERVERS} from '@verdaccio/mock';
|
||||
|
||||
|
||||
describe('api with no limited access configuration', () => {
|
||||
let app;
|
||||
let mockRegistry;
|
||||
@ -19,14 +17,18 @@ describe('api with no limited access configuration', () => {
|
||||
|
||||
beforeAll(async (done) => {
|
||||
const mockServerPort = 55530;
|
||||
const configForTest = configExample({
|
||||
self_path: store,
|
||||
uplinks: {
|
||||
remote: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`
|
||||
}
|
||||
const configForTest = configExample(
|
||||
{
|
||||
self_path: store,
|
||||
uplinks: {
|
||||
remote: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, 'pkg.access.yaml', __dirname);
|
||||
'pkg.access.yaml',
|
||||
__dirname
|
||||
);
|
||||
|
||||
app = await endPointAPI(configForTest);
|
||||
const binPath = require.resolve('verdaccio/bin/verdaccio');
|
||||
@ -35,8 +37,7 @@ describe('api with no limited access configuration', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
afterAll(function(done) {
|
||||
afterAll(function (done) {
|
||||
const [registry, pid] = mockRegistry;
|
||||
registry.stop();
|
||||
logger.info(`registry ${pid} has been stopped`);
|
||||
@ -45,16 +46,14 @@ describe('api with no limited access configuration', () => {
|
||||
});
|
||||
|
||||
describe('test proxy packages partially restricted', () => {
|
||||
|
||||
|
||||
test('should test fails on fetch endpoint /-/not-found', (done) => {
|
||||
request(app)
|
||||
// @ts-ignore
|
||||
// @ts-ignore
|
||||
.get('/not-found-for-sure')
|
||||
.set(HEADERS.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADERS.CONTENT_TYPE, /json/)
|
||||
.expect(HTTP_STATUS.NOT_FOUND)
|
||||
.end(function(err) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -70,7 +69,7 @@ describe('api with no limited access configuration', () => {
|
||||
.set(HEADERS.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADERS.CONTENT_TYPE, /json/)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -86,7 +85,7 @@ describe('api with no limited access configuration', () => {
|
||||
.set(HEADERS.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HEADERS.CONTENT_TYPE, /json/)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -95,5 +94,4 @@ describe('api with no limited access configuration', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,17 +1,16 @@
|
||||
import path from 'path';
|
||||
import request from 'supertest';
|
||||
|
||||
import {mockServer} from '@verdaccio/mock';
|
||||
import {API_ERROR, HTTP_STATUS, SUPPORT_ERRORS} from '@verdaccio/dev-commons';
|
||||
import {setup, logger} from '@verdaccio/logger';
|
||||
import { mockServer } from '@verdaccio/mock';
|
||||
import { API_ERROR, HTTP_STATUS, SUPPORT_ERRORS } from '@verdaccio/dev-commons';
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
import {generateRamdonStorage, getNewToken, getProfile, postProfile, configExample, DOMAIN_SERVERS} from '@verdaccio/mock';
|
||||
import { generateRamdonStorage, getNewToken, getProfile, postProfile, configExample, DOMAIN_SERVERS } from '@verdaccio/mock';
|
||||
|
||||
import endPointAPI from '../../src';
|
||||
|
||||
setup([]);
|
||||
|
||||
|
||||
describe('endpoint user profile', () => {
|
||||
let app;
|
||||
let mockRegistry;
|
||||
@ -20,15 +19,19 @@ describe('endpoint user profile', () => {
|
||||
beforeAll(async (done) => {
|
||||
const store = generateRamdonStorage();
|
||||
const mockServerPort = 55544;
|
||||
const configForTest = configExample({
|
||||
storage: store,
|
||||
uplinks: {
|
||||
remote: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`
|
||||
}
|
||||
const configForTest = configExample(
|
||||
{
|
||||
storage: store,
|
||||
uplinks: {
|
||||
remote: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
},
|
||||
},
|
||||
self_path: store,
|
||||
},
|
||||
self_path: store
|
||||
}, 'profile.yaml', __dirname);
|
||||
'profile.yaml',
|
||||
__dirname
|
||||
);
|
||||
|
||||
app = await endPointAPI(configForTest);
|
||||
const binPath = require.resolve('verdaccio/bin/verdaccio');
|
||||
@ -37,7 +40,7 @@ describe('endpoint user profile', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function(done) {
|
||||
afterAll(function (done) {
|
||||
const [registry, pid] = mockRegistry;
|
||||
registry.stop();
|
||||
logger.info(`registry ${pid} has been stopped`);
|
||||
@ -45,7 +48,6 @@ describe('endpoint user profile', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
test('should fetch a profile of logged user', async (done) => {
|
||||
const credentials = { name: 'JotaJWT', password: 'secretPass' };
|
||||
const token = await getNewToken(request(app), credentials);
|
||||
@ -62,7 +64,7 @@ describe('endpoint user profile', () => {
|
||||
password: {
|
||||
new: '12345678',
|
||||
old: credentials.password,
|
||||
}
|
||||
},
|
||||
};
|
||||
const token = await getNewToken(request(app), credentials);
|
||||
const [err1, res1] = await postProfile(request(app), body, token);
|
||||
@ -78,7 +80,7 @@ describe('endpoint user profile', () => {
|
||||
password: {
|
||||
new: 'p1',
|
||||
old: credentials.password,
|
||||
}
|
||||
},
|
||||
};
|
||||
const token = await getNewToken(request(app), credentials);
|
||||
const [, resp] = await postProfile(request(app), body, token, HTTP_STATUS.UNAUTHORIZED);
|
||||
@ -94,7 +96,7 @@ describe('endpoint user profile', () => {
|
||||
test('should report TFA is disabled', async (done) => {
|
||||
const credentials = { name: 'userTest2002', password: 'secretPass002' };
|
||||
const body = {
|
||||
tfa: {}
|
||||
tfa: {},
|
||||
};
|
||||
const token = await getNewToken(request(app), credentials);
|
||||
const [, resp] = await postProfile(request(app), body, token, HTTP_STATUS.SERVICE_UNAVAILABLE);
|
||||
|
@ -1,14 +1,14 @@
|
||||
import path from "path";
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { Config as AppConfig } from '@verdaccio/config';
|
||||
import {Config, UpLinkConf} from '@verdaccio/types';
|
||||
import { Config, UpLinkConf } from '@verdaccio/types';
|
||||
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
import {IProxy} from '@verdaccio/dev-types';
|
||||
import { API_ERROR, HTTP_STATUS } from "@verdaccio/dev-commons";
|
||||
import { IProxy } from '@verdaccio/dev-types';
|
||||
import { API_ERROR, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import { mockServer, configExample, DOMAIN_SERVERS } from '@verdaccio/mock';
|
||||
import { ProxyStorage } from '@verdaccio/proxy';
|
||||
import {setup, logger} from '@verdaccio/logger';
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
setup([]);
|
||||
|
||||
@ -16,7 +16,7 @@ describe('UpStorge', () => {
|
||||
const mockServerPort = 55547;
|
||||
let mockRegistry;
|
||||
const uplinkDefault = {
|
||||
url: `http://0.0.0.0:${mockServerPort}`
|
||||
url: `http://0.0.0.0:${mockServerPort}`,
|
||||
};
|
||||
const generateProxy = (config: UpLinkConf = uplinkDefault) => {
|
||||
const appConfig: Config = new AppConfig(configExample());
|
||||
@ -30,7 +30,7 @@ describe('UpStorge', () => {
|
||||
mockRegistry = await mockServer(mockServerPort, { storePath, silence: true }).init(binPath);
|
||||
});
|
||||
|
||||
afterAll(function(done) {
|
||||
afterAll(function (done) {
|
||||
const [registry, pid] = mockRegistry;
|
||||
registry.stop();
|
||||
logger.info(`registry ${pid} has been stopped`);
|
||||
@ -59,7 +59,7 @@ describe('UpStorge', () => {
|
||||
test('should be get remote metadata with etag', (done) => {
|
||||
const proxy = generateProxy();
|
||||
|
||||
proxy.getRemoteMetadata('jquery', {etag: '123456'}, (err, data, etag) => {
|
||||
proxy.getRemoteMetadata('jquery', { etag: '123456' }, (err, data, etag) => {
|
||||
expect(err).toBeNull();
|
||||
expect(_.isString(etag)).toBeTruthy();
|
||||
expect(data.name).toBe('jquery');
|
||||
@ -70,7 +70,7 @@ describe('UpStorge', () => {
|
||||
test('should be get remote metadata package does not exist', (done) => {
|
||||
const proxy = generateProxy();
|
||||
|
||||
proxy.getRemoteMetadata('@verdaccio/fake-package', {etag: '123456'}, (err) => {
|
||||
proxy.getRemoteMetadata('@verdaccio/fake-package', { etag: '123456' }, (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.statusCode).toBe(HTTP_STATUS.NOT_FOUND);
|
||||
expect(err.message).toMatch(API_ERROR.NOT_PACKAGE_UPLINK);
|
||||
@ -79,23 +79,21 @@ describe('UpStorge', () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('UpStorge::fetchTarball', () => {
|
||||
test('should fetch a tarball from uplink', (done) => {
|
||||
const proxy = generateProxy();
|
||||
const tarball = `http://${DOMAIN_SERVERS}:${mockServerPort}/jquery/-/jquery-1.5.1.tgz`;
|
||||
const stream = proxy.fetchTarball(tarball);
|
||||
|
||||
stream.on('error', function(err) {
|
||||
stream.on('error', function (err) {
|
||||
expect(err).toBeNull();
|
||||
done();
|
||||
});
|
||||
|
||||
stream.on('content-length', function(contentLength) {
|
||||
stream.on('content-length', function (contentLength) {
|
||||
expect(contentLength).toBeDefined();
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('should throw a 404 on fetch a tarball from uplink', (done) => {
|
||||
@ -103,7 +101,7 @@ describe('UpStorge', () => {
|
||||
const tarball = `http://${DOMAIN_SERVERS}:${mockServerPort}/jquery/-/no-exist-1.5.1.tgz`;
|
||||
const stream = proxy.fetchTarball(tarball);
|
||||
|
||||
stream.on('error', function(err: VerdaccioError) {
|
||||
stream.on('error', function (err: VerdaccioError) {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.statusCode).toBe(HTTP_STATUS.NOT_FOUND);
|
||||
expect(err.message).toMatch(API_ERROR.NOT_FILE_UPLINK);
|
||||
@ -111,11 +109,10 @@ describe('UpStorge', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
stream.on('content-length', function(contentLength) {
|
||||
stream.on('content-length', function (contentLength) {
|
||||
expect(contentLength).toBeDefined();
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('should be offline uplink', (done) => {
|
||||
@ -126,14 +123,14 @@ describe('UpStorge', () => {
|
||||
|
||||
// to test a uplink is offline we have to be try 3 times
|
||||
// the default failed request are set to 2
|
||||
process.nextTick(function() {
|
||||
stream.on('error', function(err) {
|
||||
process.nextTick(function () {
|
||||
stream.on('error', function (err) {
|
||||
expect(err).not.toBeNull();
|
||||
// expect(err.statusCode).toBe(404);
|
||||
expect(proxy.failed_requests).toBe(1);
|
||||
|
||||
const streamSecondTry = proxy.fetchTarball(tarball);
|
||||
streamSecondTry.on('error', function(err) {
|
||||
streamSecondTry.on('error', function (err) {
|
||||
expect(err).not.toBeNull();
|
||||
/*
|
||||
code: 'ENOTFOUND',
|
||||
@ -142,7 +139,7 @@ describe('UpStorge', () => {
|
||||
// expect(err.statusCode).toBe(404);
|
||||
expect(proxy.failed_requests).toBe(2);
|
||||
const streamThirdTry = proxy.fetchTarball(tarball);
|
||||
streamThirdTry.on('error', function(err: VerdaccioError) {
|
||||
streamThirdTry.on('error', function (err: VerdaccioError) {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.statusCode).toBe(HTTP_STATUS.INTERNAL_ERROR);
|
||||
expect(proxy.failed_requests).toBe(2);
|
||||
@ -156,16 +153,13 @@ describe('UpStorge', () => {
|
||||
});
|
||||
|
||||
describe('UpStorge::isUplinkValid', () => {
|
||||
|
||||
describe('valid use cases', () => {
|
||||
const validateUpLink = (
|
||||
url: string,
|
||||
tarBallUrl = `${url}/artifactory/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz`) => {
|
||||
const validateUpLink = (url: string, tarBallUrl = `${url}/artifactory/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz`) => {
|
||||
const uplinkConf = { url };
|
||||
const proxy: IProxy = generateProxy(uplinkConf);
|
||||
|
||||
return proxy.isUplinkValid(tarBallUrl);
|
||||
}
|
||||
};
|
||||
|
||||
test('should validate tarball path against uplink', () => {
|
||||
expect(validateUpLink('https://artifactory.mydomain.com')).toBe(true);
|
||||
@ -190,8 +184,7 @@ describe('UpStorge', () => {
|
||||
// corner case https://github.com/verdaccio/verdaccio/issues/571
|
||||
test('should validate tarball path against uplink case#6', () => {
|
||||
// same protocol, same domain, port === 443 which is also the standard for https
|
||||
expect(validateUpLink('https://my.domain.test',
|
||||
`https://my.domain.test:443/artifactory/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz`)).toBe(true);
|
||||
expect(validateUpLink('https://my.domain.test', `https://my.domain.test:443/artifactory/api/npm/npm/pk1-juan/-/pk1-juan-1.0.7.tgz`)).toBe(true);
|
||||
});
|
||||
|
||||
test('should validate tarball path against uplink case#7', () => {
|
||||
@ -253,7 +246,5 @@ describe('UpStorge', () => {
|
||||
expect(proxy.isUplinkValid(tarBallUrl)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,69 +1,35 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import {Writable} from 'stream';
|
||||
import { Writable } from 'stream';
|
||||
import { Config as AppConfig } from '@verdaccio/config';
|
||||
import { Storage } from '@verdaccio/store';
|
||||
import {IStorageHandler} from '@verdaccio/dev-types';
|
||||
import { IStorageHandler } from '@verdaccio/dev-types';
|
||||
|
||||
import {Config} from '@verdaccio/types';
|
||||
import {API_ERROR, HTTP_STATUS} from '@verdaccio/dev-commons';
|
||||
import {mockServer, configExample, DOMAIN_SERVERS, generateRamdonStorage} from '@verdaccio/mock';
|
||||
import { Config } from '@verdaccio/types';
|
||||
import { API_ERROR, HTTP_STATUS } from '@verdaccio/dev-commons';
|
||||
import { mockServer, configExample, DOMAIN_SERVERS, generateRamdonStorage } from '@verdaccio/mock';
|
||||
|
||||
import {setup, logger} from '@verdaccio/logger';
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
setup([]);
|
||||
|
||||
|
||||
const mockServerPort = 55548;
|
||||
|
||||
const generateStorage = async function() {
|
||||
const generateStorage = async function () {
|
||||
const storagePath = generateRamdonStorage();
|
||||
const storageConfig = configExample({
|
||||
self_path: storagePath,
|
||||
storage: storagePath,
|
||||
uplinks: {
|
||||
npmjs: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`
|
||||
}
|
||||
}
|
||||
}, 'store.spec.yaml', __dirname);
|
||||
|
||||
const config: Config = new AppConfig(storageConfig);
|
||||
const store: IStorageHandler = new Storage(config);
|
||||
await store.init(config, []);
|
||||
|
||||
return store;
|
||||
};
|
||||
|
||||
const generateSameUplinkStorage = async function() {
|
||||
const storagePath = generateRamdonStorage();
|
||||
console.log("-->storagePath", storagePath);
|
||||
const storageConfig = configExample({
|
||||
self_path: storagePath,
|
||||
storage: storagePath,
|
||||
packages: {
|
||||
jquery: {
|
||||
access: ['$all'],
|
||||
publish: ['$all'],
|
||||
proxy: ['cached'],
|
||||
const storageConfig = configExample(
|
||||
{
|
||||
self_path: storagePath,
|
||||
storage: storagePath,
|
||||
uplinks: {
|
||||
npmjs: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
},
|
||||
},
|
||||
'@jquery/*': {
|
||||
access: ['$all'],
|
||||
publish: ['$all'],
|
||||
proxy: ['notcached'],
|
||||
}
|
||||
},
|
||||
uplinks: {
|
||||
cached: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
cache: true,
|
||||
},
|
||||
notcached: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
cache: false,
|
||||
}
|
||||
}
|
||||
}, 'store.spec.yaml', __dirname);
|
||||
'store.spec.yaml',
|
||||
__dirname
|
||||
);
|
||||
|
||||
const config: Config = new AppConfig(storageConfig);
|
||||
const store: IStorageHandler = new Storage(config);
|
||||
@ -72,23 +38,65 @@ const generateSameUplinkStorage = async function() {
|
||||
return store;
|
||||
};
|
||||
|
||||
const createNullStream = () => new Writable({
|
||||
write: function(chunk, encoding, next) {
|
||||
next();
|
||||
}
|
||||
});
|
||||
const generateSameUplinkStorage = async function () {
|
||||
const storagePath = generateRamdonStorage();
|
||||
console.log('-->storagePath', storagePath);
|
||||
const storageConfig = configExample(
|
||||
{
|
||||
self_path: storagePath,
|
||||
storage: storagePath,
|
||||
packages: {
|
||||
jquery: {
|
||||
access: ['$all'],
|
||||
publish: ['$all'],
|
||||
proxy: ['cached'],
|
||||
},
|
||||
'@jquery/*': {
|
||||
access: ['$all'],
|
||||
publish: ['$all'],
|
||||
proxy: ['notcached'],
|
||||
},
|
||||
},
|
||||
uplinks: {
|
||||
cached: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
cache: true,
|
||||
},
|
||||
notcached: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
cache: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
'store.spec.yaml',
|
||||
__dirname
|
||||
);
|
||||
|
||||
const config: Config = new AppConfig(storageConfig);
|
||||
const store: IStorageHandler = new Storage(config);
|
||||
await store.init(config, []);
|
||||
|
||||
return store;
|
||||
};
|
||||
|
||||
const createNullStream = () =>
|
||||
new Writable({
|
||||
write: function (chunk, encoding, next) {
|
||||
next();
|
||||
},
|
||||
});
|
||||
|
||||
describe('StorageTest', () => {
|
||||
let mockRegistry;
|
||||
|
||||
beforeAll(async done => {
|
||||
beforeAll(async (done) => {
|
||||
const binPath = require.resolve('verdaccio/bin/verdaccio');
|
||||
const storePath = path.join(__dirname, '/mock/store');
|
||||
mockRegistry = await mockServer(mockServerPort, { storePath, silence: true }).init(binPath);
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function(done) {
|
||||
afterAll(function (done) {
|
||||
const [registry, pid] = mockRegistry;
|
||||
registry.stop();
|
||||
logger.info(`registry ${pid} has been stopped`);
|
||||
@ -185,7 +193,7 @@ describe('StorageTest', () => {
|
||||
|
||||
test.skip('should not touch if the package exists and has no uplinks', async (done) => {
|
||||
const storagePath = generateRamdonStorage();
|
||||
const storage: IStorageHandler = await generateStorage() as IStorageHandler;
|
||||
const storage: IStorageHandler = (await generateStorage()) as IStorageHandler;
|
||||
const metadataSource = path.join(__dirname, '../../partials/metadata');
|
||||
const metadataPath = path.join(storagePath, 'npm_test/package.json');
|
||||
|
||||
|
@ -2,16 +2,12 @@ import path from 'path';
|
||||
import request from 'supertest';
|
||||
import _ from 'lodash';
|
||||
|
||||
import {
|
||||
HEADERS,
|
||||
HTTP_STATUS,
|
||||
HEADER_TYPE, TOKEN_BEARER, API_ERROR, SUPPORT_ERRORS,
|
||||
} from '@verdaccio/dev-commons';
|
||||
import { HEADERS, HTTP_STATUS, HEADER_TYPE, TOKEN_BEARER, API_ERROR, SUPPORT_ERRORS } from '@verdaccio/dev-commons';
|
||||
|
||||
import {buildToken} from '@verdaccio/utils';
|
||||
import {generateRamdonStorage, DOMAIN_SERVERS, mockServer, getNewToken, configExample} from '@verdaccio/mock';
|
||||
import { buildToken } from '@verdaccio/utils';
|
||||
import { generateRamdonStorage, DOMAIN_SERVERS, mockServer, getNewToken, configExample } from '@verdaccio/mock';
|
||||
|
||||
import {setup, logger} from '@verdaccio/logger';
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
|
||||
import endPointAPI from '../../src';
|
||||
|
||||
@ -27,13 +23,13 @@ const generateTokenCLI = async (app, token, payload): Promise<any> => {
|
||||
.send(JSON.stringify(payload))
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.end(function(err, resp) {
|
||||
.end(function (err, resp) {
|
||||
if (err) {
|
||||
return reject([err, resp]);
|
||||
}
|
||||
resolve([err, resp]);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const deleteTokenCLI = async (app, token, tokenToDelete): Promise<any> => {
|
||||
@ -43,7 +39,7 @@ const deleteTokenCLI = async (app, token, tokenToDelete): Promise<any> => {
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.end(function(err, resp) {
|
||||
.end(function (err, resp) {
|
||||
if (err) {
|
||||
return reject([err, resp]);
|
||||
}
|
||||
@ -57,18 +53,22 @@ describe('endpoint unit test', () => {
|
||||
let mockRegistry;
|
||||
let token;
|
||||
|
||||
beforeAll(async function(done) {
|
||||
beforeAll(async function (done) {
|
||||
const store = generateRamdonStorage();
|
||||
const mockServerPort = 55543;
|
||||
const configForTest = configExample({
|
||||
storage: store,
|
||||
self_path: store,
|
||||
uplinks: {
|
||||
npmjs: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`
|
||||
}
|
||||
}
|
||||
}, 'token.spec.yaml', __dirname);
|
||||
const configForTest = configExample(
|
||||
{
|
||||
storage: store,
|
||||
self_path: store,
|
||||
uplinks: {
|
||||
npmjs: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
'token.spec.yaml',
|
||||
__dirname
|
||||
);
|
||||
|
||||
app = await endPointAPI(configForTest);
|
||||
const binPath = require.resolve('verdaccio/bin/verdaccio');
|
||||
@ -78,7 +78,7 @@ describe('endpoint unit test', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function(done) {
|
||||
afterAll(function (done) {
|
||||
const [registry, pid] = mockRegistry;
|
||||
registry.stop();
|
||||
logger.info(`registry ${pid} has been stopped`);
|
||||
@ -93,12 +93,12 @@ describe('endpoint unit test', () => {
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, resp) {
|
||||
.end(function (err, resp) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
const { objects, urls} = resp.body;
|
||||
const { objects, urls } = resp.body;
|
||||
expect(objects).toHaveLength(0);
|
||||
expect(urls.next).toEqual('');
|
||||
done();
|
||||
@ -109,7 +109,7 @@ describe('endpoint unit test', () => {
|
||||
await generateTokenCLI(app, token, {
|
||||
password: credentials.password,
|
||||
readonly: false,
|
||||
cidr_whitelist: []
|
||||
cidr_whitelist: [],
|
||||
});
|
||||
|
||||
request(app)
|
||||
@ -117,12 +117,12 @@ describe('endpoint unit test', () => {
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, resp) {
|
||||
.end(function (err, resp) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
const { objects, urls} = resp.body;
|
||||
const { objects, urls } = resp.body;
|
||||
|
||||
expect(objects).toHaveLength(1);
|
||||
const [tokenGenerated] = objects;
|
||||
@ -141,7 +141,7 @@ describe('endpoint unit test', () => {
|
||||
const res = await generateTokenCLI(app, token, {
|
||||
password: credentials.password,
|
||||
readonly: false,
|
||||
cidr_whitelist: []
|
||||
cidr_whitelist: [],
|
||||
});
|
||||
|
||||
const t = res[1].body.token;
|
||||
@ -153,7 +153,7 @@ describe('endpoint unit test', () => {
|
||||
.set(HEADERS.AUTHORIZATION, buildToken(TOKEN_BEARER, token))
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err) {
|
||||
.end(function (err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@ -171,7 +171,7 @@ describe('endpoint unit test', () => {
|
||||
await generateTokenCLI(app, token, {
|
||||
password: 'wrongPassword',
|
||||
readonly: false,
|
||||
cidr_whitelist: []
|
||||
cidr_whitelist: [],
|
||||
});
|
||||
done();
|
||||
} catch (e) {
|
||||
@ -187,7 +187,7 @@ describe('endpoint unit test', () => {
|
||||
try {
|
||||
const res = await generateTokenCLI(app, token, {
|
||||
password: credentials.password,
|
||||
cidr_whitelist: []
|
||||
cidr_whitelist: [],
|
||||
});
|
||||
|
||||
expect(res[0]).toBeNull();
|
||||
|
@ -1,12 +1,11 @@
|
||||
import path from 'path';
|
||||
import request from 'supertest';
|
||||
|
||||
import { HEADERS, API_ERROR, HTTP_STATUS, HEADER_TYPE, DIST_TAGS } from '@verdaccio/dev-commons';
|
||||
|
||||
import { HEADERS, API_ERROR, HTTP_STATUS, HEADER_TYPE, DIST_TAGS} from '@verdaccio/dev-commons';
|
||||
import { addUser, mockServer, DOMAIN_SERVERS, configExample, generateRamdonStorage } from '@verdaccio/mock';
|
||||
|
||||
import {addUser, mockServer, DOMAIN_SERVERS, configExample, generateRamdonStorage} from '@verdaccio/mock';
|
||||
|
||||
import {setup, logger} from '@verdaccio/logger';
|
||||
import { setup, logger } from '@verdaccio/logger';
|
||||
import endPointAPI from '../../src';
|
||||
import forbiddenPlace from './partials/forbidden-place';
|
||||
import publishMetadata from './partials/publish-api';
|
||||
@ -23,15 +22,19 @@ describe('endpoint web unit test', () => {
|
||||
beforeAll(async (done) => {
|
||||
const store = generateRamdonStorage();
|
||||
const mockServerPort = 55523;
|
||||
const configForTest = configExample({
|
||||
storage: store,
|
||||
self_path: store,
|
||||
uplinks: {
|
||||
remote: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`
|
||||
}
|
||||
const configForTest = configExample(
|
||||
{
|
||||
storage: store,
|
||||
self_path: store,
|
||||
uplinks: {
|
||||
remote: {
|
||||
url: `http://${DOMAIN_SERVERS}:${mockServerPort}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, 'web.yaml', __dirname);
|
||||
'web.yaml',
|
||||
__dirname
|
||||
);
|
||||
app = await endPointAPI(configForTest);
|
||||
const binPath = require.resolve('verdaccio/bin/verdaccio');
|
||||
const storePath = path.join(__dirname, '/mock/store');
|
||||
@ -39,7 +42,7 @@ describe('endpoint web unit test', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function(done) {
|
||||
afterAll(function (done) {
|
||||
const [registry, pid] = mockRegistry;
|
||||
registry.stop();
|
||||
logger.info(`registry ${pid} has been stopped`);
|
||||
@ -55,19 +58,15 @@ describe('endpoint web unit test', () => {
|
||||
.send(JSON.stringify(publishMetadata))
|
||||
.expect(HTTP_STATUS.CREATED);
|
||||
|
||||
await request(app)
|
||||
.put('/forbidden-place')
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(forbiddenPlace))
|
||||
.expect(HTTP_STATUS.CREATED);
|
||||
await request(app).put('/forbidden-place').set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON).send(JSON.stringify(forbiddenPlace)).expect(HTTP_STATUS.CREATED);
|
||||
});
|
||||
|
||||
describe('Packages', () => {
|
||||
test('should display all packages', (done) => {
|
||||
request(app)
|
||||
.get('/-/verdaccio/packages' )
|
||||
.get('/-/verdaccio/packages')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(res.body).toHaveLength(1);
|
||||
done();
|
||||
});
|
||||
@ -78,7 +77,7 @@ describe('endpoint web unit test', () => {
|
||||
.get('/-/verdaccio/package/readme/@scope/pk1-test')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_CHARSET)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(res.text).toMatch('<h1 id="test">test</h1>\n');
|
||||
done();
|
||||
});
|
||||
@ -90,7 +89,7 @@ describe('endpoint web unit test', () => {
|
||||
.get('/-/verdaccio/package/readme/@scope/404')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.TEXT_CHARSET)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(res.body.error).toMatch(API_ERROR.NO_PACKAGE);
|
||||
done();
|
||||
});
|
||||
@ -101,7 +100,7 @@ describe('endpoint web unit test', () => {
|
||||
.get('/-/verdaccio/sidebar/@scope/pk1-test')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
// console.log("-->", res);
|
||||
// expect(err).toBeNull();
|
||||
|
||||
@ -121,7 +120,7 @@ describe('endpoint web unit test', () => {
|
||||
.get('/-/verdaccio/sidebar/@scope/pk1-test?v=1.0.6')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
const sideBarInfo = res.body;
|
||||
const latestVersion = publishMetadata.versions[publishMetadata[DIST_TAGS].latest];
|
||||
|
||||
@ -138,7 +137,7 @@ describe('endpoint web unit test', () => {
|
||||
.get('/-/verdaccio/sidebar/@scope/404')
|
||||
.expect(HTTP_STATUS.NOT_FOUND)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.end(function() {
|
||||
.end(function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -148,19 +147,18 @@ describe('endpoint web unit test', () => {
|
||||
.get('/-/verdaccio/sidebar/@scope/pk1-test?v=0.0.0-not-found')
|
||||
.expect(HTTP_STATUS.NOT_FOUND)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.end(function() {
|
||||
.end(function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Search', () => {
|
||||
|
||||
test('should search pk1-test', (done) => {
|
||||
request(app)
|
||||
.get('/-/verdaccio/search/scope')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(res.body).toHaveLength(1);
|
||||
done();
|
||||
});
|
||||
@ -170,7 +168,7 @@ describe('endpoint web unit test', () => {
|
||||
request(app)
|
||||
.get('/-/verdaccio/search/@')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
// in a normal world, the output would be 1
|
||||
// https://github.com/verdaccio/verdaccio/issues/345
|
||||
// should fix this
|
||||
@ -183,7 +181,7 @@ describe('endpoint web unit test', () => {
|
||||
request(app)
|
||||
.get('/-/verdaccio/search/forbidden-place')
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
// this is expected since we are not logged
|
||||
// and forbidden-place is allow_access: 'nobody'
|
||||
expect(res.body).toHaveLength(0);
|
||||
@ -203,10 +201,10 @@ describe('endpoint web unit test', () => {
|
||||
.post('/-/verdaccio/login')
|
||||
.send({
|
||||
username: credentials.name,
|
||||
password: credentials.password
|
||||
password: credentials.password,
|
||||
})
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(err).toBeNull();
|
||||
expect(res.body.error).toBeUndefined();
|
||||
expect(res.body.token).toBeDefined();
|
||||
@ -219,13 +217,15 @@ describe('endpoint web unit test', () => {
|
||||
test('should fails on log unvalid user', (done) => {
|
||||
request(app)
|
||||
.post('/-/verdaccio/login')
|
||||
.send(JSON.stringify({
|
||||
username: 'fake',
|
||||
password: 'fake'
|
||||
}))
|
||||
.send(
|
||||
JSON.stringify({
|
||||
username: 'fake',
|
||||
password: 'fake',
|
||||
})
|
||||
)
|
||||
// FIXME: there should be 401
|
||||
.expect(HTTP_STATUS.OK)
|
||||
.end(function(err, res) {
|
||||
.end(function (err, res) {
|
||||
expect(res.body.error).toMatch(/bad username\/password, access denied/);
|
||||
done();
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ import assert from 'assert';
|
||||
import UrlNode from 'url';
|
||||
import _ from 'lodash';
|
||||
import { ErrorCode, isObject, getLatestVersion, tagVersion, validateName } from '@verdaccio/utils';
|
||||
import {API_ERROR, DIST_TAGS, HTTP_STATUS, STORAGE, SUPPORT_ERRORS, USERS} from '@verdaccio/dev-commons';
|
||||
import { API_ERROR, DIST_TAGS, HTTP_STATUS, STORAGE, SUPPORT_ERRORS, USERS } from '@verdaccio/dev-commons';
|
||||
import { createTarballHash } from '@verdaccio/utils';
|
||||
import { loadPlugin } from '@verdaccio/loaders';
|
||||
import LocalDatabase from '@verdaccio/local-storage';
|
||||
@ -24,19 +24,20 @@ import {
|
||||
Author,
|
||||
CallbackAction,
|
||||
onSearchPackage,
|
||||
onEndSearchPackage, StorageUpdateCallback,
|
||||
onEndSearchPackage,
|
||||
StorageUpdateCallback,
|
||||
} from '@verdaccio/types';
|
||||
import { IStorage, StringValue } from '@verdaccio/dev-types';
|
||||
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
|
||||
import {
|
||||
prepareSearchPackage ,
|
||||
prepareSearchPackage,
|
||||
generatePackageTemplate,
|
||||
normalizePackage,
|
||||
generateRevision,
|
||||
getLatestReadme,
|
||||
cleanUpReadme,
|
||||
normalizeContributors
|
||||
normalizeContributors,
|
||||
} from './storage-utils';
|
||||
|
||||
/**
|
||||
@ -60,7 +61,7 @@ class LocalStorage implements IStorage {
|
||||
return callback(ErrorCode.getNotFound('this package cannot be added'));
|
||||
}
|
||||
|
||||
storage.createPackage(name, generatePackageTemplate(name), err => {
|
||||
storage.createPackage(name, generatePackageTemplate(name), (err) => {
|
||||
// FIXME: it will be fixed here https://github.com/verdaccio/verdaccio/pull/1360
|
||||
// @ts-ignore
|
||||
if (_.isNull(err) === false && (err.code === STORAGE.FILE_EXIST_ERROR || err.code === HTTP_STATUS.CONFLICT)) {
|
||||
@ -200,7 +201,7 @@ class LocalStorage implements IStorage {
|
||||
|
||||
if (change) {
|
||||
this.logger.debug({ name }, 'updating package @{name} info');
|
||||
this._writePackage(name, packageLocalJson, function(err): void {
|
||||
this._writePackage(name, packageLocalJson, function (err): void {
|
||||
callback(err, packageLocalJson);
|
||||
});
|
||||
} else {
|
||||
@ -337,11 +338,11 @@ class LocalStorage implements IStorage {
|
||||
*/
|
||||
public changePackage(name: string, incomingPkg: Package, revision: string | void, callback: Callback): void {
|
||||
if (!isObject(incomingPkg.versions) || !isObject(incomingPkg[DIST_TAGS])) {
|
||||
this.logger.debug({name}, `changePackage bad data for @{name}`);
|
||||
this.logger.debug({ name }, `changePackage bad data for @{name}`);
|
||||
return callback(ErrorCode.getBadData());
|
||||
}
|
||||
|
||||
this.logger.debug({name}, `changePackage udapting package for @{name}`);
|
||||
this.logger.debug({ name }, `changePackage udapting package for @{name}`);
|
||||
this._updatePackage(
|
||||
name,
|
||||
(localData: Package, cb: CallbackAction): void => {
|
||||
@ -378,7 +379,7 @@ class LocalStorage implements IStorage {
|
||||
localData[DIST_TAGS] = incomingPkg[DIST_TAGS];
|
||||
cb(null);
|
||||
},
|
||||
function(err): void {
|
||||
function (err): void {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@ -434,10 +435,10 @@ class LocalStorage implements IStorage {
|
||||
const _transform = uploadStream._transform;
|
||||
const storage = this._getLocalStorage(name);
|
||||
|
||||
uploadStream.abort = function(): void {};
|
||||
uploadStream.done = function(): void {};
|
||||
uploadStream.abort = function (): void {};
|
||||
uploadStream.done = function (): void {};
|
||||
|
||||
uploadStream._transform = function(data, ...args): void {
|
||||
uploadStream._transform = function (data, ...args): void {
|
||||
shaOneHash.update(data);
|
||||
// measure the length for validation reasons
|
||||
length += data.length;
|
||||
@ -463,15 +464,15 @@ class LocalStorage implements IStorage {
|
||||
|
||||
const writeStream: IUploadTarball = storage.writeTarball(filename);
|
||||
|
||||
writeStream.on('error', err => {
|
||||
writeStream.on('error', (err) => {
|
||||
// @ts-ignore
|
||||
if (err.code === STORAGE.FILE_EXIST_ERROR || err.code === HTTP_STATUS.CONFLICT) {
|
||||
uploadStream.emit('error', ErrorCode.getConflict());
|
||||
uploadStream.abort();
|
||||
// @ts-ignore
|
||||
// @ts-ignore
|
||||
} else if (err.code === STORAGE.NO_SUCH_FILE_ERROR || err.code === HTTP_STATUS.NOT_FOUND) {
|
||||
// check if package exists to throw an appropriate message
|
||||
this.getPackageMetadata(name, function(_err: VerdaccioError, _res: Package): void {
|
||||
this.getPackageMetadata(name, function (_err: VerdaccioError, _res: Package): void {
|
||||
if (_err) {
|
||||
uploadStream.emit('error', _err);
|
||||
} else {
|
||||
@ -483,7 +484,7 @@ class LocalStorage implements IStorage {
|
||||
}
|
||||
});
|
||||
|
||||
writeStream.on('open', function(): void {
|
||||
writeStream.on('open', function (): void {
|
||||
// re-emitting open because it's handled in storage.js
|
||||
uploadStream.emit('open');
|
||||
});
|
||||
@ -497,7 +498,7 @@ class LocalStorage implements IStorage {
|
||||
};
|
||||
cb(null);
|
||||
},
|
||||
function(err): void {
|
||||
function (err): void {
|
||||
if (err) {
|
||||
uploadStream.emit('error', err);
|
||||
} else {
|
||||
@ -507,11 +508,11 @@ class LocalStorage implements IStorage {
|
||||
);
|
||||
});
|
||||
|
||||
uploadStream.abort = function(): void {
|
||||
uploadStream.abort = function (): void {
|
||||
writeStream.abort();
|
||||
};
|
||||
|
||||
uploadStream.done = function(): void {
|
||||
uploadStream.done = function (): void {
|
||||
if (!length) {
|
||||
uploadStream.emit('error', ErrorCode.getBadData('refusing to accept zero-length file'));
|
||||
writeStream.abort();
|
||||
@ -569,13 +570,13 @@ class LocalStorage implements IStorage {
|
||||
const readTarballStream = storage.readTarball(filename);
|
||||
const e404 = ErrorCode.getNotFound;
|
||||
|
||||
stream.abort = function(): void {
|
||||
stream.abort = function (): void {
|
||||
if (_.isNil(readTarballStream) === false) {
|
||||
readTarballStream.abort();
|
||||
}
|
||||
};
|
||||
|
||||
readTarballStream.on('error', function(err) {
|
||||
readTarballStream.on('error', function (err) {
|
||||
// @ts-ignore
|
||||
if (err.code === STORAGE.NO_SUCH_FILE_ERROR || err.code === HTTP_STATUS.NOT_FOUND) {
|
||||
stream.emit('error', e404('no such file available'));
|
||||
@ -584,11 +585,11 @@ class LocalStorage implements IStorage {
|
||||
}
|
||||
});
|
||||
|
||||
readTarballStream.on('content-length', function(content): void {
|
||||
readTarballStream.on('content-length', function (content): void {
|
||||
stream.emit('content-length', content);
|
||||
});
|
||||
|
||||
readTarballStream.on('open', function(): void {
|
||||
readTarballStream.on('open', function (): void {
|
||||
// re-emitting open because it's handled in storage.js
|
||||
stream.emit('open');
|
||||
readTarballStream.pipe(stream);
|
||||
@ -786,21 +787,21 @@ class LocalStorage implements IStorage {
|
||||
}
|
||||
|
||||
private _deleteAttachments(storage: any, attachments: string[], callback: Callback): void {
|
||||
this.logger.debug({l: attachments.length }, `[storage/_deleteAttachments] delete attachments total: @{l}`);
|
||||
const unlinkNext = function(cb): void {
|
||||
this.logger.debug({ l: attachments.length }, `[storage/_deleteAttachments] delete attachments total: @{l}`);
|
||||
const unlinkNext = function (cb): void {
|
||||
if (_.isEmpty(attachments)) {
|
||||
return cb();
|
||||
}
|
||||
|
||||
const attachment = attachments.shift();
|
||||
storage.deletePackage(attachment, function(): void {
|
||||
storage.deletePackage(attachment, function (): void {
|
||||
unlinkNext(cb);
|
||||
});
|
||||
};
|
||||
|
||||
unlinkNext(function(): void {
|
||||
unlinkNext(function (): void {
|
||||
// try to unlink the directory, but ignore errors because it can fail
|
||||
storage.removePackage(function(err): void {
|
||||
storage.removePackage(function (err): void {
|
||||
callback(err);
|
||||
});
|
||||
});
|
||||
@ -849,10 +850,14 @@ class LocalStorage implements IStorage {
|
||||
logger: this.logger,
|
||||
};
|
||||
|
||||
const plugins: IPluginStorage<Config>[] = loadPlugin<IPluginStorage<Config>>(this.config, this.config.store, plugin_params, (plugin): IPluginStorage<Config> => {
|
||||
return plugin.getPackageStorage;
|
||||
});
|
||||
|
||||
const plugins: IPluginStorage<Config>[] = loadPlugin<IPluginStorage<Config>>(
|
||||
this.config,
|
||||
this.config.store,
|
||||
plugin_params,
|
||||
(plugin): IPluginStorage<Config> => {
|
||||
return plugin.getPackageStorage;
|
||||
}
|
||||
);
|
||||
|
||||
return _.head(plugins);
|
||||
}
|
||||
|
@ -103,6 +103,4 @@ class Search implements IWebSearch {
|
||||
|
||||
const SearchInstance = new Search();
|
||||
|
||||
export {
|
||||
SearchInstance
|
||||
}
|
||||
export { SearchInstance };
|
||||
|
@ -29,15 +29,13 @@ export function generatePackageTemplate(name: string): Package {
|
||||
export function normalizePackage(pkg: Package): Package {
|
||||
const pkgProperties = ['versions', 'dist-tags', '_distfiles', '_attachments', '_uplinks', 'time'];
|
||||
|
||||
pkgProperties.forEach(
|
||||
(key): void => {
|
||||
const pkgProp = pkg[key];
|
||||
pkgProperties.forEach((key): void => {
|
||||
const pkgProp = pkg[key];
|
||||
|
||||
if (_.isNil(pkgProp) || isObject(pkgProp) === false) {
|
||||
pkg[key] = {};
|
||||
}
|
||||
if (_.isNil(pkgProp) || isObject(pkgProp) === false) {
|
||||
pkg[key] = {};
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (_.isString(pkg._rev) === false) {
|
||||
pkg._rev = STORAGE.DEFAULT_REVISION;
|
||||
@ -71,7 +69,7 @@ export function getLatestReadme(pkg: Package): string {
|
||||
|
||||
// In case of empty readme - trying to get ANY readme in the following order: 'next','beta','alpha','test','dev','canary'
|
||||
const readmeDistTagsPriority = ['next', 'beta', 'alpha', 'test', 'dev', 'canary'];
|
||||
readmeDistTagsPriority.forEach(function(tag): string | void {
|
||||
readmeDistTagsPriority.forEach(function (tag): string | void {
|
||||
if (readme) {
|
||||
return readme;
|
||||
}
|
||||
@ -131,80 +129,62 @@ export function cleanUpLinksRef(keepUpLinkData: boolean, result: Package): Packa
|
||||
* @param {*} localStorage
|
||||
*/
|
||||
export function checkPackageLocal(name: string, localStorage: IStorage): Promise<any> {
|
||||
return new Promise(
|
||||
(resolve, reject): void => {
|
||||
localStorage.getPackageMetadata(
|
||||
name,
|
||||
(err, results): void => {
|
||||
if (!_.isNil(err) && err.status !== HTTP_STATUS.NOT_FOUND) {
|
||||
return reject(err);
|
||||
}
|
||||
if (results) {
|
||||
return reject(ErrorCode.getConflict(API_ERROR.PACKAGE_EXIST));
|
||||
}
|
||||
return resolve();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
return new Promise((resolve, reject): void => {
|
||||
localStorage.getPackageMetadata(name, (err, results): void => {
|
||||
if (!_.isNil(err) && err.status !== HTTP_STATUS.NOT_FOUND) {
|
||||
return reject(err);
|
||||
}
|
||||
if (results) {
|
||||
return reject(ErrorCode.getConflict(API_ERROR.PACKAGE_EXIST));
|
||||
}
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function publishPackage(name: string, metadata: any, localStorage: IStorage): Promise<any> {
|
||||
return new Promise(
|
||||
(resolve, reject): void => {
|
||||
localStorage.addPackage(
|
||||
name,
|
||||
metadata,
|
||||
(err, latest): void => {
|
||||
if (!_.isNull(err)) {
|
||||
return reject(err);
|
||||
} else if (!_.isUndefined(latest)) {
|
||||
SearchInstance.add(latest);
|
||||
}
|
||||
return resolve();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
return new Promise((resolve, reject): void => {
|
||||
localStorage.addPackage(name, metadata, (err, latest): void => {
|
||||
if (!_.isNull(err)) {
|
||||
return reject(err);
|
||||
} else if (!_.isUndefined(latest)) {
|
||||
SearchInstance.add(latest);
|
||||
}
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function checkPackageRemote(name: string, isAllowPublishOffline: boolean, syncMetadata: Function): Promise<any> {
|
||||
return new Promise(
|
||||
(resolve, reject): void => {
|
||||
syncMetadata(
|
||||
name,
|
||||
null,
|
||||
{},
|
||||
(err, packageJsonLocal, upLinksErrors): void => {
|
||||
// something weird
|
||||
if (err && err.status !== HTTP_STATUS.NOT_FOUND) {
|
||||
return reject(err);
|
||||
}
|
||||
return new Promise((resolve, reject): void => {
|
||||
syncMetadata(name, null, {}, (err, packageJsonLocal, upLinksErrors): void => {
|
||||
// something weird
|
||||
if (err && err.status !== HTTP_STATUS.NOT_FOUND) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
// checking package exist already
|
||||
if (_.isNil(packageJsonLocal) === false) {
|
||||
return reject(ErrorCode.getConflict(API_ERROR.PACKAGE_EXIST));
|
||||
}
|
||||
// checking package exist already
|
||||
if (_.isNil(packageJsonLocal) === false) {
|
||||
return reject(ErrorCode.getConflict(API_ERROR.PACKAGE_EXIST));
|
||||
}
|
||||
|
||||
for (let errorItem = 0; errorItem < upLinksErrors.length; errorItem++) {
|
||||
// checking error
|
||||
// if uplink fails with a status other than 404, we report failure
|
||||
if (_.isNil(upLinksErrors[errorItem][0]) === false) {
|
||||
if (upLinksErrors[errorItem][0].status !== HTTP_STATUS.NOT_FOUND) {
|
||||
if (isAllowPublishOffline) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
return reject(ErrorCode.getServiceUnavailable(API_ERROR.UPLINK_OFFLINE_PUBLISH));
|
||||
}
|
||||
for (let errorItem = 0; errorItem < upLinksErrors.length; errorItem++) {
|
||||
// checking error
|
||||
// if uplink fails with a status other than 404, we report failure
|
||||
if (_.isNil(upLinksErrors[errorItem][0]) === false) {
|
||||
if (upLinksErrors[errorItem][0].status !== HTTP_STATUS.NOT_FOUND) {
|
||||
if (isAllowPublishOffline) {
|
||||
return resolve();
|
||||
}
|
||||
}
|
||||
|
||||
return resolve();
|
||||
return reject(ErrorCode.getServiceUnavailable(API_ERROR.UPLINK_OFFLINE_PUBLISH));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function mergeUplinkTimeIntoLocal(localMetadata: Package, remoteMetadata: Package): any {
|
||||
|
@ -5,31 +5,18 @@ import _ from 'lodash';
|
||||
import { ProxyStorage } from '@verdaccio/proxy';
|
||||
import { API_ERROR, HTTP_STATUS, DIST_TAGS } from '@verdaccio/dev-commons';
|
||||
import { ReadTarball } from '@verdaccio/streams';
|
||||
import {
|
||||
ErrorCode,
|
||||
normalizeDistTags,
|
||||
validateMetadata,
|
||||
isObject,
|
||||
hasProxyTo,
|
||||
} from '@verdaccio/utils';
|
||||
import { ErrorCode, normalizeDistTags, validateMetadata, isObject, hasProxyTo } from '@verdaccio/utils';
|
||||
import { setupUpLinks, updateVersionsHiddenUpLink } from '@verdaccio/proxy';
|
||||
import { IReadTarball, IUploadTarball, Versions, Package, Config, MergeTags, Version, DistFile, Callback, Logger } from '@verdaccio/types';
|
||||
import { IStorage, IProxy, IStorageHandler, ProxyList, StringValue, IGetPackageOptions, ISyncUplinks, IPluginFilters } from '@verdaccio/dev-types';
|
||||
import { GenericBody, TokenFilter, Token } from '@verdaccio/types';
|
||||
import { logger } from '@verdaccio/logger';
|
||||
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
import { SearchInstance }from './search';
|
||||
import { SearchInstance } from './search';
|
||||
|
||||
import { LocalStorage } from './local-storage';
|
||||
import { mergeVersions } from './metadata-utils';
|
||||
import {
|
||||
checkPackageLocal,
|
||||
publishPackage,
|
||||
checkPackageRemote,
|
||||
cleanUpLinksRef,
|
||||
mergeUplinkTimeIntoLocal,
|
||||
generatePackageTemplate
|
||||
} from './storage-utils';
|
||||
import { checkPackageLocal, publishPackage, checkPackageRemote, cleanUpLinksRef, mergeUplinkTimeIntoLocal, generatePackageTemplate } from './storage-utils';
|
||||
|
||||
class Storage implements IStorageHandler {
|
||||
public localStorage: IStorage;
|
||||
@ -41,7 +28,7 @@ class Storage implements IStorageHandler {
|
||||
public constructor(config: Config) {
|
||||
this.config = config;
|
||||
this.uplinks = setupUpLinks(config);
|
||||
this.logger = logger.child({module: 'storage'});
|
||||
this.logger = logger.child({ module: 'storage' });
|
||||
this.filters = [];
|
||||
// @ts-ignore
|
||||
this.localStorage = null;
|
||||
@ -152,7 +139,7 @@ class Storage implements IStorageHandler {
|
||||
*/
|
||||
public getTarball(name: string, filename: string): IReadTarball {
|
||||
const readStream = new ReadTarball({});
|
||||
readStream.abort = function() {};
|
||||
readStream.abort = function () {};
|
||||
|
||||
const self = this;
|
||||
|
||||
@ -163,48 +150,37 @@ class Storage implements IStorageHandler {
|
||||
// flow: should be IReadTarball
|
||||
let localStream: any = self.localStorage.getTarball(name, filename);
|
||||
let isOpen = false;
|
||||
localStream.on(
|
||||
'error',
|
||||
(err): any => {
|
||||
if (isOpen || err.status !== HTTP_STATUS.NOT_FOUND) {
|
||||
return readStream.emit('error', err);
|
||||
}
|
||||
|
||||
// local reported 404
|
||||
const err404 = err;
|
||||
localStream.abort();
|
||||
localStream = null; // we force for garbage collector
|
||||
self.localStorage.getPackageMetadata(
|
||||
name,
|
||||
(err, info: Package): void => {
|
||||
if (_.isNil(err) && info._distfiles && _.isNil(info._distfiles[filename]) === false) {
|
||||
// information about this file exists locally
|
||||
serveFile(info._distfiles[filename]);
|
||||
} else {
|
||||
// we know nothing about this file, trying to get information elsewhere
|
||||
self._syncUplinksMetadata(
|
||||
name,
|
||||
info,
|
||||
{},
|
||||
(err, info: Package): any => {
|
||||
if (_.isNil(err) === false) {
|
||||
return readStream.emit('error', err);
|
||||
}
|
||||
if (_.isNil(info._distfiles) || _.isNil(info._distfiles[filename])) {
|
||||
return readStream.emit('error', err404);
|
||||
}
|
||||
serveFile(info._distfiles[filename]);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
localStream.on('error', (err): any => {
|
||||
if (isOpen || err.status !== HTTP_STATUS.NOT_FOUND) {
|
||||
return readStream.emit('error', err);
|
||||
}
|
||||
);
|
||||
localStream.on('content-length', function(v): void {
|
||||
|
||||
// local reported 404
|
||||
const err404 = err;
|
||||
localStream.abort();
|
||||
localStream = null; // we force for garbage collector
|
||||
self.localStorage.getPackageMetadata(name, (err, info: Package): void => {
|
||||
if (_.isNil(err) && info._distfiles && _.isNil(info._distfiles[filename]) === false) {
|
||||
// information about this file exists locally
|
||||
serveFile(info._distfiles[filename]);
|
||||
} else {
|
||||
// we know nothing about this file, trying to get information elsewhere
|
||||
self._syncUplinksMetadata(name, info, {}, (err, info: Package): any => {
|
||||
if (_.isNil(err) === false) {
|
||||
return readStream.emit('error', err);
|
||||
}
|
||||
if (_.isNil(info._distfiles) || _.isNil(info._distfiles[filename])) {
|
||||
return readStream.emit('error', err404);
|
||||
}
|
||||
serveFile(info._distfiles[filename]);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
localStream.on('content-length', function (v): void {
|
||||
readStream.emit('content-length', v);
|
||||
});
|
||||
localStream.on('open', function(): void {
|
||||
localStream.on('open', function (): void {
|
||||
isOpen = true;
|
||||
localStream.pipe(readStream);
|
||||
});
|
||||
@ -239,24 +215,24 @@ class Storage implements IStorageHandler {
|
||||
savestream = self.localStorage.addTarball(name, filename);
|
||||
}
|
||||
|
||||
let on_open = function(): void {
|
||||
let on_open = function (): void {
|
||||
// prevent it from being called twice
|
||||
on_open = function() {};
|
||||
on_open = function () {};
|
||||
const rstream2 = uplink.fetchTarball(file.url);
|
||||
rstream2.on('error', function(err): void {
|
||||
rstream2.on('error', function (err): void {
|
||||
if (savestream) {
|
||||
savestream.abort();
|
||||
}
|
||||
savestream = null;
|
||||
readStream.emit('error', err);
|
||||
});
|
||||
rstream2.on('end', function(): void {
|
||||
rstream2.on('end', function (): void {
|
||||
if (savestream) {
|
||||
savestream.done();
|
||||
}
|
||||
});
|
||||
|
||||
rstream2.on('content-length', function(v): void {
|
||||
rstream2.on('content-length', function (v): void {
|
||||
readStream.emit('content-length', v);
|
||||
if (savestream) {
|
||||
savestream.emit('content-length', v);
|
||||
@ -269,11 +245,11 @@ class Storage implements IStorageHandler {
|
||||
};
|
||||
|
||||
if (savestream) {
|
||||
savestream.on('open', function(): void {
|
||||
savestream.on('open', function (): void {
|
||||
on_open();
|
||||
});
|
||||
|
||||
savestream.on('error', function(err): void {
|
||||
savestream.on('error', function (err): void {
|
||||
self.logger.warn({ err: err, fileName: file }, 'error saving file @{fileName}: @{err.message}\n@{err.stack}');
|
||||
if (savestream) {
|
||||
savestream.abort();
|
||||
@ -301,32 +277,29 @@ class Storage implements IStorageHandler {
|
||||
* @property {function} options.callback Callback for receive data
|
||||
*/
|
||||
public getPackage(options: IGetPackageOptions): void {
|
||||
this.localStorage.getPackageMetadata(
|
||||
options.name,
|
||||
(err, data): void => {
|
||||
if (err && (!err.status || err.status >= HTTP_STATUS.INTERNAL_ERROR)) {
|
||||
// report internal errors right away
|
||||
this.localStorage.getPackageMetadata(options.name, (err, data): void => {
|
||||
if (err && (!err.status || err.status >= HTTP_STATUS.INTERNAL_ERROR)) {
|
||||
// report internal errors right away
|
||||
return options.callback(err);
|
||||
}
|
||||
|
||||
this._syncUplinksMetadata(options.name, data, { req: options.req, uplinksLook: options.uplinksLook }, function getPackageSynUpLinksCallback(
|
||||
err,
|
||||
result: Package,
|
||||
uplinkErrors
|
||||
): void {
|
||||
if (err) {
|
||||
return options.callback(err);
|
||||
}
|
||||
|
||||
this._syncUplinksMetadata(options.name, data, { req: options.req, uplinksLook: options.uplinksLook }, function getPackageSynUpLinksCallback(
|
||||
err,
|
||||
result: Package,
|
||||
uplinkErrors
|
||||
): void {
|
||||
if (err) {
|
||||
return options.callback(err);
|
||||
}
|
||||
normalizeDistTags(cleanUpLinksRef(options.keepUpLinkData, result));
|
||||
|
||||
normalizeDistTags(cleanUpLinksRef(options.keepUpLinkData, result));
|
||||
// npm can throw if this field doesn't exist
|
||||
result._attachments = {};
|
||||
|
||||
// npm can throw if this field doesn't exist
|
||||
result._attachments = {};
|
||||
|
||||
options.callback(null, result, uplinkErrors);
|
||||
});
|
||||
}
|
||||
);
|
||||
options.callback(null, result, uplinkErrors);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -348,7 +321,7 @@ class Storage implements IStorageHandler {
|
||||
|
||||
async.eachSeries(
|
||||
Object.keys(this.uplinks),
|
||||
function(up_name, cb): void {
|
||||
function (up_name, cb): void {
|
||||
// shortcut: if `local=1` is supplied, don't call uplinks
|
||||
if (options.req.query.local !== undefined) {
|
||||
return cb();
|
||||
@ -356,40 +329,34 @@ class Storage implements IStorageHandler {
|
||||
// search by keyword for each uplink
|
||||
const lstream: IUploadTarball = self.uplinks[up_name].search(options);
|
||||
// join streams
|
||||
lstream.pipe(
|
||||
stream,
|
||||
{ end: false }
|
||||
);
|
||||
lstream.on('error', function(err): void {
|
||||
lstream.pipe(stream, { end: false });
|
||||
lstream.on('error', function (err): void {
|
||||
self.logger.error({ err: err }, 'uplink error: @{err.message}');
|
||||
cb();
|
||||
cb = function(): void {};
|
||||
cb = function (): void {};
|
||||
});
|
||||
lstream.on('end', function(): void {
|
||||
lstream.on('end', function (): void {
|
||||
cb();
|
||||
cb = function(): void {};
|
||||
cb = function (): void {};
|
||||
});
|
||||
|
||||
stream.abort = function(): void {
|
||||
stream.abort = function (): void {
|
||||
if (lstream.abort) {
|
||||
lstream.abort();
|
||||
}
|
||||
cb();
|
||||
cb = function(): void {};
|
||||
cb = function (): void {};
|
||||
};
|
||||
},
|
||||
// executed after all series
|
||||
function(): void {
|
||||
function (): void {
|
||||
// attach a local search results
|
||||
const lstream: IReadTarball = self.localStorage.search(startkey, options);
|
||||
stream.abort = function(): void {
|
||||
stream.abort = function (): void {
|
||||
lstream.abort();
|
||||
};
|
||||
lstream.pipe(
|
||||
stream,
|
||||
{ end: true }
|
||||
);
|
||||
lstream.on('error', function(err: VerdaccioError): void {
|
||||
lstream.pipe(stream, { end: true });
|
||||
lstream.on('error', function (err: VerdaccioError): void {
|
||||
self.logger.error({ err: err }, 'search error: @{err.message}');
|
||||
stream.end();
|
||||
});
|
||||
@ -405,49 +372,47 @@ class Storage implements IStorageHandler {
|
||||
*/
|
||||
public getLocalDatabase(callback: Callback): void {
|
||||
const self = this;
|
||||
this.localStorage.storagePlugin.get(
|
||||
(err, locals): void => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
}
|
||||
|
||||
const packages: Version[] = [];
|
||||
const getPackage = function(itemPkg): void {
|
||||
self.localStorage.getPackageMetadata(locals[itemPkg], function(err, pkgMetadata: Package): void {
|
||||
if (_.isNil(err)) {
|
||||
const latest = pkgMetadata[DIST_TAGS].latest;
|
||||
if (latest && pkgMetadata.versions[latest]) {
|
||||
const version: Version = pkgMetadata.versions[latest];
|
||||
const timeList = pkgMetadata.time as GenericBody;
|
||||
const time = timeList[latest];
|
||||
// @ts-ignore
|
||||
version.time = time;
|
||||
|
||||
// Add for stars api
|
||||
// @ts-ignore
|
||||
version.users = pkgMetadata.users;
|
||||
|
||||
packages.push(version);
|
||||
} else {
|
||||
self.logger.warn({ package: locals[itemPkg] }, 'package @{package} does not have a "latest" tag?');
|
||||
}
|
||||
}
|
||||
|
||||
if (itemPkg >= locals.length - 1) {
|
||||
callback(null, packages);
|
||||
} else {
|
||||
getPackage(itemPkg + 1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (locals.length) {
|
||||
getPackage(0);
|
||||
} else {
|
||||
callback(null, []);
|
||||
}
|
||||
this.localStorage.storagePlugin.get((err, locals): void => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
}
|
||||
);
|
||||
|
||||
const packages: Version[] = [];
|
||||
const getPackage = function (itemPkg): void {
|
||||
self.localStorage.getPackageMetadata(locals[itemPkg], function (err, pkgMetadata: Package): void {
|
||||
if (_.isNil(err)) {
|
||||
const latest = pkgMetadata[DIST_TAGS].latest;
|
||||
if (latest && pkgMetadata.versions[latest]) {
|
||||
const version: Version = pkgMetadata.versions[latest];
|
||||
const timeList = pkgMetadata.time as GenericBody;
|
||||
const time = timeList[latest];
|
||||
// @ts-ignore
|
||||
version.time = time;
|
||||
|
||||
// Add for stars api
|
||||
// @ts-ignore
|
||||
version.users = pkgMetadata.users;
|
||||
|
||||
packages.push(version);
|
||||
} else {
|
||||
self.logger.warn({ package: locals[itemPkg] }, 'package @{package} does not have a "latest" tag?');
|
||||
}
|
||||
}
|
||||
|
||||
if (itemPkg >= locals.length - 1) {
|
||||
callback(null, packages);
|
||||
} else {
|
||||
getPackage(itemPkg + 1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (locals.length) {
|
||||
getPackage(0);
|
||||
} else {
|
||||
callback(null, []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -488,59 +453,55 @@ class Storage implements IStorageHandler {
|
||||
_options.etag = upLinkMeta.etag;
|
||||
}
|
||||
|
||||
upLink.getRemoteMetadata(
|
||||
name,
|
||||
_options,
|
||||
(err, upLinkResponse, eTag): void => {
|
||||
if (err && err.remoteStatus === 304) {
|
||||
upLinkMeta.fetched = Date.now();
|
||||
}
|
||||
|
||||
if (err || !upLinkResponse) {
|
||||
return cb(null, [err || ErrorCode.getInternalError('no data')]);
|
||||
}
|
||||
|
||||
try {
|
||||
validateMetadata(upLinkResponse, name);
|
||||
} catch (err) {
|
||||
self.logger.error(
|
||||
{
|
||||
sub: 'out',
|
||||
err: err,
|
||||
},
|
||||
'package.json validating error @{!err.message}\n@{err.stack}'
|
||||
);
|
||||
return cb(null, [err]);
|
||||
}
|
||||
|
||||
packageInfo._uplinks[upLink.upname] = {
|
||||
etag: eTag,
|
||||
fetched: Date.now(),
|
||||
};
|
||||
|
||||
packageInfo.time = mergeUplinkTimeIntoLocal(packageInfo, upLinkResponse);
|
||||
|
||||
updateVersionsHiddenUpLink(upLinkResponse.versions, upLink);
|
||||
|
||||
try {
|
||||
mergeVersions(packageInfo, upLinkResponse);
|
||||
} catch (err) {
|
||||
self.logger.error(
|
||||
{
|
||||
sub: 'out',
|
||||
err: err,
|
||||
},
|
||||
'package.json parsing error @{!err.message}\n@{err.stack}'
|
||||
);
|
||||
return cb(null, [err]);
|
||||
}
|
||||
|
||||
// if we got to this point, assume that the correct package exists
|
||||
// on the uplink
|
||||
found = true;
|
||||
cb();
|
||||
upLink.getRemoteMetadata(name, _options, (err, upLinkResponse, eTag): void => {
|
||||
if (err && err.remoteStatus === 304) {
|
||||
upLinkMeta.fetched = Date.now();
|
||||
}
|
||||
);
|
||||
|
||||
if (err || !upLinkResponse) {
|
||||
return cb(null, [err || ErrorCode.getInternalError('no data')]);
|
||||
}
|
||||
|
||||
try {
|
||||
validateMetadata(upLinkResponse, name);
|
||||
} catch (err) {
|
||||
self.logger.error(
|
||||
{
|
||||
sub: 'out',
|
||||
err: err,
|
||||
},
|
||||
'package.json validating error @{!err.message}\n@{err.stack}'
|
||||
);
|
||||
return cb(null, [err]);
|
||||
}
|
||||
|
||||
packageInfo._uplinks[upLink.upname] = {
|
||||
etag: eTag,
|
||||
fetched: Date.now(),
|
||||
};
|
||||
|
||||
packageInfo.time = mergeUplinkTimeIntoLocal(packageInfo, upLinkResponse);
|
||||
|
||||
updateVersionsHiddenUpLink(upLinkResponse.versions, upLink);
|
||||
|
||||
try {
|
||||
mergeVersions(packageInfo, upLinkResponse);
|
||||
} catch (err) {
|
||||
self.logger.error(
|
||||
{
|
||||
sub: 'out',
|
||||
err: err,
|
||||
},
|
||||
'package.json parsing error @{!err.message}\n@{err.stack}'
|
||||
);
|
||||
return cb(null, [err]);
|
||||
}
|
||||
|
||||
// if we got to this point, assume that the correct package exists
|
||||
// on the uplink
|
||||
found = true;
|
||||
cb();
|
||||
});
|
||||
},
|
||||
// @ts-ignore
|
||||
(err: Error, upLinksErrors: any): AsyncResultArrayCallback<unknown, Error> => {
|
||||
@ -620,4 +581,4 @@ class Storage implements IStorageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
export { Storage }
|
||||
export { Storage };
|
||||
|
@ -1,20 +1,19 @@
|
||||
import path from 'path';
|
||||
import rimRaf from 'rimraf';
|
||||
|
||||
import {Config as AppConfig } from '@verdaccio/config';
|
||||
import { Config as AppConfig } from '@verdaccio/config';
|
||||
// @ts-ignore
|
||||
import { logger, setup} from '@verdaccio/logger';
|
||||
import { logger, setup } from '@verdaccio/logger';
|
||||
import { configExample, generateNewVersion } from '@verdaccio/mock';
|
||||
import {IStorage} from '@verdaccio/dev-types';
|
||||
import { LocalStorage } from '../src/local-storage';
|
||||
|
||||
import { IStorage } from '@verdaccio/dev-types';
|
||||
|
||||
const readMetadata = (fileName = 'metadata') => readFile(`../fixtures/${fileName}`).toString();
|
||||
import {Config, MergeTags, Package} from '@verdaccio/types';
|
||||
import { API_ERROR, HTTP_STATUS, DIST_TAGS} from '@verdaccio/dev-commons';
|
||||
import { Config, MergeTags, Package } from '@verdaccio/types';
|
||||
import { API_ERROR, HTTP_STATUS, DIST_TAGS } from '@verdaccio/dev-commons';
|
||||
import { VerdaccioError } from '@verdaccio/commons-api';
|
||||
import {generatePackageTemplate} from '../src/storage-utils';
|
||||
import {readFile} from './fixtures/test.utils';
|
||||
import { LocalStorage } from '../src/local-storage';
|
||||
import { generatePackageTemplate } from '../src/storage-utils';
|
||||
import { readFile } from './fixtures/test.utils';
|
||||
|
||||
setup([]);
|
||||
|
||||
@ -26,16 +25,18 @@ describe('LocalStorage', () => {
|
||||
const tarballName2 = `${pkgName}-add-tarball-1.0.5.tgz`;
|
||||
|
||||
const getStorage = (LocalStorageClass = LocalStorage) => {
|
||||
const config: Config = new AppConfig(configExample({
|
||||
self_path: path.join('../partials/store')
|
||||
}));
|
||||
const config: Config = new AppConfig(
|
||||
configExample({
|
||||
self_path: path.join('../partials/store'),
|
||||
})
|
||||
);
|
||||
|
||||
return new LocalStorageClass(config, logger);
|
||||
}
|
||||
};
|
||||
|
||||
const getPackageMetadataFromStore = (pkgName: string): Promise<Package> => {
|
||||
return new Promise((resolve) => {
|
||||
storage.getPackageMetadata(pkgName, (err, data ) => {
|
||||
storage.getPackageMetadata(pkgName, (err, data) => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
@ -143,7 +144,7 @@ describe('LocalStorage', () => {
|
||||
await addNewVersion(pkgName, '3.0.0');
|
||||
const tags: MergeTags = {
|
||||
beta: '3.0.0',
|
||||
latest: '2.0.0'
|
||||
latest: '2.0.0',
|
||||
};
|
||||
|
||||
storage.mergeTags(pkgName, tags, async (err, data) => {
|
||||
@ -166,7 +167,7 @@ describe('LocalStorage', () => {
|
||||
await addNewVersion(pkgName, '2.0.0');
|
||||
await addNewVersion(pkgName, '3.0.0');
|
||||
const tags: MergeTags = {
|
||||
beta: '9999.0.0'
|
||||
beta: '9999.0.0',
|
||||
};
|
||||
|
||||
storage.mergeTags(pkgName, tags, async (err) => {
|
||||
@ -180,7 +181,7 @@ describe('LocalStorage', () => {
|
||||
test('should fails on mergeTags', async (done) => {
|
||||
const tags: MergeTags = {
|
||||
beta: '3.0.0',
|
||||
latest: '2.0.0'
|
||||
latest: '2.0.0',
|
||||
};
|
||||
|
||||
storage.mergeTags('not-found', tags, async (err) => {
|
||||
@ -215,7 +216,7 @@ describe('LocalStorage', () => {
|
||||
await addPackageToStore(pkgName, generatePackageTemplate(pkgName));
|
||||
await addNewVersion(pkgName, version);
|
||||
|
||||
storage.addVersion(pkgName, version, generateNewVersion(pkgName, version), '', err => {
|
||||
storage.addVersion(pkgName, version, generateNewVersion(pkgName, version), '', (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.statusCode).toEqual(HTTP_STATUS.CONFLICT);
|
||||
expect(err.message).toMatch(API_ERROR.PACKAGE_EXIST);
|
||||
@ -230,7 +231,7 @@ describe('LocalStorage', () => {
|
||||
const tarballName = `${pkgName}-${version}.tgz`;
|
||||
await addTarballToStore(pkgName, tarballName);
|
||||
|
||||
storage.addVersion(pkgName, version, generateNewVersion(pkgName, version, 'fake'), '', err => {
|
||||
storage.addVersion(pkgName, version, generateNewVersion(pkgName, version, 'fake'), '', (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.statusCode).toEqual(HTTP_STATUS.BAD_REQUEST);
|
||||
expect(err.message).toMatch(/shasum error/);
|
||||
@ -258,18 +259,18 @@ describe('LocalStorage', () => {
|
||||
const pkgName = 'add-update-versions-test-1';
|
||||
const version = '1.0.2';
|
||||
let _storage;
|
||||
beforeEach(done => {
|
||||
beforeEach((done) => {
|
||||
class MockLocalStorage extends LocalStorage {}
|
||||
// @ts-ignore
|
||||
MockLocalStorage.prototype._writePackage = jest.fn(LocalStorage.prototype._writePackage)
|
||||
MockLocalStorage.prototype._writePackage = jest.fn(LocalStorage.prototype._writePackage);
|
||||
_storage = getStorage(MockLocalStorage);
|
||||
rimRaf(path.join(configExample().storage, pkgName), async () => {
|
||||
await addPackageToStore(pkgName, generatePackageTemplate(pkgName));
|
||||
await addNewVersion(pkgName, '1.0.1');
|
||||
await addNewVersion(pkgName, version);
|
||||
done();
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
test('should update versions from external source', async (done) => {
|
||||
_storage.updateVersions(pkgName, metadata, (err, data) => {
|
||||
@ -290,22 +291,22 @@ describe('LocalStorage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('should not update if the metadata match', done => {
|
||||
_storage.updateVersions(pkgName, metadata, e => {
|
||||
expect(e).toBeNull()
|
||||
_storage.updateVersions(pkgName, metadata, err => {
|
||||
expect(err).toBeNull()
|
||||
test('should not update if the metadata match', (done) => {
|
||||
_storage.updateVersions(pkgName, metadata, (e) => {
|
||||
expect(e).toBeNull();
|
||||
_storage.updateVersions(pkgName, metadata, (err) => {
|
||||
expect(err).toBeNull();
|
||||
expect(_storage._writePackage).toHaveBeenCalledTimes(1);
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('LocalStorage::changePackage', () => {
|
||||
const pkgName = 'change-package';
|
||||
|
||||
test('should unpublish a version', async done => {
|
||||
test('should unpublish a version', async (done) => {
|
||||
await addPackageToStore(pkgName, generatePackageTemplate(pkgName));
|
||||
await addNewVersion(pkgName, '1.0.1');
|
||||
await addNewVersion(pkgName, '1.0.2');
|
||||
@ -315,7 +316,7 @@ describe('LocalStorage', () => {
|
||||
|
||||
storage.changePackage(pkgName, metadata, rev, (err) => {
|
||||
expect(err).toBeUndefined();
|
||||
storage.getPackageMetadata(pkgName, (err, data ) => {
|
||||
storage.getPackageMetadata(pkgName, (err, data) => {
|
||||
expect(err).toBeNull();
|
||||
expect(data.versions['1.0.1']).toBeDefined();
|
||||
expect(data.versions['1.0.2']).toBeUndefined();
|
||||
@ -327,9 +328,7 @@ describe('LocalStorage', () => {
|
||||
});
|
||||
|
||||
describe('LocalStorage::tarball operations', () => {
|
||||
|
||||
describe('LocalStorage::addTarball', () => {
|
||||
|
||||
test('should add a new tarball', (done) => {
|
||||
const tarballData = JSON.parse(readMetadata('addTarball'));
|
||||
const stream = storage.addTarball(pkgName, tarballName);
|
||||
@ -338,7 +337,7 @@ describe('LocalStorage', () => {
|
||||
expect(err).toBeNull();
|
||||
done();
|
||||
});
|
||||
stream.on('success', function() {
|
||||
stream.on('success', function () {
|
||||
done();
|
||||
});
|
||||
|
||||
@ -353,7 +352,7 @@ describe('LocalStorage', () => {
|
||||
expect(err).toBeNull();
|
||||
done();
|
||||
});
|
||||
stream.on('success', function() {
|
||||
stream.on('success', function () {
|
||||
done();
|
||||
});
|
||||
|
||||
@ -394,7 +393,7 @@ describe('LocalStorage', () => {
|
||||
|
||||
test('should fails on use invalid package name on add a new tarball', (done) => {
|
||||
const stream = storage.addTarball(pkgName, `${pkgName}-fails-add-tarball-1.0.4.tgz`);
|
||||
stream.on('error', function(err: VerdaccioError) {
|
||||
stream.on('error', function (err: VerdaccioError) {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.statusCode).toEqual(HTTP_STATUS.BAD_DATA);
|
||||
expect(err.message).toMatch(/refusing to accept zero-length file/);
|
||||
@ -407,7 +406,7 @@ describe('LocalStorage', () => {
|
||||
test('should fails on abort on add a new tarball', (done) => {
|
||||
const stream = storage.addTarball('__proto__', `${pkgName}-fails-add-tarball-1.0.4.tgz`);
|
||||
stream.abort();
|
||||
stream.on('error', function(err: VerdaccioError) {
|
||||
stream.on('error', function (err: VerdaccioError) {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.statusCode).toEqual(HTTP_STATUS.FORBIDDEN);
|
||||
expect(err.message).toMatch(/can't use this filename/);
|
||||
@ -416,10 +415,8 @@ describe('LocalStorage', () => {
|
||||
|
||||
stream.done();
|
||||
});
|
||||
|
||||
});
|
||||
describe('LocalStorage::removeTarball', () => {
|
||||
|
||||
test('should remove a tarball', (done) => {
|
||||
storage.removeTarball(pkgName, tarballName2, 'rev', (err, pkg) => {
|
||||
expect(err).toBeNull();
|
||||
@ -441,18 +438,18 @@ describe('LocalStorage', () => {
|
||||
describe('LocalStorage::getTarball', () => {
|
||||
test('should get a existing tarball', (done) => {
|
||||
const stream = storage.getTarball(pkgName, tarballName);
|
||||
stream.on('content-length', function(contentLength) {
|
||||
stream.on('content-length', function (contentLength) {
|
||||
expect(contentLength).toBe(279);
|
||||
done();
|
||||
});
|
||||
stream.on('open', function() {
|
||||
stream.on('open', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('should fails on get a tarball that does not exist', (done) => {
|
||||
const stream = storage.getTarball('fake', tarballName);
|
||||
stream.on('error', function(err: VerdaccioError) {
|
||||
stream.on('error', function (err: VerdaccioError) {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.statusCode).toEqual(HTTP_STATUS.NOT_FOUND);
|
||||
expect(err.message).toMatch(/no such file available/);
|
||||
@ -470,16 +467,15 @@ describe('LocalStorage', () => {
|
||||
expect(pkg.name).toEqual(pkgName);
|
||||
});
|
||||
|
||||
stream.on('error', function(err) {
|
||||
stream.on('error', function (err) {
|
||||
expect(err).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
|
||||
stream.on('end', function() {
|
||||
stream.on('end', function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@ -502,7 +498,7 @@ describe('LocalStorage', () => {
|
||||
|
||||
test('should fails with package not found', (done) => {
|
||||
const pkgName = 'npm_test_fake';
|
||||
storage.removePackage(pkgName, err => {
|
||||
storage.removePackage(pkgName, (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.message).toMatch(/no such package available/);
|
||||
done();
|
||||
@ -510,7 +506,7 @@ describe('LocalStorage', () => {
|
||||
});
|
||||
|
||||
test('should fails with @scoped package not found', (done) => {
|
||||
storage.removePackage(pkgNameScoped, err => {
|
||||
storage.removePackage(pkgNameScoped, (err) => {
|
||||
expect(err).not.toBeNull();
|
||||
expect(err.message).toMatch(API_ERROR.NO_PACKAGE);
|
||||
done();
|
||||
@ -518,5 +514,4 @@ describe('LocalStorage', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -2,74 +2,62 @@ import assert from 'assert';
|
||||
import { semverSort } from '@verdaccio/utils';
|
||||
import { setup } from '@verdaccio/logger';
|
||||
|
||||
import {mergeVersions} from '../src/metadata-utils';
|
||||
import { mergeVersions } from '../src/metadata-utils';
|
||||
|
||||
setup([]);
|
||||
|
||||
describe('Storage._merge_versions versions', () => {
|
||||
|
||||
test('simple', () => {
|
||||
let pkg = {
|
||||
'versions': {a: 1, b: 1, c: 1},
|
||||
versions: { a: 1, b: 1, c: 1 },
|
||||
'dist-tags': {},
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
mergeVersions(pkg, {versions: {a: 2, q: 2}});
|
||||
mergeVersions(pkg, { versions: { a: 2, q: 2 } });
|
||||
|
||||
assert.deepEqual(pkg, {
|
||||
'versions': {a: 1, b: 1, c: 1, q: 2},
|
||||
versions: { a: 1, b: 1, c: 1, q: 2 },
|
||||
'dist-tags': {},
|
||||
});
|
||||
});
|
||||
|
||||
test('dist-tags - compat', () => {
|
||||
let pkg = {
|
||||
'versions': {},
|
||||
'dist-tags': {q: '1.1.1', w: '2.2.2'},
|
||||
versions: {},
|
||||
'dist-tags': { q: '1.1.1', w: '2.2.2' },
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
mergeVersions(pkg, {'dist-tags': {q: '2.2.2', w: '3.3.3', t: '4.4.4'}});
|
||||
mergeVersions(pkg, { 'dist-tags': { q: '2.2.2', w: '3.3.3', t: '4.4.4' } });
|
||||
|
||||
assert.deepEqual(pkg, {
|
||||
'versions': {},
|
||||
'dist-tags': {q: '2.2.2', w: '3.3.3', t: '4.4.4'},
|
||||
versions: {},
|
||||
'dist-tags': { q: '2.2.2', w: '3.3.3', t: '4.4.4' },
|
||||
});
|
||||
});
|
||||
|
||||
test('dist-tags - staging', () => {
|
||||
|
||||
let pkg = {
|
||||
versions: {},
|
||||
// we've been locally publishing 1.1.x in preparation for the next
|
||||
// public release
|
||||
'dist-tags': {q:'1.1.10',w:'2.2.2'},
|
||||
'dist-tags': { q: '1.1.10', w: '2.2.2' },
|
||||
};
|
||||
// 1.1.2 is the latest public release, but we want to continue testing
|
||||
// against our local 1.1.10, which may end up published as 1.1.3 in the
|
||||
// future
|
||||
|
||||
// @ts-ignore
|
||||
mergeVersions(pkg, {'dist-tags':{q:'1.1.2',w:'3.3.3',t:'4.4.4'}})
|
||||
mergeVersions(pkg, { 'dist-tags': { q: '1.1.2', w: '3.3.3', t: '4.4.4' } });
|
||||
|
||||
assert.deepEqual(pkg, {
|
||||
versions: {},
|
||||
'dist-tags': {q:'1.1.10',w:'3.3.3',t:'4.4.4'},
|
||||
'dist-tags': { q: '1.1.10', w: '3.3.3', t: '4.4.4' },
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('semverSort', () => {
|
||||
|
||||
assert.deepEqual(semverSort(['1.2.3', '1.2', '1.2.3a', '1.2.3c', '1.2.3-b']),
|
||||
['1.2.3a',
|
||||
'1.2.3-b',
|
||||
'1.2.3c',
|
||||
'1.2.3']
|
||||
);
|
||||
|
||||
assert.deepEqual(semverSort(['1.2.3', '1.2', '1.2.3a', '1.2.3c', '1.2.3-b']), ['1.2.3a', '1.2.3-b', '1.2.3c', '1.2.3']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
@ -32,12 +32,12 @@ const packages = [
|
||||
];
|
||||
|
||||
describe('search', () => {
|
||||
beforeAll(async function() {
|
||||
beforeAll(async function () {
|
||||
const config = new Config(configExample());
|
||||
const storage = new Storage(config);
|
||||
await storage.init(config);
|
||||
SearchInstance.configureStorage(storage);
|
||||
packages.map(function(item) {
|
||||
packages.map(function (item) {
|
||||
// @ts-ignore
|
||||
SearchInstance.add(item);
|
||||
});
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {Package} from '@verdaccio/types';
|
||||
import { Package } from '@verdaccio/types';
|
||||
import { STORAGE, DIST_TAGS } from '@verdaccio/dev-commons';
|
||||
import {normalizePackage, mergeUplinkTimeIntoLocal} from '../src/storage-utils';
|
||||
import { normalizePackage, mergeUplinkTimeIntoLocal } from '../src/storage-utils';
|
||||
|
||||
import {readFile} from "./fixtures/test.utils";
|
||||
import { readFile } from './fixtures/test.utils';
|
||||
|
||||
describe('Storage Utils', () => {
|
||||
describe('normalizePackage', () => {
|
||||
@ -48,13 +48,13 @@ describe('Storage Utils', () => {
|
||||
|
||||
describe('mergeTime', () => {
|
||||
const vGroup1 = {
|
||||
"1.0.15": "2018-06-12T23:15:05.864Z",
|
||||
"1.0.16": "2018-06-12T23:17:46.578Z",
|
||||
"1.0.17": "2018-06-12T23:20:59.106Z"
|
||||
'1.0.15': '2018-06-12T23:15:05.864Z',
|
||||
'1.0.16': '2018-06-12T23:17:46.578Z',
|
||||
'1.0.17': '2018-06-12T23:20:59.106Z',
|
||||
};
|
||||
const vGroup2 = {
|
||||
"1.0.6": "2018-06-07T05:50:21.505Z",
|
||||
"1.0.7": "2018-06-12T20:35:07.621Z"
|
||||
'1.0.6': '2018-06-07T05:50:21.505Z',
|
||||
'1.0.7': '2018-06-12T20:35:07.621Z',
|
||||
};
|
||||
test('mergeTime basic', () => {
|
||||
const pkg1: Package = {
|
||||
@ -63,9 +63,9 @@ describe('Storage Utils', () => {
|
||||
_rev: '',
|
||||
_uplinks: {},
|
||||
time: {
|
||||
"modified": "2018-06-13T06:44:45.747Z",
|
||||
"created": "2018-06-07T05:50:21.505Z",
|
||||
...vGroup1
|
||||
modified: '2018-06-13T06:44:45.747Z',
|
||||
created: '2018-06-07T05:50:21.505Z',
|
||||
...vGroup1,
|
||||
},
|
||||
name: '',
|
||||
versions: {},
|
||||
@ -79,17 +79,16 @@ describe('Storage Utils', () => {
|
||||
_uplinks: {},
|
||||
name: '',
|
||||
time: {
|
||||
"modified": "2019-06-13T06:44:45.747Z",
|
||||
"created": "2019-06-07T05:50:21.505Z",
|
||||
...vGroup2
|
||||
modified: '2019-06-13T06:44:45.747Z',
|
||||
created: '2019-06-07T05:50:21.505Z',
|
||||
...vGroup2,
|
||||
},
|
||||
versions: {},
|
||||
[DIST_TAGS]: {},
|
||||
};
|
||||
|
||||
const mergedPkg = mergeUplinkTimeIntoLocal(pkg1, pkg2);
|
||||
expect(Object.keys(mergedPkg)).toEqual(["modified", "created",
|
||||
...Object.keys(vGroup1), ...Object.keys(vGroup2)]);
|
||||
expect(Object.keys(mergedPkg)).toEqual(['modified', 'created', ...Object.keys(vGroup1), ...Object.keys(vGroup2)]);
|
||||
});
|
||||
|
||||
test('mergeTime remote empty', () => {
|
||||
@ -100,9 +99,9 @@ describe('Storage Utils', () => {
|
||||
_uplinks: {},
|
||||
name: '',
|
||||
time: {
|
||||
"modified": "2018-06-13T06:44:45.747Z",
|
||||
"created": "2018-06-07T05:50:21.505Z",
|
||||
...vGroup1
|
||||
modified: '2018-06-13T06:44:45.747Z',
|
||||
created: '2018-06-07T05:50:21.505Z',
|
||||
...vGroup1,
|
||||
},
|
||||
versions: {},
|
||||
[DIST_TAGS]: {},
|
||||
@ -118,7 +117,7 @@ describe('Storage Utils', () => {
|
||||
[DIST_TAGS]: {},
|
||||
};
|
||||
const mergedPkg = mergeUplinkTimeIntoLocal(pkg1, pkg2);
|
||||
expect(Object.keys(mergedPkg)).toEqual(["modified", "created", ...Object.keys(vGroup1)]);
|
||||
expect(Object.keys(mergedPkg)).toEqual(['modified', 'created', ...Object.keys(vGroup1)]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -23,7 +23,7 @@ export const defaultNonLoggedUserRoles = [
|
||||
ROLES.$ANONYMOUS,
|
||||
// groups without '$' are going to be deprecated eventually
|
||||
ROLES.DEPRECATED_ALL,
|
||||
ROLES.DEPRECATED_ANONYMOUS
|
||||
ROLES.DEPRECATED_ANONYMOUS,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -65,14 +65,17 @@ export type ActionsAllowed = 'publish' | 'unpublish' | 'access';
|
||||
|
||||
export function allow_action(action: ActionsAllowed, logger): AllowAction {
|
||||
return function allowActionCallback(user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {
|
||||
logger.trace({remote: user.name}, `[auth/allow_action]: user: @{user.name}`);
|
||||
logger.trace({ remote: user.name }, `[auth/allow_action]: user: @{user.name}`);
|
||||
const { name, groups } = user;
|
||||
const groupAccess = pkg[action] as string[];
|
||||
const hasPermission = groupAccess.some(group => name === group || groups.includes(group));
|
||||
logger.trace({pkgName: pkg.name, hasPermission, remote: user.name, groupAccess}, `[auth/allow_action]: hasPermission? @{hasPermission} for user: @{user}`);
|
||||
const hasPermission = groupAccess.some((group) => name === group || groups.includes(group));
|
||||
logger.trace(
|
||||
{ pkgName: pkg.name, hasPermission, remote: user.name, groupAccess },
|
||||
`[auth/allow_action]: hasPermission? @{hasPermission} for user: @{user}`
|
||||
);
|
||||
|
||||
if (hasPermission) {
|
||||
logger.trace({remote: user.name}, `auth/allow_action: access granted to: @{user}`);
|
||||
logger.trace({ remote: user.name }, `auth/allow_action: access granted to: @{user}`);
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
@ -88,18 +91,18 @@ export function allow_action(action: ActionsAllowed, logger): AllowAction {
|
||||
*
|
||||
*/
|
||||
export function handleSpecialUnpublish(logger): any {
|
||||
return function(user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {
|
||||
return function (user: RemoteUser, pkg: AuthPackageAllow, callback: AllowActionCallback): void {
|
||||
const action = 'unpublish';
|
||||
// verify whether the unpublish prop has been defined
|
||||
const isUnpublishMissing: boolean = _.isNil(pkg[action]);
|
||||
const hasGroups: boolean = isUnpublishMissing ? false : ((pkg[action]) as string[]).length > 0;
|
||||
logger.trace({user: user.name, name: pkg.name, hasGroups}, `fallback unpublish for @{name} has groups: @{hasGroups} for @{user}`);
|
||||
const hasGroups: boolean = isUnpublishMissing ? false : (pkg[action] as string[]).length > 0;
|
||||
logger.trace({ user: user.name, name: pkg.name, hasGroups }, `fallback unpublish for @{name} has groups: @{hasGroups} for @{user}`);
|
||||
|
||||
if (isUnpublishMissing || hasGroups === false) {
|
||||
return callback(null, undefined);
|
||||
}
|
||||
|
||||
logger.trace({user: user.name, name: pkg.name, action, hasGroups}, `allow_action for @{action} for @{name} has groups: @{hasGroups} for @{user}`);
|
||||
logger.trace({ user: user.name, name: pkg.name, action, hasGroups }, `allow_action for @{action} for @{name} has groups: @{hasGroups} for @{user}`);
|
||||
return allow_action(action, logger)(user, pkg, callback);
|
||||
};
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import assert from 'assert';
|
||||
import _ from 'lodash';
|
||||
import minimatch from 'minimatch';
|
||||
|
||||
|
||||
import { PackageList, UpLinksConfList } from '@verdaccio/types';
|
||||
import { MatchedPackage, LegacyPackageList } from '@verdaccio/dev-types';
|
||||
import { ErrorCode } from './utils';
|
||||
@ -20,23 +19,21 @@ const BLACKLIST = {
|
||||
* @return {Array}
|
||||
*/
|
||||
export function normalizeUserList(groupsList: any): any {
|
||||
const result: any[] = [];
|
||||
if (_.isNil(groupsList)) {
|
||||
return result;
|
||||
}
|
||||
const result: any[] = [];
|
||||
if (_.isNil(groupsList)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// if it's a string, split it to array
|
||||
if (_.isString(groupsList)) {
|
||||
const groupsArray = groupsList.split(/\s+/);
|
||||
// if it's a string, split it to array
|
||||
if (_.isString(groupsList)) {
|
||||
const groupsArray = groupsList.split(/\s+/);
|
||||
|
||||
result.push(groupsArray);
|
||||
} else if (Array.isArray(groupsList)) {
|
||||
result.push(groupsList);
|
||||
} else {
|
||||
throw ErrorCode.getInternalError(
|
||||
'CONFIG: bad package acl (array or string expected): ' + JSON.stringify(groupsList)
|
||||
);
|
||||
}
|
||||
result.push(groupsArray);
|
||||
} else if (Array.isArray(groupsList)) {
|
||||
result.push(groupsList);
|
||||
} else {
|
||||
throw ErrorCode.getInternalError('CONFIG: bad package acl (array or string expected): ' + JSON.stringify(groupsList));
|
||||
}
|
||||
|
||||
return _.flatten(result);
|
||||
}
|
||||
@ -58,10 +55,7 @@ export function uplinkSanityCheck(uplinks: UpLinksConfList, users: any = BLACKLI
|
||||
}
|
||||
|
||||
export function sanityCheckNames(item: string, users: any): any {
|
||||
assert(
|
||||
item !== 'all' && item !== 'owner' && item !== 'anonymous' && item !== 'undefined' && item !== 'none',
|
||||
'CONFIG: reserved uplink name: ' + item
|
||||
);
|
||||
assert(item !== 'all' && item !== 'owner' && item !== 'anonymous' && item !== 'undefined' && item !== 'none', 'CONFIG: reserved uplink name: ' + item);
|
||||
assert(!item.match(/\s/), 'CONFIG: invalid uplink name: ' + item);
|
||||
assert(_.isNil(users[item]), 'CONFIG: duplicate uplink name: ' + item);
|
||||
users[item] = true;
|
||||
@ -90,7 +84,7 @@ export function hasProxyTo(pkg: string, upLink: string, packages: PackageList):
|
||||
const matchedPkg: MatchedPackage = getMatchedPackagesSpec(pkg, packages);
|
||||
const proxyList = typeof matchedPkg !== 'undefined' ? matchedPkg.proxy : [];
|
||||
if (proxyList) {
|
||||
return proxyList.some(curr => upLink === curr);
|
||||
return proxyList.some((curr) => upLink === curr);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -112,7 +106,7 @@ export function normalisePackageAccess(packages: LegacyPackageList): LegacyPacka
|
||||
normalizedPkgs['**'] = {
|
||||
access: [],
|
||||
publish: [],
|
||||
proxy: []
|
||||
proxy: [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -126,9 +120,7 @@ export function normalisePackageAccess(packages: LegacyPackageList): LegacyPacka
|
||||
normalizedPkgs[pkg].publish = normalizeUserList(packageAccess.publish);
|
||||
normalizedPkgs[pkg].proxy = normalizeUserList(packageAccess.proxy);
|
||||
// if unpublish is not defined, we set to false to fallback in publish access
|
||||
normalizedPkgs[pkg].unpublish = _.isUndefined(packageAccess.unpublish)
|
||||
? false
|
||||
: normalizeUserList(packageAccess.unpublish);
|
||||
normalizedPkgs[pkg].unpublish = _.isUndefined(packageAccess.unpublish) ? false : normalizeUserList(packageAccess.unpublish);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,9 +42,7 @@ export function createTarballHash(): Hash {
|
||||
* @return {String}
|
||||
*/
|
||||
export function stringToMD5(data: Buffer | string): string {
|
||||
return createHash('md5')
|
||||
.update(data)
|
||||
.digest('hex');
|
||||
return createHash('md5').update(data).digest('hex');
|
||||
}
|
||||
|
||||
export function generateRandomHexString(length = 8): string {
|
||||
@ -59,7 +57,7 @@ export function generateRandomHexString(length = 8): string {
|
||||
* @param options
|
||||
*/
|
||||
export async function signPayload(payload: RemoteUser, secretOrPrivateKey: string, options: JWTSignOptions = {}): Promise<string> {
|
||||
return new Promise(function(resolve, reject): Promise<string> {
|
||||
return new Promise(function (resolve, reject): Promise<string> {
|
||||
return jwt.sign(
|
||||
payload,
|
||||
secretOrPrivateKey,
|
||||
|
@ -8,16 +8,7 @@ import YAML from 'js-yaml';
|
||||
import { Request } from 'express';
|
||||
|
||||
import sanitizyReadme from '@verdaccio/readme';
|
||||
import {
|
||||
APP_ERROR,
|
||||
DEFAULT_PORT,
|
||||
DEFAULT_DOMAIN,
|
||||
DEFAULT_PROTOCOL,
|
||||
CHARACTER_ENCODING,
|
||||
HEADERS,
|
||||
DIST_TAGS,
|
||||
DEFAULT_USER,
|
||||
} from '@verdaccio/dev-commons';
|
||||
import { APP_ERROR, DEFAULT_PORT, DEFAULT_DOMAIN, DEFAULT_PROTOCOL, CHARACTER_ENCODING, HEADERS, DIST_TAGS, DEFAULT_USER } from '@verdaccio/dev-commons';
|
||||
|
||||
import { Package, Version, Author } from '@verdaccio/types';
|
||||
import { StringValue } from '@verdaccio/dev-types';
|
||||
@ -34,7 +25,6 @@ import {
|
||||
getCode,
|
||||
} from '@verdaccio/commons-api';
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
|
||||
@ -184,12 +174,7 @@ export function convertDistRemoteToLocalTarballUrls(pkg: Package, req: Request,
|
||||
* @param {*} uri
|
||||
* @return {String} a parsed url
|
||||
*/
|
||||
export function getLocalRegistryTarballUri(
|
||||
uri: string,
|
||||
pkgName: string,
|
||||
req: Request,
|
||||
urlPrefix: string | void
|
||||
): string {
|
||||
export function getLocalRegistryTarballUri(uri: string, pkgName: string, req: Request, urlPrefix: string | void): string {
|
||||
const currentHost = req.headers.host;
|
||||
|
||||
if (!currentHost) {
|
||||
@ -287,10 +272,10 @@ export function parseAddress(urlAddress: any): any {
|
||||
* Function filters out bad semver versions and sorts the array.
|
||||
* @return {Array} sorted Array
|
||||
*/
|
||||
export function semverSort(listVersions: string[], /* logger */): string[] {
|
||||
export function semverSort(listVersions: string[] /* logger */): string[] {
|
||||
return (
|
||||
listVersions
|
||||
.filter(function(x): boolean {
|
||||
.filter(function (x): boolean {
|
||||
if (!semver.parse(x, true)) {
|
||||
// FIXME: logger is always undefined
|
||||
// logger.warn({ ver: x }, 'ignoring bad version @{ver}');
|
||||
@ -365,8 +350,8 @@ export function parseInterval(interval: any): number {
|
||||
}
|
||||
let result = 0;
|
||||
let last_suffix = Infinity;
|
||||
interval.split(/\s+/).forEach(function(x): void {
|
||||
if (!x){
|
||||
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|)$/);
|
||||
@ -452,7 +437,7 @@ export function fileExists(path: string): boolean {
|
||||
}
|
||||
|
||||
export function sortByName(packages: any[], orderAscending: boolean | void = true): string[] {
|
||||
return packages.slice().sort(function(a, b): number {
|
||||
return packages.slice().sort(function (a, b): number {
|
||||
const comparatorNames = a.name.toLowerCase() < b.name.toLowerCase();
|
||||
|
||||
return orderAscending ? (comparatorNames ? -1 : 1) : comparatorNames ? 1 : -1;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user