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:
parent
3bb374c84a
commit
1570810f99
@ -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.
|
||||
|
82
src/api/web/endpoint/package.js
Normal file
82
src/api/web/endpoint/package.js
Normal file
@ -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;
|
40
src/api/web/endpoint/search.js
Normal file
40
src/api/web/endpoint/search.js
Normal file
@ -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;
|
28
src/api/web/endpoint/user.js
Normal file
28
src/api/web/endpoint/user.js
Normal file
@ -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';
|
||||
|
Loading…
Reference in New Issue
Block a user