mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-04-01 02:42:23 -05:00
change code style to jshttp
close #155, see reasons there This is a huge commit, so let me know if it will cause any trouble, I might consider reverting it if it's the case.
This commit is contained in:
parent
bac62f6969
commit
6a778e8c17
51 changed files with 4403 additions and 4420 deletions
|
@ -6,16 +6,7 @@ root = true
|
|||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[*.js]
|
||||
indent_style = tab
|
||||
|
||||
# 2 space indentation
|
||||
[{.,}*.y{a,}ml]
|
||||
[{.,}*.{js,yml,yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Indentation override for Gruntfile
|
||||
[Gruntfile.js]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
|
68
Gruntfile.js
68
Gruntfile.js
|
@ -1,39 +1,39 @@
|
|||
module.exports = function(grunt) {
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readYAML('package.yaml'),
|
||||
browserify: {
|
||||
dist: {
|
||||
files: {
|
||||
'lib/static/main.js': ['lib/GUI/js/main.js']
|
||||
},
|
||||
options: {
|
||||
debug: true,
|
||||
transform: ['browserify-handlebars']
|
||||
}
|
||||
}
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readYAML('package.yaml'),
|
||||
browserify: {
|
||||
dist: {
|
||||
files: {
|
||||
'lib/static/main.js': [ 'lib/GUI/js/main.js' ]
|
||||
},
|
||||
less: {
|
||||
dist: {
|
||||
files: {
|
||||
'lib/static/main.css': ['lib/GUI/css/main.less']
|
||||
},
|
||||
options: {
|
||||
sourceMap: true
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
files: [ "lib/GUI/**/*"],
|
||||
tasks: [ 'default' ]
|
||||
options: {
|
||||
debug: true,
|
||||
transform: [ 'browserify-handlebars' ]
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
less: {
|
||||
dist: {
|
||||
files: {
|
||||
'lib/static/main.css': [ 'lib/GUI/css/main.less' ]
|
||||
},
|
||||
options: {
|
||||
sourceMap: true
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
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'
|
||||
]);
|
||||
};
|
||||
grunt.registerTask('default', [
|
||||
'browserify',
|
||||
'less'
|
||||
])
|
||||
}
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
@media (max-width: 992px) {
|
||||
.body {
|
||||
.main-header {
|
||||
.npm-logo {
|
||||
width: 100px;
|
||||
float: left;
|
||||
}
|
||||
.body {
|
||||
.main-header {
|
||||
.npm-logo {
|
||||
width: 100px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.packages-header {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.packages-header {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.body {
|
||||
.content {
|
||||
padding-top: @mainHeaderHeight + @packagesHeaderHeight + @smRegistryInfoHeight + 10;
|
||||
.body {
|
||||
.content {
|
||||
padding-top: @mainHeaderHeight + @packagesHeaderHeight + @smRegistryInfoHeight + 10;
|
||||
|
||||
.entry {
|
||||
.title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.entry {
|
||||
.title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.author {
|
||||
float: none !important;
|
||||
clear: both;
|
||||
padding: 0 0 5px 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.author {
|
||||
float: none !important;
|
||||
clear: both;
|
||||
padding: 0 0 5px 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-results {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
}
|
||||
.no-results {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,194 +9,194 @@
|
|||
|
||||
/*** Main Styles ***/
|
||||
.body {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left:0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left:0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.main-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: @white;
|
||||
z-index: 1;
|
||||
.main-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: @white;
|
||||
z-index: 1;
|
||||
|
||||
.navbar {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.navbar {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.npm-logo {
|
||||
width: 79px;
|
||||
height: @mainHeaderHeight;
|
||||
background-image: url( -/logo-sm );
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE8AAAAoCAYAAAC/xadkAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMDA4QUE4NzQ1QkMxMUU0QTVCMUYwQjdEREYxNENGMiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozMDA4QUE4ODQ1QkMxMUU0QTVCMUYwQjdEREYxNENGMiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMwMDhBQTg1NDVCQzExRTRBNUIxRjBCN0RERjE0Q0YyIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMwMDhBQTg2NDVCQzExRTRBNUIxRjBCN0RERjE0Q0YyIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+ypkYZgAACelJREFUeNrsWmtsHFcZPXd2Zh9+r+31+5W4tSPSoKZUKgpIgdAWqorSULVFqJREKpVAAqRKhfIPKfCDtP2TKilpRRACUpBC+UlbQRG0RUmo0gBtWoyT2HHstR0/Yq/X9j5mh/PdmfX7te42tZyMdO2dmTt3vnvu+Z53lHOm9VcwQntRuvMq0qMKmTSgDNw4Fh4OG3Gxyh3E/l2GZOxVE0UtDShqLUbCLkb9o0D6KmAnbgA4D7cMcbMIXAUw+BoQ2QXEu5pMAjqB2q8Cl34BXDwC1D3IzkkgNUEAfTeAE+B8IWJhAl0vAKECoOa7wIVDEwbsaQUMA03fAMJbgJ5fAxlS1Crjg/Z1DhznbxbzB1l3+SWguBpo2cdzamd6wjBcdqXYeoHa+8m8T/PncSBJ5gUiHCB9nQLHefvLCRK1sPe3QNUtQAO1ElE2MWumcJHEg881iE43ULGbQIdJ0RNA5V1EuxWY7r++VFiAC5JlcRLqyp+olV8CSm938RGn4foDwyR2xowzcQy3Q8l24KYi6vVviHyc6ryTAPa5OLt/Nq9HddiCddTM94GxvwFbHwIKb6YpE+CEa9n5K2HewoOXMj1AAQfY9h2g8xi1mjpevYdATnoob+JQxEeHcOUNauYHQNtjVN0KFw8sgEppFyJQLmQTDWRmgP3LXQDPU+ejpK+/lNeTm5R9jlZEpEgQRbJse5z//ZxvdDFw7mHM5eHCe2wxtnqglfHfMEOZcJt7eVOyjzA4hGP0TaD8EZ7TWeKC5w+WfsBc1o7xVvpqCom+Dhi+KWCSIMbb+QLxzJnNCZ6iyo50w4l2kHRjCDYTHstw7eDi/ssxj51VGMOvnsK5b55Awc0RbwXObn6HQVW1Y2/DoMbu/PN+BBoreTm21LxJL0MtO5AiroKvMg0XY8fa/GGKIZ7AdCMzzatltEwJMkl4DsNZDJ7PoPOxYIQs/vZhuqsL9tRkbuwj5YNNTfCVlOhUJzM1hamLXbnPyW8huGWr+9uykIj2ITU8nD9NoJxmaSlVtYmWyeY71GpDk3mJtLGMN5kTM6ZJ5Rgan3gCwYZGYrB2m6eYxPQfP474++f072BjI5p/8EPK6qzZ78hzqZER9L34ol7U6YEBRO67D6W7djGDyo/9lXfE33sX/S8d50IVr7IoyrN5RSZ/rZLD2jbsiQlUP/wwAnX1OQs2+vpfMPbWmxTKD5MMrHrwodyJkUrj8pHDWuzk4ABKP/NZVD3wQF41dowL2/PcIQRqi9eGNxy1Ou/F9lFtk/396xIqk0jQa1nalgiL13Mk+nq9kJQpkeVnJHA17+ZOzIDi2Gu0BBKq5KdwlxocRCaVdGPNUAhWRcU6ihhpjnNF2x+HbDc5hq+gIOdx7PExpMfHKYihF91fVT0TVNjxONI0AbKQ8h4rEtELm7tXltGh8mJxz+3fh7fvuAOnb7sVnU8+uT52RaN454t348wX9uDUJ3dg+JVX1jVOz+HDOLnjFpzZvRv/2bsX9uTkzL0rf3wZJ7d/Au/cfRfO7Pk8ndfFnB2LDttsnZ7lx12Jatr0pPI/k0yuz2hnHGSmp8nglDthe30qLqYhMzkFm2NJmxvkZsT5UU5p2oQ4OWZLKpvKubltXmpNvqIi7eoFPF9haJ0xlqEdioCXIXi5q5M3TCCgZZGxfMXF85RLO62yMn1PwBMvmzt6oraGtnf5CZQcZ07L0zj4iMbJ0zuMDZdtCROyplht5FRQ6eh4w5SIxcMm6TREbRP0umK3Nmbdb149b4OsJW1cQVubdjhmRaW2WxuYefnztvk4AnV12Pn6X+es8Mau4Bj5CpI/kvragixnxfNVMqS8jLPYOq++vI4X8ftr61bIVdzUy6G9MoLzQxUjGOT15IciUqC+fsZDarWWKs1yYZOEIZRDZJay2twsxSoLu3GoNydhe/bwM9tw1hSjuhMx4RXWV07iTJjFJeh9/oiehBQJsgNI+qMsE+mxMZjhsBZoqqsLvUd/rstPkqolenrgr65m2hRb8T2Sr/a+cJTBcQYq4PdsswODk0+PjupSlDAl2FCvs4/U8NA8Wdx4sxDjb/2DC12r3y0LeumZpxn7BaH8FibOnkWQgIl9NcidS88+C7OyQsd7kx0d8NdUr4GNTtbmqZULV1IUMH0wykrR98tjFDYOwzTnsVLqdKGtrTr4FCESPd3ofOop9vNpMIPNzbAqIwR4fOX8mAB1HTigmSHsUToY5d+0rQPfUOtWLbevphZjb/wdg384MU+WbHYhwAkI8pycdx88qOWQUpq/shKBhgaXYYbCZRIi42UhIn9wSzMXZWIVAGeZt+wGUCbBRH1oElZVmGy4CrOonG3phN+OJXTLGvpATZM3G7LICripltQBVzCxFoUvaG/XoYrJxbLH4pxYcuaZ1FDMW3epcgdm37EoV3QY8swWSq2K2nkBciI6JJGGC0BpBCidZVQyOoJ03A+fOe3VLdVypFKzm96LeQCjKIRASx38RYNAMAJd2HcyOdt8J5FhTikfzJCJUk2WVRcm2PbMhBSZJQVPPb7YowQBLAkC4dA13KwTKNLwDfXAKG+nxgX1+fIlKdub4jwBZVIZVH55B8q3n2KHTwF1j7J7LDfwZD/AF8EHj/+MNuo0gi31SA0M6IqGZuHc9Mhwd6mUPwAf29T5KNqf/x4iX7mTxj96bcIWzXA6m77fQzlnYNSUecDEMb/a7ngZhiPfWBhz0CPSRg3/jUJ1H4WvSr5X2SfFHLaCHCchfUvd2hnBEhsorEsP9Hs2Zc5Y+hs4n1ZXfZ2q5yukkTfl+YlrFPM5btv6feL1O+B/zwHNj9DV1y7e/NYZhiXbZxmvWCDANQOxd4Gel4EIQSu+nzc6PPoa61CDKUxdGMDEQI/+YkvbkRWMcbJ/Qv+Pj/QgOTCqnweuFXhZAOnYCr8GlNO+d9L7N95DQt7GW5e8OSlPbXUZ3puQ2gIMvwZET/OBH5M0O9jnPS/9XW8KPIb6b9+J8J52WOFi1zuvpTI9EkPJ7S3Q38LhWsbx2Z1Ezrvyc7T1jUD3AaCK2hK5l7f6kN2OVE7nPf9F67fa9Nnl41xk2rUtBC4gmyBdEjp+SGGEsYydUAj3O8C1MkhqeYMuC/Bx7RdLwEzwJMC/8BMgRNmbvi4hO6E51mXCX8Yr9CqdT7sdt/3UEzgfwGWjoSGvrYcFH+dGu8z/snzEDbQ/QwAP0g4eAm76ERUxrJRz/t4hFLVUwLqVYcF+dpbv0CaxeC93qUTdWUD1jVQ2Ujn0XS2/TrtsA4P0UWpn4qR8rzhuYvRfHUhNtaG8OoZLjyk3FDFmH1ZzQpn5HwWpZZP42WtqWaHUjOxqaQeiVkv21Rr7L86gFoumlh5j7nnGRSLY5iDeX4jRf3b9X4ABADgctRMdEJWtAAAAAElFTkSuQmCC);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
.npm-logo {
|
||||
width: 79px;
|
||||
height: @mainHeaderHeight;
|
||||
background-image: url( -/logo-sm );
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE8AAAAoCAYAAAC/xadkAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMDA4QUE4NzQ1QkMxMUU0QTVCMUYwQjdEREYxNENGMiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozMDA4QUE4ODQ1QkMxMUU0QTVCMUYwQjdEREYxNENGMiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjMwMDhBQTg1NDVCQzExRTRBNUIxRjBCN0RERjE0Q0YyIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjMwMDhBQTg2NDVCQzExRTRBNUIxRjBCN0RERjE0Q0YyIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+ypkYZgAACelJREFUeNrsWmtsHFcZPXd2Zh9+r+31+5W4tSPSoKZUKgpIgdAWqorSULVFqJREKpVAAqRKhfIPKfCDtP2TKilpRRACUpBC+UlbQRG0RUmo0gBtWoyT2HHstR0/Yq/X9j5mh/PdmfX7te42tZyMdO2dmTt3vnvu+Z53lHOm9VcwQntRuvMq0qMKmTSgDNw4Fh4OG3Gxyh3E/l2GZOxVE0UtDShqLUbCLkb9o0D6KmAnbgA4D7cMcbMIXAUw+BoQ2QXEu5pMAjqB2q8Cl34BXDwC1D3IzkkgNUEAfTeAE+B8IWJhAl0vAKECoOa7wIVDEwbsaQUMA03fAMJbgJ5fAxlS1Crjg/Z1DhznbxbzB1l3+SWguBpo2cdzamd6wjBcdqXYeoHa+8m8T/PncSBJ5gUiHCB9nQLHefvLCRK1sPe3QNUtQAO1ElE2MWumcJHEg881iE43ULGbQIdJ0RNA5V1EuxWY7r++VFiAC5JlcRLqyp+olV8CSm938RGn4foDwyR2xowzcQy3Q8l24KYi6vVviHyc6ryTAPa5OLt/Nq9HddiCddTM94GxvwFbHwIKb6YpE+CEa9n5K2HewoOXMj1AAQfY9h2g8xi1mjpevYdATnoob+JQxEeHcOUNauYHQNtjVN0KFw8sgEppFyJQLmQTDWRmgP3LXQDPU+ejpK+/lNeTm5R9jlZEpEgQRbJse5z//ZxvdDFw7mHM5eHCe2wxtnqglfHfMEOZcJt7eVOyjzA4hGP0TaD8EZ7TWeKC5w+WfsBc1o7xVvpqCom+Dhi+KWCSIMbb+QLxzJnNCZ6iyo50w4l2kHRjCDYTHstw7eDi/ssxj51VGMOvnsK5b55Awc0RbwXObn6HQVW1Y2/DoMbu/PN+BBoreTm21LxJL0MtO5AiroKvMg0XY8fa/GGKIZ7AdCMzzatltEwJMkl4DsNZDJ7PoPOxYIQs/vZhuqsL9tRkbuwj5YNNTfCVlOhUJzM1hamLXbnPyW8huGWr+9uykIj2ITU8nD9NoJxmaSlVtYmWyeY71GpDk3mJtLGMN5kTM6ZJ5Rgan3gCwYZGYrB2m6eYxPQfP474++f072BjI5p/8EPK6qzZ78hzqZER9L34ol7U6YEBRO67D6W7djGDyo/9lXfE33sX/S8d50IVr7IoyrN5RSZ/rZLD2jbsiQlUP/wwAnX1OQs2+vpfMPbWmxTKD5MMrHrwodyJkUrj8pHDWuzk4ABKP/NZVD3wQF41dowL2/PcIQRqi9eGNxy1Ou/F9lFtk/396xIqk0jQa1nalgiL13Mk+nq9kJQpkeVnJHA17+ZOzIDi2Gu0BBKq5KdwlxocRCaVdGPNUAhWRcU6ihhpjnNF2x+HbDc5hq+gIOdx7PExpMfHKYihF91fVT0TVNjxONI0AbKQ8h4rEtELm7tXltGh8mJxz+3fh7fvuAOnb7sVnU8+uT52RaN454t348wX9uDUJ3dg+JVX1jVOz+HDOLnjFpzZvRv/2bsX9uTkzL0rf3wZJ7d/Au/cfRfO7Pk8ndfFnB2LDttsnZ7lx12Jatr0pPI/k0yuz2hnHGSmp8nglDthe30qLqYhMzkFm2NJmxvkZsT5UU5p2oQ4OWZLKpvKubltXmpNvqIi7eoFPF9haJ0xlqEdioCXIXi5q5M3TCCgZZGxfMXF85RLO62yMn1PwBMvmzt6oraGtnf5CZQcZ07L0zj4iMbJ0zuMDZdtCROyplht5FRQ6eh4w5SIxcMm6TREbRP0umK3Nmbdb149b4OsJW1cQVubdjhmRaW2WxuYefnztvk4AnV12Pn6X+es8Mau4Bj5CpI/kvragixnxfNVMqS8jLPYOq++vI4X8ftr61bIVdzUy6G9MoLzQxUjGOT15IciUqC+fsZDarWWKs1yYZOEIZRDZJay2twsxSoLu3GoNydhe/bwM9tw1hSjuhMx4RXWV07iTJjFJeh9/oiehBQJsgNI+qMsE+mxMZjhsBZoqqsLvUd/rstPkqolenrgr65m2hRb8T2Sr/a+cJTBcQYq4PdsswODk0+PjupSlDAl2FCvs4/U8NA8Wdx4sxDjb/2DC12r3y0LeumZpxn7BaH8FibOnkWQgIl9NcidS88+C7OyQsd7kx0d8NdUr4GNTtbmqZULV1IUMH0wykrR98tjFDYOwzTnsVLqdKGtrTr4FCESPd3ofOop9vNpMIPNzbAqIwR4fOX8mAB1HTigmSHsUToY5d+0rQPfUOtWLbevphZjb/wdg384MU+WbHYhwAkI8pycdx88qOWQUpq/shKBhgaXYYbCZRIi42UhIn9wSzMXZWIVAGeZt+wGUCbBRH1oElZVmGy4CrOonG3phN+OJXTLGvpATZM3G7LICripltQBVzCxFoUvaG/XoYrJxbLH4pxYcuaZ1FDMW3epcgdm37EoV3QY8swWSq2K2nkBciI6JJGGC0BpBCidZVQyOoJ03A+fOe3VLdVypFKzm96LeQCjKIRASx38RYNAMAJd2HcyOdt8J5FhTikfzJCJUk2WVRcm2PbMhBSZJQVPPb7YowQBLAkC4dA13KwTKNLwDfXAKG+nxgX1+fIlKdub4jwBZVIZVH55B8q3n2KHTwF1j7J7LDfwZD/AF8EHj/+MNuo0gi31SA0M6IqGZuHc9Mhwd6mUPwAf29T5KNqf/x4iX7mTxj96bcIWzXA6m77fQzlnYNSUecDEMb/a7ngZhiPfWBhz0CPSRg3/jUJ1H4WvSr5X2SfFHLaCHCchfUvd2hnBEhsorEsP9Hs2Zc5Y+hs4n1ZXfZ2q5yukkTfl+YlrFPM5btv6feL1O+B/zwHNj9DV1y7e/NYZhiXbZxmvWCDANQOxd4Gel4EIQSu+nzc6PPoa61CDKUxdGMDEQI/+YkvbkRWMcbJ/Qv+Pj/QgOTCqnweuFXhZAOnYCr8GlNO+d9L7N95DQt7GW5e8OSlPbXUZ3puQ2gIMvwZET/OBH5M0O9jnPS/9XW8KPIb6b9+J8J52WOFi1zuvpTI9EkPJ7S3Q38LhWsbx2Z1Ezrvyc7T1jUD3AaCK2hK5l7f6kN2OVE7nPf9F67fa9Nnl41xk2rUtBC4gmyBdEjp+SGGEsYydUAj3O8C1MkhqeYMuC/Bx7RdLwEzwJMC/8BMgRNmbvi4hO6E51mXCX8Yr9CqdT7sdt/3UEzgfwGWjoSGvrYcFH+dGu8z/snzEDbQ/QwAP0g4eAm76ERUxrJRz/t4hFLVUwLqVYcF+dpbv0CaxeC93qUTdWUD1jVQ2Ujn0XS2/TrtsA4P0UWpn4qR8rzhuYvRfHUhNtaG8OoZLjyk3FDFmH1ZzQpn5HwWpZZP42WtqWaHUjOxqaQeiVkv21Rr7L86gFoumlh5j7nnGRSLY5iDeX4jRf3b9X4ABADgctRMdEJWtAAAAAElFTkSuQmCC);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
|
||||
>a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
>a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.setup {
|
||||
line-height: 1.3em;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.setup {
|
||||
line-height: 1.3em;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.packages-header {
|
||||
border-bottom: @headerBorderWidth solid #e6e6e6;
|
||||
.packages-header {
|
||||
border-bottom: @headerBorderWidth solid #e6e6e6;
|
||||
|
||||
.search-container {
|
||||
top: 9px;
|
||||
.search-container {
|
||||
top: 9px;
|
||||
|
||||
.search-icon {
|
||||
background: #e6e6e6;
|
||||
}
|
||||
}
|
||||
}
|
||||
.search-icon {
|
||||
background: #e6e6e6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sm-registry-info {
|
||||
height: @smRegistryInfoHeight;
|
||||
line-height: 1.7em;
|
||||
}
|
||||
}
|
||||
.sm-registry-info {
|
||||
height: @smRegistryInfoHeight;
|
||||
line-height: 1.7em;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-top: @mainHeaderHeight + @packagesHeaderHeight + 10;
|
||||
.content {
|
||||
padding-top: @mainHeaderHeight + @packagesHeaderHeight + 10;
|
||||
|
||||
.entry {
|
||||
.transition(height .3s);
|
||||
padding: 9px 10px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #E7E7E7;
|
||||
.entry {
|
||||
.transition(height .3s);
|
||||
padding: 9px 10px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #E7E7E7;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:nth-child( even ) {
|
||||
background: @entryBg;
|
||||
}
|
||||
&:nth-child( even ) {
|
||||
background: @entryBg;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0 0 5px 10px;
|
||||
}
|
||||
.title {
|
||||
margin: 0 0 5px 10px;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0 0 0 18px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.description {
|
||||
margin: 0 0 0 18px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.name:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.name:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.name:before {
|
||||
margin: 0;
|
||||
margin-left: -10px;
|
||||
.transformTransition(.2s);
|
||||
}
|
||||
.name:before {
|
||||
margin: 0;
|
||||
margin-left: -10px;
|
||||
.transformTransition(.2s);
|
||||
}
|
||||
|
||||
&.open .name:before {
|
||||
.rotate(90deg);
|
||||
}
|
||||
&.open .name:before {
|
||||
.rotate(90deg);
|
||||
}
|
||||
|
||||
.version {
|
||||
color: #666;
|
||||
}
|
||||
.version {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.author {
|
||||
color: #666;
|
||||
}
|
||||
.author {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.readme {
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
background: @white;
|
||||
padding: 10px 12px;
|
||||
.border-radius(3px);
|
||||
border: 1px solid darken( @entryBg, 10% );
|
||||
}
|
||||
}
|
||||
}
|
||||
.readme {
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
background: @white;
|
||||
padding: 10px 12px;
|
||||
.border-radius(3px);
|
||||
border: 1px solid darken( @entryBg, 10% );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pkg-search-container {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.packages-container {
|
||||
.search-ajax {
|
||||
display: block;
|
||||
margin: 50px auto;
|
||||
}
|
||||
.search-ajax {
|
||||
display: block;
|
||||
margin: 50px auto;
|
||||
}
|
||||
}
|
||||
|
||||
.no-results {
|
||||
text-align: center;
|
||||
margin: 50px 0;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
margin: 50px 0;
|
||||
color: #888;
|
||||
|
||||
big {
|
||||
font-size: 38px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
big {
|
||||
font-size: 38px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
code {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.red {
|
||||
color: @npmRed;
|
||||
color: @npmRed;
|
||||
}
|
||||
|
||||
.light-red {
|
||||
color: lighten( @npmRed, 10% );
|
||||
color: lighten( @npmRed, 10% );
|
||||
}
|
||||
|
||||
.white {
|
||||
color: @white !important;
|
||||
color: @white !important;
|
||||
}
|
||||
|
||||
.red-bg {
|
||||
background: @npmRed;
|
||||
background: @npmRed;
|
||||
}
|
||||
|
||||
.light-red-bg {
|
||||
background: lighten( @npmRed, 10% );
|
||||
background: lighten( @npmRed, 10% );
|
||||
}
|
||||
|
||||
.no-bg {
|
||||
background: none !important;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.no-border {
|
||||
border: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
.no-rnd-cnr {
|
||||
.border-radius( 0 );
|
||||
.border-radius( 0 );
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
margin-left: 10px;
|
||||
margin-top: 5px;
|
||||
margin-left: 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.pad-right-10 {
|
||||
padding-right: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
<div class="entry" data-name="{{ name }}" data-version="{{ version }}">
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-sm-8">
|
||||
<h4 class="title">
|
||||
<a class='name icon-angle-right red' href='javascript:void(0)'>{{ name }}</a>
|
||||
<small class='version'>v{{ version }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-4">
|
||||
<div class="author pull-right">
|
||||
<small>By: {{ _npmUser.name }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p class="description">{{ description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-sm-8">
|
||||
<h4 class="title">
|
||||
<a class='name icon-angle-right red' href='javascript:void(0)'>{{ name }}</a>
|
||||
<small class='version'>v{{ version }}</small>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-4">
|
||||
<div class="author pull-right">
|
||||
<small>By: {{ _npmUser.name }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p class="description">{{ description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,127 +1,127 @@
|
|||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ name }}</title>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ name }}</title>
|
||||
|
||||
<link rel="icon" type="image/ico" href="{{ baseUrl }}/-/static/favicon.png"/>
|
||||
<link rel="stylesheet" type="text/css" href="{{ baseUrl }}/-/static/main.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body class="body">
|
||||
<header class="main-header">
|
||||
<nav class="navbar navbar-default red-bg white no-border no-rnd-cnr" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-header clearfix">
|
||||
<div class="npm-logo brand">
|
||||
<a href="{{ baseUrl }}"></a>
|
||||
</div>
|
||||
<link rel="icon" type="image/ico" href="{{ baseUrl }}/-/static/favicon.png"/>
|
||||
<link rel="stylesheet" type="text/css" href="{{ baseUrl }}/-/static/main.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body class="body">
|
||||
<header class="main-header">
|
||||
<nav class="navbar navbar-default red-bg white no-border no-rnd-cnr" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-header clearfix">
|
||||
<div class="npm-logo brand">
|
||||
<a href="{{ baseUrl }}"></a>
|
||||
</div>
|
||||
|
||||
<!-- login/logout for small devices -->
|
||||
<div class="pull-right visible-xs pad-right-10">
|
||||
<div>
|
||||
{{#if username}}
|
||||
<p class="white no-bg navbar-text pad-right-10 inline-block">Hi {{username}}</p>
|
||||
<button type="submit" class="btn btn-danger inline-block js-userLogoutBtn">Logout</button>
|
||||
{{else}}
|
||||
<p class="white no-bg navbar-text pad-right-10 inline-block"> </p>
|
||||
<button type="submit" class="btn btn-danger inline-block" data-toggle="modal" data-target="#login-form" onclick="return false">Login</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- login/logout for small devices -->
|
||||
<div class="pull-right visible-xs pad-right-10">
|
||||
<div>
|
||||
{{#if username}}
|
||||
<p class="white no-bg navbar-text pad-right-10 inline-block">Hi {{username}}</p>
|
||||
<button type="submit" class="btn btn-danger inline-block js-userLogoutBtn">Logout</button>
|
||||
{{else}}
|
||||
<p class="white no-bg navbar-text pad-right-10 inline-block"> </p>
|
||||
<button type="submit" class="btn btn-danger inline-block" data-toggle="modal" data-target="#login-form" onclick="return false">Login</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navbar-left hidden-xs"> </div>
|
||||
<div class="navbar-left hidden-xs"> </div>
|
||||
|
||||
<div class="navbar-left setup hidden-xs">
|
||||
<code class="white no-bg">npm set registry {{ baseUrl }}</code><br>
|
||||
<code class="white no-bg">npm adduser --registry {{ baseUrl }}</code>
|
||||
</div>
|
||||
<div class="navbar-left setup hidden-xs">
|
||||
<code class="white no-bg">npm set registry {{ baseUrl }}</code><br>
|
||||
<code class="white no-bg">npm adduser --registry {{ baseUrl }}</code>
|
||||
</div>
|
||||
|
||||
<!-- login/logout for large devices -->
|
||||
<div class="navbar-collapse collapse">
|
||||
<div class="navbar-right">
|
||||
<form class="navbar-form navbar-right">
|
||||
{{#if username}}
|
||||
<p class="white no-bg pad-right-10 inline-block">Hi {{username}}</p>
|
||||
<button type="submit" class="btn btn-danger inline-block js-userLogoutBtn">Logout</button>
|
||||
{{else}}
|
||||
<button type="submit" class="btn btn-danger inline-block" data-toggle="modal" data-target="#login-form" onclick="return false">Login</button>
|
||||
{{/if}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- login/logout for large devices -->
|
||||
<div class="navbar-collapse collapse">
|
||||
<div class="navbar-right">
|
||||
<form class="navbar-form navbar-right">
|
||||
{{#if username}}
|
||||
<p class="white no-bg pad-right-10 inline-block">Hi {{username}}</p>
|
||||
<button type="submit" class="btn btn-danger inline-block js-userLogoutBtn">Logout</button>
|
||||
{{else}}
|
||||
<button type="submit" class="btn btn-danger inline-block" data-toggle="modal" data-target="#login-form" onclick="return false">Login</button>
|
||||
{{/if}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
<header class="sm-registry-info light-red-bg center hidden-sm hidden-lg hidden-md">
|
||||
<code class="white no-bg">{{ baseUrl }}</code><br>
|
||||
</header>
|
||||
<header class="packages-header container">
|
||||
<div class="row">
|
||||
<div class="col-md-5 hidden-xs hidden-sm">
|
||||
<h2 class="title">Available Packages</h2>
|
||||
</div>
|
||||
<div class="col-md-4 col-md-offset-3 col-sm-12">
|
||||
<form id='search-form'>
|
||||
<div class="input-group input-group-lg search-container">
|
||||
<input type="text" class="form-control" placeholder="Search for packages">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default search-icon js-search-btn"><i class="icon-search"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</header>
|
||||
</div>
|
||||
</nav>
|
||||
<header class="sm-registry-info light-red-bg center hidden-sm hidden-lg hidden-md">
|
||||
<code class="white no-bg">{{ baseUrl }}</code><br>
|
||||
</header>
|
||||
<header class="packages-header container">
|
||||
<div class="row">
|
||||
<div class="col-md-5 hidden-xs hidden-sm">
|
||||
<h2 class="title">Available Packages</h2>
|
||||
</div>
|
||||
<div class="col-md-4 col-md-offset-3 col-sm-12">
|
||||
<form id='search-form'>
|
||||
<div class="input-group input-group-lg search-container">
|
||||
<input type="text" class="form-control" placeholder="Search for packages">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default search-icon js-search-btn"><i class="icon-search"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</header>
|
||||
|
||||
<section class="content container packages-container" id="all-packages">
|
||||
{{#each packages}}
|
||||
{{> entry}}
|
||||
{{/each}}
|
||||
<section class="content container packages-container" id="all-packages">
|
||||
{{#each packages}}
|
||||
{{> entry}}
|
||||
{{/each}}
|
||||
|
||||
{{#unless packages.length}}
|
||||
<div class='no-results'>
|
||||
<big>No Packages</big><br>
|
||||
Use <code>npm publish</code>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</section>
|
||||
{{#unless packages.length}}
|
||||
<div class='no-results'>
|
||||
<big>No Packages</big><br>
|
||||
Use <code>npm publish</code>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</section>
|
||||
|
||||
<section class="content container pkg-search-container" id="search-results"></section>
|
||||
<section class="content container pkg-search-container" id="search-results"></section>
|
||||
|
||||
<div class="modal fade" id="login-form" tabindex="-1" role="dialog" aria-labelledby="login-form-label" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
|
||||
<h5 class="modal-title" id="login-form-label">Welcome back</h5>
|
||||
</div>
|
||||
<form role="form" action="{{ baseUrl }}/-/login" method="post" id="login-form" autocomplete="off">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="user" class="sr-only">Email</label>
|
||||
<input name="user" id="user" class="form-control" placeholder="Username" type="text">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="pass" class="sr-only">Password</label>
|
||||
<input name="pass" id="pass" class="form-control" placeholder="Password" type="password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="login-form" tabindex="-1" role="dialog" aria-labelledby="login-form-label" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
|
||||
<h5 class="modal-title" id="login-form-label">Welcome back</h5>
|
||||
</div>
|
||||
<form role="form" action="{{ baseUrl }}/-/login" method="post" id="login-form" autocomplete="off">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="user" class="sr-only">Email</label>
|
||||
<input name="user" id="user" class="form-control" placeholder="Username" type="text">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="pass" class="sr-only">Password</label>
|
||||
<input name="pass" id="pass" class="form-control" placeholder="Password" type="password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Log in</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="{{ baseUrl }}/-/logout" method="post" class="hide" id="userLogoutForm"></form>
|
||||
<form action="{{ baseUrl }}/-/logout" method="post" class="hide" id="userLogoutForm"></form>
|
||||
|
||||
<script src="{{ baseUrl }}/-/static/jquery.min.js"></script>
|
||||
<script type='text/javascript' src='{{ baseUrl }}/-/static/main.js'></script>
|
||||
</body>
|
||||
<script src="{{ baseUrl }}/-/static/jquery.min.js"></script>
|
||||
<script type='text/javascript' src='{{ baseUrl }}/-/static/main.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,71 +1,71 @@
|
|||
var $ = require('unopinionate').selector,
|
||||
onClick = require('onclick'),
|
||||
transitionComplete = require('transition-complete');
|
||||
var $ = require('unopinionate').selector
|
||||
var onClick = require('onclick')
|
||||
var transitionComplete = require('transition-complete')
|
||||
|
||||
$(function() {
|
||||
onClick('.entry .name', function() {
|
||||
var $this = $(this),
|
||||
$entry = $this.closest('.entry');
|
||||
onClick('.entry .name', function() {
|
||||
var $this = $(this)
|
||||
var $entry = $this.closest('.entry')
|
||||
|
||||
//Close entry
|
||||
if($entry.hasClass('open')) {
|
||||
$entry
|
||||
.height($entry.outerHeight())
|
||||
.removeClass('open');
|
||||
if ($entry.hasClass('open')) {
|
||||
// Close entry
|
||||
$entry
|
||||
.height($entry.outerHeight())
|
||||
.removeClass('open')
|
||||
|
||||
setTimeout(function() {
|
||||
$entry.css('height', $entry.attr('data-height') + 'px');
|
||||
}, 0);
|
||||
setTimeout(function() {
|
||||
$entry.css('height', $entry.attr('data-height') + 'px')
|
||||
}, 0)
|
||||
|
||||
transitionComplete(function() {
|
||||
$entry.find('.readme').remove();
|
||||
$entry.css('height', 'auto');
|
||||
});
|
||||
}
|
||||
//Open entry
|
||||
else {
|
||||
//Close open entries
|
||||
$('.entry.open').each(function() {
|
||||
var $entry = $(this);
|
||||
$entry
|
||||
.height($entry.outerHeight())
|
||||
.removeClass('open');
|
||||
transitionComplete(function() {
|
||||
$entry.find('.readme').remove()
|
||||
$entry.css('height', 'auto')
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
$entry.css('height', $entry.attr('data-height') + 'px');
|
||||
}, 0);
|
||||
} else {
|
||||
// Open entry
|
||||
$('.entry.open').each(function() {
|
||||
// Close open entries
|
||||
var $entry = $(this)
|
||||
$entry
|
||||
.height($entry.outerHeight())
|
||||
.removeClass('open')
|
||||
|
||||
transitionComplete(function() {
|
||||
$entry.find('.readme').remove();
|
||||
$entry.css('height', 'auto');
|
||||
});
|
||||
});
|
||||
setTimeout(function() {
|
||||
$entry.css('height', $entry.attr('data-height') + 'px')
|
||||
}, 0)
|
||||
|
||||
//Add the open class
|
||||
$entry.addClass('open');
|
||||
transitionComplete(function() {
|
||||
$entry.find('.readme').remove()
|
||||
$entry.css('height', 'auto')
|
||||
})
|
||||
})
|
||||
|
||||
//Explicitly set heights for transitions
|
||||
var height = $entry.outerHeight();
|
||||
$entry
|
||||
.attr('data-height', height)
|
||||
.css('height', height);
|
||||
// Add the open class
|
||||
$entry.addClass('open')
|
||||
|
||||
//Get the data
|
||||
$.ajax({
|
||||
url: '-/readme/'+$entry.attr('data-name')+'/'+$entry.attr('data-version'),
|
||||
dataType: 'text',
|
||||
success: function(html) {
|
||||
var $readme = $("<div class='readme'>")
|
||||
.html(html)
|
||||
.appendTo($entry);
|
||||
// Explicitly set heights for transitions
|
||||
var height = $entry.outerHeight()
|
||||
$entry
|
||||
.attr('data-height', height)
|
||||
.css('height', height)
|
||||
|
||||
$entry.height(height + $readme.outerHeight());
|
||||
// Get the data
|
||||
$.ajax({
|
||||
url: '-/readme/'+$entry.attr('data-name')+'/'+$entry.attr('data-version'),
|
||||
dataType: 'text',
|
||||
success: function(html) {
|
||||
var $readme = $("<div class='readme'>")
|
||||
.html(html)
|
||||
.appendTo($entry)
|
||||
|
||||
transitionComplete(function() {
|
||||
$entry.css('height', 'auto');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
$entry.height(height + $readme.outerHeight())
|
||||
|
||||
transitionComplete(function() {
|
||||
$entry.css('height', 'auto')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -6,7 +6,8 @@ require('./bootstrap-modal')
|
|||
require('./search')
|
||||
require('./entry')
|
||||
|
||||
var $ = require('unopinionate').selector
|
||||
$(document).on('click', '.js-userLogoutBtn', function() {
|
||||
$('#userLogoutForm').submit()
|
||||
return false
|
||||
$('#userLogoutForm').submit()
|
||||
return false
|
||||
})
|
||||
|
|
|
@ -1,81 +1,79 @@
|
|||
var $ = require('unopinionate').selector,
|
||||
template = require('../entry.hbs'),
|
||||
onScroll = require('onscroll');
|
||||
var $ = require('unopinionate').selector
|
||||
var template = require('../entry.hbs')
|
||||
var onScroll = require('onscroll')
|
||||
|
||||
$(function() {
|
||||
'use strict';
|
||||
;(function(window, document) {
|
||||
var $form = $('#search-form')
|
||||
var $input = $form.find('input')
|
||||
var $body = $('body')
|
||||
var $clear = $form.find('.clear')
|
||||
var $searchResults = $('#search-results')
|
||||
var $pkgListing = $('#all-packages')
|
||||
var $searchBtn = $('.js-search-btn')
|
||||
var request
|
||||
var currentResults
|
||||
|
||||
(function( window, document ) {
|
||||
var toggle = function(validQuery) {
|
||||
$searchResults.toggleClass('show', validQuery)
|
||||
$pkgListing.toggleClass('hide', validQuery)
|
||||
|
||||
var $form = $('#search-form');
|
||||
var $input = $form.find('input');
|
||||
var $body = $('body');
|
||||
var $clear = $form.find('.clear');
|
||||
var $searchResults = $("#search-results");
|
||||
var $pkgListing = $("#all-packages");
|
||||
var $searchBtn = $('.js-search-btn');
|
||||
var request;
|
||||
var currentResults;
|
||||
$searchBtn.find('i').toggleClass('icon-cancel', validQuery)
|
||||
$searchBtn.find('i').toggleClass('icon-search', !validQuery)
|
||||
}
|
||||
|
||||
var toggle = function( validQuery ) {
|
||||
$searchResults.toggleClass( 'show', validQuery );
|
||||
$pkgListing.toggleClass( 'hide', validQuery );
|
||||
$form.bind('submit keyup', function(e) {
|
||||
var q, qBool
|
||||
|
||||
e.preventDefault()
|
||||
|
||||
$searchBtn.find('i').toggleClass( 'icon-cancel', validQuery );
|
||||
$searchBtn.find('i').toggleClass( 'icon-search', !validQuery );
|
||||
};
|
||||
q = $input.val()
|
||||
qBool = (q !== '')
|
||||
|
||||
$form.bind('submit keyup', function(e) {
|
||||
var q, qBool;
|
||||
toggle(qBool)
|
||||
|
||||
e.preventDefault();
|
||||
if (!qBool) {
|
||||
if (request && typeof request.abort === 'function') {
|
||||
request.abort()
|
||||
}
|
||||
|
||||
q = $input.val();
|
||||
qBool = q !== '';
|
||||
currentResults = null
|
||||
$searchResults.html('')
|
||||
return
|
||||
}
|
||||
|
||||
toggle( qBool );
|
||||
if (request && typeof request.abort === 'function') {
|
||||
request.abort()
|
||||
}
|
||||
|
||||
if( !qBool ) {
|
||||
if( request && typeof request.abort === 'function' ) {
|
||||
request.abort();
|
||||
}
|
||||
if (!currentResults) {
|
||||
$searchResults.html(
|
||||
"<img class='search-ajax' src='-/static/ajax.gif' alt='Spinner'/>")
|
||||
}
|
||||
|
||||
currentResults = null;
|
||||
$searchResults.html('');
|
||||
return;
|
||||
}
|
||||
request = $.getJSON('-/search/' + q, function( results ) {
|
||||
currentResults = results
|
||||
|
||||
if( request && typeof request.abort === 'function' ) {
|
||||
request.abort();
|
||||
}
|
||||
if (results.length > 0) {
|
||||
var html = ''
|
||||
|
||||
if( !currentResults ) {
|
||||
$searchResults.html( "<img class='search-ajax' src='-/static/ajax.gif' alt='Spinner'/>" );
|
||||
}
|
||||
$.each(results, function(i, entry) {
|
||||
html += template(entry)
|
||||
})
|
||||
|
||||
request = $.getJSON('-/search/' + q, function( results ) {
|
||||
currentResults = results;
|
||||
$searchResults.html(html)
|
||||
} else {
|
||||
$searchResults.html(
|
||||
"<div class='no-results'><big>No Results</big></div>")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if( results.length > 0 ) {
|
||||
var html = '';
|
||||
$(document).on('click', '.icon-cancel', function(e) {
|
||||
e.preventDefault()
|
||||
$input.val('')
|
||||
$form.keyup()
|
||||
})
|
||||
|
||||
$.each(results, function( i, entry ) {
|
||||
html += template( entry );
|
||||
});
|
||||
|
||||
$searchResults.html(html);
|
||||
} else {
|
||||
$searchResults.html("<div class='no-results'><big>No Results</big></div>");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$( document ).on( 'click', '.icon-cancel', function( e ) {
|
||||
e.preventDefault();
|
||||
$input.val('');
|
||||
$form.keyup();
|
||||
});
|
||||
|
||||
})( window, window.document );
|
||||
});
|
||||
})(window, window.document)
|
||||
})
|
||||
|
|
333
lib/auth.js
333
lib/auth.js
|
@ -1,218 +1,221 @@
|
|||
var Path = require('path')
|
||||
, crypto = require('crypto')
|
||||
, Error = require('http-errors')
|
||||
, Logger = require('./logger')
|
||||
, assert = require('assert')
|
||||
var assert = require('assert')
|
||||
var Crypto = require('crypto')
|
||||
var Error = require('http-errors')
|
||||
var Path = require('path')
|
||||
var Logger = require('./logger')
|
||||
|
||||
module.exports = Auth
|
||||
|
||||
function Auth(config) {
|
||||
if (!(this instanceof Auth)) return new Auth(config)
|
||||
this.config = config
|
||||
this.logger = Logger.logger.child({sub: 'auth'})
|
||||
var stuff = {
|
||||
config: config,
|
||||
logger: this.logger,
|
||||
}
|
||||
var self = Object.create(Auth.prototype)
|
||||
self.config = config
|
||||
self.logger = Logger.logger.child({ sub: 'auth' })
|
||||
|
||||
if (config.users_file) {
|
||||
if (!config.auth || !config.auth.htpasswd) {
|
||||
// b/w compat
|
||||
config.auth = config.auth || {}
|
||||
config.auth.htpasswd = {file: config.users_file}
|
||||
}
|
||||
}
|
||||
var stuff = {
|
||||
config: config,
|
||||
logger: self.logger,
|
||||
}
|
||||
|
||||
this.plugins = Object.keys(config.auth || {}).map(function(p) {
|
||||
var plugin, name
|
||||
try {
|
||||
name = 'sinopia-' + p
|
||||
plugin = require(name)
|
||||
} catch(x) {
|
||||
try {
|
||||
name = p
|
||||
plugin = require(name)
|
||||
} catch(x) {}
|
||||
}
|
||||
if (config.users_file) {
|
||||
if (!config.auth || !config.auth.htpasswd) {
|
||||
// b/w compat
|
||||
config.auth = config.auth || {}
|
||||
config.auth.htpasswd = { file: config.users_file }
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin == null) {
|
||||
throw Error('"' + p + '" auth plugin not found\n'
|
||||
+ 'try "npm install sinopia-' + p + '"')
|
||||
}
|
||||
self.plugins = Object.keys(config.auth || {}).map(function(p) {
|
||||
var plugin, name
|
||||
try {
|
||||
name = 'sinopia-' + p
|
||||
plugin = require(name)
|
||||
} catch(x) {
|
||||
try {
|
||||
name = p
|
||||
plugin = require(name)
|
||||
} catch(x) {}
|
||||
}
|
||||
|
||||
if (typeof(plugin) !== 'function')
|
||||
throw Error('"' + name + '" doesn\'t look like a valid auth plugin')
|
||||
if (plugin == null) {
|
||||
throw Error('"' + p + '" auth plugin not found\n'
|
||||
+ 'try "npm install sinopia-' + p + '"')
|
||||
}
|
||||
|
||||
plugin = plugin(config.auth[p], stuff)
|
||||
if (typeof(plugin) !== 'function')
|
||||
throw Error('"' + name + '" doesn\'t look like a valid auth plugin')
|
||||
|
||||
if (plugin == null || typeof(plugin.authenticate) !== 'function')
|
||||
throw Error('"' + name + '" doesn\'t look like a valid auth plugin')
|
||||
plugin = plugin(config.auth[p], stuff)
|
||||
|
||||
return plugin
|
||||
})
|
||||
if (plugin == null || typeof(plugin.authenticate) !== 'function')
|
||||
throw Error('"' + name + '" doesn\'t look like a valid auth plugin')
|
||||
|
||||
this.plugins.unshift({
|
||||
authenticate: function(user, password, cb) {
|
||||
if (config.users != null
|
||||
&& config.users[user] != null
|
||||
&& (crypto.createHash('sha1').update(password).digest('hex')
|
||||
=== config.users[user].password)
|
||||
) {
|
||||
return cb(null, [ user ])
|
||||
}
|
||||
return plugin
|
||||
})
|
||||
|
||||
return cb()
|
||||
},
|
||||
self.plugins.unshift({
|
||||
authenticate: function(user, password, cb) {
|
||||
if (config.users != null
|
||||
&& config.users[user] != null
|
||||
&& (Crypto.createHash('sha1').update(password).digest('hex')
|
||||
=== config.users[user].password)
|
||||
) {
|
||||
return cb(null, [ user ])
|
||||
}
|
||||
|
||||
adduser: function(user, password, cb) {
|
||||
if (config.users && config.users[user])
|
||||
return cb(Error[403]('this user already exists'))
|
||||
return cb()
|
||||
},
|
||||
|
||||
return cb()
|
||||
},
|
||||
})
|
||||
adduser: function(user, password, cb) {
|
||||
if (config.users && config.users[user])
|
||||
return cb( Error[403]('self user already exists') )
|
||||
|
||||
this.plugins.push({
|
||||
authenticate: function(user, password, cb) {
|
||||
return cb(Error[403]('bad username/password, access denied'))
|
||||
},
|
||||
return cb()
|
||||
},
|
||||
})
|
||||
|
||||
adduser: function(user, password, cb) {
|
||||
return cb(Error[409]('registration is disabled'))
|
||||
},
|
||||
})
|
||||
self.plugins.push({
|
||||
authenticate: function(user, password, cb) {
|
||||
return cb( Error[403]('bad username/password, access denied') )
|
||||
},
|
||||
|
||||
adduser: function(user, password, cb) {
|
||||
return cb( Error[409]('registration is disabled') )
|
||||
},
|
||||
})
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
Auth.prototype.authenticate = function(user, password, cb) {
|
||||
var plugins = this.plugins.slice(0)
|
||||
var plugins = this.plugins.slice(0)
|
||||
|
||||
!function next() {
|
||||
var p = plugins.shift()
|
||||
p.authenticate(user, password, function(err, groups) {
|
||||
if (err || groups) return cb(err, groups)
|
||||
next()
|
||||
})
|
||||
}()
|
||||
;(function next() {
|
||||
var p = plugins.shift()
|
||||
p.authenticate(user, password, function(err, groups) {
|
||||
if (err || groups) return cb(err, groups)
|
||||
next()
|
||||
})
|
||||
})()
|
||||
}
|
||||
|
||||
Auth.prototype.add_user = function(user, password, cb) {
|
||||
var plugins = this.plugins.slice(0)
|
||||
var plugins = this.plugins.slice(0)
|
||||
|
||||
!function next() {
|
||||
var p = plugins.shift()
|
||||
var n = 'adduser'
|
||||
if (typeof(p[n]) !== 'function') {
|
||||
n = 'add_user'
|
||||
}
|
||||
if (typeof(p[n]) !== 'function') {
|
||||
next()
|
||||
} else {
|
||||
p[n](user, password, function(err, ok) {
|
||||
if (err || ok) return cb(err, ok)
|
||||
next()
|
||||
})
|
||||
}
|
||||
}()
|
||||
;(function next() {
|
||||
var p = plugins.shift()
|
||||
var n = 'adduser'
|
||||
if (typeof(p[n]) !== 'function') {
|
||||
n = 'add_user'
|
||||
}
|
||||
if (typeof(p[n]) !== 'function') {
|
||||
next()
|
||||
} else {
|
||||
p[n](user, password, function(err, ok) {
|
||||
if (err || ok) return cb(err, ok)
|
||||
next()
|
||||
})
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
Auth.prototype.auth_middleware = function() {
|
||||
var self = this
|
||||
return function(req, res, _next) {
|
||||
req.pause()
|
||||
function next(err) {
|
||||
req.resume()
|
||||
// uncomment this to reject users with bad auth headers
|
||||
//return _next.apply(null, arguments)
|
||||
var self = this
|
||||
return function(req, res, _next) {
|
||||
req.pause()
|
||||
function next(err) {
|
||||
req.resume()
|
||||
// uncomment this to reject users with bad auth headers
|
||||
//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()
|
||||
}
|
||||
// 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 (req.remote_user != null && req.remote_user.name !== undefined) return next()
|
||||
req.remote_user = AnonymousUser()
|
||||
if (req.remote_user != null && req.remote_user.name !== undefined)
|
||||
return next()
|
||||
req.remote_user = AnonymousUser()
|
||||
|
||||
var authorization = req.headers.authorization
|
||||
if (authorization == null) return next()
|
||||
var authorization = req.headers.authorization
|
||||
if (authorization == null) return next()
|
||||
|
||||
var parts = authorization.split(' ')
|
||||
var parts = authorization.split(' ')
|
||||
|
||||
if (parts.length !== 2) return next({
|
||||
status: 400,
|
||||
message: 'bad authorization header',
|
||||
})
|
||||
if (parts.length !== 2)
|
||||
return next( Error[400]('bad authorization header') )
|
||||
|
||||
var scheme = parts[0]
|
||||
, credentials = new Buffer(parts[1], 'base64').toString()
|
||||
, index = credentials.indexOf(':')
|
||||
var scheme = parts[0]
|
||||
var credentials = Buffer(parts[1], 'base64').toString()
|
||||
var index = credentials.indexOf(':')
|
||||
|
||||
if (scheme !== 'Basic' || index < 0) return next({
|
||||
status: 400,
|
||||
message: 'bad authorization header',
|
||||
})
|
||||
if (scheme !== 'Basic' || index < 0)
|
||||
return next( Error[400]('bad authorization header') )
|
||||
|
||||
var user = credentials.slice(0, index)
|
||||
, pass = credentials.slice(index + 1)
|
||||
var user = credentials.slice(0, index)
|
||||
var pass = credentials.slice(index + 1)
|
||||
|
||||
self.authenticate(user, pass, function(err, groups) {
|
||||
if (!err && groups != null && groups != false) {
|
||||
req.remote_user = AuthenticatedUser(user, groups)
|
||||
next()
|
||||
} else {
|
||||
req.remote_user = AnonymousUser()
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
self.authenticate(user, pass, function(err, groups) {
|
||||
if (!err && groups != null && groups != false) {
|
||||
req.remote_user = AuthenticatedUser(user, groups)
|
||||
next()
|
||||
} else {
|
||||
req.remote_user = AnonymousUser()
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Auth.prototype.cookie_middleware = function() {
|
||||
var self = this
|
||||
return function(req, res, _next) {
|
||||
req.pause()
|
||||
function next(err) {
|
||||
req.resume()
|
||||
return _next()
|
||||
}
|
||||
var self = this
|
||||
|
||||
if (req.remote_user != null && req.remote_user.name !== undefined) return next()
|
||||
req.remote_user = AnonymousUser()
|
||||
return function(req, res, _next) {
|
||||
req.pause()
|
||||
function next(err) {
|
||||
req.resume()
|
||||
return _next()
|
||||
}
|
||||
|
||||
var cookie = req.cookies.get('token')
|
||||
if (cookie == null) return next()
|
||||
if (req.remote_user != null && req.remote_user.name !== undefined)
|
||||
return next()
|
||||
|
||||
var credentials = new Buffer(cookie, 'base64').toString()
|
||||
var index = credentials.indexOf(':')
|
||||
req.remote_user = AnonymousUser()
|
||||
|
||||
var user = credentials.slice(0, index)
|
||||
var pass = credentials.slice(index + 1)
|
||||
var cookie = req.cookies.get('token')
|
||||
if (cookie == null) return next()
|
||||
|
||||
self.authenticate(user, pass, function(err, groups) {
|
||||
if (!err && groups != null && groups != false) {
|
||||
req.remote_user = AuthenticatedUser(user, groups)
|
||||
next()
|
||||
} else {
|
||||
req.remote_user = AnonymousUser()
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
var credentials = Buffer(cookie, 'base64').toString()
|
||||
var index = credentials.indexOf(':')
|
||||
|
||||
var user = credentials.slice(0, index)
|
||||
var pass = credentials.slice(index + 1)
|
||||
|
||||
self.authenticate(user, pass, function(err, groups) {
|
||||
if (!err && groups != null && groups != false) {
|
||||
req.remote_user = AuthenticatedUser(user, groups)
|
||||
next()
|
||||
} else {
|
||||
req.remote_user = AnonymousUser()
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function AnonymousUser() {
|
||||
return {
|
||||
name: undefined,
|
||||
// groups without '@' are going to be deprecated eventually
|
||||
groups: ['$all', '$anonymous', '@all', '@anonymous', 'all', 'undefined', 'anonymous'],
|
||||
}
|
||||
return {
|
||||
name: undefined,
|
||||
// groups without '@' are going to be deprecated eventually
|
||||
groups: [ '$all', '$anonymous', '@all', '@anonymous', 'all', 'undefined', 'anonymous' ],
|
||||
}
|
||||
}
|
||||
|
||||
function AuthenticatedUser(name, groups) {
|
||||
groups = groups.concat(['$all', '$authenticated', '@all', '@authenticated', 'all'])
|
||||
return {
|
||||
name: name,
|
||||
groups: groups,
|
||||
}
|
||||
groups = groups.concat([ '$all', '$authenticated', '@all', '@authenticated', 'all' ])
|
||||
return {
|
||||
name: name,
|
||||
groups: groups,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
200
lib/cli.js
200
lib/cli.js
|
@ -3,147 +3,147 @@
|
|||
/*eslint no-sync:0*/
|
||||
|
||||
if (process.getuid && process.getuid() === 0) {
|
||||
global.console.error("Sinopia doesn't need superuser privileges. Don't run it under root.")
|
||||
global.console.error("Sinopia doesn't need superuser privileges. Don't run it under root.")
|
||||
}
|
||||
|
||||
process.title = 'sinopia'
|
||||
|
||||
try {
|
||||
// for debugging memory leaks
|
||||
// totally optional
|
||||
require('heapdump')
|
||||
} catch(err){}
|
||||
// for debugging memory leaks
|
||||
// totally optional
|
||||
require('heapdump')
|
||||
} catch(err) {}
|
||||
|
||||
var logger = require('./logger')
|
||||
logger.setup() // default setup
|
||||
|
||||
var pkg_file = '../package.yaml'
|
||||
, fs = require('fs')
|
||||
, yaml = require('js-yaml')
|
||||
, commander = require('commander')
|
||||
, server = require('./index')
|
||||
, crypto = require('crypto')
|
||||
, Path = require('path')
|
||||
, pkg = yaml.safeLoad(fs.readFileSync(__dirname + '/' + pkg_file, 'utf8'))
|
||||
var commander = require('commander')
|
||||
var fs = require('fs')
|
||||
var YAML = require('js-yaml')
|
||||
var Path = require('path')
|
||||
var server = require('./index')
|
||||
var pkg_file = '../package.yaml'
|
||||
var pkg = YAML.safeLoad(fs.readFileSync(__dirname+'/'+ pkg_file, 'utf8'))
|
||||
|
||||
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(pkg.version)
|
||||
.parse(process.argv)
|
||||
.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(pkg.version)
|
||||
.parse(process.argv)
|
||||
|
||||
if (commander.args.length == 1 && !commander.config) {
|
||||
// handling "sinopia [config]" case if "-c" is missing in commandline
|
||||
commander.config = commander.args.pop()
|
||||
// handling "sinopia [config]" case if "-c" is missing in commandline
|
||||
commander.config = commander.args.pop()
|
||||
}
|
||||
|
||||
if (commander.args.length != 0) {
|
||||
commander.help()
|
||||
commander.help()
|
||||
}
|
||||
|
||||
var config, config_path, have_question
|
||||
try {
|
||||
if (commander.config) {
|
||||
config_path = commander.config
|
||||
config = yaml.safeLoad(fs.readFileSync(config_path, 'utf8'))
|
||||
} else {
|
||||
config_path = './config.yaml'
|
||||
try {
|
||||
config = yaml.safeLoad(fs.readFileSync(config_path, 'utf8'))
|
||||
} catch(err) {
|
||||
var readline = require('readline')
|
||||
var rl = readline.createInterface(process.stdin, process.stdout)
|
||||
var timeout = setTimeout(function() {
|
||||
global.console.log('I got tired waiting for an answer. Exitting...')
|
||||
process.exit(1)
|
||||
}, 20000)
|
||||
if (commander.config) {
|
||||
config_path = commander.config
|
||||
config = YAML.safeLoad(fs.readFileSync(config_path, 'utf8'))
|
||||
} else {
|
||||
config_path = './config.yaml'
|
||||
try {
|
||||
config = YAML.safeLoad(fs.readFileSync(config_path, 'utf8'))
|
||||
} catch(err) {
|
||||
var readline = require('readline')
|
||||
var rl = readline.createInterface(process.stdin, process.stdout)
|
||||
var timeout = setTimeout(function() {
|
||||
global.console.log('I got tired waiting for an answer. Exitting...')
|
||||
process.exit(1)
|
||||
}, 20000)
|
||||
|
||||
;(function askUser() {
|
||||
have_question = true
|
||||
rl.question('Config file doesn\'t exist, create a new one? (Y/n) ', function(x) {
|
||||
clearTimeout(timeout)
|
||||
if (x[0] == 'Y' || x[0] == 'y' || x === '') {
|
||||
rl.close()
|
||||
;(function askUser() {
|
||||
have_question = true
|
||||
rl.question('Config file doesn\'t exist, create a new one? (Y/n) ', function(x) {
|
||||
clearTimeout(timeout)
|
||||
if (x[0] == 'Y' || x[0] == 'y' || x === '') {
|
||||
rl.close()
|
||||
|
||||
var created_config = require('../lib/config_gen')()
|
||||
config = yaml.safeLoad(created_config.yaml)
|
||||
write_config_banner(created_config, config)
|
||||
fs.writeFileSync(config_path, created_config.yaml)
|
||||
afterConfigLoad()
|
||||
} else if (x[0] == 'N' || x[0] == 'n') {
|
||||
rl.close()
|
||||
global.console.log('So, you just accidentally run me in a wrong folder. Exitting...')
|
||||
process.exit(1)
|
||||
} else {
|
||||
askUser()
|
||||
}
|
||||
})
|
||||
})()
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
logger.logger.fatal({file: config_path, err: err}, 'cannot open config file @{file}: @{!err.message}')
|
||||
process.exit(1)
|
||||
var created_config = require('../lib/config_gen')()
|
||||
config = YAML.safeLoad(created_config.yaml)
|
||||
write_config_banner(created_config, config)
|
||||
fs.writeFileSync(config_path, created_config.yaml)
|
||||
afterConfigLoad()
|
||||
} else if (x[0] == 'N' || x[0] == 'n') {
|
||||
rl.close()
|
||||
global.console.log('So, you just accidentally run me in a wrong folder. Exitting...')
|
||||
process.exit(1)
|
||||
} else {
|
||||
askUser()
|
||||
}
|
||||
})
|
||||
})()
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.logger.fatal({ file: config_path, err: err }, 'cannot open config file @{file}: @{!err.message}')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (!have_question) afterConfigLoad()
|
||||
|
||||
function get_hostport() {
|
||||
// command line || config file || default
|
||||
var hostport = commander.listen || String(config.listen || '') || '4873'
|
||||
// command line || config file || default
|
||||
var hostport = commander.listen || String(config.listen || '') || '4873'
|
||||
|
||||
hostport = hostport.split(':')
|
||||
if (hostport.length < 2) {
|
||||
hostport = [undefined, hostport[0]]
|
||||
}
|
||||
if (hostport[0] == null) {
|
||||
hostport[0] = 'localhost'
|
||||
}
|
||||
return hostport
|
||||
hostport = hostport.split(':')
|
||||
if (hostport.length < 2) {
|
||||
hostport = [ undefined, hostport[0] ]
|
||||
}
|
||||
if (hostport[0] == null) {
|
||||
hostport[0] = 'localhost'
|
||||
}
|
||||
return hostport
|
||||
}
|
||||
|
||||
function afterConfigLoad() {
|
||||
if (!config.user_agent) config.user_agent = 'Sinopia/'+pkg.version
|
||||
if (!config.self_path) config.self_path = Path.resolve(config_path)
|
||||
if (!config.user_agent) config.user_agent = 'Sinopia/'+pkg.version
|
||||
if (!config.self_path) config.self_path = Path.resolve(config_path)
|
||||
|
||||
logger.setup(config.logs)
|
||||
logger.setup(config.logs)
|
||||
|
||||
var hostport = get_hostport()
|
||||
server(config)
|
||||
.listen(hostport[1], hostport[0])
|
||||
.on('error', function(err) {
|
||||
logger.logger.fatal({err: err}, 'cannot create server: @{err.message}')
|
||||
process.exit(2)
|
||||
})
|
||||
var hostport = get_hostport()
|
||||
server(config)
|
||||
.listen(hostport[1], hostport[0])
|
||||
.on('error', function(err) {
|
||||
logger.logger.fatal({ err: err }, 'cannot create server: @{err.message}')
|
||||
process.exit(2)
|
||||
})
|
||||
|
||||
logger.logger.warn({addr: 'http://'+hostport[0]+':'+hostport[1]+'/', version: 'Sinopia/'+pkg.version}, 'Server is listening on @{addr}')
|
||||
logger.logger.warn({ addr: 'http://'+hostport[0]+':'+hostport[1]+'/', version: 'Sinopia/'+pkg.version }, 'Server is listening on @{addr}')
|
||||
|
||||
// undocumented stuff for tests
|
||||
if (typeof(process.send) === 'function') {
|
||||
process.send({sinopia_started: hostport})
|
||||
}
|
||||
// undocumented stuff for tests
|
||||
if (typeof(process.send) === 'function') {
|
||||
process.send({ sinopia_started: hostport })
|
||||
}
|
||||
}
|
||||
|
||||
function write_config_banner(def, config) {
|
||||
var hostport = get_hostport()
|
||||
var log = global.console.log
|
||||
var hostport = get_hostport()
|
||||
var log = global.console.log
|
||||
|
||||
log('===========================================================')
|
||||
log(' Creating a new configuration file: "%s"', config_path)
|
||||
log(' ')
|
||||
log(' If you want to setup npm to work with this registry,')
|
||||
log(' run following commands:')
|
||||
log(' ')
|
||||
log(' $ npm set registry http://%s:%s/', hostport[0], hostport[1])
|
||||
log(' $ npm set always-auth true')
|
||||
log(' $ npm adduser')
|
||||
log(' Username: %s', def.user)
|
||||
log(' Password: %s', def.pass)
|
||||
log('===========================================================')
|
||||
log('===========================================================')
|
||||
log(' Creating a new configuration file: "%s"', config_path)
|
||||
log(' ')
|
||||
log(' If you want to setup npm to work with this registry,')
|
||||
log(' run following commands:')
|
||||
log(' ')
|
||||
log(' $ npm set registry http://%s:%s/', hostport[0], hostport[1])
|
||||
log(' $ npm set always-auth true')
|
||||
log(' $ npm adduser')
|
||||
log(' Username: %s', def.user)
|
||||
log(' Password: %s', def.pass)
|
||||
log('===========================================================')
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
|
|
270
lib/config.js
270
lib/config.js
|
@ -1,187 +1,189 @@
|
|||
var assert = require('assert')
|
||||
, crypto = require('crypto')
|
||||
, Path = require('path')
|
||||
, minimatch = require('minimatch')
|
||||
, Error = require('http-errors')
|
||||
, LocalList = require('./local-list')
|
||||
, utils = require('./utils')
|
||||
var assert = require('assert')
|
||||
var Crypto = require('crypto')
|
||||
var Error = require('http-errors')
|
||||
var minimatch = require('minimatch')
|
||||
var Path = require('path')
|
||||
var LocalList = require('./local-list')
|
||||
var Utils = require('./utils')
|
||||
|
||||
// [[a, [b, c]], d] -> [a, b, c, d]
|
||||
function flatten(array) {
|
||||
var result = []
|
||||
for (var i=0; i<array.length; i++) {
|
||||
if (Array.isArray(array[i])) {
|
||||
result.push.apply(result, flatten(array[i]))
|
||||
} else {
|
||||
result.push(array[i])
|
||||
}
|
||||
}
|
||||
return result
|
||||
var result = []
|
||||
for (var i=0; i<array.length; i++) {
|
||||
if (Array.isArray(array[i])) {
|
||||
result.push.apply(result, flatten(array[i]))
|
||||
} else {
|
||||
result.push(array[i])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function Config(config) {
|
||||
if (!(this instanceof Config)) return new Config(config)
|
||||
for (var i in config) {
|
||||
if (this[i] == null) this[i] = config[i]
|
||||
}
|
||||
var self = Object.create(Config.prototype)
|
||||
for (var i in config) {
|
||||
if (self[i] == null) self[i] = config[i]
|
||||
}
|
||||
|
||||
// some weird shell scripts are valid yaml files parsed as string
|
||||
assert.equal(typeof(config), 'object', 'CONFIG: this doesn\'t look like a valid config file')
|
||||
// some weird shell scripts are valid yaml files parsed as string
|
||||
assert.equal(typeof(config), 'object', 'CONFIG: self doesn\'t look like a valid config file')
|
||||
|
||||
assert(this.storage, 'CONFIG: storage path not defined')
|
||||
this.localList = LocalList(
|
||||
Path.join(
|
||||
Path.resolve(Path.dirname(this.self_path), this.storage),
|
||||
'.sinopia-db.json'
|
||||
)
|
||||
)
|
||||
assert(self.storage, 'CONFIG: storage path not defined')
|
||||
self.localList = LocalList(
|
||||
Path.join(
|
||||
Path.resolve(Path.dirname(self.self_path), self.storage),
|
||||
'.sinopia-db.json'
|
||||
)
|
||||
)
|
||||
|
||||
var users = {all:true, anonymous:true, 'undefined':true, owner:true, none:true}
|
||||
var 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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
;['users', 'uplinks', 'packages'].forEach(function(x) {
|
||||
if (this[x] == null) this[x] = {}
|
||||
assert(utils.is_object(this[x]), 'CONFIG: bad "'+x+'" value (object expected)')
|
||||
})
|
||||
;[ '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 this.users) check_user_or_uplink(i)
|
||||
for (var i in this.uplinks) check_user_or_uplink(i)
|
||||
for (var i in self.users) check_user_or_uplink(i)
|
||||
for (var i in self.uplinks) check_user_or_uplink(i)
|
||||
|
||||
for (var i in this.users) {
|
||||
assert(this.users[i].password, 'CONFIG: no password for user: ' + i)
|
||||
assert(
|
||||
typeof(this.users[i].password) === 'string' &&
|
||||
this.users[i].password.match(/^[a-f0-9]{40}$/)
|
||||
, 'CONFIG: wrong password format for user: ' + i + ', sha1 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' &&
|
||||
self.users[i].password.match(/^[a-f0-9]{40}$/)
|
||||
, 'CONFIG: wrong password format for user: ' + i + ', sha1 expected')
|
||||
}
|
||||
|
||||
for (var i in this.uplinks) {
|
||||
assert(this.uplinks[i].url, 'CONFIG: no url for uplink: ' + i)
|
||||
assert(
|
||||
typeof(this.uplinks[i].url) === 'string'
|
||||
, 'CONFIG: wrong url format for uplink: ' + i)
|
||||
this.uplinks[i].url = this.uplinks[i].url.replace(/\/$/, '')
|
||||
}
|
||||
for (var i in self.uplinks) {
|
||||
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(/\/$/, '')
|
||||
}
|
||||
|
||||
function check_userlist(i, hash, action) {
|
||||
if (hash[action] == null) hash[action] = []
|
||||
function check_userlist(i, hash, action) {
|
||||
if (hash[action] == null) hash[action] = []
|
||||
|
||||
// if it's a string, split it to array
|
||||
if (typeof(hash[action]) === 'string') {
|
||||
hash[action] = hash[action].split(/\s+/)
|
||||
}
|
||||
// if it's a string, split it to array
|
||||
if (typeof(hash[action]) === 'string') {
|
||||
hash[action] = hash[action].split(/\s+/)
|
||||
}
|
||||
|
||||
assert(
|
||||
typeof(hash[action]) === 'object' &&
|
||||
Array.isArray(hash[action])
|
||||
, 'CONFIG: bad "'+i+'" package '+action+' description (array or string expected)')
|
||||
hash[action] = flatten(hash[action])
|
||||
}
|
||||
assert(
|
||||
typeof(hash[action]) === 'object' &&
|
||||
Array.isArray(hash[action])
|
||||
, 'CONFIG: bad "'+i+'" package '+action+' description (array or string expected)')
|
||||
hash[action] = flatten(hash[action])
|
||||
}
|
||||
|
||||
for (var i in this.packages) {
|
||||
assert(
|
||||
typeof(this.packages[i]) === 'object' &&
|
||||
!Array.isArray(this.packages[i])
|
||||
, 'CONFIG: bad "'+i+'" package description (object expected)')
|
||||
for (var i in self.packages) {
|
||||
assert(
|
||||
typeof(self.packages[i]) === 'object' &&
|
||||
!Array.isArray(self.packages[i])
|
||||
, 'CONFIG: bad "'+i+'" package description (object expected)')
|
||||
|
||||
check_userlist(i, this.packages[i], 'allow_access')
|
||||
check_userlist(i, this.packages[i], 'allow_publish')
|
||||
check_userlist(i, this.packages[i], 'proxy_access')
|
||||
check_userlist(i, this.packages[i], 'proxy_publish')
|
||||
check_userlist(i, self.packages[i], 'allow_access')
|
||||
check_userlist(i, self.packages[i], 'allow_publish')
|
||||
check_userlist(i, self.packages[i], 'proxy_access')
|
||||
check_userlist(i, self.packages[i], 'proxy_publish')
|
||||
|
||||
// deprecated
|
||||
check_userlist(i, this.packages[i], 'access')
|
||||
check_userlist(i, this.packages[i], 'proxy')
|
||||
check_userlist(i, this.packages[i], 'publish')
|
||||
}
|
||||
// deprecated
|
||||
check_userlist(i, self.packages[i], 'access')
|
||||
check_userlist(i, self.packages[i], 'proxy')
|
||||
check_userlist(i, self.packages[i], 'publish')
|
||||
}
|
||||
|
||||
// loading these from ENV if aren't in config
|
||||
;['http_proxy', 'https_proxy', 'no_proxy'].forEach((function(v) {
|
||||
if (!(v in this)) {
|
||||
this[v] = process.env[v] || process.env[v.toUpperCase()]
|
||||
}
|
||||
}).bind(this))
|
||||
// loading these from ENV if aren't in config
|
||||
;[ 'http_proxy', 'https_proxy', 'no_proxy' ].forEach((function(v) {
|
||||
if (!(v in self)) {
|
||||
self[v] = process.env[v] || process.env[v.toUpperCase()]
|
||||
}
|
||||
}).bind(self))
|
||||
|
||||
// unique identifier of this server (or a cluster), used to avoid loops
|
||||
if (!this.server_id) {
|
||||
this.server_id = crypto.pseudoRandomBytes(6).toString('hex')
|
||||
}
|
||||
// unique identifier of self server (or a cluster), used to avoid loops
|
||||
if (!self.server_id) {
|
||||
self.server_id = Crypto.pseudoRandomBytes(6).toString('hex')
|
||||
}
|
||||
|
||||
if (this.ignore_latest_tag == null) this.ignore_latest_tag = false
|
||||
if (self.ignore_latest_tag == null) self.ignore_latest_tag = false
|
||||
|
||||
return this
|
||||
return self
|
||||
}
|
||||
|
||||
function allow_action(package, who, action) {
|
||||
return (this.get_package_setting(package, action) || []).reduce(function(prev, curr) {
|
||||
if (typeof(who) === 'string' && curr === who) return true
|
||||
if (Array.isArray(who) && who.indexOf(curr) !== -1) return true
|
||||
return prev
|
||||
}, false)
|
||||
return (this.get_package_setting(package, action) || []).reduce(function(prev, curr) {
|
||||
if (typeof(who) === 'string' && curr === who) return true
|
||||
if (Array.isArray(who) && who.indexOf(curr) !== -1) return true
|
||||
return prev
|
||||
}, false)
|
||||
}
|
||||
|
||||
Config.prototype.allow_access = function(package, user) {
|
||||
return allow_action.call(this, package, user.groups, 'allow_access') || allow_action.call(this, package, user.groups, 'access')
|
||||
return allow_action.call(this, package, user.groups, 'allow_access')
|
||||
|| allow_action.call(this, package, user.groups, 'access')
|
||||
}
|
||||
|
||||
Config.prototype.allow_publish = function(package, user) {
|
||||
return allow_action.call(this, package, user.groups, 'allow_publish') || allow_action.call(this, package, user.groups, 'publish')
|
||||
return allow_action.call(this, package, user.groups, 'allow_publish')
|
||||
|| allow_action.call(this, package, user.groups, 'publish')
|
||||
}
|
||||
|
||||
Config.prototype.proxy_access = function(package, uplink) {
|
||||
return allow_action.call(this, package, uplink, 'proxy_access') || allow_action.call(this, package, uplink, 'proxy')
|
||||
return allow_action.call(this, package, uplink, 'proxy_access')
|
||||
|| allow_action.call(this, package, uplink, 'proxy')
|
||||
}
|
||||
|
||||
Config.prototype.proxy_publish = function(package, uplink) {
|
||||
throw new Error('deprecated')
|
||||
//return allow_action.call(this, package, uplink, 'proxy_publish')
|
||||
throw Error('deprecated')
|
||||
//return allow_action.call(this, package, uplink, 'proxy_publish')
|
||||
}
|
||||
|
||||
Config.prototype.get_package_setting = function(package, setting) {
|
||||
for (var i in this.packages) {
|
||||
if (minimatch.makeRe(i).exec(package)) {
|
||||
return this.packages[i][setting]
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
for (var i in this.packages) {
|
||||
if (minimatch.makeRe(i).exec(package)) {
|
||||
return this.packages[i][setting]
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
module.exports = Config
|
||||
|
||||
var 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,
|
||||
'': 1000,
|
||||
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
|
||||
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 (!m
|
||||
|| parse_interval_table[m[4]] >= last_suffix
|
||||
|| (m[4] === '' && last_suffix !== Infinity)) {
|
||||
throw new Error('invalid interval: ' + interval)
|
||||
}
|
||||
last_suffix = parse_interval_table[m[4]]
|
||||
result += Number(m[1]) * parse_interval_table[m[4]]
|
||||
})
|
||||
return result
|
||||
var result = 0
|
||||
var 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 (!m
|
||||
|| 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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
var fs = require('fs')
|
||||
, crypto = require('crypto')
|
||||
var Crypto = require('crypto')
|
||||
var fs = require('fs')
|
||||
|
||||
module.exports = function create_config() {
|
||||
var pass = crypto.randomBytes(8).toString('base64').replace(/[=+\/]/g, '')
|
||||
, pass_digest = crypto.createHash('sha1').update(pass).digest('hex')
|
||||
var pass = Crypto.randomBytes(8).toString('base64').replace(/[=+\/]/g, '')
|
||||
var pass_digest = Crypto.createHash('sha1').update(pass).digest('hex')
|
||||
|
||||
/*eslint no-sync:0*/
|
||||
var config = fs.readFileSync(require.resolve('./config_def.yaml'), 'utf8')
|
||||
/*eslint no-sync:0*/
|
||||
var config = fs.readFileSync(require.resolve('./config_def.yaml'), 'utf8')
|
||||
|
||||
config = config.replace('__PASSWORD__', pass_digest)
|
||||
config = config.replace('__PASSWORD__', pass_digest)
|
||||
|
||||
return {
|
||||
yaml: config,
|
||||
user: 'admin',
|
||||
pass: pass,
|
||||
}
|
||||
return {
|
||||
yaml: config,
|
||||
user: 'admin',
|
||||
pass: pass,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
187
lib/index-web.js
187
lib/index-web.js
|
@ -1,112 +1,113 @@
|
|||
var fs = require('fs')
|
||||
var marked = require('marked')
|
||||
var search = require('./search')
|
||||
var Cookies = require('cookies')
|
||||
var express = require('express')
|
||||
var fs = require('fs')
|
||||
var marked = require('marked')
|
||||
var Handlebars = require('handlebars')
|
||||
var Error = require('http-errors')
|
||||
var express = require('express')
|
||||
var Cookies = require('cookies')
|
||||
var Error = require('http-errors')
|
||||
var Search = require('./search')
|
||||
|
||||
module.exports = function(config, auth, storage) {
|
||||
var app = express()
|
||||
app.use(Cookies.express())
|
||||
app.use(express.urlencoded())
|
||||
app.use(auth.cookie_middleware())
|
||||
app.use(function(req, res, next) {
|
||||
// disable loading in frames (clickjacking, etc.)
|
||||
res.header('X-Frame-Options', 'deny')
|
||||
next()
|
||||
})
|
||||
var app = express()
|
||||
app.use(Cookies.express())
|
||||
app.use(express.urlencoded())
|
||||
app.use(auth.cookie_middleware())
|
||||
app.use(function(req, res, next) {
|
||||
// disable loading in frames (clickjacking, etc.)
|
||||
res.header('X-Frame-Options', 'deny')
|
||||
next()
|
||||
})
|
||||
|
||||
search.configureStorage(storage)
|
||||
Search.configureStorage(storage)
|
||||
|
||||
Handlebars.registerPartial('entry', fs.readFileSync(require.resolve('./GUI/entry.hbs'), 'utf8'));
|
||||
var template = Handlebars.compile(fs.readFileSync(require.resolve('./GUI/index.hbs'), 'utf8'));
|
||||
Handlebars.registerPartial('entry', fs.readFileSync(require.resolve('./GUI/entry.hbs'), 'utf8'))
|
||||
var template = Handlebars.compile(fs.readFileSync(require.resolve('./GUI/index.hbs'), 'utf8'))
|
||||
|
||||
app.get('/', function(req, res, next) {
|
||||
var base = config.url_prefix || req.protocol + '://' + req.get('host')
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
app.get('/', function(req, res, next) {
|
||||
var base = config.url_prefix || req.protocol + '://' + req.get('host')
|
||||
res.setHeader('Content-Type', 'text/html')
|
||||
|
||||
storage.get_local(function(err, packages) {
|
||||
res.send(template({
|
||||
name: config.web.title || "Sinopia",
|
||||
packages: packages,
|
||||
baseUrl: base,
|
||||
username: req.remote_user.name,
|
||||
}));
|
||||
});
|
||||
});
|
||||
storage.get_local(function(err, packages) {
|
||||
if (err) throw err // that function shouldn't produce any
|
||||
res.send(template({
|
||||
name: config.web.title || 'Sinopia',
|
||||
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
|
||||
fs.exists(file, function(exists) {
|
||||
if (exists) {
|
||||
res.sendfile(file)
|
||||
} else {
|
||||
res.status(404);
|
||||
res.send("File Not Found")
|
||||
}
|
||||
})
|
||||
})
|
||||
// Static
|
||||
app.get('/-/static/:filename', function(req, res, next) {
|
||||
var file = __dirname + '/static/' + req.params.filename
|
||||
fs.exists(file, function(exists) {
|
||||
if (exists) {
|
||||
res.sendfile(file)
|
||||
} else {
|
||||
next( Error[404]('File Not Found') )
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
app.get('/-/logo', function(req, res, next) {
|
||||
res.sendfile(config.web.logo ? config.web.logo : __dirname + "/static/logo.png")
|
||||
})
|
||||
app.get('/-/logo', function(req, res, next) {
|
||||
res.sendfile(config.web.logo ? config.web.logo : __dirname + '/static/logo.png')
|
||||
})
|
||||
|
||||
app.get('/-/logo-sm', function(req, res, next) {
|
||||
res.sendfile(config.web.logosm ? config.web.logosm : __dirname + "/static/logo-sm.png")
|
||||
})
|
||||
app.get('/-/logo-sm', function(req, res, next) {
|
||||
res.sendfile(config.web.logosm ? config.web.logosm : __dirname + '/static/logo-sm.png')
|
||||
})
|
||||
|
||||
app.post('/-/login', function(req, res, next) {
|
||||
var base = config.url_prefix || req.protocol + '://' + req.get('host')
|
||||
res.cookies.set('token', Buffer(req.body.user + ':' + req.body.pass).toString('base64'))
|
||||
res.redirect(base)
|
||||
})
|
||||
app.post('/-/login', function(req, res, next) {
|
||||
var base = config.url_prefix || req.protocol + '://' + req.get('host')
|
||||
res.cookies.set('token', Buffer(req.body.user + ':' + req.body.pass).toString('base64'))
|
||||
res.redirect(base)
|
||||
})
|
||||
|
||||
app.post('/-/logout', function(req, res, next) {
|
||||
var base = config.url_prefix || req.protocol + '://' + req.get('host')
|
||||
res.cookies.set('token', '')
|
||||
res.redirect(base)
|
||||
})
|
||||
app.post('/-/logout', function(req, res, next) {
|
||||
var base = config.url_prefix || 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),
|
||||
packages = []
|
||||
// Search
|
||||
app.get('/-/search/:anything', function(req, res, next) {
|
||||
var results = Search.query(req.params.anything),
|
||||
packages = []
|
||||
|
||||
var getData = function(i) {
|
||||
storage.get_package(results[i].ref, function(err, entry) {
|
||||
if (entry) {
|
||||
packages.push(entry.versions[entry['dist-tags'].latest])
|
||||
}
|
||||
var getData = function(i) {
|
||||
storage.get_package(results[i].ref, function(err, entry) {
|
||||
if (!err && entry) {
|
||||
packages.push(entry.versions[entry['dist-tags'].latest])
|
||||
}
|
||||
|
||||
if (i >= results.length - 1) {
|
||||
res.send(packages)
|
||||
} else {
|
||||
getData(i + 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
if (i >= results.length - 1) {
|
||||
res.send(packages)
|
||||
} else {
|
||||
getData(i + 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (results.length) {
|
||||
getData(0);
|
||||
} else {
|
||||
res.send([])
|
||||
}
|
||||
})
|
||||
if (results.length) {
|
||||
getData(0)
|
||||
} else {
|
||||
res.send([])
|
||||
}
|
||||
})
|
||||
|
||||
// Readme
|
||||
marked.setOptions({
|
||||
highlight: function (code) {
|
||||
return require('highlight.js').highlightAuto(code).value
|
||||
}
|
||||
})
|
||||
// Readme
|
||||
marked.setOptions({
|
||||
highlight: function (code) {
|
||||
return require('highlight.js').highlightAuto(code).value
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/-/readme/:package/:version?', function(req, res, next) {
|
||||
storage.get_package(req.params.package, {req: req}, function(err, info) {
|
||||
if (err) return next(err)
|
||||
res.send(marked(info.readme || 'ERROR: No README data found!'))
|
||||
})
|
||||
})
|
||||
return app
|
||||
app.get('/-/readme/:package/:version?', function(req, res, next) {
|
||||
storage.get_package(req.params.package, {req: req}, function(err, info) {
|
||||
if (err) return next(err)
|
||||
res.send( marked(info.readme || 'ERROR: No README data found!') )
|
||||
})
|
||||
})
|
||||
return app
|
||||
}
|
||||
|
||||
|
|
731
lib/index.js
731
lib/index.js
|
@ -1,433 +1,416 @@
|
|||
var express = require('express')
|
||||
, cookies = require('cookies')
|
||||
, utils = require('./utils')
|
||||
, Storage = require('./storage')
|
||||
, Config = require('./config')
|
||||
, Error = require('http-errors')
|
||||
, Middleware = require('./middleware')
|
||||
, Logger = require('./logger')
|
||||
, Cats = require('./status-cats')
|
||||
, validate_name = Middleware.validate_name
|
||||
, media = Middleware.media
|
||||
, expect_json = Middleware.expect_json
|
||||
, fs = require('fs')
|
||||
, Auth = require('./auth')
|
||||
var Cookies = require('cookies')
|
||||
var express = require('express')
|
||||
var fs = require('fs')
|
||||
var Error = require('http-errors')
|
||||
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')
|
||||
var Utils = require('./utils')
|
||||
var expect_json = Middleware.expect_json
|
||||
var media = Middleware.media
|
||||
var validate_name = Middleware.validate_name
|
||||
|
||||
function match(regexp) {
|
||||
return function(req, res, next, value, name) {
|
||||
if (regexp.exec(value)) {
|
||||
next()
|
||||
} else {
|
||||
next('route')
|
||||
}
|
||||
}
|
||||
return function(req, res, next, value, name) {
|
||||
if (regexp.exec(value)) {
|
||||
next()
|
||||
} else {
|
||||
next('route')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function(config_hash) {
|
||||
var config = new Config(config_hash)
|
||||
, storage = new Storage(config)
|
||||
, auth = new Auth(config)
|
||||
var config = Config(config_hash)
|
||||
var storage = Storage(config)
|
||||
var auth = Auth(config)
|
||||
|
||||
var can = function(action) {
|
||||
return function(req, res, next) {
|
||||
if (config['allow_'+action](req.params.package, req.remote_user)) {
|
||||
next()
|
||||
} else {
|
||||
if (!req.remote_user.name) {
|
||||
if (req.remote_user.error) {
|
||||
var message = "can't "+action+' restricted package, ' + req.remote_user.error
|
||||
} else {
|
||||
var message = "can't "+action+" restricted package without auth, did you forget 'npm set always-auth true'?"
|
||||
}
|
||||
next(Error[403](message))
|
||||
} else {
|
||||
next(Error[403]('user ' + req.remote_user.name
|
||||
+ ' not allowed to ' + action + ' it'))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var can = function(action) {
|
||||
return function(req, res, next) {
|
||||
if (config['allow_'+action](req.params.package, req.remote_user)) {
|
||||
next()
|
||||
} else {
|
||||
if (!req.remote_user.name) {
|
||||
if (req.remote_user.error) {
|
||||
var message = "can't "+action+' restricted package, ' + req.remote_user.error
|
||||
} else {
|
||||
var message = "can't "+action+" restricted package without auth, did you forget 'npm set always-auth true'?"
|
||||
}
|
||||
next( Error[403](message) )
|
||||
} else {
|
||||
next( Error[403]('user ' + req.remote_user.name
|
||||
+ ' not allowed to ' + action + ' it') )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var app = express()
|
||||
var 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')
|
||||
// run in production mode by default, just in case
|
||||
// it shouldn't make any difference anyway
|
||||
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)
|
||||
res.send({error: err.message || 'unknown error'})
|
||||
}
|
||||
} else {
|
||||
Logger.logger.error({err: err}, 'unexpected error: @{!err.message}\n@{err.stack}')
|
||||
if (!res.status || !res.send) {
|
||||
Logger.logger.error('this is an error in express.js, please report this')
|
||||
res.destroy()
|
||||
} else if (!res.headersSent) {
|
||||
res.status(500)
|
||||
res.send({error: 'internal server error'})
|
||||
} else {
|
||||
// socket should be already closed
|
||||
}
|
||||
}
|
||||
}
|
||||
next()
|
||||
}
|
||||
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)
|
||||
res.send({ error: err.message || 'unknown error' })
|
||||
}
|
||||
} else {
|
||||
Logger.logger.error( { err: err }
|
||||
, 'unexpected error: @{!err.message}\n@{err.stack}')
|
||||
if (!res.status || !res.send) {
|
||||
Logger.logger.error('this is an error in express.js, please report this')
|
||||
res.destroy()
|
||||
} else if (!res.headersSent) {
|
||||
res.status(500)
|
||||
res.send({ error: 'internal server error' })
|
||||
} else {
|
||||
// socket should be already closed
|
||||
}
|
||||
}
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
app.use(error_reporting_middleware)
|
||||
app.use(Middleware.log_and_etagify)
|
||||
app.use(function(req, res, next) {
|
||||
res.setHeader('X-Powered-By', config.user_agent)
|
||||
next()
|
||||
})
|
||||
app.use(Cats.middleware)
|
||||
app.use(auth.auth_middleware())
|
||||
app.use(express.json({strict: false, limit: config.max_body_size || '10mb'}))
|
||||
app.use(express.compress())
|
||||
app.use(Middleware.anti_loop(config))
|
||||
app.use(error_reporting_middleware)
|
||||
app.use(Middleware.log_and_etagify)
|
||||
app.use(function(req, res, next) {
|
||||
res.setHeader('X-Powered-By', config.user_agent)
|
||||
next()
|
||||
})
|
||||
app.use(Cats.middleware)
|
||||
app.use(auth.auth_middleware())
|
||||
app.use(express.json({ strict: false, limit: config.max_body_size || '10mb' }))
|
||||
app.use(express.compress())
|
||||
app.use(Middleware.anti_loop(config))
|
||||
|
||||
// validate all of these params as a package name
|
||||
// this might be too harsh, so ask if it causes trouble
|
||||
app.param('package', validate_name)
|
||||
app.param('filename', validate_name)
|
||||
app.param('tag', validate_name)
|
||||
app.param('version', validate_name)
|
||||
app.param('revision', validate_name)
|
||||
// validate all of these params as a package name
|
||||
// this might be too harsh, so ask if it causes trouble
|
||||
app.param('package', validate_name)
|
||||
app.param('filename', validate_name)
|
||||
app.param('tag', validate_name)
|
||||
app.param('version', validate_name)
|
||||
app.param('revision', 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(/.*/))
|
||||
// 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.get('/-/all', function(req, res) {
|
||||
var https = require('https')
|
||||
var JSONStream = require('JSONStream')
|
||||
var request = require('request')({
|
||||
url: 'https://registry.npmjs.org/-/all',
|
||||
})
|
||||
.pipe(JSONStream.parse('*'))
|
||||
.on('data', function(d) {
|
||||
console.log(d)
|
||||
})
|
||||
})*/
|
||||
var https = require('https')
|
||||
var JSONStream = require('JSONStream')
|
||||
var request = require('request')({
|
||||
url: 'https://registry.npmjs.org/-/all',
|
||||
})
|
||||
.pipe(JSONStream.parse('*'))
|
||||
.on('data', function(d) {
|
||||
console.log(d)
|
||||
})
|
||||
})*/
|
||||
|
||||
// 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)
|
||||
// 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)
|
||||
|
||||
var version = req.params.version
|
||||
, t
|
||||
if (!version) {
|
||||
return res.send(info)
|
||||
}
|
||||
var version = req.params.version
|
||||
if (!version) return res.send(info)
|
||||
|
||||
if ((t = utils.get_version(info, version)) != null) {
|
||||
return res.send(t)
|
||||
}
|
||||
var t = Utils.get_version(info, version)
|
||||
if (t != null) return res.send(t)
|
||||
|
||||
if (info['dist-tags'] != null) {
|
||||
if (info['dist-tags'][version] != null) {
|
||||
version = info['dist-tags'][version]
|
||||
if ((t = utils.get_version(info, version)) != null) {
|
||||
return res.send(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info['dist-tags'] != null) {
|
||||
if (info['dist-tags'][version] != null) {
|
||||
version = info['dist-tags'][version]
|
||||
if ((t = Utils.get_version(info, version)) != null) {
|
||||
return res.send(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)
|
||||
stream.on('content-length', function(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)
|
||||
})
|
||||
app.get('/:package/-/:filename', can('access'), function(req, res, next) {
|
||||
var stream = storage.get_tarball(req.params.package, req.params.filename)
|
||||
stream.on('content-length', function(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)
|
||||
})
|
||||
|
||||
// searching packages
|
||||
app.get('/-/all/:anything?', function(req, res, next) {
|
||||
storage.search(req.param.startkey || 0, {req: req}, function(err, result) {
|
||||
if (err) return next(err)
|
||||
for (var pkg in result) {
|
||||
if (!config.allow_access(pkg, req.remote_user)) {
|
||||
delete result[pkg]
|
||||
}
|
||||
}
|
||||
return res.send(result)
|
||||
})
|
||||
})
|
||||
// searching packages
|
||||
app.get('/-/all/:anything?', function(req, res, next) {
|
||||
storage.search(req.param.startkey || 0, {req: req}, function(err, result) {
|
||||
if (err) return next(err)
|
||||
for (var pkg in result) {
|
||||
if (!config.allow_access(pkg, req.remote_user)) {
|
||||
delete result[pkg]
|
||||
}
|
||||
}
|
||||
return res.send(result)
|
||||
})
|
||||
})
|
||||
|
||||
//app.get('/*', function(req, res) {
|
||||
// proxy.request(req, res)
|
||||
//})
|
||||
//app.get('/*', function(req, res) {
|
||||
// proxy.request(req, res)
|
||||
//})
|
||||
|
||||
// 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) {
|
||||
res.cookies.set('AuthSession', String(Math.random()), {
|
||||
// npmjs.org sets 10h expire
|
||||
expires: new Date(Date.now() + 10*60*60*1000)
|
||||
})
|
||||
res.send({'ok':true,'name':'somebody','roles':[]})
|
||||
})
|
||||
// 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) {
|
||||
res.cookies.set('AuthSession', String(Math.random()), {
|
||||
// npmjs.org sets 10h expire
|
||||
expires: new Date(Date.now() + 10*60*60*1000)
|
||||
})
|
||||
res.send({ ok: true, name: 'somebody', roles: [] })
|
||||
})
|
||||
|
||||
app.get('/-/user/:org_couchdb_user', function(req, res, next) {
|
||||
res.status(200)
|
||||
return res.send({
|
||||
ok: 'you are authenticated as "' + req.remote_user.name + '"',
|
||||
})
|
||||
})
|
||||
app.get('/-/user/:org_couchdb_user', function(req, res, next) {
|
||||
res.status(200)
|
||||
return res.send({
|
||||
ok: 'you are authenticated as "' + req.remote_user.name + '"',
|
||||
})
|
||||
})
|
||||
|
||||
app.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req, res, next) {
|
||||
if (req.remote_user.name != null) {
|
||||
res.status(201)
|
||||
return res.send({
|
||||
ok: 'you are authenticated as "' + req.remote_user.name + '"',
|
||||
})
|
||||
} else {
|
||||
if (typeof(req.body.name) !== 'string' || typeof(req.body.password) !== 'string') {
|
||||
return next(Error[400]('user/password is not found in request (npm issue?)'))
|
||||
}
|
||||
auth.add_user(req.body.name, req.body.password, function(err) {
|
||||
if (err) {
|
||||
if (err.status < 500 && err.message === 'this user already exists') {
|
||||
// with npm registering is the same as logging in
|
||||
// so we replace message in case of conflict
|
||||
return next(Error[409]('bad username/password, access denied'))
|
||||
}
|
||||
return next(err)
|
||||
}
|
||||
app.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req, res, next) {
|
||||
if (req.remote_user.name != null) {
|
||||
res.status(201)
|
||||
return res.send({
|
||||
ok: 'you are authenticated as "' + req.remote_user.name + '"',
|
||||
})
|
||||
} else {
|
||||
if (typeof(req.body.name) !== 'string' || typeof(req.body.password) !== 'string') {
|
||||
return next( Error[400]('user/password is not found in request (npm issue?)') )
|
||||
}
|
||||
auth.add_user(req.body.name, req.body.password, function(err) {
|
||||
if (err) {
|
||||
if (err.status < 500 && err.message === 'this user already exists') {
|
||||
// with npm registering is the same as logging in
|
||||
// so we replace message in case of conflict
|
||||
return next( Error[409]('bad username/password, access denied') )
|
||||
}
|
||||
return next(err)
|
||||
}
|
||||
|
||||
res.status(201)
|
||||
return res.send({
|
||||
ok: 'user "' + req.body.name + '" created',
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
res.status(201)
|
||||
return res.send({ ok: 'user "' + req.body.name + '" created' })
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// tagging a package
|
||||
app.put('/:package/:tag', can('publish'), media('application/json'), function(req, res, next) {
|
||||
if (typeof(req.body) !== 'string') return next('route')
|
||||
// tagging a package
|
||||
app.put('/:package/:tag', can('publish'), media('application/json'), function(req, res, next) {
|
||||
if (typeof(req.body) !== 'string') return next('route')
|
||||
|
||||
var tags = {}
|
||||
tags[req.params.tag] = req.body
|
||||
storage.add_tags(req.params.package, tags, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return res.send({
|
||||
ok: 'package tagged'
|
||||
})
|
||||
})
|
||||
})
|
||||
var tags = {}
|
||||
tags[req.params.tag] = req.body
|
||||
storage.add_tags(req.params.package, tags, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return res.send({ ok: 'package tagged' })
|
||||
})
|
||||
})
|
||||
|
||||
// publishing a package
|
||||
app.put('/:package/:_rev?/:revision?', can('publish'), media('application/json'), expect_json, function(req, res, next) {
|
||||
var name = req.params.package
|
||||
// publishing a package
|
||||
app.put('/:package/:_rev?/:revision?', can('publish'), media('application/json'), expect_json, function(req, res, next) {
|
||||
var 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'))
|
||||
}
|
||||
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') )
|
||||
}
|
||||
|
||||
try {
|
||||
var metadata = utils.validate_metadata(req.body, name)
|
||||
} catch(err) {
|
||||
return next(Error[422]('bad incoming package data'))
|
||||
}
|
||||
try {
|
||||
var metadata = Utils.validate_metadata(req.body, name)
|
||||
} catch(err) {
|
||||
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')
|
||||
})
|
||||
} else {
|
||||
storage.add_package(name, metadata, function(err) {
|
||||
after_change(err, 'created new package')
|
||||
})
|
||||
}
|
||||
if (req.params._rev) {
|
||||
storage.change_package(name, metadata, req.params.revision, function(err) {
|
||||
after_change(err, 'package changed')
|
||||
})
|
||||
} else {
|
||||
storage.add_package(name, metadata, function(err) {
|
||||
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 res.send({
|
||||
ok: ok_message
|
||||
})
|
||||
}
|
||||
function after_change(err, ok_message) {
|
||||
// old npm behaviour
|
||||
if (metadata._attachments == null) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return res.send({ ok: ok_message })
|
||||
}
|
||||
|
||||
// npm-registry-client 0.3+ embeds tarball into the json upload
|
||||
// https://github.com/isaacs/npm-registry-client/commit/e9fbeb8b67f249394f735c74ef11fe4720d46ca0
|
||||
// issue #31, dealing with it here:
|
||||
// npm-registry-client 0.3+ embeds tarball into the json upload
|
||||
// https://github.com/isaacs/npm-registry-client/commit/e9fbeb8b67f249394f735c74ef11fe4720d46ca0
|
||||
// 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) {
|
||||
if (typeof(metadata._attachments) !== 'object'
|
||||
|| Object.keys(metadata._attachments).length !== 1
|
||||
|| typeof(metadata.versions) !== 'object'
|
||||
|| Object.keys(metadata.versions).length !== 1) {
|
||||
|
||||
// npm is doing something strange again
|
||||
// if this happens in normal circumstances, report it as a bug
|
||||
return next(Error[400]('unsupported registry call'))
|
||||
}
|
||||
// npm is doing something strange again
|
||||
// if this happens in normal circumstances, report it as a bug
|
||||
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]
|
||||
create_tarball(t1, metadata._attachments[t1], function(err) {
|
||||
if (err) return next(err)
|
||||
// at this point document is either created or existed before
|
||||
var t1 = Object.keys(metadata._attachments)[0]
|
||||
create_tarball(t1, metadata._attachments[t1], function(err) {
|
||||
if (err) return next(err)
|
||||
|
||||
var 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)
|
||||
var 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)
|
||||
|
||||
add_tags(metadata['dist-tags'], function(err) {
|
||||
if (err) return next(err)
|
||||
add_tags(metadata['dist-tags'], function(err) {
|
||||
if (err) return next(err)
|
||||
|
||||
res.status(201)
|
||||
return res.send({
|
||||
ok: ok_message
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
res.status(201)
|
||||
return res.send({ ok: ok_message })
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function create_tarball(filename, data, cb) {
|
||||
var stream = storage.add_tarball(name, filename)
|
||||
stream.on('error', function(err) {
|
||||
cb(err)
|
||||
})
|
||||
stream.on('success', function() {
|
||||
cb()
|
||||
})
|
||||
function create_tarball(filename, data, cb) {
|
||||
var stream = storage.add_tarball(name, filename)
|
||||
stream.on('error', function(err) {
|
||||
cb(err)
|
||||
})
|
||||
stream.on('success', function() {
|
||||
cb()
|
||||
})
|
||||
|
||||
// this is dumb and memory-consuming, but what choices do we have?
|
||||
stream.end(new Buffer(data.data, 'base64'))
|
||||
stream.done()
|
||||
}
|
||||
// this is dumb and memory-consuming, but what choices do we have?
|
||||
stream.end(Buffer(data.data, 'base64'))
|
||||
stream.done()
|
||||
}
|
||||
|
||||
function create_version(version, data, cb) {
|
||||
storage.add_version(name, version, data, null, cb)
|
||||
}
|
||||
function create_version(version, data, cb) {
|
||||
storage.add_version(name, version, data, null, cb)
|
||||
}
|
||||
|
||||
function add_tags(tags, cb) {
|
||||
storage.add_tags(name, tags, cb)
|
||||
}
|
||||
})
|
||||
function add_tags(tags, cb) {
|
||||
storage.add_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 res.send({
|
||||
ok: 'package removed'
|
||||
})
|
||||
})
|
||||
})
|
||||
// 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 res.send({ 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 res.send({
|
||||
ok: 'tarball 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 res.send({ 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
|
||||
// uploading package tarball
|
||||
app.put('/:package/-/:filename/*', can('publish'), media('application/octet-stream'), function(req, res, next) {
|
||||
var name = req.params.package
|
||||
|
||||
var stream = storage.add_tarball(name, req.params.filename)
|
||||
req.pipe(stream)
|
||||
var stream = storage.add_tarball(name, req.params.filename)
|
||||
req.pipe(stream)
|
||||
|
||||
// checking if end event came before closing
|
||||
var complete = false
|
||||
req.on('end', function() {
|
||||
complete = true
|
||||
stream.done()
|
||||
})
|
||||
req.on('close', function() {
|
||||
if (!complete) {
|
||||
stream.abort()
|
||||
}
|
||||
})
|
||||
// checking if end event came before closing
|
||||
var complete = false
|
||||
req.on('end', function() {
|
||||
complete = true
|
||||
stream.done()
|
||||
})
|
||||
req.on('close', function() {
|
||||
if (!complete) {
|
||||
stream.abort()
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('error', function(err) {
|
||||
return res.report_error(err)
|
||||
})
|
||||
stream.on('success', function() {
|
||||
res.status(201)
|
||||
return res.send({
|
||||
ok: 'tarball uploaded successfully'
|
||||
})
|
||||
})
|
||||
})
|
||||
stream.on('error', function(err) {
|
||||
return res.report_error(err)
|
||||
})
|
||||
stream.on('success', function() {
|
||||
res.status(201)
|
||||
return res.send({
|
||||
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
|
||||
, version = req.params.version
|
||||
, tag = req.params.tag
|
||||
// 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
|
||||
|
||||
storage.add_version(name, version, req.body, tag, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return res.send({
|
||||
ok: 'package published'
|
||||
})
|
||||
})
|
||||
})
|
||||
storage.add_version(name, version, req.body, tag, function(err) {
|
||||
if (err) return next(err)
|
||||
res.status(201)
|
||||
return res.send({ ok: 'package published' })
|
||||
})
|
||||
})
|
||||
|
||||
// hook for tests only
|
||||
if (config._debug) {
|
||||
app.get('/-/_debug', function(req, res) {
|
||||
var do_gc = typeof(global.gc) !== 'undefined'
|
||||
if (do_gc) global.gc()
|
||||
res.send({
|
||||
pid: process.pid,
|
||||
main: process.mainModule.filename,
|
||||
conf: config.self_path,
|
||||
mem: process.memoryUsage(),
|
||||
gc: do_gc,
|
||||
})
|
||||
})
|
||||
}
|
||||
// hook for tests only
|
||||
if (config._debug) {
|
||||
app.get('/-/_debug', function(req, res) {
|
||||
var do_gc = typeof(global.gc) !== 'undefined'
|
||||
if (do_gc) global.gc()
|
||||
res.send({
|
||||
pid : process.pid,
|
||||
main : process.mainModule.filename,
|
||||
conf : config.self_path,
|
||||
mem : process.memoryUsage(),
|
||||
gc : do_gc,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
app.use(app.router)
|
||||
app.use(function(err, req, res, 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(){})
|
||||
}
|
||||
res.report_error(err)
|
||||
})
|
||||
app.use(app.router)
|
||||
app.use(function(err, req, res, 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(){})
|
||||
}
|
||||
res.report_error(err)
|
||||
})
|
||||
|
||||
if (config.web && config.web.enable) {
|
||||
app.use(require('./index-web')(config, auth, storage))
|
||||
} else {
|
||||
app.get('/', function(req, res) {
|
||||
res.send('Web interface is disabled in the config file')
|
||||
})
|
||||
}
|
||||
if (config.web && config.web.enable) {
|
||||
app.use(require('./index-web')(config, auth, storage))
|
||||
} else {
|
||||
app.get('/', function(req, res) {
|
||||
res.send('Web interface is disabled in the config file')
|
||||
})
|
||||
}
|
||||
|
||||
return app
|
||||
return app
|
||||
}
|
||||
|
||||
|
|
376
lib/local-fs.js
376
lib/local-fs.js
|
@ -1,270 +1,270 @@
|
|||
var fs = require('fs')
|
||||
, Path = require('path')
|
||||
, mkdirp = require('mkdirp')
|
||||
, mystreams = require('./streams')
|
||||
, Error = require('http-errors')
|
||||
var fs = require('fs')
|
||||
var Error = require('http-errors')
|
||||
var mkdirp = require('mkdirp')
|
||||
var Path = require('path')
|
||||
var MyStreams = require('./streams')
|
||||
|
||||
function FSError(code) {
|
||||
var err = Error(code)
|
||||
err.code = code
|
||||
return err
|
||||
var err = Error(code)
|
||||
err.code = code
|
||||
return err
|
||||
}
|
||||
|
||||
try {
|
||||
var fsExt = require('fs-ext')
|
||||
} catch(e) {
|
||||
fsExt = {
|
||||
flock: function() {
|
||||
arguments[arguments.length-1]()
|
||||
}
|
||||
}
|
||||
var fsExt = require('fs-ext')
|
||||
} catch (e) {
|
||||
fsExt = {
|
||||
flock: function() {
|
||||
arguments[arguments.length-1]()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
_cb(err)
|
||||
}
|
||||
function cb(err) {
|
||||
if (err) fs.unlink(src)
|
||||
_cb(err)
|
||||
}
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
return fs.rename(src, dst, cb)
|
||||
}
|
||||
if (process.platform !== 'win32') {
|
||||
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)
|
||||
fs.rename(dst, tmp, function(err) {
|
||||
fs.rename(src, dst, cb)
|
||||
if (!err) fs.unlink(tmp)
|
||||
})
|
||||
// windows can't remove opened file,
|
||||
// but it seem to be able to rename it
|
||||
var tmp = tempFile(dst)
|
||||
fs.rename(dst, tmp, function(err) {
|
||||
fs.rename(src, dst, cb)
|
||||
if (!err) fs.unlink(tmp)
|
||||
})
|
||||
}
|
||||
|
||||
function write(dest, data, cb) {
|
||||
var safe_write = function(cb) {
|
||||
var tmpname = tempFile(dest)
|
||||
fs.writeFile(tmpname, data, function(err) {
|
||||
if (err) return cb(err)
|
||||
renameTmp(tmpname, dest, cb)
|
||||
})
|
||||
}
|
||||
var safe_write = function(cb) {
|
||||
var tmpname = tempFile(dest)
|
||||
fs.writeFile(tmpname, data, function(err) {
|
||||
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)
|
||||
})
|
||||
} else {
|
||||
cb(err)
|
||||
}
|
||||
})
|
||||
safe_write(function(err) {
|
||||
if (err && err.code === 'ENOENT') {
|
||||
mkdirp(Path.dirname(dest), function(err) {
|
||||
if (err) return cb(err)
|
||||
safe_write(cb)
|
||||
})
|
||||
} else {
|
||||
cb(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function write_stream(name) {
|
||||
var stream = new mystreams.UploadTarballStream()
|
||||
var stream = MyStreams.UploadTarballStream()
|
||||
|
||||
var _ended = 0
|
||||
stream.on('end', function() {
|
||||
_ended = 1
|
||||
})
|
||||
var _ended = 0
|
||||
stream.on('end', function() {
|
||||
_ended = 1
|
||||
})
|
||||
|
||||
fs.exists(name, function(exists) {
|
||||
if (exists) return stream.emit('error', FSError('EEXISTS'))
|
||||
fs.exists(name, function(exists) {
|
||||
if (exists) return stream.emit('error', FSError('EEXISTS'))
|
||||
|
||||
var tmpname = name + '.tmp-'+String(Math.random()).replace(/^0\./, '')
|
||||
, file = fs.createWriteStream(tmpname)
|
||||
, opened = false
|
||||
stream.pipe(file)
|
||||
var tmpname = name + '.tmp-'+String(Math.random()).replace(/^0\./, '')
|
||||
var file = fs.createWriteStream(tmpname)
|
||||
var 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)
|
||||
} else {
|
||||
stream.emit('success')
|
||||
}
|
||||
})
|
||||
})
|
||||
file.destroySoon()
|
||||
}
|
||||
if (_ended) {
|
||||
onend()
|
||||
} else {
|
||||
stream.on('end', onend)
|
||||
}
|
||||
}
|
||||
stream.abort = function() {
|
||||
if (opened) {
|
||||
opened = false
|
||||
file.on('close', function() {
|
||||
fs.unlink(tmpname, function(){})
|
||||
})
|
||||
}
|
||||
file.destroySoon()
|
||||
}
|
||||
file.on('open', function() {
|
||||
opened = true
|
||||
// re-emitting open because it's handled in storage.js
|
||||
stream.emit('open')
|
||||
})
|
||||
file.on('error', function(err) {
|
||||
stream.emit('error', err)
|
||||
})
|
||||
})
|
||||
return stream
|
||||
stream.done = function() {
|
||||
function onend() {
|
||||
file.on('close', function() {
|
||||
renameTmp(tmpname, name, function(err) {
|
||||
if (err) {
|
||||
stream.emit('error', err)
|
||||
} else {
|
||||
stream.emit('success')
|
||||
}
|
||||
})
|
||||
})
|
||||
file.destroySoon()
|
||||
}
|
||||
if (_ended) {
|
||||
onend()
|
||||
} else {
|
||||
stream.on('end', onend)
|
||||
}
|
||||
}
|
||||
stream.abort = function() {
|
||||
if (opened) {
|
||||
opened = false
|
||||
file.on('close', function() {
|
||||
fs.unlink(tmpname, function(){})
|
||||
})
|
||||
}
|
||||
file.destroySoon()
|
||||
}
|
||||
file.on('open', function() {
|
||||
opened = true
|
||||
// re-emitting open because it's handled in storage.js
|
||||
stream.emit('open')
|
||||
})
|
||||
file.on('error', function(err) {
|
||||
stream.emit('error', err)
|
||||
})
|
||||
})
|
||||
return stream
|
||||
}
|
||||
|
||||
function read_stream(name, stream, callback) {
|
||||
var rstream = fs.createReadStream(name)
|
||||
rstream.on('error', function(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)
|
||||
})
|
||||
})
|
||||
var rstream = fs.createReadStream(name)
|
||||
rstream.on('error', function(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)
|
||||
})
|
||||
})
|
||||
|
||||
var stream = new mystreams.ReadTarballStream()
|
||||
stream.abort = function() {
|
||||
rstream.close()
|
||||
}
|
||||
return stream
|
||||
var stream = MyStreams.ReadTarballStream()
|
||||
stream.abort = function() {
|
||||
rstream.close()
|
||||
}
|
||||
return stream
|
||||
}
|
||||
|
||||
function create(name, contents, callback) {
|
||||
fs.exists(name, function(exists) {
|
||||
if (exists) return callback(FSError('EEXISTS'))
|
||||
write(name, contents, callback)
|
||||
})
|
||||
fs.exists(name, function(exists) {
|
||||
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)
|
||||
})
|
||||
fs.exists(name, function(exists) {
|
||||
if (!exists) return callback( FSError('ENOENT') )
|
||||
write(name, contents, callback)
|
||||
})
|
||||
}
|
||||
|
||||
function read(name, callback) {
|
||||
fs.readFile(name, callback)
|
||||
fs.readFile(name, callback)
|
||||
}
|
||||
|
||||
// open and flock with exponential backoff
|
||||
function open_flock(name, opmod, flmod, tries, backoff, cb) {
|
||||
fs.open(name, opmod, function(err, fd) {
|
||||
if (err) return cb(err, fd)
|
||||
fs.open(name, opmod, function(err, fd) {
|
||||
if (err) return cb(err, fd)
|
||||
|
||||
fsExt.flock(fd, flmod, function(err) {
|
||||
if (err) {
|
||||
if (!tries) {
|
||||
fs.close(fd, function() {
|
||||
cb(err)
|
||||
})
|
||||
} else {
|
||||
fs.close(fd, function() {
|
||||
setTimeout(function() {
|
||||
open_flock(name, opmod, flmod, tries-1, backoff*2, cb)
|
||||
}, backoff)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
cb(null, fd)
|
||||
}
|
||||
})
|
||||
})
|
||||
fsExt.flock(fd, flmod, function(err) {
|
||||
if (err) {
|
||||
if (!tries) {
|
||||
fs.close(fd, function() {
|
||||
cb(err)
|
||||
})
|
||||
} else {
|
||||
fs.close(fd, function() {
|
||||
setTimeout(function() {
|
||||
open_flock(name, opmod, flmod, tries-1, backoff*2, cb)
|
||||
}, backoff)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
cb(null, fd)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// this function neither unlocks file nor closes it
|
||||
// it'll have to be done manually later
|
||||
function lock_and_read(name, _callback) {
|
||||
open_flock(name, 'r', 'exnb', 4, 10, function(err, fd) {
|
||||
function callback(err) {
|
||||
if (err && fd) {
|
||||
fs.close(fd, function(err2) {
|
||||
_callback(err)
|
||||
})
|
||||
} else {
|
||||
_callback.apply(null, arguments)
|
||||
}
|
||||
}
|
||||
open_flock(name, 'r', 'exnb', 4, 10, function(err, fd) {
|
||||
function callback(err) {
|
||||
if (err && fd) {
|
||||
fs.close(fd, function(err2) {
|
||||
_callback(err)
|
||||
})
|
||||
} else {
|
||||
_callback.apply(null, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
if (err) return callback(err, fd)
|
||||
if (err) return callback(err, fd)
|
||||
|
||||
fs.fstat(fd, function(err, st) {
|
||||
if (err) return callback(err, fd)
|
||||
fs.fstat(fd, function(err, st) {
|
||||
if (err) return callback(err, fd)
|
||||
|
||||
var buffer = new Buffer(st.size)
|
||||
if (st.size === 0) return onRead(null, 0, buffer)
|
||||
fs.read(fd, buffer, 0, st.size, null, onRead)
|
||||
var buffer = Buffer(st.size)
|
||||
if (st.size === 0) return onRead(null, 0, buffer)
|
||||
fs.read(fd, buffer, 0, st.size, null, onRead)
|
||||
|
||||
function onRead(err, bytesRead, buffer) {
|
||||
if (err) return callback(err, fd)
|
||||
if (bytesRead != st.size) return callback(new Error('st.size != bytesRead'), fd)
|
||||
function onRead(err, bytesRead, buffer) {
|
||||
if (err) return callback(err, fd)
|
||||
if (bytesRead != st.size) return callback(Error('st.size != bytesRead'), fd)
|
||||
|
||||
callback(null, fd, buffer)
|
||||
}
|
||||
})
|
||||
})
|
||||
callback(null, fd, buffer)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports.read = read
|
||||
|
||||
module.exports.read_json = function(name, cb) {
|
||||
read(name, function(err, res) {
|
||||
if (err) return cb(err)
|
||||
read(name, function(err, res) {
|
||||
if (err) return cb(err)
|
||||
|
||||
var args = []
|
||||
try {
|
||||
args = [null, JSON.parse(res.toString('utf8'))]
|
||||
} catch(err) {
|
||||
args = [err]
|
||||
}
|
||||
cb.apply(null, args)
|
||||
})
|
||||
var args = []
|
||||
try {
|
||||
args = [ null, JSON.parse(res.toString('utf8')) ]
|
||||
} catch(err) {
|
||||
args = [ err ]
|
||||
}
|
||||
cb.apply(null, args)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports.lock_and_read = lock_and_read
|
||||
|
||||
module.exports.lock_and_read_json = function(name, cb) {
|
||||
lock_and_read(name, function(err, fd, res) {
|
||||
if (err) return cb(err, fd)
|
||||
lock_and_read(name, function(err, fd, res) {
|
||||
if (err) return cb(err, fd)
|
||||
|
||||
var args = []
|
||||
try {
|
||||
args = [null, fd, JSON.parse(res.toString('utf8'))]
|
||||
} catch(err) {
|
||||
args = [err, fd]
|
||||
}
|
||||
cb.apply(null, args)
|
||||
})
|
||||
var args = []
|
||||
try {
|
||||
args = [ null, fd, JSON.parse(res.toString('utf8')) ]
|
||||
} catch(err) {
|
||||
args = [ err, fd ]
|
||||
}
|
||||
cb.apply(null, args)
|
||||
})
|
||||
}
|
||||
|
||||
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_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_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
|
||||
|
|
|
@ -1,38 +1,40 @@
|
|||
var fs = require('fs')
|
||||
|
||||
function LocalList(path) {
|
||||
var self = Object.create(LocalList.prototype)
|
||||
self.path = path
|
||||
try {
|
||||
self.list = JSON.parse(fs.readFileSync(self.path, 'utf8')).list
|
||||
} catch(_) {
|
||||
self.list = []
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
LocalList.prototype = {
|
||||
add: function(name) {
|
||||
if (this.list.indexOf(name) === -1) {
|
||||
this.list.push(name)
|
||||
this.sync()
|
||||
}
|
||||
},
|
||||
remove: function(name) {
|
||||
var i = this.list.indexOf(name)
|
||||
if (i !== -1) {
|
||||
this.list.splice(i, 1)
|
||||
}
|
||||
|
||||
this.sync()
|
||||
},
|
||||
get: function() {
|
||||
return this.list
|
||||
},
|
||||
sync: function() {
|
||||
fs.writeFileSync(this.path, JSON.stringify({list: this.list})); //Uses sync to prevent ugly race condition
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = LocalList
|
||||
|
||||
function LocalList(path) {
|
||||
var self = Object.create(LocalList.prototype)
|
||||
self.path = path
|
||||
try {
|
||||
self.list = JSON.parse(fs.readFileSync(self.path, 'utf8')).list
|
||||
} catch(_) {
|
||||
self.list = []
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
LocalList.prototype.add = function(name) {
|
||||
if (this.list.indexOf(name) === -1) {
|
||||
this.list.push(name)
|
||||
this.sync()
|
||||
}
|
||||
}
|
||||
|
||||
LocalList.prototype.remove = function(name) {
|
||||
var i = this.list.indexOf(name)
|
||||
if (i !== -1) {
|
||||
this.list.splice(i, 1)
|
||||
}
|
||||
|
||||
this.sync()
|
||||
}
|
||||
|
||||
LocalList.prototype.get = function() {
|
||||
return this.list
|
||||
}
|
||||
|
||||
LocalList.prototype.sync = function() {
|
||||
// Uses sync to prevent ugly race condition
|
||||
fs.writeFileSync(this.path, JSON.stringify({ list: this.list }))
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
229
lib/logger.js
229
lib/logger.js
|
@ -1,77 +1,77 @@
|
|||
var Logger = require('bunyan')
|
||||
, Error = require('http-errors')
|
||||
, Stream = require('stream')
|
||||
, utils = require('./utils')
|
||||
var Error = require('http-errors')
|
||||
var Stream = require('stream')
|
||||
var Utils = require('./utils')
|
||||
|
||||
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' }]
|
||||
var 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) {
|
||||
var 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
|
||||
var 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 {
|
||||
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 {
|
||||
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.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: 'sinopia',
|
||||
streams: streams,
|
||||
serializers: {
|
||||
err: Logger.stdSerializers.err,
|
||||
req: Logger.stdSerializers.req,
|
||||
res: Logger.stdSerializers.res,
|
||||
},
|
||||
})
|
||||
var logger = new Logger({
|
||||
name: 'sinopia',
|
||||
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
|
||||
|
@ -80,75 +80,74 @@ module.exports.setup = function(logs) {
|
|||
|
||||
// level to color
|
||||
var levels = {
|
||||
fatal: 31,
|
||||
error: 31,
|
||||
warn: 33,
|
||||
http: 35,
|
||||
info: 36,
|
||||
debug: 90,
|
||||
trace: 90,
|
||||
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)
|
||||
max = Math.max(max, l.length)
|
||||
}
|
||||
|
||||
function pad(str) {
|
||||
if (str.length < max) return str + new Array(max - str.length + 1).join(' ')
|
||||
return str
|
||||
if (str.length < max) return str + Array(max - str.length + 1).join(' ')
|
||||
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 : '\033[32m<--\033[39m',
|
||||
out : '\033[33m-->\033[39m',
|
||||
fs : '\033[90m-=-\033[39m',
|
||||
default : '\033[34m---\033[39m',
|
||||
}, {
|
||||
in: '<--',
|
||||
out: '-->',
|
||||
fs: '-=-',
|
||||
default: '---',
|
||||
in : '<--',
|
||||
out : '-->',
|
||||
fs : '-=-',
|
||||
default : '---',
|
||||
}]
|
||||
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(str) === 'string') {
|
||||
if (!colors || ~str.indexOf('\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, void 0, void 0, colors)
|
||||
}
|
||||
})
|
||||
var sub = subsystems[+!colors][obj.sub] || subsystems[+!colors].default
|
||||
// ^^--- black magic... kidding, just "colors ? 0 : 1"
|
||||
if (typeof(str) === 'string') {
|
||||
if (!colors || ~str.indexOf('\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 (colors) {
|
||||
return ' \033[' + levels[type] + 'm' + (pad(type)) + '\033[39m ' + sub + ' ' + finalmsg
|
||||
} else {
|
||||
return ' ' + (pad(type)) + ' ' + sub + ' ' + finalmsg
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,151 +1,150 @@
|
|||
var crypto = require('crypto')
|
||||
, Error = require('http-errors')
|
||||
, utils = require('./utils')
|
||||
, Logger = require('./logger')
|
||||
var Error = require('http-errors')
|
||||
var utils = require('./utils')
|
||||
var Logger = require('./logger')
|
||||
|
||||
module.exports.validate_name = function validate_name(req, res, next, value, name) {
|
||||
if (value.charAt(0) === '-') {
|
||||
// special case in couchdb usually
|
||||
next('route')
|
||||
} else if (utils.validate_name(value)) {
|
||||
next()
|
||||
} else {
|
||||
next(Error[403]('invalid ' + name))
|
||||
}
|
||||
if (value.charAt(0) === '-') {
|
||||
// special case in couchdb usually
|
||||
next('route')
|
||||
} else if (utils.validate_name(value)) {
|
||||
next()
|
||||
} else {
|
||||
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']))
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
return function(req, res, next) {
|
||||
if (req.headers['content-type'] !== expect) {
|
||||
next( Error[415]('wrong content-type, expect: ' + expect
|
||||
+ ', got: '+req.headers['content-type']) )
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.expect_json = function expect_json(req, res, next) {
|
||||
if (!utils.is_object(req.body)) {
|
||||
return next({
|
||||
status: 400,
|
||||
message: 'can\'t parse incoming json',
|
||||
})
|
||||
}
|
||||
next()
|
||||
if (!utils.is_object(req.body)) {
|
||||
return next( Error[400]("can't parse incoming json") )
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
module.exports.anti_loop = function(config) {
|
||||
return function(req, res, next) {
|
||||
if (req.headers.via != null) {
|
||||
var arr = req.headers.via.split(',')
|
||||
for (var i=0; i<arr.length; i++) {
|
||||
var m = arr[i].match(/\s*(\S+)\s+(\S+)/)
|
||||
if (m && m[2] === config.server_id) {
|
||||
return next(Error[508]('loop detected'))
|
||||
}
|
||||
}
|
||||
}
|
||||
next()
|
||||
}
|
||||
return function(req, res, next) {
|
||||
if (req.headers.via != null) {
|
||||
var arr = req.headers.via.split(',')
|
||||
|
||||
for (var i=0; i<arr.length; i++) {
|
||||
var m = arr[i].match(/\s*(\S+)\s+(\S+)/)
|
||||
if (m && m[2] === config.server_id) {
|
||||
return next( Error[508]('loop detected') )
|
||||
}
|
||||
}
|
||||
}
|
||||
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.log_and_etagify = function(req, res, next) {
|
||||
// logger
|
||||
req.log = Logger.logger.child({sub: 'in'})
|
||||
// logger
|
||||
req.log = Logger.logger.child({ sub: 'in' })
|
||||
|
||||
var _auth = req.headers.authorization
|
||||
if (_auth) req.headers.authorization = '<Classified>'
|
||||
req.log.info({req: req, ip: req.ip}, '@{ip} requested \'@{req.method} @{req.url}\'')
|
||||
if (_auth) req.headers.authorization = _auth
|
||||
var _auth = req.headers.authorization
|
||||
if (_auth) req.headers.authorization = '<Classified>'
|
||||
req.log.info( { req: req, ip: req.ip }
|
||||
, '@{ip} requested \'@{req.method} @{req.url}\'' )
|
||||
if (_auth) req.headers.authorization = _auth
|
||||
|
||||
var bytesin = 0
|
||||
req.on('data', function(chunk) {
|
||||
bytesin += chunk.length
|
||||
})
|
||||
var bytesin = 0
|
||||
req.on('data', function(chunk) {
|
||||
bytesin += chunk.length
|
||||
})
|
||||
|
||||
var _send = res.send
|
||||
res.send = function(body) {
|
||||
try {
|
||||
if (typeof(body) === 'string' || typeof(body) === 'object') {
|
||||
if (!res.getHeader('Content-type')) {
|
||||
res.header('Content-type', 'application/json')
|
||||
}
|
||||
var _send = res.send
|
||||
res.send = function(body) {
|
||||
try {
|
||||
if (typeof(body) === 'string' || typeof(body) === 'object') {
|
||||
if (!res.getHeader('Content-type')) {
|
||||
res.header('Content-type', 'application/json')
|
||||
}
|
||||
|
||||
if (typeof(body) === 'object' && body != null) {
|
||||
if (typeof(body.error) === 'string') {
|
||||
res._sinopia_error = body.error
|
||||
}
|
||||
body = JSON.stringify(body, undefined, '\t') + '\n'
|
||||
}
|
||||
if (typeof(body) === 'object' && body != null) {
|
||||
if (typeof(body.error) === 'string') {
|
||||
res._sinopia_error = body.error
|
||||
}
|
||||
body = JSON.stringify(body, undefined, '\t') + '\n'
|
||||
}
|
||||
|
||||
// don't send etags with errors
|
||||
if (!res.statusCode || (res.statusCode >= 200 && res.statusCode < 300)) {
|
||||
res.header('ETag', '"' + md5sum(body) + '"')
|
||||
}
|
||||
} else {
|
||||
// send(null), send(204), etc.
|
||||
}
|
||||
} catch(err) {
|
||||
// if sinopia sends headers first, and then calls res.send()
|
||||
// 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
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
// don't send etags with errors
|
||||
if (!res.statusCode || (res.statusCode >= 200 && res.statusCode < 300)) {
|
||||
res.header('ETag', '"' + md5sum(body) + '"')
|
||||
}
|
||||
} else {
|
||||
// send(null), send(204), etc.
|
||||
}
|
||||
} catch(err) {
|
||||
// if sinopia sends headers first, and then calls res.send()
|
||||
// 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
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
res.send = _send
|
||||
res.send(body)
|
||||
}
|
||||
res.send = _send
|
||||
res.send(body)
|
||||
}
|
||||
|
||||
var bytesout = 0
|
||||
, _write = res.write
|
||||
res.write = function(buf) {
|
||||
bytesout += buf.length
|
||||
_write.apply(res, arguments)
|
||||
}
|
||||
var bytesout = 0
|
||||
var _write = res.write
|
||||
res.write = function(buf) {
|
||||
bytesout += buf.length
|
||||
_write.apply(res, arguments)
|
||||
}
|
||||
|
||||
function log() {
|
||||
var message = '@{status}, user: @{user}, req: \'@{request.method} @{request.url}\''
|
||||
if (res._sinopia_error) {
|
||||
message += ', error: @{!error}'
|
||||
} else {
|
||||
message += ', bytes: @{bytes.in}/@{bytes.out}'
|
||||
}
|
||||
req.log.warn({
|
||||
request: {method: req.method, url: req.url},
|
||||
level: 35, // http
|
||||
user: req.remote_user.name,
|
||||
status: res.statusCode,
|
||||
error: res._sinopia_error,
|
||||
bytes: {
|
||||
in: bytesin,
|
||||
out: bytesout,
|
||||
}
|
||||
}, message)
|
||||
}
|
||||
function log() {
|
||||
var message = "@{status}, user: @{user}, req: '@{request.method} @{request.url}'"
|
||||
if (res._sinopia_error) {
|
||||
message += ', error: @{!error}'
|
||||
} else {
|
||||
message += ', bytes: @{bytes.in}/@{bytes.out}'
|
||||
}
|
||||
req.log.warn({
|
||||
request : { method: req.method, url: req.url },
|
||||
level : 35, // http
|
||||
user : req.remote_user.name,
|
||||
status : res.statusCode,
|
||||
error : res._sinopia_error,
|
||||
bytes : {
|
||||
in : bytesin,
|
||||
out : bytesout,
|
||||
}
|
||||
}, message)
|
||||
}
|
||||
|
||||
req.on('close', function() {
|
||||
log(true)
|
||||
})
|
||||
req.on('close', function() {
|
||||
log(true)
|
||||
})
|
||||
|
||||
var _end = res.end
|
||||
res.end = function(buf) {
|
||||
if (buf) bytesout += buf.length
|
||||
_end.apply(res, arguments)
|
||||
log()
|
||||
}
|
||||
next()
|
||||
var _end = res.end
|
||||
res.end = function(buf) {
|
||||
if (buf) bytesout += buf.length
|
||||
_end.apply(res, arguments)
|
||||
log()
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,45 +1,48 @@
|
|||
var lunr = require('lunr')
|
||||
|
||||
var Search = function() {
|
||||
this.index = lunr(function () {
|
||||
this.field('name', {boost: 10});
|
||||
this.field('description', {boost: 4});
|
||||
this.field('author', {boost: 6});
|
||||
this.field('readme');
|
||||
});
|
||||
};
|
||||
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
|
||||
}
|
||||
|
||||
Search.prototype = {
|
||||
query: function(q) {
|
||||
return this.index.search(q);
|
||||
},
|
||||
add: function(package) {
|
||||
this.index.add({
|
||||
id: package.name,
|
||||
name: package.name,
|
||||
description: package.description,
|
||||
author: package._npmUser ? package._npmUser.name : '???'
|
||||
});
|
||||
},
|
||||
remove: function(name) {
|
||||
this.index.remove({
|
||||
id: name
|
||||
});
|
||||
},
|
||||
reindex: function() {
|
||||
var self = this;
|
||||
this.storage.get_local(function(err, packages) {
|
||||
var i = packages.length;
|
||||
Search.prototype.query = function(q) {
|
||||
return this.index.search(q)
|
||||
}
|
||||
|
||||
while(i--) {
|
||||
self.add(packages[i]);
|
||||
}
|
||||
});
|
||||
},
|
||||
configureStorage: function(storage) {
|
||||
this.storage = storage;
|
||||
this.reindex();
|
||||
}
|
||||
};
|
||||
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();
|
||||
|
|
|
@ -2,73 +2,73 @@
|
|||
// see https://secure.flickr.com/photos/girliemac/sets/72157628409467125
|
||||
|
||||
var images = {
|
||||
100: 'aVvDhR', // '6512768893', // 100 - Continue
|
||||
101: 'aXXExP', // '6540479029', // 101 - Switching Protocols
|
||||
200: 'aVuVsF', // '6512628175', // 200 - OK
|
||||
201: 'aXWm1Z', // '6540221577', // 201 - Created
|
||||
202: 'aXXEyF', // '6540479079', // 202 - Accepted
|
||||
204: 'aYyJ7B', // '6547319943', // 204 - No Content
|
||||
206: 'aVEnUP', // '6514473163', // 206 - Partial Content
|
||||
207: 'aVEnRD', // '6514472979', // 207 - Multi-Status
|
||||
300: 'aW7mac', // '6519540181', // 300 - Multiple Choices
|
||||
301: 'aW7mb4', // '6519540231', // 301 - Moved Permanently
|
||||
302: 'aV6jKp', // '6508023829', // 302 - Found
|
||||
303: 'aVxtaK', // '6513125065', // 303 - See Other
|
||||
304: 'aXY3dH', // '6540551929', // 304 - Not Modified
|
||||
305: 'aXX5LK', // '6540365403', // 305 - Use Proxy
|
||||
307: 'aVwQnk', // '6513001269', // 307 - Temporary Redirect
|
||||
400: 'aXYDeT', // '6540669737', // 400 - Bad Request
|
||||
401: 'aV6jwe', // '6508023065', // 401 - Unauthorized
|
||||
402: 'aVwQoe', // '6513001321', // 402 - Payment Required
|
||||
403: 'aV6jFK', // '6508023617', // 403 - Forbidden
|
||||
404: 'aV6juR', // '6508022985', // 404 - Not Found
|
||||
405: 'aV6jE8', // '6508023523', // 405 - Method Not Allowed
|
||||
406: 'aV6jxa', // '6508023119', // 406 - Not Acceptable
|
||||
408: 'aV6jyc', // '6508023179', // 408 - Request Timeout
|
||||
409: 'aV6jzz', // '6508023259', // 409 - Conflict
|
||||
410: 'aVES2H', // '6514567755', // 410 - Gone
|
||||
411: 'aXYVpT', // '6540724141', // 411 - Length Required
|
||||
413: 'aV6jHZ', // '6508023747', // 413 - Request Entity Too Large
|
||||
414: 'aV6jBa', // '6508023351', // 414 - Request-URI Too Long
|
||||
416: 'aVxQvr', // '6513196851', // 416 - Requested Range Not Satisfiable
|
||||
417: 'aV6jGP', // '6508023679', // 417 - Expectation Failed
|
||||
418: 'aV6J7c', // '6508102407', // 418 - I'm a teapot
|
||||
422: 'aVEnTt', // '6514473085', // 422 - Unprocessable Entity
|
||||
423: 'aVEyVZ', // '6514510235', // 423 - Locked
|
||||
424: 'aVEWZ6', // '6514584423', // 424 - Failed Dependency
|
||||
425: 'aXYdzH', // '6540586787', // 425 - Unordered Collection
|
||||
426: 'aVdo4M', // '6509400771', // 426 - Upgrade Required
|
||||
429: 'aVdo8F', // '6509400997', // 429 - Too Many Requests
|
||||
431: 'aVdo3n', // '6509400689', // 431 - Request Header Fields Too Large
|
||||
444: 'aVdo1P', // '6509400599', // 444 - No Response
|
||||
450: 'aVxtbK', // '6513125123', // 450 - Blocked by Windows Parental Controls
|
||||
451: 'eTiGQd', // '9113233540', // 451 - Unavailable for Legal Reasons
|
||||
500: 'aVdo6e', // '6509400855', // 500 - Internal Server Error
|
||||
502: 'aV6jCv', // '6508023429', // 502 - Bad Gateway
|
||||
503: 'aXYvop', // '6540643319', // 503 - Service Unavailable
|
||||
506: 'aXYvnH', // '6540643279', // 506 - Variant Also Negotiates
|
||||
507: 'aVdnZa', // '6509400503', // 507 - Insufficient Storage
|
||||
508: 'aVdnYa', // '6509400445', // 508 - Loop Detected
|
||||
509: 'aXXg1V', // '6540399865', // 509 - Bandwidth Limit Exceeded
|
||||
599: 'aVdo7v', // '6509400929', // 599 - Network connect timeout error
|
||||
100: 'aVvDhR', // '6512768893', // 100 - Continue
|
||||
101: 'aXXExP', // '6540479029', // 101 - Switching Protocols
|
||||
200: 'aVuVsF', // '6512628175', // 200 - OK
|
||||
201: 'aXWm1Z', // '6540221577', // 201 - Created
|
||||
202: 'aXXEyF', // '6540479079', // 202 - Accepted
|
||||
204: 'aYyJ7B', // '6547319943', // 204 - No Content
|
||||
206: 'aVEnUP', // '6514473163', // 206 - Partial Content
|
||||
207: 'aVEnRD', // '6514472979', // 207 - Multi-Status
|
||||
300: 'aW7mac', // '6519540181', // 300 - Multiple Choices
|
||||
301: 'aW7mb4', // '6519540231', // 301 - Moved Permanently
|
||||
302: 'aV6jKp', // '6508023829', // 302 - Found
|
||||
303: 'aVxtaK', // '6513125065', // 303 - See Other
|
||||
304: 'aXY3dH', // '6540551929', // 304 - Not Modified
|
||||
305: 'aXX5LK', // '6540365403', // 305 - Use Proxy
|
||||
307: 'aVwQnk', // '6513001269', // 307 - Temporary Redirect
|
||||
400: 'aXYDeT', // '6540669737', // 400 - Bad Request
|
||||
401: 'aV6jwe', // '6508023065', // 401 - Unauthorized
|
||||
402: 'aVwQoe', // '6513001321', // 402 - Payment Required
|
||||
403: 'aV6jFK', // '6508023617', // 403 - Forbidden
|
||||
404: 'aV6juR', // '6508022985', // 404 - Not Found
|
||||
405: 'aV6jE8', // '6508023523', // 405 - Method Not Allowed
|
||||
406: 'aV6jxa', // '6508023119', // 406 - Not Acceptable
|
||||
408: 'aV6jyc', // '6508023179', // 408 - Request Timeout
|
||||
409: 'aV6jzz', // '6508023259', // 409 - Conflict
|
||||
410: 'aVES2H', // '6514567755', // 410 - Gone
|
||||
411: 'aXYVpT', // '6540724141', // 411 - Length Required
|
||||
413: 'aV6jHZ', // '6508023747', // 413 - Request Entity Too Large
|
||||
414: 'aV6jBa', // '6508023351', // 414 - Request-URI Too Long
|
||||
416: 'aVxQvr', // '6513196851', // 416 - Requested Range Not Satisfiable
|
||||
417: 'aV6jGP', // '6508023679', // 417 - Expectation Failed
|
||||
418: 'aV6J7c', // '6508102407', // 418 - I'm a teapot
|
||||
422: 'aVEnTt', // '6514473085', // 422 - Unprocessable Entity
|
||||
423: 'aVEyVZ', // '6514510235', // 423 - Locked
|
||||
424: 'aVEWZ6', // '6514584423', // 424 - Failed Dependency
|
||||
425: 'aXYdzH', // '6540586787', // 425 - Unordered Collection
|
||||
426: 'aVdo4M', // '6509400771', // 426 - Upgrade Required
|
||||
429: 'aVdo8F', // '6509400997', // 429 - Too Many Requests
|
||||
431: 'aVdo3n', // '6509400689', // 431 - Request Header Fields Too Large
|
||||
444: 'aVdo1P', // '6509400599', // 444 - No Response
|
||||
450: 'aVxtbK', // '6513125123', // 450 - Blocked by Windows Parental Controls
|
||||
451: 'eTiGQd', // '9113233540', // 451 - Unavailable for Legal Reasons
|
||||
500: 'aVdo6e', // '6509400855', // 500 - Internal Server Error
|
||||
502: 'aV6jCv', // '6508023429', // 502 - Bad Gateway
|
||||
503: 'aXYvop', // '6540643319', // 503 - Service Unavailable
|
||||
506: 'aXYvnH', // '6540643279', // 506 - Variant Also Negotiates
|
||||
507: 'aVdnZa', // '6509400503', // 507 - Insufficient Storage
|
||||
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/'
|
||||
}
|
||||
if (status in images) {
|
||||
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
|
||||
res.writeHead = function(status) {
|
||||
if (status in images) {
|
||||
res.setHeader('X-Status-Cat', module.exports.get_image(status))
|
||||
}
|
||||
_writeHead.apply(res, arguments)
|
||||
}
|
||||
var _writeHead = res.writeHead
|
||||
res.writeHead = function(status) {
|
||||
if (status in images) {
|
||||
res.setHeader('X-Status-Cat', module.exports.get_image(status))
|
||||
}
|
||||
_writeHead.apply(res, arguments)
|
||||
}
|
||||
|
||||
next()
|
||||
next()
|
||||
}
|
||||
|
||||
|
|
719
lib/storage.js
719
lib/storage.js
|
@ -1,32 +1,33 @@
|
|||
var async = require('async')
|
||||
, assert = require('assert')
|
||||
, Error = require('http-errors')
|
||||
, Local = require('./local-storage')
|
||||
, Proxy = require('./up-storage')
|
||||
, mystreams = require('./streams')
|
||||
, utils = require('./utils')
|
||||
, Logger = require('./logger')
|
||||
var assert = require('assert')
|
||||
var async = require('async')
|
||||
var Error = require('http-errors')
|
||||
var Local = require('./local-storage')
|
||||
var Logger = require('./logger')
|
||||
var MyStreams = require('./streams')
|
||||
var Proxy = require('./up-storage')
|
||||
var Utils = require('./utils')
|
||||
|
||||
module.exports = Storage
|
||||
|
||||
//
|
||||
// Implements Storage interface
|
||||
// (same for storage.js, local-storage.js, up-storage.js)
|
||||
//
|
||||
function Storage(config) {
|
||||
if (!(this instanceof Storage)) return new Storage(config)
|
||||
var self = Object.create(Storage.prototype)
|
||||
self.config = config
|
||||
|
||||
this.config = config
|
||||
// we support a number of uplinks, but only one local storage
|
||||
// Proxy and Local classes should have similar API interfaces
|
||||
self.uplinks = {}
|
||||
for (var p in config.uplinks) {
|
||||
self.uplinks[p] = Proxy(config.uplinks[p], config)
|
||||
self.uplinks[p].upname = p
|
||||
}
|
||||
self.local = Local(config)
|
||||
self.logger = Logger.logger.child()
|
||||
|
||||
// we support a number of uplinks, but only one local storage
|
||||
// Proxy and Local classes should have similar API interfaces
|
||||
this.uplinks = {}
|
||||
for (var p in config.uplinks) {
|
||||
this.uplinks[p] = new Proxy(config.uplinks[p], config)
|
||||
this.uplinks[p].upname = p
|
||||
}
|
||||
this.local = new Local(config)
|
||||
this.logger = Logger.logger.child()
|
||||
|
||||
return this
|
||||
return self
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -38,65 +39,61 @@ function Storage(config) {
|
|||
// Used storages: local (write) && uplinks
|
||||
//
|
||||
Storage.prototype.add_package = function(name, metadata, callback) {
|
||||
var self = this
|
||||
var self = this
|
||||
|
||||
// NOTE:
|
||||
// - when we checking package for existance, we ask ALL uplinks
|
||||
// - when we publishing package, we only publish it to some of them
|
||||
// so all requests are necessary
|
||||
// NOTE:
|
||||
// - when we checking package for existance, we ask ALL uplinks
|
||||
// - when we publishing package, we only publish it to some of them
|
||||
// so all requests are necessary
|
||||
|
||||
check_package_local(function(err) {
|
||||
if (err) return callback(err)
|
||||
check_package_local(function(err) {
|
||||
if (err) return callback(err)
|
||||
|
||||
check_package_remote(function(err) {
|
||||
if (err) return callback(err)
|
||||
check_package_remote(function(err) {
|
||||
if (err) return callback(err)
|
||||
|
||||
publish_package(function(err) {
|
||||
if (err) return callback(err)
|
||||
callback()
|
||||
})
|
||||
})
|
||||
})
|
||||
publish_package(function(err) {
|
||||
if (err) return callback(err)
|
||||
callback()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function check_package_local(cb) {
|
||||
self.local.get_package(name, {}, function(err, results) {
|
||||
if (err && err.status !== 404) return cb(err)
|
||||
function check_package_local(cb) {
|
||||
self.local.get_package(name, {}, function(err, results) {
|
||||
if (err && err.status !== 404) return cb(err)
|
||||
|
||||
if (results) {
|
||||
return cb(Error[409]('this package is already present'))
|
||||
}
|
||||
if (results) return cb( Error[409]('this package is already present') )
|
||||
|
||||
cb()
|
||||
})
|
||||
}
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
function check_package_remote(cb) {
|
||||
self._sync_package_with_uplinks(name, null, {}, function(err, results, err_results) {
|
||||
// something weird
|
||||
if (err && err.status !== 404) return cb(err)
|
||||
function check_package_remote(cb) {
|
||||
self._sync_package_with_uplinks(name, null, {}, function(err, results, err_results) {
|
||||
// something weird
|
||||
if (err && err.status !== 404) return cb(err)
|
||||
|
||||
// checking package
|
||||
if (results) {
|
||||
return cb(Error[409]('this package is already present'))
|
||||
}
|
||||
// checking package
|
||||
if (results) return cb( Error[409]('this package is already present') )
|
||||
|
||||
for (var i=0; i<err_results.length; i++) {
|
||||
// checking error
|
||||
// if uplink fails with a status other than 404, we report failure
|
||||
if (err_results[i][0] != null) {
|
||||
if (err_results[i][0].status !== 404) {
|
||||
return cb(Error[503]('one of the uplinks is down, refuse to publish'))
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i=0; i<err_results.length; i++) {
|
||||
// checking error
|
||||
// if uplink fails with a status other than 404, we report failure
|
||||
if (err_results[i][0] != null) {
|
||||
if (err_results[i][0].status !== 404) {
|
||||
return cb( Error[503]('one of the uplinks is down, refuse to publish') )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cb()
|
||||
})
|
||||
}
|
||||
return cb()
|
||||
})
|
||||
}
|
||||
|
||||
function publish_package(cb) {
|
||||
self.local.add_package(name, metadata, callback)
|
||||
}
|
||||
function publish_package(cb) {
|
||||
self.local.add_package(name, metadata, callback)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -105,7 +102,7 @@ Storage.prototype.add_package = function(name, metadata, callback) {
|
|||
// Used storages: local (write)
|
||||
//
|
||||
Storage.prototype.add_version = function(name, version, metadata, tag, callback) {
|
||||
return this.local.add_version(name, version, metadata, tag, callback)
|
||||
return this.local.add_version(name, version, metadata, tag, callback)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -114,7 +111,7 @@ Storage.prototype.add_version = function(name, version, metadata, tag, callback)
|
|||
// Used storages: local (write)
|
||||
//
|
||||
Storage.prototype.add_tags = function(name, tag_hash, callback) {
|
||||
return this.local.add_tags(name, tag_hash, callback)
|
||||
return this.local.add_tags(name, tag_hash, callback)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -126,7 +123,7 @@ Storage.prototype.add_tags = function(name, tag_hash, callback) {
|
|||
// Used storages: local (write)
|
||||
//
|
||||
Storage.prototype.change_package = function(name, metadata, revision, callback) {
|
||||
return this.local.change_package(name, metadata, revision, callback)
|
||||
return this.local.change_package(name, metadata, revision, callback)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -137,7 +134,7 @@ Storage.prototype.change_package = function(name, metadata, revision, callback)
|
|||
// Used storages: local (write)
|
||||
//
|
||||
Storage.prototype.remove_package = function(name, callback) {
|
||||
return this.local.remove_package(name, callback)
|
||||
return this.local.remove_package(name, callback)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -150,7 +147,7 @@ Storage.prototype.remove_package = function(name, callback) {
|
|||
// Used storages: local (write)
|
||||
//
|
||||
Storage.prototype.remove_tarball = function(name, filename, revision, callback) {
|
||||
return this.local.remove_tarball(name, filename, revision, callback)
|
||||
return this.local.remove_tarball(name, filename, revision, callback)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -161,7 +158,7 @@ Storage.prototype.remove_tarball = function(name, filename, revision, callback)
|
|||
// Used storages: local (write)
|
||||
//
|
||||
Storage.prototype.add_tarball = function(name, filename) {
|
||||
return this.local.add_tarball(name, filename)
|
||||
return this.local.add_tarball(name, filename)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -175,102 +172,103 @@ Storage.prototype.add_tarball = function(name, filename) {
|
|||
// Used storages: local || uplink (just one)
|
||||
//
|
||||
Storage.prototype.get_tarball = function(name, filename) {
|
||||
var stream = new mystreams.ReadTarballStream()
|
||||
stream.abort = function() {}
|
||||
var stream = MyStreams.ReadTarballStream()
|
||||
stream.abort = function() {}
|
||||
|
||||
var self = this
|
||||
var self = this
|
||||
|
||||
// if someone requesting tarball, it means that we should already have some
|
||||
// information about it, so fetching package info is unnecessary
|
||||
// if someone requesting tarball, it means that we should already have some
|
||||
// information about it, so fetching package info is unnecessary
|
||||
|
||||
// trying local first
|
||||
var rstream = self.local.get_tarball(name, filename)
|
||||
var is_open = false
|
||||
rstream.on('error', function(err) {
|
||||
if (is_open || err.status !== 404) {
|
||||
return stream.emit('error', err)
|
||||
}
|
||||
// trying local first
|
||||
var rstream = self.local.get_tarball(name, filename)
|
||||
var is_open = false
|
||||
rstream.on('error', function(err) {
|
||||
if (is_open || err.status !== 404) {
|
||||
return stream.emit('error', err)
|
||||
}
|
||||
|
||||
// local reported 404
|
||||
var err404 = err
|
||||
var uplink = null
|
||||
rstream.abort()
|
||||
rstream = null // gc
|
||||
// local reported 404
|
||||
var err404 = err
|
||||
var uplink = null
|
||||
rstream.abort()
|
||||
rstream = null // gc
|
||||
|
||||
self.local.get_package(name, function(err, info) {
|
||||
if (!err && info._distfiles && info._distfiles[filename] != null) {
|
||||
// information about this file exists locally
|
||||
serve_file(info._distfiles[filename])
|
||||
self.local.get_package(name, function(err, info) {
|
||||
if (!err && info._distfiles && info._distfiles[filename] != null) {
|
||||
// information about this file exists locally
|
||||
serve_file(info._distfiles[filename])
|
||||
|
||||
} else {
|
||||
// we know nothing about this file, trying to get information elsewhere
|
||||
} else {
|
||||
// we know nothing about this file, trying to get information elsewhere
|
||||
|
||||
self._sync_package_with_uplinks(name, info, {}, function(err, info) {
|
||||
if (err) return stream.emit('error', err)
|
||||
self._sync_package_with_uplinks(name, info, {}, function(err, info) {
|
||||
if (err) return stream.emit('error', err)
|
||||
|
||||
if (!info._distfiles || info._distfiles[filename] == null) {
|
||||
return stream.emit('error', err404)
|
||||
}
|
||||
if (!info._distfiles || info._distfiles[filename] == null) {
|
||||
return stream.emit('error', err404)
|
||||
}
|
||||
|
||||
serve_file(info._distfiles[filename])
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
rstream.on('content-length', function(v) {
|
||||
stream.emit('content-length', v)
|
||||
})
|
||||
rstream.on('open', function() {
|
||||
is_open = true
|
||||
rstream.pipe(stream)
|
||||
})
|
||||
return stream
|
||||
serve_file(info._distfiles[filename])
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
rstream.on('content-length', function(v) {
|
||||
stream.emit('content-length', v)
|
||||
})
|
||||
rstream.on('open', function() {
|
||||
is_open = true
|
||||
rstream.pipe(stream)
|
||||
})
|
||||
return stream
|
||||
|
||||
function serve_file(file) {
|
||||
var uplink = null
|
||||
for (var p in self.uplinks) {
|
||||
if (self.uplinks[p].can_fetch_url(file.url)) {
|
||||
uplink = self.uplinks[p]
|
||||
}
|
||||
}
|
||||
if (uplink == null) {
|
||||
uplink = new Proxy({
|
||||
url: file.url,
|
||||
_autogenerated: true,
|
||||
}, self.config)
|
||||
}
|
||||
function serve_file(file) {
|
||||
var uplink = null
|
||||
for (var p in self.uplinks) {
|
||||
if (self.uplinks[p].can_fetch_url(file.url)) {
|
||||
uplink = self.uplinks[p]
|
||||
}
|
||||
}
|
||||
if (uplink == null) {
|
||||
uplink = Proxy({
|
||||
url: file.url,
|
||||
_autogenerated: true,
|
||||
}, self.config)
|
||||
}
|
||||
|
||||
var savestream = self.local.add_tarball(name, filename)
|
||||
var on_open = function() {
|
||||
on_open = function(){} // prevent it from being called twice
|
||||
var rstream2 = uplink.get_url(file.url)
|
||||
rstream2.on('error', function(err) {
|
||||
if (savestream) savestream.abort()
|
||||
savestream = null
|
||||
stream.emit('error', err)
|
||||
})
|
||||
rstream2.on('end', function() {
|
||||
if (savestream) savestream.done()
|
||||
})
|
||||
var savestream = self.local.add_tarball(name, filename)
|
||||
var on_open = function() {
|
||||
on_open = function(){} // prevent it from being called twice
|
||||
var rstream2 = uplink.get_url(file.url)
|
||||
rstream2.on('error', function(err) {
|
||||
if (savestream) savestream.abort()
|
||||
savestream = null
|
||||
stream.emit('error', err)
|
||||
})
|
||||
rstream2.on('end', function() {
|
||||
if (savestream) savestream.done()
|
||||
})
|
||||
|
||||
rstream2.on('content-length', function(v) {
|
||||
stream.emit('content-length', v)
|
||||
if (savestream) savestream.emit('content-length', v)
|
||||
})
|
||||
rstream2.pipe(stream)
|
||||
if (savestream) rstream2.pipe(savestream)
|
||||
}
|
||||
rstream2.on('content-length', function(v) {
|
||||
stream.emit('content-length', v)
|
||||
if (savestream) savestream.emit('content-length', v)
|
||||
})
|
||||
rstream2.pipe(stream)
|
||||
if (savestream) rstream2.pipe(savestream)
|
||||
}
|
||||
|
||||
savestream.on('open', function() {
|
||||
on_open()
|
||||
})
|
||||
savestream.on('error', function(err) {
|
||||
self.logger.warn({err: err}, 'error saving file: @{err.message}\n@{err.stack}')
|
||||
if (savestream) savestream.abort()
|
||||
savestream = null
|
||||
on_open()
|
||||
})
|
||||
}
|
||||
savestream.on('open', function() {
|
||||
on_open()
|
||||
})
|
||||
savestream.on('error', function(err) {
|
||||
self.logger.warn( { err: err }
|
||||
, 'error saving file: @{err.message}\n@{err.stack}' )
|
||||
if (savestream) savestream.abort()
|
||||
savestream = null
|
||||
on_open()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -283,40 +281,40 @@ Storage.prototype.get_tarball = function(name, filename) {
|
|||
// Used storages: local && uplink (proxy_access)
|
||||
//
|
||||
Storage.prototype.get_package = function(name, options, callback) {
|
||||
if (typeof(options) === 'function') callback = options, options = {}
|
||||
if (typeof(options) === 'function') callback = options, options = {}
|
||||
|
||||
var self = this
|
||||
var self = this
|
||||
|
||||
self.local.get_package(name, options, function(err, data) {
|
||||
if (err && (!err.status || err.status >= 500)) {
|
||||
// report internal errors right away
|
||||
return callback(err)
|
||||
}
|
||||
self.local.get_package(name, options, function(err, data) {
|
||||
if (err && (!err.status || err.status >= 500)) {
|
||||
// report internal errors right away
|
||||
return callback(err)
|
||||
}
|
||||
|
||||
self._sync_package_with_uplinks(name, data, options, function(err, result, uplink_errors) {
|
||||
if (err) return callback(err)
|
||||
var whitelist = ['_rev', 'name', 'versions', 'dist-tags', 'readme']
|
||||
for (var i in result) {
|
||||
if (whitelist.indexOf(i) === -1) delete result[i]
|
||||
}
|
||||
self._sync_package_with_uplinks(name, data, options, function(err, result, uplink_errors) {
|
||||
if (err) return callback(err)
|
||||
var whitelist = [ '_rev', 'name', 'versions', 'dist-tags', 'readme' ]
|
||||
for (var i in result) {
|
||||
if (whitelist.indexOf(i) === -1) delete result[i]
|
||||
}
|
||||
|
||||
if (self.config.ignore_latest_tag || !result['dist-tags'].latest) {
|
||||
result['dist-tags'].latest = utils.semver_sort(Object.keys(result.versions))
|
||||
}
|
||||
if (self.config.ignore_latest_tag || !result['dist-tags'].latest) {
|
||||
result['dist-tags'].latest = Utils.semver_sort(Object.keys(result.versions))
|
||||
}
|
||||
|
||||
for (var i in result['dist-tags']) {
|
||||
if (Array.isArray(result['dist-tags'][i])) {
|
||||
result['dist-tags'][i] = result['dist-tags'][i][result['dist-tags'][i].length-1]
|
||||
if (result['dist-tags'][i] == null) delete result['dist-tags'][i]
|
||||
}
|
||||
}
|
||||
for (var i in result['dist-tags']) {
|
||||
if (Array.isArray(result['dist-tags'][i])) {
|
||||
result['dist-tags'][i] = result['dist-tags'][i][result['dist-tags'][i].length-1]
|
||||
if (result['dist-tags'][i] == null) delete result['dist-tags'][i]
|
||||
}
|
||||
}
|
||||
|
||||
// npm can throw if this field doesn't exist
|
||||
result._attachments = {}
|
||||
// npm can throw if this field doesn't exist
|
||||
result._attachments = {}
|
||||
|
||||
callback(null, result, uplink_errors)
|
||||
})
|
||||
})
|
||||
callback(null, result, uplink_errors)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -328,225 +326,222 @@ Storage.prototype.get_package = function(name, options, callback) {
|
|||
// Used storages: local && uplink (proxy_access)
|
||||
//
|
||||
Storage.prototype.search = function(startkey, options, callback) {
|
||||
var self = this
|
||||
var uplinks = []
|
||||
var i = 0
|
||||
var self = this
|
||||
var uplinks = []
|
||||
var i = 0
|
||||
|
||||
var uplinks
|
||||
for (var p in self.uplinks) {
|
||||
uplinks.push(p)
|
||||
}
|
||||
var uplinks
|
||||
for (var p in self.uplinks) {
|
||||
uplinks.push(p)
|
||||
}
|
||||
|
||||
function merge_with_local_packages(err, res, body) {
|
||||
if (err) return callback(err)
|
||||
var j = 0
|
||||
function merge_with_local_packages(err, res, body) {
|
||||
if (err) return callback(err)
|
||||
var j = 0
|
||||
|
||||
self.local.get_recent_packages(startkey, function(err, list) {
|
||||
if (err) return callback(err)
|
||||
self.local.get_recent_packages(startkey, function(err, list) {
|
||||
if (err) return callback(err)
|
||||
|
||||
var listL = list.length
|
||||
var listL = list.length
|
||||
|
||||
if (!listL) return callback(null, body)
|
||||
if (!listL) return callback(null, body)
|
||||
|
||||
list.forEach(function(item) {
|
||||
self.local.get_package(item.name, options, function(err, data) {
|
||||
if (err) return callback(err)
|
||||
list.forEach(function(item) {
|
||||
self.local.get_package(item.name, options, function(err, data) {
|
||||
if (err) return callback(err)
|
||||
|
||||
var versions = utils.semver_sort(Object.keys(data.versions))
|
||||
var latest = versions[versions.length - 1]
|
||||
var versions = Utils.semver_sort(Object.keys(data.versions))
|
||||
var latest = versions[versions.length - 1]
|
||||
|
||||
if (data.versions[latest]) {
|
||||
body[item.name] = {
|
||||
name : data.versions[latest].name,
|
||||
description : data.versions[latest].description,
|
||||
'dist-tags' : {
|
||||
latest: latest
|
||||
},
|
||||
maintainers : data.versions[latest].maintainers || [data.versions[latest]._npmUser].filter(Boolean),
|
||||
readmeFilename: data.versions[latest].readmeFilename || '',
|
||||
time : {
|
||||
modified: new Date(item.time).toISOString()
|
||||
},
|
||||
versions : {},
|
||||
repository : data.versions[latest].repository,
|
||||
keywords : data.versions[latest].keywords
|
||||
}
|
||||
body[item.name].versions[latest] = 'latest'
|
||||
}
|
||||
if (data.versions[latest]) {
|
||||
body[item.name] = {
|
||||
name : data.versions[latest].name,
|
||||
description : data.versions[latest].description,
|
||||
'dist-tags' : { latest: latest },
|
||||
maintainers : data.versions[latest].maintainers || [data.versions[latest]._npmUser].filter(Boolean),
|
||||
readmeFilename: data.versions[latest].readmeFilename || '',
|
||||
time : {
|
||||
modified: new Date(item.time).toISOString()
|
||||
},
|
||||
versions : {},
|
||||
repository : data.versions[latest].repository,
|
||||
keywords : data.versions[latest].keywords
|
||||
}
|
||||
body[item.name].versions[latest] = 'latest'
|
||||
}
|
||||
|
||||
if (++j !== listL) {
|
||||
return false
|
||||
}
|
||||
if (++j !== listL) {
|
||||
return false
|
||||
}
|
||||
|
||||
return callback(null, body)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
return callback(null, body)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function remote_search() {
|
||||
var uplink = self.uplinks[uplinks[i]]
|
||||
if (!uplink) {
|
||||
return merge_with_local_packages(null, null, {})
|
||||
}
|
||||
self.uplinks[uplinks[i]].request({
|
||||
uri: options.req.url,
|
||||
timeout: self.uplinks[p].timeout,
|
||||
json: true
|
||||
}, function(err, res, body) {
|
||||
if (err || Math.floor(res.statusCode / 100) > 3) {
|
||||
i++
|
||||
return remote_search()
|
||||
}
|
||||
return merge_with_local_packages(err, res, body)
|
||||
})
|
||||
}
|
||||
function remote_search() {
|
||||
var uplink = self.uplinks[uplinks[i]]
|
||||
if (!uplink) {
|
||||
return merge_with_local_packages(null, null, {})
|
||||
}
|
||||
self.uplinks[uplinks[i]].request({
|
||||
uri: options.req.url,
|
||||
timeout: self.uplinks[p].timeout,
|
||||
json: true,
|
||||
}, function(err, res, body) {
|
||||
if (err || Math.floor(res.statusCode / 100) > 3) {
|
||||
i++
|
||||
return remote_search()
|
||||
}
|
||||
return merge_with_local_packages(err, res, body)
|
||||
})
|
||||
}
|
||||
|
||||
remote_search()
|
||||
remote_search()
|
||||
}
|
||||
|
||||
Storage.prototype.get_local = function(callback) {
|
||||
var self = this
|
||||
, locals = this.config.localList.get()
|
||||
, packages = [];
|
||||
var self = this
|
||||
var locals = this.config.localList.get()
|
||||
var packages = []
|
||||
|
||||
var getPackage = function(i) {
|
||||
self.local.get_package(locals[i], function(err, info) {
|
||||
if (!err) {
|
||||
var latest = Array.isArray(info['dist-tags'].latest)
|
||||
? utils.semver_sort(info['dist-tags'].latest).pop()
|
||||
: info['dist-tags'].latest
|
||||
if (info.versions[latest]) {
|
||||
packages.push(info.versions[latest])
|
||||
} else {
|
||||
self.logger.warn({package: locals[i]}, 'package @{package} does not have a "latest" tag?')
|
||||
}
|
||||
}
|
||||
var getPackage = function(i) {
|
||||
self.local.get_package(locals[i], function(err, info) {
|
||||
if (!err) {
|
||||
var latest = Array.isArray(info['dist-tags'].latest)
|
||||
? Utils.semver_sort(info['dist-tags'].latest).pop()
|
||||
: info['dist-tags'].latest
|
||||
if (info.versions[latest]) {
|
||||
packages.push(info.versions[latest])
|
||||
} else {
|
||||
self.logger.warn( { package: locals[i] }
|
||||
, 'package @{package} does not have a "latest" tag?' )
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= locals.length - 1) {
|
||||
callback(null, packages);
|
||||
} else {
|
||||
getPackage(i + 1);
|
||||
}
|
||||
});
|
||||
};
|
||||
if (i >= locals.length - 1) {
|
||||
callback(null, packages)
|
||||
} else {
|
||||
getPackage(i + 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if(locals.length) {
|
||||
getPackage(0);
|
||||
}
|
||||
else {
|
||||
callback(null, []);
|
||||
}
|
||||
};
|
||||
if (locals.length) {
|
||||
getPackage(0)
|
||||
} else {
|
||||
callback(null, [])
|
||||
}
|
||||
}
|
||||
|
||||
// function fetches package information from uplinks and synchronizes it with local data
|
||||
// if package is available locally, it MUST be provided in pkginfo
|
||||
// returns callback(err, result, uplink_errors)
|
||||
Storage.prototype._sync_package_with_uplinks = function(name, pkginfo, options, callback) {
|
||||
var self = this
|
||||
var self = this
|
||||
|
||||
if (!pkginfo) {
|
||||
var exists = false
|
||||
if (!pkginfo) {
|
||||
var exists = false
|
||||
|
||||
pkginfo = {
|
||||
name: name,
|
||||
versions: {},
|
||||
'dist-tags': {},
|
||||
_uplinks: {},
|
||||
}
|
||||
} else {
|
||||
var exists = true
|
||||
}
|
||||
pkginfo = {
|
||||
name : name,
|
||||
versions : {},
|
||||
'dist-tags' : {},
|
||||
_uplinks : {},
|
||||
}
|
||||
} else {
|
||||
var exists = true
|
||||
}
|
||||
|
||||
var uplinks = []
|
||||
for (var i in self.uplinks) {
|
||||
if (self.config.proxy_access(name, i)) {
|
||||
uplinks.push(self.uplinks[i])
|
||||
}
|
||||
}
|
||||
var uplinks = []
|
||||
for (var i in self.uplinks) {
|
||||
if (self.config.proxy_access(name, i)) {
|
||||
uplinks.push(self.uplinks[i])
|
||||
}
|
||||
}
|
||||
|
||||
async.map(uplinks, function(up, cb) {
|
||||
var _options = Object.create(options)
|
||||
if (utils.is_object(pkginfo._uplinks[up.upname])) {
|
||||
var fetched = pkginfo._uplinks[up.upname].fetched
|
||||
if (fetched && fetched > (Date.now() - up.maxage)) {
|
||||
return cb()
|
||||
}
|
||||
async.map(uplinks, function(up, cb) {
|
||||
var _options = Object.create(options)
|
||||
if (Utils.is_object(pkginfo._uplinks[up.upname])) {
|
||||
var fetched = pkginfo._uplinks[up.upname].fetched
|
||||
if (fetched && fetched > (Date.now() - up.maxage)) {
|
||||
return cb()
|
||||
}
|
||||
|
||||
_options.etag = pkginfo._uplinks[up.upname].etag
|
||||
}
|
||||
_options.etag = pkginfo._uplinks[up.upname].etag
|
||||
}
|
||||
|
||||
up.get_package(name, _options, function(err, up_res, etag) {
|
||||
if (err && err.status === 304)
|
||||
pkginfo._uplinks[up.upname].fetched = Date.now()
|
||||
up.get_package(name, _options, function(err, up_res, etag) {
|
||||
if (err && err.status === 304)
|
||||
pkginfo._uplinks[up.upname].fetched = Date.now()
|
||||
|
||||
if (err || !up_res) return cb(null, [err || Error('no data')])
|
||||
if (err || !up_res) return cb(null, [err || Error('no data')])
|
||||
|
||||
try {
|
||||
utils.validate_metadata(up_res, name)
|
||||
} catch(err) {
|
||||
self.logger.error({
|
||||
sub: 'out',
|
||||
err: err,
|
||||
}, 'package.json validating error @{!err.message}\n@{err.stack}')
|
||||
return cb(null, [err])
|
||||
}
|
||||
try {
|
||||
Utils.validate_metadata(up_res, name)
|
||||
} catch(err) {
|
||||
self.logger.error({
|
||||
sub: 'out',
|
||||
err: err,
|
||||
}, 'package.json validating error @{!err.message}\n@{err.stack}')
|
||||
return cb(null, [ err ])
|
||||
}
|
||||
|
||||
pkginfo._uplinks[up.upname] = {
|
||||
etag: etag,
|
||||
fetched: Date.now()
|
||||
}
|
||||
pkginfo._uplinks[up.upname] = {
|
||||
etag: etag,
|
||||
fetched: Date.now()
|
||||
}
|
||||
|
||||
try {
|
||||
Storage._merge_versions(pkginfo, up_res, self.config)
|
||||
} catch(err) {
|
||||
self.logger.error({
|
||||
sub: 'out',
|
||||
err: err,
|
||||
}, 'package.json parsing error @{!err.message}\n@{err.stack}')
|
||||
return cb(null, [err])
|
||||
}
|
||||
try {
|
||||
Storage._merge_versions(pkginfo, up_res, self.config)
|
||||
} catch(err) {
|
||||
self.logger.error({
|
||||
sub: 'out',
|
||||
err: err,
|
||||
}, 'package.json parsing error @{!err.message}\n@{err.stack}')
|
||||
return cb(null, [ err ])
|
||||
}
|
||||
|
||||
// if we got to this point, assume that the correct package exists
|
||||
// on the uplink
|
||||
exists = true
|
||||
cb()
|
||||
})
|
||||
}, function(err, uplink_errors) {
|
||||
assert(!err && Array.isArray(uplink_errors))
|
||||
// if we got to this point, assume that the correct package exists
|
||||
// on the uplink
|
||||
exists = true
|
||||
cb()
|
||||
})
|
||||
}, function(err, uplink_errors) {
|
||||
assert(!err && Array.isArray(uplink_errors))
|
||||
|
||||
if (!exists) {
|
||||
return callback( Error[404]('no such package available')
|
||||
, null
|
||||
, uplink_errors )
|
||||
}
|
||||
if (!exists) {
|
||||
return callback( Error[404]('no such package available')
|
||||
, null
|
||||
, uplink_errors )
|
||||
}
|
||||
|
||||
self.local.update_versions(name, pkginfo, function(err, pkginfo) {
|
||||
if (err) return callback(err)
|
||||
return callback(null, pkginfo, uplink_errors)
|
||||
})
|
||||
})
|
||||
self.local.update_versions(name, pkginfo, function(err, pkginfo) {
|
||||
if (err) return callback(err)
|
||||
return callback(null, pkginfo, uplink_errors)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// function gets a local info and an info from uplinks and tries to merge it
|
||||
// exported for unit tests only
|
||||
Storage._merge_versions = function(local, up, config) {
|
||||
// copy new versions to a cache
|
||||
// NOTE: if a certain version was updated, we can't refresh it reliably
|
||||
for (var i in up.versions) {
|
||||
if (local.versions[i] == null) {
|
||||
local.versions[i] = up.versions[i]
|
||||
}
|
||||
}
|
||||
// copy new versions to a cache
|
||||
// NOTE: if a certain version was updated, we can't refresh it reliably
|
||||
for (var i in up.versions) {
|
||||
if (local.versions[i] == null) {
|
||||
local.versions[i] = up.versions[i]
|
||||
}
|
||||
}
|
||||
|
||||
// refresh dist-tags
|
||||
for (var i in up['dist-tags']) {
|
||||
var added = utils.tag_version(local, up['dist-tags'][i], i, config || {})
|
||||
if (i === 'latest' && added) {
|
||||
// if remote has more fresh package, we should borrow its readme
|
||||
local.readme = up.readme
|
||||
}
|
||||
}
|
||||
// refresh dist-tags
|
||||
for (var i in up['dist-tags']) {
|
||||
var added = Utils.tag_version(local, up['dist-tags'][i], i, config || {})
|
||||
if (i === 'latest' && added) {
|
||||
// if remote has more fresh package, we should borrow its readme
|
||||
local.readme = up.readme
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Storage
|
||||
|
|
|
@ -1,53 +1,60 @@
|
|||
var stream = require('stream')
|
||||
, util = require('util')
|
||||
var Stream = require('stream')
|
||||
var Util = require('util')
|
||||
|
||||
module.exports.ReadTarballStream = ReadTarball
|
||||
module.exports.UploadTarballStream = UploadTarball
|
||||
|
||||
//
|
||||
// This stream is used to read tarballs from repository
|
||||
//
|
||||
function ReadTarball(options) {
|
||||
stream.PassThrough.call(this, options)
|
||||
var self = new Stream.PassThrough(options)
|
||||
self.__proto__ = ReadTarball.prototype
|
||||
|
||||
// called when data is not needed anymore
|
||||
add_abstract_method(this, 'abort')
|
||||
// called when data is not needed anymore
|
||||
add_abstract_method(self, 'abort')
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
util.inherits(ReadTarball, stream.PassThrough)
|
||||
module.exports.ReadTarballStream = ReadTarball
|
||||
Util.inherits(ReadTarball, Stream.PassThrough)
|
||||
|
||||
//
|
||||
// This stream is used to upload tarballs to a repository
|
||||
//
|
||||
function UploadTarball(options) {
|
||||
stream.PassThrough.call(this, options)
|
||||
var self = new Stream.PassThrough(options)
|
||||
self.__proto__ = UploadTarball.prototype
|
||||
|
||||
// called when user closes connection before upload finishes
|
||||
add_abstract_method(this, 'abort')
|
||||
// called when user closes connection before upload finishes
|
||||
add_abstract_method(self, 'abort')
|
||||
|
||||
// called when upload finishes successfully
|
||||
add_abstract_method(this, 'done')
|
||||
// called when upload finishes successfully
|
||||
add_abstract_method(self, 'done')
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
util.inherits(UploadTarball, stream.PassThrough)
|
||||
module.exports.UploadTarballStream = UploadTarball
|
||||
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.__defineGetter__(name, function() {
|
||||
return function() {
|
||||
self._called_methods[name] = true
|
||||
}
|
||||
})
|
||||
self.__defineSetter__(name, function(fn) {
|
||||
delete self[name]
|
||||
self[name] = fn
|
||||
if (self._called_methods && self._called_methods[name]) {
|
||||
delete self._called_methods[name]
|
||||
self[name]()
|
||||
}
|
||||
})
|
||||
self._called_methods = self._called_methods || {}
|
||||
self.__defineGetter__(name, function() {
|
||||
return function() {
|
||||
self._called_methods[name] = true
|
||||
}
|
||||
})
|
||||
self.__defineSetter__(name, function(fn) {
|
||||
delete self[name]
|
||||
self[name] = fn
|
||||
if (self._called_methods && self._called_methods[name]) {
|
||||
delete self._called_methods[name]
|
||||
self[name]()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,345 +1,346 @@
|
|||
var URL = require('url')
|
||||
, request = require('request')
|
||||
, Stream = require('stream')
|
||||
, zlib = require('zlib')
|
||||
, Error = require('http-errors')
|
||||
, mystreams = require('./streams')
|
||||
, Logger = require('./logger')
|
||||
, utils = require('./utils')
|
||||
, parse_interval = require('./config').parse_interval
|
||||
, encode = encodeURIComponent
|
||||
var Error = require('http-errors')
|
||||
var request = require('request')
|
||||
var Stream = require('stream')
|
||||
var URL = require('url')
|
||||
var zlib = require('zlib')
|
||||
var parse_interval = require('./config').parse_interval
|
||||
var Logger = require('./logger')
|
||||
var MyStreams = require('./streams')
|
||||
var Utils = require('./utils')
|
||||
var encode = encodeURIComponent
|
||||
|
||||
module.exports = Storage
|
||||
|
||||
//
|
||||
// Implements Storage interface
|
||||
// (same for storage.js, local-storage.js, up-storage.js)
|
||||
//
|
||||
function Storage(config, mainconfig) {
|
||||
if (!(this instanceof Storage)) return new Storage(config)
|
||||
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
|
||||
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
|
||||
|
||||
this.url = URL.parse(this.config.url)
|
||||
if (this.url.hostname === 'registry.npmjs.org') {
|
||||
// npm registry is too slow working with ssl :(
|
||||
/*if (this.config._autogenerated) {
|
||||
// encrypt all the things!
|
||||
this.url.protocol = 'https'
|
||||
this.config.url = URL.format(this.url)
|
||||
}*/
|
||||
}
|
||||
self.url = URL.parse(self.config.url)
|
||||
if (self.url.hostname === 'registry.npmjs.org') {
|
||||
// npm registry is too slow working with ssl :(
|
||||
/*if (self.config._autogenerated) {
|
||||
// encrypt all the things!
|
||||
self.url.protocol = 'https'
|
||||
self.config.url = URL.format(self.url)
|
||||
}*/
|
||||
}
|
||||
|
||||
_setupProxy.call(this, this.url.hostname, config, mainconfig, this.url.protocol === 'https:')
|
||||
_setupProxy.call(self, self.url.hostname, config, mainconfig, self.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 + '\nWe changed time format to nginx-like one\n(see http://wiki.nginx.org/ConfigNotation)\nso please update your config accordingly')
|
||||
}
|
||||
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
|
||||
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
|
||||
// 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
|
||||
}
|
||||
// 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'
|
||||
var no_proxy
|
||||
var proxy_key = isHTTPS ? 'https_proxy' : 'http_proxy'
|
||||
|
||||
// get http_proxy and no_proxy configs
|
||||
if (proxy_key in config) {
|
||||
this.proxy = config[proxy_key]
|
||||
} else if (proxy_key in mainconfig) {
|
||||
this.proxy = mainconfig[proxy_key]
|
||||
}
|
||||
if ('no_proxy' in config) {
|
||||
no_proxy = config.no_proxy
|
||||
} else if ('no_proxy' in mainconfig) {
|
||||
no_proxy = mainconfig.no_proxy
|
||||
}
|
||||
// get http_proxy and no_proxy configs
|
||||
if (proxy_key in config) {
|
||||
this.proxy = config[proxy_key]
|
||||
} else if (proxy_key in mainconfig) {
|
||||
this.proxy = mainconfig[proxy_key]
|
||||
}
|
||||
if ('no_proxy' in config) {
|
||||
no_proxy = config.no_proxy
|
||||
} else if ('no_proxy' in mainconfig) {
|
||||
no_proxy = mainconfig.no_proxy
|
||||
}
|
||||
|
||||
// use wget-like algorithm to determine if proxy shouldn't be used
|
||||
if (hostname[0] !== '.') hostname = '.' + hostname
|
||||
if (typeof(no_proxy) === 'string' && no_proxy.length) {
|
||||
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
|
||||
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
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// use wget-like algorithm to determine if proxy shouldn't be used
|
||||
if (hostname[0] !== '.') hostname = '.' + hostname
|
||||
if (typeof(no_proxy) === 'string' && no_proxy.length) {
|
||||
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
|
||||
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
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it's non-string (i.e. "false"), don't use it
|
||||
if (typeof(this.proxy) !== 'string') {
|
||||
delete this.proxy
|
||||
} else {
|
||||
this.logger.debug({url: this.url.href, proxy: this.proxy},
|
||||
'using proxy @{proxy} for @{url}')
|
||||
}
|
||||
// if it's non-string (i.e. "false"), don't use it
|
||||
if (typeof(this.proxy) !== 'string') {
|
||||
delete this.proxy
|
||||
} else {
|
||||
this.logger.debug( { url: this.url.href, proxy: this.proxy }
|
||||
, 'using proxy @{proxy} for @{url}' )
|
||||
}
|
||||
}
|
||||
|
||||
Storage.prototype.request = function(options, cb) {
|
||||
if (!this.status_check()) {
|
||||
var req = new Stream.Readable()
|
||||
process.nextTick(function() {
|
||||
if (typeof(cb) === 'function') cb(Error('uplink is offline'))
|
||||
req.emit('error', Error('uplink is offline'))
|
||||
})
|
||||
// preventing 'Uncaught, unspecified "error" event'
|
||||
req.on('error', function(){})
|
||||
return req
|
||||
}
|
||||
if (!this.status_check()) {
|
||||
var req = new Stream.Readable()
|
||||
process.nextTick(function() {
|
||||
if (typeof(cb) === 'function') cb(Error('uplink is offline'))
|
||||
req.emit('error', Error('uplink is offline'))
|
||||
})
|
||||
// preventing 'Uncaught, unspecified "error" event'
|
||||
req.on('error', function(){})
|
||||
return req
|
||||
}
|
||||
|
||||
var self = this
|
||||
, headers = options.headers || {}
|
||||
headers['Accept'] = headers['Accept'] || 'application/json'
|
||||
headers['Accept-Encoding'] = headers['Accept-Encoding'] || 'gzip'
|
||||
headers['User-Agent'] = headers['User-Agent'] || this.userAgent
|
||||
var self = this
|
||||
var headers = options.headers || {}
|
||||
headers['Accept'] = headers['Accept'] || 'application/json'
|
||||
headers['Accept-Encoding'] = headers['Accept-Encoding'] || 'gzip'
|
||||
headers['User-Agent'] = headers['User-Agent'] || this.userAgent
|
||||
|
||||
var method = options.method || 'GET'
|
||||
, uri = options.uri_full || (this.config.url + options.uri)
|
||||
self.logger.info({
|
||||
method: method,
|
||||
headers: headers,
|
||||
uri: uri,
|
||||
}, "making request: '@{method} @{uri}'")
|
||||
var method = options.method || 'GET'
|
||||
var uri = options.uri_full || (this.config.url + options.uri)
|
||||
|
||||
if (utils.is_object(options.json)) {
|
||||
var json = JSON.stringify(options.json)
|
||||
headers['Content-Type'] = headers['Content-Type'] || 'application/json'
|
||||
}
|
||||
self.logger.info({
|
||||
method : method,
|
||||
headers : headers,
|
||||
uri : uri,
|
||||
}, "making request: '@{method} @{uri}'")
|
||||
|
||||
var req = request({
|
||||
url: uri,
|
||||
method: method,
|
||||
headers: headers,
|
||||
body: json,
|
||||
ca: this.ca,
|
||||
proxy: this.proxy,
|
||||
encoding: null,
|
||||
timeout: this.timeout,
|
||||
}, function(err, res, body) {
|
||||
var error
|
||||
var res_length = err ? 0 : body.length
|
||||
if (Utils.is_object(options.json)) {
|
||||
var json = JSON.stringify(options.json)
|
||||
headers['Content-Type'] = headers['Content-Type'] || 'application/json'
|
||||
}
|
||||
|
||||
do_gunzip(function() {
|
||||
do_decode()
|
||||
do_log()
|
||||
if (cb) cb(err, res, body)
|
||||
})
|
||||
var req = request({
|
||||
url : uri,
|
||||
method : method,
|
||||
headers : headers,
|
||||
body : json,
|
||||
ca : this.ca,
|
||||
proxy : this.proxy,
|
||||
encoding : null,
|
||||
timeout : this.timeout,
|
||||
}, function(err, res, body) {
|
||||
var error
|
||||
var res_length = err ? 0 : body.length
|
||||
|
||||
function do_gunzip(cb) {
|
||||
if (err) return cb()
|
||||
if (res.headers['content-encoding'] !== 'gzip') return cb()
|
||||
zlib.gunzip(body, function(er, buf) {
|
||||
if (er) err = er
|
||||
body = buf
|
||||
return cb()
|
||||
})
|
||||
}
|
||||
do_gunzip(function() {
|
||||
do_decode()
|
||||
do_log()
|
||||
if (cb) cb(err, res, body)
|
||||
})
|
||||
|
||||
function do_decode() {
|
||||
if (err) {
|
||||
error = err.message
|
||||
return
|
||||
}
|
||||
function do_gunzip(cb) {
|
||||
if (err) return cb()
|
||||
if (res.headers['content-encoding'] !== 'gzip') return cb()
|
||||
zlib.gunzip(body, function(er, buf) {
|
||||
if (er) err = er
|
||||
body = buf
|
||||
return cb()
|
||||
})
|
||||
}
|
||||
|
||||
if (options.json && res.statusCode < 300) {
|
||||
try {
|
||||
body = JSON.parse(body.toString('utf8'))
|
||||
} catch(_err) {
|
||||
body = {}
|
||||
err = _err
|
||||
error = err.message
|
||||
}
|
||||
}
|
||||
function do_decode() {
|
||||
if (err) {
|
||||
error = err.message
|
||||
return
|
||||
}
|
||||
|
||||
if (!err && utils.is_object(body)) {
|
||||
if (typeof(body.error) === 'string') {
|
||||
error = body.error
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.json && res.statusCode < 300) {
|
||||
try {
|
||||
body = JSON.parse(body.toString('utf8'))
|
||||
} catch(_err) {
|
||||
body = {}
|
||||
err = _err
|
||||
error = err.message
|
||||
}
|
||||
}
|
||||
|
||||
function do_log() {
|
||||
var message = '@{!status}, req: \'@{request.method} @{request.url}\''
|
||||
if (error) {
|
||||
message += ', error: @{!error}'
|
||||
} else {
|
||||
message += ', 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)
|
||||
}
|
||||
})
|
||||
if (!err && Utils.is_object(body)) {
|
||||
if (typeof(body.error) === 'string') {
|
||||
error = body.error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var status_called = false
|
||||
req.on('response', function(res) {
|
||||
if (!req._sinopia_aborted && !status_called) {
|
||||
status_called = true
|
||||
self.status_check(true)
|
||||
}
|
||||
})
|
||||
req.on('error', function(_err) {
|
||||
if (!req._sinopia_aborted && !status_called) {
|
||||
status_called = true
|
||||
self.status_check(false)
|
||||
}
|
||||
})
|
||||
return req
|
||||
function do_log() {
|
||||
var message = '@{!status}, req: \'@{request.method} @{request.url}\''
|
||||
message += error
|
||||
? ', error: @{!error}'
|
||||
: ', 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)
|
||||
}
|
||||
})
|
||||
|
||||
var status_called = false
|
||||
req.on('response', function(res) {
|
||||
if (!req._sinopia_aborted && !status_called) {
|
||||
status_called = true
|
||||
self.status_check(true)
|
||||
}
|
||||
})
|
||||
req.on('error', function(_err) {
|
||||
if (!req._sinopia_aborted && !status_called) {
|
||||
status_called = true
|
||||
self.status_check(false)
|
||||
}
|
||||
})
|
||||
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
|
||||
} else {
|
||||
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.failed_requests = 0
|
||||
} else {
|
||||
this.failed_requests++
|
||||
if (this.failed_requests === this.max_fails) {
|
||||
this.logger.warn({host: this.url.host}, 'host @{host} is now offline')
|
||||
}
|
||||
}
|
||||
this.last_request_time = Date.now()
|
||||
}
|
||||
if (arguments.length === 0) {
|
||||
if (this.failed_requests >= this.max_fails
|
||||
&& Math.abs(Date.now() - this.last_request_time) < this.fail_timeout) {
|
||||
return false
|
||||
} else {
|
||||
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.failed_requests = 0
|
||||
} else {
|
||||
this.failed_requests++
|
||||
if (this.failed_requests === this.max_fails) {
|
||||
this.logger.warn({ host: this.url.host }, 'host @{host} is now offline')
|
||||
}
|
||||
}
|
||||
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
|
||||
return url.protocol === this.url.protocol
|
||||
&& url.host === this.url.host
|
||||
&& 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 = {}
|
||||
if (options.etag) {
|
||||
headers['If-None-Match'] = options.etag
|
||||
headers['Accept'] = 'application/octet-stream'
|
||||
}
|
||||
this._add_proxy_headers(options.req, headers)
|
||||
var headers = {}
|
||||
if (options.etag) {
|
||||
headers['If-None-Match'] = options.etag
|
||||
headers['Accept'] = 'application/octet-stream'
|
||||
}
|
||||
this._add_proxy_headers(options.req, headers)
|
||||
|
||||
this.request({
|
||||
uri: '/' + encode(name),
|
||||
json: true,
|
||||
headers: headers,
|
||||
}, function(err, res, body) {
|
||||
if (err) return callback(err)
|
||||
if (res.statusCode === 404) {
|
||||
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)
|
||||
}
|
||||
callback(null, body, res.headers.etag)
|
||||
})
|
||||
this.request({
|
||||
uri : '/' + encode(name),
|
||||
json : true,
|
||||
headers : headers,
|
||||
}, function(err, res, body) {
|
||||
if (err) return callback(err)
|
||||
if (res.statusCode === 404) {
|
||||
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)
|
||||
}
|
||||
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 = new mystreams.ReadTarballStream()
|
||||
stream.abort = function() {}
|
||||
var current_length = 0, expected_length
|
||||
var stream = MyStreams.ReadTarballStream()
|
||||
stream.abort = function() {}
|
||||
var current_length = 0, expected_length
|
||||
|
||||
var rstream = this.request({
|
||||
uri_full: url,
|
||||
encoding: null,
|
||||
headers: {
|
||||
Accept: 'application/octet-stream',
|
||||
},
|
||||
})
|
||||
var rstream = this.request({
|
||||
uri_full: url,
|
||||
encoding: null,
|
||||
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"))
|
||||
}
|
||||
if (!(res.statusCode >= 200 && res.statusCode < 300)) {
|
||||
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'])
|
||||
}
|
||||
rstream.on('response', function(res) {
|
||||
if (res.statusCode === 404) {
|
||||
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))
|
||||
}
|
||||
if (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)
|
||||
})
|
||||
rstream.on('data', function(d) {
|
||||
current_length += d.length
|
||||
})
|
||||
rstream.on('end', function(d) {
|
||||
if (d) current_length += d.length
|
||||
if (expected_length && current_length != expected_length)
|
||||
stream.emit('error', Error('content length mismatch'))
|
||||
})
|
||||
return stream
|
||||
rstream.on('error', function(err) {
|
||||
stream.emit('error', err)
|
||||
})
|
||||
rstream.on('data', function(d) {
|
||||
current_length += d.length
|
||||
})
|
||||
rstream.on('end', function(d) {
|
||||
if (d) current_length += d.length
|
||||
if (expected_length && current_length != expected_length)
|
||||
stream.emit('error', Error('content length mismatch'))
|
||||
})
|
||||
return stream
|
||||
}
|
||||
|
||||
Storage.prototype._add_proxy_headers = function(req, headers) {
|
||||
if (req) {
|
||||
headers['X-Forwarded-For'] = (
|
||||
(req && req.headers['x-forwarded-for']) ?
|
||||
req.headers['x-forwarded-for'] + ', ' :
|
||||
''
|
||||
) + req.connection.remoteAddress
|
||||
}
|
||||
if (req) {
|
||||
headers['X-Forwarded-For'] = (
|
||||
req && req.headers['x-forwarded-for']
|
||||
? req.headers['x-forwarded-for'] + ', '
|
||||
: ''
|
||||
) + req.connection.remoteAddress
|
||||
}
|
||||
|
||||
// always attach Via header to avoid loops, even if we're not proxying
|
||||
headers['Via'] =
|
||||
(req && req.headers['via']) ?
|
||||
req.headers['via'] + ', ' :
|
||||
''
|
||||
// always attach Via header to avoid loops, even if we're not proxying
|
||||
headers['Via'] =
|
||||
req && req.headers['via']
|
||||
? req.headers['via'] + ', '
|
||||
: ''
|
||||
|
||||
headers['Via'] += '1.1 ' + this.server_id + ' (Sinopia)'
|
||||
headers['Via'] += '1.1 ' + this.server_id + ' (Sinopia)'
|
||||
}
|
||||
|
||||
module.exports = Storage
|
||||
|
||||
|
|
212
lib/utils.js
212
lib/utils.js
|
@ -1,149 +1,149 @@
|
|||
var assert = require('assert')
|
||||
, semver = require('semver')
|
||||
, Logger = require('./logger')
|
||||
, URL = require('url')
|
||||
var Semver = require('semver')
|
||||
var URL = require('url')
|
||||
var Logger = require('./logger')
|
||||
|
||||
// from normalize-package-data/lib/fixer.js
|
||||
module.exports.validate_name = function(name) {
|
||||
if (typeof(name) !== 'string') return false
|
||||
name = name.toLowerCase()
|
||||
if (
|
||||
// all URL-safe characters and "@" for issue #75
|
||||
!name.match(/^[-a-zA-Z0-9_.!~*'()@]+$/) ||
|
||||
name.charAt(0) === '.' || // ".bin", etc.
|
||||
name.charAt(0) === '-' || // "-" is reserved by couchdb
|
||||
name === 'node_modules' ||
|
||||
name === '__proto__' ||
|
||||
name === 'package.json' ||
|
||||
name === 'favicon.ico'
|
||||
) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
if (typeof(name) !== 'string') return false
|
||||
name = name.toLowerCase()
|
||||
|
||||
// all URL-safe characters and "@" for issue #75
|
||||
if (!name.match(/^[-a-zA-Z0-9_.!~*'()@]+$/)
|
||||
|| name.charAt(0) === '.' // ".bin", etc.
|
||||
|| name.charAt(0) === '-' // "-" is reserved by couchdb
|
||||
|| name === 'node_modules'
|
||||
|| name === '__proto__'
|
||||
|| name === 'package.json'
|
||||
|| name === 'favicon.ico'
|
||||
) {
|
||||
return false
|
||||
} else {
|
||||
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'] = {}
|
||||
}
|
||||
if (!module.exports.is_object(object['dist-tags'])) {
|
||||
object['dist-tags'] = {}
|
||||
}
|
||||
|
||||
if (!module.exports.is_object(object['versions'])) {
|
||||
object['versions'] = {}
|
||||
}
|
||||
if (!module.exports.is_object(object['versions'])) {
|
||||
object['versions'] = {}
|
||||
}
|
||||
|
||||
return object
|
||||
return object
|
||||
}
|
||||
|
||||
module.exports.parse_tarball_url = function(_url) {
|
||||
var url = URL.parse(_url)
|
||||
var url = URL.parse(_url)
|
||||
|
||||
var path = url.path.replace(/^\//, '').split('/')
|
||||
if (path.length >= 3 && path[path.length-2] === '-') {
|
||||
var filename = path.pop()
|
||||
, pkgpath = '/' + filename // tarball name
|
||||
pkgpath = '/' + path.pop() + pkgpath // "-"
|
||||
pkgpath = '/' + path.pop() + pkgpath // package.name
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
var path = url.path.replace(/^\//, '').split('/')
|
||||
if (path.length >= 3 && path[path.length-2] === '-') {
|
||||
var filename = path.pop()
|
||||
var pkgpath = '/' + filename // tarball name
|
||||
pkgpath = '/' + path.pop() + pkgpath // "-"
|
||||
pkgpath = '/' + path.pop() + pkgpath // package.name
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
protocol: url.protocol,
|
||||
host: url.host,
|
||||
prepath: '/' + path.join('/'),
|
||||
pkgpath: pkgpath,
|
||||
filename: filename,
|
||||
}
|
||||
return {
|
||||
protocol : url.protocol,
|
||||
host : url.host,
|
||||
prepath : '/' + path.join('/'),
|
||||
pkgpath : pkgpath,
|
||||
filename : filename,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.filter_tarball_urls = function(pkg, req, config) {
|
||||
function filter(_url) {
|
||||
if (!req.headers.host) return _url
|
||||
function filter(_url) {
|
||||
if (!req.headers.host) return _url
|
||||
|
||||
var url = module.exports.parse_tarball_url(_url)
|
||||
// weird url, just return it
|
||||
if (url == null) return _url
|
||||
var url = module.exports.parse_tarball_url(_url)
|
||||
// weird url, just return it
|
||||
if (url == null) return _url
|
||||
|
||||
if (config.url_prefix != null) {
|
||||
var result = config.url_prefix.replace(/\/$/, '')
|
||||
} else {
|
||||
var result = req.protocol + '://' + req.headers.host
|
||||
}
|
||||
if (config.url_prefix != null) {
|
||||
var result = config.url_prefix.replace(/\/$/, '')
|
||||
} else {
|
||||
var result = req.protocol + '://' + req.headers.host
|
||||
}
|
||||
|
||||
return result + url.pkgpath
|
||||
}
|
||||
return result + url.pkgpath
|
||||
}
|
||||
|
||||
for (var ver in pkg.versions) {
|
||||
var dist = pkg.versions[ver].dist
|
||||
if (dist != null && dist.tarball != null) {
|
||||
//dist.__sinopia_orig_tarball = dist.tarball
|
||||
dist.tarball = filter(dist.tarball)
|
||||
}
|
||||
}
|
||||
return pkg
|
||||
for (var ver in pkg.versions) {
|
||||
var dist = pkg.versions[ver].dist
|
||||
if (dist != null && dist.tarball != null) {
|
||||
//dist.__sinopia_orig_tarball = dist.tarball
|
||||
dist.tarball = filter(dist.tarball)
|
||||
}
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
|
||||
function can_add_tag(tag, config) {
|
||||
if (!tag) return false
|
||||
if (tag === 'latest' && config.ignore_latest_tag) return false
|
||||
return true
|
||||
if (!tag) return false
|
||||
if (tag === 'latest' && config.ignore_latest_tag) return false
|
||||
return true
|
||||
}
|
||||
|
||||
module.exports.tag_version = function(data, version, tag, config) {
|
||||
if (!can_add_tag(tag, config)) return false
|
||||
if (!can_add_tag(tag, config)) return false
|
||||
|
||||
switch(typeof(data['dist-tags'][tag])) {
|
||||
case 'string':
|
||||
data['dist-tags'][tag] = [data['dist-tags'][tag]]
|
||||
break
|
||||
case 'object': // array
|
||||
break
|
||||
default:
|
||||
data['dist-tags'][tag] = []
|
||||
}
|
||||
if (data['dist-tags'][tag].indexOf(version) === -1) {
|
||||
data['dist-tags'][tag].push(version)
|
||||
data['dist-tags'][tag] = module.exports.semver_sort(data['dist-tags'][tag])
|
||||
return data['dist-tags'][tag][data['dist-tags'][tag].length - 1] === version
|
||||
}
|
||||
return false
|
||||
switch (typeof(data['dist-tags'][tag])) {
|
||||
case 'string':
|
||||
data['dist-tags'][tag] = [ data['dist-tags'][tag] ]
|
||||
break
|
||||
case 'object': // array
|
||||
break
|
||||
default:
|
||||
data['dist-tags'][tag] = []
|
||||
}
|
||||
if (data['dist-tags'][tag].indexOf(version) === -1) {
|
||||
data['dist-tags'][tag].push(version)
|
||||
data['dist-tags'][tag] = module.exports.semver_sort(data['dist-tags'][tag])
|
||||
return data['dist-tags'][tag][data['dist-tags'][tag].length - 1] === version
|
||||
}
|
||||
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) {
|
||||
if (version.compare(semver.parse(k, true)) === 0) {
|
||||
return object.versions[k]
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
return undefined
|
||||
}
|
||||
try {
|
||||
version = Semver.parse(version, true)
|
||||
for (var k in object.versions) {
|
||||
if (version.compare(Semver.parse(k, true)) === 0) {
|
||||
return object.versions[k]
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
return true
|
||||
})
|
||||
.sort(semver.compareLoose)
|
||||
.map(String)
|
||||
return array
|
||||
.filter(function(x) {
|
||||
if (!Semver.parse(x, true)) {
|
||||
Logger.logger.warn( {ver: x}, 'ignoring bad version @{ver}' )
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
.sort(Semver.compareLoose)
|
||||
.map(String)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,58 +1,58 @@
|
|||
var 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 server = process.server
|
||||
|
||||
it('add tag - 404', function(cb) {
|
||||
server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('add tag - 404', function(cb) {
|
||||
server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('addtag', function() {
|
||||
before(function(cb) {
|
||||
server.put_package('testpkg-tag', eval(
|
||||
'(' + readfile('fixtures/publish.json5')
|
||||
.toString('utf8')
|
||||
.replace(/__NAME__/g, 'testpkg-tag')
|
||||
.replace(/__VERSION__/g, '0.0.1')
|
||||
+ ')'
|
||||
), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('addtag', function() {
|
||||
before(function(cb) {
|
||||
server.put_package('testpkg-tag', eval(
|
||||
'(' + readfile('fixtures/publish.json5')
|
||||
.toString('utf8')
|
||||
.replace(/__NAME__/g, 'testpkg-tag')
|
||||
.replace(/__VERSION__/g, '0.0.1')
|
||||
+ ')'
|
||||
), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('add testpkg-tag', function(){})
|
||||
it('add testpkg-tag', function(){})
|
||||
|
||||
it('add tag - bad ver', function(cb) {
|
||||
server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1-x', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('version doesn\'t exist'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('add tag - bad ver', function(cb) {
|
||||
server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1-x', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('version doesn\'t exist'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('add tag - bad tag', function(cb) {
|
||||
server.add_tag('testpkg-tag', 'tag/tag/tag', '0.0.1-x', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(~body.error.indexOf('invalid tag'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('add tag - bad tag', function(cb) {
|
||||
server.add_tag('testpkg-tag', 'tag/tag/tag', '0.0.1-x', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(~body.error.indexOf('invalid tag'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('add tag - good', function(cb) {
|
||||
server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1', function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('tagged'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
it('add tag - good', function(cb) {
|
||||
server.add_tag('testpkg-tag', 'tagtagtag', '0.0.1', function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('tagged'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,35 +2,35 @@ var assert = require('assert')
|
|||
var Server = require('./lib/server')
|
||||
|
||||
module.exports = function() {
|
||||
var server = new Server('http://localhost:55551/')
|
||||
var server = new Server('http://localhost:55551/')
|
||||
|
||||
describe('adduser', function() {
|
||||
var user = String(Math.random())
|
||||
var pass = String(Math.random())
|
||||
before(function(cb) {
|
||||
server.auth(user, pass, function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok.match(/user .* created/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('adduser', function() {
|
||||
var user = String(Math.random())
|
||||
var pass = String(Math.random())
|
||||
before(function(cb) {
|
||||
server.auth(user, pass, function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok.match(/user .* created/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('creating new user', function(){})
|
||||
it('creating new user', function(){})
|
||||
|
||||
it('should log in', function(cb) {
|
||||
server.auth(user, pass, function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok.match(/you are authenticated as/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('should log in', function(cb) {
|
||||
server.auth(user, pass, function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok.match(/you are authenticated as/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('should not register more users', function(cb) {
|
||||
server.auth(String(Math.random()), String(Math.random()), function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/maximum amount of users reached/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
it('should not register more users', function(cb) {
|
||||
server.auth(String(Math.random()), String(Math.random()), function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/maximum amount of users reached/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,133 +1,133 @@
|
|||
require('./lib/startup')
|
||||
|
||||
var assert = require('assert')
|
||||
, async = require('async')
|
||||
, crypto = require('crypto')
|
||||
var async = require('async')
|
||||
var 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
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
|
||||
it('trying to fetch non-existent package', function(cb) {
|
||||
server.get_package('testpkg', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('trying to fetch non-existent package', function(cb) {
|
||||
server.get_package('testpkg', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('testpkg', function() {
|
||||
before(server.add_package.bind(server, 'testpkg'))
|
||||
describe('testpkg', function() {
|
||||
before(server.add_package.bind(server, 'testpkg'))
|
||||
|
||||
it('creating new package', function(){/* test for before() */})
|
||||
it('creating new package', function(){/* test for before() */})
|
||||
|
||||
it('downloading non-existent tarball', function(cb) {
|
||||
server.get_tarball('testpkg', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such file'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('downloading non-existent tarball', function(cb) {
|
||||
server.get_tarball('testpkg', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such file'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('uploading incomplete tarball', function(cb) {
|
||||
server.put_tarball_incomplete('testpkg', 'blahblah1', readfile('fixtures/binary'), 3000, function(res, body) {
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('uploading incomplete tarball', function(cb) {
|
||||
server.put_tarball_incomplete('testpkg', 'blahblah1', readfile('fixtures/binary'), 3000, function(res, body) {
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('tarball', function() {
|
||||
before(function(cb) {
|
||||
server.put_tarball('testpkg', 'blahblah', readfile('fixtures/binary'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('tarball', function() {
|
||||
before(function(cb) {
|
||||
server.put_tarball('testpkg', 'blahblah', readfile('fixtures/binary'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('uploading new tarball', function(){/* test for before() */})
|
||||
it('uploading new tarball', function(){/* test for before() */})
|
||||
|
||||
it('downloading newly created tarball', function(cb) {
|
||||
server.get_tarball('testpkg', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.deepEqual(body, readfile('fixtures/binary').toString('utf8'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('downloading newly created tarball', function(cb) {
|
||||
server.get_tarball('testpkg', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.deepEqual(body, readfile('fixtures/binary').toString('utf8'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('uploading new package version (bad sha)', function(cb) {
|
||||
var pkg = require('./lib/package')('testpkg')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update('fake').digest('hex')
|
||||
server.put_version('testpkg', '0.0.1', pkg, function(res, body) {
|
||||
assert.equal(res.statusCode, 400)
|
||||
assert(~body.error.indexOf('shasum error'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('uploading new package version (bad sha)', function(cb) {
|
||||
var pkg = require('./lib/package')('testpkg')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update('fake').digest('hex')
|
||||
server.put_version('testpkg', '0.0.1', pkg, function(res, body) {
|
||||
assert.equal(res.statusCode, 400)
|
||||
assert(~body.error.indexOf('shasum error'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('version', function() {
|
||||
before(function(cb) {
|
||||
var pkg = require('./lib/package')('testpkg')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex')
|
||||
server.put_version('testpkg', '0.0.1', pkg, function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('published'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('version', function() {
|
||||
before(function(cb) {
|
||||
var pkg = require('./lib/package')('testpkg')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex')
|
||||
server.put_version('testpkg', '0.0.1', pkg, function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('published'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('uploading new package version', function(){/* test for before() */})
|
||||
it('uploading new package version', function(){/* test for before() */})
|
||||
|
||||
it('downloading newly created package', function(cb) {
|
||||
server.get_package('testpkg', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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'})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('downloading newly created package', function(cb) {
|
||||
server.get_package('testpkg', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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'})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('downloading package via server2', function(cb) {
|
||||
server2.get_package('testpkg', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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'})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
it('downloading package via server2', function(cb) {
|
||||
server2.get_package('testpkg', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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'})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('uploading new package version for bad pkg', function(cb) {
|
||||
server.put_version('testpxg', '0.0.1', require('./lib/package')('testpxg'), function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('uploading new package version for bad pkg', function(cb) {
|
||||
server.put_version('testpxg', '0.0.1', require('./lib/package')('testpxg'), function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('doubleerr test', function(cb) {
|
||||
server.put_tarball('testfwd2', 'blahblah', readfile('fixtures/binary'), function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(body.error)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('doubleerr test', function(cb) {
|
||||
server.put_tarball('testfwd2', 'blahblah', readfile('fixtures/binary'), function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(body.error)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('publishing package / bad ro uplink', function(cb) {
|
||||
server.put_package('baduplink', require('./lib/package')('baduplink'), function(res, body) {
|
||||
assert.equal(res.statusCode, 503)
|
||||
assert(~body.error.indexOf('one of the uplinks is down, refuse to publish'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('publishing package / bad ro uplink', function(cb) {
|
||||
server.put_package('baduplink', require('./lib/package')('baduplink'), function(res, body) {
|
||||
assert.equal(res.statusCode, 503)
|
||||
assert(~body.error.indexOf('one of the uplinks is down, refuse to publish'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,75 +1,74 @@
|
|||
var assert = require('assert')
|
||||
, crypto = require('crypto')
|
||||
, ex = module.exports
|
||||
var 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
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
|
||||
it('downloading non-existent tarball #1 / srv2', function(cb) {
|
||||
server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('downloading non-existent tarball #1 / srv2', function(cb) {
|
||||
server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('pkg-gh29', function() {
|
||||
before(function(cb) {
|
||||
server.put_package('testpkg-gh29', require('./lib/package')('testpkg-gh29'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('created new package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('pkg-gh29', function() {
|
||||
before(function(cb) {
|
||||
server.put_package('testpkg-gh29', require('./lib/package')('testpkg-gh29'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('created new package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('creating new package / srv1', function(){})
|
||||
it('creating new package / srv1', function(){})
|
||||
|
||||
it('downloading non-existent tarball #2 / srv2', function(cb) {
|
||||
server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such file'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('downloading non-existent tarball #2 / srv2', function(cb) {
|
||||
server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such file'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('tarball', function() {
|
||||
before(function(cb) {
|
||||
server.put_tarball('testpkg-gh29', 'blahblah', readfile('fixtures/binary'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('tarball', function() {
|
||||
before(function(cb) {
|
||||
server.put_tarball('testpkg-gh29', 'blahblah', readfile('fixtures/binary'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('uploading new tarball / srv1', function(){})
|
||||
it('uploading new tarball / srv1', function(){})
|
||||
|
||||
describe('pkg version', function() {
|
||||
before(function(cb) {
|
||||
var pkg = require('./lib/package')('testpkg-gh29')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex')
|
||||
server.put_version('testpkg-gh29', '0.0.1', pkg, function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('published'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('pkg version', function() {
|
||||
before(function(cb) {
|
||||
var pkg = require('./lib/package')('testpkg-gh29')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex')
|
||||
server.put_version('testpkg-gh29', '0.0.1', pkg, function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('published'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('uploading new package version / srv1', function(){})
|
||||
it('uploading new package version / srv1', function(){})
|
||||
|
||||
it('downloading newly created tarball / srv2', function(cb) {
|
||||
server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.deepEqual(body, readfile('fixtures/binary').toString('utf8'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
it('downloading newly created tarball / srv2', function(cb) {
|
||||
server2.get_tarball('testpkg-gh29', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.deepEqual(body, readfile('fixtures/binary').toString('utf8'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,93 +1,93 @@
|
|||
require('./lib/startup')
|
||||
|
||||
var assert = require('assert')
|
||||
, async = require('async')
|
||||
, crypto = require('crypto')
|
||||
var async = require('async')
|
||||
var 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 express = process.express
|
||||
var server = process.server
|
||||
var express = process.express
|
||||
|
||||
describe('testexp_gzip', function() {
|
||||
before(function() {
|
||||
express.get('/testexp_gzip', function(req, res) {
|
||||
var x = eval(
|
||||
'(' + readfile('fixtures/publish.json5')
|
||||
.toString('utf8')
|
||||
.replace(/__NAME__/g, 'testexp_gzip')
|
||||
.replace(/__VERSION__/g, '0.0.1')
|
||||
+ ')'
|
||||
)
|
||||
describe('testexp_gzip', function() {
|
||||
before(function() {
|
||||
express.get('/testexp_gzip', function(req, res) {
|
||||
var 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']
|
||||
// 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']
|
||||
|
||||
require('zlib').gzip(JSON.stringify(x), function(err, buf) {
|
||||
assert(!err)
|
||||
assert.equal(req.headers['accept-encoding'], 'gzip')
|
||||
res.header('content-encoding', 'gzip')
|
||||
res.send(buf)
|
||||
})
|
||||
})
|
||||
require('zlib').gzip(JSON.stringify(x), function(err, buf) {
|
||||
assert(!err)
|
||||
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]))
|
||||
})
|
||||
})
|
||||
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]))
|
||||
})
|
||||
})
|
||||
|
||||
it('should not fail on bad gzip', function(cb) {
|
||||
server.get_package('testexp_baddata', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('should not fail on bad gzip', function(cb) {
|
||||
server.get_package('testexp_baddata', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('should understand gzipped data from uplink', function(cb) {
|
||||
server.get_package('testexp_gzip', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(res.headers['content-encoding'], undefined)
|
||||
assert.equal(body.name, 'testexp_gzip')
|
||||
assert.equal(Object.keys(body.versions).length, 9)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('should understand gzipped data from uplink', function(cb) {
|
||||
server.get_package('testexp_gzip', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(res.headers['content-encoding'], undefined)
|
||||
assert.equal(body.name, 'testexp_gzip')
|
||||
assert.equal(Object.keys(body.versions).length, 9)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('should serve gzipped data', function(cb) {
|
||||
server.request({
|
||||
uri: '/testexp_gzip',
|
||||
encoding: null,
|
||||
headers: {
|
||||
'Accept-encoding': 'gzip',
|
||||
},
|
||||
json: false,
|
||||
}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(res.headers['content-encoding'], 'gzip')
|
||||
assert.throws(function() {
|
||||
JSON.parse(body.toString('utf8'))
|
||||
})
|
||||
require('zlib').gunzip(body, function(err, buf) {
|
||||
assert(!err)
|
||||
body = JSON.parse(buf)
|
||||
assert.equal(body.name, 'testexp_gzip')
|
||||
assert.equal(Object.keys(body.versions).length, 9)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
it('should serve gzipped data', function(cb) {
|
||||
server.request({
|
||||
uri: '/testexp_gzip',
|
||||
encoding: null,
|
||||
headers: {
|
||||
'Accept-encoding': 'gzip',
|
||||
},
|
||||
json: false,
|
||||
}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(res.headers['content-encoding'], 'gzip')
|
||||
assert.throws(function() {
|
||||
JSON.parse(body.toString('utf8'))
|
||||
})
|
||||
require('zlib').gunzip(body, function(err, buf) {
|
||||
assert(!err)
|
||||
body = JSON.parse(buf)
|
||||
assert.equal(body.name, 'testexp_gzip')
|
||||
assert.equal(Object.keys(body.versions).length, 9)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,67 +1,66 @@
|
|||
var assert = require('assert')
|
||||
, ex = module.exports
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var express = process.express
|
||||
var server = process.server
|
||||
var express = process.express
|
||||
|
||||
describe('Incomplete', function() {
|
||||
var on_tarball
|
||||
describe('Incomplete', function() {
|
||||
var on_tarball
|
||||
|
||||
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"
|
||||
}
|
||||
},
|
||||
"0.1.1": {
|
||||
"name": "testexp_tags",
|
||||
"version": "0.1.1",
|
||||
"dist": {
|
||||
"shasum": "fake",
|
||||
"tarball": "http://localhost:55550/testexp-incomplete/-/chunked.tar.gz"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
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"
|
||||
}
|
||||
},
|
||||
"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) {
|
||||
it('should not store tarballs / ' + type, function(_cb) {
|
||||
var 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')
|
||||
setTimeout(function() {
|
||||
res.socket.write('200\nsss\n')
|
||||
res.socket.destroy()
|
||||
cb()
|
||||
}, 10)
|
||||
})
|
||||
;['content-length', 'chunked'].forEach(function(type) {
|
||||
it('should not store tarballs / ' + type, function(_cb) {
|
||||
var 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')
|
||||
setTimeout(function() {
|
||||
res.socket.write('200\nsss\n')
|
||||
res.socket.destroy()
|
||||
cb()
|
||||
}, 10)
|
||||
})
|
||||
|
||||
server.request({uri:'/testexp-incomplete/-/'+type+'.tar.gz'}, function(err, res, body) {
|
||||
if (type !== 'chunked') assert.equal(res.headers['content-length'], 1e6)
|
||||
assert(body.match(/test test test/))
|
||||
})
|
||||
server.request({uri:'/testexp-incomplete/-/'+type+'.tar.gz'}, function(err, res, body) {
|
||||
if (type !== 'chunked') assert.equal(res.headers['content-length'], 1e6)
|
||||
assert(body.match(/test test test/))
|
||||
})
|
||||
|
||||
function cb() {
|
||||
server.request({uri:'/testexp-incomplete/-/'+type+'.tar.gz'}, function(err, res, body) {
|
||||
assert.equal(body.error, 'internal server error')
|
||||
_cb()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
function cb() {
|
||||
server.request({uri:'/testexp-incomplete/-/'+type+'.tar.gz'}, function(err, res, body) {
|
||||
assert.equal(body.error, 'internal server error')
|
||||
_cb()
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,80 +1,79 @@
|
|||
require('./lib/startup')
|
||||
|
||||
var assert = require('assert')
|
||||
, async = require('async')
|
||||
, crypto = require('crypto')
|
||||
, exec = require('child_process').exec
|
||||
, ex = module.exports
|
||||
var async = require('async')
|
||||
var exec = require('child_process').exec
|
||||
var crypto = require('crypto')
|
||||
|
||||
function readfile(x) {
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
return require('fs').readFileSync(__dirname + '/' + x)
|
||||
}
|
||||
|
||||
describe('Func', function() {
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
var server = process.server
|
||||
var 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(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(cb) {
|
||||
async.map([server, server2], function(server, cb) {
|
||||
server.debug(function(res, body) {
|
||||
server.pid = body.pid
|
||||
exec('lsof -p ' + Number(server.pid), function(err, result) {
|
||||
server.fdlist = result
|
||||
cb()
|
||||
})
|
||||
})
|
||||
}, cb)
|
||||
})
|
||||
before(function(cb) {
|
||||
async.map([server, server2], function(server, cb) {
|
||||
server.debug(function(res, body) {
|
||||
server.pid = body.pid
|
||||
exec('lsof -p ' + Number(server.pid), function(err, result) {
|
||||
server.fdlist = result
|
||||
cb()
|
||||
})
|
||||
})
|
||||
}, cb)
|
||||
})
|
||||
|
||||
before(function auth(cb) {
|
||||
async.map([server, server2], function(server, cb) {
|
||||
server.auth('test', 'test', function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert.notEqual(body.ok.indexOf('"test"'), -1)
|
||||
cb()
|
||||
})
|
||||
}, cb)
|
||||
})
|
||||
before(function auth(cb) {
|
||||
async.map([server, server2], function(server, cb) {
|
||||
server.auth('test', 'test', function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert.notEqual(body.ok.indexOf('"test"'), -1)
|
||||
cb()
|
||||
})
|
||||
}, cb)
|
||||
})
|
||||
|
||||
it('authenticate', function(){/* test for before() */})
|
||||
it('authenticate', function(){/* test for before() */})
|
||||
|
||||
require('./basic')()
|
||||
require('./gh29')()
|
||||
require('./tags')()
|
||||
require('./gzip')()
|
||||
require('./incomplete')()
|
||||
require('./mirror')()
|
||||
require('./newnpmreg')()
|
||||
require('./nullstorage')()
|
||||
require('./race')()
|
||||
require('./racycrash')()
|
||||
require('./security')()
|
||||
require('./adduser')()
|
||||
require('./addtag')()
|
||||
require('./basic')()
|
||||
require('./gh29')()
|
||||
require('./tags')()
|
||||
require('./gzip')()
|
||||
require('./incomplete')()
|
||||
require('./mirror')()
|
||||
require('./newnpmreg')()
|
||||
require('./nullstorage')()
|
||||
require('./race')()
|
||||
require('./racycrash')()
|
||||
require('./security')()
|
||||
require('./adduser')()
|
||||
require('./addtag')()
|
||||
|
||||
after(function(cb) {
|
||||
async.map([server, server2], function(server, cb) {
|
||||
exec('lsof -p ' + Number(server.pid), function(err, result) {
|
||||
assert.equal(server.fdlist, 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'))
|
||||
cb()
|
||||
})
|
||||
}, cb)
|
||||
})
|
||||
after(function(cb) {
|
||||
async.map([server, server2], function(server, cb) {
|
||||
exec('lsof -p ' + Number(server.pid), function(err, result) {
|
||||
assert.equal(server.fdlist, 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'))
|
||||
cb()
|
||||
})
|
||||
}, cb)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
|
||||
module.exports = function(name, version) {
|
||||
return {
|
||||
"name": name,
|
||||
"version": version || "0.0.0",
|
||||
"dist": {
|
||||
"shasum": "fake",
|
||||
"tarball": "http://localhost:55551/"+escape(name)+"/-/blahblah"
|
||||
}
|
||||
}
|
||||
return {
|
||||
"name": name,
|
||||
"version": version || "0.0.0",
|
||||
"dist": {
|
||||
"shasum": "fake",
|
||||
"tarball": "http://localhost:55551/"+escape(name)+"/-/blahblah"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,143 +1,144 @@
|
|||
var assert = require('assert')
|
||||
var request = require('request')
|
||||
, assert = require('assert')
|
||||
|
||||
function Server(url) {
|
||||
if (!(this instanceof Server)) return new Server(url)
|
||||
this.url = url.replace(/\/$/, '')
|
||||
this.userAgent = 'node/v0.10.8 linux x64'
|
||||
this.authstr = 'Basic '+(new Buffer('test:test')).toString('base64')
|
||||
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
|
||||
}
|
||||
|
||||
function prep(cb) {
|
||||
return function(err, res, body) {
|
||||
if (err) throw err
|
||||
cb(res, body)
|
||||
}
|
||||
return function(err, res, body) {
|
||||
if (err) throw err
|
||||
cb(res, body)
|
||||
}
|
||||
}
|
||||
|
||||
Server.prototype.request = function(options, cb) {
|
||||
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
|
||||
return request({
|
||||
url: this.url + options.uri,
|
||||
method: options.method || 'GET',
|
||||
headers: headers,
|
||||
encoding: options.encoding,
|
||||
json: options.json != null ? options.json : true,
|
||||
}, cb)
|
||||
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
|
||||
return request({
|
||||
url: this.url + options.uri,
|
||||
method: options.method || 'GET',
|
||||
headers: headers,
|
||||
encoding: options.encoding,
|
||||
json: options.json != null ? options.json : true,
|
||||
}, cb)
|
||||
}
|
||||
|
||||
Server.prototype.auth = function(user, pass, cb) {
|
||||
this.authstr = 'Basic '+(new Buffer(user+':'+pass)).toString('base64')
|
||||
this.request({
|
||||
uri: '/-/user/org.couchdb.user:'+encodeURIComponent(user)+'/-rev/undefined',
|
||||
method: 'PUT',
|
||||
json: {
|
||||
name: user,
|
||||
password: pass,
|
||||
email: 'test@example.com',
|
||||
_id: 'org.couchdb.user:' + user,
|
||||
type: 'user',
|
||||
roles: [],
|
||||
date: new Date(),
|
||||
}
|
||||
}, prep(cb))
|
||||
this.authstr = 'Basic '+(Buffer(user+':'+pass)).toString('base64')
|
||||
this.request({
|
||||
uri: '/-/user/org.couchdb.user:'+encodeURIComponent(user)+'/-rev/undefined',
|
||||
method: 'PUT',
|
||||
json: {
|
||||
name: user,
|
||||
password: pass,
|
||||
email: 'test@example.com',
|
||||
_id: 'org.couchdb.user:' + user,
|
||||
type: 'user',
|
||||
roles: [],
|
||||
date: new Date(),
|
||||
}
|
||||
}, prep(cb))
|
||||
}
|
||||
|
||||
Server.prototype.get_package = function(name, cb) {
|
||||
this.request({
|
||||
uri: '/'+name,
|
||||
method: 'GET',
|
||||
}, prep(cb))
|
||||
this.request({
|
||||
uri: '/'+name,
|
||||
method: 'GET',
|
||||
}, prep(cb))
|
||||
}
|
||||
|
||||
Server.prototype.put_package = function(name, data, cb) {
|
||||
if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data)
|
||||
this.request({
|
||||
uri: '/'+encodeURIComponent(name),
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}, prep(cb)).end(data)
|
||||
if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data)
|
||||
this.request({
|
||||
uri: '/'+encodeURIComponent(name),
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}, prep(cb)).end(data)
|
||||
}
|
||||
|
||||
Server.prototype.put_version = function(name, version, data, cb) {
|
||||
if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data)
|
||||
this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(version)+'/-tag/latest',
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}, prep(cb)).end(data)
|
||||
if (typeof(data) === 'object' && !Buffer.isBuffer(data)) data = JSON.stringify(data)
|
||||
this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(version)+'/-tag/latest',
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}, prep(cb)).end(data)
|
||||
}
|
||||
|
||||
Server.prototype.get_tarball = function(name, filename, cb) {
|
||||
this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename),
|
||||
method: 'GET',
|
||||
}, prep(cb))
|
||||
this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename),
|
||||
method: 'GET',
|
||||
}, prep(cb))
|
||||
}
|
||||
|
||||
Server.prototype.put_tarball = function(name, filename, data, cb) {
|
||||
this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever',
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/octet-stream'
|
||||
},
|
||||
}, prep(cb)).end(data)
|
||||
this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever',
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/octet-stream'
|
||||
},
|
||||
}, prep(cb)).end(data)
|
||||
}
|
||||
|
||||
Server.prototype.add_tag = function(name, tag, version, cb) {
|
||||
this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(tag),
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}, prep(cb)).end(JSON.stringify(version))
|
||||
this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/'+encodeURIComponent(tag),
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}, prep(cb)).end(JSON.stringify(version))
|
||||
}
|
||||
|
||||
Server.prototype.put_tarball_incomplete = function(name, filename, data, size, cb) {
|
||||
var req = this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever',
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/octet-stream',
|
||||
'content-length': size,
|
||||
},
|
||||
timeout: 1000,
|
||||
}, function(err) {
|
||||
assert(err)
|
||||
cb()
|
||||
})
|
||||
req.write(data)
|
||||
setTimeout(function() {
|
||||
req.req.abort()
|
||||
}, 20)
|
||||
var req = this.request({
|
||||
uri: '/'+encodeURIComponent(name)+'/-/'+encodeURIComponent(filename)+'/whatever',
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'content-type': 'application/octet-stream',
|
||||
'content-length': size,
|
||||
},
|
||||
timeout: 1000,
|
||||
}, function(err) {
|
||||
assert(err)
|
||||
cb()
|
||||
})
|
||||
req.write(data)
|
||||
setTimeout(function() {
|
||||
req.req.abort()
|
||||
}, 20)
|
||||
}
|
||||
|
||||
Server.prototype.add_package = function(name, cb) {
|
||||
this.put_package(name, require('./package')(name), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('created new package'))
|
||||
cb()
|
||||
})
|
||||
this.put_package(name, require('./package')(name), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('created new package'))
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
Server.prototype.debug = function(cb) {
|
||||
this.request({
|
||||
uri: '/-/_debug',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}, prep(cb))
|
||||
this.request({
|
||||
uri: '/-/_debug',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
}, prep(cb))
|
||||
}
|
||||
|
||||
module.exports = Server
|
||||
|
|
|
@ -1,44 +1,43 @@
|
|||
var rimraf = require('rimraf')
|
||||
, fork = require('child_process').fork
|
||||
, assert = require('assert')
|
||||
, express = require('express')
|
||||
, readfile = require('fs').readFileSync
|
||||
, Server = require('./server')
|
||||
var assert = require('assert')
|
||||
var fork = require('child_process').fork
|
||||
var express = require('express')
|
||||
var readfile = require('fs').readFileSync
|
||||
var rimraf = require('rimraf')
|
||||
var Server = require('./server')
|
||||
|
||||
var forks = process.forks = []
|
||||
process.server = new Server('http://localhost:55551/')
|
||||
process.server2 = new Server('http://localhost:55552/')
|
||||
process.server = Server('http://localhost:55551/')
|
||||
process.server2 = Server('http://localhost:55552/')
|
||||
process.express = express()
|
||||
process.express.listen(55550)
|
||||
|
||||
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'
|
||||
})
|
||||
rimraf(__dirname + '/../' + dir, function() {
|
||||
// filter out --debug-brk
|
||||
var oldArgv = process.execArgv
|
||||
process.execArgv = process.execArgv.filter(function(x) {
|
||||
return x !== '--debug-brk'
|
||||
})
|
||||
|
||||
var f = fork(__dirname + '/../../../bin/sinopia'
|
||||
, ['-c', __dirname + '/../' + conf]
|
||||
, {silent: !process.env.TRAVIS}
|
||||
)
|
||||
forks.push(f)
|
||||
f.on('message', function(msg) {
|
||||
if ('sinopia_started' in msg) {
|
||||
cb()
|
||||
cb = function(){}
|
||||
}
|
||||
})
|
||||
f.on('error', function(err) {
|
||||
throw err
|
||||
})
|
||||
process.execArgv = oldArgv
|
||||
})
|
||||
var f = fork(__dirname + '/../../../bin/sinopia'
|
||||
, ['-c', __dirname + '/../' + conf]
|
||||
, {silent: !process.env.TRAVIS}
|
||||
)
|
||||
forks.push(f)
|
||||
f.on('message', function(msg) {
|
||||
if ('sinopia_started' in msg) {
|
||||
cb(), cb = function(){}
|
||||
}
|
||||
})
|
||||
f.on('error', function(err) {
|
||||
throw 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,75 +1,74 @@
|
|||
var assert = require('assert')
|
||||
, ex = module.exports
|
||||
|
||||
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
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
|
||||
it('testing anti-loop', function(cb) {
|
||||
server2.get_package('testloop', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('testing anti-loop', function(cb) {
|
||||
server2.get_package('testloop', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
;['fwd', /*'loop'*/].forEach(function(pkg) {
|
||||
var prefix = pkg + ': '
|
||||
pkg = 'test' + pkg
|
||||
;['fwd', /*'loop'*/].forEach(function(pkg) {
|
||||
var prefix = pkg + ': '
|
||||
pkg = 'test' + pkg
|
||||
|
||||
describe(pkg, function() {
|
||||
before(function(cb) {
|
||||
server.put_package(pkg, require('./lib/package')(pkg), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('created new package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe(pkg, function() {
|
||||
before(function(cb) {
|
||||
server.put_package(pkg, require('./lib/package')(pkg), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('created new package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it(prefix+'creating new package', function(){})
|
||||
it(prefix+'creating new package', function(){})
|
||||
|
||||
describe(pkg, function() {
|
||||
before(function(cb) {
|
||||
server.put_version(pkg, '0.1.1', require('./lib/package')(pkg), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('published'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe(pkg, function() {
|
||||
before(function(cb) {
|
||||
server.put_version(pkg, '0.1.1', require('./lib/package')(pkg), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('published'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it(prefix+'uploading new package version', function(){})
|
||||
it(prefix+'uploading new package version', function(){})
|
||||
|
||||
it(prefix+'uploading incomplete tarball', function(cb) {
|
||||
server.put_tarball_incomplete(pkg, pkg+'.bad', readfile('fixtures/binary'), 3000, function(res, body) {
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it(prefix+'uploading incomplete tarball', function(cb) {
|
||||
server.put_tarball_incomplete(pkg, pkg+'.bad', readfile('fixtures/binary'), 3000, function(res, body) {
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('tarball', function() {
|
||||
before(function(cb) {
|
||||
server.put_tarball(pkg, pkg+'.file', readfile('fixtures/binary'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('tarball', function() {
|
||||
before(function(cb) {
|
||||
server.put_tarball(pkg, pkg+'.file', readfile('fixtures/binary'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it(prefix+'uploading new tarball', function(){})
|
||||
it(prefix+'uploading new tarball', function(){})
|
||||
|
||||
it(prefix+'downloading tarball from server1', function(cb) {
|
||||
server.get_tarball(pkg, pkg+'.file', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.deepEqual(body, readfile('fixtures/binary').toString('utf8'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
it(prefix+'downloading tarball from server1', function(cb) {
|
||||
server.get_tarball(pkg, pkg+'.file', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.deepEqual(body, readfile('fixtures/binary').toString('utf8'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,132 +1,132 @@
|
|||
var 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
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
var express = process.express
|
||||
|
||||
describe('newnpmreg', function() {
|
||||
before(function(cb) {
|
||||
server.request({
|
||||
uri: '/testpkg-newnpmreg',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'PUT',
|
||||
json: JSON.parse(readfile('fixtures/newnpmreg.json')),
|
||||
}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('newnpmreg', function() {
|
||||
before(function(cb) {
|
||||
server.request({
|
||||
uri: '/testpkg-newnpmreg',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'PUT',
|
||||
json: JSON.parse(readfile('fixtures/newnpmreg.json')),
|
||||
}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('add pkg', function(){})
|
||||
it('add pkg', function(){})
|
||||
|
||||
it('server1 - tarball', function(cb) {
|
||||
server.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
// not real sha due to utf8 conversion
|
||||
assert.strictEqual(sha(body), '789ca61e3426ce55c4983451b58e62b04abceaf6')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('server1 - tarball', function(cb) {
|
||||
server.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
// not real sha due to utf8 conversion
|
||||
assert.strictEqual(sha(body), '789ca61e3426ce55c4983451b58e62b04abceaf6')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('server2 - tarball', function(cb) {
|
||||
server2.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
// not real sha due to utf8 conversion
|
||||
assert.strictEqual(sha(body), '789ca61e3426ce55c4983451b58e62b04abceaf6')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('server2 - tarball', function(cb) {
|
||||
server2.get_tarball('testpkg-newnpmreg', 'testpkg-newnpmreg-0.0.0.tgz', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
// not real sha due to utf8 conversion
|
||||
assert.strictEqual(sha(body), '789ca61e3426ce55c4983451b58e62b04abceaf6')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('server1 - package', function(cb) {
|
||||
server.get_package('testpkg-newnpmreg', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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'})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('server1 - package', function(cb) {
|
||||
server.get_package('testpkg-newnpmreg', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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'})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('server2 - package', function(cb) {
|
||||
server2.get_package('testpkg-newnpmreg', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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'})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('server2 - package', function(cb) {
|
||||
server2.get_package('testpkg-newnpmreg', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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'})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('server1 - readme', function(cb) {
|
||||
server.request({uri:'/-/readme/testpkg-newnpmreg'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(body, '<p>blah blah blah</p>\n')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('server1 - readme', function(cb) {
|
||||
server.request({uri:'/-/readme/testpkg-newnpmreg'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(body, '<p>blah blah blah</p>\n')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('server2 - readme', function(cb) {
|
||||
server2.request({uri:'/-/readme/testpkg-newnpmreg'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(body, '<p>blah blah blah</p>\n')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('server2 - readme', function(cb) {
|
||||
server2.request({uri:'/-/readme/testpkg-newnpmreg'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(body, '<p>blah blah blah</p>\n')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('search', function() {
|
||||
function check(obj) {
|
||||
obj.testpkg.time.modified = '2014-10-02T07:07:51.000Z'
|
||||
assert.deepEqual(obj.testpkg, {
|
||||
"name": "testpkg",
|
||||
"dist-tags": {
|
||||
"latest": "0.0.1"
|
||||
},
|
||||
"maintainers": [],
|
||||
"readmeFilename": "",
|
||||
"time": {
|
||||
"modified": "2014-10-02T07:07:51.000Z"
|
||||
},
|
||||
"versions": {
|
||||
"0.0.1": "latest"
|
||||
}
|
||||
})
|
||||
}
|
||||
describe('search', function() {
|
||||
function check(obj) {
|
||||
obj.testpkg.time.modified = '2014-10-02T07:07:51.000Z'
|
||||
assert.deepEqual(obj.testpkg, {
|
||||
"name": "testpkg",
|
||||
"dist-tags": {
|
||||
"latest": "0.0.1"
|
||||
},
|
||||
"maintainers": [],
|
||||
"readmeFilename": "",
|
||||
"time": {
|
||||
"modified": "2014-10-02T07:07:51.000Z"
|
||||
},
|
||||
"versions": {
|
||||
"0.0.1": "latest"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
before(function(cb) {
|
||||
express.get('/-/all', function(req, res) {
|
||||
res.send({})
|
||||
})
|
||||
cb()
|
||||
})
|
||||
before(function(cb) {
|
||||
express.get('/-/all', function(req, res) {
|
||||
res.send({})
|
||||
})
|
||||
cb()
|
||||
})
|
||||
|
||||
it('server1 - search', function(cb) {
|
||||
server.request({uri:'/-/all'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
check(body)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('server1 - search', function(cb) {
|
||||
server.request({uri:'/-/all'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
check(body)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('server2 - search', function(cb) {
|
||||
server2.request({uri:'/-/all'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
check(body)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
it('server2 - search', function(cb) {
|
||||
server2.request({uri:'/-/all'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
check(body)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,78 +1,78 @@
|
|||
require('./lib/startup')
|
||||
|
||||
var assert = require('assert')
|
||||
, async = require('async')
|
||||
, crypto = require('crypto')
|
||||
var async = require('async')
|
||||
var 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
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
|
||||
it('trying to fetch non-existent package / null storage', function(cb) {
|
||||
server.get_package('test-nullstorage-nonexist', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('trying to fetch non-existent package / null storage', function(cb) {
|
||||
server.get_package('test-nullstorage-nonexist', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('test-nullstorage on server2', function() {
|
||||
before(server2.add_package.bind(server2, 'test-nullstorage2'))
|
||||
describe('test-nullstorage on server2', function() {
|
||||
before(server2.add_package.bind(server2, '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(cb) {
|
||||
server.get_tarball('test-nullstorage2', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such file'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('downloading non-existent tarball', function(cb) {
|
||||
server.get_tarball('test-nullstorage2', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such file'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('tarball', function() {
|
||||
before(function(cb) {
|
||||
server2.put_tarball('test-nullstorage2', 'blahblah', readfile('fixtures/binary'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('tarball', function() {
|
||||
before(function(cb) {
|
||||
server2.put_tarball('test-nullstorage2', 'blahblah', readfile('fixtures/binary'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(body.ok)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
before(function(cb) {
|
||||
var pkg = require('./lib/package')('test-nullstorage2')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex')
|
||||
server2.put_version('test-nullstorage2', '0.0.1', pkg, function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('published'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
before(function(cb) {
|
||||
var pkg = require('./lib/package')('test-nullstorage2')
|
||||
pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex')
|
||||
server2.put_version('test-nullstorage2', '0.0.1', pkg, function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('published'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('uploading new tarball', function(){/* test for before() */})
|
||||
it('uploading new tarball', function(){/* test for before() */})
|
||||
|
||||
it('downloading newly created tarball', function(cb) {
|
||||
server.get_tarball('test-nullstorage2', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.deepEqual(body, readfile('fixtures/binary').toString('utf8'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('downloading newly created tarball', function(cb) {
|
||||
server.get_tarball('test-nullstorage2', 'blahblah', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.deepEqual(body, readfile('fixtures/binary').toString('utf8'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('downloading newly created package', function(cb) {
|
||||
server.get_package('test-nullstorage2', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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'})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
it('downloading newly created package', function(cb) {
|
||||
server.get_package('test-nullstorage2', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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'})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,94 +1,94 @@
|
|||
var assert = require('assert')
|
||||
, readfile = require('fs').readFileSync
|
||||
, ex = module.exports
|
||||
, async = require('async')
|
||||
, _oksum = 0
|
||||
var assert = require('assert')
|
||||
var async = require('async')
|
||||
var readfile = require('fs').readFileSync
|
||||
var ex = module.exports
|
||||
var _oksum = 0
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
|
||||
describe('race', function() {
|
||||
before(function(cb) {
|
||||
server.put_package('race', require('./lib/package')('race'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('created new package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
describe('race', function() {
|
||||
before(function(cb) {
|
||||
server.put_package('race', require('./lib/package')('race'), function(res, body) {
|
||||
assert.equal(res.statusCode, 201)
|
||||
assert(~body.ok.indexOf('created new package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('creating new package', function(){})
|
||||
it('creating new package', function(){})
|
||||
|
||||
it('uploading 10 same versions', function(cb) {
|
||||
var fns = []
|
||||
for (var i=0; i<10; i++) {
|
||||
fns.push(function(cb_) {
|
||||
var data = require('./lib/package')('race')
|
||||
data.rand = Math.random()
|
||||
server.put_version('race', '0.0.1', data, function(res, body) {
|
||||
cb_(null, res, body)
|
||||
})
|
||||
})
|
||||
}
|
||||
it('uploading 10 same versions', function(cb) {
|
||||
var fns = []
|
||||
for (var i=0; i<10; i++) {
|
||||
fns.push(function(cb_) {
|
||||
var data = require('./lib/package')('race')
|
||||
data.rand = Math.random()
|
||||
server.put_version('race', '0.0.1', data, function(res, body) {
|
||||
cb_(null, res, body)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async.parallel(fns, function(err, res) {
|
||||
var okcount = 0
|
||||
, failcount = 0
|
||||
async.parallel(fns, function(err, res) {
|
||||
var okcount = 0
|
||||
, failcount = 0
|
||||
|
||||
res.forEach(function(arr) {
|
||||
var resp = arr[0]
|
||||
, body = arr[1]
|
||||
res.forEach(function(arr) {
|
||||
var resp = arr[0]
|
||||
, 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
|
||||
|
||||
cb()
|
||||
})
|
||||
})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('uploading 10 diff versions', function(cb) {
|
||||
var fns = []
|
||||
for (var i=0; i<10; i++) {
|
||||
;(function(i) {
|
||||
fns.push(function(cb_) {
|
||||
server.put_version('race', '0.1.'+String(i), require('./lib/package')('race'), function(res, body) {
|
||||
cb_(null, res, body)
|
||||
})
|
||||
})
|
||||
})(i)
|
||||
}
|
||||
it('uploading 10 diff versions', function(cb) {
|
||||
var fns = []
|
||||
for (var i=0; i<10; i++) {
|
||||
;(function(i) {
|
||||
fns.push(function(cb_) {
|
||||
server.put_version('race', '0.1.'+String(i), require('./lib/package')('race'), function(res, body) {
|
||||
cb_(null, res, body)
|
||||
})
|
||||
})
|
||||
})(i)
|
||||
}
|
||||
|
||||
async.parallel(fns, function(err, res) {
|
||||
var okcount = 0
|
||||
, failcount = 0
|
||||
async.parallel(fns, function(err, res) {
|
||||
var okcount = 0
|
||||
, failcount = 0
|
||||
|
||||
res.forEach(function(arr) {
|
||||
var resp = arr[0]
|
||||
, 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)
|
||||
_oksum += okcount
|
||||
res.forEach(function(arr) {
|
||||
var resp = arr[0]
|
||||
, 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)
|
||||
_oksum += okcount
|
||||
|
||||
cb()
|
||||
})
|
||||
})
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
// XXX: this should be after anything else, but we can't really ensure that with mocha
|
||||
it('downloading package', function(cb) {
|
||||
server.get_package('race', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(Object.keys(body.versions).length, _oksum)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
// XXX: this should be after anything else, but we can't really ensure that with mocha
|
||||
it('downloading package', function(cb) {
|
||||
server.get_package('race', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(Object.keys(body.versions).length, _oksum)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,69 +1,68 @@
|
|||
var assert = require('assert')
|
||||
, ex = module.exports
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var express = process.express
|
||||
var server = process.server
|
||||
var express = process.express
|
||||
|
||||
describe('Racy', function() {
|
||||
var on_tarball
|
||||
describe('Racy', function() {
|
||||
var 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
express.get('/testexp-racycrash/-/test.tar.gz', function(_, res) {
|
||||
on_tarball(res)
|
||||
})
|
||||
})
|
||||
express.get('/testexp-racycrash/-/test.tar.gz', function(_, 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')
|
||||
setTimeout(function() {
|
||||
res.write('test test test\n')
|
||||
res.socket.destroy()
|
||||
cb()
|
||||
}, 200)
|
||||
}
|
||||
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')
|
||||
setTimeout(function() {
|
||||
res.write('test test test\n')
|
||||
res.socket.destroy()
|
||||
cb()
|
||||
}, 200)
|
||||
}
|
||||
|
||||
server.request({uri:'/testexp-racycrash/-/test.tar.gz'}, function(err, res, body) {
|
||||
assert.equal(body, 'test test test\n')
|
||||
})
|
||||
server.request({uri:'/testexp-racycrash/-/test.tar.gz'}, function(err, res, body) {
|
||||
assert.equal(body, 'test test test\n')
|
||||
})
|
||||
|
||||
function cb() {
|
||||
// test for NOT crashing
|
||||
server.request({uri:'/testexp-racycrash'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
_cb()
|
||||
})
|
||||
}
|
||||
})
|
||||
function cb() {
|
||||
// test for NOT crashing
|
||||
server.request({uri:'/testexp-racycrash'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
_cb()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it('should not store tarball', function(cb) {
|
||||
on_tarball = function(res) {
|
||||
res.socket.destroy()
|
||||
}
|
||||
it('should not store tarball', function(cb) {
|
||||
on_tarball = function(res) {
|
||||
res.socket.destroy()
|
||||
}
|
||||
|
||||
server.request({uri:'/testexp-racycrash/-/test.tar.gz'}, function(err, res, body) {
|
||||
assert.equal(body.error, 'internal server error')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
server.request({uri:'/testexp-racycrash/-/test.tar.gz'}, function(err, res, body) {
|
||||
assert.equal(body.error, 'internal server error')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,88 +1,87 @@
|
|||
var assert = require('assert')
|
||||
, ex = module.exports
|
||||
|
||||
module.exports = function() {
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
var server = process.server
|
||||
var server2 = process.server2
|
||||
|
||||
describe('Security', function() {
|
||||
before(server.add_package.bind(server, 'testpkg-sec'))
|
||||
describe('Security', function() {
|
||||
before(server.add_package.bind(server, 'testpkg-sec'))
|
||||
|
||||
it('bad pkg #1', function(cb) {
|
||||
server.get_package('package.json', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(~body.error.indexOf('invalid package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('bad pkg #1', function(cb) {
|
||||
server.get_package('package.json', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(~body.error.indexOf('invalid package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('bad pkg #2', function(cb) {
|
||||
server.get_package('__proto__', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(~body.error.indexOf('invalid package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('bad pkg #2', function(cb) {
|
||||
server.get_package('__proto__', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(~body.error.indexOf('invalid package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('__proto__, connect stuff', function(cb) {
|
||||
server.request({uri:'/testpkg-sec?__proto__=1'}, function(err, res, body) {
|
||||
// test for NOT outputting stack trace
|
||||
assert(!body || typeof(body) === 'object' || body.indexOf('node_modules') === -1)
|
||||
it('__proto__, connect stuff', function(cb) {
|
||||
server.request({uri:'/testpkg-sec?__proto__=1'}, function(err, res, body) {
|
||||
// test for NOT outputting stack trace
|
||||
assert(!body || typeof(body) === 'object' || body.indexOf('node_modules') === -1)
|
||||
|
||||
// test for NOT crashing
|
||||
server.request({uri:'/testpkg-sec'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
// test for NOT crashing
|
||||
server.request({uri:'/testpkg-sec'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('do not return package.json as an attachment', function(cb) {
|
||||
server.request({uri:'/testpkg-sec/-/package.json'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/invalid filename/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('do not return package.json as an attachment', function(cb) {
|
||||
server.request({uri:'/testpkg-sec/-/package.json'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/invalid filename/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('silly things - reading #1', function(cb) {
|
||||
server.request({uri:'/testpkg-sec/-/../../../../../../../../etc/passwd'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('silly things - reading #1', function(cb) {
|
||||
server.request({uri:'/testpkg-sec/-/../../../../../../../../etc/passwd'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 404)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('silly things - reading #2', function(cb) {
|
||||
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'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/invalid filename/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('silly things - reading #2', function(cb) {
|
||||
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'}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/invalid filename/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('silly things - writing #1', function(cb) {
|
||||
server.put_tarball('testpkg-sec', 'package.json', '{}', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/invalid filename/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('silly things - writing #1', function(cb) {
|
||||
server.put_tarball('testpkg-sec', 'package.json', '{}', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/invalid filename/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('silly things - writing #3', function(cb) {
|
||||
server.put_tarball('testpkg-sec', 'node_modules', '{}', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/invalid filename/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('silly things - writing #3', function(cb) {
|
||||
server.put_tarball('testpkg-sec', 'node_modules', '{}', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/invalid filename/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
it('silly things - writing #4', function(cb) {
|
||||
server.put_tarball('testpkg-sec', '../testpkg.tgz', '{}', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/invalid filename/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
it('silly things - writing #4', function(cb) {
|
||||
server.put_tarball('testpkg-sec', '../testpkg.tgz', '{}', function(res, body) {
|
||||
assert.equal(res.statusCode, 403)
|
||||
assert(body.error.match(/invalid filename/))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,51 +1,50 @@
|
|||
var assert = require('assert')
|
||||
, ex = module.exports
|
||||
|
||||
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
|
||||
var server = process.server
|
||||
var express = process.express
|
||||
|
||||
it('tags - testing for 404', function(cb) {
|
||||
server.get_package('testexp_tags', function(res, body) {
|
||||
// shouldn't exist yet
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('tags - testing for 404', function(cb) {
|
||||
server.get_package('testexp_tags', function(res, body) {
|
||||
// shouldn't exist yet
|
||||
assert.equal(res.statusCode, 404)
|
||||
assert(~body.error.indexOf('no such package'))
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
describe('tags', function() {
|
||||
before(function(cb) {
|
||||
express.get('/testexp_tags', function(req, res) {
|
||||
res.send(JSON.parse(readfile('fixtures/tags.json')))
|
||||
})
|
||||
cb()
|
||||
})
|
||||
describe('tags', function() {
|
||||
before(function(cb) {
|
||||
express.get('/testexp_tags', function(req, res) {
|
||||
res.send(JSON.parse(readfile('fixtures/tags.json')))
|
||||
})
|
||||
cb()
|
||||
})
|
||||
|
||||
it('fetching package again', function(cb) {
|
||||
server.get_package('testexp_tags', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
it('fetching package again', function(cb) {
|
||||
server.get_package('testexp_tags', function(res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
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)
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
;['0.1.1alpha', '0.1.1-alpha', '0000.00001.001-alpha'].forEach(function(ver) {
|
||||
it('fetching '+ver, function(cb) {
|
||||
server.request({uri:'/testexp_tags/'+ver}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(body.version, '0.1.1alpha')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
;['0.1.1alpha', '0.1.1-alpha', '0000.00001.001-alpha'].forEach(function(ver) {
|
||||
it('fetching '+ver, function(cb) {
|
||||
server.request({uri:'/testexp_tags/'+ver}, function(err, res, body) {
|
||||
assert.equal(res.statusCode, 200)
|
||||
assert.equal(body.version, '0.1.1alpha')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
describe('config.yaml', function() {
|
||||
it('should be parseable', function() {
|
||||
var source = require('fs').readFileSync(__dirname + '/../../lib/config_def.yaml', 'utf8')
|
||||
require('js-yaml').safeLoad(source)
|
||||
})
|
||||
it('should be parseable', function() {
|
||||
var source = require('fs').readFileSync(__dirname + '/../../lib/config_def.yaml', 'utf8')
|
||||
require('js-yaml').safeLoad(source)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
var ReadTarball = require('../../lib/streams').ReadTarballStream
|
||||
|
||||
describe('mystreams', function() {
|
||||
it('should delay events', function(cb) {
|
||||
var test = new ReadTarball()
|
||||
test.abort()
|
||||
setTimeout(function() {
|
||||
test.abort = function() {
|
||||
cb()
|
||||
}
|
||||
test.abort = function() {
|
||||
throw new Error('fail')
|
||||
}
|
||||
}, 10)
|
||||
})
|
||||
it('should delay events', function(cb) {
|
||||
var test = new ReadTarball()
|
||||
test.abort()
|
||||
setTimeout(function() {
|
||||
test.abort = function() {
|
||||
cb()
|
||||
}
|
||||
test.abort = function() {
|
||||
throw Error('fail')
|
||||
}
|
||||
}, 10)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,88 +1,88 @@
|
|||
var assert = require('assert')
|
||||
, Storage = require('../../lib/up-storage')
|
||||
var assert = require('assert')
|
||||
var Storage = require('../../lib/up-storage')
|
||||
|
||||
require('../../lib/logger').setup([])
|
||||
|
||||
function setup(host, config, mainconfig) {
|
||||
config.url = host
|
||||
return new Storage(config, mainconfig)
|
||||
config.url = host
|
||||
return 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)
|
||||
})
|
||||
it('should work fine without proxy', function() {
|
||||
var 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')
|
||||
})
|
||||
it('local config should take priority', function() {
|
||||
var 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')
|
||||
})
|
||||
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')
|
||||
})
|
||||
|
||||
it('no_proxy - simple/include', function() {
|
||||
var x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'localhost'})
|
||||
assert.equal(x.proxy, undefined)
|
||||
})
|
||||
it('no_proxy - simple/include', function() {
|
||||
var 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')
|
||||
})
|
||||
it('no_proxy - simple/not', function() {
|
||||
var 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')
|
||||
})
|
||||
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')
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
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)
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
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)
|
||||
})
|
||||
|
||||
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')
|
||||
})
|
||||
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')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
var assert = require('assert')
|
||||
, parse_interval = require('../../lib/config').parse_interval
|
||||
var assert = require('assert')
|
||||
var 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))
|
||||
})
|
||||
} else {
|
||||
assert.strictEqual(parse_interval(str), res)
|
||||
}
|
||||
})
|
||||
}
|
||||
function add_test(str, res) {
|
||||
it('parse ' + str, function() {
|
||||
if (res === null) {
|
||||
assert.throws(function() {
|
||||
console.log(parse_interval(str))
|
||||
})
|
||||
} else {
|
||||
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,53 +1,53 @@
|
|||
var assert = require('assert')
|
||||
, semver_sort = require('../../lib/utils').semver_sort
|
||||
, merge = require('../../lib/storage')._merge_versions
|
||||
var assert = require('assert')
|
||||
var semver_sort = require('../../lib/utils').semver_sort
|
||||
var 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},
|
||||
'dist-tags': {},
|
||||
}
|
||||
merge(x, {versions: {a:2,q:2}})
|
||||
assert.deepEqual(x, {
|
||||
versions: {a:1,b:1,c:1,q:2},
|
||||
'dist-tags': {},
|
||||
})
|
||||
})
|
||||
it('simple', function() {
|
||||
var x = {
|
||||
versions: {a:1,b:1,c:1},
|
||||
'dist-tags': {},
|
||||
}
|
||||
merge(x, {versions: {a:2,q:2}})
|
||||
assert.deepEqual(x, {
|
||||
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'}})
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {q:['1.1.1','2.2.2'],w:['2.2.2','3.3.3'],t:['4.4.4']},
|
||||
})
|
||||
})
|
||||
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'}})
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {q:['1.1.1','2.2.2'],w:['2.2.2','3.3.3'],t:['4.4.4']},
|
||||
})
|
||||
})
|
||||
|
||||
it('dist-tags - sort', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {w:['2.2.2','1.1.1','12.2.2','2.2.2-rc2']},
|
||||
}
|
||||
merge(x, {'dist-tags':{w:'3.3.3'}})
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {w:["1.1.1","2.2.2-rc2","2.2.2","3.3.3","12.2.2"]},
|
||||
})
|
||||
})
|
||||
it('dist-tags - sort', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {w:['2.2.2','1.1.1','12.2.2','2.2.2-rc2']},
|
||||
}
|
||||
merge(x, {'dist-tags':{w:'3.3.3'}})
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {w:["1.1.1","2.2.2-rc2","2.2.2","3.3.3","12.2.2"]},
|
||||
})
|
||||
})
|
||||
|
||||
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',
|
||||
'1.2.3-b',
|
||||
'1.2.3c',
|
||||
'1.2.3' ]
|
||||
)
|
||||
})
|
||||
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',
|
||||
'1.2.3-b',
|
||||
'1.2.3c',
|
||||
'1.2.3' ]
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,55 +1,55 @@
|
|||
var assert = require('assert')
|
||||
, tag_version = require('../../lib/utils').tag_version
|
||||
var assert = require('assert')
|
||||
var tag_version = require('../../lib/utils').tag_version
|
||||
|
||||
require('../../lib/logger').setup([])
|
||||
|
||||
describe('tag_version', function() {
|
||||
it('add new one', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {},
|
||||
}
|
||||
assert(tag_version(x, '1.1.1', 'foo', {}))
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.1']},
|
||||
})
|
||||
})
|
||||
it('add new one', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {},
|
||||
}
|
||||
assert(tag_version(x, '1.1.1', 'foo', {}))
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.1']},
|
||||
})
|
||||
})
|
||||
|
||||
it('add (compat)', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {foo: '1.1.0'},
|
||||
}
|
||||
assert(tag_version(x, '1.1.1', 'foo', {}))
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.0', '1.1.1']},
|
||||
})
|
||||
})
|
||||
it('add (compat)', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {foo: '1.1.0'},
|
||||
}
|
||||
assert(tag_version(x, '1.1.1', 'foo', {}))
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.0', '1.1.1']},
|
||||
})
|
||||
})
|
||||
|
||||
it('add fresh tag', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.0']},
|
||||
}
|
||||
assert(tag_version(x, '1.1.1', 'foo', {}))
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.0', '1.1.1']},
|
||||
})
|
||||
})
|
||||
it('add fresh tag', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.0']},
|
||||
}
|
||||
assert(tag_version(x, '1.1.1', 'foo', {}))
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.0', '1.1.1']},
|
||||
})
|
||||
})
|
||||
|
||||
it('add stale tag', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.2']},
|
||||
}
|
||||
assert(!tag_version(x, '1.1.1', 'foo', {}))
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.1', '1.1.2']},
|
||||
})
|
||||
})
|
||||
it('add stale tag', function() {
|
||||
var x = {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.2']},
|
||||
}
|
||||
assert(!tag_version(x, '1.1.1', 'foo', {}))
|
||||
assert.deepEqual(x, {
|
||||
versions: {},
|
||||
'dist-tags': {foo: ['1.1.1', '1.1.2']},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
var assert = require('assert')
|
||||
, validate = require('../../lib/utils').validate_name
|
||||
var assert = require('assert')
|
||||
var validate = require('../../lib/utils').validate_name
|
||||
|
||||
describe('Validate', function() {
|
||||
it('good ones', function() {
|
||||
assert(validate('sinopia'))
|
||||
assert(validate('some.weird.package-zzz'))
|
||||
assert(validate('old-package@0.1.2.tgz'))
|
||||
})
|
||||
it('good ones', function() {
|
||||
assert( validate('sinopia') )
|
||||
assert( validate('some.weird.package-zzz') )
|
||||
assert( validate('old-package@0.1.2.tgz') )
|
||||
})
|
||||
|
||||
it('uppercase', function() {
|
||||
assert(validate('EVE'))
|
||||
assert(validate('JSONStream'))
|
||||
})
|
||||
it('uppercase', function() {
|
||||
assert( validate('EVE') )
|
||||
assert( validate('JSONStream') )
|
||||
})
|
||||
|
||||
it('no package.json', function() {
|
||||
assert(!validate('package.json'))
|
||||
})
|
||||
it('no package.json', function() {
|
||||
assert( !validate('package.json') )
|
||||
})
|
||||
|
||||
it('no path seps', function() {
|
||||
assert(!validate('some/thing'))
|
||||
assert(!validate('some\\thing'))
|
||||
})
|
||||
|
||||
it('no hidden', function() {
|
||||
assert(!validate('.bin'))
|
||||
})
|
||||
|
||||
it('no reserved', function() {
|
||||
assert(!validate('favicon.ico'))
|
||||
assert(!validate('node_modules'))
|
||||
assert(!validate('__proto__'))
|
||||
})
|
||||
it('no path seps', function() {
|
||||
assert( !validate('some/thing') )
|
||||
assert( !validate('some\\thing') )
|
||||
})
|
||||
|
||||
it('no hidden', function() {
|
||||
assert( !validate('.bin') )
|
||||
})
|
||||
|
||||
it('no reserved', function() {
|
||||
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'))
|
||||
})
|
||||
it('other', function() {
|
||||
assert( !validate('pk g') )
|
||||
assert( !validate('pk\tg') )
|
||||
assert( !validate('pk%20g') )
|
||||
assert( !validate('pk+g') )
|
||||
assert( !validate('pk:g') )
|
||||
})
|
||||
})
|
||||
|
|
|
@ -3,31 +3,31 @@
|
|||
var assert = require('assert')
|
||||
|
||||
describe('index.js app', function() {
|
||||
var source = require('fs').readFileSync(__dirname + '/../../lib/index.js', 'utf8')
|
||||
var source = require('fs').readFileSync(__dirname + '/../../lib/index.js', 'utf8')
|
||||
|
||||
var very_scary_regexp = /\n\s*app\.(\w+)\s*\(\s*(("[^"]*")|('[^']*'))\s*,/g
|
||||
var m
|
||||
var params = {}
|
||||
var very_scary_regexp = /\n\s*app\.(\w+)\s*\(\s*(("[^"]*")|('[^']*'))\s*,/g
|
||||
var m
|
||||
var params = {}
|
||||
|
||||
while ((m = very_scary_regexp.exec(source)) != null) {
|
||||
if (m[1] === 'set') continue
|
||||
while ((m = very_scary_regexp.exec(source)) != null) {
|
||||
if (m[1] === 'set') continue
|
||||
|
||||
var inner = m[2].slice(1, m[2].length-1)
|
||||
var t
|
||||
var inner = m[2].slice(1, m[2].length-1)
|
||||
var t
|
||||
|
||||
inner.split('/').forEach(function(x) {
|
||||
if (m[1] === 'param') {
|
||||
params[x] = 'ok'
|
||||
} else if (t = x.match(/^:([^?:]*)\??$/)) {
|
||||
params[t[1]] = params[t[1]] || m[0].trim()
|
||||
}
|
||||
})
|
||||
}
|
||||
inner.split('/').forEach(function(x) {
|
||||
if (m[1] === 'param') {
|
||||
params[x] = 'ok'
|
||||
} else if (t = x.match(/^:([^?:]*)\??$/)) {
|
||||
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')
|
||||
})
|
||||
})
|
||||
Object.keys(params).forEach(function(param) {
|
||||
it('should validate ":'+param+'"', function() {
|
||||
assert.equal(params[param], 'ok')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue