mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-02-21 07:29:37 +01:00
Merge branch 'feat-new-detail-page' of github.com:verdaccio/verdaccio into feat-new-detail-page
This commit is contained in:
commit
f725597baa
@ -12,3 +12,5 @@ Dockerfile
|
|||||||
*.rpi
|
*.rpi
|
||||||
*.html
|
*.html
|
||||||
*.scss
|
*.scss
|
||||||
|
*.png
|
||||||
|
*.jpg
|
||||||
|
17
README.md
17
README.md
@ -164,16 +164,15 @@ Verdaccio aims to support all features of a standard npm client that make sense
|
|||||||
- npm audit - **supported**
|
- npm audit - **supported**
|
||||||
|
|
||||||
|
|
||||||
## Sponsors
|
## Special Thanks
|
||||||
|
|
||||||
#### Open Source License
|
Thanks to the following companies to help us to achieve our goals providing free open source licenses.
|
||||||
|
|
||||||
Thanks to the following sponsors to help to achieve our goals providing us free open source licenses.
|
[](https://www.jetbrains.com/)
|
||||||
|
[](https://crowdin.com/)
|
||||||
|
[](https://balsamiq.com/)
|
||||||
|
|
||||||
[](https://www.jetbrains.com/)
|
## Open Collective Sponsors
|
||||||
[](https://crowdin.com/)
|
|
||||||
|
|
||||||
#### Open Collective
|
|
||||||
|
|
||||||
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/verdaccio#sponsor)]
|
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/verdaccio#sponsor)]
|
||||||
|
|
||||||
@ -188,7 +187,7 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
|
|||||||
[](https://opencollective.com/verdaccio/sponsor/8/website)
|
[](https://opencollective.com/verdaccio/sponsor/8/website)
|
||||||
[](https://opencollective.com/verdaccio/sponsor/9/website)
|
[](https://opencollective.com/verdaccio/sponsor/9/website)
|
||||||
|
|
||||||
## Backers
|
## Open Collective Backers
|
||||||
|
|
||||||
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/verdaccio#backer)]
|
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/verdaccio#backer)]
|
||||||
|
|
||||||
@ -218,5 +217,5 @@ If you have any issue you can try the following options, do no desist to ask or
|
|||||||
|
|
||||||
Verdaccio is [MIT licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)
|
Verdaccio is [MIT licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE)
|
||||||
|
|
||||||
The Verdaccio documentation and logos (e.g., .md, .png, .sketch) files in the /docs and /assets folder) is
|
The Verdaccio documentation and logos (excluding /thanks, e.g., .md, .png, .sketch) files within the /assets folder) is
|
||||||
[Creative Commons licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE-docs).
|
[Creative Commons licensed](https://github.com/verdaccio/verdaccio/blob/master/LICENSE-docs).
|
||||||
|
BIN
assets/thanks/balsamiq/logo.jpg
Normal file
BIN
assets/thanks/balsamiq/logo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
@ -78,7 +78,7 @@
|
|||||||
"@commitlint/config-conventional": "7.1.2",
|
"@commitlint/config-conventional": "7.1.2",
|
||||||
"@material-ui/core": "3.9.0",
|
"@material-ui/core": "3.9.0",
|
||||||
"@material-ui/icons": "3.0.2",
|
"@material-ui/icons": "3.0.2",
|
||||||
"@verdaccio/types": "4.1.3",
|
"@verdaccio/types": "4.1.4",
|
||||||
"autosuggest-highlight": "3.1.1",
|
"autosuggest-highlight": "3.1.1",
|
||||||
"babel-core": "7.0.0-bridge.0",
|
"babel-core": "7.0.0-bridge.0",
|
||||||
"babel-eslint": "10.0.1",
|
"babel-eslint": "10.0.1",
|
||||||
|
@ -61,7 +61,7 @@ export default function(route, auth, storage) {
|
|||||||
stream.on('data', function each(pkg) {
|
stream.on('data', function each(pkg) {
|
||||||
processing_pkgs++;
|
processing_pkgs++;
|
||||||
|
|
||||||
auth.allow_access(pkg.name, req.remote_user, function(err, allowed) {
|
auth.allow_access({ packageName: pkg.name }, req.remote_user, function(err, allowed) {
|
||||||
processing_pkgs--;
|
processing_pkgs--;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { validateName as utilValidateName, validatePackage as utilValidatePackage, isObject, ErrorCode } from '../lib/utils';
|
import { validateName as utilValidateName, validatePackage as utilValidatePackage, getVersionFromTarball, isObject, ErrorCode } from '../lib/utils';
|
||||||
import { API_ERROR, HEADER_TYPE, HEADERS, HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER } from '../lib/constants';
|
import { API_ERROR, HEADER_TYPE, HEADERS, HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER } from '../lib/constants';
|
||||||
import { stringToMD5 } from '../lib/crypto-utils';
|
import { stringToMD5 } from '../lib/crypto-utils';
|
||||||
import type { $ResponseExtend, $RequestExtend, $NextFunctionVer, IAuth } from '../../types';
|
import type { $ResponseExtend, $RequestExtend, $NextFunctionVer, IAuth } from '../../types';
|
||||||
@ -99,12 +99,11 @@ export function allow(auth: IAuth) {
|
|||||||
return function(action: string) {
|
return function(action: string) {
|
||||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||||
req.pause();
|
req.pause();
|
||||||
let packageName = req.params.package;
|
const packageName = req.params.scope ? `@${req.params.scope}/${req.params.package}` : req.params.package;
|
||||||
if (req.params.scope) {
|
const packageVersion = req.params.filename ? getVersionFromTarball(req.params.filename) : undefined;
|
||||||
packageName = `@${req.params.scope}/${packageName}`;
|
|
||||||
}
|
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
auth['allow_' + action](packageName, req.remote_user, function(error, allowed) {
|
auth['allow_' + action]({ packageName, packageVersion }, req.remote_user, function(error, allowed) {
|
||||||
req.resume();
|
req.resume();
|
||||||
if (error) {
|
if (error) {
|
||||||
next(error);
|
next(error);
|
||||||
|
@ -7,6 +7,7 @@ import _ from 'lodash';
|
|||||||
import { addScope, addGravatarSupport, deleteProperties, sortByName, parseReadme } from '../../../lib/utils';
|
import { addScope, addGravatarSupport, deleteProperties, sortByName, parseReadme } from '../../../lib/utils';
|
||||||
import { allow } from '../../middleware';
|
import { allow } from '../../middleware';
|
||||||
import { DIST_TAGS, HEADER_TYPE, HEADERS, HTTP_STATUS } from '../../../lib/constants';
|
import { DIST_TAGS, HEADER_TYPE, HEADERS, HTTP_STATUS } from '../../../lib/constants';
|
||||||
|
import logger from '../../../lib/logger';
|
||||||
import type { Router } from 'express';
|
import type { Router } from 'express';
|
||||||
import type { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler, $SidebarPackage } from '../../../../types';
|
import type { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler, $SidebarPackage } from '../../../../types';
|
||||||
import type { Config } from '@verdaccio/types';
|
import type { Config } from '@verdaccio/types';
|
||||||
@ -17,7 +18,7 @@ function addPackageWebApi(route: Router, storage: IStorageHandler, auth: IAuth,
|
|||||||
const checkAllow = (name, remoteUser) =>
|
const checkAllow = (name, remoteUser) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
auth.allow_access(name, remoteUser, (err, allowed) => {
|
auth.allow_access({ packageName: name }, remoteUser, (err, allowed) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
} else {
|
} else {
|
||||||
@ -44,6 +45,7 @@ function addPackageWebApi(route: Router, storage: IStorageHandler, auth: IAuth,
|
|||||||
permissions.push(pkg);
|
permissions.push(pkg);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
logger.logger.error({ name: pkg.name, error: err }, 'permission process for @{name} has failed: @{error}');
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ function addSearchWebApi(route: Router, storage: IStorageHandler, auth: IAuth) {
|
|||||||
uplinksLook: false,
|
uplinksLook: false,
|
||||||
callback: (err, entry) => {
|
callback: (err, entry) => {
|
||||||
if (!err && entry) {
|
if (!err && entry) {
|
||||||
auth.allow_access(entry.name, req.remote_user, function(err, allowed) {
|
auth.allow_access({ packageName: entry.name }, req.remote_user, function(err, allowed) {
|
||||||
if (err || !allowed) {
|
if (err || !allowed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
import { convertPayloadToBase64, ErrorCode } from './utils';
|
import { convertPayloadToBase64, ErrorCode } from './utils';
|
||||||
import { getMatchedPackagesSpec } from './config-utils';
|
import { getMatchedPackagesSpec } from './config-utils';
|
||||||
|
|
||||||
import type { Config, Logger, Callback, IPluginAuth, RemoteUser, JWTSignOptions, Security } from '@verdaccio/types';
|
import type { Config, Logger, Callback, IPluginAuth, RemoteUser, JWTSignOptions, Security, AuthPluginPackage } from '@verdaccio/types';
|
||||||
import type { $Response, NextFunction } from 'express';
|
import type { $Response, NextFunction } from 'express';
|
||||||
import type { $RequestExtend, IAuth } from '../../types';
|
import type { $RequestExtend, IAuth } from '../../types';
|
||||||
|
|
||||||
@ -160,10 +160,10 @@ class Auth implements IAuth {
|
|||||||
/**
|
/**
|
||||||
* Allow user to access a package.
|
* Allow user to access a package.
|
||||||
*/
|
*/
|
||||||
allow_access(packageName: string, user: RemoteUser, callback: Callback) {
|
allow_access({ packageName, packageVersion }: AuthPluginPackage, user: RemoteUser, callback: Callback) {
|
||||||
const plugins = this.plugins.slice(0);
|
const plugins = this.plugins.slice(0);
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
const pkg = Object.assign({ name: packageName }, getMatchedPackagesSpec(packageName, this.config.packages));
|
const pkg = Object.assign({ name: packageName, version: packageVersion }, getMatchedPackagesSpec(packageName, this.config.packages));
|
||||||
const self = this;
|
const self = this;
|
||||||
this.logger.trace({ packageName }, 'allow access for @{packageName}');
|
this.logger.trace({ packageName }, 'allow access for @{packageName}');
|
||||||
|
|
||||||
@ -193,11 +193,11 @@ class Auth implements IAuth {
|
|||||||
/**
|
/**
|
||||||
* Allow user to publish a package.
|
* Allow user to publish a package.
|
||||||
*/
|
*/
|
||||||
allow_publish(packageName: string, user: string, callback: Callback) {
|
allow_publish({ packageName, packageVersion }: AuthPluginPackage, user: string, callback: Callback) {
|
||||||
const plugins = this.plugins.slice(0);
|
const plugins = this.plugins.slice(0);
|
||||||
const self = this;
|
const self = this;
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
const pkg = Object.assign({ name: packageName }, getMatchedPackagesSpec(packageName, this.config.packages));
|
const pkg = Object.assign({ name: packageName, version: packageVersion }, getMatchedPackagesSpec(packageName, this.config.packages));
|
||||||
this.logger.trace({ packageName }, 'allow publish for @{packageName}');
|
this.logger.trace({ packageName }, 'allow publish for @{packageName}');
|
||||||
|
|
||||||
(function next() {
|
(function next() {
|
||||||
|
@ -642,7 +642,7 @@ class LocalStorage implements IStorage {
|
|||||||
const { packages } = this.config;
|
const { packages } = this.config;
|
||||||
|
|
||||||
if (packages) {
|
if (packages) {
|
||||||
const listPackagesConf = Object.keys(packages || {});
|
const listPackagesConf = Object.keys(packages);
|
||||||
|
|
||||||
listPackagesConf.map(pkg => {
|
listPackagesConf.map(pkg => {
|
||||||
if (packages[pkg].storage) {
|
if (packages[pkg].storage) {
|
||||||
|
@ -322,17 +322,20 @@ class Storage implements IStorageHandler {
|
|||||||
);
|
);
|
||||||
lstream.on('error', function(err) {
|
lstream.on('error', function(err) {
|
||||||
self.logger.error({ err: err }, 'uplink error: @{err.message}');
|
self.logger.error({ err: err }, 'uplink error: @{err.message}');
|
||||||
cb(), (cb = function() {});
|
cb();
|
||||||
|
cb = function() {};
|
||||||
});
|
});
|
||||||
lstream.on('end', function() {
|
lstream.on('end', function() {
|
||||||
cb(), (cb = function() {});
|
cb();
|
||||||
|
cb = function() {};
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.abort = function() {
|
stream.abort = function() {
|
||||||
if (lstream.abort) {
|
if (lstream.abort) {
|
||||||
lstream.abort();
|
lstream.abort();
|
||||||
}
|
}
|
||||||
cb(), (cb = function() {});
|
cb();
|
||||||
|
cb = function() {};
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// executed after all series
|
// executed after all series
|
||||||
@ -411,7 +414,7 @@ class Storage implements IStorageHandler {
|
|||||||
const upLinks = [];
|
const upLinks = [];
|
||||||
const hasToLookIntoUplinks = _.isNil(options.uplinksLook) || options.uplinksLook;
|
const hasToLookIntoUplinks = _.isNil(options.uplinksLook) || options.uplinksLook;
|
||||||
|
|
||||||
if (!packageInfo || packageInfo === null) {
|
if (!packageInfo) {
|
||||||
exists = false;
|
exists = false;
|
||||||
packageInfo = generatePackageTemplate(name);
|
packageInfo = generatePackageTemplate(name);
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ class ProxyStorage implements IProxy {
|
|||||||
let error;
|
let error;
|
||||||
const responseLength = err ? 0 : body.length;
|
const responseLength = err ? 0 : body.length;
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
processBody(err, body);
|
processBody();
|
||||||
logActivity();
|
logActivity();
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
cb(err, res, body);
|
cb(err, res, body);
|
||||||
@ -552,7 +552,7 @@ class ProxyStorage implements IProxy {
|
|||||||
// https://github.com/rlidwka/sinopia/issues/254
|
// https://github.com/rlidwka/sinopia/issues/254
|
||||||
//
|
//
|
||||||
if (this.proxy === false) {
|
if (this.proxy === false) {
|
||||||
headers['X-Forwarded-For'] = (req && req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'] + ', ' : '') + req.connection.remoteAddress;
|
headers['X-Forwarded-For'] = (req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'] + ', ' : '') + req.connection.remoteAddress;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,3 +502,13 @@ export function parseReadme(packageName: string, readme: string): string {
|
|||||||
export function buildToken(type: string, token: string): string {
|
export function buildToken(type: string, token: string): string {
|
||||||
return `${_.capitalize(type)} ${token}`;
|
return `${_.capitalize(type)} ${token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return package version from tarball name
|
||||||
|
* @param {String} name
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
export function getVersionFromTarball(name: string) {
|
||||||
|
// $FlowFixMe
|
||||||
|
return /.+-(\d.+)\.tgz/.test(name) ? name.match(/.+-(\d.+)\.tgz/)[1] : undefined;
|
||||||
|
}
|
||||||
|
@ -34,11 +34,10 @@ export const GENERIC_AVATAR: string = `
|
|||||||
* Generate gravatar url from email address
|
* Generate gravatar url from email address
|
||||||
*/
|
*/
|
||||||
export function generateGravatarUrl(email: string = '', online: boolean = true): string {
|
export function generateGravatarUrl(email: string = '', online: boolean = true): string {
|
||||||
let emailCopy = email;
|
|
||||||
if (online) {
|
if (online) {
|
||||||
if (_.isString(email) && _.size(email) > 0) {
|
if (_.isString(email) && _.size(email) > 0) {
|
||||||
emailCopy = email.trim().toLocaleLowerCase();
|
email = email.trim().toLocaleLowerCase();
|
||||||
const emailMD5 = stringToMD5(emailCopy);
|
const emailMD5 = stringToMD5(email);
|
||||||
return `https://www.gravatar.com/avatar/${emailMD5}`;
|
return `https://www.gravatar.com/avatar/${emailMD5}`;
|
||||||
}
|
}
|
||||||
return GENERIC_AVATAR;
|
return GENERIC_AVATAR;
|
||||||
|
@ -24,7 +24,7 @@ export default class ExampleMiddlewarePlugin implements IPluginMiddleware {
|
|||||||
name: 'test'
|
name: 'test'
|
||||||
};
|
};
|
||||||
auth.authenticate('user', 'password', () => {});
|
auth.authenticate('user', 'password', () => {});
|
||||||
auth.allow_access('packageName', remoteUser, () => {});
|
auth.allow_access({packageName: 'packageName'}, remoteUser, () => {});
|
||||||
auth.add_user('user', 'password', () => {});
|
auth.add_user('user', 'password', () => {});
|
||||||
auth.aesEncrypt(new Buffer('pass'));
|
auth.aesEncrypt(new Buffer('pass'));
|
||||||
// storage
|
// storage
|
||||||
|
@ -11,7 +11,8 @@ import {
|
|||||||
combineBaseUrl,
|
combineBaseUrl,
|
||||||
getVersion,
|
getVersion,
|
||||||
normalizeDistTags,
|
normalizeDistTags,
|
||||||
getWebProtocol
|
getWebProtocol,
|
||||||
|
getVersionFromTarball
|
||||||
} from '../../../src/lib/utils';
|
} from '../../../src/lib/utils';
|
||||||
import { DIST_TAGS } from '../../../src/lib/constants';
|
import { DIST_TAGS } from '../../../src/lib/constants';
|
||||||
import Logger, { setup } from '../../../src/lib/logger';
|
import Logger, { setup } from '../../../src/lib/logger';
|
||||||
@ -259,6 +260,21 @@ describe('Utilities', () => {
|
|||||||
}).toThrow(expect.hasAssertions());
|
}).toThrow(expect.hasAssertions());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getVersionFromTarball', () => {
|
||||||
|
test('should get the right version', () => {
|
||||||
|
const simpleName = 'test-name-4.2.12.tgz'
|
||||||
|
const complexName = 'test-5.6.4-beta.2.tgz'
|
||||||
|
const otherComplexName = 'test-3.5.0-6.tgz'
|
||||||
|
expect(getVersionFromTarball(simpleName)).toEqual('4.2.12')
|
||||||
|
expect(getVersionFromTarball(complexName)).toEqual('5.6.4-beta.2')
|
||||||
|
expect(getVersionFromTarball(otherComplexName)).toEqual('3.5.0-6')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should don\'n fall at incorrect tarball name', () => {
|
||||||
|
expect(getVersionFromTarball('incorrectName')).toBeUndefined()
|
||||||
|
})
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('String utilities', () => {
|
describe('String utilities', () => {
|
||||||
|
BIN
yarn.lock
BIN
yarn.lock
Binary file not shown.
Loading…
Reference in New Issue
Block a user