mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-11-13 03:35:52 +01:00
refactor: enable pragma prettify to api modules (#1019)
no logic changes, just formatting Co-Authored-By: Priscila <priscilawebdev@users.noreply.github.com> Co-Authored-By: Ayush Sharma <ayush.aceit@gmail.com>
This commit is contained in:
parent
353860d0ba
commit
44144b40ce
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
@ -5,20 +9,20 @@ import type {$Application} from 'express';
|
||||
import type {$ResponseExtend, $RequestExtend, $NextFunctionVer} from '../../../types';
|
||||
|
||||
export default (app: $Application, selfPath: string) => {
|
||||
// Hook for tests only
|
||||
app.get('/-/_debug', function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
const doGarbabeCollector = _.isNil(global.gc) === false;
|
||||
// Hook for tests only
|
||||
app.get('/-/_debug', function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
const doGarbabeCollector = _.isNil(global.gc) === false;
|
||||
|
||||
if (doGarbabeCollector) {
|
||||
global.gc();
|
||||
}
|
||||
if (doGarbabeCollector) {
|
||||
global.gc();
|
||||
}
|
||||
|
||||
next({
|
||||
pid: process.pid,
|
||||
main: process.mainModule.filename,
|
||||
conf: selfPath,
|
||||
mem: process.memoryUsage(),
|
||||
gc: doGarbabeCollector,
|
||||
});
|
||||
next({
|
||||
pid: process.pid,
|
||||
main: process.mainModule.filename,
|
||||
conf: selfPath,
|
||||
mem: process.memoryUsage(),
|
||||
gc: doGarbabeCollector,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import mime from 'mime';
|
||||
@ -62,14 +66,14 @@ export default function(route: Router, auth: IAuth, storage: IStorageHandler) {
|
||||
});
|
||||
|
||||
route.post('/-/package/:package/dist-tags', can('publish'), function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
storage.mergeTags(req.params.package, req.body, function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.status(HTTP_STATUS.CREATED);
|
||||
return next({
|
||||
ok: API_MESSAGE.TAG_UPDATED,
|
||||
});
|
||||
storage.mergeTags(req.params.package, req.body, function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.status(HTTP_STATUS.CREATED);
|
||||
return next({
|
||||
ok: API_MESSAGE.TAG_UPDATED,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import type {Router} from 'express';
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
@ -18,8 +22,11 @@ export default function(router: Router, auth: IAuth, storage: IStorageHandler, c
|
||||
const can = allow(auth);
|
||||
|
||||
// publishing a package
|
||||
router.put('/:package/:_rev?/:revision?', can('publish'),
|
||||
media(mime.getType('json')), expectJson, function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
router.put('/:package/:_rev?/:revision?', can('publish'), media(mime.getType('json')), expectJson, function(
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
) {
|
||||
const name = req.params.package;
|
||||
let metadata;
|
||||
const create_tarball = function(filename: string, data, cb: Callback) {
|
||||
@ -61,10 +68,12 @@ export default function(router: Router, auth: IAuth, storage: IStorageHandler, c
|
||||
// https://github.com/isaacs/npm-registry-client/commit/e9fbeb8b67f249394f735c74ef11fe4720d46ca0
|
||||
// issue https://github.com/rlidwka/sinopia/issues/31, dealing with it here:
|
||||
|
||||
if (typeof(metadata._attachments) !== 'object'
|
||||
|| Object.keys(metadata._attachments).length !== 1
|
||||
|| typeof(metadata.versions) !== 'object'
|
||||
|| Object.keys(metadata.versions).length !== 1) {
|
||||
if (
|
||||
typeof metadata._attachments !== 'object' ||
|
||||
Object.keys(metadata._attachments).length !== 1 ||
|
||||
typeof metadata.versions !== 'object' ||
|
||||
Object.keys(metadata.versions).length !== 1
|
||||
) {
|
||||
// npm is doing something strange again
|
||||
// if this happens in normal circumstances, report it as a bug
|
||||
return next(ErrorCode.getBadRequest('unsupported registry call'));
|
||||
@ -140,8 +149,7 @@ export default function(router: Router, auth: IAuth, storage: IStorageHandler, c
|
||||
});
|
||||
|
||||
// removing a tarball
|
||||
router.delete('/:package/-/:filename/-rev/:revision', can('publish'),
|
||||
function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
router.delete('/:package/-/:filename/-rev/:revision', can('publish'), function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
storage.removeTarball(req.params.package, req.params.filename, req.params.revision, function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
@ -152,8 +160,11 @@ export default function(router: Router, auth: IAuth, storage: IStorageHandler, c
|
||||
});
|
||||
|
||||
// uploading package tarball
|
||||
router.put('/:package/-/:filename/*', can('publish'), media(HEADERS.OCTET_STREAM),
|
||||
function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
router.put('/:package/-/:filename/*', can('publish'), media(HEADERS.OCTET_STREAM), function(
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
) {
|
||||
const name = req.params.package;
|
||||
const stream = storage.addTarball(name, req.params.filename);
|
||||
req.pipe(stream);
|
||||
@ -184,8 +195,11 @@ export default function(router: Router, auth: IAuth, storage: IStorageHandler, c
|
||||
});
|
||||
|
||||
// adding a version
|
||||
router.put('/:package/:version/-tag/:tag', can('publish'),
|
||||
media(mime.getType('json')), expectJson, function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
router.put('/:package/:version/-tag/:tag', can('publish'), media(mime.getType('json')), expectJson, function(
|
||||
req: $RequestExtend,
|
||||
res: $ResponseExtend,
|
||||
next: $NextFunctionVer
|
||||
) {
|
||||
const {version, tag} = req.params;
|
||||
const name = req.params.package;
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
export default function(route, auth, storage) {
|
||||
// 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;
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
@ -23,7 +27,7 @@ export default function(route: Router, auth: IAuth, config: Config) {
|
||||
const {name, password} = req.body;
|
||||
|
||||
if (_.isNil(req.remote_user.name) === false) {
|
||||
const token = (name && password) ? await getApiToken(auth, config, req.remote_user, password) : undefined;
|
||||
const token = name && password ? await getApiToken(auth, config, req.remote_user, password) : undefined;
|
||||
|
||||
res.status(HTTP_STATUS.CREATED);
|
||||
|
||||
@ -38,17 +42,17 @@ export default function(route: Router, auth: IAuth, config: Config) {
|
||||
// With npm registering is the same as logging in,
|
||||
// and npm accepts only an 409 error.
|
||||
// So, changing status code here.
|
||||
return next( ErrorCode.getCode(err.status, err.message) || ErrorCode.getConflict(err.message));
|
||||
return next(ErrorCode.getCode(err.status, err.message) || ErrorCode.getConflict(err.message));
|
||||
}
|
||||
return next(err);
|
||||
}
|
||||
|
||||
const token = (name && password) ? await getApiToken(auth, config, user, password) : undefined;
|
||||
const token = name && password ? await getApiToken(auth, config, user, password) : undefined;
|
||||
|
||||
req.remote_user = user;
|
||||
res.status(HTTP_STATUS.CREATED);
|
||||
return next({
|
||||
ok: `user '${req.body.name }' created`,
|
||||
ok: `user '${req.body.name}' created`,
|
||||
token,
|
||||
});
|
||||
});
|
||||
@ -62,7 +66,6 @@ export default function(route: Router, auth: IAuth, config: Config) {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// placeholder 'cause npm require to be authenticated to publish
|
||||
// we do not do any real authentication yet
|
||||
route.post('/_session', Cookies.express(), function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
|
@ -1,18 +1,28 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import type {$Response, Router} from 'express';
|
||||
import type {$RequestExtend, $NextFunctionVer} from '../../../../types';
|
||||
|
||||
export default function(route: Router) {
|
||||
route.get('/whoami', (req: $RequestExtend, res: $Response, next: $NextFunctionVer): void => {
|
||||
if (req.headers.referer === 'whoami') {
|
||||
next({username: req.remote_user.name});
|
||||
} else {
|
||||
next('route');
|
||||
route.get(
|
||||
'/whoami',
|
||||
(req: $RequestExtend, res: $Response, next: $NextFunctionVer): void => {
|
||||
if (req.headers.referer === 'whoami') {
|
||||
next({username: req.remote_user.name});
|
||||
} else {
|
||||
next('route');
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
route.get('/-/whoami', (req: $RequestExtend, res: $Response, next: $NextFunctionVer): mixed => {
|
||||
next({username: req.remote_user.name});
|
||||
});
|
||||
route.get(
|
||||
'/-/whoami',
|
||||
(req: $RequestExtend, res: $Response, next: $NextFunctionVer): mixed => {
|
||||
next({username: req.remote_user.name});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import type {IAuth, IStorageHandler} from '../../../types';
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
@ -16,16 +20,8 @@ import webAPI from './web/api';
|
||||
import web from './web';
|
||||
|
||||
import type {$Application} from 'express';
|
||||
import type {
|
||||
$ResponseExtend,
|
||||
$RequestExtend,
|
||||
$NextFunctionVer,
|
||||
IStorageHandler,
|
||||
IAuth} from '../../types';
|
||||
import type {
|
||||
Config as IConfig,
|
||||
IPluginMiddleware,
|
||||
} from '@verdaccio/types';
|
||||
import type {$ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler, IAuth} from '../../types';
|
||||
import type {Config as IConfig, IPluginMiddleware} from '@verdaccio/types';
|
||||
|
||||
const LoggerApp = require('../lib/logger');
|
||||
const Middleware = require('./middleware');
|
||||
@ -67,7 +63,7 @@ const defineAPI = function(config: IConfig, storage: IStorageHandler) {
|
||||
const plugins = loadPlugin(config, config.middlewares, plugin_params, function(plugin: IPluginMiddleware) {
|
||||
return plugin.register_middlewares;
|
||||
});
|
||||
plugins.forEach((plugin) => {
|
||||
plugins.forEach(plugin => {
|
||||
plugin.register_middlewares(app, auth, storage);
|
||||
});
|
||||
|
||||
@ -111,11 +107,11 @@ const defineAPI = function(config: IConfig, storage: IStorageHandler) {
|
||||
return app;
|
||||
};
|
||||
|
||||
export default async function(configHash: any) {
|
||||
export default (async function(configHash: any) {
|
||||
LoggerApp.setup(configHash.logs);
|
||||
const config: IConfig = new AppConfig(configHash);
|
||||
const storage: IStorageHandler = new Storage(config);
|
||||
// waits until init calls have been intialized
|
||||
await storage.init(config);
|
||||
return defineAPI(config, storage);
|
||||
}
|
||||
});
|
||||
|
@ -1,12 +1,12 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import {
|
||||
validateName as utilValidateName,
|
||||
validatePackage as utilValidatePackage,
|
||||
isObject,
|
||||
ErrorCode} from '../lib/utils';
|
||||
import {validateName as utilValidateName, validatePackage as utilValidatePackage, isObject, ErrorCode} from '../lib/utils';
|
||||
import {API_ERROR, HEADER_TYPE, HEADERS, HTTP_STATUS, TOKEN_BASIC, TOKEN_BEARER} from '../lib/constants';
|
||||
import {stringToMD5} from '../lib/crypto-utils';
|
||||
import type {$ResponseExtend, $RequestExtend, $NextFunctionVer, IAuth} from '../../types';
|
||||
@ -39,7 +39,7 @@ export function validateName(req: $RequestExtend, res: $ResponseExtend, next: $N
|
||||
} else if (utilValidateName(value)) {
|
||||
next();
|
||||
} else {
|
||||
next( ErrorCode.getForbidden('invalid ' + name));
|
||||
next(ErrorCode.getForbidden('invalid ' + name));
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,15 +52,14 @@ export function validatePackage(req: $RequestExtend, res: $ResponseExtend, next:
|
||||
} else if (utilValidatePackage(value)) {
|
||||
next();
|
||||
} else {
|
||||
next( ErrorCode.getForbidden('invalid ' + name) );
|
||||
next(ErrorCode.getForbidden('invalid ' + name));
|
||||
}
|
||||
}
|
||||
|
||||
export function media(expect: string) {
|
||||
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
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]) );
|
||||
next(ErrorCode.getCode(HTTP_STATUS.UNSUPPORTED_MEDIA, 'wrong content-type, expect: ' + expect + ', got: ' + req.headers[HEADER_TYPE.CONTENT_TYPE]));
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
@ -77,7 +76,7 @@ export function encodeScopePackage(req: $RequestExtend, res: $ResponseExtend, ne
|
||||
|
||||
export function expectJson(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
if (!isObject(req.body)) {
|
||||
return next( ErrorCode.getBadRequest('can\'t parse incoming json') );
|
||||
return next(ErrorCode.getBadRequest("can't parse incoming json"));
|
||||
}
|
||||
next();
|
||||
}
|
||||
@ -87,10 +86,10 @@ export function anti_loop(config: Config) {
|
||||
if (req.headers.via != null) {
|
||||
let arr = req.headers.via.split(',');
|
||||
|
||||
for (let i=0; i<arr.length; i++) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const m = arr[i].match(/\s*(\S+)\s+(\S+)/);
|
||||
if (m && m[2] === config.server_id) {
|
||||
return next( ErrorCode.getCode(HTTP_STATUS.LOOP_DETECTED, 'loop detected') );
|
||||
return next(ErrorCode.getCode(HTTP_STATUS.LOOP_DETECTED, 'loop detected'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,7 +122,7 @@ export function allow(auth: IAuth) {
|
||||
};
|
||||
}
|
||||
|
||||
export function final(body: any, req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
export function final(body: any, req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
if (res.statusCode === HTTP_STATUS.UNAUTHORIZED && !res.getHeader(HEADERS.WWW_AUTH)) {
|
||||
// they say it's required for 401, so...
|
||||
res.header(HEADERS.WWW_AUTH, `${TOKEN_BASIC}, ${TOKEN_BEARER}`);
|
||||
@ -135,8 +134,8 @@ export function allow(auth: IAuth) {
|
||||
res.header(HEADERS.CONTENT_TYPE, HEADERS.JSON);
|
||||
}
|
||||
|
||||
if (typeof(body) === 'object' && _.isNil(body) === false) {
|
||||
if (typeof(body.error) === 'string') {
|
||||
if (typeof body === 'object' && _.isNil(body) === false) {
|
||||
if (typeof body.error === 'string') {
|
||||
res._verdaccio_error = body.error;
|
||||
}
|
||||
body = JSON.stringify(body, undefined, ' ') + '\n';
|
||||
@ -181,8 +180,7 @@ export function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFuncti
|
||||
}
|
||||
|
||||
req.url = req.originalUrl;
|
||||
req.log.info( {req: req, ip: req.ip}
|
||||
, '@{ip} requested \'@{req.method} @{req.url}\'' );
|
||||
req.log.info({req: req, ip: req.ip}, "@{ip} requested '@{req.method} @{req.url}'");
|
||||
req.originalUrl = req.url;
|
||||
|
||||
if (_.isNil(_auth) === false) {
|
||||
@ -210,7 +208,7 @@ export function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFuncti
|
||||
let forwardedFor = req.headers['x-forwarded-for'];
|
||||
let remoteAddress = req.connection.remoteAddress;
|
||||
let remoteIP = forwardedFor ? `${forwardedFor} via ${remoteAddress}` : remoteAddress;
|
||||
let message = '@{status}, user: @{user}(@{remoteIP}), req: \'@{request.method} @{request.url}\'';
|
||||
let message = "@{status}, user: @{user}(@{remoteIP}), req: '@{request.method} @{request.url}'";
|
||||
if (res._verdaccio_error) {
|
||||
message += ', error: @{!error}';
|
||||
} else {
|
||||
@ -218,21 +216,24 @@ export function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFuncti
|
||||
}
|
||||
|
||||
req.url = req.originalUrl;
|
||||
req.log.warn({
|
||||
request: {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
req.log.warn(
|
||||
{
|
||||
request: {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
},
|
||||
level: 35, // http
|
||||
user: (req.remote_user && req.remote_user.name) || null,
|
||||
remoteIP,
|
||||
status: res.statusCode,
|
||||
error: res._verdaccio_error,
|
||||
bytes: {
|
||||
in: bytesin,
|
||||
out: bytesout,
|
||||
},
|
||||
},
|
||||
level: 35, // http
|
||||
user: req.remote_user && req.remote_user.name || null,
|
||||
remoteIP,
|
||||
status: res.statusCode,
|
||||
error: res._verdaccio_error,
|
||||
bytes: {
|
||||
in: bytesin,
|
||||
out: bytesout,
|
||||
},
|
||||
}, message);
|
||||
message
|
||||
);
|
||||
req.originalUrl = req.url;
|
||||
};
|
||||
|
||||
@ -254,25 +255,27 @@ export function log(req: $RequestExtend, res: $ResponseExtend, next: $NextFuncti
|
||||
|
||||
// Middleware
|
||||
export function errorReportingMiddleware(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
res.report_error = res.report_error || function(err) {
|
||||
if (err.status && err.status >= HTTP_STATUS.BAD_REQUEST && err.status < 600) {
|
||||
if (_.isNil(res.headersSent) === false) {
|
||||
res.status(err.status);
|
||||
next({error: err.message || API_ERROR.UNKNOWN_ERROR});
|
||||
}
|
||||
} else {
|
||||
Logger.logger.error( {err: err}, 'unexpected error: @{!err.message}\n@{err.stack}');
|
||||
if (!res.status || !res.send) {
|
||||
Logger.logger.error('this is an error in express.js, please report this');
|
||||
res.destroy();
|
||||
} else if (!res.headersSent) {
|
||||
res.status(HTTP_STATUS.INTERNAL_ERROR);
|
||||
next({error: API_ERROR.INTERNAL_SERVER_ERROR});
|
||||
res.report_error =
|
||||
res.report_error ||
|
||||
function(err) {
|
||||
if (err.status && err.status >= HTTP_STATUS.BAD_REQUEST && err.status < 600) {
|
||||
if (_.isNil(res.headersSent) === false) {
|
||||
res.status(err.status);
|
||||
next({error: err.message || API_ERROR.UNKNOWN_ERROR});
|
||||
}
|
||||
} else {
|
||||
// socket should be already closed
|
||||
Logger.logger.error({err: err}, 'unexpected error: @{!err.message}\n@{err.stack}');
|
||||
if (!res.status || !res.send) {
|
||||
Logger.logger.error('this is an error in express.js, please report this');
|
||||
res.destroy();
|
||||
} else if (!res.headersSent) {
|
||||
res.status(HTTP_STATUS.INTERNAL_ERROR);
|
||||
next({error: API_ERROR.INTERNAL_SERVER_ERROR});
|
||||
} else {
|
||||
// socket should be already closed
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
next();
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import {Router} from 'express';
|
||||
|
@ -1,34 +1,32 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
import {addScope, addGravatarSupport, deleteProperties, sortByName, DIST_TAGS, parseReadme} from '../../../lib/utils';
|
||||
import {allow} from '../../middleware';
|
||||
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';
|
||||
|
||||
function addPackageWebApi(route: Router, storage: IStorageHandler, auth: IAuth) {
|
||||
const can = allow(auth);
|
||||
|
||||
const checkAllow = (name, remoteUser) => new Promise((resolve, reject) => {
|
||||
try {
|
||||
auth.allow_access(name, remoteUser, (err, allowed) => {
|
||||
if (err) {
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(allowed);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
const checkAllow = (name, remoteUser) =>
|
||||
new Promise((resolve, reject) => {
|
||||
try {
|
||||
auth.allow_access(name, remoteUser, (err, allowed) => {
|
||||
if (err) {
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(allowed);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
|
||||
// Get list of all visible package
|
||||
route.get('/packages', function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
@ -57,8 +55,7 @@ function addPackageWebApi(route: Router, storage: IStorageHandler, auth: IAuth)
|
||||
});
|
||||
|
||||
// Get package readme
|
||||
route.get('/package/readme/(@:scope/)?:package/:version?', can('access'),
|
||||
function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
route.get('/package/readme/(@:scope/)?:package/:version?', can('access'), function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
const packageName = req.params.scope ? addScope(req.params.scope, req.params.package) : req.params.package;
|
||||
|
||||
storage.getPackage({
|
||||
@ -75,8 +72,7 @@ function addPackageWebApi(route: Router, storage: IStorageHandler, auth: IAuth)
|
||||
});
|
||||
});
|
||||
|
||||
route.get('/sidebar/(@:scope/)?:package',
|
||||
function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
route.get('/sidebar/(@:scope/)?:package', function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
||||
const packageName: string = req.params.scope ? addScope(req.params.scope, req.params.package) : req.params.package;
|
||||
|
||||
storage.getPackage({
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import Search from '../../../lib/search';
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import {HTTP_STATUS} from '../../../lib/constants';
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
@ -16,10 +20,10 @@ const templatePath = path.join(env.DIST_PATH, '/index.html');
|
||||
const existTemplate = fs.existsSync(templatePath);
|
||||
|
||||
if (!existTemplate) {
|
||||
const err = new VError('missing file: "%s", run `yarn build:webui`', templatePath);
|
||||
/* eslint no-console:off */
|
||||
const err = new VError('missing file: "%s", run `yarn build:webui`', templatePath);
|
||||
/* eslint no-console:off */
|
||||
console.error(chalk.red(err.message));
|
||||
/* eslint no-console:off */
|
||||
/* eslint no-console:off */
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
@ -56,9 +60,7 @@ module.exports = function(config, auth, storage) {
|
||||
});
|
||||
|
||||
router.get('/', function(req, res) {
|
||||
const base = Utils.combineBaseUrl(
|
||||
Utils.getWebProtocol(req.get(HEADERS.FORWARDED_PROTO), req.protocol),
|
||||
req.get('host'), config.url_prefix);
|
||||
const base = Utils.combineBaseUrl(Utils.getWebProtocol(req.get(HEADERS.FORWARDED_PROTO), req.protocol), req.get('host'), config.url_prefix);
|
||||
|
||||
let webPage = template
|
||||
.replace(/ToReplaceByVerdaccio/g, base)
|
||||
|
@ -1,3 +1,9 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const APP_ROOT = path.resolve(__dirname, '../../');
|
||||
|
@ -1,34 +1,24 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
import {convertPayloadToBase64, ErrorCode} from './utils';
|
||||
import {API_ERROR, HTTP_STATUS, ROLES, TIME_EXPIRATION_7D, TOKEN_BASIC, TOKEN_BEARER, CHARACTER_ENCODING} from './constants';
|
||||
|
||||
import type {
|
||||
RemoteUser,
|
||||
Package,
|
||||
Callback,
|
||||
Config,
|
||||
Security,
|
||||
APITokenOptions,
|
||||
JWTOptions} from '@verdaccio/types';
|
||||
import type {
|
||||
CookieSessionToken, IAuthWebUI, AuthMiddlewarePayload, AuthTokenHeader, BasicPayload,
|
||||
} from '../../types';
|
||||
import type {RemoteUser, Package, Callback, Config, Security, APITokenOptions, JWTOptions} from '@verdaccio/types';
|
||||
import type {CookieSessionToken, IAuthWebUI, AuthMiddlewarePayload, AuthTokenHeader, BasicPayload} from '../../types';
|
||||
import {aesDecrypt, verifyPayload} from './crypto-utils';
|
||||
|
||||
|
||||
/**
|
||||
* Create a RemoteUser object
|
||||
* @return {Object} { name: xx, pluginGroups: [], real_groups: [] }
|
||||
*/
|
||||
export function createRemoteUser(name: string, pluginGroups: Array<string>): RemoteUser {
|
||||
const isGroupValid: boolean = Array.isArray(pluginGroups);
|
||||
const groups = (isGroupValid ? pluginGroups : []).concat([
|
||||
ROLES.$ALL,
|
||||
ROLES.$AUTH,
|
||||
ROLES.DEPRECATED_ALL,
|
||||
ROLES.DEPRECATED_AUTH,
|
||||
ROLES.ALL]);
|
||||
const groups = (isGroupValid ? pluginGroups : []).concat([ROLES.$ALL, ROLES.$AUTH, ROLES.DEPRECATED_ALL, ROLES.DEPRECATED_AUTH, ROLES.ALL]);
|
||||
|
||||
return {
|
||||
name,
|
||||
@ -37,7 +27,6 @@ export function createRemoteUser(name: string, pluginGroups: Array<string>): Rem
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds an anonymous remote user in case none is logged in.
|
||||
* @return {Object} { name: xx, groups: [], real_groups: [] }
|
||||
@ -46,12 +35,7 @@ export function createAnonymousRemoteUser(): RemoteUser {
|
||||
return {
|
||||
name: undefined,
|
||||
// groups without '$' are going to be deprecated eventually
|
||||
groups: [
|
||||
ROLES.$ALL,
|
||||
ROLES.$ANONYMOUS,
|
||||
ROLES.DEPRECATED_ALL,
|
||||
ROLES.DEPRECATED_ANONYMOUS,
|
||||
],
|
||||
groups: [ROLES.$ALL, ROLES.$ANONYMOUS, ROLES.DEPRECATED_ALL, ROLES.DEPRECATED_ANONYMOUS],
|
||||
real_groups: [],
|
||||
};
|
||||
}
|
||||
@ -59,7 +43,7 @@ export function createAnonymousRemoteUser(): RemoteUser {
|
||||
export function allow_action(action: string) {
|
||||
return function(user: RemoteUser, pkg: Package, callback: Callback) {
|
||||
const {name, groups} = user;
|
||||
const hasPermission = pkg[action].some((group) => name === group || groups.includes(group));
|
||||
const hasPermission = pkg[action].some(group => name === group || groups.includes(group));
|
||||
|
||||
if (hasPermission) {
|
||||
return callback(null, true);
|
||||
@ -105,8 +89,8 @@ const defaultWebTokenOptions: JWTOptions = {
|
||||
};
|
||||
|
||||
const defaultApiTokenConf: APITokenOptions = {
|
||||
legacy: true,
|
||||
sign: {},
|
||||
legacy: true,
|
||||
sign: {},
|
||||
};
|
||||
|
||||
export function getSecurity(config: Config): Security {
|
||||
@ -133,32 +117,28 @@ export function buildUserBuffer(name: string, password: string) {
|
||||
export function isAESLegacy(security: Security): boolean {
|
||||
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> {
|
||||
export async function getApiToken(auth: IAuthWebUI, config: Config, remoteUser: RemoteUser, aesPassword: string): Promise<string> {
|
||||
const security: Security = getSecurity(config);
|
||||
|
||||
if (isAESLegacy(security)) {
|
||||
// fallback all goes to AES encryption
|
||||
return await new Promise((resolve) => {
|
||||
// fallback all goes to AES encryption
|
||||
return await new Promise(resolve => {
|
||||
resolve(auth.aesEncrypt(buildUserBuffer((remoteUser: any).name, aesPassword)).toString('base64'));
|
||||
});
|
||||
} else {
|
||||
// i am wiling to use here _.isNil but flow does not like it yet.
|
||||
// i am wiling to use here _.isNil but flow does not like it yet.
|
||||
const {jwt} = security.api;
|
||||
|
||||
if (jwt && jwt.sign) {
|
||||
return await auth.jwtEncrypt(remoteUser, jwt.sign);
|
||||
} else {
|
||||
return await new Promise((resolve) => {
|
||||
return await new Promise(resolve => {
|
||||
resolve(auth.aesEncrypt(buildUserBuffer((remoteUser: any).name, aesPassword)).toString('base64'));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,8 +161,7 @@ export function parseBasicPayload(credentials: string): BasicPayload {
|
||||
return {user, password};
|
||||
}
|
||||
|
||||
export function parseAESCredentials(
|
||||
authorizationHeader: string, secret: string) {
|
||||
export function parseAESCredentials(authorizationHeader: string, secret: string) {
|
||||
const {scheme, token} = parseAuthTokenHeader(authorizationHeader);
|
||||
|
||||
// basic is deprecated and should not be enforced
|
||||
@ -222,11 +201,7 @@ export function isAuthHeaderValid(authorization: string): boolean {
|
||||
return authorization.split(' ').length === 2;
|
||||
}
|
||||
|
||||
export function getMiddlewareCredentials(
|
||||
security: Security,
|
||||
secret: string,
|
||||
authorizationHeader: string
|
||||
): AuthMiddlewarePayload {
|
||||
export function getMiddlewareCredentials(security: Security, secret: string, authorizationHeader: string): AuthMiddlewarePayload {
|
||||
if (isAESLegacy(security)) {
|
||||
const credentials = parseAESCredentials(authorizationHeader, secret);
|
||||
if (!credentials) {
|
||||
@ -243,7 +218,7 @@ export function getMiddlewareCredentials(
|
||||
const {scheme, token} = parseAuthTokenHeader(authorizationHeader);
|
||||
|
||||
if (_.isString(token) && scheme.toUpperCase() === TOKEN_BEARER.toUpperCase()) {
|
||||
return verifyJWTPayload(token, secret);
|
||||
return verifyJWTPayload(token, secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
@ -12,14 +16,15 @@ import {
|
||||
createAnonymousRemoteUser,
|
||||
isAuthHeaderValid,
|
||||
getSecurity,
|
||||
isAESLegacy, parseAuthTokenHeader, parseBasicPayload, createRemoteUser,
|
||||
isAESLegacy,
|
||||
parseAuthTokenHeader,
|
||||
parseBasicPayload,
|
||||
createRemoteUser,
|
||||
} from './auth-utils';
|
||||
import {convertPayloadToBase64, ErrorCode} from './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} from '@verdaccio/types';
|
||||
import type {$Response, NextFunction} from 'express';
|
||||
import type {$RequestExtend, IAuth} from '../../types';
|
||||
|
||||
@ -66,10 +71,10 @@ class Auth implements IAuth {
|
||||
return next();
|
||||
}
|
||||
|
||||
self.logger.trace( {username}, 'authenticating @{username}');
|
||||
self.logger.trace({username}, 'authenticating @{username}');
|
||||
plugin.authenticate(username, password, function(err, groups) {
|
||||
if (err) {
|
||||
self.logger.trace( {username, err}, 'authenticating for user @{username} failed. Error: @{err.message}');
|
||||
self.logger.trace({username, err}, 'authenticating for user @{username} failed. Error: @{err.message}');
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
@ -90,7 +95,7 @@ class Auth implements IAuth {
|
||||
throw new TypeError(API_ERROR.BAD_FORMAT_USER_GROUP);
|
||||
}
|
||||
|
||||
self.logger.trace( {username, groups}, 'authentication for user @{username} was successfully. Groups: @{groups}');
|
||||
self.logger.trace({username, groups}, 'authentication for user @{username} was successfully. Groups: @{groups}');
|
||||
return cb(err, createRemoteUser(username, groups));
|
||||
}
|
||||
next();
|
||||
@ -101,7 +106,7 @@ class Auth implements IAuth {
|
||||
add_user(user: string, password: string, cb: Callback) {
|
||||
let self = this;
|
||||
let plugins = this.plugins.slice(0);
|
||||
this.logger.trace( {user}, 'add user @{user}');
|
||||
this.logger.trace({user}, 'add user @{user}');
|
||||
|
||||
(function next() {
|
||||
let plugin = plugins.shift();
|
||||
@ -115,11 +120,11 @@ class Auth implements IAuth {
|
||||
// p.add_user() execution
|
||||
plugin[method](user, password, function(err, ok) {
|
||||
if (err) {
|
||||
self.logger.trace( {user, err}, 'the user @{user} could not being added. Error: @{err}');
|
||||
self.logger.trace({user, err}, 'the user @{user} could not being added. Error: @{err}');
|
||||
return cb(err);
|
||||
}
|
||||
if (ok) {
|
||||
self.logger.trace( {user}, 'the user @{user} has been added');
|
||||
self.logger.trace({user}, 'the user @{user} has been added');
|
||||
return self.authenticate(user, password, cb);
|
||||
}
|
||||
next();
|
||||
@ -136,7 +141,7 @@ class Auth implements IAuth {
|
||||
// $FlowFixMe
|
||||
let pkg = Object.assign({name: packageName}, getMatchedPackagesSpec(packageName, this.config.packages));
|
||||
const self = this;
|
||||
this.logger.trace( {packageName}, 'allow access for @{packageName}');
|
||||
this.logger.trace({packageName}, 'allow access for @{packageName}');
|
||||
|
||||
(function next() {
|
||||
const plugin = plugins.shift();
|
||||
@ -147,12 +152,12 @@ class Auth implements IAuth {
|
||||
|
||||
plugin.allow_access(user, pkg, function(err, ok: boolean) {
|
||||
if (err) {
|
||||
self.logger.trace( {packageName, err}, 'forbidden access for @{packageName}. Error: @{err.message}');
|
||||
self.logger.trace({packageName, err}, 'forbidden access for @{packageName}. Error: @{err.message}');
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
self.logger.trace( {packageName}, 'allowed access for @{packageName}');
|
||||
self.logger.trace({packageName}, 'allowed access for @{packageName}');
|
||||
return callback(null, ok);
|
||||
}
|
||||
|
||||
@ -169,7 +174,7 @@ class Auth implements IAuth {
|
||||
const self = this;
|
||||
// $FlowFixMe
|
||||
let pkg = Object.assign({name: packageName}, getMatchedPackagesSpec(packageName, this.config.packages));
|
||||
this.logger.trace( {packageName}, 'allow publish for @{packageName}');
|
||||
this.logger.trace({packageName}, 'allow publish for @{packageName}');
|
||||
|
||||
(function next() {
|
||||
const plugin = plugins.shift();
|
||||
@ -180,12 +185,12 @@ class Auth implements IAuth {
|
||||
|
||||
plugin.allow_publish(user, pkg, (err, ok: boolean) => {
|
||||
if (err) {
|
||||
self.logger.trace( {packageName}, 'forbidden publish for @{packageName}');
|
||||
self.logger.trace({packageName}, 'forbidden publish for @{packageName}');
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
self.logger.trace( {packageName}, 'allowed publish for @{packageName}');
|
||||
self.logger.trace({packageName}, 'allowed publish for @{packageName}');
|
||||
return callback(null, ok);
|
||||
}
|
||||
next(); // cb(null, false) causes next plugin to roll
|
||||
@ -223,7 +228,7 @@ class Auth implements IAuth {
|
||||
|
||||
if (!isAuthHeaderValid(authorization)) {
|
||||
this.logger.trace('api middleware auth heather is not valid');
|
||||
return next( ErrorCode.getBadRequest(API_ERROR.BAD_AUTH_HEADER) );
|
||||
return next(ErrorCode.getBadRequest(API_ERROR.BAD_AUTH_HEADER));
|
||||
}
|
||||
|
||||
const security: Security = getSecurity(this.config);
|
||||
@ -239,12 +244,7 @@ class Auth implements IAuth {
|
||||
};
|
||||
}
|
||||
|
||||
_handleJWTAPIMiddleware(
|
||||
req: $RequestExtend,
|
||||
security: Security,
|
||||
secret: string,
|
||||
authorization: string,
|
||||
next: Function) {
|
||||
_handleJWTAPIMiddleware(req: $RequestExtend, security: Security, secret: string, authorization: string, next: Function) {
|
||||
const {scheme, token} = parseAuthTokenHeader(authorization);
|
||||
if (scheme.toUpperCase() === TOKEN_BASIC.toUpperCase()) {
|
||||
// this should happen when client tries to login with an existing user
|
||||
@ -273,11 +273,7 @@ class Auth implements IAuth {
|
||||
}
|
||||
}
|
||||
|
||||
_handleAESMiddleware(req: $RequestExtend,
|
||||
security: Security,
|
||||
secret: string,
|
||||
authorization: string,
|
||||
next: Function) {
|
||||
_handleAESMiddleware(req: $RequestExtend, security: Security, secret: string, authorization: string, next: Function) {
|
||||
const credentials: any = getMiddlewareCredentials(security, secret, authorization);
|
||||
if (credentials) {
|
||||
const {user, password} = credentials;
|
||||
@ -297,8 +293,7 @@ class Auth implements IAuth {
|
||||
}
|
||||
|
||||
_isRemoteUserMissing(remote_user: RemoteUser): boolean {
|
||||
return _.isUndefined(remote_user) === false &&
|
||||
(_.isUndefined(remote_user.name) === false);
|
||||
return _.isUndefined(remote_user) === false && _.isUndefined(remote_user.name) === false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -307,11 +302,11 @@ class Auth implements IAuth {
|
||||
webUIJWTmiddleware() {
|
||||
return (req: $RequestExtend, res: $Response, _next: NextFunction) => {
|
||||
if (this._isRemoteUserMissing(req.remote_user)) {
|
||||
return _next();
|
||||
return _next();
|
||||
}
|
||||
|
||||
req.pause();
|
||||
const next = (err) => {
|
||||
const next = err => {
|
||||
req.resume();
|
||||
if (err) {
|
||||
// req.remote_user.error = err.message;
|
||||
@ -327,7 +322,7 @@ class Auth implements IAuth {
|
||||
}
|
||||
|
||||
if (!isAuthHeaderValid(authorization)) {
|
||||
return next( ErrorCode.getBadRequest(API_ERROR.BAD_AUTH_HEADER) );
|
||||
return next(ErrorCode.getBadRequest(API_ERROR.BAD_AUTH_HEADER));
|
||||
}
|
||||
|
||||
const token = (authorization || '').replace(`${TOKEN_BEARER} `, '');
|
||||
@ -339,7 +334,7 @@ class Auth implements IAuth {
|
||||
try {
|
||||
credentials = verifyJWTPayload(token, this.config.secret);
|
||||
} catch (err) {
|
||||
// FIXME: intended behaviour, do we want it?
|
||||
// FIXME: intended behaviour, do we want it?
|
||||
}
|
||||
|
||||
if (credentials) {
|
||||
|
121
src/lib/bootstrap.js
vendored
121
src/lib/bootstrap.js
vendored
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import {assign, isObject, isFunction} from 'lodash';
|
||||
@ -24,17 +28,12 @@ const logger = require('./logger');
|
||||
* @param {String} pkgVersion
|
||||
* @param {String} pkgName
|
||||
*/
|
||||
function startVerdaccio(config: any,
|
||||
cliListen: string,
|
||||
configPath: string,
|
||||
pkgVersion: string,
|
||||
pkgName: string,
|
||||
callback: Callback) {
|
||||
function startVerdaccio(config: any, cliListen: string, configPath: string, pkgVersion: string, pkgName: string, callback: Callback) {
|
||||
if (isObject(config) === false) {
|
||||
throw new Error(API_ERROR.CONFIG_BAD_FORMAT);
|
||||
}
|
||||
|
||||
endPointAPI(config).then((app)=> {
|
||||
endPointAPI(config).then(app => {
|
||||
const addresses = getListListenAddresses(cliListen, config.listen);
|
||||
|
||||
addresses.forEach(function(addr) {
|
||||
@ -46,7 +45,8 @@ function startVerdaccio(config: any,
|
||||
}
|
||||
|
||||
webServer = handleHTTPS(app, configPath, config);
|
||||
} else { // http
|
||||
} else {
|
||||
// http
|
||||
webServer = http.createServer(app);
|
||||
}
|
||||
|
||||
@ -64,25 +64,31 @@ function unlinkAddressPath(addr) {
|
||||
}
|
||||
|
||||
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'));
|
||||
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);
|
||||
}
|
||||
|
||||
@ -106,41 +112,46 @@ function handleHTTPS(app, configPath, config) {
|
||||
});
|
||||
}
|
||||
return https.createServer(httpsOptions, app);
|
||||
} catch (err) { // catch errors related to certificate loading
|
||||
} catch (err) {
|
||||
// catch errors related to certificate loading
|
||||
logger.logger.fatal({err: err}, 'cannot create server: @{err.message}');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
function listenDefaultCallback(webServer: $Application, addr: any, pkgName: string, pkgVersion: string) {
|
||||
webServer.listen(addr.port || addr.path, addr.host, () => {
|
||||
// send a message for tests
|
||||
if (isFunction(process.send)) {
|
||||
process.send({
|
||||
verdaccio_started: true,
|
||||
});
|
||||
}
|
||||
// $FlowFixMe
|
||||
}).on('error', function(err) {
|
||||
logger.logger.fatal({err: err}, 'cannot create server: @{err.message}');
|
||||
process.exit(2);
|
||||
});
|
||||
webServer
|
||||
.listen(addr.port || addr.path, addr.host, () => {
|
||||
// send a message for tests
|
||||
if (isFunction(process.send)) {
|
||||
process.send({
|
||||
verdaccio_started: true,
|
||||
});
|
||||
}
|
||||
})
|
||||
// $FlowFixMe
|
||||
.on('error', function(err) {
|
||||
logger.logger.fatal({err: err}, 'cannot create server: @{err.message}');
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
logger.logger.warn({
|
||||
addr: ( addr.path
|
||||
? URL.format({
|
||||
protocol: 'unix',
|
||||
pathname: addr.path,
|
||||
})
|
||||
: URL.format({
|
||||
protocol: addr.proto,
|
||||
hostname: addr.host,
|
||||
port: addr.port,
|
||||
pathname: '/',
|
||||
})
|
||||
),
|
||||
version: pkgName + '/' + pkgVersion,
|
||||
}, 'http address - @{addr} - @{version}');
|
||||
logger.logger.warn(
|
||||
{
|
||||
addr: addr.path
|
||||
? URL.format({
|
||||
protocol: 'unix',
|
||||
pathname: addr.path,
|
||||
})
|
||||
: URL.format({
|
||||
protocol: addr.proto,
|
||||
hostname: addr.host,
|
||||
port: addr.port,
|
||||
pathname: '/',
|
||||
}),
|
||||
version: pkgName + '/' + pkgVersion,
|
||||
},
|
||||
'http address - @{addr} - @{version}'
|
||||
);
|
||||
}
|
||||
|
||||
export {startVerdaccio, listenDefaultCallback};
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
/* eslint no-sync:0 */
|
||||
/* eslint no-empty:0 */
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import semver from 'semver';
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import path from 'path';
|
||||
@ -34,18 +38,20 @@ export function getListListenAddresses(argListen: string, configListen: mixed) {
|
||||
} else {
|
||||
addresses = [DEFAULT_PORT];
|
||||
}
|
||||
addresses = addresses.map(function(addr) {
|
||||
const parsedAddr = parseAddress(addr);
|
||||
addresses = addresses
|
||||
.map(function(addr) {
|
||||
const parsedAddr = parseAddress(addr);
|
||||
|
||||
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/")');
|
||||
}
|
||||
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/")'
|
||||
);
|
||||
}
|
||||
|
||||
return parsedAddr;
|
||||
}).filter(Boolean);
|
||||
return parsedAddr;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import fs from 'fs';
|
||||
@ -58,29 +62,28 @@ function createConfigFolder(configLocation) {
|
||||
}
|
||||
|
||||
function updateStorageLinks(configLocation, defaultConfig) {
|
||||
if (configLocation.type !== XDG) {
|
||||
return defaultConfig;
|
||||
}
|
||||
if (configLocation.type !== XDG) {
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored,
|
||||
// If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
|
||||
// $FlowFixMe
|
||||
let dataDir = process.env.XDG_DATA_HOME || Path.join(process.env.HOME, '.local', 'share');
|
||||
if (folderExists(dataDir)) {
|
||||
dataDir = Path.resolve(Path.join(dataDir, pkgJSON.name, 'storage'));
|
||||
return defaultConfig.replace(/^storage: .\/storage$/m, `storage: ${dataDir}`);
|
||||
} else {
|
||||
return defaultConfig;
|
||||
}
|
||||
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored,
|
||||
// If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
|
||||
// $FlowFixMe
|
||||
let dataDir = process.env.XDG_DATA_HOME || Path.join(process.env.HOME, '.local', 'share');
|
||||
if (folderExists(dataDir)) {
|
||||
dataDir = Path.resolve(Path.join(dataDir, pkgJSON.name, 'storage'));
|
||||
return defaultConfig.replace(/^storage: .\/storage$/m, `storage: ${dataDir}`);
|
||||
} else {
|
||||
return defaultConfig;
|
||||
}
|
||||
}
|
||||
|
||||
function getConfigPaths() {
|
||||
return [getXDGDirectory(), getWindowsDirectory(), getRelativeDefaultDirectory(), getOldDirectory()].filter((path) => !!path);
|
||||
return [getXDGDirectory(), getWindowsDirectory(), getRelativeDefaultDirectory(), getOldDirectory()].filter(path => !!path);
|
||||
}
|
||||
|
||||
const getXDGDirectory = () => {
|
||||
const XDGConfig = getXDGHome() ||
|
||||
process.env.HOME && Path.join(process.env.HOME, '.config');
|
||||
const XDGConfig = getXDGHome() || (process.env.HOME && Path.join(process.env.HOME, '.config'));
|
||||
|
||||
if (XDGConfig && folderExists(XDGConfig)) {
|
||||
return {
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
import _ from 'lodash';
|
||||
import assert from 'assert';
|
||||
@ -24,7 +28,7 @@ export function normalizeUserlist(oldFormat: any, newFormat: any) {
|
||||
const result = [];
|
||||
/* eslint prefer-rest-params: "off" */
|
||||
|
||||
for (let i=0; i < arguments.length; i++) {
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
if (arguments[i] == null) {
|
||||
continue;
|
||||
}
|
||||
@ -72,7 +76,7 @@ export function sanityCheckUplinksProps(configUpLinks: any) {
|
||||
for (let uplink in uplinks) {
|
||||
if (Object.prototype.hasOwnProperty.call(uplinks, uplink)) {
|
||||
assert(uplinks[uplink].url, 'CONFIG: no url for uplink: ' + uplink);
|
||||
assert( _.isString(uplinks[uplink].url), 'CONFIG: wrong url format for uplink: ' + uplink);
|
||||
assert(_.isString(uplinks[uplink].url), 'CONFIG: wrong url format for uplink: ' + uplink);
|
||||
uplinks[uplink].url = uplinks[uplink].url.replace(/\/$/, '');
|
||||
}
|
||||
}
|
||||
@ -87,7 +91,7 @@ export function hasProxyTo(pkg: string, upLink: string, packages: PackageList):
|
||||
const matchedPkg: MatchedPackage = (getMatchedPackagesSpec(pkg, packages): MatchedPackage);
|
||||
const proxyList = typeof matchedPkg !== 'undefined' ? matchedPkg.proxy : [];
|
||||
if (proxyList) {
|
||||
return proxyList.some((curr) => upLink === curr);
|
||||
return proxyList.some(curr => upLink === curr);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -112,8 +116,7 @@ export function normalisePackageAccess(packages: PackageList): PackageList {
|
||||
|
||||
for (let pkg in packages) {
|
||||
if (Object.prototype.hasOwnProperty.call(packages, pkg)) {
|
||||
assert(_.isObject(packages[pkg]) && _.isArray(packages[pkg]) === false,
|
||||
`CONFIG: bad "'${pkg}'" package description (object expected)`);
|
||||
assert(_.isObject(packages[pkg]) && _.isArray(packages[pkg]) === false, `CONFIG: bad "'${pkg}'" package description (object expected)`);
|
||||
normalizedPkgs[pkg].access = normalizeUserlist(packages[pkg].allow_access, packages[pkg].access);
|
||||
delete normalizedPkgs[pkg].allow_access;
|
||||
normalizedPkgs[pkg].publish = normalizeUserlist(packages[pkg].allow_publish, packages[pkg].publish);
|
||||
@ -125,4 +128,3 @@ export function normalisePackageAccess(packages: PackageList): PackageList {
|
||||
|
||||
return normalizedPkgs;
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,18 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
import assert from 'assert';
|
||||
|
||||
import {generateRandomHexString} from './crypto-utils';
|
||||
import {
|
||||
getMatchedPackagesSpec,
|
||||
normalisePackageAccess,
|
||||
sanityCheckUplinksProps,
|
||||
uplinkSanityCheck} from './config-utils';
|
||||
import {getMatchedPackagesSpec, normalisePackageAccess, sanityCheckUplinksProps, uplinkSanityCheck} from './config-utils';
|
||||
import {getUserAgent, isObject} from './utils';
|
||||
import {APP_ERROR} from './constants';
|
||||
|
||||
import type {
|
||||
PackageList,
|
||||
Config as AppConfig,
|
||||
Security,
|
||||
Logger,
|
||||
} from '@verdaccio/types';
|
||||
import type {PackageList, Config as AppConfig, Security, Logger} from '@verdaccio/types';
|
||||
|
||||
import type {MatchedPackage, StartUpConfig} from '../../types';
|
||||
|
||||
@ -82,7 +77,7 @@ class Config implements AppConfig {
|
||||
this.packages = normalisePackageAccess(self.packages);
|
||||
|
||||
// loading these from ENV if aren't in config
|
||||
allowedEnvConfig.forEach((envConf) => {
|
||||
allowedEnvConfig.forEach(envConf => {
|
||||
if (!(envConf in self)) {
|
||||
self[envConf] = process.env[envConf] || process.env[envConf.toUpperCase()];
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
export const DEFAULT_PORT: string = '4873';
|
||||
export const DEFAULT_PROTOCOL: string = 'http';
|
||||
export const DEFAULT_DOMAIN: string = 'localhost';
|
||||
export const TIME_EXPIRATION_24H: string ='24h';
|
||||
export const TIME_EXPIRATION_24H: string = '24h';
|
||||
export const TIME_EXPIRATION_7D: string = '7d';
|
||||
export const DIST_TAGS = 'dist-tags';
|
||||
|
||||
@ -24,8 +28,8 @@ export const HEADERS = {
|
||||
};
|
||||
|
||||
export const CHARACTER_ENCODING = {
|
||||
UTF8: 'utf8'
|
||||
}
|
||||
UTF8: 'utf8',
|
||||
};
|
||||
|
||||
export const HEADER_TYPE = {
|
||||
CONTENT_ENCODING: 'content-encoding',
|
||||
@ -95,9 +99,9 @@ export const API_ERROR = {
|
||||
UPLINK_OFFLINE_PUBLISH: 'one of the uplinks is down, refuse to publish',
|
||||
UPLINK_OFFLINE: 'uplink is offline',
|
||||
CONTENT_MISMATCH: 'content length mismatch',
|
||||
NOT_FILE_UPLINK: 'file doesn\'t exist on uplink',
|
||||
NOT_FILE_UPLINK: "file doesn't exist on uplink",
|
||||
MAX_USERS_REACHED: 'maximum amount of users reached',
|
||||
VERSION_NOT_EXIST: 'this version doesn\'t exist',
|
||||
VERSION_NOT_EXIST: "this version doesn't exist",
|
||||
FILE_NOT_FOUND: 'File not found',
|
||||
BAD_STATUS_CODE: 'bad status code',
|
||||
PACKAGE_EXIST: 'this package is already present',
|
||||
@ -123,5 +127,5 @@ export const PACKAGE_ACCESS = {
|
||||
};
|
||||
|
||||
export const UPDATE_BANNER = {
|
||||
CHANGELOG_URL: 'https://github.com/verdaccio/verdaccio/releases/tag/'
|
||||
}
|
||||
CHANGELOG_URL: 'https://github.com/verdaccio/verdaccio/releases/tag/',
|
||||
};
|
||||
|
@ -1,11 +1,10 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import {
|
||||
createDecipher,
|
||||
createCipher,
|
||||
createHash,
|
||||
pseudoRandomBytes,
|
||||
} from 'crypto';
|
||||
import {createDecipher, createCipher, createHash, pseudoRandomBytes} from 'crypto';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
import type {JWTSignOptions, RemoteUser} from '@verdaccio/types';
|
||||
@ -22,7 +21,6 @@ export function aesEncrypt(buf: Buffer, secret: string): Buffer {
|
||||
return Buffer.concat([b1, b2]);
|
||||
}
|
||||
|
||||
|
||||
export function aesDecrypt(buf: Buffer, secret: string) {
|
||||
try {
|
||||
// deprecated
|
||||
@ -48,22 +46,26 @@ export function createTarballHash() {
|
||||
* @return {String}
|
||||
*/
|
||||
export function stringToMD5(data: Buffer | string) {
|
||||
return createHash('md5').update(data).digest('hex');
|
||||
return createHash('md5')
|
||||
.update(data)
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
export function generateRandomHexString(length: number = 8) {
|
||||
return pseudoRandomBytes(length).toString('hex');
|
||||
}
|
||||
|
||||
export async function signPayload(
|
||||
payload: RemoteUser,
|
||||
secretOrPrivateKey: string,
|
||||
options: JWTSignOptions): Promise<string> {
|
||||
export async function signPayload(payload: RemoteUser, secretOrPrivateKey: string, options: JWTSignOptions): Promise<string> {
|
||||
return new Promise(function(resolve, reject) {
|
||||
return jwt.sign(payload, secretOrPrivateKey, {
|
||||
notBefore: '1', // Make sure the time will not rollback :)
|
||||
...options,
|
||||
}, (error, token) => error ? reject(error) : resolve(token));
|
||||
return jwt.sign(
|
||||
payload,
|
||||
secretOrPrivateKey,
|
||||
{
|
||||
notBefore: '1', // Make sure the time will not rollback :)
|
||||
...options,
|
||||
},
|
||||
(error, token) => (error ? reject(error) : resolve(token))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
/* eslint prefer-rest-params: 0 */
|
||||
@ -8,31 +12,25 @@ import _ from 'lodash';
|
||||
// $FlowFixMe
|
||||
import {ErrorCode, isObject, getLatestVersion, tagVersion, validateName, DIST_TAGS} from './utils';
|
||||
import {
|
||||
generatePackageTemplate, normalizePackage, generateRevision, getLatestReadme, cleanUpReadme, normalizeContributors,
|
||||
fileExist, noSuchFile, DEFAULT_REVISION, pkgFileName,
|
||||
generatePackageTemplate,
|
||||
normalizePackage,
|
||||
generateRevision,
|
||||
getLatestReadme,
|
||||
cleanUpReadme,
|
||||
normalizeContributors,
|
||||
fileExist,
|
||||
noSuchFile,
|
||||
DEFAULT_REVISION,
|
||||
pkgFileName,
|
||||
} from './storage-utils';
|
||||
import {createTarballHash} from './crypto-utils';
|
||||
import {prepareSearchPackage} from './storage-utils';
|
||||
import loadPlugin from '../lib/plugin-loader';
|
||||
import LocalDatabase from '@verdaccio/local-storage';
|
||||
import {UploadTarball, ReadTarball} from '@verdaccio/streams';
|
||||
import type {
|
||||
Package,
|
||||
Config,
|
||||
MergeTags,
|
||||
Version,
|
||||
DistFile,
|
||||
Callback,
|
||||
Logger,
|
||||
} from '@verdaccio/types';
|
||||
import type {
|
||||
ILocalData,
|
||||
IPackageStorage,
|
||||
} from '@verdaccio/local-storage';
|
||||
import type {
|
||||
IUploadTarball,
|
||||
IReadTarball,
|
||||
} from '@verdaccio/streams';
|
||||
import type {Package, Config, MergeTags, Version, DistFile, Callback, Logger} from '@verdaccio/types';
|
||||
import type {ILocalData, IPackageStorage} from '@verdaccio/local-storage';
|
||||
import type {IUploadTarball, IReadTarball} from '@verdaccio/streams';
|
||||
import type {IStorage, StringValue} from '../../types';
|
||||
import {API_ERROR} from './constants';
|
||||
|
||||
@ -54,12 +52,12 @@ class LocalStorage implements IStorage {
|
||||
const storage: any = this._getLocalStorage(name);
|
||||
|
||||
if (_.isNil(storage)) {
|
||||
return callback( ErrorCode.getNotFound('this package cannot be added'));
|
||||
return callback(ErrorCode.getNotFound('this package cannot be added'));
|
||||
}
|
||||
|
||||
storage.createPackage(name, generatePackageTemplate(name), (err) => {
|
||||
storage.createPackage(name, generatePackageTemplate(name), err => {
|
||||
if (_.isNull(err) === false && err.code === fileExist) {
|
||||
return callback( ErrorCode.getConflict());
|
||||
return callback(ErrorCode.getConflict());
|
||||
}
|
||||
|
||||
const latest = getLatestVersion(pkg);
|
||||
@ -81,13 +79,13 @@ class LocalStorage implements IStorage {
|
||||
let storage: any = this._getLocalStorage(name);
|
||||
|
||||
if (_.isNil(storage)) {
|
||||
return callback( ErrorCode.getNotFound());
|
||||
return callback(ErrorCode.getNotFound());
|
||||
}
|
||||
|
||||
storage.readPackage(name, (err, data) => {
|
||||
if (_.isNil(err) === false) {
|
||||
if (err.code === noSuchFile) {
|
||||
return callback( ErrorCode.getNotFound());
|
||||
return callback(ErrorCode.getNotFound());
|
||||
} else {
|
||||
return callback(err);
|
||||
}
|
||||
@ -95,13 +93,13 @@ class LocalStorage implements IStorage {
|
||||
|
||||
data = normalizePackage(data);
|
||||
|
||||
this.localData.remove(name, (removeFailed) => {
|
||||
this.localData.remove(name, removeFailed => {
|
||||
if (removeFailed) {
|
||||
// This will happen when database is locked
|
||||
return callback(ErrorCode.getBadData(removeFailed.message));
|
||||
}
|
||||
|
||||
storage.deletePackage(pkgFileName, (err) => {
|
||||
storage.deletePackage(pkgFileName, err => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@ -149,10 +147,10 @@ class LocalStorage implements IStorage {
|
||||
|
||||
// we do NOT overwrite any existing records
|
||||
if (_.isNil(packageLocalJson._distfiles[filename])) {
|
||||
let hash: DistFile = packageLocalJson._distfiles[filename] = {
|
||||
let hash: DistFile = (packageLocalJson._distfiles[filename] = {
|
||||
url: version.dist.tarball,
|
||||
sha: version.dist.shasum,
|
||||
};
|
||||
});
|
||||
/* eslint spaced-comment: 0 */
|
||||
// $FlowFixMe
|
||||
const upLink: string = version[Symbol.for('__verdaccio_uplink')];
|
||||
@ -174,9 +172,10 @@ class LocalStorage implements IStorage {
|
||||
|
||||
for (let up in packageInfo._uplinks) {
|
||||
if (Object.prototype.hasOwnProperty.call(packageInfo._uplinks, up)) {
|
||||
const need_change = !isObject(packageLocalJson._uplinks[up])
|
||||
|| packageInfo._uplinks[up].etag !== packageLocalJson._uplinks[up].etag
|
||||
|| packageInfo._uplinks[up].fetched !== packageLocalJson._uplinks[up].fetched;
|
||||
const need_change =
|
||||
!isObject(packageLocalJson._uplinks[up]) ||
|
||||
packageInfo._uplinks[up].etag !== packageLocalJson._uplinks[up].etag ||
|
||||
packageInfo._uplinks[up].fetched !== packageLocalJson._uplinks[up].fetched;
|
||||
|
||||
if (need_change) {
|
||||
change = true;
|
||||
@ -209,64 +208,64 @@ class LocalStorage implements IStorage {
|
||||
* @param {*} tag
|
||||
* @param {*} callback
|
||||
*/
|
||||
addVersion(name: string,
|
||||
version: string,
|
||||
metadata: Version,
|
||||
tag: StringValue,
|
||||
callback: Callback) {
|
||||
this._updatePackage(name, (data, cb) => {
|
||||
// keep only one readme per package
|
||||
data.readme = metadata.readme;
|
||||
addVersion(name: string, version: string, metadata: Version, tag: StringValue, callback: Callback) {
|
||||
this._updatePackage(
|
||||
name,
|
||||
(data, cb) => {
|
||||
// keep only one readme per package
|
||||
data.readme = metadata.readme;
|
||||
|
||||
// TODO: lodash remove
|
||||
metadata = cleanUpReadme(metadata);
|
||||
metadata.contributors = normalizeContributors(metadata.contributors);
|
||||
// TODO: lodash remove
|
||||
metadata = cleanUpReadme(metadata);
|
||||
metadata.contributors = normalizeContributors(metadata.contributors);
|
||||
|
||||
if (data.versions[version] != null) {
|
||||
return cb( ErrorCode.getConflict() );
|
||||
}
|
||||
if (data.versions[version] != null) {
|
||||
return cb(ErrorCode.getConflict());
|
||||
}
|
||||
|
||||
// if uploaded tarball has a different shasum, it's very likely that we have some kind of error
|
||||
if (isObject(metadata.dist) && _.isString(metadata.dist.tarball)) {
|
||||
let tarball = metadata.dist.tarball.replace(/.*\//, '');
|
||||
// if uploaded tarball has a different shasum, it's very likely that we have some kind of error
|
||||
if (isObject(metadata.dist) && _.isString(metadata.dist.tarball)) {
|
||||
let tarball = metadata.dist.tarball.replace(/.*\//, '');
|
||||
|
||||
if (isObject(data._attachments[tarball])) {
|
||||
if (_.isNil(data._attachments[tarball].shasum) === false && _.isNil(metadata.dist.shasum) === false) {
|
||||
if (data._attachments[tarball].shasum != metadata.dist.shasum) {
|
||||
const errorMessage = `shasum error, ${data._attachments[tarball].shasum} != ${metadata.dist.shasum}`;
|
||||
return cb( ErrorCode.getBadRequest(errorMessage) );
|
||||
if (isObject(data._attachments[tarball])) {
|
||||
if (_.isNil(data._attachments[tarball].shasum) === false && _.isNil(metadata.dist.shasum) === false) {
|
||||
if (data._attachments[tarball].shasum != metadata.dist.shasum) {
|
||||
const errorMessage = `shasum error, ${data._attachments[tarball].shasum} != ${metadata.dist.shasum}`;
|
||||
return cb(ErrorCode.getBadRequest(errorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
let currentDate = new Date().toISOString();
|
||||
|
||||
// some old storage do not have this field #740
|
||||
if (_.isNil(data.time)) {
|
||||
data.time = {};
|
||||
}
|
||||
|
||||
data.time['modified'] = currentDate;
|
||||
|
||||
if ('created' in data.time === false) {
|
||||
data.time.created = currentDate;
|
||||
}
|
||||
|
||||
data.time[version] = currentDate;
|
||||
data._attachments[tarball].version = version;
|
||||
}
|
||||
|
||||
let currentDate = new Date().toISOString();
|
||||
|
||||
// some old storage do not have this field #740
|
||||
if (_.isNil(data.time)) {
|
||||
data.time = {};
|
||||
}
|
||||
|
||||
data.time['modified'] = currentDate;
|
||||
|
||||
if (('created' in data.time) === false) {
|
||||
data.time.created = currentDate;
|
||||
}
|
||||
|
||||
data.time[version] = currentDate;
|
||||
data._attachments[tarball].version = version;
|
||||
}
|
||||
}
|
||||
|
||||
data.versions[version] = metadata;
|
||||
tagVersion(data, version, tag);
|
||||
|
||||
this.localData.add(name, (addFailed) => {
|
||||
if (addFailed) {
|
||||
return cb(ErrorCode.getBadData(addFailed.message));
|
||||
}
|
||||
|
||||
cb();
|
||||
});
|
||||
}, callback);
|
||||
data.versions[version] = metadata;
|
||||
tagVersion(data, version, tag);
|
||||
|
||||
this.localData.add(name, addFailed => {
|
||||
if (addFailed) {
|
||||
return cb(ErrorCode.getBadData(addFailed.message));
|
||||
}
|
||||
|
||||
cb();
|
||||
});
|
||||
},
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,23 +275,27 @@ class LocalStorage implements IStorage {
|
||||
* @param {*} callback
|
||||
*/
|
||||
mergeTags(pkgName: string, tags: MergeTags, callback: Callback) {
|
||||
this._updatePackage(pkgName, (data, cb) => {
|
||||
/* eslint guard-for-in: 0 */
|
||||
for (let tag: string in tags) {
|
||||
// this handle dist-tag rm command
|
||||
if (_.isNull(tags[tag])) {
|
||||
delete data[DIST_TAGS][tag];
|
||||
continue;
|
||||
}
|
||||
this._updatePackage(
|
||||
pkgName,
|
||||
(data, cb) => {
|
||||
/* eslint guard-for-in: 0 */
|
||||
for (let tag: string in tags) {
|
||||
// this handle dist-tag rm command
|
||||
if (_.isNull(tags[tag])) {
|
||||
delete data[DIST_TAGS][tag];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_.isNil(data.versions[tags[tag]])) {
|
||||
return cb( this._getVersionNotFound() );
|
||||
if (_.isNil(data.versions[tags[tag]])) {
|
||||
return cb(this._getVersionNotFound());
|
||||
}
|
||||
const version: string = tags[tag];
|
||||
tagVersion(data, version, tag);
|
||||
}
|
||||
const version: string = tags[tag];
|
||||
tagVersion(data, version, tag);
|
||||
}
|
||||
cb();
|
||||
}, callback);
|
||||
cb();
|
||||
},
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -322,37 +325,39 @@ class LocalStorage implements IStorage {
|
||||
* @param {*} callback
|
||||
* @return {Function}
|
||||
*/
|
||||
changePackage(name: string,
|
||||
incomingPkg: Package,
|
||||
revision?: string, callback: Callback) {
|
||||
changePackage(name: string, incomingPkg: Package, revision?: string, callback: Callback) {
|
||||
if (!isObject(incomingPkg.versions) || !isObject(incomingPkg[DIST_TAGS])) {
|
||||
return callback( ErrorCode.getBadData());
|
||||
return callback(ErrorCode.getBadData());
|
||||
}
|
||||
|
||||
this._updatePackage(name, (localData, cb) => {
|
||||
for (let version in localData.versions) {
|
||||
if (_.isNil(incomingPkg.versions[version])) {
|
||||
this.logger.info( {name: name, version: version}, 'unpublishing @{name}@@{version}');
|
||||
this._updatePackage(
|
||||
name,
|
||||
(localData, cb) => {
|
||||
for (let version in localData.versions) {
|
||||
if (_.isNil(incomingPkg.versions[version])) {
|
||||
this.logger.info({name: name, version: version}, 'unpublishing @{name}@@{version}');
|
||||
|
||||
delete localData.versions[version];
|
||||
delete localData.time[version];
|
||||
delete localData.versions[version];
|
||||
delete localData.time[version];
|
||||
|
||||
for (let file in localData._attachments) {
|
||||
if (localData._attachments[file].version === version) {
|
||||
delete localData._attachments[file].version;
|
||||
for (let file in localData._attachments) {
|
||||
if (localData._attachments[file].version === version) {
|
||||
delete localData._attachments[file].version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
localData[DIST_TAGS] = incomingPkg[DIST_TAGS];
|
||||
cb();
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
localData[DIST_TAGS] = incomingPkg[DIST_TAGS];
|
||||
cb();
|
||||
},
|
||||
function(err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback();
|
||||
}
|
||||
callback();
|
||||
});
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Remove a tarball.
|
||||
@ -361,27 +366,30 @@ class LocalStorage implements IStorage {
|
||||
* @param {*} revision
|
||||
* @param {*} callback
|
||||
*/
|
||||
removeTarball(name: string, filename: string,
|
||||
revision: string, callback: Callback) {
|
||||
removeTarball(name: string, filename: string, revision: string, callback: Callback) {
|
||||
assert(validateName(filename));
|
||||
|
||||
this._updatePackage(name, (data, cb) => {
|
||||
if (data._attachments[filename]) {
|
||||
delete data._attachments[filename];
|
||||
cb();
|
||||
} else {
|
||||
cb(this._getFileNotAvailable());
|
||||
}
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
const storage = this._getLocalStorage(name);
|
||||
this._updatePackage(
|
||||
name,
|
||||
(data, cb) => {
|
||||
if (data._attachments[filename]) {
|
||||
delete data._attachments[filename];
|
||||
cb();
|
||||
} else {
|
||||
cb(this._getFileNotAvailable());
|
||||
}
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
const storage = this._getLocalStorage(name);
|
||||
|
||||
if (storage) {
|
||||
storage.deletePackage(filename, callback);
|
||||
if (storage) {
|
||||
storage.deletePackage(filename, callback);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -418,14 +426,14 @@ class LocalStorage implements IStorage {
|
||||
|
||||
if (!storage) {
|
||||
process.nextTick(() => {
|
||||
uploadStream.emit('error', ('can\'t upload this package'));
|
||||
uploadStream.emit('error', "can't upload this package");
|
||||
});
|
||||
return uploadStream;
|
||||
}
|
||||
|
||||
const writeStream: IUploadTarball = storage.writeTarball(filename);
|
||||
|
||||
writeStream.on('error', (err) => {
|
||||
writeStream.on('error', err => {
|
||||
if (err.code === fileExist) {
|
||||
uploadStream.emit('error', ErrorCode.getConflict());
|
||||
uploadStream.abort();
|
||||
@ -449,18 +457,22 @@ class LocalStorage implements IStorage {
|
||||
});
|
||||
|
||||
writeStream.on('success', () => {
|
||||
this._updatePackage(name, function updater(data, cb) {
|
||||
data._attachments[filename] = {
|
||||
shasum: shaOneHash.digest('hex'),
|
||||
};
|
||||
cb();
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
uploadStream.emit('error', err);
|
||||
} else {
|
||||
uploadStream.emit('success');
|
||||
this._updatePackage(
|
||||
name,
|
||||
function updater(data, cb) {
|
||||
data._attachments[filename] = {
|
||||
shasum: shaOneHash.digest('hex'),
|
||||
};
|
||||
cb();
|
||||
},
|
||||
function(err) {
|
||||
if (err) {
|
||||
uploadStream.emit('error', err);
|
||||
} else {
|
||||
uploadStream.emit('success');
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
(uploadStream: any).abort = function() {
|
||||
@ -561,7 +573,7 @@ class LocalStorage implements IStorage {
|
||||
getPackageMetadata(name: string, callback?: Callback = () => {}): void {
|
||||
const storage: IPackageStorage = this._getLocalStorage(name);
|
||||
if (_.isNil(storage)) {
|
||||
return callback( ErrorCode.getNotFound() );
|
||||
return callback(ErrorCode.getNotFound());
|
||||
}
|
||||
|
||||
this._readPackage(name, storage, callback);
|
||||
@ -576,28 +588,31 @@ class LocalStorage implements IStorage {
|
||||
search(startKey: string, options: any) {
|
||||
const stream = new ReadTarball({objectMode: true});
|
||||
|
||||
this._searchEachPackage((item, cb) => {
|
||||
if (item.time > parseInt(startKey, 10)) {
|
||||
this.getPackageMetadata(item.name, (err: Error, data: Package) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
const time = new Date(item.time).toISOString();
|
||||
const result = prepareSearchPackage(data, time);
|
||||
if (_.isNil(result) === false) {
|
||||
stream.push(result);
|
||||
}
|
||||
this._searchEachPackage(
|
||||
(item, cb) => {
|
||||
if (item.time > parseInt(startKey, 10)) {
|
||||
this.getPackageMetadata(item.name, (err: Error, data: Package) => {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
const time = new Date(item.time).toISOString();
|
||||
const result = prepareSearchPackage(data, time);
|
||||
if (_.isNil(result) === false) {
|
||||
stream.push(result);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
} else {
|
||||
cb();
|
||||
});
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
},
|
||||
function onEnd(err) {
|
||||
if (err) {
|
||||
return stream.emit('error', err);
|
||||
}
|
||||
stream.end();
|
||||
}
|
||||
}, function onEnd(err) {
|
||||
if (err) {
|
||||
return stream.emit('error', err);
|
||||
}
|
||||
stream.end();
|
||||
});
|
||||
);
|
||||
|
||||
return stream;
|
||||
}
|
||||
@ -620,7 +635,7 @@ class LocalStorage implements IStorage {
|
||||
storage.readPackage(name, (err, result) => {
|
||||
if (err) {
|
||||
if (err.code === noSuchFile) {
|
||||
return callback( ErrorCode.getNotFound() );
|
||||
return callback(ErrorCode.getNotFound());
|
||||
} else {
|
||||
return callback(this._internalError(err, pkgFileName, 'error reading'));
|
||||
}
|
||||
@ -643,7 +658,7 @@ class LocalStorage implements IStorage {
|
||||
if (packages) {
|
||||
const listPackagesConf = Object.keys(packages || {});
|
||||
|
||||
listPackagesConf.map( (pkg) => {
|
||||
listPackagesConf.map(pkg => {
|
||||
if (packages[pkg].storage) {
|
||||
storages[packages[pkg].storage] = true;
|
||||
}
|
||||
@ -706,7 +721,7 @@ class LocalStorage implements IStorage {
|
||||
* @return {Object} Error instance
|
||||
*/
|
||||
_internalError(err: string, file: string, message: string) {
|
||||
this.logger.error( {err: err, file: file}, `${message} @{file}: @{!err.message}` );
|
||||
this.logger.error({err: err, file: file}, `${message} @{file}: @{!err.message}`);
|
||||
|
||||
return ErrorCode.getInternalError();
|
||||
}
|
||||
@ -721,11 +736,10 @@ class LocalStorage implements IStorage {
|
||||
const storage: IPackageStorage = this._getLocalStorage(name);
|
||||
|
||||
if (!storage) {
|
||||
return callback( ErrorCode.getNotFound() );
|
||||
return callback(ErrorCode.getNotFound());
|
||||
}
|
||||
|
||||
storage.updatePackage(name, updateHandler, this._writePackage.bind(this), normalizePackage,
|
||||
callback);
|
||||
storage.updatePackage(name, updateHandler, this._writePackage.bind(this), normalizePackage, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -821,9 +835,11 @@ class LocalStorage implements IStorage {
|
||||
logger: this.logger,
|
||||
};
|
||||
|
||||
return _.head(loadPlugin(this.config, this.config.store, plugin_params, (plugin) => {
|
||||
return plugin.getPackageStorage;
|
||||
}));
|
||||
return _.head(
|
||||
loadPlugin(this.config, this.config.store, plugin_params, plugin => {
|
||||
return plugin.getPackageStorage;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
const cluster = require('cluster');
|
||||
const Logger = require('bunyan');
|
||||
const Error = require('http-errors');
|
||||
@ -15,20 +19,28 @@ const {format} = require('date-fns');
|
||||
*/
|
||||
function getlvl(x) {
|
||||
switch (true) {
|
||||
case x < 15: return 'trace';
|
||||
case x < 25: return 'debug';
|
||||
case x < 35: return 'info';
|
||||
case x == 35: return 'http';
|
||||
case x < 45: return 'warn';
|
||||
case x < 55: return 'error';
|
||||
default: return 'fatal';
|
||||
case x < 15:
|
||||
return 'trace';
|
||||
case x < 25:
|
||||
return 'debug';
|
||||
case x < 35:
|
||||
return 'info';
|
||||
case x == 35:
|
||||
return 'http';
|
||||
case x < 45:
|
||||
return 'warn';
|
||||
case x < 55:
|
||||
return 'error';
|
||||
default:
|
||||
return 'fatal';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A RotatingFileStream that modifes the message first
|
||||
*/
|
||||
class VerdaccioRotatingFileStream extends Logger.RotatingFileStream { // We depend on mv so that this is there
|
||||
class VerdaccioRotatingFileStream extends Logger.RotatingFileStream {
|
||||
// We depend on mv so that this is there
|
||||
write(obj) {
|
||||
const msg = fillInMsgTemplate(obj.msg, obj, false);
|
||||
super.write(JSON.stringify({...obj, msg}, Logger.safeCycles()) + '\n');
|
||||
@ -62,19 +74,19 @@ function setup(logs) {
|
||||
}
|
||||
|
||||
const stream = new VerdaccioRotatingFileStream(
|
||||
_.merge({},
|
||||
_.merge(
|
||||
{},
|
||||
// Defaults can be found here: https://github.com/trentm/node-bunyan#stream-type-rotating-file
|
||||
target.options || {},
|
||||
{path: target.path, level})
|
||||
{path: target.path, level}
|
||||
)
|
||||
);
|
||||
|
||||
streams.push(
|
||||
{
|
||||
type: 'raw',
|
||||
level,
|
||||
stream,
|
||||
}
|
||||
);
|
||||
streams.push({
|
||||
type: 'raw',
|
||||
level,
|
||||
stream,
|
||||
});
|
||||
} else {
|
||||
const stream = new Stream();
|
||||
stream.writable = true;
|
||||
@ -96,16 +108,16 @@ function setup(logs) {
|
||||
|
||||
if (target.format === 'pretty') {
|
||||
// making fake stream for prettypritting
|
||||
stream.write = (obj) => {
|
||||
stream.write = obj => {
|
||||
destination.write(`${print(obj.level, obj.msg, obj, destinationIsTTY)}\n`);
|
||||
};
|
||||
} else if (target.format === 'pretty-timestamped') {
|
||||
// making fake stream for pretty pritting
|
||||
stream.write = (obj) => {
|
||||
stream.write = obj => {
|
||||
destination.write(`[${format(obj.time, 'YYYY-MM-DD HH:mm:ss')}] ${print(obj.level, obj.msg, obj, destinationIsTTY)}\n`);
|
||||
};
|
||||
} else {
|
||||
stream.write = (obj) => {
|
||||
stream.write = obj => {
|
||||
const msg = fillInMsgTemplate(obj.msg, obj, destinationIsTTY);
|
||||
destination.write(`${JSON.stringify({...obj, msg}, Logger.safeCycles())}\n`);
|
||||
};
|
||||
@ -130,9 +142,9 @@ function setup(logs) {
|
||||
},
|
||||
});
|
||||
|
||||
process.on('SIGUSR2', function() {
|
||||
Logger.reopenFileStreams();
|
||||
});
|
||||
process.on('SIGUSR2', function() {
|
||||
Logger.reopenFileStreams();
|
||||
});
|
||||
|
||||
module.exports.logger = logger;
|
||||
}
|
||||
@ -190,7 +202,7 @@ function fillInMsgTemplate(msg, obj, colors) {
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(str) === 'string') {
|
||||
if (typeof str === 'string') {
|
||||
if (!colors || str.includes('\n')) {
|
||||
return str;
|
||||
} else if (is_error) {
|
||||
@ -218,23 +230,26 @@ function print(type, msg, obj, colors) {
|
||||
}
|
||||
const finalmsg = fillInMsgTemplate(msg, obj, colors);
|
||||
|
||||
const subsystems = [{
|
||||
in: chalk.green('<--'),
|
||||
out: chalk.yellow('-->'),
|
||||
fs: chalk.black('-=-'),
|
||||
default: chalk.blue('---'),
|
||||
}, {
|
||||
in: '<--',
|
||||
out: '-->',
|
||||
fs: '-=-',
|
||||
default: '---',
|
||||
}];
|
||||
const subsystems = [
|
||||
{
|
||||
in: chalk.green('<--'),
|
||||
out: chalk.yellow('-->'),
|
||||
fs: chalk.black('-=-'),
|
||||
default: chalk.blue('---'),
|
||||
},
|
||||
{
|
||||
in: '<--',
|
||||
out: '-->',
|
||||
fs: '-=-',
|
||||
default: '---',
|
||||
},
|
||||
];
|
||||
|
||||
const sub = subsystems[colors ? 0 : 1][obj.sub] || subsystems[+!colors].default;
|
||||
if (colors) {
|
||||
return ` ${levels[type]((pad(type)))}${chalk.white(`${sub} ${finalmsg}`)}`;
|
||||
return ` ${levels[type](pad(type))}${chalk.white(`${sub} ${finalmsg}`)}`;
|
||||
} else {
|
||||
return ` ${(pad(type))}${sub} ${finalmsg}`;
|
||||
return ` ${pad(type)}${sub} ${finalmsg}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import semver from 'semver';
|
||||
@ -14,24 +18,24 @@ import type {Package} from '@verdaccio/types';
|
||||
* @param {*} config
|
||||
* @static
|
||||
*/
|
||||
export function mergeVersions(local: Package, up: Package) {
|
||||
// copy new versions to a cache
|
||||
// NOTE: if a certain version was updated, we can't refresh it reliably
|
||||
for (let i in up.versions) {
|
||||
if (_.isNil(local.versions[i])) {
|
||||
local.versions[i] = up.versions[i];
|
||||
}
|
||||
export function mergeVersions(local: Package, up: Package) {
|
||||
// copy new versions to a cache
|
||||
// NOTE: if a certain version was updated, we can't refresh it reliably
|
||||
for (let i in up.versions) {
|
||||
if (_.isNil(local.versions[i])) {
|
||||
local.versions[i] = up.versions[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (let i in up[DIST_TAGS]) {
|
||||
if (local[DIST_TAGS][i] !== up[DIST_TAGS][i]) {
|
||||
if (!local[DIST_TAGS][i] || semver.lte(local[DIST_TAGS][i], up[DIST_TAGS][i])) {
|
||||
local[DIST_TAGS][i] = up[DIST_TAGS][i];
|
||||
}
|
||||
if (i === 'latest' && local[DIST_TAGS][i] === up[DIST_TAGS][i]) {
|
||||
// if remote has more fresh package, we should borrow its readme
|
||||
local.readme = up.readme;
|
||||
}
|
||||
for (let i in up[DIST_TAGS]) {
|
||||
if (local[DIST_TAGS][i] !== up[DIST_TAGS][i]) {
|
||||
if (!local[DIST_TAGS][i] || semver.lte(local[DIST_TAGS][i], up[DIST_TAGS][i])) {
|
||||
local[DIST_TAGS][i] = up[DIST_TAGS][i];
|
||||
}
|
||||
if (i === 'latest' && local[DIST_TAGS][i] === up[DIST_TAGS][i]) {
|
||||
// if remote has more fresh package, we should borrow its readme
|
||||
local.readme = up.readme;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
import Handlebars from 'handlebars';
|
||||
import _ from 'lodash';
|
||||
|
||||
@ -60,7 +64,7 @@ export function notify(metadata, config, ...moreMedatata) {
|
||||
return sendNotification(metadata, config.notify, ...moreMedatata);
|
||||
} else {
|
||||
// multiple notifications endpoints PR #108
|
||||
return Promise.all(_.map(config.notify, (key) => sendNotification(metadata, key, ...moreMedatata)));
|
||||
return Promise.all(_.map(config.notify, key => sendNotification(metadata, key, ...moreMedatata)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
import isNil from 'lodash/isNil';
|
||||
import logger from '../logger';
|
||||
import request from 'request';
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import Path from 'path';
|
||||
@ -27,7 +31,7 @@ function mergeConfig(appConfig, pluginConfig) {
|
||||
}
|
||||
|
||||
function isValid(plugin) {
|
||||
return (_.isFunction(plugin) || _.isFunction(plugin.default));
|
||||
return _.isFunction(plugin) || _.isFunction(plugin.default);
|
||||
}
|
||||
|
||||
function isES6(plugin) {
|
||||
@ -46,11 +50,7 @@ function isES6(plugin) {
|
||||
* @param {*} sanityCheck callback that check the shape that should fulfill the plugin
|
||||
* @return {Array} list of plugins
|
||||
*/
|
||||
export default function loadPlugin<T>(
|
||||
config: Config,
|
||||
pluginConfigs: any = {},
|
||||
params: any,
|
||||
sanityCheck: Function): T[] {
|
||||
export default function loadPlugin<T>(config: Config, pluginConfigs: any = {}, params: any, sanityCheck: Function): T[] {
|
||||
return Object.keys(pluginConfigs).map((pluginId: string) => {
|
||||
let plugin;
|
||||
|
||||
@ -96,17 +96,15 @@ export default function loadPlugin<T>(
|
||||
}
|
||||
|
||||
if (!isValid(plugin)) {
|
||||
logger.logger.error({content: pluginId}, '@{content} doesn\'t look like a valid plugin');
|
||||
logger.logger.error({content: pluginId}, "@{content} doesn't look like a valid plugin");
|
||||
throw Error('"' + pluginId + '" doesn\'t look like a valid plugin');
|
||||
}
|
||||
/* eslint new-cap:off */
|
||||
plugin = isES6(plugin)
|
||||
? new plugin.default(mergeConfig(config, pluginConfigs[pluginId]), params)
|
||||
: plugin(pluginConfigs[pluginId], params);
|
||||
plugin = isES6(plugin) ? new plugin.default(mergeConfig(config, pluginConfigs[pluginId]), params) : plugin(pluginConfigs[pluginId], params);
|
||||
/* eslint new-cap:off */
|
||||
|
||||
if (plugin === null || !sanityCheck(plugin)) {
|
||||
logger.logger.error({content: pluginId}, '@{content} doesn\'t look like a valid plugin');
|
||||
logger.logger.error({content: pluginId}, "@{content} doesn't look like a valid plugin");
|
||||
throw Error('"' + pluginId + '" doesn\'t look like a valid plugin');
|
||||
}
|
||||
logger.logger.warn({content: pluginId}, 'Plugin successfully loaded: @{content}');
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import lunrMutable from 'lunr-mutable-indexes';
|
||||
@ -14,8 +18,8 @@ class Search implements IWebSearch {
|
||||
* Constructor.
|
||||
*/
|
||||
constructor() {
|
||||
/* eslint no-invalid-this: "off" */
|
||||
this.index = lunrMutable(function() {
|
||||
/* eslint no-invalid-this: "off" */
|
||||
this.index = lunrMutable(function() {
|
||||
this.field('name', {boost: 10});
|
||||
this.field('description', {boost: 4});
|
||||
this.field('author', {boost: 6});
|
||||
@ -25,7 +29,7 @@ class Search implements IWebSearch {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Performs a query to the indexer.
|
||||
* If the keyword is a * it returns all local elements
|
||||
* otherwise performs a search
|
||||
@ -34,14 +38,15 @@ class Search implements IWebSearch {
|
||||
*/
|
||||
query(query: string) {
|
||||
return query === '*'
|
||||
? this.storage.localStorage.localData.get((items) => {
|
||||
items.map( function( pkg ) {
|
||||
return {ref: pkg, score: 1};
|
||||
});
|
||||
}) : this.index.search(`*${query}*`);
|
||||
}
|
||||
? this.storage.localStorage.localData.get(items => {
|
||||
items.map(function(pkg) {
|
||||
return {ref: pkg, score: 1};
|
||||
});
|
||||
})
|
||||
: this.index.search(`*${query}*`);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Add a new element to index
|
||||
* @param {*} pkg the package
|
||||
*/
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
@ -33,15 +37,9 @@ const generatePackageTemplate = function(name: string): Package {
|
||||
* @param {Object} pkg package reference.
|
||||
*/
|
||||
function normalizePackage(pkg: Package) {
|
||||
const pkgProperties = [
|
||||
'versions',
|
||||
'dist-tags',
|
||||
'_distfiles',
|
||||
'_attachments',
|
||||
'_uplinks',
|
||||
'time'];
|
||||
const pkgProperties = ['versions', 'dist-tags', '_distfiles', '_attachments', '_uplinks', 'time'];
|
||||
|
||||
pkgProperties.forEach((key) => {
|
||||
pkgProperties.forEach(key => {
|
||||
const pkgProp = pkg[key];
|
||||
|
||||
if (_.isNil(pkgProp) || isObject(pkgProp) === false) {
|
||||
@ -62,7 +60,7 @@ function normalizePackage(pkg: Package) {
|
||||
function generateRevision(rev: string): string {
|
||||
const _rev = rev.split('-');
|
||||
|
||||
return ((+_rev[0] || 0) + 1) + '-' + generateRandomHexString();
|
||||
return (+_rev[0] || 0) + 1 + '-' + generateRandomHexString();
|
||||
}
|
||||
|
||||
function getLatestReadme(pkg: Package): string {
|
||||
@ -74,13 +72,7 @@ function getLatestReadme(pkg: Package): string {
|
||||
return readme;
|
||||
}
|
||||
// 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'];
|
||||
const readmeDistTagsPriority = ['next', 'beta', 'alpha', 'test', 'dev', 'canary'];
|
||||
readmeDistTagsPriority.map(function(tag) {
|
||||
if (readme) {
|
||||
return readme;
|
||||
@ -100,8 +92,8 @@ function cleanUpReadme(version: Version): Version {
|
||||
}
|
||||
|
||||
export function normalizeContributors(contributors: Array<Author>): Array<Author> {
|
||||
if (isObject(contributors) || _.isString(contributors)) {
|
||||
return [((contributors): any)];
|
||||
if (isObject(contributors) || _.isString(contributors)) {
|
||||
return [(contributors: any)];
|
||||
}
|
||||
|
||||
return contributors;
|
||||
@ -111,17 +103,18 @@ export const WHITELIST = ['_rev', 'name', 'versions', 'dist-tags', 'readme', 'ti
|
||||
|
||||
export function cleanUpLinksRef(keepUpLinkData: boolean, result: Package): Package {
|
||||
const propertyToKeep = [...WHITELIST];
|
||||
if (keepUpLinkData === true) {
|
||||
propertyToKeep.push('_uplinks');
|
||||
}
|
||||
if (keepUpLinkData === true) {
|
||||
propertyToKeep.push('_uplinks');
|
||||
}
|
||||
|
||||
for (let i in result) {
|
||||
if (propertyToKeep.indexOf(i) === -1) { // Remove sections like '_uplinks' from response
|
||||
delete result[i];
|
||||
}
|
||||
for (let i in result) {
|
||||
if (propertyToKeep.indexOf(i) === -1) {
|
||||
// Remove sections like '_uplinks' from response
|
||||
delete result[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,14 +219,4 @@ export function prepareSearchPackage(data: Package, time: mixed) {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
generatePackageTemplate,
|
||||
normalizePackage,
|
||||
generateRevision,
|
||||
getLatestReadme,
|
||||
cleanUpReadme,
|
||||
fileExist,
|
||||
noSuchFile,
|
||||
pkgFileName,
|
||||
};
|
||||
|
||||
export {generatePackageTemplate, normalizePackage, generateRevision, getLatestReadme, cleanUpReadme, fileExist, noSuchFile, pkgFileName};
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
@ -9,22 +13,12 @@ import Search from './search';
|
||||
import {API_ERROR, HTTP_STATUS} from './constants';
|
||||
import LocalStorage from './local-storage';
|
||||
import {ReadTarball} from '@verdaccio/streams';
|
||||
import {checkPackageLocal, publishPackage, checkPackageRemote, cleanUpLinksRef,
|
||||
mergeUplinkTimeIntoLocal, generatePackageTemplate} from './storage-utils';
|
||||
import {checkPackageLocal, publishPackage, checkPackageRemote, cleanUpLinksRef, mergeUplinkTimeIntoLocal, generatePackageTemplate} from './storage-utils';
|
||||
import {setupUpLinks, updateVersionsHiddenUpLink} from './uplink-util';
|
||||
import {mergeVersions} from './metadata-utils';
|
||||
import {ErrorCode, normalizeDistTags, validateMetadata, isObject, DIST_TAGS} from './utils';
|
||||
import type {IStorage, IProxy, IStorageHandler, ProxyList, StringValue} from '../../types';
|
||||
import type {
|
||||
Versions,
|
||||
Package,
|
||||
Config,
|
||||
MergeTags,
|
||||
Version,
|
||||
DistFile,
|
||||
Callback,
|
||||
Logger,
|
||||
} from '@verdaccio/types';
|
||||
import type {Versions, Package, Config, MergeTags, Version, DistFile, Callback, Logger} from '@verdaccio/types';
|
||||
import type {IReadTarball, IUploadTarball} from '@verdaccio/streams';
|
||||
import {hasProxyTo} from './config-utils';
|
||||
|
||||
@ -66,9 +60,7 @@ class Storage implements IStorageHandler {
|
||||
}
|
||||
|
||||
_isAllowPublishOffline(): boolean {
|
||||
return typeof this.config.publish !== 'undefined'
|
||||
&& _.isBoolean(this.config.publish.allow_offline)
|
||||
&& this.config.publish.allow_offline;
|
||||
return typeof this.config.publish !== 'undefined' && _.isBoolean(this.config.publish.allow_offline) && this.config.publish.allow_offline;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,7 +139,7 @@ class Storage implements IStorageHandler {
|
||||
// flow: should be IReadTarball
|
||||
let localStream: any = self.localStorage.getTarball(name, filename);
|
||||
let isOpen = false;
|
||||
localStream.on('error', (err) => {
|
||||
localStream.on('error', err => {
|
||||
if (isOpen || err.status !== HTTP_STATUS.NOT_FOUND) {
|
||||
return readStream.emit('error', err);
|
||||
}
|
||||
@ -199,11 +191,14 @@ class Storage implements IStorageHandler {
|
||||
}
|
||||
|
||||
if (uplink == null) {
|
||||
uplink = new ProxyStorage({
|
||||
url: file.url,
|
||||
cache: true,
|
||||
_autogenerated: true,
|
||||
}, self.config);
|
||||
uplink = new ProxyStorage(
|
||||
{
|
||||
url: file.url,
|
||||
cache: true,
|
||||
_autogenerated: true,
|
||||
},
|
||||
self.config
|
||||
);
|
||||
}
|
||||
|
||||
let savestream = null;
|
||||
@ -246,8 +241,7 @@ class Storage implements IStorageHandler {
|
||||
});
|
||||
|
||||
savestream.on('error', function(err) {
|
||||
self.logger.warn( {err: err, fileName: file}
|
||||
, 'error saving file @{fileName}: @{err.message}\n@{err.stack}' );
|
||||
self.logger.warn({err: err, fileName: file}, 'error saving file @{fileName}: @{err.message}\n@{err.stack}');
|
||||
if (savestream) {
|
||||
savestream.abort();
|
||||
}
|
||||
@ -280,19 +274,18 @@ class Storage implements IStorageHandler {
|
||||
return options.callback(err);
|
||||
}
|
||||
|
||||
this._syncUplinksMetadata(options.name, data, {req: options.req},
|
||||
function getPackageSynUpLinksCallback(err, result: Package, uplinkErrors) {
|
||||
if (err) {
|
||||
return options.callback(err);
|
||||
}
|
||||
this._syncUplinksMetadata(options.name, data, {req: options.req}, function getPackageSynUpLinksCallback(err, result: Package, uplinkErrors) {
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -313,7 +306,9 @@ class Storage implements IStorageHandler {
|
||||
// stream to write a tarball
|
||||
let stream: any = new Stream.PassThrough({objectMode: true});
|
||||
|
||||
async.eachSeries(Object.keys(this.uplinks), function(up_name, cb) {
|
||||
async.eachSeries(
|
||||
Object.keys(this.uplinks),
|
||||
function(up_name, cb) {
|
||||
// shortcut: if `local=1` is supplied, don't call uplinks
|
||||
if (options.req.query.local !== undefined) {
|
||||
return cb();
|
||||
@ -321,20 +316,23 @@ class Storage implements IStorageHandler {
|
||||
// search by keyword for each uplink
|
||||
let lstream: IUploadTarball = self.uplinks[up_name].search(options);
|
||||
// join streams
|
||||
lstream.pipe(stream, {end: false});
|
||||
lstream.pipe(
|
||||
stream,
|
||||
{end: false}
|
||||
);
|
||||
lstream.on('error', function(err) {
|
||||
self.logger.error({err: err}, 'uplink error: @{err.message}');
|
||||
cb(), cb = function() {};
|
||||
cb(), (cb = function() {});
|
||||
});
|
||||
lstream.on('end', function() {
|
||||
cb(), cb = function() {};
|
||||
cb(), (cb = function() {});
|
||||
});
|
||||
|
||||
stream.abort = function() {
|
||||
if (lstream.abort) {
|
||||
lstream.abort();
|
||||
}
|
||||
cb(), cb = function() {};
|
||||
cb(), (cb = function() {});
|
||||
};
|
||||
},
|
||||
// executed after all series
|
||||
@ -344,12 +342,16 @@ class Storage implements IStorageHandler {
|
||||
stream.abort = function() {
|
||||
lstream.abort();
|
||||
};
|
||||
lstream.pipe(stream, {end: true});
|
||||
lstream.pipe(
|
||||
stream,
|
||||
{end: true}
|
||||
);
|
||||
lstream.on('error', function(err) {
|
||||
self.logger.error({err: err}, 'search error: @{err.message}');
|
||||
stream.end();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return stream;
|
||||
}
|
||||
@ -378,7 +380,7 @@ class Storage implements IStorageHandler {
|
||||
|
||||
packages.push(version);
|
||||
} else {
|
||||
self.logger.warn( {package: locals[itemPkg]}, 'package @{package} does not have a "latest" tag?' );
|
||||
self.logger.warn({package: locals[itemPkg]}, 'package @{package} does not have a "latest" tag?');
|
||||
}
|
||||
}
|
||||
|
||||
@ -419,79 +421,87 @@ class Storage implements IStorageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
async.map(upLinks, (upLink, cb) => {
|
||||
const _options = Object.assign({}, options);
|
||||
let upLinkMeta = packageInfo._uplinks[upLink.upname];
|
||||
async.map(
|
||||
upLinks,
|
||||
(upLink, cb) => {
|
||||
const _options = Object.assign({}, options);
|
||||
let upLinkMeta = packageInfo._uplinks[upLink.upname];
|
||||
|
||||
if (isObject(upLinkMeta)) {
|
||||
const fetched = upLinkMeta.fetched;
|
||||
if (isObject(upLinkMeta)) {
|
||||
const fetched = upLinkMeta.fetched;
|
||||
|
||||
if (fetched && (Date.now() - fetched) < upLink.maxage) {
|
||||
return cb();
|
||||
if (fetched && Date.now() - fetched < upLink.maxage) {
|
||||
return cb();
|
||||
}
|
||||
|
||||
_options.etag = upLinkMeta.etag;
|
||||
}
|
||||
|
||||
_options.etag = upLinkMeta.etag;
|
||||
upLink.getRemoteMetadata(name, _options, (err, upLinkResponse, eTag) => {
|
||||
if (err && err.remoteStatus === 304) {
|
||||
upLinkMeta.fetched = Date.now();
|
||||
}
|
||||
|
||||
if (err || !upLinkResponse) {
|
||||
// $FlowFixMe
|
||||
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
|
||||
exists = true;
|
||||
cb();
|
||||
});
|
||||
},
|
||||
(err: Error, upLinksErrors: any) => {
|
||||
assert(!err && Array.isArray(upLinksErrors));
|
||||
if (!exists) {
|
||||
return callback(ErrorCode.getNotFound(API_ERROR.NO_PACKAGE), null, upLinksErrors);
|
||||
}
|
||||
|
||||
self.localStorage.updateVersions(name, packageInfo, function(err, packageJsonLocal: Package) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null, packageJsonLocal, upLinksErrors);
|
||||
});
|
||||
}
|
||||
|
||||
upLink.getRemoteMetadata(name, _options, (err, upLinkResponse, eTag) => {
|
||||
if (err && err.remoteStatus === 304) {
|
||||
upLinkMeta.fetched = Date.now();
|
||||
}
|
||||
|
||||
if (err || !upLinkResponse) {
|
||||
// $FlowFixMe
|
||||
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
|
||||
exists = true;
|
||||
cb();
|
||||
});
|
||||
}, (err: Error, upLinksErrors: any) => {
|
||||
assert(!err && Array.isArray(upLinksErrors));
|
||||
if (!exists) {
|
||||
return callback( ErrorCode.getNotFound(API_ERROR.NO_PACKAGE)
|
||||
, null
|
||||
, upLinksErrors );
|
||||
}
|
||||
|
||||
self.localStorage.updateVersions(name, packageInfo, function(err, packageJsonLocal: Package) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null, packageJsonLocal, upLinksErrors);
|
||||
});
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,7 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import zlib from 'zlib';
|
||||
@ -9,13 +13,7 @@ import URL from 'url';
|
||||
import {parseInterval, isObject, ErrorCode, buildToken} from './utils';
|
||||
import {ReadTarball} from '@verdaccio/streams';
|
||||
import {ERROR_CODE, TOKEN_BASIC, TOKEN_BEARER, HEADERS, HTTP_STATUS, API_ERROR, HEADER_TYPE, CHARACTER_ENCODING} from './constants';
|
||||
import type {
|
||||
Config,
|
||||
UpLinkConf,
|
||||
Callback,
|
||||
Headers,
|
||||
Logger,
|
||||
} from '@verdaccio/types';
|
||||
import type {Config, UpLinkConf, Callback, Headers, Logger} from '@verdaccio/types';
|
||||
import type {IProxy} from '../../types';
|
||||
|
||||
const LoggerApi = require('./logger');
|
||||
@ -77,17 +75,21 @@ class ProxyStorage implements IProxy {
|
||||
this.config.url = this.config.url.replace(/\/$/, '');
|
||||
|
||||
if (this.config.timeout && Number(this.config.timeout) >= 1000) {
|
||||
this.logger.warn(['Too big timeout value: ' + this.config.timeout,
|
||||
'We changed time format to nginx-like one',
|
||||
'(see http://nginx.org/en/docs/syntax.html)',
|
||||
'so please update your config accordingly'].join('\n'));
|
||||
this.logger.warn(
|
||||
[
|
||||
'Too big timeout value: ' + this.config.timeout,
|
||||
'We changed time format to nginx-like one',
|
||||
'(see http://nginx.org/en/docs/syntax.html)',
|
||||
'so please update your config accordingly',
|
||||
].join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
// a bunch of different configurable timers
|
||||
this.maxage = parseInterval(setConfig(this.config, 'maxage', '2m' ));
|
||||
this.maxage = parseInterval(setConfig(this.config, 'maxage', '2m'));
|
||||
this.timeout = parseInterval(setConfig(this.config, 'timeout', '30s'));
|
||||
this.max_fails = Number(setConfig(this.config, 'max_fails', 2 ));
|
||||
this.fail_timeout = parseInterval(setConfig(this.config, 'fail_timeout', '5m' ));
|
||||
this.max_fails = Number(setConfig(this.config, 'max_fails', 2));
|
||||
this.fail_timeout = parseInterval(setConfig(this.config, 'fail_timeout', '5m'));
|
||||
this.strict_ssl = Boolean(setConfig(this.config, 'strict_ssl', true));
|
||||
}
|
||||
|
||||
@ -124,88 +126,97 @@ class ProxyStorage implements IProxy {
|
||||
this._overrideWithUplinkConfigHeaders(headers);
|
||||
|
||||
const method = options.method || 'GET';
|
||||
const uri = options.uri_full || (this.config.url + options.uri);
|
||||
const uri = options.uri_full || this.config.url + options.uri;
|
||||
|
||||
self.logger.info({
|
||||
method: method,
|
||||
headers: headers,
|
||||
uri: uri,
|
||||
}, 'making request: \'@{method} @{uri}\'');
|
||||
self.logger.info(
|
||||
{
|
||||
method: method,
|
||||
headers: headers,
|
||||
uri: uri,
|
||||
},
|
||||
"making request: '@{method} @{uri}'"
|
||||
);
|
||||
|
||||
if (isObject(options.json)) {
|
||||
json = JSON.stringify(options.json);
|
||||
headers['Content-Type'] = headers['Content-Type'] || HEADERS.JSON;
|
||||
}
|
||||
|
||||
let requestCallback = cb ? (function(err, res, body) {
|
||||
let error;
|
||||
const responseLength = err ? 0 : body.length;
|
||||
// $FlowFixMe
|
||||
processBody(err, body);
|
||||
logActivity();
|
||||
// $FlowFixMe
|
||||
cb(err, res, body);
|
||||
let requestCallback = cb
|
||||
? function(err, res, body) {
|
||||
let error;
|
||||
const responseLength = err ? 0 : body.length;
|
||||
// $FlowFixMe
|
||||
processBody(err, body);
|
||||
logActivity();
|
||||
// $FlowFixMe
|
||||
cb(err, res, body);
|
||||
|
||||
/**
|
||||
* Perform a decode.
|
||||
*/
|
||||
function processBody() {
|
||||
if (err) {
|
||||
error = err.message;
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Perform a decode.
|
||||
*/
|
||||
function processBody() {
|
||||
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() {
|
||||
let message = "@{!status}, req: '@{request.method} @{request.url}'";
|
||||
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;
|
||||
|
||||
if (!err && isObject(body)) {
|
||||
if (_.isString(body.error)) {
|
||||
error = body.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Perform a log.
|
||||
*/
|
||||
function logActivity() {
|
||||
let message = '@{!status}, req: \'@{request.method} @{request.url}\'';
|
||||
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;
|
||||
|
||||
const req = request({
|
||||
url: uri,
|
||||
method: method,
|
||||
headers: headers,
|
||||
body: json,
|
||||
ca: this.ca,
|
||||
proxy: this.proxy,
|
||||
encoding: null,
|
||||
gzip: true,
|
||||
timeout: this.timeout,
|
||||
strictSSL: this.strict_ssl,
|
||||
}, requestCallback);
|
||||
const req = request(
|
||||
{
|
||||
url: uri,
|
||||
method: method,
|
||||
headers: headers,
|
||||
body: json,
|
||||
ca: this.ca,
|
||||
proxy: this.proxy,
|
||||
encoding: null,
|
||||
gzip: true,
|
||||
timeout: this.timeout,
|
||||
strictSSL: this.strict_ssl,
|
||||
},
|
||||
requestCallback
|
||||
);
|
||||
|
||||
let statusCalled = false;
|
||||
req.on('response', function(res) {
|
||||
@ -216,15 +227,18 @@ class ProxyStorage implements IProxy {
|
||||
|
||||
if (_.isNil(requestCallback) === false) {
|
||||
(function do_log() {
|
||||
const message = '@{!status}, req: \'@{request.method} @{request.url}\' (streaming)';
|
||||
self.logger.warn({
|
||||
request: {
|
||||
method: method,
|
||||
url: uri,
|
||||
const message = "@{!status}, req: '@{request.method} @{request.url}' (streaming)";
|
||||
self.logger.warn(
|
||||
{
|
||||
request: {
|
||||
method: method,
|
||||
url: uri,
|
||||
},
|
||||
level: 35, // http
|
||||
status: _.isNull(res) === false ? res.statusCode : 'ERR',
|
||||
},
|
||||
level: 35, // http
|
||||
status: _.isNull(res) === false ? res.statusCode : 'ERR',
|
||||
}, message);
|
||||
message
|
||||
);
|
||||
})();
|
||||
}
|
||||
});
|
||||
@ -363,7 +377,7 @@ class ProxyStorage implements IProxy {
|
||||
// add/override headers specified in the config
|
||||
/* eslint guard-for-in: 0 */
|
||||
for (let key in this.config.headers) {
|
||||
headers[key] = this.config.headers[key];
|
||||
headers[key] = this.config.headers[key];
|
||||
}
|
||||
}
|
||||
|
||||
@ -375,8 +389,8 @@ class ProxyStorage implements IProxy {
|
||||
isUplinkValid(url: string) {
|
||||
// $FlowFixMe
|
||||
const urlParsed: Url = URL.parse(url);
|
||||
const isHTTPS = (urlDomainParsed) => urlDomainParsed.protocol === 'https:' && (urlParsed.port === null || urlParsed.port === '443');
|
||||
const getHost = (urlDomainParsed) => isHTTPS(urlDomainParsed) ? urlDomainParsed.hostname : urlDomainParsed.host;
|
||||
const isHTTPS = urlDomainParsed => urlDomainParsed.protocol === 'https:' && (urlParsed.port === null || urlParsed.port === '443');
|
||||
const getHost = urlDomainParsed => (isHTTPS(urlDomainParsed) ? urlDomainParsed.hostname : urlDomainParsed.host);
|
||||
const isMatchProtocol: boolean = urlParsed.protocol === this.url.protocol;
|
||||
const isMatchHost: boolean = getHost(urlParsed) === getHost(this.url);
|
||||
// $FlowFixMe
|
||||
@ -398,26 +412,29 @@ class ProxyStorage implements IProxy {
|
||||
headers['Accept'] = contenTypeAccept;
|
||||
}
|
||||
|
||||
this.request({
|
||||
uri: `/${encode(name)}`,
|
||||
json: true,
|
||||
headers: headers,
|
||||
req: options.req,
|
||||
}, (err, res, body) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
this.request(
|
||||
{
|
||||
uri: `/${encode(name)}`,
|
||||
json: true,
|
||||
headers: headers,
|
||||
req: options.req,
|
||||
},
|
||||
(err, res, body) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (res.statusCode === HTTP_STATUS.NOT_FOUND) {
|
||||
return callback(ErrorCode.getNotFound(API_ERROR.NOT_PACKAGE_UPLINK));
|
||||
}
|
||||
if (!(res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)) {
|
||||
const error = ErrorCode.getInternalError(`${API_ERROR.BAD_STATUS_CODE}: ${res.statusCode}`);
|
||||
// $FlowFixMe
|
||||
error.remoteStatus = res.statusCode;
|
||||
return callback(error);
|
||||
}
|
||||
callback(null, body, res.headers.etag);
|
||||
}
|
||||
if (res.statusCode === HTTP_STATUS.NOT_FOUND) {
|
||||
return callback( ErrorCode.getNotFound(API_ERROR.NOT_PACKAGE_UPLINK));
|
||||
}
|
||||
if (!(res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)) {
|
||||
const error = ErrorCode.getInternalError(`${API_ERROR.BAD_STATUS_CODE}: ${res.statusCode}`);
|
||||
// $FlowFixMe
|
||||
error.remoteStatus = res.statusCode;
|
||||
return callback(error);
|
||||
}
|
||||
callback(null, body, res.headers.etag);
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -486,13 +503,13 @@ class ProxyStorage implements IProxy {
|
||||
},
|
||||
});
|
||||
|
||||
let parsePackage = (pkg) => {
|
||||
let parsePackage = pkg => {
|
||||
if (isObject(pkg)) {
|
||||
transformStream.emit('data', pkg);
|
||||
}
|
||||
};
|
||||
|
||||
requestStream.on('response', (res) => {
|
||||
requestStream.on('response', res => {
|
||||
if (!String(res.statusCode).match(/^2\d\d$/)) {
|
||||
return transformStream.emit('error', ErrorCode.getInternalError(`bad status code ${res.statusCode} from uplink`));
|
||||
}
|
||||
@ -511,7 +528,7 @@ class ProxyStorage implements IProxy {
|
||||
});
|
||||
});
|
||||
|
||||
requestStream.on('error', (err) => {
|
||||
requestStream.on('error', err => {
|
||||
transformStream.emit('error', err);
|
||||
});
|
||||
|
||||
@ -539,19 +556,12 @@ class ProxyStorage implements IProxy {
|
||||
// https://github.com/rlidwka/sinopia/issues/254
|
||||
//
|
||||
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 && req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'] + ', ' : '') + req.connection.remoteAddress;
|
||||
}
|
||||
}
|
||||
|
||||
// always attach Via header to avoid loops, even if we're not proxying
|
||||
headers['Via'] =
|
||||
req && req.headers['via']
|
||||
? req.headers['via'] + ', '
|
||||
: '';
|
||||
headers['Via'] = req && req.headers['via'] ? req.headers['via'] + ', ' : '';
|
||||
|
||||
headers['Via'] += '1.1 ' + this.server_id + ' (Verdaccio)';
|
||||
}
|
||||
@ -567,17 +577,23 @@ class ProxyStorage implements IProxy {
|
||||
} else {
|
||||
if (alive) {
|
||||
if (this.failed_requests >= this.max_fails) {
|
||||
this.logger.warn({
|
||||
host: this.url.host,
|
||||
}, 'host @{host} is back online');
|
||||
this.logger.warn(
|
||||
{
|
||||
host: this.url.host,
|
||||
},
|
||||
'host @{host} is back online'
|
||||
);
|
||||
}
|
||||
this.failed_requests = 0;
|
||||
} else {
|
||||
this.failed_requests ++;
|
||||
this.failed_requests++;
|
||||
if (this.failed_requests === this.max_fails) {
|
||||
this.logger.warn({
|
||||
host: this.url.host,
|
||||
}, 'host @{host} is now offline');
|
||||
this.logger.warn(
|
||||
{
|
||||
host: this.url.host,
|
||||
},
|
||||
'host @{host} is now offline'
|
||||
);
|
||||
}
|
||||
}
|
||||
this.last_request_time = Date.now();
|
||||
@ -634,8 +650,7 @@ class ProxyStorage implements IProxy {
|
||||
if (noProxyItem[0] !== '.') noProxyItem = '.' + noProxyItem;
|
||||
if (hostname.lastIndexOf(noProxyItem) === hostname.length - noProxyItem.length) {
|
||||
if (this.proxy) {
|
||||
this.logger.debug({url: this.url.href, rule: noProxyItem},
|
||||
'not using proxy for @{url}, excluded by @{rule} rule');
|
||||
this.logger.debug({url: this.url.href, rule: noProxyItem}, 'not using proxy for @{url}, excluded by @{rule} rule');
|
||||
// $FlowFixMe
|
||||
this.proxy = false;
|
||||
}
|
||||
@ -648,7 +663,7 @@ class ProxyStorage implements IProxy {
|
||||
if (_.isString(this.proxy) === false) {
|
||||
delete this.proxy;
|
||||
} else {
|
||||
this.logger.debug( {url: this.url.href, proxy: this.proxy}, 'using proxy @{proxy} for @{url}' );
|
||||
this.logger.debug({url: this.url.href, proxy: this.proxy}, 'using proxy @{proxy} for @{url}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
// @prettier
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import request from 'request';
|
||||
@ -6,7 +9,7 @@ import semver from 'semver';
|
||||
import chalk from 'chalk';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { UPDATE_BANNER, DEFAULT_REGISTRY, HTTP_STATUS } from './constants';
|
||||
import {UPDATE_BANNER, DEFAULT_REGISTRY, HTTP_STATUS} from './constants';
|
||||
|
||||
const VERDACCIO_LATEST_REGISTRY_URL = `${DEFAULT_REGISTRY}/verdaccio/latest`;
|
||||
|
||||
@ -14,48 +17,48 @@ const VERDACCIO_LATEST_REGISTRY_URL = `${DEFAULT_REGISTRY}/verdaccio/latest`;
|
||||
* Creates NPM update banner using chalk
|
||||
*/
|
||||
export function createBanner(currentVersion: string, newVersion: string, releaseType: string): string {
|
||||
const changelog = `${UPDATE_BANNER.CHANGELOG_URL}v${newVersion}`
|
||||
const versionUpdate = `${chalk.bold.red(currentVersion)} → ${chalk.bold.green(newVersion)}`
|
||||
const banner = chalk`
|
||||
const changelog = `${UPDATE_BANNER.CHANGELOG_URL}v${newVersion}`;
|
||||
const versionUpdate = `${chalk.bold.red(currentVersion)} → ${chalk.bold.green(newVersion)}`;
|
||||
const banner = chalk`
|
||||
{white.bold A new ${_.upperCase(releaseType)} version of Verdaccio is available. ${versionUpdate} }
|
||||
{white.bold Run ${chalk.green.bold('npm install -g verdaccio')} to update}.
|
||||
{white.bold Registry: ${DEFAULT_REGISTRY}}
|
||||
{blue.bold Changelog: ${changelog}}
|
||||
`;
|
||||
return banner;
|
||||
return banner;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates error banner
|
||||
*/
|
||||
export function createErrorBanner(message: string): string {
|
||||
const banner = chalk`
|
||||
const banner = chalk`
|
||||
{red.bold Unable to check verdaccio version on ${DEFAULT_REGISTRY}}
|
||||
{red.bold Error: ${message}}
|
||||
`;
|
||||
return banner;
|
||||
return banner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show verdaccio update banner on start
|
||||
*/
|
||||
* Show verdaccio update banner on start
|
||||
*/
|
||||
export function verdaccioUpdateBanner(pkgVersion: string) {
|
||||
request(VERDACCIO_LATEST_REGISTRY_URL, function (error: ?Object = null, response: Object = {}) {
|
||||
if (!error && response.statusCode === HTTP_STATUS.OK && response.body) {
|
||||
// In case, NPM does not returns version, keeping version equals to
|
||||
// verdaccio version.
|
||||
const { version = pkgVersion } = JSON.parse(response.body);
|
||||
const releaseType = semver.diff(version, pkgVersion);
|
||||
request(VERDACCIO_LATEST_REGISTRY_URL, function(error: ?Object = null, response: Object = {}) {
|
||||
if (!error && response.statusCode === HTTP_STATUS.OK && response.body) {
|
||||
// In case, NPM does not returns version, keeping version equals to
|
||||
// verdaccio version.
|
||||
const {version = pkgVersion} = JSON.parse(response.body);
|
||||
const releaseType = semver.diff(version, pkgVersion);
|
||||
|
||||
if (releaseType && semver.gt(version, pkgVersion)) {
|
||||
const banner = createBanner(pkgVersion, version, releaseType);
|
||||
/* eslint-disable-next-line */
|
||||
console.log(banner);
|
||||
}
|
||||
} else {
|
||||
const errorBanner = createErrorBanner(JSON.stringify(error));
|
||||
/* eslint-disable-next-line */
|
||||
console.log(errorBanner);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (releaseType && semver.gt(version, pkgVersion)) {
|
||||
const banner = createBanner(pkgVersion, version, releaseType);
|
||||
/* eslint-disable-next-line */
|
||||
console.log(banner);
|
||||
}
|
||||
} else {
|
||||
const errorBanner = createErrorBanner(JSON.stringify(error));
|
||||
/* eslint-disable-next-line */
|
||||
console.log(errorBanner);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,12 +1,16 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
|
||||
import ProxyStorage from './up-storage';
|
||||
import type {Versions, Config} from '@verdaccio/types';
|
||||
import type {IProxy, ProxyList} from '../../types';
|
||||
|
||||
/**
|
||||
* Set up the Up Storage for each link.
|
||||
*/
|
||||
/**
|
||||
* Set up the Up Storage for each link.
|
||||
*/
|
||||
export function setupUpLinks(config: Config): ProxyList {
|
||||
const uplinks: ProxyList = {};
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
// @flow
|
||||
// @prettier
|
||||
|
||||
import _ from 'lodash';
|
||||
import fs from 'fs';
|
||||
import assert from 'assert';
|
||||
@ -9,14 +13,7 @@ import URL from 'url';
|
||||
import createError from 'http-errors';
|
||||
import marked from 'marked';
|
||||
|
||||
import {
|
||||
HTTP_STATUS,
|
||||
API_ERROR,
|
||||
DEFAULT_PORT,
|
||||
DEFAULT_DOMAIN,
|
||||
DEFAULT_PROTOCOL,
|
||||
CHARACTER_ENCODING, HEADERS
|
||||
} from './constants';
|
||||
import {HTTP_STATUS, API_ERROR, DEFAULT_PORT, DEFAULT_DOMAIN, DEFAULT_PROTOCOL, CHARACTER_ENCODING, HEADERS} from './constants';
|
||||
import {generateGravatarUrl, GRAVATAR_DEFAULT} from '../utils/user';
|
||||
|
||||
import type {Package} from '@verdaccio/types';
|
||||
@ -52,11 +49,7 @@ export function validatePackage(name: string): boolean {
|
||||
return validateName(nameList[0]);
|
||||
} else {
|
||||
// scoped package
|
||||
return (
|
||||
nameList[0][0] === '@' &&
|
||||
validateName(nameList[0].slice(1)) &&
|
||||
validateName(nameList[1])
|
||||
);
|
||||
return nameList[0][0] === '@' && validateName(nameList[0].slice(1)) && validateName(nameList[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,11 +116,7 @@ export function validateMetadata(object: Package, name: string): Object {
|
||||
* Create base url for registry.
|
||||
* @return {String} base registry url
|
||||
*/
|
||||
export function combineBaseUrl(
|
||||
protocol: string,
|
||||
host: string,
|
||||
prefix?: string
|
||||
): string {
|
||||
export function combineBaseUrl(protocol: string, host: string, prefix?: string): string {
|
||||
let result = `${protocol}://${host}`;
|
||||
|
||||
if (prefix) {
|
||||
@ -151,25 +140,13 @@ export function extractTarballFromUrl(url: string) {
|
||||
* @param {*} config
|
||||
* @return {String} a filtered package
|
||||
*/
|
||||
export function convertDistRemoteToLocalTarballUrls(
|
||||
pkg: Package,
|
||||
req: $Request,
|
||||
urlPrefix: string | void
|
||||
) {
|
||||
export function convertDistRemoteToLocalTarballUrls(pkg: Package, req: $Request, urlPrefix: string | void) {
|
||||
for (let ver in pkg.versions) {
|
||||
if (Object.prototype.hasOwnProperty.call(pkg.versions, ver)) {
|
||||
const distName = pkg.versions[ver].dist;
|
||||
|
||||
if (
|
||||
_.isNull(distName) === false &&
|
||||
_.isNull(distName.tarball) === false
|
||||
) {
|
||||
distName.tarball = getLocalRegistryTarballUri(
|
||||
distName.tarball,
|
||||
pkg.name,
|
||||
req,
|
||||
urlPrefix
|
||||
);
|
||||
if (_.isNull(distName) === false && _.isNull(distName.tarball) === false) {
|
||||
distName.tarball = getLocalRegistryTarballUri(distName.tarball, pkg.name, req, urlPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,23 +158,14 @@ export function convertDistRemoteToLocalTarballUrls(
|
||||
* @param {*} uri
|
||||
* @return {String} a parsed url
|
||||
*/
|
||||
export function getLocalRegistryTarballUri(
|
||||
uri: string,
|
||||
pkgName: string,
|
||||
req: $Request,
|
||||
urlPrefix: string | void
|
||||
) {
|
||||
export function getLocalRegistryTarballUri(uri: string, pkgName: string, req: $Request, urlPrefix: string | void) {
|
||||
const currentHost = req.headers.host;
|
||||
|
||||
if (!currentHost) {
|
||||
return uri;
|
||||
}
|
||||
const tarballName = extractTarballFromUrl(uri);
|
||||
const domainRegistry = combineBaseUrl(
|
||||
getWebProtocol(req.get(HEADERS.FORWARDED_PROTO), req.protocol),
|
||||
req.headers.host,
|
||||
urlPrefix
|
||||
);
|
||||
const domainRegistry = combineBaseUrl(getWebProtocol(req.get(HEADERS.FORWARDED_PROTO), req.protocol), req.headers.host, urlPrefix);
|
||||
|
||||
return `${domainRegistry}/${pkgName.replace(/\//g, '%2f')}/-/${tarballName}`;
|
||||
}
|
||||
@ -260,9 +228,7 @@ export function parseAddress(urlAddress: any) {
|
||||
// TODO: refactor it to something more reasonable?
|
||||
//
|
||||
// protocol : // ( host )|( ipv6 ): port /
|
||||
let urlPattern = /^((https?):(\/\/)?)?((([^\/:]*)|\[([^\[\]]+)\]):)?(\d+)\/?$/.exec(
|
||||
urlAddress
|
||||
);
|
||||
let urlPattern = /^((https?):(\/\/)?)?((([^\/:]*)|\[([^\[\]]+)\]):)?(\d+)\/?$/.exec(urlAddress);
|
||||
|
||||
if (urlPattern) {
|
||||
return {
|
||||
@ -363,11 +329,7 @@ export function parseInterval(interval: any): number {
|
||||
interval.split(/\s+/).forEach(function(x) {
|
||||
if (!x) return;
|
||||
let m = x.match(/^((0|[1-9][0-9]*)(\.[0-9]+)?)(ms|s|m|h|d|w|M|y|)$/);
|
||||
if (
|
||||
!m ||
|
||||
parseIntervalTable[m[4]] >= last_suffix ||
|
||||
(m[4] === '' && last_suffix !== Infinity)
|
||||
) {
|
||||
if (!m || parseIntervalTable[m[4]] >= last_suffix || (m[4] === '' && last_suffix !== Infinity)) {
|
||||
throw Error('invalid interval: ' + interval);
|
||||
}
|
||||
last_suffix = parseIntervalTable[m[4]];
|
||||
@ -380,7 +342,7 @@ export function parseInterval(interval: any): number {
|
||||
* Detect running protocol (http or https)
|
||||
*/
|
||||
export function getWebProtocol(headerProtocol: string | void, protocol: string): string {
|
||||
if (typeof(headerProtocol) === 'string' && headerProtocol !== '') {
|
||||
if (typeof headerProtocol === 'string' && headerProtocol !== '') {
|
||||
const commaIndex = headerProtocol.indexOf(',');
|
||||
return commaIndex > 0 ? headerProtocol.substr(0, commaIndex) : headerProtocol;
|
||||
}
|
||||
@ -390,7 +352,7 @@ export function getWebProtocol(headerProtocol: string | void, protocol: string):
|
||||
|
||||
export function getLatestVersion(pkgInfo: Package): string {
|
||||
return pkgInfo[DIST_TAGS].latest;
|
||||
};
|
||||
}
|
||||
|
||||
export const ErrorCode = {
|
||||
getConflict: (message: string = API_ERROR.PACKAGE_EXIST) => {
|
||||
@ -403,30 +365,23 @@ export const ErrorCode = {
|
||||
return createError(HTTP_STATUS.BAD_REQUEST, customMessage);
|
||||
},
|
||||
getInternalError: (customMessage?: string) => {
|
||||
return customMessage
|
||||
? createError(HTTP_STATUS.INTERNAL_ERROR, customMessage)
|
||||
: createError(HTTP_STATUS.INTERNAL_ERROR);
|
||||
return customMessage ? createError(HTTP_STATUS.INTERNAL_ERROR, customMessage) : createError(HTTP_STATUS.INTERNAL_ERROR);
|
||||
},
|
||||
getForbidden: (message: string = 'can\'t use this filename') => {
|
||||
getForbidden: (message: string = "can't use this filename") => {
|
||||
return createError(HTTP_STATUS.FORBIDDEN, message);
|
||||
},
|
||||
getServiceUnavailable: (
|
||||
message: string = API_ERROR.RESOURCE_UNAVAILABLE
|
||||
) => {
|
||||
getServiceUnavailable: (message: string = API_ERROR.RESOURCE_UNAVAILABLE) => {
|
||||
return createError(HTTP_STATUS.SERVICE_UNAVAILABLE, message);
|
||||
},
|
||||
getNotFound: (customMessage?: string) => {
|
||||
return createError(
|
||||
HTTP_STATUS.NOT_FOUND,
|
||||
customMessage || API_ERROR.NO_PACKAGE
|
||||
);
|
||||
return createError(HTTP_STATUS.NOT_FOUND, customMessage || API_ERROR.NO_PACKAGE);
|
||||
},
|
||||
getCode: (statusCode: number, customMessage: string) => {
|
||||
return createError(statusCode, customMessage);
|
||||
},
|
||||
};
|
||||
|
||||
export function parseConfigFile (configPath: string): Object {
|
||||
export function parseConfigFile(configPath: string): Object {
|
||||
return YAML.safeLoad(fs.readFileSync(configPath, CHARACTER_ENCODING.UTF8));
|
||||
}
|
||||
|
||||
@ -473,7 +428,7 @@ export function addScope(scope: string, packageName: string) {
|
||||
}
|
||||
|
||||
export function deleteProperties(propertiesToDelete: Array<string>, objectItem: any) {
|
||||
_.forEach(propertiesToDelete, (property) => {
|
||||
_.forEach(propertiesToDelete, property => {
|
||||
delete objectItem[property];
|
||||
});
|
||||
|
||||
@ -501,7 +456,7 @@ export function addGravatarSupport(pkgInfo: Object): Object {
|
||||
|
||||
// for contributors
|
||||
if (_.isEmpty(contributors) === false) {
|
||||
pkgInfoCopy.latest.contributors = contributors.map((contributor) => {
|
||||
pkgInfoCopy.latest.contributors = contributors.map(contributor => {
|
||||
if (isObject(contributor)) {
|
||||
// $FlowFixMe
|
||||
contributor.avatar = generateGravatarUrl(contributor.email);
|
||||
@ -519,7 +474,7 @@ export function addGravatarSupport(pkgInfo: Object): Object {
|
||||
|
||||
// for maintainers
|
||||
if (_.isEmpty(maintainers) === false) {
|
||||
pkgInfoCopy.latest.maintainers = maintainers.map((maintainer) => {
|
||||
pkgInfoCopy.latest.maintainers = maintainers.map(maintainer => {
|
||||
maintainer.avatar = generateGravatarUrl(maintainer.email);
|
||||
return maintainer;
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user