mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-02-21 07:29:37 +01:00
Merge pull request #159 from verdaccio/moving_es6_gradually
Upgrade syntax to ES6
This commit is contained in:
commit
6d8d05b0cc
@ -1,3 +1,4 @@
|
||||
node_modules
|
||||
lib/static
|
||||
coverage/
|
||||
coverage/
|
||||
lib/GUI/
|
||||
|
22
.eslintrc
22
.eslintrc
@ -9,8 +9,11 @@
|
||||
# Created to work with eslint@0.18.0
|
||||
#
|
||||
|
||||
extends: ["eslint:recommended", "google"]
|
||||
|
||||
env:
|
||||
node: true
|
||||
browser: true
|
||||
es6: true
|
||||
|
||||
rules:
|
||||
@ -43,3 +46,22 @@ rules:
|
||||
# useful for code clean-up
|
||||
no-unused-vars: [1, {"vars": "all", "args": "none"}]
|
||||
|
||||
max-len: [1, 160]
|
||||
|
||||
# camelcase is standard, but this should be 1 and then 2 soon
|
||||
camelcase: 0
|
||||
|
||||
# configuration that should be upgraded progresivelly
|
||||
require-jsdoc: 1
|
||||
valid-jsdoc: 1
|
||||
prefer-spread: 1
|
||||
no-constant-condition: 1
|
||||
no-var: 1
|
||||
no-empty: 1
|
||||
guard-for-in: 1
|
||||
no-invalid-this: 1
|
||||
new-cap: 1
|
||||
one-var: 1
|
||||
no-redeclare: 1
|
||||
prefer-rest-params: 1
|
||||
no-console: [1, {"allow": ["log", "warn"]}]
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -18,6 +18,4 @@ coverage/
|
||||
.jshintrc
|
||||
jsconfig.json
|
||||
|
||||
|
||||
# Yarn
|
||||
yarn*
|
||||
.idea/
|
||||
|
@ -4,4 +4,4 @@ node_js:
|
||||
- '6'
|
||||
- '7'
|
||||
sudo: false
|
||||
script: npm install . && npm run test-travis
|
||||
script: npm install . && npm run test-travis
|
36
Gruntfile.js
36
Gruntfile.js
@ -4,36 +4,36 @@ module.exports = function(grunt) {
|
||||
browserify: {
|
||||
dist: {
|
||||
files: {
|
||||
'lib/static/main.js': [ 'lib/GUI/js/main.js' ]
|
||||
'lib/static/main.js': ['lib/GUI/js/main.js'],
|
||||
},
|
||||
options: {
|
||||
debug: true,
|
||||
transform: [ 'browserify-handlebars' ]
|
||||
}
|
||||
}
|
||||
transform: ['browserify-handlebars'],
|
||||
},
|
||||
},
|
||||
},
|
||||
less: {
|
||||
dist: {
|
||||
files: {
|
||||
'lib/static/main.css': [ 'lib/GUI/css/main.less' ]
|
||||
'lib/static/main.css': ['lib/GUI/css/main.less'],
|
||||
},
|
||||
options: {
|
||||
sourceMap: false
|
||||
}
|
||||
}
|
||||
sourceMap: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
files: [ 'lib/GUI/**/*' ],
|
||||
tasks: [ 'default' ]
|
||||
}
|
||||
})
|
||||
files: ['lib/GUI/**/*'],
|
||||
tasks: ['default'],
|
||||
},
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-browserify')
|
||||
grunt.loadNpmTasks('grunt-contrib-watch')
|
||||
grunt.loadNpmTasks('grunt-contrib-less')
|
||||
grunt.loadNpmTasks('grunt-browserify');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
|
||||
grunt.registerTask('default', [
|
||||
'browserify',
|
||||
'less'
|
||||
])
|
||||
}
|
||||
'less',
|
||||
]);
|
||||
};
|
||||
|
@ -1,3 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('../lib/cli')
|
||||
require('../lib/cli');
|
||||
|
5
index.js
5
index.js
@ -1,7 +1,6 @@
|
||||
require('es6-shim')
|
||||
module.exports = require('./lib')
|
||||
module.exports = require('./lib');
|
||||
|
||||
/**package
|
||||
/** package
|
||||
{ "name": "verdaccio",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {"js-yaml": "*"},
|
||||
|
297
lib/GUI/js/bootstrap-modal.js
vendored
297
lib/GUI/js/bootstrap-modal.js
vendored
@ -7,275 +7,272 @@
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
+function($) {
|
||||
'use strict';
|
||||
|
||||
// MODAL CLASS DEFINITION
|
||||
// ======================
|
||||
|
||||
var Modal = function (element, options) {
|
||||
this.options = options
|
||||
this.$body = $(document.body)
|
||||
this.$element = $(element)
|
||||
this.$backdrop =
|
||||
this.isShown = null
|
||||
this.scrollbarWidth = 0
|
||||
let Modal = function(element, options) {
|
||||
this.options = options;
|
||||
this.$body = $(document.body);
|
||||
this.$element = $(element);
|
||||
this.$backdrop =
|
||||
this.isShown = null;
|
||||
this.scrollbarWidth = 0;
|
||||
|
||||
if (this.options.remote) {
|
||||
this.$element
|
||||
.find('.modal-content')
|
||||
.load(this.options.remote, $.proxy(function () {
|
||||
this.$element.trigger('loaded.bs.modal')
|
||||
}, this))
|
||||
.load(this.options.remote, $.proxy(function() {
|
||||
this.$element.trigger('loaded.bs.modal');
|
||||
}, this));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Modal.VERSION = '3.3.0'
|
||||
Modal.VERSION = '3.3.0';
|
||||
|
||||
Modal.TRANSITION_DURATION = 300
|
||||
Modal.BACKDROP_TRANSITION_DURATION = 150
|
||||
Modal.TRANSITION_DURATION = 300;
|
||||
Modal.BACKDROP_TRANSITION_DURATION = 150;
|
||||
|
||||
Modal.DEFAULTS = {
|
||||
backdrop: true,
|
||||
keyboard: true,
|
||||
show: true
|
||||
}
|
||||
show: true,
|
||||
};
|
||||
|
||||
Modal.prototype.toggle = function (_relatedTarget) {
|
||||
return this.isShown ? this.hide() : this.show(_relatedTarget)
|
||||
}
|
||||
Modal.prototype.toggle = function(_relatedTarget) {
|
||||
return this.isShown ? this.hide() : this.show(_relatedTarget);
|
||||
};
|
||||
|
||||
Modal.prototype.show = function (_relatedTarget) {
|
||||
var that = this
|
||||
var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
|
||||
Modal.prototype.show = function(_relatedTarget) {
|
||||
let that = this;
|
||||
let e = $.Event('show.bs.modal', {relatedTarget: _relatedTarget});
|
||||
|
||||
this.$element.trigger(e)
|
||||
this.$element.trigger(e);
|
||||
|
||||
if (this.isShown || e.isDefaultPrevented()) return
|
||||
if (this.isShown || e.isDefaultPrevented()) return;
|
||||
|
||||
this.isShown = true
|
||||
this.isShown = true;
|
||||
|
||||
this.checkScrollbar()
|
||||
this.$body.addClass('modal-open')
|
||||
this.checkScrollbar();
|
||||
this.$body.addClass('modal-open');
|
||||
|
||||
this.setScrollbar()
|
||||
this.escape()
|
||||
this.setScrollbar();
|
||||
this.escape();
|
||||
|
||||
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
|
||||
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this));
|
||||
|
||||
this.backdrop(function () {
|
||||
var transition = $.support.transition && that.$element.hasClass('fade')
|
||||
this.backdrop(function() {
|
||||
let transition = $.support.transition && that.$element.hasClass('fade');
|
||||
|
||||
if (!that.$element.parent().length) {
|
||||
that.$element.appendTo(that.$body) // don't move modals dom position
|
||||
that.$element.appendTo(that.$body); // don't move modals dom position
|
||||
}
|
||||
|
||||
that.$element
|
||||
.show()
|
||||
.scrollTop(0)
|
||||
.scrollTop(0);
|
||||
|
||||
if (transition) {
|
||||
that.$element[0].offsetWidth // force reflow
|
||||
that.$element[0].offsetWidth; // force reflow
|
||||
}
|
||||
|
||||
that.$element
|
||||
.addClass('in')
|
||||
.attr('aria-hidden', false)
|
||||
.attr('aria-hidden', false);
|
||||
|
||||
that.enforceFocus()
|
||||
that.enforceFocus();
|
||||
|
||||
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
|
||||
let e = $.Event('shown.bs.modal', {relatedTarget: _relatedTarget});
|
||||
|
||||
transition ?
|
||||
that.$element.find('.modal-dialog') // wait for modal to slide in
|
||||
.one('bsTransitionEnd', function () {
|
||||
that.$element.trigger('focus').trigger(e)
|
||||
.one('bsTransitionEnd', function() {
|
||||
that.$element.trigger('focus').trigger(e);
|
||||
})
|
||||
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
|
||||
that.$element.trigger('focus').trigger(e)
|
||||
})
|
||||
}
|
||||
that.$element.trigger('focus').trigger(e);
|
||||
});
|
||||
};
|
||||
|
||||
Modal.prototype.hide = function (e) {
|
||||
if (e) e.preventDefault()
|
||||
Modal.prototype.hide = function(e) {
|
||||
if (e) e.preventDefault();
|
||||
|
||||
e = $.Event('hide.bs.modal')
|
||||
e = $.Event('hide.bs.modal');
|
||||
|
||||
this.$element.trigger(e)
|
||||
this.$element.trigger(e);
|
||||
|
||||
if (!this.isShown || e.isDefaultPrevented()) return
|
||||
if (!this.isShown || e.isDefaultPrevented()) return;
|
||||
|
||||
this.isShown = false
|
||||
this.isShown = false;
|
||||
|
||||
this.escape()
|
||||
this.escape();
|
||||
|
||||
$(document).off('focusin.bs.modal')
|
||||
$(document).off('focusin.bs.modal');
|
||||
|
||||
this.$element
|
||||
.removeClass('in')
|
||||
.attr('aria-hidden', true)
|
||||
.off('click.dismiss.bs.modal')
|
||||
.off('click.dismiss.bs.modal');
|
||||
|
||||
$.support.transition && this.$element.hasClass('fade') ?
|
||||
this.$element
|
||||
.one('bsTransitionEnd', $.proxy(this.hideModal, this))
|
||||
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
|
||||
this.hideModal()
|
||||
}
|
||||
this.hideModal();
|
||||
};
|
||||
|
||||
Modal.prototype.enforceFocus = function () {
|
||||
Modal.prototype.enforceFocus = function() {
|
||||
$(document)
|
||||
.off('focusin.bs.modal') // guard against infinite focus loop
|
||||
.on('focusin.bs.modal', $.proxy(function (e) {
|
||||
.on('focusin.bs.modal', $.proxy(function(e) {
|
||||
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
|
||||
this.$element.trigger('focus')
|
||||
this.$element.trigger('focus');
|
||||
}
|
||||
}, this))
|
||||
}
|
||||
}, this));
|
||||
};
|
||||
|
||||
Modal.prototype.escape = function () {
|
||||
Modal.prototype.escape = function() {
|
||||
if (this.isShown && this.options.keyboard) {
|
||||
this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
|
||||
e.which == 27 && this.hide()
|
||||
}, this))
|
||||
this.$element.on('keydown.dismiss.bs.modal', $.proxy(function(e) {
|
||||
e.which == 27 && this.hide();
|
||||
}, this));
|
||||
} else if (!this.isShown) {
|
||||
this.$element.off('keydown.dismiss.bs.modal')
|
||||
this.$element.off('keydown.dismiss.bs.modal');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Modal.prototype.hideModal = function () {
|
||||
var that = this
|
||||
this.$element.hide()
|
||||
this.backdrop(function () {
|
||||
that.$body.removeClass('modal-open')
|
||||
that.resetScrollbar()
|
||||
that.$element.trigger('hidden.bs.modal')
|
||||
})
|
||||
}
|
||||
Modal.prototype.hideModal = function() {
|
||||
let that = this;
|
||||
this.$element.hide();
|
||||
this.backdrop(function() {
|
||||
that.$body.removeClass('modal-open');
|
||||
that.resetScrollbar();
|
||||
that.$element.trigger('hidden.bs.modal');
|
||||
});
|
||||
};
|
||||
|
||||
Modal.prototype.removeBackdrop = function () {
|
||||
this.$backdrop && this.$backdrop.remove()
|
||||
this.$backdrop = null
|
||||
}
|
||||
Modal.prototype.removeBackdrop = function() {
|
||||
this.$backdrop && this.$backdrop.remove();
|
||||
this.$backdrop = null;
|
||||
};
|
||||
|
||||
Modal.prototype.backdrop = function (callback) {
|
||||
var that = this
|
||||
var animate = this.$element.hasClass('fade') ? 'fade' : ''
|
||||
Modal.prototype.backdrop = function(callback) {
|
||||
let that = this;
|
||||
let animate = this.$element.hasClass('fade') ? 'fade' : '';
|
||||
|
||||
if (this.isShown && this.options.backdrop) {
|
||||
var doAnimate = $.support.transition && animate
|
||||
let doAnimate = $.support.transition && animate;
|
||||
|
||||
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
|
||||
.prependTo(this.$element)
|
||||
.on('click.dismiss.bs.modal', $.proxy(function (e) {
|
||||
if (e.target !== e.currentTarget) return
|
||||
.on('click.dismiss.bs.modal', $.proxy(function(e) {
|
||||
if (e.target !== e.currentTarget) return;
|
||||
this.options.backdrop == 'static'
|
||||
? this.$element[0].focus.call(this.$element[0])
|
||||
: this.hide.call(this)
|
||||
}, this))
|
||||
: this.hide.call(this);
|
||||
}, this));
|
||||
|
||||
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
|
||||
if (doAnimate) this.$backdrop[0].offsetWidth; // force reflow
|
||||
|
||||
this.$backdrop.addClass('in')
|
||||
this.$backdrop.addClass('in');
|
||||
|
||||
if (!callback) return
|
||||
if (!callback) return;
|
||||
|
||||
doAnimate ?
|
||||
this.$backdrop
|
||||
.one('bsTransitionEnd', callback)
|
||||
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
|
||||
callback()
|
||||
|
||||
callback();
|
||||
} else if (!this.isShown && this.$backdrop) {
|
||||
this.$backdrop.removeClass('in')
|
||||
this.$backdrop.removeClass('in');
|
||||
|
||||
var callbackRemove = function () {
|
||||
that.removeBackdrop()
|
||||
callback && callback()
|
||||
}
|
||||
let callbackRemove = function() {
|
||||
that.removeBackdrop();
|
||||
callback && callback();
|
||||
};
|
||||
$.support.transition && this.$element.hasClass('fade') ?
|
||||
this.$backdrop
|
||||
.one('bsTransitionEnd', callbackRemove)
|
||||
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
|
||||
callbackRemove()
|
||||
|
||||
callbackRemove();
|
||||
} else if (callback) {
|
||||
callback()
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Modal.prototype.checkScrollbar = function () {
|
||||
this.scrollbarWidth = this.measureScrollbar()
|
||||
}
|
||||
Modal.prototype.checkScrollbar = function() {
|
||||
this.scrollbarWidth = this.measureScrollbar();
|
||||
};
|
||||
|
||||
Modal.prototype.setScrollbar = function () {
|
||||
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
|
||||
if (this.scrollbarWidth) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
|
||||
}
|
||||
Modal.prototype.setScrollbar = function() {
|
||||
let bodyPad = parseInt((this.$body.css('padding-right') || 0), 10);
|
||||
if (this.scrollbarWidth) this.$body.css('padding-right', bodyPad + this.scrollbarWidth);
|
||||
};
|
||||
|
||||
Modal.prototype.resetScrollbar = function () {
|
||||
this.$body.css('padding-right', '')
|
||||
}
|
||||
Modal.prototype.resetScrollbar = function() {
|
||||
this.$body.css('padding-right', '');
|
||||
};
|
||||
|
||||
Modal.prototype.measureScrollbar = function () { // thx walsh
|
||||
if (document.body.clientWidth >= window.innerWidth) return 0
|
||||
var scrollDiv = document.createElement('div')
|
||||
scrollDiv.className = 'modal-scrollbar-measure'
|
||||
this.$body.append(scrollDiv)
|
||||
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
|
||||
this.$body[0].removeChild(scrollDiv)
|
||||
return scrollbarWidth
|
||||
}
|
||||
Modal.prototype.measureScrollbar = function() { // thx walsh
|
||||
if (document.body.clientWidth >= window.innerWidth) return 0;
|
||||
let scrollDiv = document.createElement('div');
|
||||
scrollDiv.className = 'modal-scrollbar-measure';
|
||||
this.$body.append(scrollDiv);
|
||||
let scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
||||
this.$body[0].removeChild(scrollDiv);
|
||||
return scrollbarWidth;
|
||||
};
|
||||
|
||||
|
||||
// MODAL PLUGIN DEFINITION
|
||||
// =======================
|
||||
|
||||
function Plugin(option, _relatedTarget) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.modal')
|
||||
var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
return this.each(function() {
|
||||
let $this = $(this);
|
||||
let data = $this.data('bs.modal');
|
||||
let options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option);
|
||||
|
||||
if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
|
||||
if (typeof option == 'string') data[option](_relatedTarget)
|
||||
else if (options.show) data.show(_relatedTarget)
|
||||
})
|
||||
if (!data) $this.data('bs.modal', (data = new Modal(this, options)));
|
||||
if (typeof option == 'string') data[option](_relatedTarget);
|
||||
else if (options.show) data.show(_relatedTarget);
|
||||
});
|
||||
}
|
||||
|
||||
var old = $.fn.modal
|
||||
let old = $.fn.modal;
|
||||
|
||||
$.fn.modal = Plugin
|
||||
$.fn.modal.Constructor = Modal
|
||||
$.fn.modal = Plugin;
|
||||
$.fn.modal.Constructor = Modal;
|
||||
|
||||
|
||||
// MODAL NO CONFLICT
|
||||
// =================
|
||||
|
||||
$.fn.modal.noConflict = function () {
|
||||
$.fn.modal = old
|
||||
return this
|
||||
}
|
||||
$.fn.modal.noConflict = function() {
|
||||
$.fn.modal = old;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
// MODAL DATA-API
|
||||
// ==============
|
||||
|
||||
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
|
||||
var $this = $(this)
|
||||
var href = $this.attr('href')
|
||||
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
|
||||
var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
|
||||
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function(e) {
|
||||
let $this = $(this);
|
||||
let href = $this.attr('href');
|
||||
let $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))); // strip for ie7
|
||||
let option = $target.data('bs.modal') ? 'toggle' : $.extend({remote: !/#/.test(href) && href}, $target.data(), $this.data());
|
||||
|
||||
if ($this.is('a')) e.preventDefault()
|
||||
|
||||
$target.one('show.bs.modal', function (showEvent) {
|
||||
if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
|
||||
$target.one('hidden.bs.modal', function () {
|
||||
$this.is(':visible') && $this.trigger('focus')
|
||||
})
|
||||
})
|
||||
Plugin.call($target, option, this)
|
||||
})
|
||||
if ($this.is('a')) e.preventDefault();
|
||||
|
||||
$target.one('show.bs.modal', function(showEvent) {
|
||||
if (showEvent.isDefaultPrevented()) return; // only register focus restorer if modal will actually get shown
|
||||
$target.one('hidden.bs.modal', function() {
|
||||
$this.is(':visible') && $this.trigger('focus');
|
||||
});
|
||||
});
|
||||
Plugin.call($target, option, this);
|
||||
});
|
||||
}(jQuery);
|
||||
|
@ -1,54 +1,53 @@
|
||||
var $ = require('unopinionate').selector
|
||||
var onClick = require('onclick')
|
||||
var transitionComplete = require('transition-complete')
|
||||
let $ = require('unopinionate').selector;
|
||||
let onClick = require('onclick');
|
||||
let transitionComplete = require('transition-complete');
|
||||
|
||||
$(function() {
|
||||
onClick('.entry .name', function() {
|
||||
var $this = $(this)
|
||||
var $entry = $this.closest('.entry')
|
||||
let $this = $(this);
|
||||
let $entry = $this.closest('.entry');
|
||||
|
||||
if ($entry.hasClass('open')) {
|
||||
// Close entry
|
||||
$entry
|
||||
.height($entry.outerHeight())
|
||||
.removeClass('open')
|
||||
.removeClass('open');
|
||||
|
||||
setTimeout(function() {
|
||||
$entry.css('height', $entry.attr('data-height') + 'px')
|
||||
}, 0)
|
||||
$entry.css('height', $entry.attr('data-height') + 'px');
|
||||
}, 0);
|
||||
|
||||
transitionComplete(function() {
|
||||
$entry.find('.readme').remove()
|
||||
$entry.css('height', 'auto')
|
||||
})
|
||||
|
||||
$entry.find('.readme').remove();
|
||||
$entry.css('height', 'auto');
|
||||
});
|
||||
} else {
|
||||
// Open entry
|
||||
$('.entry.open').each(function() {
|
||||
// Close open entries
|
||||
var $entry = $(this)
|
||||
let $entry = $(this);
|
||||
$entry
|
||||
.height($entry.outerHeight())
|
||||
.removeClass('open')
|
||||
.removeClass('open');
|
||||
|
||||
setTimeout(function() {
|
||||
$entry.css('height', $entry.attr('data-height') + 'px')
|
||||
}, 0)
|
||||
$entry.css('height', $entry.attr('data-height') + 'px');
|
||||
}, 0);
|
||||
|
||||
transitionComplete(function() {
|
||||
$entry.find('.readme').remove()
|
||||
$entry.css('height', 'auto')
|
||||
})
|
||||
})
|
||||
$entry.find('.readme').remove();
|
||||
$entry.css('height', 'auto');
|
||||
});
|
||||
});
|
||||
|
||||
// Add the open class
|
||||
$entry.addClass('open')
|
||||
$entry.addClass('open');
|
||||
|
||||
// Explicitly set heights for transitions
|
||||
var height = $entry.outerHeight()
|
||||
let height = $entry.outerHeight();
|
||||
$entry
|
||||
.attr('data-height', height)
|
||||
.css('height', height)
|
||||
.css('height', height);
|
||||
|
||||
// Get the data
|
||||
$.ajax({
|
||||
@ -57,17 +56,17 @@ $(function() {
|
||||
+ encodeURIComponent($entry.attr('data-version')),
|
||||
dataType: 'text',
|
||||
success: function(html) {
|
||||
var $readme = $("<div class='readme'>")
|
||||
let $readme = $('<div class=\'readme\'>')
|
||||
.html(html)
|
||||
.appendTo($entry)
|
||||
.appendTo($entry);
|
||||
|
||||
$entry.height(height + $readme.outerHeight())
|
||||
$entry.height(height + $readme.outerHeight());
|
||||
|
||||
transitionComplete(function() {
|
||||
$entry.css('height', 'auto')
|
||||
})
|
||||
}
|
||||
})
|
||||
$entry.css('height', 'auto');
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
@ -1,13 +1,13 @@
|
||||
// twitter bootstrap stuff;
|
||||
// not in static 'cause I want it to be bundled with the rest of javascripts
|
||||
require('./bootstrap-modal')
|
||||
require('./bootstrap-modal');
|
||||
|
||||
// our own files
|
||||
require('./search')
|
||||
require('./entry')
|
||||
require('./search');
|
||||
require('./entry');
|
||||
|
||||
var $ = require('unopinionate').selector
|
||||
let $ = require('unopinionate').selector;
|
||||
$(document).on('click', '.js-userLogoutBtn', function() {
|
||||
$('#userLogoutForm').submit()
|
||||
return false
|
||||
})
|
||||
$('#userLogoutForm').submit();
|
||||
return false;
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
var $ = require('unopinionate').selector
|
||||
var template = require('../entry.hbs')
|
||||
let $ = require('unopinionate').selector;
|
||||
let template = require('../entry.hbs');
|
||||
|
||||
$(function() {
|
||||
;(function(window, document) {
|
||||
@ -22,7 +22,8 @@ $(function() {
|
||||
$form.bind('submit keyup', function(e) {
|
||||
var query, isValidQuery
|
||||
|
||||
e.preventDefault()
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
query = $input.val()
|
||||
isValidQuery = (query !== '')
|
||||
@ -31,35 +32,35 @@ $(function() {
|
||||
|
||||
if (!isValidQuery) {
|
||||
if (request && typeof request.abort === 'function') {
|
||||
request.abort()
|
||||
request.abort();
|
||||
}
|
||||
|
||||
$searchResults.html('')
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (request && typeof request.abort === 'function') {
|
||||
request.abort()
|
||||
request.abort();
|
||||
}
|
||||
|
||||
if (query !== lastQuery) {
|
||||
lastQuery = query
|
||||
$searchResults.html(
|
||||
"<img class='search-ajax' src='-/static/ajax.gif' alt='Spinner'/>")
|
||||
'<img class=\'search-ajax\' src=\'-/static/ajax.gif\' alt=\'Spinner\'/>');
|
||||
}
|
||||
|
||||
request = $.getJSON('-/search/' + query, function( results ) {
|
||||
if (results.length > 0) {
|
||||
var html = ''
|
||||
let html = '';
|
||||
|
||||
$.each(results, function(i, entry) {
|
||||
html += template(entry)
|
||||
})
|
||||
html += template(entry);
|
||||
});
|
||||
|
||||
$searchResults.html(html)
|
||||
$searchResults.html(html);
|
||||
} else {
|
||||
$searchResults.html(
|
||||
"<div class='no-results'><big>No Results</big></div>")
|
||||
'<div class=\'no-results\'><big>No Results</big></div>');
|
||||
}
|
||||
}).fail(function () {
|
||||
$searchResults.html(
|
||||
@ -68,10 +69,9 @@ $(function() {
|
||||
})
|
||||
|
||||
$(document).on('click', '.icon-cancel', function(e) {
|
||||
e.preventDefault()
|
||||
$input.val('')
|
||||
$form.keyup()
|
||||
})
|
||||
|
||||
})(window, window.document)
|
||||
})
|
||||
e.preventDefault();
|
||||
$input.val('');
|
||||
$form.keyup();
|
||||
});
|
||||
})(window, window.document);
|
||||
});
|
||||
|
376
lib/auth.js
376
lib/auth.js
@ -1,33 +1,37 @@
|
||||
var Crypto = require('crypto')
|
||||
var jju = require('jju')
|
||||
var Error = require('http-errors')
|
||||
var Logger = require('./logger')
|
||||
var load_plugins = require('./plugin-loader').load_plugins
|
||||
/* eslint prefer-spread: "off" */
|
||||
|
||||
module.exports = Auth
|
||||
'use strict';
|
||||
|
||||
const Crypto = require('crypto');
|
||||
const jju = require('jju');
|
||||
const Error = require('http-errors');
|
||||
const Logger = require('./logger');
|
||||
const load_plugins = require('./plugin-loader').load_plugins;
|
||||
|
||||
module.exports = Auth;
|
||||
|
||||
function Auth(config) {
|
||||
var self = Object.create(Auth.prototype)
|
||||
self.config = config
|
||||
self.logger = Logger.logger.child({ sub: 'auth' })
|
||||
self.secret = config.secret
|
||||
let self = Object.create(Auth.prototype);
|
||||
self.config = config;
|
||||
self.logger = Logger.logger.child({sub: 'auth'});
|
||||
self.secret = config.secret;
|
||||
|
||||
var plugin_params = {
|
||||
let plugin_params = {
|
||||
config: config,
|
||||
logger: self.logger
|
||||
}
|
||||
logger: self.logger,
|
||||
};
|
||||
|
||||
if (config.users_file) {
|
||||
if (!config.auth || !config.auth.htpasswd) {
|
||||
// b/w compat
|
||||
config.auth = config.auth || {}
|
||||
config.auth.htpasswd = { file: config.users_file }
|
||||
config.auth = config.auth || {};
|
||||
config.auth.htpasswd = {file: config.users_file};
|
||||
}
|
||||
}
|
||||
|
||||
self.plugins = load_plugins(config, config.auth, plugin_params, function (p) {
|
||||
return p.authenticate || p.allow_access || p.allow_publish
|
||||
})
|
||||
self.plugins = load_plugins(config, config.auth, plugin_params, function(p) {
|
||||
return p.authenticate || p.allow_access || p.allow_publish;
|
||||
});
|
||||
|
||||
self.plugins.unshift({
|
||||
verdaccio_version: '1.1.0',
|
||||
@ -38,342 +42,342 @@ function Auth(config) {
|
||||
&& (Crypto.createHash('sha1').update(password).digest('hex')
|
||||
=== config.users[user].password)
|
||||
) {
|
||||
return cb(null, [ user ])
|
||||
return cb(null, [user]);
|
||||
}
|
||||
|
||||
return cb()
|
||||
return cb();
|
||||
},
|
||||
|
||||
adduser: function(user, password, cb) {
|
||||
if (config.users && config.users[user])
|
||||
return cb( Error[403]('this user already exists') )
|
||||
return cb( Error[403]('this user already exists') );
|
||||
|
||||
return cb()
|
||||
return cb();
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
function allow_action(action) {
|
||||
return function(user, package, cb) {
|
||||
var ok = package[action].reduce(function(prev, curr) {
|
||||
if (user.groups.indexOf(curr) !== -1) return true
|
||||
return prev
|
||||
}, false)
|
||||
return function(user, pkg, cb) {
|
||||
let ok = pkg[action].reduce(function(prev, curr) {
|
||||
if (user.groups.indexOf(curr) !== -1) return true;
|
||||
return prev;
|
||||
}, false);
|
||||
|
||||
if (ok) return cb(null, true)
|
||||
if (ok) return cb(null, true);
|
||||
|
||||
if (user.name) {
|
||||
cb( Error[403]('user ' + user.name + ' is not allowed to ' + action + ' package ' + package.name) )
|
||||
cb( Error[403]('user ' + user.name + ' is not allowed to ' + action + ' package ' + pkg.name) );
|
||||
} else {
|
||||
cb( Error[403]('unregistered users are not allowed to ' + action + ' package ' + package.name) )
|
||||
cb( Error[403]('unregistered users are not allowed to ' + action + ' package ' + pkg.name) );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
self.plugins.push({
|
||||
authenticate: function(user, password, cb) {
|
||||
return cb( Error[403]('bad username/password, access denied') )
|
||||
return cb( Error[403]('bad username/password, access denied') );
|
||||
},
|
||||
|
||||
add_user: function(user, password, cb) {
|
||||
return cb( Error[409]('registration is disabled') )
|
||||
return cb( Error[409]('registration is disabled') );
|
||||
},
|
||||
|
||||
allow_access: allow_action('access'),
|
||||
allow_publish: allow_action('publish'),
|
||||
})
|
||||
});
|
||||
|
||||
return self
|
||||
return self;
|
||||
}
|
||||
|
||||
Auth.prototype.authenticate = function(user, password, cb) {
|
||||
var plugins = this.plugins.slice(0)
|
||||
let plugins = this.plugins.slice(0)
|
||||
|
||||
;(function next() {
|
||||
var p = plugins.shift()
|
||||
let p = plugins.shift();
|
||||
|
||||
if (typeof(p.authenticate) !== 'function') {
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
p.authenticate(user, password, function(err, groups) {
|
||||
if (err) return cb(err)
|
||||
if (err) return cb(err);
|
||||
if (groups != null && groups != false)
|
||||
return cb(err, AuthenticatedUser(user, groups))
|
||||
next()
|
||||
})
|
||||
})()
|
||||
}
|
||||
return cb(err, authenticatedUser(user, groups));
|
||||
next();
|
||||
});
|
||||
})();
|
||||
};
|
||||
|
||||
Auth.prototype.add_user = function(user, password, cb) {
|
||||
var self = this
|
||||
var plugins = this.plugins.slice(0)
|
||||
let self = this;
|
||||
let plugins = this.plugins.slice(0)
|
||||
|
||||
;(function next() {
|
||||
var p = plugins.shift()
|
||||
var n = 'adduser'
|
||||
let p = plugins.shift();
|
||||
let n = 'adduser';
|
||||
if (typeof(p[n]) !== 'function') {
|
||||
n = 'add_user'
|
||||
n = 'add_user';
|
||||
}
|
||||
if (typeof(p[n]) !== 'function') {
|
||||
next()
|
||||
next();
|
||||
} else {
|
||||
p[n](user, password, function(err, ok) {
|
||||
if (err) return cb(err)
|
||||
if (ok) return self.authenticate(user, password, cb)
|
||||
next()
|
||||
})
|
||||
if (err) return cb(err);
|
||||
if (ok) return self.authenticate(user, password, cb);
|
||||
next();
|
||||
});
|
||||
}
|
||||
})()
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
Auth.prototype.allow_access = function(package_name, user, callback) {
|
||||
var plugins = this.plugins.slice(0)
|
||||
var package = Object.assign({ name: package_name },
|
||||
let plugins = this.plugins.slice(0);
|
||||
let pkg = Object.assign({name: package_name},
|
||||
this.config.get_package_spec(package_name))
|
||||
|
||||
;(function next() {
|
||||
var p = plugins.shift()
|
||||
let p = plugins.shift();
|
||||
|
||||
if (typeof(p.allow_access) !== 'function') {
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
p.allow_access(user, package, function(err, ok) {
|
||||
if (err) return callback(err)
|
||||
if (ok) return callback(null, ok)
|
||||
next() // cb(null, false) causes next plugin to roll
|
||||
})
|
||||
})()
|
||||
}
|
||||
p.allow_access(user, pkg, function(err, ok) {
|
||||
if (err) return callback(err);
|
||||
if (ok) return callback(null, ok);
|
||||
next(); // cb(null, false) causes next plugin to roll
|
||||
});
|
||||
})();
|
||||
};
|
||||
|
||||
Auth.prototype.allow_publish = function(package_name, user, callback) {
|
||||
var plugins = this.plugins.slice(0)
|
||||
var package = Object.assign({ name: package_name },
|
||||
let plugins = this.plugins.slice(0);
|
||||
let pkg = Object.assign({name: package_name},
|
||||
this.config.get_package_spec(package_name))
|
||||
|
||||
;(function next() {
|
||||
var p = plugins.shift()
|
||||
let p = plugins.shift();
|
||||
|
||||
if (typeof(p.allow_publish) !== 'function') {
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
p.allow_publish(user, package, function(err, ok) {
|
||||
if (err) return callback(err)
|
||||
if (ok) return callback(null, ok)
|
||||
next() // cb(null, false) causes next plugin to roll
|
||||
})
|
||||
})()
|
||||
}
|
||||
p.allow_publish(user, pkg, function(err, ok) {
|
||||
if (err) return callback(err);
|
||||
if (ok) return callback(null, ok);
|
||||
next(); // cb(null, false) causes next plugin to roll
|
||||
});
|
||||
})();
|
||||
};
|
||||
|
||||
Auth.prototype.basic_middleware = function() {
|
||||
var self = this
|
||||
let self = this;
|
||||
return function(req, res, _next) {
|
||||
req.pause()
|
||||
req.pause();
|
||||
function next(err) {
|
||||
req.resume()
|
||||
req.resume();
|
||||
// uncomment this to reject users with bad auth headers
|
||||
//return _next.apply(null, arguments)
|
||||
// return _next.apply(null, arguments)
|
||||
|
||||
// swallow error, user remains unauthorized
|
||||
// set remoteUserError to indicate that user was attempting authentication
|
||||
if (err) req.remote_user.error = err.message
|
||||
return _next()
|
||||
if (err) req.remote_user.error = err.message;
|
||||
return _next();
|
||||
}
|
||||
|
||||
if (req.remote_user != null && req.remote_user.name !== undefined)
|
||||
return next()
|
||||
req.remote_user = AnonymousUser()
|
||||
return next();
|
||||
req.remote_user = buildAnonymousUser();
|
||||
|
||||
var authorization = req.headers.authorization
|
||||
if (authorization == null) return next()
|
||||
let authorization = req.headers.authorization;
|
||||
if (authorization == null) return next();
|
||||
|
||||
var parts = authorization.split(' ')
|
||||
let parts = authorization.split(' ');
|
||||
|
||||
if (parts.length !== 2)
|
||||
return next( Error[400]('bad authorization header') )
|
||||
return next( Error[400]('bad authorization header') );
|
||||
|
||||
var scheme = parts[0]
|
||||
let scheme = parts[0];
|
||||
if (scheme === 'Basic') {
|
||||
var credentials = new Buffer(parts[1], 'base64').toString()
|
||||
var credentials = new Buffer(parts[1], 'base64').toString();
|
||||
} else if (scheme === 'Bearer') {
|
||||
var credentials = self.aes_decrypt(new Buffer(parts[1], 'base64')).toString('utf8')
|
||||
if (!credentials) return next()
|
||||
var credentials = self.aes_decrypt(new Buffer(parts[1], 'base64')).toString('utf8');
|
||||
if (!credentials) return next();
|
||||
} else {
|
||||
return next()
|
||||
return next();
|
||||
}
|
||||
|
||||
var index = credentials.indexOf(':')
|
||||
if (index < 0) return next()
|
||||
let index = credentials.indexOf(':');
|
||||
if (index < 0) return next();
|
||||
|
||||
var user = credentials.slice(0, index)
|
||||
var pass = credentials.slice(index + 1)
|
||||
let user = credentials.slice(0, index);
|
||||
let pass = credentials.slice(index + 1);
|
||||
|
||||
self.authenticate(user, pass, function(err, user) {
|
||||
if (!err) {
|
||||
req.remote_user = user
|
||||
next()
|
||||
req.remote_user = user;
|
||||
next();
|
||||
} else {
|
||||
req.remote_user = AnonymousUser()
|
||||
next(err)
|
||||
req.remote_user = buildAnonymousUser();
|
||||
next(err);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
Auth.prototype.bearer_middleware = function() {
|
||||
var self = this
|
||||
let self = this;
|
||||
return function(req, res, _next) {
|
||||
req.pause()
|
||||
req.pause();
|
||||
function next(_err) {
|
||||
req.resume()
|
||||
return _next.apply(null, arguments)
|
||||
req.resume();
|
||||
return _next.apply(null, arguments);
|
||||
}
|
||||
|
||||
if (req.remote_user != null && req.remote_user.name !== undefined)
|
||||
return next()
|
||||
req.remote_user = AnonymousUser()
|
||||
return next();
|
||||
req.remote_user = buildAnonymousUser();
|
||||
|
||||
var authorization = req.headers.authorization
|
||||
if (authorization == null) return next()
|
||||
let authorization = req.headers.authorization;
|
||||
if (authorization == null) return next();
|
||||
|
||||
var parts = authorization.split(' ')
|
||||
let parts = authorization.split(' ');
|
||||
|
||||
if (parts.length !== 2)
|
||||
return next( Error[400]('bad authorization header') )
|
||||
return next( Error[400]('bad authorization header') );
|
||||
|
||||
var scheme = parts[0]
|
||||
var token = parts[1]
|
||||
let scheme = parts[0];
|
||||
let token = parts[1];
|
||||
|
||||
if (scheme !== 'Bearer')
|
||||
return next()
|
||||
return next();
|
||||
|
||||
try {
|
||||
var user = self.decode_token(token)
|
||||
var user = self.decode_token(token);
|
||||
} catch(err) {
|
||||
return next(err)
|
||||
return next(err);
|
||||
}
|
||||
|
||||
req.remote_user = AuthenticatedUser(user.u, user.g)
|
||||
req.remote_user.token = token
|
||||
next()
|
||||
}
|
||||
}
|
||||
req.remote_user = authenticatedUser(user.u, user.g);
|
||||
req.remote_user.token = token;
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
Auth.prototype.cookie_middleware = function() {
|
||||
var self = this
|
||||
let self = this;
|
||||
return function(req, res, _next) {
|
||||
req.pause()
|
||||
req.pause();
|
||||
function next(_err) {
|
||||
req.resume()
|
||||
return _next()
|
||||
req.resume();
|
||||
return _next();
|
||||
}
|
||||
|
||||
if (req.remote_user != null && req.remote_user.name !== undefined)
|
||||
return next()
|
||||
return next();
|
||||
|
||||
req.remote_user = AnonymousUser()
|
||||
req.remote_user = buildAnonymousUser();
|
||||
|
||||
var token = req.cookies.get('token')
|
||||
if (token == null) return next()
|
||||
let token = req.cookies.get('token');
|
||||
if (token == null) return next();
|
||||
|
||||
/*try {
|
||||
/* try {
|
||||
var user = self.decode_token(token, 60*60)
|
||||
} catch(err) {
|
||||
return next()
|
||||
}
|
||||
|
||||
req.remote_user = AuthenticatedUser(user.u, user.g)
|
||||
req.remote_user = authenticatedUser(user.u, user.g)
|
||||
req.remote_user.token = token
|
||||
next()*/
|
||||
var credentials = self.aes_decrypt(new Buffer(token, 'base64')).toString('utf8')
|
||||
if (!credentials) return next()
|
||||
let credentials = self.aes_decrypt(new Buffer(token, 'base64')).toString('utf8');
|
||||
if (!credentials) return next();
|
||||
|
||||
var index = credentials.indexOf(':')
|
||||
if (index < 0) return next()
|
||||
let index = credentials.indexOf(':');
|
||||
if (index < 0) return next();
|
||||
|
||||
var user = credentials.slice(0, index)
|
||||
var pass = credentials.slice(index + 1)
|
||||
let user = credentials.slice(0, index);
|
||||
let pass = credentials.slice(index + 1);
|
||||
|
||||
self.authenticate(user, pass, function(err, user) {
|
||||
if (!err) {
|
||||
req.remote_user = user
|
||||
next()
|
||||
req.remote_user = user;
|
||||
next();
|
||||
} else {
|
||||
req.remote_user = AnonymousUser()
|
||||
next(err)
|
||||
req.remote_user = buildAnonymousUser();
|
||||
next(err);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
Auth.prototype.issue_token = function(user) {
|
||||
var data = jju.stringify({
|
||||
let data = jju.stringify({
|
||||
u: user.name,
|
||||
g: user.real_groups && user.real_groups.length ? user.real_groups : undefined,
|
||||
t: ~~(Date.now()/1000),
|
||||
}, { indent: false })
|
||||
}, {indent: false});
|
||||
|
||||
data = new Buffer(data, 'utf8')
|
||||
var mac = Crypto.createHmac('sha256', this.secret).update(data).digest()
|
||||
return Buffer.concat([ data, mac ]).toString('base64')
|
||||
}
|
||||
data = new Buffer(data, 'utf8');
|
||||
let mac = Crypto.createHmac('sha256', this.secret).update(data).digest();
|
||||
return Buffer.concat([data, mac]).toString('base64');
|
||||
};
|
||||
|
||||
Auth.prototype.decode_token = function(str, expire_time) {
|
||||
var buf = new Buffer(str, 'base64')
|
||||
if (buf.length <= 32) throw Error[401]('invalid token')
|
||||
let buf = new Buffer(str, 'base64');
|
||||
if (buf.length <= 32) throw Error[401]('invalid token');
|
||||
|
||||
var data = buf.slice(0, buf.length - 32)
|
||||
var their_mac = buf.slice(buf.length - 32)
|
||||
var good_mac = Crypto.createHmac('sha256', this.secret).update(data).digest()
|
||||
let data = buf.slice(0, buf.length - 32);
|
||||
let their_mac = buf.slice(buf.length - 32);
|
||||
let good_mac = Crypto.createHmac('sha256', this.secret).update(data).digest();
|
||||
|
||||
their_mac = Crypto.createHash('sha512').update(their_mac).digest('hex')
|
||||
good_mac = Crypto.createHash('sha512').update(good_mac).digest('hex')
|
||||
if (their_mac !== good_mac) throw Error[401]('bad signature')
|
||||
their_mac = Crypto.createHash('sha512').update(their_mac).digest('hex');
|
||||
good_mac = Crypto.createHash('sha512').update(good_mac).digest('hex');
|
||||
if (their_mac !== good_mac) throw Error[401]('bad signature');
|
||||
|
||||
// make token expire in 24 hours
|
||||
// TODO: make configurable?
|
||||
expire_time = expire_time || 24*60*60
|
||||
expire_time = expire_time || 24*60*60;
|
||||
|
||||
data = jju.parse(data.toString('utf8'))
|
||||
data = jju.parse(data.toString('utf8'));
|
||||
if (Math.abs(data.t - ~~(Date.now()/1000)) > expire_time)
|
||||
throw Error[401]('token expired')
|
||||
throw Error[401]('token expired');
|
||||
|
||||
return data
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
Auth.prototype.aes_encrypt = function(buf) {
|
||||
var c = Crypto.createCipher('aes192', this.secret)
|
||||
var b1 = c.update(buf)
|
||||
var b2 = c.final()
|
||||
return Buffer.concat([ b1, b2 ])
|
||||
}
|
||||
let c = Crypto.createCipher('aes192', this.secret);
|
||||
let b1 = c.update(buf);
|
||||
let b2 = c.final();
|
||||
return Buffer.concat([b1, b2]);
|
||||
};
|
||||
|
||||
Auth.prototype.aes_decrypt = function(buf) {
|
||||
try {
|
||||
var c = Crypto.createDecipher('aes192', this.secret)
|
||||
var b1 = c.update(buf)
|
||||
var b2 = c.final()
|
||||
let c = Crypto.createDecipher('aes192', this.secret);
|
||||
let b1 = c.update(buf);
|
||||
let b2 = c.final();
|
||||
return Buffer.concat([b1, b2]);
|
||||
} catch(_) {
|
||||
return new Buffer(0)
|
||||
return new Buffer(0);
|
||||
}
|
||||
return Buffer.concat([ b1, b2 ])
|
||||
}
|
||||
};
|
||||
|
||||
function AnonymousUser() {
|
||||
function buildAnonymousUser() {
|
||||
return {
|
||||
name: undefined,
|
||||
// groups without '$' are going to be deprecated eventually
|
||||
groups: [ '$all', '$anonymous', '@all', '@anonymous', 'all', 'undefined', 'anonymous' ],
|
||||
groups: ['$all', '$anonymous', '@all', '@anonymous', 'all', 'undefined', 'anonymous'],
|
||||
real_groups: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function AuthenticatedUser(name, groups) {
|
||||
var _groups = (groups || []).concat([ '$all', '$authenticated', '@all', '@authenticated', 'all' ])
|
||||
function authenticatedUser(name, groups) {
|
||||
let _groups = (groups || []).concat(['$all', '$authenticated', '@all', '@authenticated', 'all']);
|
||||
return {
|
||||
name: name,
|
||||
groups: _groups,
|
||||
real_groups: groups,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
134
lib/cli.js
134
lib/cli.js
@ -1,114 +1,113 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*eslint no-sync:0*/
|
||||
/* eslint no-sync:0*/
|
||||
'use strict';
|
||||
|
||||
if (process.getuid && process.getuid() === 0) {
|
||||
global.console.error("Verdaccio doesn't need superuser privileges. Don't run it under root.")
|
||||
global.console.error('Verdaccio doesn\'t need superuser privileges. Don\'t run it under root.');
|
||||
}
|
||||
|
||||
process.title = 'verdaccio'
|
||||
require('es6-shim')
|
||||
process.title = 'verdaccio';
|
||||
|
||||
try {
|
||||
// for debugging memory leaks
|
||||
// totally optional
|
||||
require('heapdump')
|
||||
require('heapdump');
|
||||
} catch(err) {}
|
||||
|
||||
var logger = require('./logger')
|
||||
logger.setup() // default setup
|
||||
let logger = require('./logger');
|
||||
logger.setup(); // default setup
|
||||
|
||||
var commander = require('commander')
|
||||
var constants = require('constants')
|
||||
var fs = require('fs')
|
||||
var http = require('http')
|
||||
var https = require('https')
|
||||
var YAML = require('js-yaml')
|
||||
var Path = require('path')
|
||||
var URL = require('url')
|
||||
var server = require('./index')
|
||||
var Utils = require('./utils')
|
||||
var pkginfo = require('pkginfo')(module); // eslint-disable-line no-unused-vars
|
||||
var pkgVersion = module.exports.version
|
||||
var pkgName = module.exports.name
|
||||
let commander = require('commander');
|
||||
let constants = require('constants');
|
||||
let fs = require('fs');
|
||||
let http = require('http');
|
||||
let https = require('https');
|
||||
let YAML = require('js-yaml');
|
||||
let Path = require('path');
|
||||
let URL = require('url');
|
||||
let server = require('./index');
|
||||
let Utils = require('./utils');
|
||||
let pkginfo = require('pkginfo')(module); // eslint-disable-line no-unused-vars
|
||||
let pkgVersion = module.exports.version;
|
||||
let pkgName = module.exports.name;
|
||||
|
||||
commander
|
||||
.option('-l, --listen <[host:]port>', 'host:port number to listen on (default: localhost:4873)')
|
||||
.option('-c, --config <config.yaml>', 'use this configuration file (default: ./config.yaml)')
|
||||
.version(pkgVersion)
|
||||
.parse(process.argv)
|
||||
.parse(process.argv);
|
||||
|
||||
if (commander.args.length == 1 && !commander.config) {
|
||||
// handling "verdaccio [config]" case if "-c" is missing in commandline
|
||||
commander.config = commander.args.pop()
|
||||
commander.config = commander.args.pop();
|
||||
}
|
||||
|
||||
if (commander.args.length != 0) {
|
||||
commander.help()
|
||||
commander.help();
|
||||
}
|
||||
|
||||
var config, config_path
|
||||
let config, config_path;
|
||||
try {
|
||||
if (commander.config) {
|
||||
config_path = Path.resolve(commander.config)
|
||||
config_path = Path.resolve(commander.config);
|
||||
} else {
|
||||
config_path = require('./config-path')()
|
||||
config_path = require('./config-path')();
|
||||
}
|
||||
config = YAML.safeLoad(fs.readFileSync(config_path, 'utf8'))
|
||||
logger.logger.warn({ file: config_path }, 'config file - @{file}')
|
||||
config = YAML.safeLoad(fs.readFileSync(config_path, 'utf8'));
|
||||
logger.logger.warn({file: config_path}, 'config file - @{file}');
|
||||
} catch (err) {
|
||||
logger.logger.fatal({ file: config_path, err: err }, 'cannot open config file @{file}: @{!err.message}')
|
||||
process.exit(1)
|
||||
logger.logger.fatal({file: config_path, err: err}, 'cannot open config file @{file}: @{!err.message}');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
afterConfigLoad()
|
||||
afterConfigLoad();
|
||||
|
||||
function get_listen_addresses() {
|
||||
// command line || config file || default
|
||||
var addresses
|
||||
let addresses;
|
||||
|
||||
if (commander.listen) {
|
||||
addresses = [ commander.listen ]
|
||||
addresses = [commander.listen];
|
||||
} else if (Array.isArray(config.listen)) {
|
||||
addresses = config.listen
|
||||
addresses = config.listen;
|
||||
} else if (config.listen) {
|
||||
addresses = [ config.listen ]
|
||||
addresses = [config.listen];
|
||||
} else {
|
||||
addresses = [ '4873' ]
|
||||
addresses = ['4873'];
|
||||
}
|
||||
|
||||
addresses = addresses.map(function(addr) {
|
||||
var parsed_addr = Utils.parse_address(addr)
|
||||
let parsed_addr = Utils.parse_address(addr);
|
||||
|
||||
if (!parsed_addr) {
|
||||
logger.logger.warn({ addr: addr },
|
||||
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/")')
|
||||
+ ' (e.g. "http://localhost:4873/")');
|
||||
}
|
||||
|
||||
return parsed_addr
|
||||
return parsed_addr;
|
||||
}).filter(Boolean);
|
||||
|
||||
}).filter(Boolean)
|
||||
|
||||
return addresses
|
||||
return addresses;
|
||||
}
|
||||
|
||||
function afterConfigLoad() {
|
||||
if (!config.self_path) config.self_path = Path.resolve(config_path)
|
||||
if (!config.https) config.https = { enable: false };
|
||||
if (!config.self_path) config.self_path = Path.resolve(config_path);
|
||||
if (!config.https) config.https = {enable: false};
|
||||
|
||||
var app = server(config)
|
||||
let app = server(config);
|
||||
|
||||
get_listen_addresses().forEach(function(addr) {
|
||||
var webServer
|
||||
let webServer;
|
||||
|
||||
if (addr.proto === 'https') { // https
|
||||
if (!config.https || !config.https.key || !config.https.cert) {
|
||||
var conf_path = function(file) {
|
||||
if (!file) return config_path
|
||||
return Path.resolve(Path.dirname(config_path), file)
|
||||
}
|
||||
let conf_path = function(file) {
|
||||
if (!file) return config_path;
|
||||
return Path.resolve(Path.dirname(config_path), file);
|
||||
};
|
||||
|
||||
logger.logger.fatal([
|
||||
'You need to specify "https.key" and "https.cert" to run https server',
|
||||
@ -123,8 +122,8 @@ function afterConfigLoad() {
|
||||
' https:',
|
||||
' key: verdaccio-key.pem',
|
||||
' cert: verdaccio-cert.pem',
|
||||
].join('\n'))
|
||||
process.exit(2)
|
||||
].join('\n'));
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
try {
|
||||
@ -133,12 +132,11 @@ function afterConfigLoad() {
|
||||
secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3,
|
||||
key: fs.readFileSync(config.https.key),
|
||||
cert: fs.readFileSync(config.https.cert),
|
||||
ca: fs.readFileSync(config.https.ca)
|
||||
|
||||
}, app)
|
||||
ca: fs.readFileSync(config.https.ca),
|
||||
}, app);
|
||||
} catch (err) { // catch errors related to certificate loading
|
||||
logger.logger.fatal({ err: err }, 'cannot create server: @{err.message}')
|
||||
process.exit(2)
|
||||
logger.logger.fatal({err: err}, 'cannot create server: @{err.message}');
|
||||
process.exit(2);
|
||||
}
|
||||
} else { // http
|
||||
webServer = http.createServer(app);
|
||||
@ -147,9 +145,9 @@ function afterConfigLoad() {
|
||||
webServer
|
||||
.listen(addr.port || addr.path, addr.host)
|
||||
.on('error', function(err) {
|
||||
logger.logger.fatal({ err: err }, 'cannot create server: @{err.message}')
|
||||
process.exit(2)
|
||||
})
|
||||
logger.logger.fatal({err: err}, 'cannot create server: @{err.message}');
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
logger.logger.warn({
|
||||
addr: ( addr.path
|
||||
@ -165,17 +163,17 @@ function afterConfigLoad() {
|
||||
})
|
||||
),
|
||||
version: pkgName + '/' + pkgVersion,
|
||||
}, 'http address - @{addr}')
|
||||
})
|
||||
}, 'http address - @{addr}');
|
||||
});
|
||||
|
||||
// undocumented stuff for tests
|
||||
if (typeof(process.send) === 'function') {
|
||||
process.send({ verdaccio_started: true })
|
||||
process.send({verdaccio_started: true});
|
||||
}
|
||||
}
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
logger.logger.fatal( { err: err }
|
||||
, 'uncaught exception, please report this\n@{err.stack}' )
|
||||
process.exit(255)
|
||||
})
|
||||
logger.logger.fatal( {err: err}
|
||||
, 'uncaught exception, please report this\n@{err.stack}' );
|
||||
process.exit(255);
|
||||
});
|
||||
|
@ -1,47 +1,49 @@
|
||||
var fs = require('fs')
|
||||
var Path = require('path')
|
||||
var logger = require('./logger')
|
||||
'use strict';
|
||||
|
||||
module.exports = find_config_file
|
||||
let fs = require('fs');
|
||||
let Path = require('path');
|
||||
let logger = require('./logger');
|
||||
|
||||
module.exports = find_config_file;
|
||||
|
||||
function find_config_file() {
|
||||
var paths = get_paths()
|
||||
let paths = get_paths();
|
||||
|
||||
for (var i=0; i<paths.length; i++) {
|
||||
if (file_exists(paths[i].path)) return paths[i].path
|
||||
for (let i=0; i<paths.length; i++) {
|
||||
if (file_exists(paths[i].path)) return paths[i].path;
|
||||
}
|
||||
|
||||
create_config_file(paths[0])
|
||||
return paths[0].path
|
||||
create_config_file(paths[0]);
|
||||
return paths[0].path;
|
||||
}
|
||||
|
||||
function create_config_file(config_path) {
|
||||
require('mkdirp').sync(Path.dirname(config_path.path))
|
||||
logger.logger.info({ file: config_path.path }, 'Creating default config file in @{file}')
|
||||
require('mkdirp').sync(Path.dirname(config_path.path));
|
||||
logger.logger.info({file: config_path.path}, 'Creating default config file in @{file}');
|
||||
|
||||
var created_config = fs.readFileSync(require.resolve('../conf/default.yaml'), 'utf8')
|
||||
let created_config = fs.readFileSync(require.resolve('../conf/default.yaml'), 'utf8');
|
||||
|
||||
if (config_path.type === 'xdg') {
|
||||
var data_dir = process.env.XDG_DATA_HOME
|
||||
|| Path.join(process.env.HOME, '.local', 'share')
|
||||
let data_dir = process.env.XDG_DATA_HOME
|
||||
|| Path.join(process.env.HOME, '.local', 'share');
|
||||
if (folder_exists(data_dir)) {
|
||||
data_dir = Path.resolve(Path.join(data_dir, 'verdaccio', 'storage'))
|
||||
created_config = created_config.replace(/^storage: .\/storage$/m, 'storage: ' + data_dir)
|
||||
data_dir = Path.resolve(Path.join(data_dir, 'verdaccio', 'storage'));
|
||||
created_config = created_config.replace(/^storage: .\/storage$/m, 'storage: ' + data_dir);
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(config_path.path, created_config)
|
||||
fs.writeFileSync(config_path.path, created_config);
|
||||
}
|
||||
|
||||
function get_paths() {
|
||||
var try_paths = []
|
||||
var xdg_config = process.env.XDG_CONFIG_HOME
|
||||
|| process.env.HOME && Path.join(process.env.HOME, '.config')
|
||||
let try_paths = [];
|
||||
let xdg_config = process.env.XDG_CONFIG_HOME
|
||||
|| process.env.HOME && Path.join(process.env.HOME, '.config');
|
||||
if (xdg_config && folder_exists(xdg_config)) {
|
||||
try_paths.push({
|
||||
path: Path.join(xdg_config, 'verdaccio', 'config.yaml'),
|
||||
type: 'xdg',
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (process.platform === 'win32'
|
||||
@ -50,40 +52,40 @@ function get_paths() {
|
||||
try_paths.push({
|
||||
path: Path.resolve(Path.join(process.env.APPDATA, 'verdaccio', 'config.yaml')),
|
||||
type: 'win',
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
try_paths.push({
|
||||
path: Path.resolve(Path.join('.', 'verdaccio', 'config.yaml')),
|
||||
type: 'def',
|
||||
})
|
||||
});
|
||||
|
||||
// backward compatibility
|
||||
try_paths.push({
|
||||
path: Path.resolve(Path.join('.', 'config.yaml')),
|
||||
type: 'old',
|
||||
})
|
||||
});
|
||||
|
||||
return try_paths
|
||||
return try_paths;
|
||||
}
|
||||
|
||||
function folder_exists(path) {
|
||||
try {
|
||||
var stat = fs.statSync(path)
|
||||
var stat = fs.statSync(path);
|
||||
} catch(_) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
return stat.isDirectory()
|
||||
return stat.isDirectory();
|
||||
}
|
||||
|
||||
function file_exists(path) {
|
||||
try {
|
||||
var stat = fs.statSync(path)
|
||||
var stat = fs.statSync(path);
|
||||
} catch(_) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
return stat.isFile()
|
||||
return stat.isFile();
|
||||
}
|
||||
|
||||
|
214
lib/config.js
214
lib/config.js
@ -1,195 +1,209 @@
|
||||
var assert = require('assert')
|
||||
var Crypto = require('crypto')
|
||||
var Error = require('http-errors')
|
||||
var minimatch = require('minimatch')
|
||||
var Path = require('path')
|
||||
var LocalData = require('./local-data')
|
||||
var Utils = require('./utils')
|
||||
var Utils = require('./utils')
|
||||
var pkginfo = require('pkginfo')(module) // eslint-disable-line no-unused-vars
|
||||
var pkgVersion = module.exports.version
|
||||
var pkgName = module.exports.name
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
let Crypto = require('crypto');
|
||||
let Error = require('http-errors');
|
||||
let minimatch = require('minimatch');
|
||||
let Path = require('path');
|
||||
let LocalData = require('./local-data');
|
||||
var Utils = require('./utils');
|
||||
var Utils = require('./utils');
|
||||
let pkginfo = require('pkginfo')(module); // eslint-disable-line no-unused-vars
|
||||
let pkgVersion = module.exports.version;
|
||||
let pkgName = module.exports.name;
|
||||
|
||||
// [[a, [b, c]], d] -> [a, b, c, d]
|
||||
function flatten(array) {
|
||||
var result = []
|
||||
for (var i=0; i<array.length; i++) {
|
||||
let result = [];
|
||||
for (let i=0; i<array.length; i++) {
|
||||
if (Array.isArray(array[i])) {
|
||||
result.push.apply(result, flatten(array[i]))
|
||||
result.push.apply(result, flatten(array[i]));
|
||||
} else {
|
||||
result.push(array[i])
|
||||
result.push(array[i]);
|
||||
}
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
function Config(config) {
|
||||
var self = Object.create(Config.prototype)
|
||||
let self = Object.create(Config.prototype);
|
||||
for (var i in config) {
|
||||
if (self[i] == null) self[i] = config[i]
|
||||
if (self[i] == null) self[i] = config[i];
|
||||
}
|
||||
|
||||
if (!self.user_agent) {
|
||||
self.user_agent = `${pkgName}/${pkgVersion}`;
|
||||
}
|
||||
if (!self.user_agent) self.user_agent = pkgName + '/' + pkgVersion
|
||||
|
||||
// some weird shell scripts are valid yaml files parsed as string
|
||||
assert.equal(typeof(config), 'object', 'CONFIG: it doesn\'t look like a valid config file')
|
||||
assert.equal(typeof(config), 'object', 'CONFIG: it doesn\'t look like a valid config file');
|
||||
|
||||
assert(self.storage, 'CONFIG: storage path not defined')
|
||||
self.localList = LocalData(
|
||||
assert(self.storage, 'CONFIG: storage path not defined');
|
||||
self.localList = new LocalData(
|
||||
Path.join(
|
||||
Path.resolve(Path.dirname(self.self_path || ''), self.storage),
|
||||
'.sinopia-db.json'
|
||||
)
|
||||
)
|
||||
);
|
||||
if (!self.secret) {
|
||||
self.secret = self.localList.data.secret
|
||||
self.secret = self.localList.data.secret;
|
||||
|
||||
if (!self.secret) {
|
||||
self.secret = Crypto.pseudoRandomBytes(32).toString('hex')
|
||||
self.localList.data.secret = self.secret
|
||||
self.localList.sync()
|
||||
self.secret = Crypto.pseudoRandomBytes(32).toString('hex');
|
||||
self.localList.data.secret = self.secret;
|
||||
self.localList.sync();
|
||||
}
|
||||
}
|
||||
|
||||
var users = {all:true, anonymous:true, 'undefined':true, owner:true, none:true}
|
||||
let users = {
|
||||
'all': true,
|
||||
'anonymous': true,
|
||||
'undefined': true,
|
||||
'owner': true,
|
||||
'none': true,
|
||||
};
|
||||
|
||||
var check_user_or_uplink = function(arg) {
|
||||
assert(arg !== 'all' && arg !== 'owner' && arg !== 'anonymous' && arg !== 'undefined' && arg !== 'none', 'CONFIG: reserved user/uplink name: ' + arg)
|
||||
assert(!arg.match(/\s/), 'CONFIG: invalid user name: ' + arg)
|
||||
assert(users[arg] == null, 'CONFIG: duplicate user/uplink name: ' + arg)
|
||||
users[arg] = true
|
||||
let check_user_or_uplink = function(arg) {
|
||||
assert(arg !== 'all' && arg !== 'owner' && arg !== 'anonymous' && arg !== 'undefined' && arg !== 'none', 'CONFIG: reserved user/uplink name: ' + arg);
|
||||
assert(!arg.match(/\s/), 'CONFIG: invalid user name: ' + arg);
|
||||
assert(users[arg] == null, 'CONFIG: duplicate user/uplink name: ' + arg);
|
||||
users[arg] = true;
|
||||
}
|
||||
|
||||
;[ 'users', 'uplinks', 'packages' ].forEach(function(x) {
|
||||
if (self[x] == null) self[x] = {}
|
||||
assert(Utils.is_object(self[x]), 'CONFIG: bad "'+x+'" value (object expected)')
|
||||
})
|
||||
|
||||
for (var i in self.users) check_user_or_uplink(i)
|
||||
for (var i in self.uplinks) check_user_or_uplink(i)
|
||||
;['users', 'uplinks', 'packages'].forEach(function(x) {
|
||||
if (self[x] == null) self[x] = {};
|
||||
assert(Utils.is_object(self[x]), 'CONFIG: bad "'+x+'" value (object expected)');
|
||||
});
|
||||
|
||||
for (var i in self.users) {
|
||||
assert(self.users[i].password, 'CONFIG: no password for user: ' + i)
|
||||
assert(
|
||||
typeof(self.users[i].password) === 'string' &&
|
||||
check_user_or_uplink(i);
|
||||
}
|
||||
for (var i in self.uplinks) {
|
||||
check_user_or_uplink(i);
|
||||
}
|
||||
|
||||
for (var i in self.users) {
|
||||
assert(self.users[i].password, 'CONFIG: no password for user: ' + i);
|
||||
assert(typeof(self.users[i].password) === 'string' &&
|
||||
self.users[i].password.match(/^[a-f0-9]{40}$/)
|
||||
, 'CONFIG: wrong password format for user: ' + i + ', sha1 expected')
|
||||
, 'CONFIG: wrong password format for user: ' + i + ', sha1 expected');
|
||||
}
|
||||
|
||||
for (var i in self.uplinks) {
|
||||
assert(self.uplinks[i].url, 'CONFIG: no url for uplink: ' + i)
|
||||
assert(self.uplinks[i].url, 'CONFIG: no url for uplink: ' + i);
|
||||
assert( typeof(self.uplinks[i].url) === 'string'
|
||||
, 'CONFIG: wrong url format for uplink: ' + i)
|
||||
self.uplinks[i].url = self.uplinks[i].url.replace(/\/$/, '')
|
||||
, 'CONFIG: wrong url format for uplink: ' + i);
|
||||
self.uplinks[i].url = self.uplinks[i].url.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
function normalize_userlist() {
|
||||
var result = []
|
||||
let result = [];
|
||||
|
||||
for (var i=0; i<arguments.length; i++) {
|
||||
if (arguments[i] == null) continue
|
||||
for (let i=0; i<arguments.length; i++) {
|
||||
if (arguments[i] == null) continue;
|
||||
|
||||
// if it's a string, split it to array
|
||||
if (typeof(arguments[i]) === 'string') {
|
||||
result.push(arguments[i].split(/\s+/))
|
||||
result.push(arguments[i].split(/\s+/));
|
||||
} else if (Array.isArray(arguments[i])) {
|
||||
result.push(arguments[i])
|
||||
result.push(arguments[i]);
|
||||
} else {
|
||||
throw Error('CONFIG: bad package acl (array or string expected): ' + JSON.stringify(arguments[i]))
|
||||
throw Error('CONFIG: bad package acl (array or string expected): ' + JSON.stringify(arguments[i]));
|
||||
}
|
||||
}
|
||||
return flatten(result)
|
||||
return flatten(result);
|
||||
}
|
||||
|
||||
// add a default rule for all packages to make writing plugins easier
|
||||
if (self.packages['**'] == null) {
|
||||
self.packages['**'] = {}
|
||||
self.packages['**'] = {};
|
||||
}
|
||||
|
||||
for (var i in self.packages) {
|
||||
assert(
|
||||
typeof(self.packages[i]) === 'object' &&
|
||||
!Array.isArray(self.packages[i])
|
||||
, 'CONFIG: bad "'+i+'" package description (object expected)')
|
||||
, 'CONFIG: bad "'+i+'" package description (object expected)');
|
||||
|
||||
self.packages[i].access = normalize_userlist(
|
||||
self.packages[i].allow_access,
|
||||
self.packages[i].access
|
||||
);
|
||||
delete self.packages[i].allow_access
|
||||
delete self.packages[i].allow_access;
|
||||
|
||||
self.packages[i].publish = normalize_userlist(
|
||||
self.packages[i].allow_publish,
|
||||
self.packages[i].publish
|
||||
);
|
||||
delete self.packages[i].allow_publish
|
||||
delete self.packages[i].allow_publish;
|
||||
|
||||
self.packages[i].proxy = normalize_userlist(
|
||||
self.packages[i].proxy_access,
|
||||
self.packages[i].proxy
|
||||
);
|
||||
delete self.packages[i].proxy_access
|
||||
delete self.packages[i].proxy_access;
|
||||
}
|
||||
|
||||
// loading these from ENV if aren't in config
|
||||
;[ 'http_proxy', 'https_proxy', 'no_proxy' ].forEach((function(v) {
|
||||
['http_proxy', 'https_proxy', 'no_proxy'].forEach((function(v) {
|
||||
if (!(v in self)) {
|
||||
self[v] = process.env[v] || process.env[v.toUpperCase()]
|
||||
self[v] = process.env[v] || process.env[v.toUpperCase()];
|
||||
}
|
||||
}).bind(self))
|
||||
}));
|
||||
|
||||
// unique identifier of self server (or a cluster), used to avoid loops
|
||||
if (!self.server_id) {
|
||||
self.server_id = Crypto.pseudoRandomBytes(6).toString('hex')
|
||||
self.server_id = Crypto.pseudoRandomBytes(6).toString('hex');
|
||||
}
|
||||
|
||||
return self
|
||||
return self;
|
||||
}
|
||||
|
||||
Config.prototype.can_proxy_to = function(package, uplink) {
|
||||
return (this.get_package_spec(package).proxy || []).reduce(function(prev, curr) {
|
||||
if (uplink === curr) return true
|
||||
return prev
|
||||
}, false)
|
||||
}
|
||||
Config.prototype.can_proxy_to = function(pkg, uplink) {
|
||||
return (this.get_package_spec(pkg).proxy || []).reduce(function(prev, curr) {
|
||||
if (uplink === curr) return true;
|
||||
return prev;
|
||||
}, false);
|
||||
};
|
||||
|
||||
Config.prototype.get_package_spec = function(package) {
|
||||
for (var i in this.packages) {
|
||||
if (minimatch.makeRe(i).exec(package)) {
|
||||
return this.packages[i]
|
||||
Config.prototype.get_package_spec = function(pkg) {
|
||||
for (let i in this.packages) {
|
||||
if (minimatch.makeRe(i).exec(pkg)) {
|
||||
return this.packages[i];
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
module.exports = Config
|
||||
module.exports = Config;
|
||||
|
||||
var parse_interval_table = {
|
||||
let parse_interval_table = {
|
||||
'': 1000,
|
||||
ms: 1,
|
||||
s: 1000,
|
||||
m: 60*1000,
|
||||
h: 60*60*1000,
|
||||
d: 86400000,
|
||||
w: 7*86400000,
|
||||
M: 30*86400000,
|
||||
y: 365*86400000,
|
||||
}
|
||||
'ms': 1,
|
||||
's': 1000,
|
||||
'm': 60*1000,
|
||||
'h': 60*60*1000,
|
||||
'd': 86400000,
|
||||
'w': 7*86400000,
|
||||
'M': 30*86400000,
|
||||
'y': 365*86400000,
|
||||
};
|
||||
|
||||
module.exports.parse_interval = function(interval) {
|
||||
if (typeof(interval) === 'number') return interval * 1000
|
||||
if (typeof(interval) === 'number') return interval * 1000;
|
||||
|
||||
var result = 0
|
||||
var last_suffix = Infinity
|
||||
let result = 0;
|
||||
let last_suffix = Infinity;
|
||||
interval.split(/\s+/).forEach(function(x) {
|
||||
if (!x) return
|
||||
var m = x.match(/^((0|[1-9][0-9]*)(\.[0-9]+)?)(ms|s|m|h|d|w|M|y|)$/)
|
||||
if (!x) return;
|
||||
let m = x.match(/^((0|[1-9][0-9]*)(\.[0-9]+)?)(ms|s|m|h|d|w|M|y|)$/);
|
||||
if (!m
|
||||
|| parse_interval_table[m[4]] >= last_suffix
|
||||
|| (m[4] === '' && last_suffix !== Infinity)) {
|
||||
throw Error('invalid interval: ' + interval)
|
||||
|| parse_interval_table[m[4]] >= last_suffix
|
||||
|| (m[4] === '' && last_suffix !== Infinity)) {
|
||||
throw Error('invalid interval: ' + interval);
|
||||
}
|
||||
last_suffix = parse_interval_table[m[4]]
|
||||
result += Number(m[1]) * parse_interval_table[m[4]]
|
||||
})
|
||||
return result
|
||||
}
|
||||
last_suffix = parse_interval_table[m[4]];
|
||||
result += Number(m[1]) * parse_interval_table[m[4]];
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
@ -1,15 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* file-locking.js - file system locking (replaces fs-ext)
|
||||
*/
|
||||
|
||||
var async = require('async'),
|
||||
let async = require('async'),
|
||||
locker = require('lockfile'),
|
||||
fs = require('fs'),
|
||||
path = require('path')
|
||||
path = require('path');
|
||||
|
||||
// locks a file by creating a lock file
|
||||
function lockFile(name, next) {
|
||||
var lockFileName = name + '.lock',
|
||||
let lockFileName = name + '.lock',
|
||||
lockOpts = {
|
||||
wait: 1000, // time (ms) to wait when checking for stale locks
|
||||
pollPeriod: 100, // how often (ms) to re-check stale locks
|
||||
@ -17,65 +19,64 @@ function lockFile(name, next) {
|
||||
stale: 5 * 60 * 1000, // locks are considered stale after 5 minutes
|
||||
|
||||
retries: 100, // number of times to attempt to create a lock
|
||||
retryWait: 100 // time (ms) between tries
|
||||
}
|
||||
retryWait: 100, // time (ms) between tries
|
||||
};
|
||||
|
||||
async.series({
|
||||
|
||||
statdir: function (callback) {
|
||||
statdir: function(callback) {
|
||||
// test to see if the directory exists
|
||||
fs.stat(path.dirname(name), function (err, stats) {
|
||||
fs.stat(path.dirname(name), function(err, stats) {
|
||||
if (err) {
|
||||
callback(err)
|
||||
callback(err);
|
||||
} else if (!stats.isDirectory()) {
|
||||
callback(new Error(path.dirname(name) + ' is not a directory'))
|
||||
callback(new Error(path.dirname(name) + ' is not a directory'));
|
||||
} else {
|
||||
callback(null)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
statfile: function (callback) {
|
||||
// test to see if the file to lock exists
|
||||
fs.stat(name, function (err, stats) {
|
||||
if (err) {
|
||||
callback(err)
|
||||
} else if (!stats.isFile()) {
|
||||
callback(new Error(path.dirname(name) + ' is not a file'))
|
||||
} else {
|
||||
callback(null)
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
lockfile: function (callback) {
|
||||
// try to lock the file
|
||||
locker.lock(lockFileName, lockOpts, callback)
|
||||
}
|
||||
statfile: function(callback) {
|
||||
// test to see if the file to lock exists
|
||||
fs.stat(name, function(err, stats) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else if (!stats.isFile()) {
|
||||
callback(new Error(path.dirname(name) + ' is not a file'));
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
}, function (err) {
|
||||
lockfile: function(callback) {
|
||||
// try to lock the file
|
||||
locker.lock(lockFileName, lockOpts, callback);
|
||||
},
|
||||
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
// lock failed
|
||||
return next(err)
|
||||
return next(err);
|
||||
}
|
||||
|
||||
// lock succeeded
|
||||
return next(null);
|
||||
})
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// unlocks file by removing existing lock file
|
||||
function unlockFile(name, next) {
|
||||
var lockFileName = name + '.lock'
|
||||
let lockFileName = name + '.lock';
|
||||
|
||||
locker.unlock(lockFileName, function (err) {
|
||||
locker.unlock(lockFileName, function(err) {
|
||||
if (err) {
|
||||
return next(err)
|
||||
return next(err);
|
||||
}
|
||||
|
||||
return next(null)
|
||||
})
|
||||
return next(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,63 +88,62 @@ function unlockFile(name, next) {
|
||||
function readFile(name, options, next) {
|
||||
if (typeof options === 'function' && next === null) {
|
||||
next = options;
|
||||
options = {}
|
||||
options = {};
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
options.lock = options.lock || false
|
||||
options.parse = options.parse || false
|
||||
options = options || {};
|
||||
options.lock = options.lock || false;
|
||||
options.parse = options.parse || false;
|
||||
|
||||
function lock(callback) {
|
||||
if (!options.lock) {
|
||||
return callback(null)
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
lockFile(name, function (err) {
|
||||
lockFile(name, function(err) {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
return callback(err);
|
||||
}
|
||||
return callback(null)
|
||||
})
|
||||
return callback(null);
|
||||
});
|
||||
}
|
||||
|
||||
function read(callback) {
|
||||
fs.readFile(name, 'utf8', function (err, contents) {
|
||||
fs.readFile(name, 'utf8', function(err, contents) {
|
||||
if (err) {
|
||||
return callback(err)
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, contents)
|
||||
|
||||
})
|
||||
callback(null, contents);
|
||||
});
|
||||
}
|
||||
|
||||
function parseJSON(contents, callback) {
|
||||
if (!options.parse) {
|
||||
return callback(null, contents)
|
||||
return callback(null, contents);
|
||||
}
|
||||
|
||||
try {
|
||||
contents = JSON.parse(contents)
|
||||
return callback(null, contents)
|
||||
contents = JSON.parse(contents);
|
||||
return callback(null, contents);
|
||||
} catch (err) {
|
||||
return callback(err)
|
||||
return callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
lock,
|
||||
read,
|
||||
parseJSON
|
||||
parseJSON,
|
||||
],
|
||||
|
||||
function (err, result) {
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
return next(err)
|
||||
return next(err);
|
||||
} else {
|
||||
return next(null, result)
|
||||
return next(null, result);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
exports.lockFile = lockFile;
|
||||
|
466
lib/index-api.js
466
lib/index-api.js
@ -1,106 +1,108 @@
|
||||
var Cookies = require('cookies')
|
||||
var express = require('express')
|
||||
var bodyParser = require('body-parser')
|
||||
var Error = require('http-errors')
|
||||
var Path = require('path')
|
||||
var Middleware = require('./middleware')
|
||||
var Notify = require('./notify')
|
||||
var Utils = require('./utils')
|
||||
var expect_json = Middleware.expect_json
|
||||
var match = Middleware.match
|
||||
var media = Middleware.media
|
||||
var validate_name = Middleware.validate_name
|
||||
var validate_pkg = Middleware.validate_package
|
||||
'use strict';
|
||||
|
||||
let Cookies = require('cookies');
|
||||
let express = require('express');
|
||||
let bodyParser = require('body-parser');
|
||||
let Error = require('http-errors');
|
||||
let Path = require('path');
|
||||
let Middleware = require('./middleware');
|
||||
let Notify = require('./notify');
|
||||
let Utils = require('./utils');
|
||||
let expect_json = Middleware.expect_json;
|
||||
let match = Middleware.match;
|
||||
let media = Middleware.media;
|
||||
let validate_name = Middleware.validate_name;
|
||||
let validate_pkg = Middleware.validate_package;
|
||||
|
||||
module.exports = function(config, auth, storage) {
|
||||
var app = express.Router()
|
||||
var can = Middleware.allow(auth)
|
||||
var notify = Notify.notify;
|
||||
let app = express.Router();
|
||||
let can = Middleware.allow(auth);
|
||||
let notify = Notify.notify;
|
||||
|
||||
// validate all of these params as a package name
|
||||
// this might be too harsh, so ask if it causes trouble
|
||||
app.param('package', validate_pkg)
|
||||
app.param('filename', validate_name)
|
||||
app.param('tag', validate_name)
|
||||
app.param('version', validate_name)
|
||||
app.param('revision', validate_name)
|
||||
app.param('token', validate_name)
|
||||
app.param('package', validate_pkg);
|
||||
app.param('filename', validate_name);
|
||||
app.param('tag', validate_name);
|
||||
app.param('version', validate_name);
|
||||
app.param('revision', validate_name);
|
||||
app.param('token', validate_name);
|
||||
|
||||
// these can't be safely put into express url for some reason
|
||||
app.param('_rev', match(/^-rev$/))
|
||||
app.param('org_couchdb_user', match(/^org\.couchdb\.user:/))
|
||||
app.param('anything', match(/.*/))
|
||||
app.param('_rev', match(/^-rev$/));
|
||||
app.param('org_couchdb_user', match(/^org\.couchdb\.user:/));
|
||||
app.param('anything', match(/.*/));
|
||||
|
||||
app.use(auth.basic_middleware())
|
||||
//app.use(auth.bearer_middleware())
|
||||
app.use(bodyParser.json({ strict: false, limit: config.max_body_size || '10mb' }))
|
||||
app.use(Middleware.anti_loop(config))
|
||||
app.use(auth.basic_middleware());
|
||||
// app.use(auth.bearer_middleware())
|
||||
app.use(bodyParser.json({strict: false, limit: config.max_body_size || '10mb'}));
|
||||
app.use(Middleware.anti_loop(config));
|
||||
|
||||
// encode / in a scoped package name to be matched as a single parameter in routes
|
||||
app.use(function(req, res, next) {
|
||||
if (req.url.indexOf('@') != -1) {
|
||||
// e.g.: /@org/pkg/1.2.3 -> /@org%2Fpkg/1.2.3, /@org%2Fpkg/1.2.3 -> /@org%2Fpkg/1.2.3
|
||||
req.url = req.url.replace(/^(\/@[^\/%]+)\/(?!$)/, '$1%2F')
|
||||
req.url = req.url.replace(/^(\/@[^\/%]+)\/(?!$)/, '$1%2F');
|
||||
}
|
||||
next()
|
||||
})
|
||||
next();
|
||||
});
|
||||
|
||||
// for "npm whoami"
|
||||
app.get('/whoami', function(req, res, next) {
|
||||
if (req.headers.referer === 'whoami') {
|
||||
next({ username: req.remote_user.name })
|
||||
next({username: req.remote_user.name});
|
||||
} else {
|
||||
next('route')
|
||||
next('route');
|
||||
}
|
||||
})
|
||||
});
|
||||
app.get('/-/whoami', function(req, res, next) {
|
||||
next({ username: req.remote_user.name })
|
||||
})
|
||||
next({username: req.remote_user.name});
|
||||
});
|
||||
|
||||
// TODO: anonymous user?
|
||||
app.get('/:package/:version?', can('access'), function(req, res, next) {
|
||||
storage.get_package(req.params.package, { req: req }, function(err, info) {
|
||||
if (err) return next(err)
|
||||
info = Utils.filter_tarball_urls(info, req, config)
|
||||
storage.get_package(req.params.package, {req: req}, function(err, info) {
|
||||
if (err) return next(err);
|
||||
info = Utils.filter_tarball_urls(info, req, config);
|
||||
|
||||
var version = req.params.version
|
||||
if (!version) return next(info)
|
||||
let version = req.params.version;
|
||||
if (!version) return next(info);
|
||||
|
||||
var t = Utils.get_version(info, version)
|
||||
if (t != null) return next(t)
|
||||
let t = Utils.get_version(info, version);
|
||||
if (t != null) return next(t);
|
||||
|
||||
if (info['dist-tags'] != null) {
|
||||
if (info['dist-tags'][version] != null) {
|
||||
version = info['dist-tags'][version]
|
||||
t = Utils.get_version(info, version)
|
||||
if (t != null) return next(t)
|
||||
version = info['dist-tags'][version];
|
||||
t = Utils.get_version(info, version);
|
||||
if (t != null) return next(t);
|
||||
}
|
||||
}
|
||||
|
||||
return next( Error[404]('version not found: ' + req.params.version) )
|
||||
})
|
||||
})
|
||||
return next( Error[404]('version not found: ' + req.params.version) );
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/:package/-/:filename', can('access'), function(req, res, next) {
|
||||
var stream = storage.get_tarball(req.params.package, req.params.filename)
|
||||
let stream = storage.get_tarball(req.params.package, req.params.filename);
|
||||
stream.on('content-length', function(v) {
|
||||
res.header('Content-Length', v)
|
||||
})
|
||||
res.header('Content-Length', v);
|
||||
});
|
||||
stream.on('error', function(err) {
|
||||
return res.report_error(err)
|
||||
})
|
||||
res.header('Content-Type', 'application/octet-stream')
|
||||
stream.pipe(res)
|
||||
})
|
||||
return res.report_error(err);
|
||||
});
|
||||
res.header('Content-Type', 'application/octet-stream');
|
||||
stream.pipe(res);
|
||||
});
|
||||
|
||||
// searching packages
|
||||
app.get('/-/all(\/since)?', function(req, res, next) {
|
||||
var received_end = false
|
||||
var response_finished = false
|
||||
var processing_pkgs = 0
|
||||
var firstPackage = true
|
||||
let received_end = false;
|
||||
let response_finished = false;
|
||||
let processing_pkgs = 0;
|
||||
let firstPackage = true;
|
||||
|
||||
res.status(200)
|
||||
res.status(200);
|
||||
|
||||
/*
|
||||
* Offical NPM registry (registry.npmjs.org) no longer return whole database,
|
||||
@ -120,105 +122,105 @@ module.exports = function(config, auth, storage) {
|
||||
* Data structure also different, whel request /-/all, response is an object, but
|
||||
* when request /-/all/since, response is an array
|
||||
*/
|
||||
var respShouldBeArray = req.path.endsWith('/since')
|
||||
res.set('Date', 'Mon, 10 Oct 1983 00:12:48 GMT')
|
||||
const respShouldBeArray = req.path.endsWith('/since');
|
||||
res.set('Date', 'Mon, 10 Oct 1983 00:12:48 GMT');
|
||||
|
||||
if (respShouldBeArray) {
|
||||
res.write('[')
|
||||
res.write('[');
|
||||
} else {
|
||||
res.write('{"_updated":' + 99999);
|
||||
}
|
||||
|
||||
var stream = storage.search(req.query.startkey || 0, { req: req })
|
||||
let stream = storage.search(req.query.startkey || 0, {req: req});
|
||||
|
||||
stream.on('data', function each(pkg) {
|
||||
processing_pkgs++
|
||||
processing_pkgs++;
|
||||
|
||||
auth.allow_access(pkg.name, req.remote_user, function(err, allowed) {
|
||||
processing_pkgs--
|
||||
processing_pkgs--;
|
||||
|
||||
if (err) {
|
||||
if (err.status && String(err.status).match(/^4\d\d$/)) {
|
||||
// auth plugin returns 4xx user error,
|
||||
// that's equivalent of !allowed basically
|
||||
allowed = false
|
||||
allowed = false;
|
||||
} else {
|
||||
stream.abort(err)
|
||||
stream.abort(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowed) {
|
||||
if (respShouldBeArray) {
|
||||
res.write(`${firstPackage ? '' : ','}${JSON.stringify(pkg)}\n`)
|
||||
res.write(`${firstPackage ? '' : ','}${JSON.stringify(pkg)}\n`);
|
||||
if (firstPackage) {
|
||||
firstPackage = false
|
||||
firstPackage = false;
|
||||
}
|
||||
} else {
|
||||
res.write(',\n' + JSON.stringify(pkg.name) + ':' + JSON.stringify(pkg))
|
||||
res.write(',\n' + JSON.stringify(pkg.name) + ':' + JSON.stringify(pkg));
|
||||
}
|
||||
}
|
||||
|
||||
check_finish()
|
||||
})
|
||||
})
|
||||
check_finish();
|
||||
});
|
||||
});
|
||||
|
||||
stream.on('error', function (_err) {
|
||||
res.socket.destroy()
|
||||
})
|
||||
stream.on('error', function(_err) {
|
||||
res.socket.destroy();
|
||||
});
|
||||
|
||||
stream.on('end', function () {
|
||||
received_end = true
|
||||
check_finish()
|
||||
})
|
||||
stream.on('end', function() {
|
||||
received_end = true;
|
||||
check_finish();
|
||||
});
|
||||
|
||||
function check_finish() {
|
||||
if (!received_end) return
|
||||
if (processing_pkgs) return
|
||||
if (response_finished) return
|
||||
if (!received_end) return;
|
||||
if (processing_pkgs) return;
|
||||
if (response_finished) return;
|
||||
|
||||
response_finished = true
|
||||
response_finished = true;
|
||||
if (respShouldBeArray) {
|
||||
res.end(']\n')
|
||||
res.end(']\n');
|
||||
} else {
|
||||
res.end('}\n')
|
||||
res.end('}\n');
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// placeholder 'cause npm require to be authenticated to publish
|
||||
// we do not do any real authentication yet
|
||||
app.post('/_session', Cookies.express(), function(req, res, next) {
|
||||
res.cookies.set('AuthSession', String(Math.random()), {
|
||||
// npmjs.org sets 10h expire
|
||||
expires: new Date(Date.now() + 10*60*60*1000)
|
||||
})
|
||||
next({ ok: true, name: 'somebody', roles: [] })
|
||||
})
|
||||
expires: new Date(Date.now() + 10*60*60*1000),
|
||||
});
|
||||
next({ok: true, name: 'somebody', roles: []});
|
||||
});
|
||||
|
||||
app.get('/-/user/:org_couchdb_user', function(req, res, next) {
|
||||
res.status(200)
|
||||
res.status(200);
|
||||
next({
|
||||
ok: 'you are authenticated as "' + req.remote_user.name + '"',
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
app.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req, res, next) {
|
||||
var token = (req.body.name && req.body.password)
|
||||
let token = (req.body.name && req.body.password)
|
||||
? auth.aes_encrypt(req.body.name + ':' + req.body.password).toString('base64')
|
||||
: undefined
|
||||
: undefined;
|
||||
if (req.remote_user.name != null) {
|
||||
res.status(201)
|
||||
res.status(201);
|
||||
return next({
|
||||
ok: "you are authenticated as '" + req.remote_user.name + "'",
|
||||
//token: auth.issue_token(req.remote_user),
|
||||
ok: 'you are authenticated as \'' + req.remote_user.name + '\'',
|
||||
// token: auth.issue_token(req.remote_user),
|
||||
token: token,
|
||||
})
|
||||
});
|
||||
} else {
|
||||
if (typeof(req.body.name) !== 'string' || typeof(req.body.password) !== 'string') {
|
||||
if (typeof(req.body.password_sha)) {
|
||||
return next( Error[422]('your npm version is outdated\nPlease update to npm@1.4.5 or greater.\nSee https://github.com/rlidwka/sinopia/issues/93 for details.') )
|
||||
return next( Error[422]('your npm version is outdated\nPlease update to npm@1.4.5 or greater.\nSee https://github.com/rlidwka/sinopia/issues/93 for details.') );
|
||||
} else {
|
||||
return next( Error[422]('user/password is not found in request (npm issue?)') )
|
||||
return next( Error[422]('user/password is not found in request (npm issue?)') );
|
||||
}
|
||||
}
|
||||
auth.add_user(req.body.name, req.body.password, function(err, user) {
|
||||
@ -227,133 +229,130 @@ module.exports = function(config, auth, storage) {
|
||||
// With npm registering is the same as logging in,
|
||||
// and npm accepts only an 409 error.
|
||||
// So, changing status code here.
|
||||
return next( Error[409](err.message) )
|
||||
return next( Error[409](err.message) );
|
||||
}
|
||||
return next(err)
|
||||
return next(err);
|
||||
}
|
||||
|
||||
req.remote_user = user
|
||||
res.status(201)
|
||||
req.remote_user = user;
|
||||
res.status(201);
|
||||
return next({
|
||||
ok: "user '" + req.body.name + "' created",
|
||||
//token: auth.issue_token(req.remote_user),
|
||||
ok: 'user \'' + req.body.name + '\' created',
|
||||
// token: auth.issue_token(req.remote_user),
|
||||
token: token,
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
app.delete('/-/user/token/*', function(req, res, next) {
|
||||
res.status(200)
|
||||
res.status(200);
|
||||
next({
|
||||
ok: 'Logged out',
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
function tag_package_version(req, res, next) {
|
||||
if (typeof(req.body) !== 'string') return next('route')
|
||||
if (typeof(req.body) !== 'string') return next('route');
|
||||
|
||||
var tags = {}
|
||||
tags[req.params.tag] = req.body
|
||||
let tags = {};
|
||||
tags[req.params.tag] = req.body;
|
||||
storage.merge_tags(req.params.package, tags, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return next({ ok: 'package tagged' })
|
||||
})
|
||||
if (err) return next(err);
|
||||
res.status(201);
|
||||
return next({ok: 'package tagged'});
|
||||
});
|
||||
}
|
||||
|
||||
// tagging a package
|
||||
app.put('/:package/:tag',
|
||||
can('publish'), media('application/json'), tag_package_version)
|
||||
can('publish'), media('application/json'), tag_package_version);
|
||||
|
||||
app.post('/-/package/:package/dist-tags/:tag',
|
||||
can('publish'), media('application/json'), tag_package_version)
|
||||
can('publish'), media('application/json'), tag_package_version);
|
||||
|
||||
app.put('/-/package/:package/dist-tags/:tag',
|
||||
can('publish'), media('application/json'), tag_package_version)
|
||||
can('publish'), media('application/json'), tag_package_version);
|
||||
|
||||
app.delete('/-/package/:package/dist-tags/:tag', can('publish'), function (req, res, next) {
|
||||
var tags = {}
|
||||
tags[req.params.tag] = null
|
||||
app.delete('/-/package/:package/dist-tags/:tag', can('publish'), function(req, res, next) {
|
||||
let tags = {};
|
||||
tags[req.params.tag] = null;
|
||||
storage.merge_tags(req.params.package, tags, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return next({ ok: 'tag removed' })
|
||||
})
|
||||
})
|
||||
if (err) return next(err);
|
||||
res.status(201);
|
||||
return next({ok: 'tag removed'});
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/-/package/:package/dist-tags', can('access'), function(req, res, next) {
|
||||
storage.get_package(req.params.package, { req: req }, function(err, info) {
|
||||
if (err) return next(err)
|
||||
storage.get_package(req.params.package, {req: req}, function(err, info) {
|
||||
if (err) return next(err);
|
||||
|
||||
next(info['dist-tags'])
|
||||
})
|
||||
})
|
||||
next(info['dist-tags']);
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/-/package/:package/dist-tags',
|
||||
can('publish'), media('application/json'), expect_json,
|
||||
function(req, res, next) {
|
||||
|
||||
storage.merge_tags(req.params.package, req.body, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return next({ ok: 'tags updated' })
|
||||
})
|
||||
})
|
||||
if (err) return next(err);
|
||||
res.status(201);
|
||||
return next({ok: 'tags updated'});
|
||||
});
|
||||
});
|
||||
|
||||
app.put('/-/package/:package/dist-tags',
|
||||
can('publish'), media('application/json'), expect_json,
|
||||
function(req, res, next) {
|
||||
|
||||
storage.replace_tags(req.params.package, req.body, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return next({ ok: 'tags updated' })
|
||||
})
|
||||
})
|
||||
if (err) return next(err);
|
||||
res.status(201);
|
||||
return next({ok: 'tags updated'});
|
||||
});
|
||||
});
|
||||
|
||||
app.delete('/-/package/:package/dist-tags',
|
||||
can('publish'), media('application/json'),
|
||||
function(req, res, next) {
|
||||
|
||||
storage.replace_tags(req.params.package, {}, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return next({ ok: 'tags removed' })
|
||||
})
|
||||
})
|
||||
if (err) return next(err);
|
||||
res.status(201);
|
||||
return next({ok: 'tags removed'});
|
||||
});
|
||||
});
|
||||
|
||||
// publishing a package
|
||||
app.put('/:package/:_rev?/:revision?', can('publish'), media('application/json'), expect_json, function(req, res, next) {
|
||||
var name = req.params.package
|
||||
let name = req.params.package;
|
||||
|
||||
if (Object.keys(req.body).length == 1 && Utils.is_object(req.body.users)) {
|
||||
// 501 status is more meaningful, but npm doesn't show error message for 5xx
|
||||
return next( Error[404]('npm star|unstar calls are not implemented') )
|
||||
return next( Error[404]('npm star|unstar calls are not implemented') );
|
||||
}
|
||||
|
||||
try {
|
||||
var metadata = Utils.validate_metadata(req.body, name)
|
||||
var metadata = Utils.validate_metadata(req.body, name);
|
||||
} catch(err) {
|
||||
return next( Error[422]('bad incoming package data') )
|
||||
return next( Error[422]('bad incoming package data') );
|
||||
}
|
||||
|
||||
if (req.params._rev) {
|
||||
storage.change_package(name, metadata, req.params.revision, function(err) {
|
||||
after_change(err, 'package changed')
|
||||
})
|
||||
after_change(err, 'package changed');
|
||||
});
|
||||
} else {
|
||||
storage.add_package(name, metadata, function(err) {
|
||||
after_change(err, 'created new package')
|
||||
})
|
||||
after_change(err, 'created new package');
|
||||
});
|
||||
}
|
||||
|
||||
function after_change(err, ok_message) {
|
||||
// old npm behaviour
|
||||
if (metadata._attachments == null) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return next({ ok: ok_message })
|
||||
if (err) return next(err);
|
||||
res.status(201);
|
||||
return next({ok: ok_message});
|
||||
}
|
||||
|
||||
// npm-registry-client 0.3+ embeds tarball into the json upload
|
||||
@ -361,121 +360,120 @@ module.exports = function(config, auth, storage) {
|
||||
// issue #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) {
|
||||
|
||||
|| 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( Error[400]('unsupported registry call') )
|
||||
return next( Error[400]('unsupported registry call') );
|
||||
}
|
||||
|
||||
if (err && err.status != 409) return next(err)
|
||||
if (err && err.status != 409) return next(err);
|
||||
|
||||
// at this point document is either created or existed before
|
||||
var t1 = Object.keys(metadata._attachments)[0]
|
||||
let t1 = Object.keys(metadata._attachments)[0];
|
||||
create_tarball(Path.basename(t1), metadata._attachments[t1], function(err) {
|
||||
if (err) return next(err)
|
||||
if (err) return next(err);
|
||||
|
||||
var t2 = Object.keys(metadata.versions)[0]
|
||||
metadata.versions[t2].readme = metadata.readme != null ? String(metadata.readme) : ''
|
||||
let t2 = Object.keys(metadata.versions)[0];
|
||||
metadata.versions[t2].readme = metadata.readme != null ? String(metadata.readme) : '';
|
||||
create_version(t2, metadata.versions[t2], function(err) {
|
||||
if (err) return next(err)
|
||||
if (err) return next(err);
|
||||
|
||||
add_tags(metadata['dist-tags'], function(err) {
|
||||
if (err) return next(err)
|
||||
notify(metadata, config)
|
||||
res.status(201)
|
||||
return next({ ok: ok_message })
|
||||
})
|
||||
})
|
||||
})
|
||||
if (err) return next(err);
|
||||
notify(metadata, config);
|
||||
res.status(201);
|
||||
return next({ok: ok_message});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function create_tarball(filename, data, cb) {
|
||||
var stream = storage.add_tarball(name, filename)
|
||||
let stream = storage.add_tarball(name, filename);
|
||||
stream.on('error', function(err) {
|
||||
cb(err)
|
||||
})
|
||||
cb(err);
|
||||
});
|
||||
stream.on('success', function() {
|
||||
cb()
|
||||
})
|
||||
cb();
|
||||
});
|
||||
|
||||
// this is dumb and memory-consuming, but what choices do we have?
|
||||
stream.end(new Buffer(data.data, 'base64'))
|
||||
stream.done()
|
||||
stream.end(new Buffer(data.data, 'base64'));
|
||||
stream.done();
|
||||
}
|
||||
|
||||
function create_version(version, data, cb) {
|
||||
storage.add_version(name, version, data, null, cb)
|
||||
storage.add_version(name, version, data, null, cb);
|
||||
}
|
||||
|
||||
function add_tags(tags, cb) {
|
||||
storage.merge_tags(name, tags, cb)
|
||||
storage.merge_tags(name, tags, cb);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// unpublishing an entire package
|
||||
app.delete('/:package/-rev/*', can('publish'), function(req, res, next) {
|
||||
storage.remove_package(req.params.package, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return next({ ok: 'package removed' })
|
||||
})
|
||||
})
|
||||
if (err) return next(err);
|
||||
res.status(201);
|
||||
return next({ok: 'package removed'});
|
||||
});
|
||||
});
|
||||
|
||||
// removing a tarball
|
||||
app.delete('/:package/-/:filename/-rev/:revision', can('publish'), function(req, res, next) {
|
||||
storage.remove_tarball(req.params.package, req.params.filename, req.params.revision, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return next({ ok: 'tarball removed' })
|
||||
})
|
||||
})
|
||||
if (err) return next(err);
|
||||
res.status(201);
|
||||
return next({ok: 'tarball removed'});
|
||||
});
|
||||
});
|
||||
|
||||
// uploading package tarball
|
||||
app.put('/:package/-/:filename/*', can('publish'), media('application/octet-stream'), function(req, res, next) {
|
||||
var name = req.params.package
|
||||
let name = req.params.package;
|
||||
|
||||
var stream = storage.add_tarball(name, req.params.filename)
|
||||
req.pipe(stream)
|
||||
let stream = storage.add_tarball(name, req.params.filename);
|
||||
req.pipe(stream);
|
||||
|
||||
// checking if end event came before closing
|
||||
var complete = false
|
||||
let complete = false;
|
||||
req.on('end', function() {
|
||||
complete = true
|
||||
stream.done()
|
||||
})
|
||||
complete = true;
|
||||
stream.done();
|
||||
});
|
||||
req.on('close', function() {
|
||||
if (!complete) {
|
||||
stream.abort()
|
||||
stream.abort();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
stream.on('error', function(err) {
|
||||
return res.report_error(err)
|
||||
})
|
||||
return res.report_error(err);
|
||||
});
|
||||
stream.on('success', function() {
|
||||
res.status(201)
|
||||
res.status(201);
|
||||
return next({
|
||||
ok: 'tarball uploaded successfully'
|
||||
})
|
||||
})
|
||||
})
|
||||
ok: 'tarball uploaded successfully',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// adding a version
|
||||
app.put('/:package/:version/-tag/:tag', can('publish'), media('application/json'), expect_json, function(req, res, next) {
|
||||
var name = req.params.package
|
||||
var version = req.params.version
|
||||
var tag = req.params.tag
|
||||
let name = req.params.package;
|
||||
let version = req.params.version;
|
||||
let tag = req.params.tag;
|
||||
|
||||
storage.add_version(name, version, req.body, tag, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return next({ ok: 'package published' })
|
||||
})
|
||||
})
|
||||
if (err) return next(err);
|
||||
res.status(201);
|
||||
return next({ok: 'package published'});
|
||||
});
|
||||
});
|
||||
|
||||
return app
|
||||
}
|
||||
return app;
|
||||
};
|
||||
|
||||
|
194
lib/index-web.js
194
lib/index-web.js
@ -1,167 +1,167 @@
|
||||
var async = require('async')
|
||||
var bodyParser = require('body-parser')
|
||||
var Cookies = require('cookies')
|
||||
var express = require('express')
|
||||
var fs = require('fs')
|
||||
var Handlebars = require('handlebars')
|
||||
var renderReadme = require('render-readme')
|
||||
var Search = require('./search')
|
||||
var Middleware = require('./middleware')
|
||||
var match = Middleware.match
|
||||
var validate_name = Middleware.validate_name
|
||||
var validate_pkg = Middleware.validate_package
|
||||
'use strict';
|
||||
|
||||
let async = require('async');
|
||||
let bodyParser = require('body-parser');
|
||||
let Cookies = require('cookies');
|
||||
let express = require('express');
|
||||
let fs = require('fs');
|
||||
let Handlebars = require('handlebars');
|
||||
let renderReadme = require('render-readme');
|
||||
let Search = require('./search');
|
||||
let Middleware = require('./middleware');
|
||||
let match = Middleware.match;
|
||||
let validate_name = Middleware.validate_name;
|
||||
let validate_pkg = Middleware.validate_package;
|
||||
|
||||
module.exports = function(config, auth, storage) {
|
||||
var app = express.Router()
|
||||
var can = Middleware.allow(auth)
|
||||
let app = express.Router();
|
||||
let can = Middleware.allow(auth);
|
||||
|
||||
// validate all of these params as a package name
|
||||
// this might be too harsh, so ask if it causes trouble
|
||||
app.param('package', validate_pkg)
|
||||
app.param('filename', validate_name)
|
||||
app.param('version', validate_name)
|
||||
app.param('anything', match(/.*/))
|
||||
app.param('package', validate_pkg);
|
||||
app.param('filename', validate_name);
|
||||
app.param('version', validate_name);
|
||||
app.param('anything', match(/.*/));
|
||||
|
||||
app.use(Cookies.express())
|
||||
app.use(bodyParser.urlencoded({ extended: false }))
|
||||
app.use(auth.cookie_middleware())
|
||||
app.use(Cookies.express());
|
||||
app.use(bodyParser.urlencoded({extended: false}));
|
||||
app.use(auth.cookie_middleware());
|
||||
app.use(function(req, res, next) {
|
||||
// disable loading in frames (clickjacking, etc.)
|
||||
res.header('X-Frame-Options', 'deny')
|
||||
next()
|
||||
})
|
||||
res.header('X-Frame-Options', 'deny');
|
||||
next();
|
||||
});
|
||||
|
||||
Search.configureStorage(storage)
|
||||
Search.configureStorage(storage);
|
||||
|
||||
Handlebars.registerPartial('entry', fs.readFileSync(require.resolve('./GUI/entry.hbs'), 'utf8'))
|
||||
|
||||
if(config.web && config.web.template) {
|
||||
var template = Handlebars.compile(fs.readFileSync(config.web.template, 'utf8'));
|
||||
}
|
||||
else {
|
||||
var template = Handlebars.compile(fs.readFileSync(require.resolve('./GUI/index.hbs'), 'utf8'))
|
||||
Handlebars.registerPartial('entry', fs.readFileSync(require.resolve('./GUI/entry.hbs'), 'utf8'));
|
||||
let template;
|
||||
if (config.web && config.web.template) {
|
||||
template = Handlebars.compile(fs.readFileSync(config.web.template, 'utf8'));
|
||||
} else {
|
||||
template = Handlebars.compile(fs.readFileSync(require.resolve('./GUI/index.hbs'), 'utf8'));
|
||||
}
|
||||
app.get('/', function(req, res, next) {
|
||||
var base = config.url_prefix
|
||||
let base = config.url_prefix
|
||||
? config.url_prefix.replace(/\/$/, '')
|
||||
: req.protocol + '://' + req.get('host')
|
||||
res.setHeader('Content-Type', 'text/html')
|
||||
: req.protocol + '://' + req.get('host');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
|
||||
storage.get_local(function(err, packages) {
|
||||
if (err) throw err // that function shouldn't produce any
|
||||
async.filterSeries(packages, function(package, cb) {
|
||||
auth.allow_access(package.name, req.remote_user, function(err, allowed) {
|
||||
setImmediate(function () {
|
||||
if (err) throw err; // that function shouldn't produce any
|
||||
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)
|
||||
cb(err, allowed);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}, function(err, packages) {
|
||||
if (err) throw err
|
||||
if (err) throw err;
|
||||
packages.sort(function(p1, p2) {
|
||||
if (p1.name < p2.name) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
next(template({
|
||||
name: config.web && config.web.title ? config.web.title : 'Verdaccio',
|
||||
tagline: config.web && config.web.tagline ? config.web.tagline : '',
|
||||
packages: packages,
|
||||
baseUrl: base,
|
||||
username: req.remote_user.name,
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
name: config.web && config.web.title ? config.web.title : 'Verdaccio',
|
||||
tagline: config.web && config.web.tagline ? config.web.tagline : '',
|
||||
packages: packages,
|
||||
baseUrl: base,
|
||||
username: req.remote_user.name,
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Static
|
||||
app.get('/-/static/:filename', function(req, res, next) {
|
||||
var file = __dirname + '/static/' + req.params.filename
|
||||
let file = __dirname + '/static/' + req.params.filename;
|
||||
res.sendFile(file, function(err) {
|
||||
if (!err) return
|
||||
if (!err) return;
|
||||
if (err.status === 404) {
|
||||
next()
|
||||
next();
|
||||
} else {
|
||||
next(err)
|
||||
next(err);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/-/logo', function(req, res, next) {
|
||||
res.sendFile( config.web && config.web.logo
|
||||
? config.web.logo
|
||||
: __dirname + '/static/logo-sm.png' )
|
||||
})
|
||||
: __dirname + '/static/logo-sm.png' );
|
||||
});
|
||||
|
||||
app.post('/-/login', function(req, res, next) {
|
||||
auth.authenticate(req.body.user, req.body.pass, function(err, user) {
|
||||
if (!err) {
|
||||
req.remote_user = user
|
||||
//res.cookies.set('token', auth.issue_token(req.remote_user))
|
||||
req.remote_user = user;
|
||||
// res.cookies.set('token', auth.issue_token(req.remote_user))
|
||||
|
||||
var str = req.body.user + ':' + req.body.pass
|
||||
res.cookies.set('token', auth.aes_encrypt(str).toString('base64'))
|
||||
let str = req.body.user + ':' + req.body.pass;
|
||||
res.cookies.set('token', auth.aes_encrypt(str).toString('base64'));
|
||||
}
|
||||
|
||||
var base = config.url_prefix
|
||||
let base = config.url_prefix
|
||||
? config.url_prefix.replace(/\/$/, '')
|
||||
: req.protocol + '://' + req.get('host')
|
||||
res.redirect(base)
|
||||
})
|
||||
})
|
||||
: req.protocol + '://' + req.get('host');
|
||||
res.redirect(base);
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/-/logout', function(req, res, next) {
|
||||
var base = config.url_prefix
|
||||
let base = config.url_prefix
|
||||
? config.url_prefix.replace(/\/$/, '')
|
||||
: req.protocol + '://' + req.get('host')
|
||||
res.cookies.set('token', '')
|
||||
res.redirect(base)
|
||||
})
|
||||
: req.protocol + '://' + req.get('host');
|
||||
res.cookies.set('token', '');
|
||||
res.redirect(base);
|
||||
});
|
||||
|
||||
// Search
|
||||
app.get('/-/search/:anything', function(req, res, next) {
|
||||
var results = Search.query(req.params.anything)
|
||||
var packages = []
|
||||
const results = Search.query(req.params.anything);
|
||||
const packages = [];
|
||||
|
||||
var getData = function(i) {
|
||||
const getData = function(i) {
|
||||
storage.get_package(results[i].ref, function(err, entry) {
|
||||
if (!err && entry) {
|
||||
auth.allow_access(entry.name, req.remote_user, function(err, allowed) { // TODO: This may cause performance issue?
|
||||
if (err || !allowed) return
|
||||
if (err || !allowed) return;
|
||||
|
||||
packages.push(entry.versions[entry['dist-tags'].latest])
|
||||
})
|
||||
packages.push(entry.versions[entry['dist-tags'].latest]);
|
||||
});
|
||||
}
|
||||
|
||||
if (i >= results.length - 1) {
|
||||
next(packages)
|
||||
next(packages);
|
||||
} else {
|
||||
getData(i + 1)
|
||||
getData(i + 1);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (results.length) {
|
||||
getData(0)
|
||||
getData(0);
|
||||
} else {
|
||||
next([])
|
||||
next([]);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
app.get('/-/readme(/@:scope?)?/:package/:version?', can('access'), function(req, res, next) {
|
||||
var packageName = req.params.package;
|
||||
if (req.params.scope) packageName = "@"+ req.params.scope + "/" + packageName;
|
||||
let packageName = req.params.package;
|
||||
if (req.params.scope) packageName = '@'+ req.params.scope + '/' + packageName;
|
||||
storage.get_package(packageName, {req: req}, function(err, info) {
|
||||
if (err) return next(err)
|
||||
next( renderReadme(info.readme || 'ERROR: No README data found!') )
|
||||
})
|
||||
})
|
||||
return app
|
||||
}
|
||||
if (err) return next(err);
|
||||
next( renderReadme(info.readme || 'ERROR: No README data found!') );
|
||||
});
|
||||
});
|
||||
return app;
|
||||
};
|
||||
|
118
lib/index.js
118
lib/index.js
@ -1,105 +1,107 @@
|
||||
var express = require('express')
|
||||
var Error = require('http-errors')
|
||||
var compression = require('compression')
|
||||
var Auth = require('./auth')
|
||||
var Logger = require('./logger')
|
||||
var Config = require('./config')
|
||||
var Middleware = require('./middleware')
|
||||
var Cats = require('./status-cats')
|
||||
var Storage = require('./storage')
|
||||
'use strict';
|
||||
|
||||
let express = require('express');
|
||||
let Error = require('http-errors');
|
||||
let compression = require('compression');
|
||||
let Auth = require('./auth');
|
||||
let Logger = require('./logger');
|
||||
let Config = require('./config');
|
||||
let Middleware = require('./middleware');
|
||||
let Cats = require('./status-cats');
|
||||
let Storage = require('./storage');
|
||||
|
||||
module.exports = function(config_hash) {
|
||||
Logger.setup(config_hash.logs)
|
||||
Logger.setup(config_hash.logs);
|
||||
|
||||
var config = Config(config_hash)
|
||||
var storage = Storage(config)
|
||||
var auth = Auth(config)
|
||||
var app = express()
|
||||
let config = Config(config_hash);
|
||||
let storage = new Storage(config);
|
||||
let auth = Auth(config);
|
||||
let app = express();
|
||||
|
||||
// run in production mode by default, just in case
|
||||
// it shouldn't make any difference anyway
|
||||
app.set('env', process.env.NODE_ENV || 'production')
|
||||
app.set('env', process.env.NODE_ENV || 'production');
|
||||
|
||||
function error_reporting_middleware(req, res, next) {
|
||||
res.report_error = res.report_error || function(err) {
|
||||
if (err.status && err.status >= 400 && err.status < 600) {
|
||||
if (!res.headersSent) {
|
||||
res.status(err.status)
|
||||
next({ error: err.message || 'unknown error' })
|
||||
res.status(err.status);
|
||||
next({error: err.message || 'unknown error'});
|
||||
}
|
||||
} else {
|
||||
Logger.logger.error( { err: err }
|
||||
, 'unexpected error: @{!err.message}\n@{err.stack}')
|
||||
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()
|
||||
Logger.logger.error('this is an error in express.js, please report this');
|
||||
res.destroy();
|
||||
} else if (!res.headersSent) {
|
||||
res.status(500)
|
||||
next({ error: 'internal server error' })
|
||||
res.status(500);
|
||||
next({error: 'internal server error'});
|
||||
} else {
|
||||
// socket should be already closed
|
||||
}
|
||||
}
|
||||
}
|
||||
next()
|
||||
};
|
||||
next();
|
||||
}
|
||||
|
||||
app.use(Middleware.log)
|
||||
app.use(error_reporting_middleware)
|
||||
app.use(Middleware.log);
|
||||
app.use(error_reporting_middleware);
|
||||
app.use(function(req, res, next) {
|
||||
res.setHeader('X-Powered-By', config.user_agent)
|
||||
next()
|
||||
})
|
||||
app.use(Cats.middleware)
|
||||
app.use(compression())
|
||||
res.setHeader('X-Powered-By', config.user_agent);
|
||||
next();
|
||||
});
|
||||
app.use(Cats.middleware);
|
||||
app.use(compression());
|
||||
|
||||
app.get('/favicon.ico', function(req, res, next) {
|
||||
req.url = '/-/static/favicon.png'
|
||||
next()
|
||||
})
|
||||
req.url = '/-/static/favicon.png';
|
||||
next();
|
||||
});
|
||||
|
||||
// hook for tests only
|
||||
if (config._debug) {
|
||||
app.get('/-/_debug', function(req, res, next) {
|
||||
var do_gc = typeof(global.gc) !== 'undefined'
|
||||
if (do_gc) global.gc()
|
||||
let do_gc = typeof(global.gc) !== 'undefined';
|
||||
if (do_gc) global.gc();
|
||||
next({
|
||||
pid : process.pid,
|
||||
main : process.mainModule.filename,
|
||||
conf : config.self_path,
|
||||
mem : process.memoryUsage(),
|
||||
gc : do_gc,
|
||||
})
|
||||
})
|
||||
pid: process.pid,
|
||||
main: process.mainModule.filename,
|
||||
conf: config.self_path,
|
||||
mem: process.memoryUsage(),
|
||||
gc: do_gc,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
app.use(require('./index-api')(config, auth, storage))
|
||||
app.use(require('./index-api')(config, auth, storage));
|
||||
|
||||
if (config.web && config.web.enable === false) {
|
||||
app.get('/', function(req, res, next) {
|
||||
next( Error[404]('web interface is disabled in the config file') )
|
||||
})
|
||||
next( Error[404]('web interface is disabled in the config file') );
|
||||
});
|
||||
} else {
|
||||
app.use(require('./index-web')(config, auth, storage))
|
||||
app.use(require('./index-web')(config, auth, storage));
|
||||
}
|
||||
|
||||
app.get('/*', function(req, res, next) {
|
||||
next( Error[404]('file not found') )
|
||||
})
|
||||
next( Error[404]('file not found') );
|
||||
});
|
||||
|
||||
app.use(function(err, req, res, next) {
|
||||
if (Object.prototype.toString.call(err) !== '[object Error]') return next(err)
|
||||
if (err.code === 'ECONNABORT' && res.statusCode === 304) return next()
|
||||
if (Object.prototype.toString.call(err) !== '[object Error]') return next(err);
|
||||
if (err.code === 'ECONNABORT' && res.statusCode === 304) return next();
|
||||
if (typeof(res.report_error) !== 'function') {
|
||||
// in case of very early error this middleware may not be loaded before error is generated
|
||||
// fixing that
|
||||
error_reporting_middleware(req, res, function(){})
|
||||
error_reporting_middleware(req, res, function() {});
|
||||
}
|
||||
res.report_error(err)
|
||||
})
|
||||
res.report_error(err);
|
||||
});
|
||||
|
||||
app.use(Middleware.final)
|
||||
app.use(Middleware.final);
|
||||
|
||||
return app
|
||||
}
|
||||
return app;
|
||||
};
|
||||
|
||||
|
@ -1,44 +1,46 @@
|
||||
var fs = require('fs')
|
||||
var Path = require('path')
|
||||
'use strict';
|
||||
|
||||
module.exports = LocalData
|
||||
const fs = require('fs');
|
||||
const Path = require('path');
|
||||
|
||||
function LocalData(path) {
|
||||
var self = Object.create(LocalData.prototype)
|
||||
self.path = path
|
||||
try {
|
||||
self.data = JSON.parse(fs.readFileSync(self.path, 'utf8'))
|
||||
} catch(_) {
|
||||
self.data = { list: [] }
|
||||
}
|
||||
return self
|
||||
}
|
||||
class LocalData {
|
||||
|
||||
LocalData.prototype.add = function(name) {
|
||||
if (this.data.list.indexOf(name) === -1) {
|
||||
this.data.list.push(name)
|
||||
this.sync()
|
||||
}
|
||||
}
|
||||
|
||||
LocalData.prototype.remove = function(name) {
|
||||
var i = this.data.list.indexOf(name)
|
||||
if (i !== -1) {
|
||||
this.data.list.splice(i, 1)
|
||||
constructor(path) {
|
||||
this.path = path;
|
||||
try {
|
||||
this.data = JSON.parse(fs.readFileSync(this.path, 'utf8'));
|
||||
} catch(_) {
|
||||
this.data = {list: []};
|
||||
}
|
||||
}
|
||||
|
||||
add(name) {
|
||||
if (this.data.list.indexOf(name) === -1) {
|
||||
this.data.list.push(name);
|
||||
this.sync();
|
||||
}
|
||||
}
|
||||
|
||||
remove(name) {
|
||||
const i = this.data.list.indexOf(name);
|
||||
if (i !== -1) {
|
||||
this.data.list.splice(i, 1);
|
||||
}
|
||||
this.sync();
|
||||
}
|
||||
|
||||
get() {
|
||||
return this.data.list;
|
||||
}
|
||||
|
||||
sync() {
|
||||
// Uses sync to prevent ugly race condition
|
||||
try {
|
||||
require('mkdirp').sync(Path.dirname(this.path));
|
||||
} catch(err) {}
|
||||
fs.writeFileSync(this.path, JSON.stringify(this.data));
|
||||
}
|
||||
|
||||
this.sync()
|
||||
}
|
||||
|
||||
LocalData.prototype.get = function() {
|
||||
return this.data.list
|
||||
}
|
||||
|
||||
LocalData.prototype.sync = function() {
|
||||
// Uses sync to prevent ugly race condition
|
||||
try {
|
||||
require('mkdirp').sync(Path.dirname(this.path))
|
||||
} catch(err) {}
|
||||
fs.writeFileSync(this.path, JSON.stringify(this.data))
|
||||
}
|
||||
|
||||
module.exports = LocalData;
|
||||
|
218
lib/local-fs.js
218
lib/local-fs.js
@ -1,213 +1,215 @@
|
||||
var fs = require('fs')
|
||||
var Error = require('http-errors')
|
||||
var mkdirp = require('mkdirp')
|
||||
var Path = require('path')
|
||||
var MyStreams = require('./streams')
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const Error = require('http-errors');
|
||||
const mkdirp = require('mkdirp');
|
||||
const Path = require('path');
|
||||
const MyStreams = require('./streams');
|
||||
|
||||
function FSError(code) {
|
||||
var err = Error(code)
|
||||
err.code = code
|
||||
return err
|
||||
let err = Error(code);
|
||||
err.code = code;
|
||||
return err;
|
||||
}
|
||||
|
||||
var locker = require('./file-locking')
|
||||
let locker = require('./file-locking');
|
||||
|
||||
function tempFile(str) {
|
||||
return str + '.tmp' + String(Math.random()).substr(2)
|
||||
return str + '.tmp' + String(Math.random()).substr(2);
|
||||
}
|
||||
|
||||
function renameTmp(src, dst, _cb) {
|
||||
function cb(err) {
|
||||
if (err) fs.unlink(src, function() {})
|
||||
_cb(err)
|
||||
if (err) fs.unlink(src, function() {});
|
||||
_cb(err);
|
||||
}
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
return fs.rename(src, dst, cb)
|
||||
return fs.rename(src, dst, cb);
|
||||
}
|
||||
|
||||
// windows can't remove opened file,
|
||||
// but it seem to be able to rename it
|
||||
var tmp = tempFile(dst)
|
||||
let tmp = tempFile(dst);
|
||||
fs.rename(dst, tmp, function(err) {
|
||||
fs.rename(src, dst, cb)
|
||||
if (!err) fs.unlink(tmp, function () {})
|
||||
})
|
||||
fs.rename(src, dst, cb);
|
||||
if (!err) fs.unlink(tmp, () => {});
|
||||
});
|
||||
}
|
||||
|
||||
function write(dest, data, cb) {
|
||||
var safe_write = function(cb) {
|
||||
var tmpname = tempFile(dest)
|
||||
let safe_write = function(cb) {
|
||||
let tmpname = tempFile(dest);
|
||||
fs.writeFile(tmpname, data, function(err) {
|
||||
if (err) return cb(err)
|
||||
renameTmp(tmpname, dest, cb)
|
||||
})
|
||||
}
|
||||
if (err) return cb(err);
|
||||
renameTmp(tmpname, dest, cb);
|
||||
});
|
||||
};
|
||||
|
||||
safe_write(function(err) {
|
||||
if (err && err.code === 'ENOENT') {
|
||||
mkdirp(Path.dirname(dest), function(err) {
|
||||
if (err) return cb(err)
|
||||
safe_write(cb)
|
||||
})
|
||||
if (err) return cb(err);
|
||||
safe_write(cb);
|
||||
});
|
||||
} else {
|
||||
cb(err)
|
||||
cb(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function write_stream(name) {
|
||||
var stream = MyStreams.UploadTarballStream()
|
||||
let stream = MyStreams.UploadTarballStream();
|
||||
|
||||
var _ended = 0
|
||||
let _ended = 0;
|
||||
stream.on('end', function() {
|
||||
_ended = 1
|
||||
})
|
||||
_ended = 1;
|
||||
});
|
||||
|
||||
fs.exists(name, function(exists) {
|
||||
if (exists) return stream.emit('error', FSError('EEXISTS'))
|
||||
if (exists) return stream.emit('error', FSError('EEXISTS'));
|
||||
|
||||
var tmpname = name + '.tmp-'+String(Math.random()).replace(/^0\./, '')
|
||||
var file = fs.createWriteStream(tmpname)
|
||||
var opened = false
|
||||
stream.pipe(file)
|
||||
let tmpname = name + '.tmp-'+String(Math.random()).replace(/^0\./, '');
|
||||
let file = fs.createWriteStream(tmpname);
|
||||
let opened = false;
|
||||
stream.pipe(file);
|
||||
|
||||
stream.done = function() {
|
||||
function onend() {
|
||||
file.on('close', function() {
|
||||
renameTmp(tmpname, name, function(err) {
|
||||
if (err) {
|
||||
stream.emit('error', err)
|
||||
stream.emit('error', err);
|
||||
} else {
|
||||
stream.emit('success')
|
||||
stream.emit('success');
|
||||
}
|
||||
})
|
||||
})
|
||||
file.destroySoon()
|
||||
});
|
||||
});
|
||||
file.destroySoon();
|
||||
}
|
||||
if (_ended) {
|
||||
onend()
|
||||
onend();
|
||||
} else {
|
||||
stream.on('end', onend)
|
||||
stream.on('end', onend);
|
||||
}
|
||||
}
|
||||
};
|
||||
stream.abort = function() {
|
||||
if (opened) {
|
||||
opened = false
|
||||
opened = false;
|
||||
file.on('close', function() {
|
||||
fs.unlink(tmpname, function(){})
|
||||
})
|
||||
fs.unlink(tmpname, function() {});
|
||||
});
|
||||
}
|
||||
file.destroySoon()
|
||||
}
|
||||
file.destroySoon();
|
||||
};
|
||||
file.on('open', function() {
|
||||
opened = true
|
||||
opened = true;
|
||||
// re-emitting open because it's handled in storage.js
|
||||
stream.emit('open')
|
||||
})
|
||||
stream.emit('open');
|
||||
});
|
||||
file.on('error', function(err) {
|
||||
stream.emit('error', err)
|
||||
})
|
||||
})
|
||||
return stream
|
||||
stream.emit('error', err);
|
||||
});
|
||||
});
|
||||
return stream;
|
||||
}
|
||||
|
||||
function read_stream(name, stream, callback) {
|
||||
var rstream = fs.createReadStream(name)
|
||||
let rstream = fs.createReadStream(name);
|
||||
rstream.on('error', function(err) {
|
||||
stream.emit('error', err)
|
||||
})
|
||||
stream.emit('error', err);
|
||||
});
|
||||
rstream.on('open', function(fd) {
|
||||
fs.fstat(fd, function(err, stats) {
|
||||
if (err) return stream.emit('error', err)
|
||||
stream.emit('content-length', stats.size)
|
||||
stream.emit('open')
|
||||
rstream.pipe(stream)
|
||||
})
|
||||
})
|
||||
if (err) return stream.emit('error', err);
|
||||
stream.emit('content-length', stats.size);
|
||||
stream.emit('open');
|
||||
rstream.pipe(stream);
|
||||
});
|
||||
});
|
||||
|
||||
stream = MyStreams.ReadTarballStream()
|
||||
stream = MyStreams.ReadTarballStream();
|
||||
stream.abort = function() {
|
||||
rstream.close()
|
||||
}
|
||||
return stream
|
||||
rstream.close();
|
||||
};
|
||||
return stream;
|
||||
}
|
||||
|
||||
function create(name, contents, callback) {
|
||||
fs.exists(name, function(exists) {
|
||||
if (exists) return callback( FSError('EEXISTS') )
|
||||
write(name, contents, callback)
|
||||
})
|
||||
if (exists) return callback( FSError('EEXISTS') );
|
||||
write(name, contents, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function update(name, contents, callback) {
|
||||
fs.exists(name, function(exists) {
|
||||
if (!exists) return callback( FSError('ENOENT') )
|
||||
write(name, contents, callback)
|
||||
})
|
||||
if (!exists) return callback( FSError('ENOENT') );
|
||||
write(name, contents, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function read(name, callback) {
|
||||
fs.readFile(name, callback)
|
||||
fs.readFile(name, callback);
|
||||
}
|
||||
|
||||
module.exports.read = read
|
||||
module.exports.read = read;
|
||||
|
||||
module.exports.read_json = function(name, cb) {
|
||||
read(name, function(err, res) {
|
||||
if (err) return cb(err)
|
||||
if (err) return cb(err);
|
||||
|
||||
var args = []
|
||||
let args = [];
|
||||
try {
|
||||
args = [ null, JSON.parse(res.toString('utf8')) ]
|
||||
args = [null, JSON.parse(res.toString('utf8'))];
|
||||
} catch(err) {
|
||||
args = [ err ]
|
||||
args = [err];
|
||||
}
|
||||
cb.apply(null, args)
|
||||
})
|
||||
}
|
||||
cb.apply(null, args);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.lock_and_read = function(name, cb) {
|
||||
locker.readFile(name, {lock: true}, function(err, res) {
|
||||
if (err) return cb(err)
|
||||
return cb(null, res)
|
||||
})
|
||||
}
|
||||
if (err) return cb(err);
|
||||
return cb(null, res);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.lock_and_read_json = function(name, cb) {
|
||||
locker.readFile(name, {lock: true, parse: true}, function(err, res) {
|
||||
if (err) return cb(err)
|
||||
if (err) return cb(err);
|
||||
return cb(null, res);
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.unlock_file = function (name, cb) {
|
||||
locker.unlockFile(name, cb)
|
||||
}
|
||||
module.exports.unlock_file = function(name, cb) {
|
||||
locker.unlockFile(name, cb);
|
||||
};
|
||||
|
||||
module.exports.create = create
|
||||
module.exports.create = create;
|
||||
|
||||
module.exports.create_json = function(name, value, cb) {
|
||||
create(name, JSON.stringify(value, null, '\t'), cb)
|
||||
}
|
||||
create(name, JSON.stringify(value, null, '\t'), cb);
|
||||
};
|
||||
|
||||
module.exports.update = update
|
||||
module.exports.update = update;
|
||||
|
||||
module.exports.update_json = function(name, value, cb) {
|
||||
update(name, JSON.stringify(value, null, '\t'), cb)
|
||||
}
|
||||
update(name, JSON.stringify(value, null, '\t'), cb);
|
||||
};
|
||||
|
||||
module.exports.write = write
|
||||
module.exports.write = write;
|
||||
|
||||
module.exports.write_json = function(name, value, cb) {
|
||||
write(name, JSON.stringify(value, null, '\t'), cb)
|
||||
}
|
||||
write(name, JSON.stringify(value, null, '\t'), cb);
|
||||
};
|
||||
|
||||
module.exports.write_stream = write_stream
|
||||
module.exports.write_stream = write_stream;
|
||||
|
||||
module.exports.read_stream = read_stream
|
||||
module.exports.read_stream = read_stream;
|
||||
|
||||
module.exports.unlink = fs.unlink
|
||||
module.exports.unlink = fs.unlink;
|
||||
|
||||
module.exports.rmdir = fs.rmdir
|
||||
module.exports.rmdir = fs.rmdir;
|
||||
|
||||
|
1364
lib/local-storage.js
1364
lib/local-storage.js
File diff suppressed because it is too large
Load Diff
281
lib/logger.js
281
lib/logger.js
@ -1,157 +1,178 @@
|
||||
var Logger = require('bunyan')
|
||||
var Error = require('http-errors')
|
||||
var Stream = require('stream')
|
||||
var Utils = require('./utils')
|
||||
'use strict';
|
||||
|
||||
const Logger = require('bunyan');
|
||||
const Error = require('http-errors');
|
||||
const Stream = require('stream');
|
||||
const chalk = require('chalk');
|
||||
const Utils = require('./utils');
|
||||
const pkgJSON = require('../package.json');
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} x
|
||||
*/
|
||||
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'
|
||||
}
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.setup = function(logs) {
|
||||
var streams = []
|
||||
if (logs == null) logs = [{ type: 'stdout', format: 'pretty', level: 'http' }]
|
||||
let streams = [];
|
||||
if (logs == null) logs = [{type: 'stdout', format: 'pretty', level: 'http'}];
|
||||
|
||||
logs.forEach(function(target) {
|
||||
var stream = new Stream()
|
||||
stream.writable = true
|
||||
logs.forEach(function(target) {
|
||||
const stream = new Stream();
|
||||
stream.writable = true;
|
||||
|
||||
if (target.type === 'stdout' || target.type === 'stderr') {
|
||||
// destination stream
|
||||
var dest = target.type === 'stdout' ? process.stdout : process.stderr
|
||||
if (target.type === 'stdout' || target.type === 'stderr') {
|
||||
// destination stream
|
||||
const dest = target.type === 'stdout' ? process.stdout : process.stderr;
|
||||
|
||||
if (target.format === 'pretty') {
|
||||
// making fake stream for prettypritting
|
||||
stream.write = function(obj) {
|
||||
dest.write(print(obj.level, obj.msg, obj, dest.isTTY) + '\n')
|
||||
}
|
||||
} else if (target.format === 'pretty-timestamped') {
|
||||
// making fake stream for prettypritting
|
||||
stream.write = function(obj) {
|
||||
dest.write(obj.time.toISOString() + print(obj.level, obj.msg, obj, dest.isTTY) + '\n')
|
||||
}
|
||||
} else {
|
||||
stream.write = function(obj) {
|
||||
dest.write(JSON.stringify(obj, Logger.safeCycles()) + '\n')
|
||||
}
|
||||
}
|
||||
} else if (target.type === 'file') {
|
||||
var dest = require('fs').createWriteStream(target.path, {flags: 'a', encoding: 'utf8'})
|
||||
dest.on('error', function (err) {
|
||||
Logger.emit('error', err)
|
||||
})
|
||||
stream.write = function(obj) {
|
||||
if (target.format === 'pretty') {
|
||||
dest.write(print(obj.level, obj.msg, obj, false) + '\n')
|
||||
} else {
|
||||
dest.write(JSON.stringify(obj, Logger.safeCycles()) + '\n')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw Error('wrong target type for a log')
|
||||
}
|
||||
if (target.format === 'pretty') {
|
||||
// making fake stream for prettypritting
|
||||
stream.write = function(obj) {
|
||||
dest.write(print(obj.level, obj.msg, obj, dest.isTTY) + '\n');
|
||||
};
|
||||
} else if (target.format === 'pretty-timestamped') {
|
||||
// making fake stream for prettypritting
|
||||
stream.write = function(obj) {
|
||||
dest.write(obj.time.toISOString() + print(obj.level, obj.msg, obj, dest.isTTY) + '\n');
|
||||
};
|
||||
} else {
|
||||
stream.write = function(obj) {
|
||||
dest.write(JSON.stringify(obj, Logger.safeCycles()) + '\n');
|
||||
};
|
||||
}
|
||||
} else if (target.type === 'file') {
|
||||
const dest = require('fs').createWriteStream(target.path, {flags: 'a', encoding: 'utf8'});
|
||||
dest.on('error', function(err) {
|
||||
Logger.emit('error', err);
|
||||
});
|
||||
stream.write = function(obj) {
|
||||
if (target.format === 'pretty') {
|
||||
dest.write(print(obj.level, obj.msg, obj, false) + '\n');
|
||||
} else {
|
||||
dest.write(JSON.stringify(obj, Logger.safeCycles()) + '\n');
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw Error('wrong target type for a log');
|
||||
}
|
||||
|
||||
if (target.level === 'http') target.level = 35
|
||||
streams.push({
|
||||
type: 'raw',
|
||||
level: target.level || 35,
|
||||
stream: stream,
|
||||
})
|
||||
})
|
||||
if (target.level === 'http') target.level = 35;
|
||||
streams.push({
|
||||
type: 'raw',
|
||||
level: target.level || 35,
|
||||
stream: stream,
|
||||
});
|
||||
});
|
||||
|
||||
var logger = new Logger({
|
||||
name: 'verdaccio',
|
||||
streams: streams,
|
||||
serializers: {
|
||||
err: Logger.stdSerializers.err,
|
||||
req: Logger.stdSerializers.req,
|
||||
res: Logger.stdSerializers.res,
|
||||
},
|
||||
})
|
||||
let logger = new Logger({
|
||||
name: pkgJSON.name,
|
||||
streams: streams,
|
||||
serializers: {
|
||||
err: Logger.stdSerializers.err,
|
||||
req: Logger.stdSerializers.req,
|
||||
res: Logger.stdSerializers.res,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports.logger = logger
|
||||
}
|
||||
module.exports.logger = logger;
|
||||
};
|
||||
|
||||
// adopted from socket.io
|
||||
// this part was converted to coffee-script and back again over the years,
|
||||
// so it might look weird
|
||||
|
||||
// level to color
|
||||
var levels = {
|
||||
fatal : 31,
|
||||
error : 31,
|
||||
warn : 33,
|
||||
http : 35,
|
||||
info : 36,
|
||||
debug : 90,
|
||||
trace : 90,
|
||||
}
|
||||
|
||||
var max = 0
|
||||
for (var l in levels) {
|
||||
max = Math.max(max, l.length)
|
||||
let levels = {
|
||||
fatal: chalk.red,
|
||||
error: chalk.red,
|
||||
warn: chalk.yellow,
|
||||
http: chalk.magenta,
|
||||
info: chalk.cyan,
|
||||
debug: chalk.black,
|
||||
trace: chalk.white,
|
||||
};
|
||||
|
||||
let max = 0;
|
||||
for (let l in levels) {
|
||||
max = Math.max(max, l.length);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} str
|
||||
*/
|
||||
function pad(str) {
|
||||
if (str.length < max) return str + ' '.repeat(max - str.length)
|
||||
return str
|
||||
if (str.length < max) return str + ' '.repeat(max - str.length);
|
||||
return str;
|
||||
}
|
||||
|
||||
var subsystems = [{
|
||||
in : '\033[32m<--\033[39m',
|
||||
out : '\033[33m-->\033[39m',
|
||||
fs : '\033[90m-=-\033[39m',
|
||||
default : '\033[34m---\033[39m',
|
||||
}, {
|
||||
in : '<--',
|
||||
out : '-->',
|
||||
fs : '-=-',
|
||||
default : '---',
|
||||
}]
|
||||
|
||||
/**
|
||||
* Build a string
|
||||
* @param {*} type
|
||||
* @param {*} msg
|
||||
* @param {*} obj
|
||||
* @param {*} colors
|
||||
*/
|
||||
function print(type, msg, obj, colors) {
|
||||
if (typeof type === 'number') type = getlvl(type)
|
||||
var finalmsg = msg.replace(/@{(!?[$A-Za-z_][$0-9A-Za-z\._]*)}/g, function(_, name) {
|
||||
var str = obj, is_error
|
||||
if (name[0] === '!') {
|
||||
name = name.substr(1)
|
||||
is_error = true
|
||||
}
|
||||
if (typeof type === 'number') type = getlvl(type);
|
||||
let finalmsg = msg.replace(/@{(!?[$A-Za-z_][$0-9A-Za-z\._]*)}/g, function(_, name) {
|
||||
let str = obj;
|
||||
let is_error;
|
||||
if (name[0] === '!') {
|
||||
name = name.substr(1);
|
||||
is_error = true;
|
||||
}
|
||||
|
||||
var _ref = name.split('.')
|
||||
for (var _i = 0; _i < _ref.length; _i++) {
|
||||
var id = _ref[_i]
|
||||
if (Utils.is_object(str) || Array.isArray(str)) {
|
||||
str = str[id]
|
||||
} else {
|
||||
str = undefined
|
||||
}
|
||||
}
|
||||
let _ref = name.split('.');
|
||||
for (let _i = 0; _i < _ref.length; _i++) {
|
||||
let id = _ref[_i];
|
||||
if (Utils.is_object(str) || Array.isArray(str)) {
|
||||
str = str[id];
|
||||
} else {
|
||||
str = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(str) === 'string') {
|
||||
if (!colors || str.includes('\n')) {
|
||||
return str
|
||||
} else if (is_error) {
|
||||
return '\033[31m' + str + '\033[39m'
|
||||
} else {
|
||||
return '\033[32m' + str + '\033[39m'
|
||||
}
|
||||
} else {
|
||||
return require('util').inspect(str, null, null, colors)
|
||||
}
|
||||
})
|
||||
if (typeof(str) === 'string') {
|
||||
if (!colors || str.includes('\n')) {
|
||||
return str;
|
||||
} else if (is_error) {
|
||||
return chalk.red(str);
|
||||
} else {
|
||||
return chalk.green(str);
|
||||
}
|
||||
} else {
|
||||
return require('util').inspect(str, null, null, colors);
|
||||
}
|
||||
});
|
||||
|
||||
var sub = subsystems[colors ? 0 : 1][obj.sub] || subsystems[+!colors].default
|
||||
if (colors) {
|
||||
return ' \033[' + levels[type] + 'm' + (pad(type)) + '\033[39m ' + sub + ' ' + finalmsg
|
||||
} else {
|
||||
return ' ' + (pad(type)) + ' ' + sub + ' ' + finalmsg
|
||||
}
|
||||
const subsystems = [{
|
||||
in: chalk.green('<--'),
|
||||
out: chalk.yellow('-->'),
|
||||
fs: chalk.black('-=-'),
|
||||
default: chalk.blue('---'),
|
||||
}, {
|
||||
in: '<--',
|
||||
out: '-->',
|
||||
fs: '-=-',
|
||||
default: '---',
|
||||
}];
|
||||
|
||||
let sub = subsystems[colors ? 0 : 1][obj.sub] || subsystems[+!colors].default;
|
||||
if (colors) {
|
||||
// return ' \033[' + levels[type] + 'm' + (pad(type)) + '\033[39m ' + sub + ' ' + finalmsg
|
||||
return ` ${levels[type]((pad(type)))}${chalk.white(`${sub} ${finalmsg}`)}`;
|
||||
} else {
|
||||
return ` ${(pad(type))}${sub} ${finalmsg}`;
|
||||
}
|
||||
}
|
||||
|
@ -1,79 +1,81 @@
|
||||
var crypto = require('crypto')
|
||||
var Error = require('http-errors')
|
||||
var utils = require('./utils')
|
||||
var Logger = require('./logger')
|
||||
'use strict';
|
||||
|
||||
let crypto = require('crypto');
|
||||
let Error = require('http-errors');
|
||||
let utils = require('./utils');
|
||||
let Logger = require('./logger');
|
||||
|
||||
module.exports.match = function match(regexp) {
|
||||
return function(req, res, next, value, name) {
|
||||
if (regexp.exec(value)) {
|
||||
next()
|
||||
next();
|
||||
} else {
|
||||
next('route')
|
||||
next('route');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.validate_name = function validate_name(req, res, next, value, name) {
|
||||
if (value.charAt(0) === '-') {
|
||||
// special case in couchdb usually
|
||||
next('route')
|
||||
next('route');
|
||||
} else if (utils.validate_name(value)) {
|
||||
next()
|
||||
next();
|
||||
} else {
|
||||
next( Error[403]('invalid ' + name) )
|
||||
next( Error[403]('invalid ' + name) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.validate_package = function validate_package(req, res, next, value, name) {
|
||||
if (value.charAt(0) === '-') {
|
||||
// special case in couchdb usually
|
||||
next('route')
|
||||
next('route');
|
||||
} else if (utils.validate_package(value)) {
|
||||
next()
|
||||
next();
|
||||
} else {
|
||||
next( Error[403]('invalid ' + name) )
|
||||
next( Error[403]('invalid ' + name) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.media = function media(expect) {
|
||||
return function(req, res, next) {
|
||||
if (req.headers['content-type'] !== expect) {
|
||||
next( Error[415]('wrong content-type, expect: ' + expect
|
||||
+ ', got: '+req.headers['content-type']) )
|
||||
+ ', got: '+req.headers['content-type']) );
|
||||
} else {
|
||||
next()
|
||||
next();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.expect_json = function expect_json(req, res, next) {
|
||||
if (!utils.is_object(req.body)) {
|
||||
return next( Error[400]("can't parse incoming json") )
|
||||
return next( Error[400]('can\'t parse incoming json') );
|
||||
}
|
||||
next()
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports.anti_loop = function(config) {
|
||||
return function(req, res, next) {
|
||||
if (req.headers.via != null) {
|
||||
var arr = req.headers.via.split(',')
|
||||
let arr = req.headers.via.split(',');
|
||||
|
||||
for (var i=0; i<arr.length; i++) {
|
||||
var m = arr[i].match(/\s*(\S+)\s+(\S+)/)
|
||||
for (let i=0; i<arr.length; i++) {
|
||||
let m = arr[i].match(/\s*(\S+)\s+(\S+)/);
|
||||
if (m && m[2] === config.server_id) {
|
||||
return next( Error[508]('loop detected') )
|
||||
return next( Error[508]('loop detected') );
|
||||
}
|
||||
}
|
||||
}
|
||||
next()
|
||||
}
|
||||
}
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
// express doesn't do etags with requests <= 1024b
|
||||
// we use md5 here, it works well on 1k+ bytes, but sucks with fewer data
|
||||
// could improve performance using crc32 after benchmarks
|
||||
function md5sum(data) {
|
||||
return crypto.createHash('md5').update(data).digest('hex')
|
||||
return crypto.createHash('md5').update(data).digest('hex');
|
||||
}
|
||||
|
||||
module.exports.allow = function(auth) {
|
||||
@ -83,41 +85,41 @@ module.exports.allow = function(auth) {
|
||||
auth['allow_'+action](req.params.package, req.remote_user, function(error, allowed) {
|
||||
req.resume();
|
||||
if (error) {
|
||||
next(error)
|
||||
next(error);
|
||||
} else if (allowed) {
|
||||
next()
|
||||
next();
|
||||
} else {
|
||||
// last plugin (that's our built-in one) returns either
|
||||
// cb(err) or cb(null, true), so this should never happen
|
||||
throw Error('bug in the auth plugin system')
|
||||
throw Error('bug in the auth plugin system');
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.final = function(body, req, res, next) {
|
||||
if (res.statusCode === 401 && !res.getHeader('WWW-Authenticate')) {
|
||||
// they say it's required for 401, so...
|
||||
res.header('WWW-Authenticate', 'Basic, Bearer')
|
||||
res.header('WWW-Authenticate', 'Basic, Bearer');
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof(body) === 'string' || typeof(body) === 'object') {
|
||||
if (!res.getHeader('Content-type')) {
|
||||
res.header('Content-type', 'application/json')
|
||||
res.header('Content-type', 'application/json');
|
||||
}
|
||||
|
||||
if (typeof(body) === 'object' && body != null) {
|
||||
if (typeof(body.error) === 'string') {
|
||||
res._verdaccio_error = body.error
|
||||
res._verdaccio_error = body.error;
|
||||
}
|
||||
body = JSON.stringify(body, undefined, ' ') + '\n'
|
||||
body = JSON.stringify(body, undefined, ' ') + '\n';
|
||||
}
|
||||
|
||||
// don't send etags with errors
|
||||
if (!res.statusCode || (res.statusCode >= 200 && res.statusCode < 300)) {
|
||||
res.header('ETag', '"' + md5sum(body) + '"')
|
||||
res.header('ETag', '"' + md5sum(body) + '"');
|
||||
}
|
||||
} else {
|
||||
// send(null), send(204), etc.
|
||||
@ -127,77 +129,77 @@ module.exports.final = function(body, req, res, next) {
|
||||
// as an error handler, we can't report error properly,
|
||||
// and should just close socket
|
||||
if (err.message.match(/set headers after they are sent/)) {
|
||||
if (res.socket != null) res.socket.destroy()
|
||||
return
|
||||
if (res.socket != null) res.socket.destroy();
|
||||
return;
|
||||
} else {
|
||||
throw err
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
res.send(body)
|
||||
}
|
||||
res.send(body);
|
||||
};
|
||||
|
||||
module.exports.log = function(req, res, next) {
|
||||
// logger
|
||||
req.log = Logger.logger.child({ sub: 'in' })
|
||||
req.log = Logger.logger.child({sub: 'in'});
|
||||
|
||||
var _auth = req.headers.authorization
|
||||
if (_auth != null) req.headers.authorization = '<Classified>'
|
||||
var _cookie = req.headers.cookie
|
||||
if (_cookie != null) req.headers.cookie = '<Classified>'
|
||||
let _auth = req.headers.authorization;
|
||||
if (_auth != null) req.headers.authorization = '<Classified>';
|
||||
let _cookie = req.headers.cookie;
|
||||
if (_cookie != null) req.headers.cookie = '<Classified>';
|
||||
|
||||
req.url = req.originalUrl
|
||||
req.log.info( { req: req, ip: req.ip }
|
||||
, '@{ip} requested \'@{req.method} @{req.url}\'' )
|
||||
req.originalUrl = req.url
|
||||
req.url = req.originalUrl;
|
||||
req.log.info( {req: req, ip: req.ip}
|
||||
, '@{ip} requested \'@{req.method} @{req.url}\'' );
|
||||
req.originalUrl = req.url;
|
||||
|
||||
if (_auth != null) req.headers.authorization = _auth
|
||||
if (_cookie != null) req.headers.cookie = _cookie
|
||||
if (_auth != null) req.headers.authorization = _auth;
|
||||
if (_cookie != null) req.headers.cookie = _cookie;
|
||||
|
||||
var bytesin = 0
|
||||
let bytesin = 0;
|
||||
req.on('data', function(chunk) {
|
||||
bytesin += chunk.length
|
||||
})
|
||||
bytesin += chunk.length;
|
||||
});
|
||||
|
||||
var bytesout = 0
|
||||
var _write = res.write
|
||||
let bytesout = 0;
|
||||
let _write = res.write;
|
||||
res.write = function(buf) {
|
||||
bytesout += buf.length
|
||||
_write.apply(res, arguments)
|
||||
}
|
||||
bytesout += buf.length;
|
||||
_write.apply(res, arguments);
|
||||
};
|
||||
|
||||
function log() {
|
||||
var message = "@{status}, user: @{user}, req: '@{request.method} @{request.url}'"
|
||||
let message = '@{status}, user: @{user}, req: \'@{request.method} @{request.url}\'';
|
||||
if (res._verdaccio_error) {
|
||||
message += ', error: @{!error}'
|
||||
message += ', error: @{!error}';
|
||||
} else {
|
||||
message += ', bytes: @{bytes.in}/@{bytes.out}'
|
||||
message += ', bytes: @{bytes.in}/@{bytes.out}';
|
||||
}
|
||||
|
||||
req.url = req.originalUrl
|
||||
req.url = req.originalUrl;
|
||||
req.log.warn({
|
||||
request : { method: req.method, url: req.url },
|
||||
level : 35, // http
|
||||
user : req.remote_user && req.remote_user.name,
|
||||
status : res.statusCode,
|
||||
error : res._verdaccio_error,
|
||||
bytes : {
|
||||
in : bytesin,
|
||||
out : bytesout,
|
||||
}
|
||||
}, message)
|
||||
req.originalUrl = req.url
|
||||
request: {method: req.method, url: req.url},
|
||||
level: 35, // http
|
||||
user: req.remote_user && req.remote_user.name,
|
||||
status: res.statusCode,
|
||||
error: res._verdaccio_error,
|
||||
bytes: {
|
||||
in: bytesin,
|
||||
out: bytesout,
|
||||
},
|
||||
}, message);
|
||||
req.originalUrl = req.url;
|
||||
}
|
||||
|
||||
req.on('close', function() {
|
||||
log(true)
|
||||
})
|
||||
log(true);
|
||||
});
|
||||
|
||||
var _end = res.end
|
||||
let _end = res.end;
|
||||
res.end = function(buf) {
|
||||
if (buf) bytesout += buf.length
|
||||
_end.apply(res, arguments)
|
||||
log()
|
||||
}
|
||||
next()
|
||||
}
|
||||
if (buf) bytesout += buf.length;
|
||||
_end.apply(res, arguments);
|
||||
log();
|
||||
};
|
||||
next();
|
||||
};
|
||||
|
@ -1,24 +1,24 @@
|
||||
var Handlebars = require('handlebars')
|
||||
var request = require('request')
|
||||
var Logger = require('./logger')
|
||||
'use strict';
|
||||
|
||||
let Handlebars = require('handlebars');
|
||||
let request = require('request');
|
||||
let Logger = require('./logger');
|
||||
|
||||
module.exports.notify = function(metadata, config) {
|
||||
|
||||
if (config.notify && config.notify.content) {
|
||||
let template = Handlebars.compile(config.notify.content);
|
||||
let content = template( metadata );
|
||||
|
||||
var template = Handlebars.compile(config.notify.content)
|
||||
var content = template( metadata )
|
||||
|
||||
var options = {
|
||||
body: content
|
||||
}
|
||||
let options = {
|
||||
body: content,
|
||||
};
|
||||
|
||||
// provides fallback support, it's accept an Object {} and Array of {}
|
||||
if ( config.notify.headers && Array.isArray(config.notify.headers) ) {
|
||||
var header = {};
|
||||
let header = {};
|
||||
config.notify.headers.map(function(item) {
|
||||
if (Object.is(item, item)) {
|
||||
for (var key in item) {
|
||||
for (let key in item) {
|
||||
header[key] = item[key];
|
||||
}
|
||||
}
|
||||
@ -31,19 +31,18 @@ module.exports.notify = function(metadata, config) {
|
||||
options.method = config.notify.method;
|
||||
|
||||
if (config.notify.endpoint) {
|
||||
options.url = config.notify.endpoint
|
||||
options.url = config.notify.endpoint;
|
||||
}
|
||||
|
||||
request(options, function(err, response, body) {
|
||||
if (err) {
|
||||
Logger.logger.error( { err: err }, ' notify error: @{err.message}' );
|
||||
Logger.logger.error( {err: err}, ' notify error: @{err.message}' );
|
||||
} else {
|
||||
Logger.logger.info({ content: content}, 'A notification has been shipped: @{content}')
|
||||
Logger.logger.info({content: content}, 'A notification has been shipped: @{content}');
|
||||
if (body) {
|
||||
Logger.logger.debug( { body: body }, ' body: @{body}' );
|
||||
Logger.logger.debug( {body: body}, ' body: @{body}' );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,57 +1,59 @@
|
||||
var Path = require('path')
|
||||
'use strict';
|
||||
|
||||
let Path = require('path');
|
||||
|
||||
function try_load(path) {
|
||||
try {
|
||||
return require(path)
|
||||
return require(path);
|
||||
} catch(err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
throw err
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function load_plugins(config, plugin_configs, params, sanity_check) {
|
||||
var plugins = Object.keys(plugin_configs || {}).map(function(p) {
|
||||
var plugin
|
||||
let plugins = Object.keys(plugin_configs || {}).map(function(p) {
|
||||
let plugin;
|
||||
|
||||
// try local plugins first
|
||||
plugin = try_load(Path.resolve(__dirname + '/plugins', p))
|
||||
plugin = try_load(Path.resolve(__dirname + '/plugins', p));
|
||||
|
||||
// npm package
|
||||
if (plugin === null && p.match(/^[^\.\/]/)) {
|
||||
plugin = try_load('verdaccio-' + p)
|
||||
plugin = try_load(`verdaccio-${p}`);
|
||||
// compatibility for old sinopia plugins
|
||||
if (!plugin) {
|
||||
plugin = try_load('sinopia-' + p)
|
||||
plugin = try_load(`sinopia-${p}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin === null) {
|
||||
plugin = try_load(p)
|
||||
plugin = try_load(p);
|
||||
}
|
||||
|
||||
// relative to config path
|
||||
if (plugin === null && p.match(/^\.\.?($|\/)/)) {
|
||||
plugin = try_load(Path.resolve(Path.dirname(config.self_path), p))
|
||||
plugin = try_load(Path.resolve(Path.dirname(config.self_path), p));
|
||||
}
|
||||
|
||||
if (plugin === null) {
|
||||
throw new Error('"' + p + '" plugin not found\ntry "npm install verdaccio-' + p + '"')
|
||||
throw Error('"' + p + '" plugin not found\ntry "npm install verdaccio-' + p + '"');
|
||||
}
|
||||
|
||||
if (typeof(plugin) !== 'function')
|
||||
throw new Error('"' + p + '" doesn\'t look like a valid plugin')
|
||||
throw Error('"' + p + '" doesn\'t look like a valid plugin');
|
||||
|
||||
plugin = plugin(plugin_configs[p], params)
|
||||
plugin = plugin(plugin_configs[p], params);
|
||||
|
||||
if (plugin === null || !sanity_check(plugin))
|
||||
throw new Error('"' + p + '" doesn\'t look like a valid plugin')
|
||||
throw Error('"' + p + '" doesn\'t look like a valid plugin');
|
||||
|
||||
return plugin;
|
||||
})
|
||||
});
|
||||
|
||||
return plugins
|
||||
return plugins;
|
||||
}
|
||||
|
||||
exports.load_plugins = load_plugins;
|
||||
|
@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/** Node.js Crypt(3) Library
|
||||
|
||||
Inspired by (and intended to be compatible with) sendanor/crypt3
|
||||
@ -10,7 +12,7 @@
|
||||
|
||||
*/
|
||||
|
||||
var crypt = require('unix-crypt-td-js'),
|
||||
let crypt = require('unix-crypt-td-js'),
|
||||
crypto = require('crypto');
|
||||
|
||||
function createSalt(type) {
|
||||
@ -33,7 +35,6 @@ function createSalt(type) {
|
||||
default:
|
||||
throw new TypeError('Unknown salt type at crypt3.createSalt: ' + type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function crypt3(key, salt) {
|
||||
|
@ -1,49 +1,51 @@
|
||||
var fs = require('fs')
|
||||
var Path = require('path')
|
||||
var utils = require('./utils')
|
||||
'use strict';
|
||||
|
||||
module.exports = HTPasswd
|
||||
let fs = require('fs');
|
||||
let Path = require('path');
|
||||
let utils = require('./utils');
|
||||
|
||||
module.exports = HTPasswd;
|
||||
|
||||
function HTPasswd(config, stuff) {
|
||||
var self = Object.create(HTPasswd.prototype)
|
||||
self._users = {}
|
||||
let self = Object.create(HTPasswd.prototype);
|
||||
self._users = {};
|
||||
|
||||
// config for this module
|
||||
self._config = config
|
||||
self._config = config;
|
||||
|
||||
// verdaccio logger
|
||||
self._logger = stuff.logger
|
||||
self._logger = stuff.logger;
|
||||
|
||||
// verdaccio main config object
|
||||
self._verdaccio_config = stuff.config
|
||||
self._verdaccio_config = stuff.config;
|
||||
|
||||
// all this "verdaccio_config" stuff is for b/w compatibility only
|
||||
self._maxusers = self._config.max_users
|
||||
if (!self._maxusers) self._maxusers = self._verdaccio_config.max_users
|
||||
self._maxusers = self._config.max_users;
|
||||
if (!self._maxusers) self._maxusers = self._verdaccio_config.max_users;
|
||||
// set maxusers to Infinity if not specified
|
||||
if (!self._maxusers) self._maxusers = Infinity
|
||||
if (!self._maxusers) self._maxusers = Infinity;
|
||||
|
||||
self._last_time = null
|
||||
var file = self._config.file
|
||||
if (!file) file = self._verdaccio_config.users_file
|
||||
if (!file) throw new Error('should specify "file" in config')
|
||||
self._path = Path.resolve(Path.dirname(self._verdaccio_config.self_path), file)
|
||||
return self
|
||||
self._last_time = null;
|
||||
let file = self._config.file;
|
||||
if (!file) file = self._verdaccio_config.users_file;
|
||||
if (!file) throw new Error('should specify "file" in config');
|
||||
self._path = Path.resolve(Path.dirname(self._verdaccio_config.self_path), file);
|
||||
return self;
|
||||
}
|
||||
|
||||
HTPasswd.prototype.authenticate = function (user, password, cb) {
|
||||
var self = this
|
||||
self._reload(function (err) {
|
||||
if (err) return cb(err.code === 'ENOENT' ? null : err)
|
||||
if (!self._users[user]) return cb(null, false)
|
||||
if (!utils.verify_password(user, password, self._users[user])) return cb(null, false)
|
||||
HTPasswd.prototype.authenticate = function(user, password, cb) {
|
||||
let self = this;
|
||||
self._reload(function(err) {
|
||||
if (err) return cb(err.code === 'ENOENT' ? null : err);
|
||||
if (!self._users[user]) return cb(null, false);
|
||||
if (!utils.verify_password(user, password, self._users[user])) return cb(null, false);
|
||||
|
||||
// authentication succeeded!
|
||||
// return all usergroups this user has access to;
|
||||
// (this particular package has no concept of usergroups, so just return user herself)
|
||||
return cb(null, [user])
|
||||
})
|
||||
}
|
||||
return cb(null, [user]);
|
||||
});
|
||||
};
|
||||
|
||||
// hopefully race-condition-free way to add users:
|
||||
// 1. lock file for writing (other processes can still read)
|
||||
@ -52,85 +54,82 @@ HTPasswd.prototype.authenticate = function (user, password, cb) {
|
||||
// 4. move .htpasswd.tmp to .htpasswd
|
||||
// 5. reload .htpasswd
|
||||
// 6. unlock file
|
||||
HTPasswd.prototype.adduser = function (user, password, real_cb) {
|
||||
var self = this
|
||||
HTPasswd.prototype.adduser = function(user, password, real_cb) {
|
||||
let self = this;
|
||||
|
||||
function sanity_check() {
|
||||
var err = null
|
||||
let err = null;
|
||||
if (self._users[user]) {
|
||||
err = Error('this user already exists')
|
||||
err = Error('this user already exists');
|
||||
} else if (Object.keys(self._users).length >= self._maxusers) {
|
||||
err = Error('maximum amount of users reached')
|
||||
err = Error('maximum amount of users reached');
|
||||
}
|
||||
if (err) err.status = 403
|
||||
return err
|
||||
if (err) err.status = 403;
|
||||
return err;
|
||||
}
|
||||
|
||||
// preliminary checks, just to ensure that file won't be reloaded if it's not needed
|
||||
var s_err = sanity_check()
|
||||
if (s_err) return real_cb(s_err, false)
|
||||
let s_err = sanity_check();
|
||||
if (s_err) return real_cb(s_err, false);
|
||||
|
||||
utils.lock_and_read(self._path, function (err, res) {
|
||||
var locked = false
|
||||
utils.lock_and_read(self._path, function(err, res) {
|
||||
let locked = false;
|
||||
|
||||
// callback that cleans up lock first
|
||||
function cb(err) {
|
||||
if (locked) {
|
||||
utils.unlock_file(self._path, function () {
|
||||
utils.unlock_file(self._path, function() {
|
||||
// ignore any error from the unlock
|
||||
real_cb(err, !err)
|
||||
})
|
||||
real_cb(err, !err);
|
||||
});
|
||||
} else {
|
||||
real_cb(err, !err)
|
||||
real_cb(err, !err);
|
||||
}
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
locked = true
|
||||
locked = true;
|
||||
}
|
||||
|
||||
// ignore ENOENT errors, we'll just create .htpasswd in that case
|
||||
if (err && err.code !== 'ENOENT') return cb(err)
|
||||
if (err && err.code !== 'ENOENT') return cb(err);
|
||||
|
||||
var body = (res || '').toString('utf8')
|
||||
self._users = utils.parse_htpasswd(body)
|
||||
let body = (res || '').toString('utf8');
|
||||
self._users = utils.parse_htpasswd(body);
|
||||
|
||||
// real checks, to prevent race conditions
|
||||
var s_err = sanity_check()
|
||||
if (s_err) return cb(s_err)
|
||||
let s_err = sanity_check();
|
||||
if (s_err) return cb(s_err);
|
||||
|
||||
try {
|
||||
body = utils.add_user_to_htpasswd(body, user, password)
|
||||
body = utils.add_user_to_htpasswd(body, user, password);
|
||||
} catch (err) {
|
||||
return cb(err)
|
||||
return cb(err);
|
||||
}
|
||||
fs.writeFile(self._path, body, function (err) {
|
||||
if (err) return cb(err)
|
||||
self._reload(function () {
|
||||
cb(null, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
HTPasswd.prototype._reload = function (_callback) {
|
||||
var self = this
|
||||
|
||||
fs.stat(self._path, function (err, stats) {
|
||||
if (err) return _callback(err)
|
||||
|
||||
if (self._last_time === stats.mtime) return _callback()
|
||||
self._last_time = stats.mtime
|
||||
|
||||
fs.readFile(self._path, 'utf8', function (err, buffer) {
|
||||
if (err) return _callback(err)
|
||||
|
||||
self._users = utils.parse_htpasswd(buffer)
|
||||
|
||||
_callback()
|
||||
|
||||
fs.writeFile(self._path, body, function(err) {
|
||||
if (err) return cb(err);
|
||||
self._reload(function() {
|
||||
cb(null, true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
HTPasswd.prototype._reload = function(_callback) {
|
||||
let self = this;
|
||||
|
||||
fs.stat(self._path, function(err, stats) {
|
||||
if (err) return _callback(err);
|
||||
|
||||
if (self._last_time === stats.mtime) return _callback();
|
||||
self._last_time = stats.mtime;
|
||||
|
||||
fs.readFile(self._path, 'utf8', function(err, buffer) {
|
||||
if (err) return _callback(err);
|
||||
|
||||
self._users = utils.parse_htpasswd(buffer);
|
||||
|
||||
_callback();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -1,68 +1,70 @@
|
||||
var crypto = require('crypto')
|
||||
var crypt3 = require('./crypt3')
|
||||
var md5 = require('apache-md5')
|
||||
var locker = require('../../file-locking')
|
||||
'use strict';
|
||||
|
||||
let crypto = require('crypto');
|
||||
let crypt3 = require('./crypt3');
|
||||
let md5 = require('apache-md5');
|
||||
let locker = require('../../file-locking');
|
||||
|
||||
// this function neither unlocks file nor closes it
|
||||
// it'll have to be done manually later
|
||||
function lock_and_read(name, cb) {
|
||||
locker.readFile(name, {lock: true}, function (err, res) {
|
||||
locker.readFile(name, {lock: true}, function(err, res) {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
return cb(err);
|
||||
}
|
||||
return cb(null, res)
|
||||
})
|
||||
return cb(null, res);
|
||||
});
|
||||
}
|
||||
|
||||
// close and unlock file
|
||||
function unlock_file(name, cb) {
|
||||
locker.unlockFile(name, cb)
|
||||
locker.unlockFile(name, cb);
|
||||
}
|
||||
|
||||
function parse_htpasswd(input) {
|
||||
var result = {}
|
||||
let result = {};
|
||||
input.split('\n').forEach(function(line) {
|
||||
var args = line.split(':', 3)
|
||||
if (args.length > 1) result[args[0]] = args[1]
|
||||
})
|
||||
return result
|
||||
let args = line.split(':', 3);
|
||||
if (args.length > 1) result[args[0]] = args[1];
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function verify_password(user, passwd, hash) {
|
||||
if (hash.indexOf('{PLAIN}') === 0) {
|
||||
return passwd === hash.substr(7)
|
||||
return passwd === hash.substr(7);
|
||||
} else if (hash.indexOf('{SHA}') === 0) {
|
||||
return crypto.createHash('sha1').update(passwd, 'binary').digest('base64') === hash.substr(5)
|
||||
return crypto.createHash('sha1').update(passwd, 'binary').digest('base64') === hash.substr(5);
|
||||
} else {
|
||||
return (
|
||||
// for backwards compatibility, first check md5 then check crypt3
|
||||
md5(passwd, hash) === hash ||
|
||||
crypt3(passwd, hash) === hash
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function add_user_to_htpasswd(body, user, passwd) {
|
||||
if (user !== encodeURIComponent(user)) {
|
||||
var err = Error('username should not contain non-uri-safe characters')
|
||||
err.status = 409
|
||||
throw err
|
||||
let err = Error('username should not contain non-uri-safe characters');
|
||||
err.status = 409;
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (crypt3) {
|
||||
passwd = crypt3(passwd)
|
||||
passwd = crypt3(passwd);
|
||||
} else {
|
||||
passwd = '{SHA}' + crypto.createHash('sha1').update(passwd, 'binary').digest('base64')
|
||||
passwd = '{SHA}' + crypto.createHash('sha1').update(passwd, 'binary').digest('base64');
|
||||
}
|
||||
var comment = 'autocreated ' + (new Date()).toJSON()
|
||||
let comment = 'autocreated ' + (new Date()).toJSON();
|
||||
|
||||
var newline = user + ':' + passwd + ':' + comment + '\n'
|
||||
if (body.length && body[body.length-1] !== '\n') newline = '\n' + newline
|
||||
return body + newline
|
||||
let newline = user + ':' + passwd + ':' + comment + '\n';
|
||||
if (body.length && body[body.length-1] !== '\n') newline = '\n' + newline;
|
||||
return body + newline;
|
||||
}
|
||||
|
||||
module.exports.parse_htpasswd = parse_htpasswd
|
||||
module.exports.verify_password = verify_password
|
||||
module.exports.add_user_to_htpasswd = add_user_to_htpasswd
|
||||
module.exports.lock_and_read = lock_and_read
|
||||
module.exports.unlock_file = unlock_file
|
||||
module.exports.parse_htpasswd = parse_htpasswd;
|
||||
module.exports.verify_password = verify_password;
|
||||
module.exports.add_user_to_htpasswd = add_user_to_htpasswd;
|
||||
module.exports.lock_and_read = lock_and_read;
|
||||
module.exports.unlock_file = unlock_file;
|
||||
|
@ -1,49 +1,52 @@
|
||||
var lunr = require('lunr')
|
||||
'use strict';
|
||||
|
||||
function Search() {
|
||||
var self = Object.create(Search.prototype)
|
||||
self.index = lunr(function() {
|
||||
this.field('name' , { boost: 10 })
|
||||
this.field('description' , { boost: 4 })
|
||||
this.field('author' , { boost: 6 })
|
||||
this.field('readme')
|
||||
})
|
||||
return self
|
||||
const lunr = require('lunr');
|
||||
|
||||
class Search {
|
||||
constructor() {
|
||||
this.index = lunr(function() {
|
||||
this.field('name', {boost: 10});
|
||||
this.field('description', {boost: 4});
|
||||
this.field('author', {boost: 6});
|
||||
this.field('readme');
|
||||
});
|
||||
}
|
||||
|
||||
query(q) {
|
||||
return q === '*'
|
||||
? this.storage.config.localList.get().map( function( pkg ) {
|
||||
return {ref: pkg, score: 1};
|
||||
}) : this.index.search(q);
|
||||
}
|
||||
|
||||
add(pkg) {
|
||||
this.index.add({
|
||||
id: pkg.name,
|
||||
name: pkg.name,
|
||||
description: pkg.description,
|
||||
author: pkg._npmUser ? pkg._npmUser.name : '???',
|
||||
});
|
||||
}
|
||||
|
||||
remove(name) {
|
||||
this.index.remove({id: name});
|
||||
}
|
||||
|
||||
reindex() {
|
||||
let self = this;
|
||||
this.storage.get_local(function(err, packages) {
|
||||
if (err) throw err; // that function shouldn't produce any
|
||||
let i = packages.length;
|
||||
while (i--) {
|
||||
self.add(packages[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
configureStorage(storage) {
|
||||
this.storage = storage;
|
||||
this.reindex();
|
||||
}
|
||||
}
|
||||
|
||||
Search.prototype.query = function(q) {
|
||||
return q === '*'
|
||||
? this.storage.config.localList.get().map( function( package ){ return { ref: package, score: 1 }; } )
|
||||
: this.index.search(q);
|
||||
}
|
||||
|
||||
Search.prototype.add = function(package) {
|
||||
this.index.add({
|
||||
id: package.name,
|
||||
name: package.name,
|
||||
description: package.description,
|
||||
author: package._npmUser ? package._npmUser.name : '???',
|
||||
})
|
||||
},
|
||||
|
||||
Search.prototype.remove = function(name) {
|
||||
this.index.remove({ id: name })
|
||||
}
|
||||
|
||||
Search.prototype.reindex = function() {
|
||||
var self = this
|
||||
this.storage.get_local(function(err, packages) {
|
||||
if (err) throw err // that function shouldn't produce any
|
||||
var i = packages.length
|
||||
while (i--) {
|
||||
self.add(packages[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Search.prototype.configureStorage = function(storage) {
|
||||
this.storage = storage
|
||||
this.reindex()
|
||||
}
|
||||
|
||||
module.exports = Search()
|
||||
module.exports = new Search();
|
||||
|
@ -1,7 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
// see https://secure.flickr.com/photos/girliemac/sets/72157628409467125
|
||||
|
||||
var images = {
|
||||
const images = {
|
||||
100: 'aVvDhR', // '6512768893', // 100 - Continue
|
||||
101: 'aXXExP', // '6540479029', // 101 - Switching Protocols
|
||||
200: 'aVuVsF', // '6512628175', // 200 - OK
|
||||
@ -51,24 +52,24 @@ var images = {
|
||||
508: 'aVdnYa', // '6509400445', // 508 - Loop Detected
|
||||
509: 'aXXg1V', // '6540399865', // 509 - Bandwidth Limit Exceeded
|
||||
599: 'aVdo7v', // '6509400929', // 599 - Network connect timeout error
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.get_image = function(status) {
|
||||
if (status in images) {
|
||||
return 'http://flic.kr/p/' + images[status]
|
||||
//return 'https://secure.flickr.com/photos/girliemac/'+images[status]+'/in/set-72157628409467125/lightbox/'
|
||||
return 'http://flic.kr/p/' + images[status];
|
||||
// return 'https://secure.flickr.com/photos/girliemac/'+images[status]+'/in/set-72157628409467125/lightbox/'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.middleware = function(req, res, next) {
|
||||
var _writeHead = res.writeHead
|
||||
let _writeHead = res.writeHead;
|
||||
res.writeHead = function(status) {
|
||||
if (status in images) {
|
||||
res.setHeader('X-Status-Cat', module.exports.get_image(status))
|
||||
res.setHeader('X-Status-Cat', module.exports.get_image(status));
|
||||
}
|
||||
_writeHead.apply(res, arguments)
|
||||
}
|
||||
_writeHead.apply(res, arguments);
|
||||
};
|
||||
|
||||
next()
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
|
1007
lib/storage.js
1007
lib/storage.js
File diff suppressed because it is too large
Load Diff
@ -1,60 +1,62 @@
|
||||
var Stream = require('stream')
|
||||
var Util = require('util')
|
||||
'use strict';
|
||||
|
||||
module.exports.ReadTarballStream = ReadTarball
|
||||
module.exports.UploadTarballStream = UploadTarball
|
||||
let Stream = require('stream');
|
||||
let Util = require('util');
|
||||
|
||||
module.exports.ReadTarballStream = ReadTarball;
|
||||
module.exports.UploadTarballStream = UploadTarball;
|
||||
|
||||
//
|
||||
// This stream is used to read tarballs from repository
|
||||
//
|
||||
function ReadTarball(options) {
|
||||
var self = new Stream.PassThrough(options)
|
||||
Object.setPrototypeOf(self, ReadTarball.prototype)
|
||||
let self = new Stream.PassThrough(options);
|
||||
Object.setPrototypeOf(self, ReadTarball.prototype);
|
||||
|
||||
// called when data is not needed anymore
|
||||
add_abstract_method(self, 'abort')
|
||||
add_abstract_method(self, 'abort');
|
||||
|
||||
return self
|
||||
return self;
|
||||
}
|
||||
|
||||
Util.inherits(ReadTarball, Stream.PassThrough)
|
||||
Util.inherits(ReadTarball, Stream.PassThrough);
|
||||
|
||||
//
|
||||
// This stream is used to upload tarballs to a repository
|
||||
//
|
||||
function UploadTarball(options) {
|
||||
var self = new Stream.PassThrough(options)
|
||||
Object.setPrototypeOf(self, UploadTarball.prototype)
|
||||
let self = new Stream.PassThrough(options);
|
||||
Object.setPrototypeOf(self, UploadTarball.prototype);
|
||||
|
||||
// called when user closes connection before upload finishes
|
||||
add_abstract_method(self, 'abort')
|
||||
add_abstract_method(self, 'abort');
|
||||
|
||||
// called when upload finishes successfully
|
||||
add_abstract_method(self, 'done')
|
||||
add_abstract_method(self, 'done');
|
||||
|
||||
return self
|
||||
return self;
|
||||
}
|
||||
|
||||
Util.inherits(UploadTarball, Stream.PassThrough)
|
||||
Util.inherits(UploadTarball, Stream.PassThrough);
|
||||
|
||||
//
|
||||
// This function intercepts abstract calls and replays them allowing
|
||||
// us to attach those functions after we are ready to do so
|
||||
//
|
||||
function add_abstract_method(self, name) {
|
||||
self._called_methods = self._called_methods || {}
|
||||
self._called_methods = self._called_methods || {};
|
||||
self.__defineGetter__(name, function() {
|
||||
return function() {
|
||||
self._called_methods[name] = true
|
||||
}
|
||||
})
|
||||
self._called_methods[name] = true;
|
||||
};
|
||||
});
|
||||
self.__defineSetter__(name, function(fn) {
|
||||
delete self[name]
|
||||
self[name] = fn
|
||||
delete self[name];
|
||||
self[name] = fn;
|
||||
if (self._called_methods && self._called_methods[name]) {
|
||||
delete self._called_methods[name]
|
||||
self[name]()
|
||||
delete self._called_methods[name];
|
||||
self[name]();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,374 +1,385 @@
|
||||
var JSONStream = require('JSONStream')
|
||||
var Error = require('http-errors')
|
||||
var request = require('request')
|
||||
var Stream = require('readable-stream')
|
||||
var URL = require('url')
|
||||
var parse_interval = require('./config').parse_interval
|
||||
var Logger = require('./logger')
|
||||
var MyStreams = require('./streams')
|
||||
var Utils = require('./utils')
|
||||
var encode = function(thing) {
|
||||
'use strict';
|
||||
|
||||
const JSONStream = require('JSONStream');
|
||||
const Error = require('http-errors');
|
||||
const request = require('request');
|
||||
const Stream = require('readable-stream');
|
||||
const URL = require('url');
|
||||
const parse_interval = require('./config').parse_interval;
|
||||
const Logger = require('./logger');
|
||||
const MyStreams = require('./streams');
|
||||
const Utils = require('./utils');
|
||||
const encode = function(thing) {
|
||||
return encodeURIComponent(thing).replace(/^%40/, '@');
|
||||
};
|
||||
|
||||
module.exports = Storage
|
||||
|
||||
//
|
||||
// Implements Storage interface
|
||||
// (same for storage.js, local-storage.js, up-storage.js)
|
||||
//
|
||||
function Storage(config, mainconfig) {
|
||||
var self = Object.create(Storage.prototype)
|
||||
self.config = config
|
||||
self.failed_requests = 0
|
||||
self.userAgent = mainconfig.user_agent
|
||||
self.ca = config.ca
|
||||
self.logger = Logger.logger.child({sub: 'out'})
|
||||
self.server_id = mainconfig.server_id
|
||||
|
||||
self.url = URL.parse(self.config.url)
|
||||
|
||||
_setupProxy.call(self, self.url.hostname, config, mainconfig, self.url.protocol === 'https:')
|
||||
|
||||
self.config.url = self.config.url.replace(/\/$/, '')
|
||||
if (Number(self.config.timeout) >= 1000) {
|
||||
self.logger.warn([ 'Too big timeout value: ' + self.config.timeout,
|
||||
'We changed time format to nginx-like one',
|
||||
'(see http://wiki.nginx.org/ConfigNotation)',
|
||||
'so please update your config accordingly' ].join('\n'))
|
||||
}
|
||||
|
||||
// a bunch of different configurable timers
|
||||
self.maxage = parse_interval(config_get('maxage' , '2m' ))
|
||||
self.timeout = parse_interval(config_get('timeout' , '30s'))
|
||||
self.max_fails = Number(config_get('max_fails' , 2 ))
|
||||
self.fail_timeout = parse_interval(config_get('fail_timeout', '5m' ))
|
||||
return self
|
||||
|
||||
// just a helper (`config[key] || default` doesn't work because of zeroes)
|
||||
function config_get(key, def) {
|
||||
return config[key] != null ? config[key] : def
|
||||
}
|
||||
}
|
||||
|
||||
function _setupProxy(hostname, config, mainconfig, isHTTPS) {
|
||||
var no_proxy
|
||||
var proxy_key = isHTTPS ? 'https_proxy' : 'http_proxy'
|
||||
const _setupProxy = function(hostname, config, mainconfig, isHTTPS) {
|
||||
let no_proxy;
|
||||
let proxy_key = isHTTPS ? 'https_proxy' : 'http_proxy';
|
||||
|
||||
// get http_proxy and no_proxy configs
|
||||
if (proxy_key in config) {
|
||||
this.proxy = config[proxy_key]
|
||||
this.proxy = config[proxy_key];
|
||||
} else if (proxy_key in mainconfig) {
|
||||
this.proxy = mainconfig[proxy_key]
|
||||
this.proxy = mainconfig[proxy_key];
|
||||
}
|
||||
if ('no_proxy' in config) {
|
||||
no_proxy = config.no_proxy
|
||||
no_proxy = config.no_proxy;
|
||||
} else if ('no_proxy' in mainconfig) {
|
||||
no_proxy = mainconfig.no_proxy
|
||||
no_proxy = mainconfig.no_proxy;
|
||||
}
|
||||
|
||||
// use wget-like algorithm to determine if proxy shouldn't be used
|
||||
if (hostname[0] !== '.') hostname = '.' + hostname
|
||||
if (hostname[0] !== '.') {
|
||||
hostname = '.' + hostname;
|
||||
}
|
||||
if (typeof(no_proxy) === 'string' && no_proxy.length) {
|
||||
no_proxy = no_proxy.split(',')
|
||||
no_proxy = no_proxy.split(',');
|
||||
}
|
||||
if (Array.isArray(no_proxy)) {
|
||||
for (var i=0; i<no_proxy.length; i++) {
|
||||
var no_proxy_item = no_proxy[i]
|
||||
if (no_proxy_item[0] !== '.') no_proxy_item = '.' + no_proxy_item
|
||||
for (let i=0; i<no_proxy.length; i++) {
|
||||
let no_proxy_item = no_proxy[i];
|
||||
if (no_proxy_item[0] !== '.') no_proxy_item = '.' + no_proxy_item;
|
||||
if (hostname.lastIndexOf(no_proxy_item) === hostname.length - no_proxy_item.length) {
|
||||
if (this.proxy) {
|
||||
this.logger.debug({url: this.url.href, rule: no_proxy_item},
|
||||
'not using proxy for @{url}, excluded by @{rule} rule')
|
||||
this.proxy = false
|
||||
'not using proxy for @{url}, excluded by @{rule} rule');
|
||||
this.proxy = false;
|
||||
}
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it's non-string (i.e. "false"), don't use it
|
||||
if (typeof(this.proxy) !== 'string') {
|
||||
delete this.proxy
|
||||
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}' );
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Implements Storage interface
|
||||
// (same for storage.js, local-storage.js, up-storage.js)
|
||||
//
|
||||
class Storage {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} config
|
||||
* @param {*} mainconfig
|
||||
*/
|
||||
constructor(config, mainconfig) {
|
||||
this.config = config;
|
||||
this.failed_requests = 0;
|
||||
this.userAgent = mainconfig.user_agent;
|
||||
this.ca = config.ca;
|
||||
this.logger = Logger.logger.child({sub: 'out'});
|
||||
this.server_id = mainconfig.server_id;
|
||||
|
||||
this.url = URL.parse(this.config.url);
|
||||
|
||||
_setupProxy.call(this, this.url.hostname, config, mainconfig, this.url.protocol === 'https:');
|
||||
|
||||
this.config.url = this.config.url.replace(/\/$/, '');
|
||||
if (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://wiki.nginx.org/ConfigNotation)',
|
||||
'so please update your config accordingly'].join('\n'));
|
||||
}
|
||||
|
||||
// a bunch of different configurable timers
|
||||
this.maxage = parse_interval(config_get('maxage', '2m' ));
|
||||
this.timeout = parse_interval(config_get('timeout', '30s'));
|
||||
this.max_fails = Number(config_get('max_fails', 2 ));
|
||||
this.fail_timeout = parse_interval(config_get('fail_timeout', '5m' ));
|
||||
return this;
|
||||
|
||||
// just a helper (`config[key] || default` doesn't work because of zeroes)
|
||||
function config_get(key, def) {
|
||||
return config[key] != null ? config[key] : def;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Storage.prototype.request = function(options, cb) {
|
||||
if (!this.status_check()) {
|
||||
var req = new Stream.Readable()
|
||||
var req = new Stream.Readable();
|
||||
process.nextTick(function() {
|
||||
if (typeof(cb) === 'function') cb(Error('uplink is offline'))
|
||||
req.emit('error', Error('uplink is offline'))
|
||||
})
|
||||
req._read = function(){}
|
||||
if (typeof(cb) === 'function') cb(Error('uplink is offline'));
|
||||
req.emit('error', Error('uplink is offline'));
|
||||
});
|
||||
req._read = function() {};
|
||||
// preventing 'Uncaught, unspecified "error" event'
|
||||
req.on('error', function(){})
|
||||
return req
|
||||
req.on('error', function() {});
|
||||
return req;
|
||||
}
|
||||
|
||||
var self = this
|
||||
var headers = options.headers || {}
|
||||
headers['Accept'] = headers['Accept'] || 'application/json'
|
||||
headers['Accept-Encoding'] = headers['Accept-Encoding'] || 'gzip'
|
||||
let self = this;
|
||||
let headers = options.headers || {};
|
||||
headers['Accept'] = headers['Accept'] || 'application/json';
|
||||
headers['Accept-Encoding'] = headers['Accept-Encoding'] || 'gzip';
|
||||
// registry.npmjs.org will only return search result if user-agent include string 'npm'
|
||||
headers['User-Agent'] = headers['User-Agent'] || `npm (${this.userAgent})`
|
||||
this._add_proxy_headers(options.req, headers)
|
||||
headers['User-Agent'] = headers['User-Agent'] || `npm (${this.userAgent})`;
|
||||
this._add_proxy_headers(options.req, headers);
|
||||
|
||||
// add/override headers specified in the config
|
||||
for (var key in this.config.headers) {
|
||||
headers[key] = this.config.headers[key]
|
||||
for (let key in this.config.headers) {
|
||||
headers[key] = this.config.headers[key];
|
||||
}
|
||||
|
||||
var method = options.method || 'GET'
|
||||
var uri = options.uri_full || (this.config.url + options.uri)
|
||||
let method = options.method || 'GET';
|
||||
let uri = options.uri_full || (this.config.url + options.uri);
|
||||
|
||||
self.logger.info({
|
||||
method : method,
|
||||
headers : headers,
|
||||
uri : uri,
|
||||
}, "making request: '@{method} @{uri}'")
|
||||
method: method,
|
||||
headers: headers,
|
||||
uri: uri,
|
||||
}, 'making request: \'@{method} @{uri}\'');
|
||||
|
||||
if (Utils.is_object(options.json)) {
|
||||
var json = JSON.stringify(options.json)
|
||||
headers['Content-Type'] = headers['Content-Type'] || 'application/json'
|
||||
var json = JSON.stringify(options.json);
|
||||
headers['Content-Type'] = headers['Content-Type'] || 'application/json';
|
||||
}
|
||||
|
||||
var request_callback = cb ? (function (err, res, body) {
|
||||
var error
|
||||
var res_length = err ? 0 : body.length
|
||||
let request_callback = cb ? (function(err, res, body) {
|
||||
let error;
|
||||
let res_length = err ? 0 : body.length;
|
||||
|
||||
do_decode()
|
||||
do_log()
|
||||
cb(err, res, body)
|
||||
do_decode();
|
||||
do_log();
|
||||
cb(err, res, body);
|
||||
|
||||
function do_decode() {
|
||||
if (err) {
|
||||
error = err.message
|
||||
return
|
||||
error = err.message;
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.json && res.statusCode < 300) {
|
||||
try {
|
||||
body = JSON.parse(body.toString('utf8'))
|
||||
body = JSON.parse(body.toString('utf8'));
|
||||
} catch(_err) {
|
||||
body = {}
|
||||
err = _err
|
||||
error = err.message
|
||||
body = {};
|
||||
err = _err;
|
||||
error = err.message;
|
||||
}
|
||||
}
|
||||
|
||||
if (!err && Utils.is_object(body)) {
|
||||
if (typeof(body.error) === 'string') {
|
||||
error = body.error
|
||||
error = body.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function do_log() {
|
||||
var message = '@{!status}, req: \'@{request.method} @{request.url}\''
|
||||
let message = '@{!status}, req: \'@{request.method} @{request.url}\'';
|
||||
message += error
|
||||
? ', error: @{!error}'
|
||||
: ', bytes: @{bytes.in}/@{bytes.out}'
|
||||
: ', bytes: @{bytes.in}/@{bytes.out}';
|
||||
self.logger.warn({
|
||||
err : err,
|
||||
request : { method: method, url: uri },
|
||||
level : 35, // http
|
||||
status : res != null ? res.statusCode : 'ERR',
|
||||
error : error,
|
||||
bytes : {
|
||||
in : json ? json.length : 0,
|
||||
out : res_length || 0,
|
||||
}
|
||||
}, message)
|
||||
err: err,
|
||||
request: {method: method, url: uri},
|
||||
level: 35, // http
|
||||
status: res != null ? res.statusCode : 'ERR',
|
||||
error: error,
|
||||
bytes: {
|
||||
in: json ? json.length : 0,
|
||||
out: res_length || 0,
|
||||
},
|
||||
}, message);
|
||||
}
|
||||
}) : undefined
|
||||
}) : undefined;
|
||||
|
||||
var req = request({
|
||||
url : uri,
|
||||
method : method,
|
||||
headers : headers,
|
||||
body : json,
|
||||
ca : this.ca,
|
||||
proxy : this.proxy,
|
||||
encoding : null,
|
||||
gzip : true,
|
||||
timeout : this.timeout,
|
||||
}, request_callback)
|
||||
url: uri,
|
||||
method: method,
|
||||
headers: headers,
|
||||
body: json,
|
||||
ca: this.ca,
|
||||
proxy: this.proxy,
|
||||
encoding: null,
|
||||
gzip: true,
|
||||
timeout: this.timeout,
|
||||
}, request_callback);
|
||||
|
||||
var status_called = false
|
||||
let status_called = false;
|
||||
req.on('response', function(res) {
|
||||
if (!req._verdaccio_aborted && !status_called) {
|
||||
status_called = true
|
||||
self.status_check(true)
|
||||
status_called = true;
|
||||
self.status_check(true);
|
||||
}
|
||||
|
||||
if (!request_callback) {
|
||||
;(function do_log() {
|
||||
var message = '@{!status}, req: \'@{request.method} @{request.url}\' (streaming)'
|
||||
(function do_log() {
|
||||
let message = '@{!status}, req: \'@{request.method} @{request.url}\' (streaming)';
|
||||
self.logger.warn({
|
||||
request : { method: method, url: uri },
|
||||
level : 35, // http
|
||||
status : res != null ? res.statusCode : 'ERR',
|
||||
}, message)
|
||||
})()
|
||||
request: {method: method, url: uri},
|
||||
level: 35, // http
|
||||
status: res != null ? res.statusCode : 'ERR',
|
||||
}, message);
|
||||
})();
|
||||
}
|
||||
})
|
||||
});
|
||||
req.on('error', function(_err) {
|
||||
if (!req._verdaccio_aborted && !status_called) {
|
||||
status_called = true
|
||||
self.status_check(false)
|
||||
status_called = true;
|
||||
self.status_check(false);
|
||||
}
|
||||
})
|
||||
return req
|
||||
}
|
||||
});
|
||||
return req;
|
||||
};
|
||||
|
||||
Storage.prototype.status_check = function(alive) {
|
||||
if (arguments.length === 0) {
|
||||
if (this.failed_requests >= this.max_fails
|
||||
&& Math.abs(Date.now() - this.last_request_time) < this.fail_timeout) {
|
||||
return false
|
||||
return false;
|
||||
} else {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
} 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
|
||||
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()
|
||||
this.last_request_time = Date.now();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Storage.prototype.can_fetch_url = function(url) {
|
||||
url = URL.parse(url)
|
||||
url = URL.parse(url);
|
||||
|
||||
return url.protocol === this.url.protocol
|
||||
&& url.host === this.url.host
|
||||
&& url.path.indexOf(this.url.path) === 0
|
||||
}
|
||||
&& url.path.indexOf(this.url.path) === 0;
|
||||
};
|
||||
|
||||
Storage.prototype.get_package = function(name, options, callback) {
|
||||
if (typeof(options) === 'function') callback = options, options = {}
|
||||
if (typeof(options) === 'function') callback = options, options = {};
|
||||
|
||||
var headers = {}
|
||||
let headers = {};
|
||||
if (options.etag) {
|
||||
headers['If-None-Match'] = options.etag
|
||||
headers['Accept'] = 'application/octet-stream'
|
||||
headers['If-None-Match'] = options.etag;
|
||||
headers['Accept'] = 'application/octet-stream';
|
||||
}
|
||||
|
||||
this.request({
|
||||
uri : '/' + encode(name),
|
||||
json : true,
|
||||
headers : headers,
|
||||
req : options.req,
|
||||
uri: '/' + encode(name),
|
||||
json: true,
|
||||
headers: headers,
|
||||
req: options.req,
|
||||
}, function(err, res, body) {
|
||||
if (err) return callback(err)
|
||||
if (err) return callback(err);
|
||||
if (res.statusCode === 404) {
|
||||
return callback( Error[404]("package doesn't exist on uplink") )
|
||||
return callback( Error[404]('package doesn\'t exist on uplink') );
|
||||
}
|
||||
if (!(res.statusCode >= 200 && res.statusCode < 300)) {
|
||||
var error = Error('bad status code: ' + res.statusCode)
|
||||
error.remoteStatus = res.statusCode
|
||||
return callback(error)
|
||||
let error = Error('bad status code: ' + res.statusCode);
|
||||
error.remoteStatus = res.statusCode;
|
||||
return callback(error);
|
||||
}
|
||||
callback(null, body, res.headers.etag)
|
||||
})
|
||||
}
|
||||
callback(null, body, res.headers.etag);
|
||||
});
|
||||
};
|
||||
|
||||
Storage.prototype.get_tarball = function(name, options, filename) {
|
||||
if (!options) options = {}
|
||||
return this.get_url(this.config.url + '/' + name + '/-/' + filename)
|
||||
}
|
||||
if (!options) options = {};
|
||||
return this.get_url(this.config.url + '/' + name + '/-/' + filename);
|
||||
};
|
||||
|
||||
Storage.prototype.get_url = function(url) {
|
||||
var stream = MyStreams.ReadTarballStream()
|
||||
stream.abort = function() {}
|
||||
var current_length = 0, expected_length
|
||||
let stream = MyStreams.ReadTarballStream();
|
||||
stream.abort = function() {};
|
||||
let current_length = 0, expected_length;
|
||||
|
||||
var rstream = this.request({
|
||||
let rstream = this.request({
|
||||
uri_full: url,
|
||||
encoding: null,
|
||||
headers: { Accept: 'application/octet-stream' },
|
||||
})
|
||||
headers: {Accept: 'application/octet-stream'},
|
||||
});
|
||||
|
||||
rstream.on('response', function(res) {
|
||||
if (res.statusCode === 404) {
|
||||
return stream.emit('error', Error[404]("file doesn't exist on uplink"))
|
||||
return stream.emit('error', Error[404]('file doesn\'t exist on uplink'));
|
||||
}
|
||||
if (!(res.statusCode >= 200 && res.statusCode < 300)) {
|
||||
return stream.emit('error', Error('bad uplink status code: ' + res.statusCode))
|
||||
return stream.emit('error', Error('bad uplink status code: ' + res.statusCode));
|
||||
}
|
||||
if (res.headers['content-length']) {
|
||||
expected_length = res.headers['content-length']
|
||||
stream.emit('content-length', res.headers['content-length'])
|
||||
expected_length = res.headers['content-length'];
|
||||
stream.emit('content-length', res.headers['content-length']);
|
||||
}
|
||||
|
||||
rstream.pipe(stream)
|
||||
})
|
||||
rstream.pipe(stream);
|
||||
});
|
||||
|
||||
rstream.on('error', function(err) {
|
||||
stream.emit('error', err)
|
||||
})
|
||||
stream.emit('error', err);
|
||||
});
|
||||
rstream.on('data', function(d) {
|
||||
current_length += d.length
|
||||
})
|
||||
current_length += d.length;
|
||||
});
|
||||
rstream.on('end', function(d) {
|
||||
if (d) current_length += d.length
|
||||
if (d) current_length += d.length;
|
||||
if (expected_length && current_length != expected_length)
|
||||
stream.emit('error', Error('content length mismatch'))
|
||||
})
|
||||
return stream
|
||||
}
|
||||
stream.emit('error', Error('content length mismatch'));
|
||||
});
|
||||
return stream;
|
||||
};
|
||||
|
||||
Storage.prototype.search = function(startkey, options) {
|
||||
var self = this
|
||||
let self = this;
|
||||
|
||||
var stream = new Stream.PassThrough({ objectMode: true })
|
||||
let stream = new Stream.PassThrough({objectMode: true});
|
||||
|
||||
var req = self.request({
|
||||
let req = self.request({
|
||||
uri: options.req.url,
|
||||
req: options.req,
|
||||
headers: {
|
||||
referer: options.req.headers.referer
|
||||
}
|
||||
})
|
||||
referer: options.req.headers.referer,
|
||||
},
|
||||
});
|
||||
|
||||
req.on('response', function (res) {
|
||||
req.on('response', (res) => {
|
||||
if (!String(res.statusCode).match(/^2\d\d$/)) {
|
||||
return stream.emit('error', Error('bad status code ' + res.statusCode + ' from uplink'))
|
||||
return stream.emit('error', Error('bad status code ' + res.statusCode + ' from uplink'));
|
||||
}
|
||||
|
||||
res.pipe(JSONStream.parse('*')).on('data', function (pkg) {
|
||||
res.pipe(JSONStream.parse('*')).on('data', (pkg) => {
|
||||
if (Utils.is_object(pkg)) {
|
||||
stream.emit('data', pkg)
|
||||
stream.emit('data', pkg);
|
||||
}
|
||||
})
|
||||
res.on('end', function () {
|
||||
stream.emit('end')
|
||||
})
|
||||
})
|
||||
});
|
||||
res.on('end', () => {
|
||||
stream.emit('end');
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function (err) {
|
||||
stream.emit('error', err)
|
||||
})
|
||||
req.on('error', (err) => {
|
||||
stream.emit('error', err);
|
||||
});
|
||||
|
||||
stream.abort = function () {
|
||||
req.abort()
|
||||
stream.emit('end')
|
||||
}
|
||||
stream.abort = () => {
|
||||
req.abort();
|
||||
stream.emit('end');
|
||||
};
|
||||
|
||||
return stream
|
||||
}
|
||||
|
||||
return stream;
|
||||
};
|
||||
|
||||
Storage.prototype._add_proxy_headers = function(req, headers) {
|
||||
if (req) {
|
||||
@ -383,7 +394,7 @@ Storage.prototype._add_proxy_headers = function(req, headers) {
|
||||
req && req.headers['x-forwarded-for']
|
||||
? req.headers['x-forwarded-for'] + ', '
|
||||
: ''
|
||||
) + req.connection.remoteAddress
|
||||
) + req.connection.remoteAddress;
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,7 +402,10 @@ Storage.prototype._add_proxy_headers = function(req, headers) {
|
||||
headers['Via'] =
|
||||
req && req.headers['via']
|
||||
? req.headers['via'] + ', '
|
||||
: ''
|
||||
: '';
|
||||
|
||||
headers['Via'] += '1.1 ' + this.server_id + ' (Verdaccio)'
|
||||
}
|
||||
headers['Via'] += '1.1 ' + this.server_id + ' (Verdaccio)';
|
||||
};
|
||||
|
||||
|
||||
module.exports = Storage;
|
||||
|
139
lib/utils.js
139
lib/utils.js
@ -1,25 +1,27 @@
|
||||
var assert = require('assert')
|
||||
var Semver = require('semver')
|
||||
var URL = require('url')
|
||||
var Logger = require('./logger')
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
let Semver = require('semver');
|
||||
let URL = require('url');
|
||||
let Logger = require('./logger');
|
||||
|
||||
module.exports.validate_package = function(name) {
|
||||
name = name.split('/', 2)
|
||||
name = name.split('/', 2);
|
||||
if (name.length === 1) {
|
||||
// normal package
|
||||
return module.exports.validate_name(name[0])
|
||||
return module.exports.validate_name(name[0]);
|
||||
} else {
|
||||
// scoped package
|
||||
return name[0][0] === '@'
|
||||
&& module.exports.validate_name(name[0].slice(1))
|
||||
&& module.exports.validate_name(name[1])
|
||||
&& module.exports.validate_name(name[1]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// from normalize-package-data/lib/fixer.js
|
||||
module.exports.validate_name = function(name) {
|
||||
if (typeof(name) !== 'string') return false
|
||||
name = name.toLowerCase()
|
||||
if (typeof(name) !== 'string') return false;
|
||||
name = name.toLowerCase();
|
||||
|
||||
// all URL-safe characters and "@" for issue #75
|
||||
if (!name.match(/^[-a-zA-Z0-9_.!~*'()@]+$/)
|
||||
@ -30,88 +32,88 @@ module.exports.validate_name = function(name) {
|
||||
|| name === 'package.json'
|
||||
|| name === 'favicon.ico'
|
||||
) {
|
||||
return false
|
||||
return false;
|
||||
} else {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.is_object = function(obj) {
|
||||
return typeof(obj) === 'object' && obj !== null && !Array.isArray(obj)
|
||||
}
|
||||
return typeof(obj) === 'object' && obj !== null && !Array.isArray(obj);
|
||||
};
|
||||
|
||||
module.exports.validate_metadata = function(object, name) {
|
||||
assert(module.exports.is_object(object), 'not a json object')
|
||||
assert.equal(object.name, name)
|
||||
assert(module.exports.is_object(object), 'not a json object');
|
||||
assert.equal(object.name, name);
|
||||
|
||||
if (!module.exports.is_object(object['dist-tags'])) {
|
||||
object['dist-tags'] = {}
|
||||
object['dist-tags'] = {};
|
||||
}
|
||||
|
||||
if (!module.exports.is_object(object['versions'])) {
|
||||
object['versions'] = {}
|
||||
object['versions'] = {};
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
return object;
|
||||
};
|
||||
|
||||
module.exports.filter_tarball_urls = function(pkg, req, config) {
|
||||
function filter(_url) {
|
||||
if (!req.headers.host) return _url
|
||||
if (!req.headers.host) return _url;
|
||||
|
||||
var filename = URL.parse(_url).pathname.replace(/^.*\//, '')
|
||||
let filename = URL.parse(_url).pathname.replace(/^.*\//, '');
|
||||
|
||||
if (config.url_prefix != null) {
|
||||
var result = config.url_prefix.replace(/\/$/, '')
|
||||
var result = config.url_prefix.replace(/\/$/, '');
|
||||
} else {
|
||||
var result = req.protocol + '://' + req.headers.host
|
||||
var result = req.protocol + '://' + req.headers.host;
|
||||
}
|
||||
|
||||
return result + '/' + pkg.name.replace(/\//g, '%2f') + '/-/' + filename
|
||||
return `${result}/${pkg.name.replace(/\//g, '%2f')}/-/${filename}`;
|
||||
}
|
||||
|
||||
for (var ver in pkg.versions) {
|
||||
var dist = pkg.versions[ver].dist
|
||||
for (let ver in pkg.versions) {
|
||||
let dist = pkg.versions[ver].dist;
|
||||
if (dist != null && dist.tarball != null) {
|
||||
//dist.__verdaccio_orig_tarball = dist.tarball
|
||||
dist.tarball = filter(dist.tarball)
|
||||
// dist.__verdaccio_orig_tarball = dist.tarball
|
||||
dist.tarball = filter(dist.tarball);
|
||||
}
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
return pkg;
|
||||
};
|
||||
|
||||
module.exports.tag_version = function(data, version, tag) {
|
||||
if (tag) {
|
||||
if (data['dist-tags'][tag] !== version) {
|
||||
if (Semver.parse(version, true)) {
|
||||
// valid version - store
|
||||
data['dist-tags'][tag] = version
|
||||
return true
|
||||
data['dist-tags'][tag] = version;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Logger.logger.warn({ver: version, tag: tag}, 'ignoring bad version @{ver} in @{tag}')
|
||||
Logger.logger.warn({ver: version, tag: tag}, 'ignoring bad version @{ver} in @{tag}');
|
||||
if (tag && data['dist-tags'][tag]) {
|
||||
delete data['dist-tags'][tag]
|
||||
delete data['dist-tags'][tag];
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// gets version from a package object taking into account semver weirdness
|
||||
module.exports.get_version = function(object, version) {
|
||||
if (object.versions[version] != null) return object.versions[version]
|
||||
if (object.versions[version] != null) return object.versions[version];
|
||||
|
||||
try {
|
||||
version = Semver.parse(version, true)
|
||||
for (var k in object.versions) {
|
||||
version = Semver.parse(version, true);
|
||||
for (let k in object.versions) {
|
||||
if (version.compare(Semver.parse(k, true)) === 0) {
|
||||
return object.versions[k]
|
||||
return object.versions[k];
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
return undefined
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.parse_address = function parse_address(addr) {
|
||||
//
|
||||
@ -129,68 +131,67 @@ module.exports.parse_address = function parse_address(addr) {
|
||||
// TODO: refactor it to something more reasonable?
|
||||
//
|
||||
// protocol : // ( host )|( ipv6 ): port /
|
||||
var m = /^((https?):(\/\/)?)?((([^\/:]*)|\[([^\[\]]+)\]):)?(\d+)\/?$/.exec(addr)
|
||||
var m = /^((https?):(\/\/)?)?((([^\/:]*)|\[([^\[\]]+)\]):)?(\d+)\/?$/.exec(addr);
|
||||
|
||||
if (m) return {
|
||||
proto: m[2] || 'http',
|
||||
host: m[6] || m[7] || 'localhost',
|
||||
port: m[8] || '4873',
|
||||
}
|
||||
host: m[6] || m[7] || 'localhost',
|
||||
port: m[8] || '4873',
|
||||
};
|
||||
|
||||
var m = /^((https?):(\/\/)?)?unix:(.*)$/.exec(addr)
|
||||
var m = /^((https?):(\/\/)?)?unix:(.*)$/.exec(addr);
|
||||
|
||||
if (m) return {
|
||||
proto: m[2] || 'http',
|
||||
path: m[4],
|
||||
}
|
||||
path: m[4],
|
||||
};
|
||||
|
||||
return null
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// function filters out bad semver versions and sorts the array
|
||||
module.exports.semver_sort = function semver_sort(array) {
|
||||
return array
|
||||
.filter(function(x) {
|
||||
if (!Semver.parse(x, true)) {
|
||||
Logger.logger.warn( {ver: x}, 'ignoring bad version @{ver}' )
|
||||
return false
|
||||
Logger.logger.warn( {ver: x}, 'ignoring bad version @{ver}' );
|
||||
return false;
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
})
|
||||
.sort(Semver.compareLoose)
|
||||
.map(String)
|
||||
}
|
||||
.map(String);
|
||||
};
|
||||
|
||||
// flatten arrays of tags
|
||||
module.exports.normalize_dist_tags = function (data) {
|
||||
var sorted
|
||||
module.exports.normalize_dist_tags = function(data) {
|
||||
let sorted;
|
||||
|
||||
if (!data['dist-tags'].latest) {
|
||||
// overwrite latest with highest known version based on semver sort
|
||||
sorted = module.exports.semver_sort(Object.keys(data.versions))
|
||||
sorted = module.exports.semver_sort(Object.keys(data.versions));
|
||||
if (sorted && sorted.length) {
|
||||
data['dist-tags'].latest = sorted.pop()
|
||||
data['dist-tags'].latest = sorted.pop();
|
||||
}
|
||||
}
|
||||
|
||||
for (var tag in data['dist-tags']) {
|
||||
for (let tag in data['dist-tags']) {
|
||||
if (Array.isArray(data['dist-tags'][tag])) {
|
||||
if (data['dist-tags'][tag].length) {
|
||||
// sort array
|
||||
sorted = module.exports.semver_sort(data['dist-tags'][tag])
|
||||
sorted = module.exports.semver_sort(data['dist-tags'][tag]);
|
||||
if (sorted.length) {
|
||||
// use highest version based on semver sort
|
||||
data['dist-tags'][tag] = sorted.pop()
|
||||
data['dist-tags'][tag] = sorted.pop();
|
||||
}
|
||||
|
||||
} else {
|
||||
delete data['dist-tags'][tag]
|
||||
delete data['dist-tags'][tag];
|
||||
}
|
||||
} else if (typeof data['dist-tags'][tag] === 'string') {
|
||||
if (!Semver.parse(data['dist-tags'][tag], true)) {
|
||||
// if the version is invalid, delete the dist-tag entry
|
||||
delete data['dist-tags'][tag]
|
||||
delete data['dist-tags'][tag];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
16
package.json
16
package.json
@ -20,10 +20,10 @@
|
||||
"async": "^2.0.1",
|
||||
"body-parser": "^1.15.0",
|
||||
"bunyan": "^1.8.0",
|
||||
"chalk": "^1.1.3",
|
||||
"commander": "^2.9.0",
|
||||
"compression": "^1.6.1",
|
||||
"cookies": "^0.6.1",
|
||||
"es6-shim": "^0.35.0",
|
||||
"express": "^4.13.4",
|
||||
"handlebars": "^4.0.5",
|
||||
"highlight.js": "^9.3.0",
|
||||
@ -32,7 +32,7 @@
|
||||
"js-yaml": "^3.6.0",
|
||||
"lockfile": "^1.0.1",
|
||||
"lunr": "^0.7.0",
|
||||
"minimatch": "^3.0.0",
|
||||
"minimatch": "^3.0.2",
|
||||
"mkdirp": "^0.5.1",
|
||||
"pkginfo": "^0.4.0",
|
||||
"readable-stream": "^2.1.2",
|
||||
@ -42,16 +42,16 @@
|
||||
"unix-crypt-td-js": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bluebird": "^3.3.5",
|
||||
"browserify": "^13.0.0",
|
||||
"browserify-handlebars": "^1.0.0",
|
||||
"eslint": "^2.9.0",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-config-google": "^0.7.1",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-browserify": "^5.0.0",
|
||||
"grunt-cli": "^1.2.0",
|
||||
"grunt-contrib-less": "^1.3.0",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"mocha": "^2.4.5",
|
||||
"mocha": "^3.2.0",
|
||||
"nyc": "^10.1.2",
|
||||
"onclick": "^0.1.0",
|
||||
"rimraf": "^2.5.2",
|
||||
@ -68,16 +68,16 @@
|
||||
"server"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "eslint . && mocha ./test/functional ./test/unit",
|
||||
"test": "npm run lint && mocha ./test/functional ./test/unit",
|
||||
"test:coverage": "nyc --reporter=html --reporter=text mocha -R spec ./test/functional ./test/unit",
|
||||
"test-travis": "eslint . && npm run test:coverage",
|
||||
"test-travis": "npm run lint && npm run test:coverage",
|
||||
"test-only": "mocha ./test/functional ./test/unit",
|
||||
"lint": "eslint .",
|
||||
"build-docker": "docker build -t verdaccio .",
|
||||
"build-docker:rpi": "docker build -f Dockerfile.rpi -t verdaccio:rpi ."
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=4.6.1"
|
||||
},
|
||||
"preferGlobal": true,
|
||||
"publishConfig": {
|
||||
|
@ -1,5 +1,12 @@
|
||||
# vim: syntax=yaml
|
||||
|
||||
extends: ["eslint:recommended"]
|
||||
|
||||
env:
|
||||
node: true
|
||||
mocha: true
|
||||
es6: true
|
||||
|
||||
valid-jsdoc: 0
|
||||
no-redeclare: 1
|
||||
no-console: 1
|
@ -1,80 +1,73 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function () {
|
||||
describe('access control', function () {
|
||||
var server = process.server
|
||||
var oldauth
|
||||
module.exports = function() {
|
||||
describe('access control', function() {
|
||||
let server = process.server;
|
||||
let oldauth;
|
||||
|
||||
before(function () {
|
||||
oldauth = server.authstr
|
||||
})
|
||||
before(function() {
|
||||
oldauth = server.authstr;
|
||||
});
|
||||
|
||||
after(function () {
|
||||
server.authstr = oldauth
|
||||
})
|
||||
after(function() {
|
||||
server.authstr = oldauth;
|
||||
});
|
||||
|
||||
function check_access(auth, pkg, ok) {
|
||||
it((ok ? 'allows' : 'forbids') +' access ' + auth + ' to ' + pkg, function () {
|
||||
server.authstr = auth
|
||||
? 'Basic '+(new Buffer(auth).toString('base64'))
|
||||
: undefined
|
||||
|
||||
var req = server.get_package(pkg)
|
||||
|
||||
it((ok ? 'allows' : 'forbids') +' access ' + auth + ' to ' + pkg, function() {
|
||||
server.authstr = auth? `Basic ${(new Buffer(auth).toString('base64'))}`: undefined;
|
||||
let req = server.get_package(pkg);
|
||||
if (ok) {
|
||||
return req.status(404)
|
||||
.body_error(/no such package available/)
|
||||
return req.status(404).body_error(/no such package available/);
|
||||
} else {
|
||||
return req.status(403)
|
||||
.body_error(/not allowed to access package/)
|
||||
return req.status(403).body_error(/not allowed to access package/);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function check_publish(auth, pkg, ok) {
|
||||
it((ok ? 'allows' : 'forbids') + ' publish ' + auth + ' to ' + pkg, function () {
|
||||
server.authstr = auth
|
||||
? 'Basic '+(new Buffer(auth).toString('base64'))
|
||||
: undefined
|
||||
|
||||
var req = server.put_package(pkg, require('./lib/package')(pkg))
|
||||
|
||||
it(`${(ok ? 'allows' : 'forbids')} publish ${auth} to ${pkg}`, function() {
|
||||
server.authstr = auth? `Basic ${(new Buffer(auth).toString('base64'))}`: undefined;
|
||||
let req = server.put_package(pkg, require('./lib/package')(pkg));
|
||||
if (ok) {
|
||||
return req.status(404)
|
||||
.body_error(/this package cannot be added/)
|
||||
return req.status(404).body_error(/this package cannot be added/);
|
||||
} else {
|
||||
return req.status(403)
|
||||
.body_error(/not allowed to publish package/)
|
||||
return req.status(403).body_error(/not allowed to publish package/);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
const badPass = 'test:badpass';
|
||||
const testPass = 'test:test';
|
||||
const testAccessOnly = 'test-access-only';
|
||||
const testPublishOnly = 'test-publish-only';
|
||||
const testOnlyTest = 'test-only-test';
|
||||
const testOnlyAuth = 'test-only-auth';
|
||||
check_access(testPass, testAccessOnly, true);
|
||||
check_access(undefined, testAccessOnly, true);
|
||||
check_access(badPass, testAccessOnly, true);
|
||||
check_publish(testPass, testAccessOnly, false);
|
||||
check_publish(undefined, testAccessOnly, false);
|
||||
check_publish(badPass, testAccessOnly, false);
|
||||
|
||||
check_access('test:test', 'test-access-only', true)
|
||||
check_access(undefined, 'test-access-only', true)
|
||||
check_access('test:badpass', 'test-access-only', true)
|
||||
check_publish('test:test', 'test-access-only', false)
|
||||
check_publish(undefined, 'test-access-only', false)
|
||||
check_publish('test:badpass', 'test-access-only', false)
|
||||
check_access(testPass, testPublishOnly, false);
|
||||
check_access(undefined, testPublishOnly, false);
|
||||
check_access(badPass, testPublishOnly, false);
|
||||
check_publish(testPass, testPublishOnly, true);
|
||||
check_publish(undefined, testPublishOnly, true);
|
||||
check_publish(badPass, testPublishOnly, true);
|
||||
|
||||
check_access('test:test', 'test-publish-only', false)
|
||||
check_access(undefined, 'test-publish-only', false)
|
||||
check_access('test:badpass', 'test-publish-only', false)
|
||||
check_publish('test:test', 'test-publish-only', true)
|
||||
check_publish(undefined, 'test-publish-only', true)
|
||||
check_publish('test:badpass', 'test-publish-only', true)
|
||||
|
||||
check_access('test:test', 'test-only-test', true)
|
||||
check_access(undefined, 'test-only-test', false)
|
||||
check_access('test:badpass', 'test-only-test', false)
|
||||
check_publish('test:test', 'test-only-test', true)
|
||||
check_publish(undefined, 'test-only-test', false)
|
||||
check_publish('test:badpass', 'test-only-test', false)
|
||||
|
||||
check_access('test:test', 'test-only-auth', true)
|
||||
check_access(undefined, 'test-only-auth', false)
|
||||
check_access('test:badpass', 'test-only-auth', false)
|
||||
check_publish('test:test', 'test-only-auth', true)
|
||||
check_publish(undefined, 'test-only-auth', false)
|
||||
check_publish('test:badpass', 'test-only-auth', false)
|
||||
})
|
||||
}
|
||||
check_access(testPass, testOnlyTest, true);
|
||||
check_access(undefined, testOnlyTest, false);
|
||||
check_access(badPass, testOnlyTest, false);
|
||||
check_publish(testPass, testOnlyTest, true);
|
||||
check_publish(undefined, testOnlyTest, false);
|
||||
check_publish(badPass, testOnlyTest, false);
|
||||
|
||||
check_access(testPass, testOnlyAuth, true);
|
||||
check_access(undefined, testOnlyAuth, false);
|
||||
check_access(badPass, testOnlyAuth, false);
|
||||
check_publish(testPass, testOnlyAuth, true);
|
||||
check_publish(undefined, testOnlyAuth, false);
|
||||
check_publish(badPass, testOnlyAuth, false);
|
||||
});
|
||||
};
|
||||
|
@ -1,46 +1,47 @@
|
||||
'use strict';
|
||||
|
||||
function readfile(x) {
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
return require('fs').readFileSync(__dirname + '/' + x);
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
var server = process.server
|
||||
module.exports = function() {
|
||||
let server = process.server;
|
||||
|
||||
it('add tag - 404', function () {
|
||||
return server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1')
|
||||
.status(404)
|
||||
.body_error(/no such package/)
|
||||
})
|
||||
it('add tag - 404', function() {
|
||||
return server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1').status(404).body_error(/no such package/);
|
||||
});
|
||||
|
||||
describe('addtag', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server.put_package('testpkg-tag', eval(
|
||||
'(' + readfile('fixtures/publish.json5')
|
||||
.toString('utf8')
|
||||
.replace(/__NAME__/g, 'testpkg-tag')
|
||||
.replace(/__VERSION__/g, '0.0.1')
|
||||
+ ')'
|
||||
)).status(201)
|
||||
})
|
||||
'(' + readfile('fixtures/publish.json5')
|
||||
.toString('utf8')
|
||||
.replace(/__NAME__/g, 'testpkg-tag')
|
||||
.replace(/__VERSION__/g, '0.0.1')
|
||||
+ ')'
|
||||
)).status(201);
|
||||
});
|
||||
|
||||
it('add testpkg-tag', function(){})
|
||||
it('add testpkg-tag', function() {
|
||||
// TODO: ?
|
||||
});
|
||||
|
||||
it('add tag - bad ver', function () {
|
||||
it('add tag - bad ver', function() {
|
||||
return server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1-x')
|
||||
.status(404)
|
||||
.body_error(/version doesn't exist/)
|
||||
})
|
||||
.status(404)
|
||||
.body_error(/version doesn't exist/);
|
||||
});
|
||||
|
||||
it('add tag - bad tag', function () {
|
||||
it('add tag - bad tag', function() {
|
||||
return server.add_tag('testpkg-tag', 'tag/tag/tag', '0.0.1-x')
|
||||
.status(403)
|
||||
.body_error(/invalid tag/)
|
||||
})
|
||||
.status(403)
|
||||
.body_error(/invalid tag/);
|
||||
});
|
||||
|
||||
it('add tag - good', function () {
|
||||
it('add tag - good', function() {
|
||||
return server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1')
|
||||
.status(201)
|
||||
.body_ok(/tagged/)
|
||||
})
|
||||
})
|
||||
}
|
||||
.status(201)
|
||||
.body_ok(/tagged/);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -1,47 +1,49 @@
|
||||
var Server = require('./lib/server')
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
'use strict';
|
||||
|
||||
const Server = require('./lib/server');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = function() {
|
||||
var server = new Server('http://localhost:55551/')
|
||||
const server = new Server('http://localhost:55551/');
|
||||
|
||||
describe('adduser', function() {
|
||||
var user = String(Math.random())
|
||||
var pass = String(Math.random())
|
||||
before(function () {
|
||||
const user = String(Math.random());
|
||||
const pass = String(Math.random());
|
||||
before(function() {
|
||||
return server.auth(user, pass)
|
||||
.status(201)
|
||||
.body_ok(/user .* created/)
|
||||
})
|
||||
.body_ok(/user .* created/);
|
||||
});
|
||||
|
||||
it('creating new user', function(){})
|
||||
it('creating new user', function() {});
|
||||
|
||||
it('should log in', function () {
|
||||
it('should log in', function() {
|
||||
return server.auth(user, pass)
|
||||
.status(201)
|
||||
.body_ok(/you are authenticated as/)
|
||||
})
|
||||
.body_ok(/you are authenticated as/);
|
||||
});
|
||||
|
||||
it('should not register more users', function () {
|
||||
it('should not register more users', function() {
|
||||
return server.auth(String(Math.random()), String(Math.random()))
|
||||
.status(409)
|
||||
.body_error(/maximum amount of users reached/)
|
||||
})
|
||||
})
|
||||
.body_error(/maximum amount of users reached/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('adduser created with htpasswd', function() {
|
||||
var user = 'preexisting'
|
||||
var pass = 'preexisting'
|
||||
before(function () {
|
||||
let user = 'preexisting';
|
||||
let pass = 'preexisting';
|
||||
before(function() {
|
||||
return fs.appendFileSync(
|
||||
path.join(__dirname, 'test-storage', '.htpasswd'),
|
||||
'preexisting:$apr1$4YSboUa9$yVKjE7.PxIOuK3M4D7VjX.'
|
||||
)
|
||||
})
|
||||
it('should log in', function () {
|
||||
);
|
||||
});
|
||||
it('should log in', function() {
|
||||
return server.auth(user, pass)
|
||||
.status(201)
|
||||
.body_ok(/you are authenticated as/)
|
||||
})
|
||||
})
|
||||
}
|
||||
.body_ok(/you are authenticated as/);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -1,119 +1,121 @@
|
||||
require('./lib/startup')
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert')
|
||||
var crypto = require('crypto')
|
||||
require('./lib/startup');
|
||||
|
||||
const assert = require('assert');
|
||||
const crypto = require('crypto');
|
||||
|
||||
function readfile(x) {
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
return require('fs').readFileSync(__dirname + '/' + x);
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
module.exports = function() {
|
||||
let server = process.server;
|
||||
let server2 = process.server2;
|
||||
|
||||
it('trying to fetch non-existent package', function () {
|
||||
return server.get_package('testpkg').status(404).body_error(/no such package/)
|
||||
})
|
||||
it('trying to fetch non-existent package', function() {
|
||||
return server.get_package('testpkg').status(404).body_error(/no such package/);
|
||||
});
|
||||
|
||||
describe('testpkg', function () {
|
||||
before(function () {
|
||||
return server.add_package('testpkg')
|
||||
})
|
||||
describe('testpkg', function() {
|
||||
before(function() {
|
||||
return server.add_package('testpkg');
|
||||
});
|
||||
|
||||
it('creating new package', function (){/* test for before() */})
|
||||
it('creating new package', function() {/* test for before() */});
|
||||
|
||||
it('downloading non-existent tarball', function () {
|
||||
return server.get_tarball('testpkg', 'blahblah').status(404).body_error(/no such file/)
|
||||
})
|
||||
it('downloading non-existent tarball', function() {
|
||||
return server.get_tarball('testpkg', 'blahblah').status(404).body_error(/no such file/);
|
||||
});
|
||||
|
||||
it('uploading incomplete tarball', function () {
|
||||
return server.put_tarball_incomplete('testpkg', 'blahblah1', readfile('fixtures/binary'), 3000)
|
||||
})
|
||||
it('uploading incomplete tarball', function() {
|
||||
return server.put_tarball_incomplete('testpkg', 'blahblah1', readfile('fixtures/binary'), 3000);
|
||||
});
|
||||
|
||||
describe('tarball', function () {
|
||||
before(function () {
|
||||
describe('tarball', function() {
|
||||
before(function() {
|
||||
return server.put_tarball('testpkg', 'blahblah', readfile('fixtures/binary'))
|
||||
.status(201)
|
||||
.body_ok(/.*/)
|
||||
})
|
||||
.body_ok(/.*/);
|
||||
});
|
||||
|
||||
it('uploading new tarball', function (){/* test for before() */})
|
||||
it('uploading new tarball', function() {/* test for before() */});
|
||||
|
||||
it('downloading newly created tarball', function () {
|
||||
it('downloading newly created tarball', function() {
|
||||
return server.get_tarball('testpkg', 'blahblah')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.deepEqual(body, readfile('fixtures/binary'))
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.deepEqual(body, readfile('fixtures/binary'));
|
||||
});
|
||||
});
|
||||
|
||||
it('uploading new package version (bad sha)', function () {
|
||||
var pkg = require('./lib/package')('testpkg')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update('fake').digest('hex')
|
||||
it('uploading new package version (bad sha)', function() {
|
||||
let pkg = require('./lib/package')('testpkg');
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update('fake').digest('hex');
|
||||
|
||||
return server.put_version('testpkg', '0.0.1', pkg)
|
||||
.status(400)
|
||||
.body_error(/shasum error/)
|
||||
})
|
||||
.body_error(/shasum error/);
|
||||
});
|
||||
|
||||
describe('version', function () {
|
||||
before(function () {
|
||||
var pkg = require('./lib/package')('testpkg')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex')
|
||||
describe('version', function() {
|
||||
before(function() {
|
||||
let pkg = require('./lib/package')('testpkg');
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex');
|
||||
return server.put_version('testpkg', '0.0.1', pkg)
|
||||
.status(201)
|
||||
.body_ok(/published/)
|
||||
})
|
||||
.body_ok(/published/);
|
||||
});
|
||||
|
||||
it('uploading new package version', function (){/* test for before() */})
|
||||
it('uploading new package version', function() {/* test for before() */});
|
||||
|
||||
it('downloading newly created package', function () {
|
||||
it('downloading newly created package', function() {
|
||||
return server.get_package('testpkg')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body.name, 'testpkg')
|
||||
assert.equal(body.versions['0.0.1'].name, 'testpkg')
|
||||
assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/testpkg/-/blahblah')
|
||||
assert.deepEqual(body['dist-tags'], {latest: '0.0.1'})
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.equal(body.name, 'testpkg');
|
||||
assert.equal(body.versions['0.0.1'].name, 'testpkg');
|
||||
assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/testpkg/-/blahblah');
|
||||
assert.deepEqual(body['dist-tags'], {latest: '0.0.1'});
|
||||
});
|
||||
});
|
||||
|
||||
it('downloading package via server2', function () {
|
||||
it('downloading package via server2', function() {
|
||||
return server2.get_package('testpkg')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body.name, 'testpkg')
|
||||
assert.equal(body.versions['0.0.1'].name, 'testpkg')
|
||||
assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55552/testpkg/-/blahblah')
|
||||
assert.deepEqual(body['dist-tags'], {latest: '0.0.1'})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.equal(body.name, 'testpkg');
|
||||
assert.equal(body.versions['0.0.1'].name, 'testpkg');
|
||||
assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55552/testpkg/-/blahblah');
|
||||
assert.deepEqual(body['dist-tags'], {latest: '0.0.1'});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('uploading new package version for bad pkg', function () {
|
||||
it('uploading new package version for bad pkg', function() {
|
||||
return server.put_version('testpxg', '0.0.1', require('./lib/package')('testpxg'))
|
||||
.status(404)
|
||||
.body_error(/no such package/)
|
||||
})
|
||||
.body_error(/no such package/);
|
||||
});
|
||||
|
||||
it('doubleerr test', function () {
|
||||
it('doubleerr test', function() {
|
||||
return server.put_tarball('testfwd2', 'blahblah', readfile('fixtures/binary'))
|
||||
.status(404)
|
||||
.body_error(/no such/)
|
||||
})
|
||||
.body_error(/no such/);
|
||||
});
|
||||
|
||||
it('publishing package / bad ro uplink', function () {
|
||||
it('publishing package / bad ro uplink', function() {
|
||||
return server.put_package('baduplink', require('./lib/package')('baduplink'))
|
||||
.status(503)
|
||||
.body_error(/one of the uplinks is down, refuse to publish/)
|
||||
})
|
||||
.body_error(/one of the uplinks is down, refuse to publish/);
|
||||
});
|
||||
|
||||
it('who am I?', function () {
|
||||
return server.whoami().then(function (username) {
|
||||
assert.equal(username, 'test')
|
||||
})
|
||||
})
|
||||
}
|
||||
it('who am I?', function() {
|
||||
return server.whoami().then(function(username) {
|
||||
assert.equal(username, 'test');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,64 +1,66 @@
|
||||
var assert = require('assert')
|
||||
var crypto = require('crypto')
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const crypto = require('crypto');
|
||||
|
||||
function readfile(x) {
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
return require('fs').readFileSync(__dirname + '/' + x);
|
||||
}
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
let server = process.server;
|
||||
let server2 = process.server2;
|
||||
|
||||
it('downloading non-existent tarball #1 / srv2', function () {
|
||||
it('downloading non-existent tarball #1 / srv2', function() {
|
||||
return server2.get_tarball('testpkg-gh29', 'blahblah')
|
||||
.status(404)
|
||||
.body_error(/no such package/)
|
||||
})
|
||||
.body_error(/no such package/);
|
||||
});
|
||||
|
||||
describe('pkg-gh29', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server.put_package('testpkg-gh29', require('./lib/package')('testpkg-gh29'))
|
||||
.status(201)
|
||||
.body_ok(/created new package/)
|
||||
})
|
||||
.body_ok(/created new package/);
|
||||
});
|
||||
|
||||
it('creating new package / srv1', function(){})
|
||||
it('creating new package / srv1', function() {});
|
||||
|
||||
it('downloading non-existent tarball #2 / srv2', function () {
|
||||
it('downloading non-existent tarball #2 / srv2', function() {
|
||||
return server2.get_tarball('testpkg-gh29', 'blahblah')
|
||||
.status(404)
|
||||
.body_error(/no such file/)
|
||||
})
|
||||
.body_error(/no such file/);
|
||||
});
|
||||
|
||||
describe('tarball', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server.put_tarball('testpkg-gh29', 'blahblah', readfile('fixtures/binary'))
|
||||
.status(201)
|
||||
.body_ok(/.*/)
|
||||
})
|
||||
.body_ok(/.*/);
|
||||
});
|
||||
|
||||
it('uploading new tarball / srv1', function(){})
|
||||
it('uploading new tarball / srv1', function() {});
|
||||
|
||||
describe('pkg version', function() {
|
||||
before(function () {
|
||||
var pkg = require('./lib/package')('testpkg-gh29')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex')
|
||||
before(function() {
|
||||
let pkg = require('./lib/package')('testpkg-gh29');
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex');
|
||||
return server.put_version('testpkg-gh29', '0.0.1', pkg)
|
||||
.status(201)
|
||||
.body_ok(/published/)
|
||||
})
|
||||
.body_ok(/published/);
|
||||
});
|
||||
|
||||
it('uploading new package version / srv1', function(){})
|
||||
it('uploading new package version / srv1', function() {});
|
||||
|
||||
it('downloading newly created tarball / srv2', function () {
|
||||
it('downloading newly created tarball / srv2', function() {
|
||||
return server2.get_tarball('testpkg-gh29', 'blahblah')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.deepEqual(body, readfile('fixtures/binary'))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
.then(function(body) {
|
||||
assert.deepEqual(body, readfile('fixtures/binary'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,70 +1,71 @@
|
||||
require('./lib/startup')
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert')
|
||||
var Promise = require('bluebird')
|
||||
require('./lib/startup');
|
||||
|
||||
let assert = require('assert');
|
||||
|
||||
function readfile(x) {
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
return require('fs').readFileSync(__dirname + '/' + x);
|
||||
}
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var express = process.express
|
||||
let server = process.server;
|
||||
let express = process.express;
|
||||
|
||||
describe('testexp_gzip', function() {
|
||||
before(function() {
|
||||
express.get('/testexp_gzip', function(req, res) {
|
||||
var x = eval(
|
||||
let x = eval(
|
||||
'(' + readfile('fixtures/publish.json5')
|
||||
.toString('utf8')
|
||||
.replace(/__NAME__/g, 'testexp_gzip')
|
||||
.replace(/__VERSION__/g, '0.0.1')
|
||||
+ ')'
|
||||
)
|
||||
);
|
||||
|
||||
// overcoming compress threshold
|
||||
x.versions['0.0.2'] = x.versions['0.0.1']
|
||||
x.versions['0.0.3'] = x.versions['0.0.1']
|
||||
x.versions['0.0.4'] = x.versions['0.0.1']
|
||||
x.versions['0.0.5'] = x.versions['0.0.1']
|
||||
x.versions['0.0.6'] = x.versions['0.0.1']
|
||||
x.versions['0.0.7'] = x.versions['0.0.1']
|
||||
x.versions['0.0.8'] = x.versions['0.0.1']
|
||||
x.versions['0.0.9'] = x.versions['0.0.1']
|
||||
x.versions['0.0.2'] = x.versions['0.0.1'];
|
||||
x.versions['0.0.3'] = x.versions['0.0.1'];
|
||||
x.versions['0.0.4'] = x.versions['0.0.1'];
|
||||
x.versions['0.0.5'] = x.versions['0.0.1'];
|
||||
x.versions['0.0.6'] = x.versions['0.0.1'];
|
||||
x.versions['0.0.7'] = x.versions['0.0.1'];
|
||||
x.versions['0.0.8'] = x.versions['0.0.1'];
|
||||
x.versions['0.0.9'] = x.versions['0.0.1'];
|
||||
|
||||
require('zlib').gzip(JSON.stringify(x), function(err, buf) {
|
||||
assert.equal(err, null)
|
||||
assert.equal(req.headers['accept-encoding'], 'gzip')
|
||||
res.header('content-encoding', 'gzip')
|
||||
res.send(buf)
|
||||
})
|
||||
})
|
||||
assert.equal(err, null);
|
||||
assert.equal(req.headers['accept-encoding'], 'gzip');
|
||||
res.header('content-encoding', 'gzip');
|
||||
res.send(buf);
|
||||
});
|
||||
});
|
||||
|
||||
express.get('/testexp_baddata', function(req, res) {
|
||||
assert.equal(req.headers['accept-encoding'], 'gzip')
|
||||
res.header('content-encoding', 'gzip')
|
||||
res.send(new Buffer([1,2,3,4,5,6,7,7,6,5,4,3,2,1]))
|
||||
})
|
||||
})
|
||||
assert.equal(req.headers['accept-encoding'], 'gzip');
|
||||
res.header('content-encoding', 'gzip');
|
||||
res.send(new Buffer([1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1]));
|
||||
});
|
||||
});
|
||||
|
||||
it('should not fail on bad gzip', function () {
|
||||
it('should not fail on bad gzip', function() {
|
||||
return server.get_package('testexp_baddata')
|
||||
.status(404)
|
||||
})
|
||||
.status(404);
|
||||
});
|
||||
|
||||
it('should understand gzipped data from uplink', function () {
|
||||
it('should understand gzipped data from uplink', function() {
|
||||
return server.get_package('testexp_gzip')
|
||||
.status(200)
|
||||
.response(function (res) {
|
||||
assert.equal(res.headers['content-encoding'], undefined)
|
||||
.response(function(res) {
|
||||
assert.equal(res.headers['content-encoding'], undefined);
|
||||
})
|
||||
.then(function (body) {
|
||||
assert.equal(body.name, 'testexp_gzip')
|
||||
assert.equal(Object.keys(body.versions).length, 9)
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.equal(body.name, 'testexp_gzip');
|
||||
assert.equal(Object.keys(body.versions).length, 9);
|
||||
});
|
||||
});
|
||||
|
||||
it('should serve gzipped data', function () {
|
||||
it('should serve gzipped data', function() {
|
||||
return server.request({
|
||||
uri: '/testexp_gzip',
|
||||
encoding: null,
|
||||
@ -73,25 +74,25 @@ module.exports = function() {
|
||||
},
|
||||
json: false,
|
||||
}).status(200)
|
||||
.response(function (res) {
|
||||
assert.equal(res.headers['content-encoding'], 'gzip')
|
||||
.response(function(res) {
|
||||
assert.equal(res.headers['content-encoding'], 'gzip');
|
||||
})
|
||||
.then(function (body) {
|
||||
.then(function(body) {
|
||||
assert.throws(function() {
|
||||
JSON.parse(body.toString('utf8'))
|
||||
})
|
||||
JSON.parse(body.toString('utf8'));
|
||||
});
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
return new Promise(function(resolve) {
|
||||
require('zlib').gunzip(body, function(err, buf) {
|
||||
assert.equal(err, null)
|
||||
body = JSON.parse(buf)
|
||||
assert.equal(body.name, 'testexp_gzip')
|
||||
assert.equal(Object.keys(body.versions).length, 9)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
assert.equal(err, null);
|
||||
body = JSON.parse(buf);
|
||||
assert.equal(body.name, 'testexp_gzip');
|
||||
assert.equal(Object.keys(body.versions).length, 9);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,67 +1,71 @@
|
||||
var assert = require('assert')
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var express = process.express
|
||||
let server = process.server;
|
||||
let express = process.express;
|
||||
|
||||
describe('Incomplete', function() {
|
||||
before(function() {
|
||||
express.get('/testexp-incomplete', function(_, res) {
|
||||
res.send({
|
||||
"name": "testexp-incomplete",
|
||||
"versions": {
|
||||
"0.1.0": {
|
||||
"name": "testexp_tags",
|
||||
"version": "0.1.0",
|
||||
"dist": {
|
||||
"shasum": "fake",
|
||||
"tarball": "http://localhost:55550/testexp-incomplete/-/content-length.tar.gz"
|
||||
}
|
||||
'name': 'testexp-incomplete',
|
||||
'versions': {
|
||||
'0.1.0': {
|
||||
'name': 'testexp_tags',
|
||||
'version': '0.1.0',
|
||||
'dist': {
|
||||
'shasum': 'fake',
|
||||
'tarball': 'http://localhost:55550/testexp-incomplete/-/content-length.tar.gz',
|
||||
},
|
||||
},
|
||||
"0.1.1": {
|
||||
"name": "testexp_tags",
|
||||
"version": "0.1.1",
|
||||
"dist": {
|
||||
"shasum": "fake",
|
||||
"tarball": "http://localhost:55550/testexp-incomplete/-/chunked.tar.gz"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
'0.1.1': {
|
||||
'name': 'testexp_tags',
|
||||
'version': '0.1.1',
|
||||
'dist': {
|
||||
'shasum': 'fake',
|
||||
'tarball': 'http://localhost:55550/testexp-incomplete/-/chunked.tar.gz',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
;[ 'content-length', 'chunked' ].forEach(function(type) {
|
||||
;['content-length', 'chunked'].forEach(function(type) {
|
||||
it('should not store tarballs / ' + type, function(_cb) {
|
||||
var called
|
||||
let called;
|
||||
express.get('/testexp-incomplete/-/'+type+'.tar.gz', function(_, res) {
|
||||
if (called) return res.socket.destroy()
|
||||
called = true
|
||||
if (type !== 'chunked') res.header('content-length', 1e6)
|
||||
res.write('test test test\n')
|
||||
if (called) return res.socket.destroy();
|
||||
called = true;
|
||||
if (type !== 'chunked') res.header('content-length', 1e6);
|
||||
res.write('test test test\n');
|
||||
setTimeout(function() {
|
||||
res.socket.write('200\nsss\n')
|
||||
res.socket.destroy()
|
||||
cb()
|
||||
}, 10)
|
||||
})
|
||||
res.socket.write('200\nsss\n');
|
||||
res.socket.destroy();
|
||||
cb();
|
||||
}, 10);
|
||||
});
|
||||
|
||||
server.request({ uri: '/testexp-incomplete/-/'+type+'.tar.gz' })
|
||||
server.request({uri: '/testexp-incomplete/-/'+type+'.tar.gz'})
|
||||
.status(200)
|
||||
.response(function (res) {
|
||||
if (type !== 'chunked') assert.equal(res.headers['content-length'], 1e6)
|
||||
})
|
||||
.then(function (body) {
|
||||
assert(body.match(/test test test/))
|
||||
.response(function(res) {
|
||||
if (type !== 'chunked') assert.equal(res.headers['content-length'], 1e6);
|
||||
})
|
||||
.then(function(body) {
|
||||
assert(body.match(/test test test/));
|
||||
});
|
||||
|
||||
function cb() {
|
||||
server.request({ uri: '/testexp-incomplete/-/'+type+'.tar.gz' })
|
||||
server.request({uri: '/testexp-incomplete/-/'+type+'.tar.gz'})
|
||||
.body_error('internal server error')
|
||||
.then(function () { _cb() })
|
||||
.then(function() {
|
||||
_cb();
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,88 +1,94 @@
|
||||
//require('es6-shim')
|
||||
require('./lib/startup')
|
||||
var Promise = require('bluebird')
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert')
|
||||
var async = require('async')
|
||||
var exec = require('child_process').exec
|
||||
require('./lib/startup');
|
||||
|
||||
const assert = require('assert');
|
||||
const exec = require('child_process').exec;
|
||||
|
||||
describe('Func', function() {
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
const server = process.server;
|
||||
const server2 = process.server2;
|
||||
|
||||
before(function (cb) {
|
||||
async.parallel([
|
||||
function (cb) {
|
||||
require('./lib/startup').start('./test-storage', './config-1.yaml', cb)
|
||||
},
|
||||
function (cb) {
|
||||
require('./lib/startup').start('./test-storage2', './config-2.yaml', cb)
|
||||
},
|
||||
], cb)
|
||||
})
|
||||
before(function(done) {
|
||||
Promise.all([
|
||||
require('./lib/startup').start('./test-storage', './config-1.yaml'),
|
||||
require('./lib/startup').start('./test-storage2', './config-2.yaml'),
|
||||
]).then(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
before(function() {
|
||||
return Promise.all([ server, server2 ].map(function(server) {
|
||||
return server.debug().status(200).then(function (body) {
|
||||
server.pid = body.pid
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
return Promise.all([server, server2].map(function(server) {
|
||||
return server.debug().status(200).then(function(body) {
|
||||
server.pid = body.pid;
|
||||
return new Promise(function(resolve, reject) {
|
||||
exec('lsof -p ' + Number(server.pid), function(err, result) {
|
||||
assert.equal(err, null)
|
||||
server.fdlist = result.replace(/ +/g, ' ')
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
}))
|
||||
})
|
||||
assert.equal(err, null);
|
||||
server.fdlist = result.replace(/ +/g, ' ');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
before(function auth() {
|
||||
return Promise.all([ server, server2 ].map(function(server, cb) {
|
||||
return server.auth('test', 'test').status(201).body_ok(/'test'/)
|
||||
}))
|
||||
})
|
||||
return Promise.all([server, server2].map(function(server, cb) {
|
||||
return server.auth('test', 'test').status(201).body_ok(/'test'/);
|
||||
}));
|
||||
});
|
||||
|
||||
it('authenticate', function(){/* test for before() */})
|
||||
it('authenticate', function() {/* test for before() */});
|
||||
|
||||
require('./access')()
|
||||
require('./basic')()
|
||||
require('./gh29')()
|
||||
require('./tags')()
|
||||
require('./gzip')()
|
||||
require('./incomplete')()
|
||||
require('./mirror')()
|
||||
require('./newnpmreg')()
|
||||
require('./nullstorage')()
|
||||
require('./race')()
|
||||
require('./racycrash')()
|
||||
require('./scoped')()
|
||||
require('./security')()
|
||||
require('./adduser')()
|
||||
require('./logout')()
|
||||
require('./addtag')()
|
||||
require('./plugins')()
|
||||
require('./access')();
|
||||
require('./basic')();
|
||||
require('./gh29')();
|
||||
require('./tags')();
|
||||
require('./gzip')();
|
||||
require('./incomplete')();
|
||||
require('./mirror')();
|
||||
require('./newnpmreg')();
|
||||
require('./nullstorage')();
|
||||
require('./race')();
|
||||
require('./racycrash')();
|
||||
require('./scoped')();
|
||||
require('./security')();
|
||||
require('./adduser')();
|
||||
require('./logout')();
|
||||
require('./addtag')();
|
||||
require('./plugins')();
|
||||
|
||||
after(function (cb) {
|
||||
async.map([ server, server2 ], function(server, cb) {
|
||||
exec('lsof -p ' + Number(server.pid), function(err, result) {
|
||||
assert.equal(err, null)
|
||||
result = result.split('\n').filter(function(q) {
|
||||
if (q.match(/TCP .*->.* \(ESTABLISHED\)/)) return false
|
||||
if (q.match(/\/libcrypt-[^\/]+\.so/)) return false
|
||||
if (q.match(/\/node_modules\/crypt3\/build\/Release/)) return false
|
||||
return true
|
||||
}).join('\n').replace(/ +/g, ' ')
|
||||
after(function(done) {
|
||||
const check = (server) => {
|
||||
return new Promise(function(resolve, reject) {
|
||||
exec('lsof -p ' + parseInt(server.pid, 10), function(err, result) {
|
||||
if (err) {
|
||||
reject();
|
||||
} else {
|
||||
result = result.split('\n').filter(function(q) {
|
||||
if (q.match(/TCP .*->.* \(ESTABLISHED\)/)) return false;
|
||||
if (q.match(/\/libcrypt-[^\/]+\.so/)) return false;
|
||||
if (q.match(/\/node_modules\/crypt3\/build\/Release/)) return false;
|
||||
return true;
|
||||
}).join('\n').replace(/ +/g, ' ');
|
||||
assert.equal(server.fdlist, result);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
Promise.all([check(server), check(server2)]).then(function() {
|
||||
done();
|
||||
}, (reason) => {
|
||||
assert.equal(reason, null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
assert.equal(server.fdlist, result)
|
||||
cb()
|
||||
})
|
||||
}, cb)
|
||||
})
|
||||
})
|
||||
|
||||
process.on('unhandledRejection', function (err) {
|
||||
process.nextTick(function () {
|
||||
throw err
|
||||
})
|
||||
})
|
||||
process.on('unhandledRejection', function(err) {
|
||||
process.nextTick(function() {
|
||||
throw err;
|
||||
});
|
||||
});
|
||||
|
@ -1,12 +1,11 @@
|
||||
|
||||
module.exports = function(name, version) {
|
||||
return {
|
||||
"name": name,
|
||||
"version": version || "0.0.0",
|
||||
"dist": {
|
||||
"shasum": "fake",
|
||||
"tarball": "http://localhost:55551/"+encodeURIComponent(name)+"/-/blahblah"
|
||||
}
|
||||
}
|
||||
}
|
||||
name,
|
||||
version: version || '0.0.0',
|
||||
dist: {
|
||||
shasum: 'fake',
|
||||
tarball: `http://localhost:55551/${encodeURIComponent(name)}/-/blahblah`,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,21 +1,22 @@
|
||||
var assert = require('assert')
|
||||
var request = require('./smart_request')
|
||||
var Promise = require('bluebird')
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const request = require('./smart_request');
|
||||
|
||||
function Server(url) {
|
||||
var self = Object.create(Server.prototype)
|
||||
self.url = url.replace(/\/$/, '')
|
||||
self.userAgent = 'node/v0.10.8 linux x64'
|
||||
self.authstr = 'Basic '+(new Buffer('test:test')).toString('base64')
|
||||
return self
|
||||
let self = Object.create(Server.prototype);
|
||||
self.url = url.replace(/\/$/, '');
|
||||
self.userAgent = 'node/v0.10.8 linux x64';
|
||||
self.authstr = 'Basic '+(new Buffer('test:test')).toString('base64');
|
||||
return self;
|
||||
}
|
||||
|
||||
Server.prototype.request = function(options) {
|
||||
assert(options.uri)
|
||||
var headers = options.headers || {}
|
||||
headers.accept = headers.accept || 'application/json'
|
||||
headers['user-agent'] = headers['user-agent'] || this.userAgent
|
||||
headers.authorization = headers.authorization || this.authstr
|
||||
assert(options.uri);
|
||||
let headers = options.headers || {};
|
||||
headers.accept = headers.accept || 'application/json';
|
||||
headers['user-agent'] = headers['user-agent'] || this.userAgent;
|
||||
headers.authorization = headers.authorization || this.authstr;
|
||||
|
||||
return request({
|
||||
url: this.url + options.uri,
|
||||
@ -23,11 +24,11 @@ Server.prototype.request = function(options) {
|
||||
headers: headers,
|
||||
encoding: options.encoding,
|
||||
json: options.json != null ? options.json : true,
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.auth = function(user, pass) {
|
||||
this.authstr = 'Basic '+(new Buffer(user+':'+pass)).toString('base64')
|
||||
this.authstr = 'Basic '+(new Buffer(user+':'+pass)).toString('base64');
|
||||
return this.request({
|
||||
uri: '/-/user/org.couchdb.user:'+encodeURIComponent(user)+'/-rev/undefined',
|
||||
method: 'PUT',
|
||||
@ -39,76 +40,76 @@ Server.prototype.auth = function(user, pass) {
|
||||
type: 'user',
|
||||
roles: [],
|
||||
date: new Date(),
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.logout = function(token) {
|
||||
return this.request({
|
||||
uri: '/-/user/token/'+encodeURIComponent(token),
|
||||
method: 'DELETE'
|
||||
})
|
||||
}
|
||||
method: 'DELETE',
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.get_package = function(name) {
|
||||
return this.request({
|
||||
uri: '/'+encodeURIComponent(name),
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.put_package = function(name, data) {
|
||||
if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data)
|
||||
if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data);
|
||||
return this.request({
|
||||
uri: '/'+encodeURIComponent(name),
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
}).send(data)
|
||||
}
|
||||
}).send(data);
|
||||
};
|
||||
|
||||
Server.prototype.put_version = function(name, version, data) {
|
||||
if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data)
|
||||
if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data);
|
||||
return this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(version)+'/-tag/latest',
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
}).send(data)
|
||||
}
|
||||
}).send(data);
|
||||
};
|
||||
|
||||
Server.prototype.get_tarball = function(name, filename) {
|
||||
return this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename),
|
||||
method: 'GET',
|
||||
encoding: null
|
||||
})
|
||||
}
|
||||
encoding: null,
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.put_tarball = function(name, filename, data) {
|
||||
return this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever',
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/octet-stream'
|
||||
'content-type': 'application/octet-stream',
|
||||
},
|
||||
}).send(data)
|
||||
}
|
||||
}).send(data);
|
||||
};
|
||||
|
||||
Server.prototype.add_tag = function(name, tag, version) {
|
||||
return this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(tag),
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
}).send(JSON.stringify(version))
|
||||
}
|
||||
}).send(JSON.stringify(version));
|
||||
};
|
||||
|
||||
Server.prototype.put_tarball_incomplete = function(name, filename, data, size, cb) {
|
||||
var promise = this.request({
|
||||
let promise = this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever',
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
@ -116,51 +117,53 @@ Server.prototype.put_tarball_incomplete = function(name, filename, data, size, c
|
||||
'content-length': size,
|
||||
},
|
||||
timeout: 1000,
|
||||
})
|
||||
});
|
||||
|
||||
promise.request(function (req) {
|
||||
req.write(data)
|
||||
promise.request(function(req) {
|
||||
req.write(data);
|
||||
setTimeout(function() {
|
||||
req.req.abort()
|
||||
}, 20)
|
||||
})
|
||||
req.req.abort();
|
||||
}, 20);
|
||||
});
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
promise
|
||||
.then(function() {
|
||||
reject(Error('no error'))
|
||||
reject(Error('no error'));
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (err.code === 'ECONNRESET') {
|
||||
resolve()
|
||||
resolve();
|
||||
} else {
|
||||
reject(err)
|
||||
reject(err);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.add_package = function(name) {
|
||||
return this.put_package(name, require('./package')(name))
|
||||
.status(201)
|
||||
.body_ok('created new package')
|
||||
}
|
||||
.body_ok('created new package');
|
||||
};
|
||||
|
||||
Server.prototype.whoami = function() {
|
||||
return this.request({ uri:'/-/whoami' })
|
||||
return this.request({uri: '/-/whoami'})
|
||||
.status(200)
|
||||
.then(function(x) { return x.username })
|
||||
}
|
||||
.then(function(x) {
|
||||
return x.username;
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.debug = function() {
|
||||
return this.request({
|
||||
uri: '/-/_debug',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Server
|
||||
module.exports = Server;
|
||||
|
||||
|
@ -1,104 +1,104 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert')
|
||||
var request = require('request')
|
||||
var Promise = require('bluebird')
|
||||
var sym = Symbol('smart_request_data')
|
||||
const assert = require('assert');
|
||||
const request = require('request');
|
||||
const sym = Symbol('smart_request_data');
|
||||
|
||||
function smart_request(options) {
|
||||
var self = {}
|
||||
self[sym] = {}
|
||||
self[sym].error = Error()
|
||||
Error.captureStackTrace(self[sym].error, smart_request)
|
||||
let self = {};
|
||||
self[sym] = {};
|
||||
self[sym].error = Error();
|
||||
Error.captureStackTrace(self[sym].error, smart_request);
|
||||
|
||||
var result = new Promise(function (resolve, reject) {
|
||||
self[sym].request = request(options, function (err, res, body) {
|
||||
if (err) return reject(err)
|
||||
self[sym].response = res
|
||||
resolve(body)
|
||||
})
|
||||
})
|
||||
let result = new Promise(function(resolve, reject) {
|
||||
self[sym].request = request(options, function(err, res, body) {
|
||||
if (err) return reject(err);
|
||||
self[sym].response = res;
|
||||
resolve(body);
|
||||
});
|
||||
});
|
||||
|
||||
return extend(self, result)
|
||||
return extend(self, result);
|
||||
}
|
||||
|
||||
function extend(self, promise) {
|
||||
promise[sym] = self[sym]
|
||||
Object.setPrototypeOf(promise, extensions)
|
||||
return promise
|
||||
promise[sym] = self[sym];
|
||||
Object.setPrototypeOf(promise, extensions);
|
||||
return promise;
|
||||
}
|
||||
|
||||
var extensions = Object.create(Promise.prototype)
|
||||
var extensions = Object.create(Promise.prototype);
|
||||
|
||||
extensions.status = function (expected) {
|
||||
var self_data = this[sym]
|
||||
extensions.status = function(expected) {
|
||||
let self_data = this[sym];
|
||||
|
||||
return extend(this, this.then(function (body) {
|
||||
return extend(this, this.then(function(body) {
|
||||
try {
|
||||
assert.equal(self_data.response.statusCode, expected)
|
||||
assert.equal(self_data.response.statusCode, expected);
|
||||
} catch(err) {
|
||||
self_data.error.message = err.message
|
||||
throw self_data.error
|
||||
self_data.error.message = err.message;
|
||||
throw self_data.error;
|
||||
}
|
||||
return body
|
||||
}))
|
||||
}
|
||||
return body;
|
||||
}));
|
||||
};
|
||||
|
||||
extensions.body_ok = function (expected) {
|
||||
var self_data = this[sym]
|
||||
extensions.body_ok = function(expected) {
|
||||
let self_data = this[sym];
|
||||
|
||||
return extend(this, this.then(function (body) {
|
||||
return extend(this, this.then(function(body) {
|
||||
try {
|
||||
if (Object.prototype.toString.call(expected) === '[object RegExp]') {
|
||||
assert(body.ok.match(expected), "'" + body.ok + "' doesn't match " + expected)
|
||||
assert(body.ok.match(expected), '\'' + body.ok + '\' doesn\'t match ' + expected);
|
||||
} else {
|
||||
assert.equal(body.ok, expected)
|
||||
assert.equal(body.ok, expected);
|
||||
}
|
||||
assert.equal(body.error, null)
|
||||
assert.equal(body.error, null);
|
||||
} catch(err) {
|
||||
self_data.error.message = err.message
|
||||
throw self_data.error
|
||||
self_data.error.message = err.message;
|
||||
throw self_data.error;
|
||||
}
|
||||
return body
|
||||
}))
|
||||
}
|
||||
return body;
|
||||
}));
|
||||
};
|
||||
|
||||
extensions.body_error = function (expected) {
|
||||
var self_data = this[sym]
|
||||
extensions.body_error = function(expected) {
|
||||
let self_data = this[sym];
|
||||
|
||||
return extend(this, this.then(function (body) {
|
||||
return extend(this, this.then(function(body) {
|
||||
try {
|
||||
if (Object.prototype.toString.call(expected) === '[object RegExp]') {
|
||||
assert(body.error.match(expected), body.error + " doesn't match " + expected)
|
||||
assert(body.error.match(expected), body.error + ' doesn\'t match ' + expected);
|
||||
} else {
|
||||
assert.equal(body.error, expected)
|
||||
assert.equal(body.error, expected);
|
||||
}
|
||||
assert.equal(body.ok, null)
|
||||
assert.equal(body.ok, null);
|
||||
} catch(err) {
|
||||
self_data.error.message = err.message
|
||||
throw self_data.error
|
||||
self_data.error.message = err.message;
|
||||
throw self_data.error;
|
||||
}
|
||||
return body
|
||||
}))
|
||||
}
|
||||
return body;
|
||||
}));
|
||||
};
|
||||
|
||||
extensions.request = function (cb) {
|
||||
cb(this[sym].request)
|
||||
return this
|
||||
}
|
||||
extensions.request = function(cb) {
|
||||
cb(this[sym].request);
|
||||
return this;
|
||||
};
|
||||
|
||||
extensions.response = function (cb) {
|
||||
var self_data = this[sym]
|
||||
extensions.response = function(cb) {
|
||||
let self_data = this[sym];
|
||||
|
||||
return extend(this, this.then(function (body) {
|
||||
cb(self_data.response)
|
||||
return body
|
||||
}))
|
||||
}
|
||||
return extend(this, this.then(function(body) {
|
||||
cb(self_data.response);
|
||||
return body;
|
||||
}));
|
||||
};
|
||||
|
||||
extensions.send = function (data) {
|
||||
this[sym].request.end(data)
|
||||
return this
|
||||
}
|
||||
extensions.send = function(data) {
|
||||
this[sym].request.end(data);
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = smart_request
|
||||
module.exports = smart_request;
|
||||
|
||||
|
@ -1,40 +1,44 @@
|
||||
var fork = require('child_process').fork
|
||||
var express = require('express')
|
||||
var rimraf = require('rimraf')
|
||||
var Server = require('./server')
|
||||
'use strict';
|
||||
|
||||
var forks = process.forks = []
|
||||
process.server = Server('http://localhost:55551/')
|
||||
process.server2 = Server('http://localhost:55552/')
|
||||
process.express = express()
|
||||
process.express.listen(55550)
|
||||
const fork = require('child_process').fork;
|
||||
const express = require('express');
|
||||
const rimraf = require('rimraf');
|
||||
const Server = require('./server');
|
||||
|
||||
module.exports.start = function start(dir, conf, cb) {
|
||||
rimraf(__dirname + '/../' + dir, function() {
|
||||
// filter out --debug-brk
|
||||
var oldArgv = process.execArgv
|
||||
process.execArgv = process.execArgv.filter(function(x) {
|
||||
return x !== '--debug-brk'
|
||||
})
|
||||
const forks = process.forks = [];
|
||||
process.server = Server('http://localhost:55551/');
|
||||
process.server2 = Server('http://localhost:55552/');
|
||||
process.express = express();
|
||||
process.express.listen(55550);
|
||||
|
||||
var f = fork(__dirname + '/../../../bin/verdaccio'
|
||||
, ['-c', __dirname + '/../' + conf]
|
||||
, {silent: !process.env.TRAVIS}
|
||||
)
|
||||
forks.push(f)
|
||||
f.on('message', function(msg) {
|
||||
if ('verdaccio_started' in msg) {
|
||||
cb(), cb = function(){}
|
||||
}
|
||||
})
|
||||
f.on('error', function(err) {
|
||||
throw err
|
||||
})
|
||||
process.execArgv = oldArgv
|
||||
})
|
||||
}
|
||||
module.exports.start = function(dir, conf) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
rimraf(__dirname + '/../' + dir, function() {
|
||||
// filter out --debug-brk
|
||||
let oldArgv = process.execArgv;
|
||||
process.execArgv = process.execArgv.filter(function(x) {
|
||||
return x !== '--debug-brk';
|
||||
});
|
||||
|
||||
const f = fork(__dirname + '/../../../bin/verdaccio'
|
||||
, ['-c', __dirname + '/../' + conf]
|
||||
, {silent: !process.env.TRAVIS}
|
||||
);
|
||||
forks.push(f);
|
||||
f.on('message', function(msg) {
|
||||
if ('verdaccio_started' in msg) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
f.on('error', function(err) {
|
||||
reject(err);
|
||||
});
|
||||
process.execArgv = oldArgv;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
process.on('exit', function() {
|
||||
if (forks[0]) forks[0].kill()
|
||||
if (forks[1]) forks[1].kill()
|
||||
})
|
||||
if (forks[0]) forks[0].kill();
|
||||
if (forks[1]) forks[1].kill();
|
||||
});
|
||||
|
@ -1,11 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
let server = process.server;
|
||||
|
||||
describe('logout', function() {
|
||||
it('should log out', function () {
|
||||
it('should log out', function() {
|
||||
return server.logout('some-token')
|
||||
.status(200)
|
||||
.body_ok(/Logged out/)
|
||||
})
|
||||
})
|
||||
}
|
||||
.body_ok(/Logged out/);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -1,64 +1,66 @@
|
||||
var assert = require('assert')
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
|
||||
function readfile(x) {
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
return require('fs').readFileSync(__dirname + '/' + x);
|
||||
}
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
let server = process.server;
|
||||
let server2 = process.server2;
|
||||
|
||||
it('testing anti-loop', function () {
|
||||
it('testing anti-loop', function() {
|
||||
return server2.get_package('testloop')
|
||||
.status(404)
|
||||
.body_error(/no such package/)
|
||||
.body_error(/no such package/);
|
||||
})
|
||||
|
||||
;['fwd', /*'loop'*/].forEach(function(pkg) {
|
||||
var prefix = pkg + ': '
|
||||
pkg = 'test' + pkg
|
||||
;['fwd'].forEach(function(pkg) {
|
||||
let prefix = pkg + ': ';
|
||||
pkg = 'test' + pkg;
|
||||
|
||||
describe(pkg, function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server.put_package(pkg, require('./lib/package')(pkg))
|
||||
.status(201)
|
||||
.body_ok(/created new package/)
|
||||
})
|
||||
.body_ok(/created new package/);
|
||||
});
|
||||
|
||||
it(prefix+'creating new package', function(){})
|
||||
it(prefix+'creating new package', function() {});
|
||||
|
||||
describe(pkg, function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server.put_version(pkg, '0.1.1', require('./lib/package')(pkg))
|
||||
.status(201)
|
||||
.body_ok(/published/)
|
||||
})
|
||||
.body_ok(/published/);
|
||||
});
|
||||
|
||||
it(prefix+'uploading new package version', function(){})
|
||||
it(prefix+'uploading new package version', function() {});
|
||||
|
||||
it(prefix+'uploading incomplete tarball', function () {
|
||||
return server.put_tarball_incomplete(pkg, pkg+'.bad', readfile('fixtures/binary'), 3000)
|
||||
})
|
||||
it(prefix+'uploading incomplete tarball', function() {
|
||||
return server.put_tarball_incomplete(pkg, pkg+'.bad', readfile('fixtures/binary'), 3000);
|
||||
});
|
||||
|
||||
describe('tarball', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server.put_tarball(pkg, pkg+'.file', readfile('fixtures/binary'))
|
||||
.status(201)
|
||||
.body_ok(/.*/)
|
||||
})
|
||||
.body_ok(/.*/);
|
||||
});
|
||||
|
||||
it(prefix+'uploading new tarball', function(){})
|
||||
it(prefix+'uploading new tarball', function() {});
|
||||
|
||||
it(prefix+'downloading tarball from server1', function () {
|
||||
it(prefix+'downloading tarball from server1', function() {
|
||||
return server.get_tarball(pkg, pkg+'.file')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.deepEqual(body, readfile('fixtures/binary'))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
.then(function(body) {
|
||||
assert.deepEqual(body, readfile('fixtures/binary'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,20 +1,22 @@
|
||||
var assert = require('assert')
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
|
||||
function readfile(x) {
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
return require('fs').readFileSync(__dirname + '/' + x);
|
||||
}
|
||||
|
||||
function sha(x) {
|
||||
return require('crypto').createHash('sha1', 'binary').update(x).digest('hex')
|
||||
return require('crypto').createHash('sha1', 'binary').update(x).digest('hex');
|
||||
}
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
var express = process.express
|
||||
let server = process.server;
|
||||
let server2 = process.server2;
|
||||
let express = process.express;
|
||||
|
||||
describe('newnpmreg', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server.request({
|
||||
uri: '/testpkg-newnpmreg',
|
||||
headers: {
|
||||
@ -22,100 +24,100 @@ module.exports = function() {
|
||||
},
|
||||
method: 'PUT',
|
||||
json: JSON.parse(readfile('fixtures/newnpmreg.json')),
|
||||
}).status(201)
|
||||
})
|
||||
}).status(201);
|
||||
});
|
||||
|
||||
it('add pkg', function () {})
|
||||
it('add pkg', function() {});
|
||||
|
||||
it('server1 - tarball', function () {
|
||||
it('server1 - tarball', function() {
|
||||
return server.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
.then(function(body) {
|
||||
// not real sha due to utf8 conversion
|
||||
assert.strictEqual(sha(body), '8ee7331cbc641581b1a8cecd9d38d744a8feb863')
|
||||
})
|
||||
})
|
||||
assert.strictEqual(sha(body), '8ee7331cbc641581b1a8cecd9d38d744a8feb863');
|
||||
});
|
||||
});
|
||||
|
||||
it('server2 - tarball', function () {
|
||||
it('server2 - tarball', function() {
|
||||
return server2.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
.then(function(body) {
|
||||
// not real sha due to utf8 conversion
|
||||
assert.strictEqual(sha(body), '8ee7331cbc641581b1a8cecd9d38d744a8feb863')
|
||||
})
|
||||
})
|
||||
assert.strictEqual(sha(body), '8ee7331cbc641581b1a8cecd9d38d744a8feb863');
|
||||
});
|
||||
});
|
||||
|
||||
it('server1 - package', function () {
|
||||
it('server1 - package', function() {
|
||||
return server.get_package('testpkg-newnpmreg')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body.name, 'testpkg-newnpmreg')
|
||||
assert.equal(body.versions['0.0.0'].name, 'testpkg-newnpmreg')
|
||||
assert.equal(body.versions['0.0.0'].dist.tarball, 'http://localhost:55551/testpkg-newnpmreg/-/testpkg-newnpmreg-0.0.0.tgz')
|
||||
assert.deepEqual(body['dist-tags'], {foo: '0.0.0', latest: '0.0.0'})
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.equal(body.name, 'testpkg-newnpmreg');
|
||||
assert.equal(body.versions['0.0.0'].name, 'testpkg-newnpmreg');
|
||||
assert.equal(body.versions['0.0.0'].dist.tarball, 'http://localhost:55551/testpkg-newnpmreg/-/testpkg-newnpmreg-0.0.0.tgz');
|
||||
assert.deepEqual(body['dist-tags'], {foo: '0.0.0', latest: '0.0.0'});
|
||||
});
|
||||
});
|
||||
|
||||
it('server2 - package', function () {
|
||||
it('server2 - package', function() {
|
||||
return server2.get_package('testpkg-newnpmreg')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body.name, 'testpkg-newnpmreg')
|
||||
assert.equal(body.versions['0.0.0'].name, 'testpkg-newnpmreg')
|
||||
assert.equal(body.versions['0.0.0'].dist.tarball, 'http://localhost:55552/testpkg-newnpmreg/-/testpkg-newnpmreg-0.0.0.tgz')
|
||||
assert.deepEqual(body['dist-tags'], {foo: '0.0.0', latest: '0.0.0'})
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.equal(body.name, 'testpkg-newnpmreg');
|
||||
assert.equal(body.versions['0.0.0'].name, 'testpkg-newnpmreg');
|
||||
assert.equal(body.versions['0.0.0'].dist.tarball, 'http://localhost:55552/testpkg-newnpmreg/-/testpkg-newnpmreg-0.0.0.tgz');
|
||||
assert.deepEqual(body['dist-tags'], {foo: '0.0.0', latest: '0.0.0'});
|
||||
});
|
||||
});
|
||||
|
||||
it('server1 - readme', function () {
|
||||
return server.request({ uri: '/-/readme/testpkg-newnpmreg' })
|
||||
it('server1 - readme', function() {
|
||||
return server.request({uri: '/-/readme/testpkg-newnpmreg'})
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body, '<p>blah blah blah</p>\n')
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.equal(body, '<p>blah blah blah</p>\n');
|
||||
});
|
||||
});
|
||||
|
||||
it('server2 - readme', function () {
|
||||
return server2.request({ uri: '/-/readme/testpkg-newnpmreg' })
|
||||
it('server2 - readme', function() {
|
||||
return server2.request({uri: '/-/readme/testpkg-newnpmreg'})
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body, '<p>blah blah blah</p>\n')
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.equal(body, '<p>blah blah blah</p>\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', function() {
|
||||
function check(obj) {
|
||||
obj['testpkg-newnpmreg'].time.modified = '2014-10-02T07:07:51.000Z'
|
||||
obj['testpkg-newnpmreg'].time.modified = '2014-10-02T07:07:51.000Z';
|
||||
assert.deepEqual(obj['testpkg-newnpmreg'],
|
||||
{ name: 'testpkg-newnpmreg',
|
||||
description: '',
|
||||
author: '',
|
||||
license: 'ISC',
|
||||
'dist-tags': { latest: '0.0.0' },
|
||||
maintainers: [ { name: 'alex', email: 'alex@kocharin.ru' } ],
|
||||
readmeFilename: '',
|
||||
time: { modified: '2014-10-02T07:07:51.000Z' },
|
||||
versions: {},
|
||||
repository: { type: 'git', url: '' } })
|
||||
{'name': 'testpkg-newnpmreg',
|
||||
'description': '',
|
||||
'author': '',
|
||||
'license': 'ISC',
|
||||
'dist-tags': {latest: '0.0.0'},
|
||||
'maintainers': [{name: 'alex', email: 'alex@kocharin.ru'}],
|
||||
'readmeFilename': '',
|
||||
'time': {modified: '2014-10-02T07:07:51.000Z'},
|
||||
'versions': {},
|
||||
'repository': {type: 'git', url: ''}});
|
||||
}
|
||||
|
||||
before(function () {
|
||||
before(function() {
|
||||
express.get('/-/all', function(req, res) {
|
||||
res.send({})
|
||||
})
|
||||
})
|
||||
res.send({});
|
||||
});
|
||||
});
|
||||
|
||||
it('server1 - search', function () {
|
||||
return server.request({ uri: '/-/all' })
|
||||
it('server1 - search', function() {
|
||||
return server.request({uri: '/-/all'})
|
||||
.status(200)
|
||||
.then(check)
|
||||
})
|
||||
.then(check);
|
||||
});
|
||||
|
||||
it('server2 - search', function () {
|
||||
return server2.request({ uri: '/-/all' })
|
||||
it('server2 - search', function() {
|
||||
return server2.request({uri: '/-/all'})
|
||||
.status(200)
|
||||
.then(check)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
.then(check);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -1,71 +1,73 @@
|
||||
require('./lib/startup')
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert')
|
||||
var crypto = require('crypto')
|
||||
require('./lib/startup');
|
||||
|
||||
let assert = require('assert');
|
||||
let crypto = require('crypto');
|
||||
|
||||
function readfile(x) {
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
return require('fs').readFileSync(__dirname + '/' + x);
|
||||
}
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
let server = process.server;
|
||||
let server2 = process.server2;
|
||||
|
||||
it('trying to fetch non-existent package / null storage', function () {
|
||||
it('trying to fetch non-existent package / null storage', function() {
|
||||
return server.get_package('test-nullstorage-nonexist')
|
||||
.status(404)
|
||||
.body_error(/no such package/)
|
||||
})
|
||||
.body_error(/no such package/);
|
||||
});
|
||||
|
||||
describe('test-nullstorage on server2', function() {
|
||||
before(function () {
|
||||
return server2.add_package('test-nullstorage2')
|
||||
})
|
||||
before(function() {
|
||||
return server2.add_package('test-nullstorage2');
|
||||
});
|
||||
|
||||
it('creating new package - server2', function(){/* test for before() */})
|
||||
it('creating new package - server2', function() {/* test for before() */});
|
||||
|
||||
it('downloading non-existent tarball', function () {
|
||||
it('downloading non-existent tarball', function() {
|
||||
return server.get_tarball('test-nullstorage2', 'blahblah')
|
||||
.status(404)
|
||||
.body_error(/no such file/)
|
||||
})
|
||||
.body_error(/no such file/);
|
||||
});
|
||||
|
||||
describe('tarball', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server2.put_tarball('test-nullstorage2', 'blahblah', readfile('fixtures/binary'))
|
||||
.status(201)
|
||||
.body_ok(/.*/)
|
||||
})
|
||||
.body_ok(/.*/);
|
||||
});
|
||||
|
||||
before(function () {
|
||||
var pkg = require('./lib/package')('test-nullstorage2')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex')
|
||||
before(function() {
|
||||
let pkg = require('./lib/package')('test-nullstorage2');
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex');
|
||||
return server2.put_version('test-nullstorage2', '0.0.1', pkg)
|
||||
.status(201)
|
||||
.body_ok(/published/)
|
||||
})
|
||||
.body_ok(/published/);
|
||||
});
|
||||
|
||||
it('uploading new tarball', function () {/* test for before() */})
|
||||
it('uploading new tarball', function() {/* test for before() */});
|
||||
|
||||
it('downloading newly created tarball', function () {
|
||||
it('downloading newly created tarball', function() {
|
||||
return server.get_tarball('test-nullstorage2', 'blahblah')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.deepEqual(body, readfile('fixtures/binary'))
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.deepEqual(body, readfile('fixtures/binary'));
|
||||
});
|
||||
});
|
||||
|
||||
it('downloading newly created package', function () {
|
||||
it('downloading newly created package', function() {
|
||||
return server.get_package('test-nullstorage2')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body.name, 'test-nullstorage2')
|
||||
assert.equal(body.versions['0.0.1'].name, 'test-nullstorage2')
|
||||
assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/test-nullstorage2/-/blahblah')
|
||||
assert.deepEqual(body['dist-tags'], {latest: '0.0.1'})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
.then(function(body) {
|
||||
assert.equal(body.name, 'test-nullstorage2');
|
||||
assert.equal(body.versions['0.0.1'].name, 'test-nullstorage2');
|
||||
assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/test-nullstorage2/-/blahblah');
|
||||
assert.deepEqual(body['dist-tags'], {latest: '0.0.1'});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,120 +1,122 @@
|
||||
require('./lib/startup')
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert')
|
||||
require('./lib/startup');
|
||||
|
||||
let assert = require('assert');
|
||||
|
||||
module.exports = function() {
|
||||
var server2 = process.server2
|
||||
let server2 = process.server2;
|
||||
|
||||
describe('authentication', function() {
|
||||
var authstr
|
||||
let authstr;
|
||||
|
||||
before(function() {
|
||||
authstr = server2.authstr
|
||||
})
|
||||
authstr = server2.authstr;
|
||||
});
|
||||
|
||||
it('should not authenticate with wrong password', function () {
|
||||
it('should not authenticate with wrong password', function() {
|
||||
return server2.auth('authtest', 'wrongpass')
|
||||
.status(409)
|
||||
.body_error('this user already exists')
|
||||
.then(function () {
|
||||
return server2.whoami()
|
||||
.then(function() {
|
||||
return server2.whoami();
|
||||
})
|
||||
.then(function (username) {
|
||||
assert.equal(username, null)
|
||||
})
|
||||
})
|
||||
.then(function(username) {
|
||||
assert.equal(username, null);
|
||||
});
|
||||
});
|
||||
|
||||
it('wrong password handled by plugin', function () {
|
||||
it('wrong password handled by plugin', function() {
|
||||
return server2.auth('authtest2', 'wrongpass')
|
||||
.status(409)
|
||||
.body_error('registration is disabled')
|
||||
.then(function () {
|
||||
return server2.whoami()
|
||||
.then(function() {
|
||||
return server2.whoami();
|
||||
})
|
||||
.then(function (username) {
|
||||
assert.equal(username, null)
|
||||
})
|
||||
})
|
||||
.then(function(username) {
|
||||
assert.equal(username, null);
|
||||
});
|
||||
});
|
||||
|
||||
it('right password handled by plugin', function () {
|
||||
it('right password handled by plugin', function() {
|
||||
return server2.auth('authtest2', 'blahblah')
|
||||
.status(201)
|
||||
.body_ok(/'authtest2'/)
|
||||
.then(function () {
|
||||
return server2.whoami()
|
||||
.then(function() {
|
||||
return server2.whoami();
|
||||
})
|
||||
.then(function (username) {
|
||||
assert.equal(username, 'authtest2')
|
||||
})
|
||||
})
|
||||
.then(function(username) {
|
||||
assert.equal(username, 'authtest2');
|
||||
});
|
||||
});
|
||||
|
||||
after(function() {
|
||||
server2.authstr = authstr
|
||||
})
|
||||
})
|
||||
server2.authstr = authstr;
|
||||
});
|
||||
});
|
||||
|
||||
describe('authorization', function() {
|
||||
var authstr
|
||||
let authstr;
|
||||
|
||||
before(function() {
|
||||
authstr = server2.authstr
|
||||
})
|
||||
authstr = server2.authstr;
|
||||
});
|
||||
|
||||
describe('authtest', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server2.auth('authtest', 'test')
|
||||
.status(201)
|
||||
.body_ok(/'authtest'/)
|
||||
})
|
||||
.body_ok(/'authtest'/);
|
||||
});
|
||||
|
||||
it('access test-auth-allow', function () {
|
||||
it('access test-auth-allow', function() {
|
||||
return server2.get_package('test-auth-allow')
|
||||
.status(404)
|
||||
.body_error('no such package available')
|
||||
})
|
||||
.body_error('no such package available');
|
||||
});
|
||||
|
||||
it('access test-auth-deny', function () {
|
||||
it('access test-auth-deny', function() {
|
||||
return server2.get_package('test-auth-deny')
|
||||
.status(403)
|
||||
.body_error("you're not allowed here")
|
||||
})
|
||||
.body_error('you\'re not allowed here');
|
||||
});
|
||||
|
||||
it('access test-auth-regular', function () {
|
||||
it('access test-auth-regular', function() {
|
||||
return server2.get_package('test-auth-regular')
|
||||
.status(404)
|
||||
.body_error('no such package available')
|
||||
})
|
||||
})
|
||||
.body_error('no such package available');
|
||||
});
|
||||
});
|
||||
|
||||
describe('authtest2', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server2.auth('authtest2', 'blahblah')
|
||||
.status(201)
|
||||
.body_ok(/'authtest2'/)
|
||||
})
|
||||
.body_ok(/'authtest2'/);
|
||||
});
|
||||
|
||||
it('access test-auth-allow', function () {
|
||||
it('access test-auth-allow', function() {
|
||||
return server2.get_package('test-auth-allow')
|
||||
.status(403)
|
||||
.body_error("i don't know anything about you")
|
||||
})
|
||||
.body_error('i don\'t know anything about you');
|
||||
});
|
||||
|
||||
it('access test-auth-deny', function () {
|
||||
it('access test-auth-deny', function() {
|
||||
return server2.get_package('test-auth-deny')
|
||||
.status(403)
|
||||
.body_error("i don't know anything about you")
|
||||
})
|
||||
.body_error('i don\'t know anything about you');
|
||||
});
|
||||
|
||||
it('access test-auth-regular', function () {
|
||||
it('access test-auth-regular', function() {
|
||||
return server2.get_package('test-auth-regular')
|
||||
.status(404)
|
||||
.body_error('no such package available')
|
||||
})
|
||||
})
|
||||
.body_error('no such package available');
|
||||
});
|
||||
});
|
||||
|
||||
after(function() {
|
||||
server2.authstr = authstr
|
||||
})
|
||||
})
|
||||
}
|
||||
server2.authstr = authstr;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,25 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = Plugin
|
||||
module.exports = Plugin;
|
||||
|
||||
function Plugin(config, stuff) {
|
||||
var self = Object.create(Plugin.prototype)
|
||||
self._config = config
|
||||
return self
|
||||
let self = Object.create(Plugin.prototype);
|
||||
self._config = config;
|
||||
return self;
|
||||
}
|
||||
|
||||
// plugin is expected to be compatible with...
|
||||
Plugin.prototype.verdaccio_version = '1.1.0'
|
||||
Plugin.prototype.verdaccio_version = '1.1.0';
|
||||
|
||||
Plugin.prototype.authenticate = function(user, password, cb) {
|
||||
var self = this
|
||||
let self = this;
|
||||
if (user !== self._config.accept_user) {
|
||||
// delegate to next plugin
|
||||
return cb(null, false)
|
||||
return cb(null, false);
|
||||
}
|
||||
if (password !== self._config.with_password) {
|
||||
var err = Error("i don't like your password")
|
||||
err.status = 403
|
||||
return cb(err)
|
||||
let err = Error('i don\'t like your password');
|
||||
err.status = 403;
|
||||
return cb(err);
|
||||
}
|
||||
return cb(null, [ user ])
|
||||
}
|
||||
return cb(null, [user]);
|
||||
};
|
||||
|
@ -1,30 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = Plugin
|
||||
module.exports = Plugin;
|
||||
|
||||
function Plugin(config, stuff) {
|
||||
var self = Object.create(Plugin.prototype)
|
||||
self._config = config
|
||||
return self
|
||||
let self = Object.create(Plugin.prototype);
|
||||
self._config = config;
|
||||
return self;
|
||||
}
|
||||
|
||||
// plugin is expected to be compatible with...
|
||||
Plugin.prototype.verdaccio_version = '1.1.0'
|
||||
Plugin.prototype.verdaccio_version = '1.1.0';
|
||||
|
||||
Plugin.prototype.allow_access = function(user, package, cb) {
|
||||
var self = this
|
||||
if (!package.handled_by_auth_plugin) {
|
||||
Plugin.prototype.allow_access = function(user, pkg, cb) {
|
||||
let self = this;
|
||||
if (!pkg.handled_by_auth_plugin) {
|
||||
// delegate to next plugin
|
||||
return cb(null, false)
|
||||
return cb(null, false);
|
||||
}
|
||||
if (user.name !== self._config.allow_user) {
|
||||
var err = Error("i don't know anything about you")
|
||||
err.status = 403
|
||||
return cb(err)
|
||||
let err = Error('i don\'t know anything about you');
|
||||
err.status = 403;
|
||||
return cb(err);
|
||||
}
|
||||
if (package.name !== self._config.to_access) {
|
||||
var err = Error("you're not allowed here")
|
||||
err.status = 403
|
||||
return cb(err)
|
||||
if (pkg.name !== self._config.to_access) {
|
||||
let err = Error('you\'re not allowed here');
|
||||
err.status = 403;
|
||||
return cb(err);
|
||||
}
|
||||
return cb(null, true)
|
||||
}
|
||||
return cb(null, true);
|
||||
};
|
||||
|
@ -1,99 +1,105 @@
|
||||
var assert = require('assert')
|
||||
var async = require('async')
|
||||
var _oksum = 0
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
let async = require('async');
|
||||
let _oksum = 0;
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
let server = process.server;
|
||||
|
||||
describe('race', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server.put_package('race', require('./lib/package')('race'))
|
||||
.status(201)
|
||||
.body_ok(/created new package/)
|
||||
})
|
||||
.body_ok(/created new package/);
|
||||
});
|
||||
|
||||
it('creating new package', function(){})
|
||||
it('creating new package', function() {});
|
||||
|
||||
it('uploading 10 same versions', function (callback) {
|
||||
var fns = []
|
||||
for (var i=0; i<10; i++) {
|
||||
it('uploading 10 same versions', function(callback) {
|
||||
let fns = [];
|
||||
for (let i=0; i<10; i++) {
|
||||
fns.push(function(cb_) {
|
||||
var data = require('./lib/package')('race')
|
||||
data.rand = Math.random()
|
||||
let data = require('./lib/package')('race');
|
||||
data.rand = Math.random();
|
||||
|
||||
var _res
|
||||
let _res;
|
||||
server.put_version('race', '0.0.1', data)
|
||||
.response(function (res) { _res = res })
|
||||
.then(function (body) {
|
||||
cb_(null, [ _res, body ])
|
||||
})
|
||||
})
|
||||
.response(function(res) {
|
||||
_res = res;
|
||||
})
|
||||
.then(function(body) {
|
||||
cb_(null, [_res, body]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async.parallel(fns, function(err, res) {
|
||||
var okcount = 0
|
||||
var failcount = 0
|
||||
let okcount = 0;
|
||||
let failcount = 0;
|
||||
|
||||
assert.equal(err, null)
|
||||
assert.equal(err, null);
|
||||
|
||||
res.forEach(function(arr) {
|
||||
var resp = arr[0]
|
||||
var body = arr[1]
|
||||
let resp = arr[0];
|
||||
let body = arr[1];
|
||||
|
||||
if (resp.statusCode === 201 && ~body.ok.indexOf('published')) okcount++
|
||||
if (resp.statusCode === 409 && ~body.error.indexOf('already present')) failcount++
|
||||
if (resp.statusCode === 503 && ~body.error.indexOf('unavailable')) failcount++
|
||||
})
|
||||
assert.equal(okcount + failcount, 10)
|
||||
assert.equal(okcount, 1)
|
||||
_oksum += okcount
|
||||
if (resp.statusCode === 201 && ~body.ok.indexOf('published')) okcount++;
|
||||
if (resp.statusCode === 409 && ~body.error.indexOf('already present')) failcount++;
|
||||
if (resp.statusCode === 503 && ~body.error.indexOf('unavailable')) failcount++;
|
||||
});
|
||||
assert.equal(okcount + failcount, 10);
|
||||
assert.equal(okcount, 1);
|
||||
_oksum += okcount;
|
||||
|
||||
callback()
|
||||
})
|
||||
})
|
||||
callback();
|
||||
});
|
||||
});
|
||||
|
||||
it('uploading 10 diff versions', function (callback) {
|
||||
var fns = []
|
||||
for (var i=0; i<10; i++) {
|
||||
;(function(i) {
|
||||
it('uploading 10 diff versions', function(callback) {
|
||||
let fns = [];
|
||||
for (let i=0; i<10; i++) {
|
||||
(function(i) {
|
||||
fns.push(function(cb_) {
|
||||
var _res
|
||||
let _res;
|
||||
server.put_version('race', '0.1.'+String(i), require('./lib/package')('race'))
|
||||
.response(function (res) { _res = res })
|
||||
.then(function (body) {
|
||||
cb_(null, [ _res, body ])
|
||||
})
|
||||
})
|
||||
})(i)
|
||||
.response(function(res) {
|
||||
_res = res;
|
||||
})
|
||||
.then(function(body) {
|
||||
cb_(null, [_res, body]);
|
||||
});
|
||||
});
|
||||
})(i);
|
||||
}
|
||||
|
||||
async.parallel(fns, function(err, res) {
|
||||
var okcount = 0
|
||||
var failcount = 0
|
||||
let okcount = 0;
|
||||
let failcount = 0;
|
||||
|
||||
assert.equal(err, null)
|
||||
assert.equal(err, null);
|
||||
res.forEach(function(arr) {
|
||||
var resp = arr[0]
|
||||
var body = arr[1]
|
||||
if (resp.statusCode === 201 && ~body.ok.indexOf('published')) okcount++
|
||||
if (resp.statusCode === 409 && ~body.error.indexOf('already present')) failcount++
|
||||
if (resp.statusCode === 503 && ~body.error.indexOf('unavailable')) failcount++
|
||||
})
|
||||
assert.equal(okcount + failcount, 10)
|
||||
assert.notEqual(okcount, 1)
|
||||
_oksum += okcount
|
||||
let resp = arr[0];
|
||||
let body = arr[1];
|
||||
if (resp.statusCode === 201 && ~body.ok.indexOf('published')) okcount++;
|
||||
if (resp.statusCode === 409 && ~body.error.indexOf('already present')) failcount++;
|
||||
if (resp.statusCode === 503 && ~body.error.indexOf('unavailable')) failcount++;
|
||||
});
|
||||
assert.equal(okcount + failcount, 10);
|
||||
assert.notEqual(okcount, 1);
|
||||
_oksum += okcount;
|
||||
|
||||
callback()
|
||||
})
|
||||
})
|
||||
callback();
|
||||
});
|
||||
});
|
||||
|
||||
after('downloading package', function () {
|
||||
after('downloading package', function() {
|
||||
return server.get_package('race')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(Object.keys(body.versions).length, _oksum)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
.then(function(body) {
|
||||
assert.equal(Object.keys(body.versions).length, _oksum);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,66 +1,70 @@
|
||||
var assert = require('assert')
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var express = process.express
|
||||
let server = process.server;
|
||||
let express = process.express;
|
||||
|
||||
describe('Racy', function() {
|
||||
var on_tarball
|
||||
let on_tarball;
|
||||
|
||||
before(function() {
|
||||
express.get('/testexp-racycrash', function(_, res) {
|
||||
res.send({
|
||||
"name": "testexp-racycrash",
|
||||
"versions": {
|
||||
"0.1.0": {
|
||||
"name": "testexp_tags",
|
||||
"version": "0.1.0",
|
||||
"dist": {
|
||||
"shasum": "fake",
|
||||
"tarball": "http://localhost:55550/testexp-racycrash/-/test.tar.gz"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
'name': 'testexp-racycrash',
|
||||
'versions': {
|
||||
'0.1.0': {
|
||||
'name': 'testexp_tags',
|
||||
'version': '0.1.0',
|
||||
'dist': {
|
||||
'shasum': 'fake',
|
||||
'tarball': 'http://localhost:55550/testexp-racycrash/-/test.tar.gz',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
express.get('/testexp-racycrash/-/test.tar.gz', function(_, res) {
|
||||
on_tarball(res)
|
||||
})
|
||||
})
|
||||
on_tarball(res);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not crash on error if client disconnects', function(_cb) {
|
||||
on_tarball = function(res) {
|
||||
res.header('content-length', 1e6)
|
||||
res.write('test test test\n')
|
||||
res.header('content-length', 1e6);
|
||||
res.write('test test test\n');
|
||||
setTimeout(function() {
|
||||
res.write('test test test\n')
|
||||
res.socket.destroy()
|
||||
cb()
|
||||
}, 200)
|
||||
}
|
||||
res.write('test test test\n');
|
||||
res.socket.destroy();
|
||||
cb();
|
||||
}, 200);
|
||||
};
|
||||
|
||||
server.request({ uri: '/testexp-racycrash/-/test.tar.gz' })
|
||||
.then(function (body) {
|
||||
assert.equal(body, 'test test test\n')
|
||||
})
|
||||
server.request({uri: '/testexp-racycrash/-/test.tar.gz'})
|
||||
.then(function(body) {
|
||||
assert.equal(body, 'test test test\n');
|
||||
});
|
||||
|
||||
function cb() {
|
||||
// test for NOT crashing
|
||||
server.request({ uri: '/testexp-racycrash' })
|
||||
server.request({uri: '/testexp-racycrash'})
|
||||
.status(200)
|
||||
.then(function () { _cb() })
|
||||
.then(function() {
|
||||
_cb();
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('should not store tarball', function () {
|
||||
it('should not store tarball', function() {
|
||||
on_tarball = function(res) {
|
||||
res.socket.destroy()
|
||||
}
|
||||
res.socket.destroy();
|
||||
};
|
||||
|
||||
return server.request({ uri: '/testexp-racycrash/-/test.tar.gz' })
|
||||
.body_error('internal server error')
|
||||
})
|
||||
})
|
||||
}
|
||||
return server.request({uri: '/testexp-racycrash/-/test.tar.gz'})
|
||||
.body_error('internal server error');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,19 +1,21 @@
|
||||
var assert = require('assert')
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
|
||||
function readfile(x) {
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
return require('fs').readFileSync(__dirname + '/' + x);
|
||||
}
|
||||
|
||||
function sha(x) {
|
||||
return require('crypto').createHash('sha1', 'binary').update(x).digest('hex')
|
||||
return require('crypto').createHash('sha1', 'binary').update(x).digest('hex');
|
||||
}
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
let server = process.server;
|
||||
let server2 = process.server2;
|
||||
|
||||
describe('test-scoped', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server.request({
|
||||
uri: '/@test%2fscoped',
|
||||
headers: {
|
||||
@ -21,58 +23,58 @@ module.exports = function() {
|
||||
},
|
||||
method: 'PUT',
|
||||
json: JSON.parse(readfile('fixtures/scoped.json')),
|
||||
}).status(201)
|
||||
})
|
||||
}).status(201);
|
||||
});
|
||||
|
||||
it('add pkg', function () {})
|
||||
it('add pkg', function() {});
|
||||
|
||||
it('server1 - tarball', function () {
|
||||
it('server1 - tarball', function() {
|
||||
return server.get_tarball('@test/scoped', 'scoped-1.0.0.tgz')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
.then(function(body) {
|
||||
// not real sha due to utf8 conversion
|
||||
assert.strictEqual(sha(body), '6e67b14e2c0e450b942e2bc8086b49e90f594790')
|
||||
})
|
||||
})
|
||||
assert.strictEqual(sha(body), '6e67b14e2c0e450b942e2bc8086b49e90f594790');
|
||||
});
|
||||
});
|
||||
|
||||
it('server2 - tarball', function () {
|
||||
it('server2 - tarball', function() {
|
||||
return server2.get_tarball('@test/scoped', 'scoped-1.0.0.tgz')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
.then(function(body) {
|
||||
// not real sha due to utf8 conversion
|
||||
assert.strictEqual(sha(body), '6e67b14e2c0e450b942e2bc8086b49e90f594790')
|
||||
})
|
||||
})
|
||||
assert.strictEqual(sha(body), '6e67b14e2c0e450b942e2bc8086b49e90f594790');
|
||||
});
|
||||
});
|
||||
|
||||
it('server1 - package', function () {
|
||||
it('server1 - package', function() {
|
||||
return server.get_package('@test/scoped')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body.name, '@test/scoped')
|
||||
assert.equal(body.versions['1.0.0'].name, '@test/scoped')
|
||||
assert.equal(body.versions['1.0.0'].dist.tarball, 'http://localhost:55551/@test%2fscoped/-/scoped-1.0.0.tgz')
|
||||
assert.deepEqual(body['dist-tags'], {latest: '1.0.0'})
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.equal(body.name, '@test/scoped');
|
||||
assert.equal(body.versions['1.0.0'].name, '@test/scoped');
|
||||
assert.equal(body.versions['1.0.0'].dist.tarball, 'http://localhost:55551/@test%2fscoped/-/scoped-1.0.0.tgz');
|
||||
assert.deepEqual(body['dist-tags'], {latest: '1.0.0'});
|
||||
});
|
||||
});
|
||||
|
||||
it('server2 - package', function () {
|
||||
it('server2 - package', function() {
|
||||
return server2.get_package('@test/scoped')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body.name, '@test/scoped')
|
||||
assert.equal(body.versions['1.0.0'].name, '@test/scoped')
|
||||
assert.equal(body.versions['1.0.0'].dist.tarball, 'http://localhost:55552/@test%2fscoped/-/scoped-1.0.0.tgz')
|
||||
assert.deepEqual(body['dist-tags'], {latest: '1.0.0'})
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.equal(body.name, '@test/scoped');
|
||||
assert.equal(body.versions['1.0.0'].name, '@test/scoped');
|
||||
assert.equal(body.versions['1.0.0'].dist.tarball, 'http://localhost:55552/@test%2fscoped/-/scoped-1.0.0.tgz');
|
||||
assert.deepEqual(body['dist-tags'], {latest: '1.0.0'});
|
||||
});
|
||||
});
|
||||
|
||||
it('server2 - nginx workaround', function () {
|
||||
return server2.request({ uri: '/@test/scoped/1.0.0' })
|
||||
it('server2 - nginx workaround', function() {
|
||||
return server2.request({uri: '/@test/scoped/1.0.0'})
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body.name, '@test/scoped')
|
||||
assert.equal(body.dist.tarball, 'http://localhost:55552/@test%2fscoped/-/scoped-1.0.0.tgz')
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
.then(function(body) {
|
||||
assert.equal(body.name, '@test/scoped');
|
||||
assert.equal(body.dist.tarball, 'http://localhost:55552/@test%2fscoped/-/scoped-1.0.0.tgz');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -1,70 +1,72 @@
|
||||
var assert = require('assert')
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
let server = process.server;
|
||||
|
||||
describe('Security', function() {
|
||||
before(function() {
|
||||
return server.add_package('testpkg-sec')
|
||||
})
|
||||
return server.add_package('testpkg-sec');
|
||||
});
|
||||
|
||||
it('bad pkg #1', function () {
|
||||
it('bad pkg #1', function() {
|
||||
return server.get_package('package.json')
|
||||
.status(403)
|
||||
.body_error(/invalid package/)
|
||||
})
|
||||
.body_error(/invalid package/);
|
||||
});
|
||||
|
||||
it('bad pkg #2', function () {
|
||||
it('bad pkg #2', function() {
|
||||
return server.get_package('__proto__')
|
||||
.status(403)
|
||||
.body_error(/invalid package/)
|
||||
})
|
||||
.body_error(/invalid package/);
|
||||
});
|
||||
|
||||
it('__proto__, connect stuff', function () {
|
||||
return server.request({ uri: '/testpkg-sec?__proto__=1' })
|
||||
.then(function (body) {
|
||||
it('__proto__, connect stuff', function() {
|
||||
return server.request({uri: '/testpkg-sec?__proto__=1'})
|
||||
.then(function(body) {
|
||||
// test for NOT outputting stack trace
|
||||
assert(!body || typeof(body) === 'object' || body.indexOf('node_modules') === -1)
|
||||
assert(!body || typeof(body) === 'object' || body.indexOf('node_modules') === -1);
|
||||
|
||||
// test for NOT crashing
|
||||
return server.request({ uri: '/testpkg-sec' }).status(200)
|
||||
})
|
||||
})
|
||||
return server.request({uri: '/testpkg-sec'}).status(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('do not return package.json as an attachment', function () {
|
||||
return server.request({ uri: '/testpkg-sec/-/package.json' })
|
||||
it('do not return package.json as an attachment', function() {
|
||||
return server.request({uri: '/testpkg-sec/-/package.json'})
|
||||
.status(403)
|
||||
.body_error(/invalid filename/)
|
||||
})
|
||||
.body_error(/invalid filename/);
|
||||
});
|
||||
|
||||
it('silly things - reading #1', function () {
|
||||
return server.request({ uri: '/testpkg-sec/-/../../../../../../../../etc/passwd' })
|
||||
.status(404)
|
||||
})
|
||||
it('silly things - reading #1', function() {
|
||||
return server.request({uri: '/testpkg-sec/-/../../../../../../../../etc/passwd'})
|
||||
.status(404);
|
||||
});
|
||||
|
||||
it('silly things - reading #2', function () {
|
||||
return server.request({ uri: '/testpkg-sec/-/%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd' })
|
||||
it('silly things - reading #2', function() {
|
||||
return server.request({uri: '/testpkg-sec/-/%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd'})
|
||||
.status(403)
|
||||
.body_error(/invalid filename/)
|
||||
})
|
||||
.body_error(/invalid filename/);
|
||||
});
|
||||
|
||||
it('silly things - writing #1', function () {
|
||||
it('silly things - writing #1', function() {
|
||||
return server.put_tarball('testpkg-sec', 'package.json', '{}')
|
||||
.status(403)
|
||||
.body_error(/invalid filename/)
|
||||
})
|
||||
.body_error(/invalid filename/);
|
||||
});
|
||||
|
||||
it('silly things - writing #3', function () {
|
||||
it('silly things - writing #3', function() {
|
||||
return server.put_tarball('testpkg-sec', 'node_modules', '{}')
|
||||
.status(403)
|
||||
.body_error(/invalid filename/)
|
||||
})
|
||||
.body_error(/invalid filename/);
|
||||
});
|
||||
|
||||
it('silly things - writing #4', function () {
|
||||
it('silly things - writing #4', function() {
|
||||
return server.put_tarball('testpkg-sec', '../testpkg.tgz', '{}')
|
||||
.status(403)
|
||||
.body_error(/invalid filename/)
|
||||
})
|
||||
})
|
||||
}
|
||||
.body_error(/invalid filename/);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,168 +1,170 @@
|
||||
var assert = require('assert')
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
function readfile(x) {
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
return require('fs').readFileSync(__dirname + '/' + x);
|
||||
}
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var express = process.express
|
||||
let server = process.server;
|
||||
let express = process.express;
|
||||
|
||||
it('tags - testing for 404', function () {
|
||||
it('tags - testing for 404', function() {
|
||||
return server.get_package('testexp_tags')
|
||||
// shouldn't exist yet
|
||||
.status(404)
|
||||
.body_error(/no such package/)
|
||||
})
|
||||
.body_error(/no such package/);
|
||||
});
|
||||
|
||||
describe('tags', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
express.get('/testexp_tags', function(req, res) {
|
||||
var f = readfile('fixtures/tags.json').toString().replace(/__NAME__/g, 'testexp_tags')
|
||||
res.send(JSON.parse(f))
|
||||
})
|
||||
})
|
||||
let f = readfile('fixtures/tags.json').toString().replace(/__NAME__/g, 'testexp_tags');
|
||||
res.send(JSON.parse(f));
|
||||
});
|
||||
});
|
||||
|
||||
it('fetching package again', function () {
|
||||
it('fetching package again', function() {
|
||||
return server.get_package('testexp_tags')
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(typeof(body.versions['1.1']), 'object')
|
||||
assert.equal(body['dist-tags'].something, '0.1.1alpha')
|
||||
.then(function(body) {
|
||||
assert.equal(typeof(body.versions['1.1']), 'object');
|
||||
assert.equal(body['dist-tags'].something, '0.1.1alpha');
|
||||
// note: 5.4.3 is invalid tag, 0.1.3alpha is highest semver
|
||||
assert.equal(body['dist-tags'].latest, '0.1.3alpha')
|
||||
assert.equal(body['dist-tags'].bad, null)
|
||||
})
|
||||
assert.equal(body['dist-tags'].latest, '0.1.3alpha');
|
||||
assert.equal(body['dist-tags'].bad, null);
|
||||
});
|
||||
})
|
||||
|
||||
;['0.1.1alpha', '0.1.1-alpha', '0000.00001.001-alpha'].forEach(function(ver) {
|
||||
it('fetching '+ver, function () {
|
||||
return server.request({uri:'/testexp_tags/'+ver})
|
||||
it('fetching '+ver, function() {
|
||||
return server.request({uri: '/testexp_tags/'+ver})
|
||||
.status(200)
|
||||
.then(function (body) {
|
||||
assert.equal(body.version, '0.1.1alpha')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.then(function(body) {
|
||||
assert.equal(body.version, '0.1.1alpha');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('dist-tags methods', function() {
|
||||
before(function () {
|
||||
before(function() {
|
||||
express.get('/testexp_tags2', function(req, res) {
|
||||
var f = readfile('fixtures/tags.json').toString().replace(/__NAME__/g, 'testexp_tags2')
|
||||
res.send(JSON.parse(f))
|
||||
})
|
||||
})
|
||||
let f = readfile('fixtures/tags.json').toString().replace(/__NAME__/g, 'testexp_tags2');
|
||||
res.send(JSON.parse(f));
|
||||
});
|
||||
});
|
||||
|
||||
// populate cache
|
||||
before(function () {
|
||||
before(function() {
|
||||
return server.get_package('testexp_tags2')
|
||||
.status(200)
|
||||
})
|
||||
.status(200);
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
beforeEach(function() {
|
||||
return server.request({
|
||||
method: 'PUT',
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
json: {
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
json: {
|
||||
foo: '0.1.0',
|
||||
bar: '0.1.1alpha',
|
||||
baz: '0.1.2',
|
||||
},
|
||||
}).status(201).body_ok(/tags updated/)
|
||||
})
|
||||
}).status(201).body_ok(/tags updated/);
|
||||
});
|
||||
|
||||
it('fetching tags', function () {
|
||||
it('fetching tags', function() {
|
||||
return server.request({
|
||||
method: 'GET',
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
}).status(200).then(function (body) {
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
}).status(200).then(function(body) {
|
||||
assert.deepEqual(body,
|
||||
{ foo: '0.1.0',
|
||||
{foo: '0.1.0',
|
||||
bar: '0.1.1alpha',
|
||||
baz: '0.1.2',
|
||||
latest: '0.1.3alpha' })
|
||||
})
|
||||
})
|
||||
latest: '0.1.3alpha'});
|
||||
});
|
||||
});
|
||||
|
||||
it('merging tags', function () {
|
||||
it('merging tags', function() {
|
||||
return server.request({
|
||||
method: 'POST',
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
json: {
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
json: {
|
||||
foo: '0.1.2',
|
||||
quux: '0.1.0',
|
||||
},
|
||||
}).status(201).body_ok(/updated/).then(function () {
|
||||
}).status(201).body_ok(/updated/).then(function() {
|
||||
return server.request({
|
||||
method: 'GET',
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
}).status(200).then(function (body) {
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
}).status(200).then(function(body) {
|
||||
assert.deepEqual(body,
|
||||
{ foo: '0.1.2',
|
||||
{foo: '0.1.2',
|
||||
bar: '0.1.1alpha',
|
||||
baz: '0.1.2',
|
||||
quux: '0.1.0',
|
||||
latest: '0.1.3alpha' })
|
||||
})
|
||||
})
|
||||
})
|
||||
latest: '0.1.3alpha'});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('replacing tags', function () {
|
||||
it('replacing tags', function() {
|
||||
return server.request({
|
||||
method: 'PUT',
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
json: {
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
json: {
|
||||
foo: '0.1.2',
|
||||
quux: '0.1.0',
|
||||
},
|
||||
}).status(201).body_ok(/updated/).then(function () {
|
||||
}).status(201).body_ok(/updated/).then(function() {
|
||||
return server.request({
|
||||
method: 'GET',
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
}).status(200).then(function (body) {
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
}).status(200).then(function(body) {
|
||||
assert.deepEqual(body,
|
||||
{ foo: '0.1.2',
|
||||
{foo: '0.1.2',
|
||||
quux: '0.1.0',
|
||||
latest: '0.1.3alpha' })
|
||||
})
|
||||
})
|
||||
})
|
||||
latest: '0.1.3alpha'});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('adding a tag', function () {
|
||||
it('adding a tag', function() {
|
||||
return server.request({
|
||||
method: 'PUT',
|
||||
uri: '/-/package/testexp_tags2/dist-tags/foo',
|
||||
json: '0.1.3alpha',
|
||||
}).status(201).body_ok(/tagged/).then(function () {
|
||||
uri: '/-/package/testexp_tags2/dist-tags/foo',
|
||||
json: '0.1.3alpha',
|
||||
}).status(201).body_ok(/tagged/).then(function() {
|
||||
return server.request({
|
||||
method: 'GET',
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
}).status(200).then(function (body) {
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
}).status(200).then(function(body) {
|
||||
assert.deepEqual(body,
|
||||
{ foo: '0.1.3alpha',
|
||||
{foo: '0.1.3alpha',
|
||||
bar: '0.1.1alpha',
|
||||
baz: '0.1.2',
|
||||
latest: '0.1.3alpha' })
|
||||
})
|
||||
})
|
||||
})
|
||||
latest: '0.1.3alpha'});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('removing a tag', function () {
|
||||
it('removing a tag', function() {
|
||||
return server.request({
|
||||
method: 'DELETE',
|
||||
uri: '/-/package/testexp_tags2/dist-tags/foo',
|
||||
}).status(201).body_ok(/removed/).then(function () {
|
||||
uri: '/-/package/testexp_tags2/dist-tags/foo',
|
||||
}).status(201).body_ok(/removed/).then(function() {
|
||||
return server.request({
|
||||
method: 'GET',
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
}).status(200).then(function (body) {
|
||||
uri: '/-/package/testexp_tags2/dist-tags',
|
||||
}).status(200).then(function(body) {
|
||||
assert.deepEqual(body,
|
||||
{ bar: '0.1.1alpha',
|
||||
{bar: '0.1.1alpha',
|
||||
baz: '0.1.2',
|
||||
latest: '0.1.3alpha' })
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
latest: '0.1.3alpha'});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@ -1,8 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
describe('config.yaml', function() {
|
||||
it('should be parseable', function() {
|
||||
var source = require('fs').readFileSync(__dirname + '/../../conf/default.yaml', 'utf8')
|
||||
require('js-yaml').safeLoad(source)
|
||||
})
|
||||
})
|
||||
let source = require('fs').readFileSync(__dirname + '/../../conf/default.yaml', 'utf8');
|
||||
require('js-yaml').safeLoad(source);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,42 +1,44 @@
|
||||
var assert = require('assert')
|
||||
var parse = require('../../lib/utils').parse_address
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
let parse = require('../../lib/utils').parse_address;
|
||||
|
||||
describe('Parse address', function() {
|
||||
function addTest(what, proto, host, port) {
|
||||
it(what, function() {
|
||||
if (proto === null) {
|
||||
assert.strictEqual(parse(what), null)
|
||||
assert.strictEqual(parse(what), null);
|
||||
} else if (port) {
|
||||
assert.deepEqual(parse(what), {
|
||||
proto: proto,
|
||||
host: host,
|
||||
port: port,
|
||||
})
|
||||
});
|
||||
} else {
|
||||
assert.deepEqual(parse(what), {
|
||||
proto: proto,
|
||||
path: host,
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
addTest('4873', 'http', 'localhost', '4873')
|
||||
addTest(':4873', 'http', 'localhost', '4873')
|
||||
addTest('blah:4873', 'http', 'blah', '4873')
|
||||
addTest('http://:4873', 'http', 'localhost', '4873')
|
||||
addTest('https::4873', 'https', 'localhost', '4873')
|
||||
addTest('https:blah:4873', 'https', 'blah', '4873')
|
||||
addTest('https://blah:4873/', 'https', 'blah', '4873')
|
||||
addTest('[::1]:4873', 'http', '::1', '4873')
|
||||
addTest('https:[::1]:4873', 'https', '::1', '4873')
|
||||
addTest('4873', 'http', 'localhost', '4873');
|
||||
addTest(':4873', 'http', 'localhost', '4873');
|
||||
addTest('blah:4873', 'http', 'blah', '4873');
|
||||
addTest('http://:4873', 'http', 'localhost', '4873');
|
||||
addTest('https::4873', 'https', 'localhost', '4873');
|
||||
addTest('https:blah:4873', 'https', 'blah', '4873');
|
||||
addTest('https://blah:4873/', 'https', 'blah', '4873');
|
||||
addTest('[::1]:4873', 'http', '::1', '4873');
|
||||
addTest('https:[::1]:4873', 'https', '::1', '4873');
|
||||
|
||||
addTest('unix:/tmp/foo.sock', 'http', '/tmp/foo.sock')
|
||||
addTest('http:unix:foo.sock', 'http', 'foo.sock')
|
||||
addTest('https://unix:foo.sock', 'https', 'foo.sock')
|
||||
addTest('unix:/tmp/foo.sock', 'http', '/tmp/foo.sock');
|
||||
addTest('http:unix:foo.sock', 'http', 'foo.sock');
|
||||
addTest('https://unix:foo.sock', 'https', 'foo.sock');
|
||||
|
||||
addTest('blah', null)
|
||||
addTest('blah://4873', null)
|
||||
addTest('https://blah:4873///', null)
|
||||
addTest('unix:1234', 'http', 'unix', '1234') // not unix socket
|
||||
})
|
||||
addTest('blah', null);
|
||||
addTest('blah://4873', null);
|
||||
addTest('https://blah:4873///', null);
|
||||
addTest('unix:1234', 'http', 'unix', '1234'); // not unix socket
|
||||
});
|
||||
|
@ -1,17 +1,19 @@
|
||||
var ReadTarball = require('../../lib/streams').ReadTarballStream
|
||||
'use strict';
|
||||
|
||||
let ReadTarball = require('../../lib/streams').ReadTarballStream;
|
||||
|
||||
describe('mystreams', function() {
|
||||
it('should delay events', function(cb) {
|
||||
var test = new ReadTarball()
|
||||
test.abort()
|
||||
let test = new ReadTarball();
|
||||
test.abort();
|
||||
setTimeout(function() {
|
||||
test.abort = function() {
|
||||
cb()
|
||||
}
|
||||
cb();
|
||||
};
|
||||
test.abort = function() {
|
||||
throw Error('fail')
|
||||
}
|
||||
}, 10)
|
||||
})
|
||||
})
|
||||
throw Error('fail');
|
||||
};
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,88 +1,90 @@
|
||||
var assert = require('assert')
|
||||
var Storage = require('../../lib/up-storage')
|
||||
'use strict';
|
||||
|
||||
require('../../lib/logger').setup([])
|
||||
let assert = require('assert');
|
||||
let Storage = require('../../lib/up-storage');
|
||||
|
||||
require('../../lib/logger').setup([]);
|
||||
|
||||
function setup(host, config, mainconfig) {
|
||||
config.url = host
|
||||
return Storage(config, mainconfig)
|
||||
config.url = host;
|
||||
return new Storage(config, mainconfig);
|
||||
}
|
||||
|
||||
describe('Use proxy', function() {
|
||||
it('should work fine without proxy', function() {
|
||||
var x = setup('http://x/x', {}, {})
|
||||
assert.equal(x.proxy, null)
|
||||
})
|
||||
let x = setup('http://x/x', {}, {});
|
||||
assert.equal(x.proxy, null);
|
||||
});
|
||||
|
||||
it('local config should take priority', function() {
|
||||
var x = setup('http://x/x', {http_proxy: '123'}, {http_proxy: '456'})
|
||||
assert.equal(x.proxy, '123')
|
||||
})
|
||||
let x = setup('http://x/x', {http_proxy: '123'}, {http_proxy: '456'});
|
||||
assert.equal(x.proxy, '123');
|
||||
});
|
||||
|
||||
it('no_proxy is invalid', function() {
|
||||
var x = setup('http://x/x', {http_proxy: '123', no_proxy: false}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://x/x', {http_proxy: '123', no_proxy: null}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://x/x', {http_proxy: '123', no_proxy: []}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://x/x', {http_proxy: '123', no_proxy: ''}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
})
|
||||
let x = setup('http://x/x', {http_proxy: '123', no_proxy: false}, {});
|
||||
assert.equal(x.proxy, '123');
|
||||
x = setup('http://x/x', {http_proxy: '123', no_proxy: null}, {});
|
||||
assert.equal(x.proxy, '123');
|
||||
x = setup('http://x/x', {http_proxy: '123', no_proxy: []}, {});
|
||||
assert.equal(x.proxy, '123');
|
||||
x = setup('http://x/x', {http_proxy: '123', no_proxy: ''}, {});
|
||||
assert.equal(x.proxy, '123');
|
||||
});
|
||||
|
||||
it('no_proxy - simple/include', function() {
|
||||
var x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'localhost'})
|
||||
assert.equal(x.proxy, undefined)
|
||||
})
|
||||
let x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'localhost'});
|
||||
assert.equal(x.proxy, undefined);
|
||||
});
|
||||
|
||||
it('no_proxy - simple/not', function() {
|
||||
var x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'blah'})
|
||||
assert.equal(x.proxy, '123')
|
||||
})
|
||||
let x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'blah'});
|
||||
assert.equal(x.proxy, '123');
|
||||
});
|
||||
|
||||
it('no_proxy - various, single string', function() {
|
||||
var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: 'blah'})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://blah.blah', {}, {http_proxy: '123', no_proxy: 'blah'})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://blahblah', {}, {http_proxy: '123', no_proxy: '.blah'})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://blah.blah', {http_proxy: '123', no_proxy: '.blah'}, {})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://blah', {http_proxy: '123', no_proxy: '.blah'}, {})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://blahh', {http_proxy: '123', no_proxy: 'blah'}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
})
|
||||
let x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: 'blah'});
|
||||
assert.equal(x.proxy, '123');
|
||||
x = setup('http://blah.blah', {}, {http_proxy: '123', no_proxy: 'blah'});
|
||||
assert.equal(x.proxy, null);
|
||||
x = setup('http://blahblah', {}, {http_proxy: '123', no_proxy: '.blah'});
|
||||
assert.equal(x.proxy, '123');
|
||||
x = setup('http://blah.blah', {http_proxy: '123', no_proxy: '.blah'}, {});
|
||||
assert.equal(x.proxy, null);
|
||||
x = setup('http://blah', {http_proxy: '123', no_proxy: '.blah'}, {});
|
||||
assert.equal(x.proxy, null);
|
||||
x = setup('http://blahh', {http_proxy: '123', no_proxy: 'blah'}, {});
|
||||
assert.equal(x.proxy, '123');
|
||||
});
|
||||
|
||||
it('no_proxy - various, array', function() {
|
||||
var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://blah.blah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://blah.foo', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://foo.baz', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: ['foo','bar','blah']})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://blah.blah', {http_proxy: '123'}, {no_proxy: ['foo','bar','blah']})
|
||||
assert.equal(x.proxy, null)
|
||||
})
|
||||
let x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'});
|
||||
assert.equal(x.proxy, '123');
|
||||
x = setup('http://blah.blah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'});
|
||||
assert.equal(x.proxy, null);
|
||||
x = setup('http://blah.foo', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'});
|
||||
assert.equal(x.proxy, null);
|
||||
x = setup('http://foo.baz', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'});
|
||||
assert.equal(x.proxy, '123');
|
||||
x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: ['foo', 'bar', 'blah']});
|
||||
assert.equal(x.proxy, '123');
|
||||
x = setup('http://blah.blah', {http_proxy: '123'}, {no_proxy: ['foo', 'bar', 'blah']});
|
||||
assert.equal(x.proxy, null);
|
||||
});
|
||||
|
||||
it('no_proxy - hostport', function() {
|
||||
var x = setup('http://localhost:80', {http_proxy: '123'}, {no_proxy: 'localhost'})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://localhost:8080', {http_proxy: '123'}, {no_proxy: 'localhost'})
|
||||
assert.equal(x.proxy, null)
|
||||
})
|
||||
let x = setup('http://localhost:80', {http_proxy: '123'}, {no_proxy: 'localhost'});
|
||||
assert.equal(x.proxy, null);
|
||||
x = setup('http://localhost:8080', {http_proxy: '123'}, {no_proxy: 'localhost'});
|
||||
assert.equal(x.proxy, null);
|
||||
});
|
||||
|
||||
it('no_proxy - secure', function() {
|
||||
var x = setup('https://something', {http_proxy: '123'}, {})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('https://something', {https_proxy: '123'}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('https://something', {http_proxy: '456', https_proxy: '123'}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
})
|
||||
})
|
||||
let x = setup('https://something', {http_proxy: '123'}, {});
|
||||
assert.equal(x.proxy, null);
|
||||
x = setup('https://something', {https_proxy: '123'}, {});
|
||||
assert.equal(x.proxy, '123');
|
||||
x = setup('https://something', {http_proxy: '456', https_proxy: '123'}, {});
|
||||
assert.equal(x.proxy, '123');
|
||||
});
|
||||
});
|
||||
|
@ -1,34 +1,36 @@
|
||||
var assert = require('assert')
|
||||
var parse_interval = require('../../lib/config').parse_interval
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
let parse_interval = require('../../lib/config').parse_interval;
|
||||
|
||||
describe('Parse interval', function() {
|
||||
function add_test(str, res) {
|
||||
it('parse ' + str, function() {
|
||||
if (res === null) {
|
||||
assert.throws(function() {
|
||||
console.log(parse_interval(str))
|
||||
})
|
||||
console.log(parse_interval(str));
|
||||
});
|
||||
} else {
|
||||
assert.strictEqual(parse_interval(str), res)
|
||||
assert.strictEqual(parse_interval(str), res);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
add_test(12345, 12345000)
|
||||
add_test('1000', 1000000)
|
||||
add_test('1.5s', 1500)
|
||||
add_test('25ms', 25)
|
||||
add_test('2m', 2*1000*60)
|
||||
add_test('3h', 3*1000*60*60)
|
||||
add_test('0.5d', 0.5*1000*60*60*24)
|
||||
add_test('0.5w', 0.5*1000*60*60*24*7)
|
||||
add_test('1M', 1000*60*60*24*30)
|
||||
add_test('5s 20ms', 5020)
|
||||
add_test('1y', 1000*60*60*24*365)
|
||||
add_test('1y 5', null)
|
||||
add_test('1m 1m', null)
|
||||
add_test('1m 1y', null)
|
||||
add_test('1y 1M 1w 1d 1h 1m 1s 1ms', 34822861001)
|
||||
add_test(' 5s 25ms ', 5025)
|
||||
})
|
||||
add_test(12345, 12345000);
|
||||
add_test('1000', 1000000);
|
||||
add_test('1.5s', 1500);
|
||||
add_test('25ms', 25);
|
||||
add_test('2m', 2*1000*60);
|
||||
add_test('3h', 3*1000*60*60);
|
||||
add_test('0.5d', 0.5*1000*60*60*24);
|
||||
add_test('0.5w', 0.5*1000*60*60*24*7);
|
||||
add_test('1M', 1000*60*60*24*30);
|
||||
add_test('5s 20ms', 5020);
|
||||
add_test('1y', 1000*60*60*24*365);
|
||||
add_test('1y 5', null);
|
||||
add_test('1m 1m', null);
|
||||
add_test('1m 1y', null);
|
||||
add_test('1y 1M 1w 1d 1h 1m 1s 1ms', 34822861001);
|
||||
add_test(' 5s 25ms ', 5025);
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
var config = {
|
||||
'use strict';
|
||||
|
||||
let config = {
|
||||
storage: __dirname + '/test-storage',
|
||||
packages: {
|
||||
'*': {
|
||||
@ -6,8 +8,8 @@ var config = {
|
||||
},
|
||||
},
|
||||
logs: [
|
||||
{type: 'stdout', format: 'pretty', level: 'fatal'}
|
||||
{type: 'stdout', format: 'pretty', level: 'fatal'},
|
||||
],
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
@ -3,6 +3,6 @@ function ValidVerdaccioPlugin() {
|
||||
// not valid method
|
||||
authenticate__: function(){}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = ValidVerdaccioPlugin;
|
||||
|
@ -2,6 +2,6 @@ function ValidVerdaccioPlugin() {
|
||||
return {
|
||||
authenticate: function(){}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = ValidVerdaccioPlugin;
|
||||
|
@ -1,39 +1,40 @@
|
||||
var assert = require('assert');
|
||||
var Search = require('../../lib/search');
|
||||
var Storage = require('../../lib/storage');
|
||||
var config_hash = require('./partials/config');
|
||||
var Config = require('../../lib/config');
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
let Search = require('../../lib/search');
|
||||
let Storage = require('../../lib/storage');
|
||||
let config_hash = require('./partials/config');
|
||||
let Config = require('../../lib/config');
|
||||
|
||||
require('../../lib/logger').setup([]);
|
||||
|
||||
var packages = [
|
||||
let packages = [
|
||||
{
|
||||
name: 'test1',
|
||||
description: 'description',
|
||||
_npmUser: {
|
||||
name: 'test_user',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
description: 'description',
|
||||
_npmUser: {
|
||||
name: 'test_user',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'test3',
|
||||
description: 'description',
|
||||
_npmUser: {
|
||||
name: 'test_user',
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
describe('search', function() {
|
||||
|
||||
before(function() {
|
||||
var config = Config(config_hash);
|
||||
let config = Config(config_hash);
|
||||
this.storage = new Storage(config);
|
||||
Search.configureStorage(this.storage);
|
||||
packages.map(function(item) {
|
||||
@ -42,24 +43,23 @@ describe('search', function() {
|
||||
});
|
||||
|
||||
it('search query item', function() {
|
||||
var result = Search.query('t');
|
||||
let result = Search.query('t');
|
||||
assert(result.length === 3);
|
||||
});
|
||||
|
||||
it('search remove item', function() {
|
||||
var item = {
|
||||
let item = {
|
||||
name: 'test6',
|
||||
description: 'description',
|
||||
_npmUser: {
|
||||
name: 'test_user',
|
||||
}
|
||||
},
|
||||
};
|
||||
Search.add(item);
|
||||
var result = Search.query('test6');
|
||||
let result = Search.query('test6');
|
||||
assert(result.length === 1);
|
||||
Search.remove(item.name);
|
||||
var result = Search.query('test6');
|
||||
result = Search.query('test6');
|
||||
assert(result.length === 0);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,41 +1,43 @@
|
||||
var assert = require('assert')
|
||||
var semver_sort = require('../../lib/utils').semver_sort
|
||||
var merge = require('../../lib/storage')._merge_versions
|
||||
'use strict';
|
||||
|
||||
require('../../lib/logger').setup([])
|
||||
let assert = require('assert');
|
||||
let semver_sort = require('../../lib/utils').semver_sort;
|
||||
let merge = require('../../lib/storage')._merge_versions;
|
||||
|
||||
require('../../lib/logger').setup([]);
|
||||
|
||||
describe('Merge', function() {
|
||||
it('simple', function() {
|
||||
var x = {
|
||||
versions: {a:1,b:1,c:1},
|
||||
let x = {
|
||||
'versions': {a: 1, b: 1, c: 1},
|
||||
'dist-tags': {},
|
||||
}
|
||||
merge(x, {versions: {a:2,q:2}})
|
||||
};
|
||||
merge(x, {versions: {a: 2, q: 2}});
|
||||
assert.deepEqual(x, {
|
||||
versions: {a:1,b:1,c:1,q:2},
|
||||
'versions': {a: 1, b: 1, c: 1, q: 2},
|
||||
'dist-tags': {},
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('dist-tags - compat', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {q:'1.1.1',w:'2.2.2'},
|
||||
}
|
||||
merge(x, {'dist-tags':{q:'2.2.2',w:'3.3.3',t:'4.4.4'}})
|
||||
let x = {
|
||||
'versions': {},
|
||||
'dist-tags': {q: '1.1.1', w: '2.2.2'},
|
||||
};
|
||||
merge(x, {'dist-tags': {q: '2.2.2', w: '3.3.3', t: '4.4.4'}});
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {q:'2.2.2',w:'3.3.3',t:'4.4.4'},
|
||||
})
|
||||
})
|
||||
'versions': {},
|
||||
'dist-tags': {q: '2.2.2', w: '3.3.3', t: '4.4.4'},
|
||||
});
|
||||
});
|
||||
|
||||
it('semver_sort', function() {
|
||||
assert.deepEqual(semver_sort(['1.2.3','1.2','1.2.3a','1.2.3c','1.2.3-b']),
|
||||
[ '1.2.3a',
|
||||
assert.deepEqual(semver_sort(['1.2.3', '1.2', '1.2.3a', '1.2.3c', '1.2.3-b']),
|
||||
['1.2.3a',
|
||||
'1.2.3-b',
|
||||
'1.2.3c',
|
||||
'1.2.3' ]
|
||||
)
|
||||
})
|
||||
})
|
||||
'1.2.3']
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,44 +1,45 @@
|
||||
var assert = require('assert')
|
||||
var tag_version = require('../../lib/utils').tag_version
|
||||
'use strict';
|
||||
|
||||
require('../../lib/logger').setup([])
|
||||
let assert = require('assert');
|
||||
let tag_version = require('../../lib/utils').tag_version;
|
||||
|
||||
require('../../lib/logger').setup([]);
|
||||
|
||||
describe('tag_version', function() {
|
||||
it('add new one', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
let x = {
|
||||
'versions': {},
|
||||
'dist-tags': {},
|
||||
}
|
||||
assert(tag_version(x, '1.1.1', 'foo', {}))
|
||||
};
|
||||
assert(tag_version(x, '1.1.1', 'foo', {}));
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'versions': {},
|
||||
'dist-tags': {foo: '1.1.1'},
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('add (compat)', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
let x = {
|
||||
'versions': {},
|
||||
'dist-tags': {foo: '1.1.0'},
|
||||
}
|
||||
assert(tag_version(x, '1.1.1', 'foo'))
|
||||
};
|
||||
assert(tag_version(x, '1.1.1', 'foo'));
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'versions': {},
|
||||
'dist-tags': {foo: '1.1.1'},
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('add fresh tag', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
let x = {
|
||||
'versions': {},
|
||||
'dist-tags': {foo: '1.1.0'},
|
||||
}
|
||||
assert(tag_version(x, '1.1.1', 'foo'))
|
||||
};
|
||||
assert(tag_version(x, '1.1.1', 'foo'));
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'versions': {},
|
||||
'dist-tags': {foo: '1.1.1'},
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,45 +1,47 @@
|
||||
var assert = require('assert')
|
||||
var express = require('express')
|
||||
var request = require('request')
|
||||
var rimraf = require('rimraf')
|
||||
var verdaccio = require('../../')
|
||||
var config = require('./partials/config');
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
let express = require('express');
|
||||
let request = require('request');
|
||||
let rimraf = require('rimraf');
|
||||
let verdaccio = require('../../');
|
||||
let config = require('./partials/config');
|
||||
|
||||
describe('toplevel', function() {
|
||||
var port
|
||||
let port;
|
||||
|
||||
before(function(done) {
|
||||
rimraf(__dirname + '/test-storage', done)
|
||||
})
|
||||
rimraf(__dirname + '/test-storage', done);
|
||||
});
|
||||
|
||||
before(function(done) {
|
||||
var app = express()
|
||||
app.use(verdaccio(config))
|
||||
let app = express();
|
||||
app.use(verdaccio(config));
|
||||
|
||||
var server = require('http').createServer(app)
|
||||
let server = require('http').createServer(app);
|
||||
server.listen(0, function() {
|
||||
port = server.address().port
|
||||
done()
|
||||
})
|
||||
})
|
||||
port = server.address().port;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should respond on /', function(done) {
|
||||
request({
|
||||
url: 'http://localhost:' + port + '/',
|
||||
}, function(err, res, body) {
|
||||
assert.equal(err, null)
|
||||
assert(body.match(/<title>Verdaccio<\/title>/))
|
||||
done()
|
||||
})
|
||||
})
|
||||
assert.equal(err, null);
|
||||
assert(body.match(/<title>Verdaccio<\/title>/));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should respond on /whatever', function(done) {
|
||||
request({
|
||||
url: 'http://localhost:' + port + '/whatever',
|
||||
}, function(err, res, body) {
|
||||
assert.equal(err, null)
|
||||
assert(body.match(/no such package available/))
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
assert.equal(err, null);
|
||||
assert(body.match(/no such package available/));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,42 +1,44 @@
|
||||
var assert = require('assert')
|
||||
var validate = require('../../lib/utils').validate_name
|
||||
'use strict';
|
||||
|
||||
let assert = require('assert');
|
||||
let validate = require('../../lib/utils').validate_name;
|
||||
|
||||
describe('Validate', function() {
|
||||
it('good ones', function() {
|
||||
assert( validate('verdaccio') )
|
||||
assert( validate('some.weird.package-zzz') )
|
||||
assert( validate('old-package@0.1.2.tgz') )
|
||||
})
|
||||
assert( validate('verdaccio') );
|
||||
assert( validate('some.weird.package-zzz') );
|
||||
assert( validate('old-package@0.1.2.tgz') );
|
||||
});
|
||||
|
||||
it('uppercase', function() {
|
||||
assert( validate('EVE') )
|
||||
assert( validate('JSONStream') )
|
||||
})
|
||||
assert( validate('EVE') );
|
||||
assert( validate('JSONStream') );
|
||||
});
|
||||
|
||||
it('no package.json', function() {
|
||||
assert( !validate('package.json') )
|
||||
})
|
||||
assert( !validate('package.json') );
|
||||
});
|
||||
|
||||
it('no path seps', function() {
|
||||
assert( !validate('some/thing') )
|
||||
assert( !validate('some\\thing') )
|
||||
})
|
||||
assert( !validate('some/thing') );
|
||||
assert( !validate('some\\thing') );
|
||||
});
|
||||
|
||||
it('no hidden', function() {
|
||||
assert( !validate('.bin') )
|
||||
})
|
||||
assert( !validate('.bin') );
|
||||
});
|
||||
|
||||
it('no reserved', function() {
|
||||
assert( !validate('favicon.ico') )
|
||||
assert( !validate('node_modules') )
|
||||
assert( !validate('__proto__') )
|
||||
})
|
||||
assert( !validate('favicon.ico') );
|
||||
assert( !validate('node_modules') );
|
||||
assert( !validate('__proto__') );
|
||||
});
|
||||
|
||||
it('other', function() {
|
||||
assert( !validate('pk g') )
|
||||
assert( !validate('pk\tg') )
|
||||
assert( !validate('pk%20g') )
|
||||
assert( !validate('pk+g') )
|
||||
assert( !validate('pk:g') )
|
||||
})
|
||||
})
|
||||
assert( !validate('pk g') );
|
||||
assert( !validate('pk\tg') );
|
||||
assert( !validate('pk%20g') );
|
||||
assert( !validate('pk+g') );
|
||||
assert( !validate('pk:g') );
|
||||
});
|
||||
});
|
||||
|
@ -1,39 +1,42 @@
|
||||
'use strict';
|
||||
|
||||
// ensure that all arguments are validated
|
||||
|
||||
var assert = require('assert')
|
||||
let assert = require('assert');
|
||||
|
||||
describe('index.js app', test('index.js'))
|
||||
describe('index-api.js app', test('index-api.js'))
|
||||
describe('index-web.js app', test('index-web.js'))
|
||||
describe('index.js app', test('index.js'));
|
||||
describe('index-api.js app', test('index-api.js'));
|
||||
describe('index-web.js app', test('index-web.js'));
|
||||
|
||||
function test(file) {
|
||||
return function() {
|
||||
var source = require('fs').readFileSync(__dirname + '/../../lib/' + file, 'utf8')
|
||||
let source = require('fs').readFileSync(__dirname + '/../../lib/' + file, 'utf8');
|
||||
|
||||
var very_scary_regexp = /\n\s*app\.(\w+)\s*\(\s*(("[^"]*")|('[^']*'))\s*,/g
|
||||
var m
|
||||
var params = {}
|
||||
let very_scary_regexp = /\n\s*app\.(\w+)\s*\(\s*(("[^"]*")|('[^']*'))\s*,/g;
|
||||
let m;
|
||||
let params = {};
|
||||
|
||||
while ((m = very_scary_regexp.exec(source)) != null) {
|
||||
if (m[1] === 'set') continue
|
||||
if (m[1] === 'set') continue;
|
||||
|
||||
var inner = m[2].slice(1, m[2].length-1)
|
||||
var t
|
||||
let inner = m[2].slice(1, m[2].length-1);
|
||||
var t;
|
||||
|
||||
inner.split('/').forEach(function(x) {
|
||||
t = x.match(/^:([^?:]*)\??$/);
|
||||
if (m[1] === 'param') {
|
||||
params[x] = 'ok'
|
||||
} else if (t = x.match(/^:([^?:]*)\??$/)) {
|
||||
params[t[1]] = params[t[1]] || m[0].trim()
|
||||
params[x] = 'ok';
|
||||
} else if (t) {
|
||||
params[t[1]] = params[t[1]] || m[0].trim();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(params).forEach(function(param) {
|
||||
it('should validate ":'+param+'"', function() {
|
||||
assert.equal(params[param], 'ok')
|
||||
})
|
||||
})
|
||||
}
|
||||
assert.equal(params[param], 'ok');
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
BIN
yarn.lock
Normal file
BIN
yarn.lock
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user