1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-02-21 07:29:37 +01:00

refactor: web api to es6

This commit is contained in:
Juan Picado @jotadeveloper 2018-02-03 19:28:23 +01:00
parent 3bb374c84a
commit 1570810f99
No known key found for this signature in database
GPG Key ID: 18AC54485952D158
6 changed files with 225 additions and 190 deletions

@ -1,201 +1,35 @@
import {Router} from 'express';
import bodyParser from 'body-parser';
import addUserAuthApi from './endpoint/user';
import addPackageWebApi from './endpoint/package';
import addSearchWebApi from './endpoint/search';
import Search from '../../lib/search';
const bodyParser = require('body-parser');
const express = require('express');
const marked = require('marked');
const _ = require('lodash');
const Middleware = require('./middleware');
const match = Middleware.match;
const validateName = Middleware.validate_name;
const validatePkg = Middleware.validate_package;
const securityIframe = Middleware.securityIframe;
const route = express.Router(); // eslint-disable-line
const async = require('async');
const HTTPError = require('http-errors');
const Utils = require('../../lib/utils');
const {generateGravatarUrl} = require('../../utils/user');
import {match, validate_name, validate_package, securityIframe} from './middleware';
const route = Router(); /* eslint new-cap: 0 */
/*
This file include all verdaccio only API(Web UI), for npm API please see ../endpoint/
*/
module.exports = function(config, auth, storage) {
Search.configureStorage(storage);
const can = Middleware.allow(auth);
// validate all of these params as a package name
// this might be too harsh, so ask if it causes trouble
route.param('package', validatePkg);
route.param('filename', validateName);
route.param('version', validateName);
route.param('package', validate_package);
route.param('filename', validate_name);
route.param('version', validate_name);
route.param('anything', match(/.*/));
route.use(bodyParser.urlencoded({extended: false}));
route.use(auth.jwtMiddleware());
route.use(securityIframe);
// Get list of all visible package
route.get('/packages', function(req, res, next) {
storage.getLocalDatabase(function(err, packages) {
if (err) {
// that function shouldn't produce any
throw err;
}
async.filterSeries(
packages,
function(pkg, cb) {
auth.allow_access(pkg.name, req.remote_user, function(err, allowed) {
setImmediate(function() {
if (err) {
cb(null, false);
} else {
cb(err, allowed);
}
});
});
},
function(err, packages) {
if (err) throw err;
packages.sort(function(a, b) {
if (a.name < b.name) {
return -1;
} else {
return 1;
}
});
next(packages);
}
);
});
});
// Get package readme
route.get('/package/readme/(@:scope/)?:package/:version?', can('access'), function(req, res, next) {
let packageName = req.params.package;
if (req.params.scope) {
packageName = `@${req.params.scope}/${packageName}`;
}
storage.get_package({
name: packageName,
req,
callback: function(err, info) {
if (err) {
return next(err);
}
res.set('Content-Type', 'text/plain');
next(marked(info.readme || 'ERROR: No README data found!'));
},
});
});
// Search package
route.get('/search/:anything', function(req, res, next) {
const results = Search.query(req.params.anything);
const packages = [];
const getPackageInfo = function(i) {
storage.get_package({
name: results[i].ref,
callback: (err, entry) => {
if (!err && entry) {
auth.allow_access(entry.name, req.remote_user, function(err, allowed) {
if (err || !allowed) {
return;
}
packages.push(entry.versions[entry['dist-tags'].latest]);
});
}
if (i >= results.length - 1) {
next(packages);
} else {
getPackageInfo(i + 1);
}
},
});
};
if (results.length) {
getPackageInfo(0);
} else {
next([]);
}
});
route.post('/login', function(req, res, next) {
auth.authenticate(req.body.username, req.body.password, (err, user) => {
if (!err) {
req.remote_user = user;
next({
token: auth.issue_token(user, '24h'),
username: req.remote_user.name,
});
} else {
next(HTTPError[err.message ? 401 : 500](err.message));
}
});
});
route.post('/-/logout', function(req, res, next) {
let base = Utils.combineBaseUrl(Utils.getWebProtocol(req), req.get('host'), config.url_prefix);
res.cookies.set('token', '');
res.redirect(base);
});
route.get('/sidebar/(@:scope/)?:package', function(req, res, next) {
let packageName = req.params.package;
if (req.params.scope) {
packageName = `@${req.params.scope}/${packageName}`;
}
storage.get_package({
name: packageName,
keepUpLinkData: true,
req,
callback: function(err, info) {
res.set('Content-Type', 'application/json');
if (!err) {
info.latest = info.versions[info['dist-tags'].latest];
let propertyToDelete = ['readme', 'versions'];
_.forEach(propertyToDelete, ((property) => {
delete info[property];
}));
if (typeof _.get(info, 'latest.author.email') === 'string') {
info.latest.author.avatar = generateGravatarUrl(info.latest.author.email);
} else {
// _.get can't guarantee author property exist
_.set(info, 'latest.author.avatar', generateGravatarUrl());
}
if (_.get(info, 'latest.contributors.length', 0) > 0) {
info.latest.contributors = _.map(info.latest.contributors, (contributor) => {
if (typeof contributor.email === 'string') {
contributor.avatar = generateGravatarUrl(contributor.email);
} else {
contributor.avatar = generateGravatarUrl();
}
return contributor;
}
);
}
res.end(JSON.stringify(info));
} else {
res.status(404);
res.end();
}
},
});
});
addPackageWebApi(route, storage, auth);
addSearchWebApi(route, storage, auth);
addUserAuthApi(route, auth, config);
// What are you looking for? logout? client side will remove token when user click logout,
// or it will auto expire after 24 hours.

@ -0,0 +1,82 @@
import _ from 'lodash';
import {addGravatarSupport, deleteProperties, sortByName} from '../../../lib/utils';
import {addScope, allow} from '../middleware';
import async from 'async';
import marked from 'marked';
function addPackageWebApi(route, storage, auth) {
const can = allow(auth);
// Get list of all visible package
route.get('/packages', function(req, res, next) {
storage.getLocalDatabase(function(err, packages) {
if (err) {
// that function shouldn't produce any
throw err;
}
async.filterSeries(
packages,
function(pkg, cb) {
auth.allow_access(pkg.name, req.remote_user, function(err, allowed) {
setImmediate(function() {
if (err) {
cb(null, false);
} else {
cb(err, allowed);
}
});
});
},
function(err, packages) {
if (err) {
throw err;
}
next(sortByName(packages));
}
);
});
});
// Get package readme
route.get('/package/readme/(@:scope/)?:package/:version?', can('access'), function(req, res, next) {
const packageName = req.params.scope ? addScope(req.params.scope, req.params.package) : req.params.package;
storage.get_package({
name: packageName,
req,
callback: function(err, info) {
if (err) {
return next(err);
}
res.set('Content-Type', 'text/plain');
next(marked(info.readme || 'ERROR: No README data found!'));
},
});
});
route.get('/sidebar/(@:scope/)?:package', function(req, res, next) {
const packageName = req.params.scope ? addScope(req.params.scope, req.params.package) : req.params.package;
storage.get_package({
name: packageName,
keepUpLinkData: true,
req,
callback: function(err, info) {
if (_.isNil(err)) {
info.latest = info.versions[info['dist-tags'].latest];
info = deleteProperties(['readme', 'versions'], info);
info = addGravatarSupport(info);
next(info);
} else {
res.status(404);
res.end();
}
},
});
});
}
export default addPackageWebApi;

@ -0,0 +1,40 @@
import Search from '../../../lib/search';
function addSearchWebApi(route, storage, auth) {
// Search package
route.get('/search/:anything', function(req, res, next) {
const results = Search.query(req.params.anything);
const packages = [];
const getPackageInfo = function(i) {
storage.get_package({
name: results[i].ref,
callback: (err, entry) => {
if (!err && entry) {
auth.allow_access(entry.name, req.remote_user, function(err, allowed) {
if (err || !allowed) {
return;
}
packages.push(entry.versions[entry['dist-tags'].latest]);
});
}
if (i >= results.length - 1) {
next(packages);
} else {
getPackageInfo(i + 1);
}
},
});
};
if (results.length) {
getPackageInfo(0);
} else {
next([]);
}
});
}
export default addSearchWebApi;

@ -0,0 +1,28 @@
import HTTPError from 'http-errors';
import {combineBaseUrl, getWebProtocol} from '../../../lib/utils';
function addUserAuthApi(route, auth, config) {
route.post('/login', function(req, res, next) {
auth.authenticate(req.body.username, req.body.password, (err, user) => {
if (!err) {
req.remote_user = user;
next({
token: auth.issue_token(user, '24h'),
username: req.remote_user.name,
});
} else {
next(HTTPError[err.message ? 401 : 500](err.message));
}
});
});
route.post('/-/logout', function(req, res, next) {
const base = combineBaseUrl(getWebProtocol(req), req.get('host'), config.url_prefix);
res.cookies.set('token', '');
res.redirect(base);
});
}
export default addUserAuthApi;

@ -1,13 +1,13 @@
'use strict';
import {generateGravatarUrl} from '../utils/user';
import assert from 'assert';
import semver from 'semver';
import YAML from 'js-yaml';
import URL from 'url';
import fs from 'fs';
import _ from 'lodash';
import createError from 'http-errors';
const assert = require('assert');
const semver = require('semver');
const YAML = require('js-yaml');
const URL = require('url');
const fs = require('fs');
const _ = require('lodash');
const Logger = require('./logger');
const createError = require('http-errors');
/**
* Validate a package.
@ -380,6 +380,56 @@ function fileExists(path) {
}
}
function sortByName(packages) {
return packages.sort(function(a, b) {
if (a.name < b.name) {
return -1;
} else {
return 1;
}
});
}
function addScope(scope, packageName) {
return `@${scope}/${packageName}`;
}
function deleteProperties(propertiesToDelete, packageInfo) {
_.forEach(propertiesToDelete, (property) => {
delete packageInfo[property];
});
return packageInfo;
}
function addGravatarSupport(info) {
if (_.isString(_.get(info, 'latest.author.email'))) {
info.latest.author.avatar = generateGravatarUrl(info.latest.author.email);
} else {
// _.get can't guarantee author property exist
_.set(info, 'latest.author.avatar', generateGravatarUrl());
}
if (_.get(info, 'latest.contributors.length', 0) > 0) {
info.latest.contributors = _.map(info.latest.contributors, (contributor) => {
if (_.isString(contributor.email)) {
contributor.avatar = generateGravatarUrl(contributor.email);
} else {
contributor.avatar = generateGravatarUrl();
}
return contributor;
}
);
}
return info;
}
module.exports.addGravatarSupport = addGravatarSupport;
module.exports.deleteProperties = deleteProperties;
module.exports.addScope = addScope;
module.exports.sortByName = sortByName;
module.exports.folder_exists = folder_exists;
module.exports.file_exists = fileExists;
module.exports.parseInterval = parseInterval;

@ -7,7 +7,8 @@ import {stringToMD5} from './string';
export function generateGravatarUrl(email?: string): string {
if (typeof email === 'string') {
email = email.trim().toLocaleLowerCase();
let emailMD5 = stringToMD5(email);
const emailMD5 = stringToMD5(email);
return `https://www.gravatar.com/avatar/${emailMD5}`;
} else {
return 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm';