diff --git a/.eslint.yaml b/.eslint.yaml deleted file mode 100644 index e2760d066..000000000 --- a/.eslint.yaml +++ /dev/null @@ -1,275 +0,0 @@ -env: - node: true - -# -# 0 - disable -# Rules that more harmful than useful, or just buggy. -# -# 1 - warning -# Rules that we didn't encounter yet. You can safely ignore them, -# but I'd like to know any interesting use-cases they forbid. -# -# 2 - error -# Rules that have proven to be useful, please follow them. -# - -rules: - # didn't understand what it does, but it fails a good code - block-scoped-var: 0 - - # fails where newlines are used to format pretty big "if": - # if ( - # name.charAt(0) === "." || - # name.match(/[\/@\s\+%:]/) || - # name !== encodeURIComponent(name) || - # name.toLowerCase() === "node_modules" - # ) { - brace-style: 1 - - # snake_case is more readable, what's up with you guys? - camelcase: 0 - - # if some functions are complex, they are for a good reason, - # ain't worth it - complexity: [0, 10] - - # never saw it, but self is preferred - consistent-this: [1, self] - - # fails good code - curly: [0, multi] - - # fails good code, where this notation is used for consistency: - # something['foo-bar'] = 123 - # something['blahblah'] = 234 - dot-notation: 0 - - # pointless in many cases (like indexOf() == -1), harmful in a few - # cases (when you do want to ignore types), fails good code - eqeqeq: 0 - - # if someone is changing prototype and makes properties enumerable, - # it's their own fault - guard-for-in: 0 - - # if some functions are complex, they are for a good reason, - # ain't worth it - max-depth: [0, 4] - max-nested-callbacks: [0, 2] - - # should it really throw for every long URL? - max-len: [0, 80, 4] - - # that's obvious by just looking at the code, you don't need lint for that - max-params: [0, 3] - - # if some functions are complex, they are for a good reason, - # ain't worth it - max-statements: [0, 10] - - # that one makes sense - new-cap: 2 - - # I'm writing javascript, not some weird reduced version of it - no-bitwise: 0 - - # not working around IE bugs, sorry - no-catch-shadow: 0 - - # see above, IE is useful for downloading other browsers only - no-comma-dangle: 0 - - # good for removing debugging code - no-console: 2 - - # good for removing debugging code - no-debugger: 2 - - # why would anyone need to check against that? - no-else-return: 0 - - # sometimes empty statement contains useful comment - no-empty: 0 - - # stupid rule - # "x == null" is "x === null || x === undefined" - no-eq-null: 0 - - # fails good code, when parens are used for grouping: - # (req && req.headers['via']) ? req.headers['via'] + ', ' : '' - # not everyone remembers priority tables, you know - no-extra-parens: 0 - - # fails defensive semicolons: - # ;['foo', 'bar'].forEach(function(x) {}) - no-extra-semi: 0 - - # fails good code: - # var fs = require('fs'), - # , open = fs.open - no-mixed-requires: [0, false] - - # new Array(12) is used to pre-allocate arrays - no-new-array: 0 - - # fails good code: - # fs.open('/file', 0666, function(){}) - no-octal: 0 - - # fails good code: - # console.log('\033[31m' + str + '\033[39m') - # also fails \0 which is not octal escape - no-octal-escape: 0 - - # I'm writing javascript, not some weird reduced version of it - no-plusplus: 0 - - # fails good code: - # if (a) { - # var x = 'foo' - # } else { - # var x = bar - # } - no-redeclare: 0 - - # sometimes useful, often isn't - # probably worth enforcing - no-shadow: 2 - - no-sync: 2 - - # I'm writing javascript, not some weird reduced version of it - no-ternary: 0 - - # the single most important rule in the entire ruleset - no-undef: 2 - - # it is failing our own underscores - no-underscore-dangle: 0 - - # fails function hoisting - no-unreachable: 0 - - # fails npm-style code, it's good once you get used to it: - # if (typeof(options) === 'function') callback = options, options = {} - no-unused-expressions: 0 - - # fails (function(_err) {}) where named argument is used to show what - # nth function argument means - no-unused-vars: [0, local] - - # fails function hoisting - no-use-before-define: 0 - - # fails foobar( (function(){}).bind(this) ) - # parens are added for readability - no-wrap-func: 0 - - # fails good code: - # var x - # if (something) { - # var y - one-var: 0 - - quote-props: 0 - - # fails situation when different quotes are used to avoid escaping - quotes: [2, single, avoid-escape] - - # http:#blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding - semi: [2, never] - - # fails good code where spaces are used for grouping: - # (x+y * y+z) - space-infix-ops: 0 - - # typeof(something) should have braces to look like a function - # a matter of taste I suppose - space-unary-word-ops: 0 - - # strict mode is just harmful, - # can I have a check to enforce not using it? - strict: 0 - - sort-vars: 0 - no-path-concat: 0 - func-names: 0 - - # how can you set a return code without process.exit? - no-process-exit: 0 - - # both styles are useful - func-style: [0, declaration] - - # fails while(1) {...} - no-constant-condition: 0 - - # fails good code: - # https://github.com/rlidwka/jju/blob/eb52ee72e5f21d48963798f9bda8ac8d68082148/lib/parse.js#L732 - no-ex-assign: 0 - - wrap-iife: [2, inside] - - # doesn't always make sense - consistent-return: 0 - no-sequences: 0 - - # fails defensive semicolons - no-space-before-semi: 0 - - new-parens: 1 - no-alert: 1 - no-array-constructor: 1 - no-caller: 1 - no-cond-assign: 1 - no-control-regex: 1 - no-delete-var: 1 - no-div-regex: 1 - no-dupe-keys: 1 - no-empty-class: 1 - no-empty-label: 1 - no-eval: 1 - no-extend-native: 1 - no-extra-boolean-cast: 1 - no-extra-strict: 1 - no-fallthrough: 1 - no-floating-decimal: 1 - no-func-assign: 1 - no-global-strict: 1 - no-implied-eval: 1 - no-invalid-regexp: 1 - no-iterator: 1 - no-labels: 1 - no-label-var: 1 - no-lone-blocks: 1 - no-loop-func: 1 - no-multi-str: 1 - no-native-reassign: 1 - no-negated-in-lhs: 1 - no-nested-ternary: 1 - no-new: 1 - no-new-func: 1 - no-new-object: 1 - no-new-wrappers: 1 - no-obj-calls: 1 - no-octal: 1 - no-proto: 1 - no-regex-spaces: 1 - no-return-assign: 1 - no-script-url: 1 - no-self-compare: 1 - no-shadow: 1 - no-shadow-restricted-names: 1 - no-spaced-func: 1 - no-sparse-arrays: 1 - no-sync: 1 - no-undef: 1 - no-undef-init: 1 - no-unreachable: 1 - no-with: 1 - no-yoda: 1 - radix: 1 - space-return-throw-case: 1 - use-isnan: 1 - valid-jsdoc: 1 - wrap-regex: 1 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..f7eb78c62 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +node_modules +lib/static diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..c71b2e398 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,44 @@ +# vim: syntax=yaml + +# +# List of very light restrictions designed to prevent obvious errors, +# not impose our own code style upon other contributors. +# +# This is supposed to be used with `eslint --reset` +# +# Created to work with eslint@0.18.0 +# + +env: + node: true + +rules: + # useful to have in node.js, + # if you're sure you don't need to handle error, rename it to "_err" + handle-callback-err: 2 + + # just to make sure we don't forget to remove them when releasing + no-debugger: 2 + + # add "falls through" for those + no-fallthrough: 2 + + # just warnings about whitespace weirdness here + eol-last: 1 + no-irregular-whitespace: 1 + no-mixed-spaces-and-tabs: [1, smart-tabs] + no-trailing-spaces: 1 + + # probably always an error, tell me if it's not + no-new-require: 2 + + # single most important rule here, without it linting won't even + # make any sense + no-undef: 2 + + # in practice, those are always errors + no-unreachable: 2 + + # useful for code clean-up + no-unused-vars: [1, {"vars": "all", "args": "none"}] + diff --git a/.travis.yml b/.travis.yml index 5ab2f3c72..993e5eec8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,8 @@ language: node_js node_js: - '0.10' - '0.12' + - 'iojs-1.0' + - 'iojs-1.6' - 'iojs' sudo: false script: npm install . && npm run test-travis diff --git a/History.md b/History.md index 6f15b3f30..4e295348d 100644 --- a/History.md +++ b/History.md @@ -1,4 +1,14 @@ +29 Mar 2015, version 1.1.0 + +- add a possibility to listen on multiple ports (issue [#172](https://github.com/rlidwka/sinopia/issues/172)) +- added https support (issues [#71](https://github.com/rlidwka/sinopia/issues/71), [#166](https://github.com/rlidwka/sinopia/issues/166)) +- added an option to use a custom template for web UI (issue [#208](https://github.com/rlidwka/sinopia/pull/208)) +- remove "from" and "resolved" fields from shrinkwrap (issue [#204](https://github.com/rlidwka/sinopia/issues/204)) +- fix hanging when rendering readme (issue [#206](https://github.com/rlidwka/sinopia/issues/206)) +- fix logger-related crash when using sinopia as a library +- all requests to uplinks should now have proper headers + 12 Feb 2015, version 1.0.1 - fixed issue with `max_users` option (issue [#184](https://github.com/rlidwka/sinopia/issues/184)) @@ -9,11 +19,12 @@ - switch markdown parser from `remarkable` to `markdown-it` - update `npm-shrinkwrap.json` +- now downloading tarballs from upstream using the same protocol as for metadata (issue [#166](https://github.com/rlidwka/sinopia/issues/166)) 22 Dec 2014, version 1.0.0-beta.2 - fix windows behavior when `$HOME` isn't set (issue [#177](https://github.com/rlidwka/sinopia/issues/177)) -- fix sanitization for highlighted code blocks in readme (issue [render-readme/#1](https://github.com/rlidwka/render-readme/issues/1) +- fix sanitization for highlighted code blocks in readme (issue [render-readme/#1](https://github.com/rlidwka/render-readme/issues/1)) 15 Dec 2014, version 1.0.0-beta diff --git a/README.md b/README.md index bea257472..6cf91dd47 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,6 @@ $ sinopia # npm configuration $ npm set registry http://localhost:4873/ -# if you have any restricted packages, you should add this: -$ npm set always-auth true - # if you use HTTPS, add an appropriate CA information # ("null" means get CA list from OS) $ npm set ca null diff --git a/conf/full.yaml b/conf/full.yaml index acc8fe923..3a5bff577 100644 --- a/conf/full.yaml +++ b/conf/full.yaml @@ -19,6 +19,7 @@ web: title: Sinopia # logo: logo.png + # template: custom.hbs auth: htpasswd: @@ -84,8 +85,22 @@ packages: # if you use nginx with custom path, use this to override links #url_prefix: https://dev.company.local/sinopia/ -# you can specify listen address (or simply a port) -#listen: localhost:4873 +# You can specify listen address (or simply a port). +# If you add multiple values, sinopia will listen on all of them. +# +# Examples: +# +#listen: +# - localhost:4873 # default value +# - http://localhost:4873 # same thing +# - 0.0.0.0:4873 # listen on all addresses (INADDR_ANY) +# - https://example.org:4873 # if you want to use https +# - [::1]:4873 # ipv6 + +# Configure HTTPS, it is required if you use "https" protocol above. +#https: +# key: path/to/server.key +# cert: path/to/server.crt # type: file | stdout | stderr # level: trace | debug | info | http (default) | warn | error | fatal diff --git a/lib/GUI/.eslintrc b/lib/GUI/.eslintrc new file mode 100644 index 000000000..13f1b4940 --- /dev/null +++ b/lib/GUI/.eslintrc @@ -0,0 +1,8 @@ + +env: + node: true + browser: true + +globals: + jQuery: true + diff --git a/lib/GUI/css/styles.less b/lib/GUI/css/styles.less index b70ffedb8..6c99480c3 100644 --- a/lib/GUI/css/styles.less +++ b/lib/GUI/css/styles.less @@ -27,8 +27,8 @@ .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+ypkYZgAACelJREFUeNrsWmts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); + // https://example.org/sinopia/-/static/../../-/logo + background-image: url( ../../-/logo ); background-repeat: no-repeat; background-position: center center; diff --git a/lib/GUI/js/search.js b/lib/GUI/js/search.js index 45218dd51..a089bad2e 100644 --- a/lib/GUI/js/search.js +++ b/lib/GUI/js/search.js @@ -1,13 +1,10 @@ var $ = require('unopinionate').selector var template = require('../entry.hbs') -var onScroll = require('onscroll') $(function() { ;(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') diff --git a/lib/auth.js b/lib/auth.js index 0d16d4a50..8c7d559a7 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -158,7 +158,7 @@ Auth.prototype.bearer_middleware = function() { var self = this return function(req, res, _next) { req.pause() - function next(err) { + function next(_err) { req.resume() return _next.apply(null, arguments) } @@ -197,7 +197,7 @@ Auth.prototype.cookie_middleware = function() { var self = this return function(req, res, _next) { req.pause() - function next(err) { + function next(_err) { req.resume() return _next() } diff --git a/lib/cli.js b/lib/cli.js index 47e092ea7..d9799e107 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -19,10 +19,15 @@ var logger = require('./logger') logger.setup() // default setup var commander = require('commander') +var constants = require('constants') var fs = require('fs') +var http = require('http') +var https = require('https') var YAML = require('js-yaml') var Path = require('path') +var URL = require('url') var server = require('./index') +var Utils = require('./utils') var pkg_file = '../package.yaml' var pkg = YAML.safeLoad(fs.readFileSync(__dirname+'/'+ pkg_file, 'utf8')) @@ -41,7 +46,7 @@ if (commander.args.length != 0) { commander.help() } -var config, config_path, have_question +var config, config_path try { if (commander.config) { config_path = Path.resolve(commander.config) @@ -57,36 +62,106 @@ try { afterConfigLoad() -function get_hostport() { +function get_listen_addresses() { // command line || config file || default - var hostport = commander.listen || String(config.listen || '') || '4873' + var addresses - hostport = hostport.split(':') - if (hostport.length < 2) { - hostport = [ undefined, hostport[0] ] + if (commander.listen) { + addresses = [ commander.listen ] + } else if (Array.isArray(config.listen)) { + addresses = config.listen + } else if (config.listen) { + addresses = [ config.listen ] + } else { + addresses = [ '4873' ] } - if (hostport[0] == null) { - hostport[0] = 'localhost' - } - return hostport + + addresses = addresses.map(function(addr) { + addr = Utils.parse_address(addr) + + if (!addr) { + logger.logger.warn({ addr: addr }, + 'invalid address - @{addr}, we expect a port (e.g. "4873"),' + + ' host:port (e.g. "localhost:4873") or full url' + + ' (e.g. "http://localhost:4873/")') + } + + return addr + + }).filter(Boolean) + + return addresses } function afterConfigLoad() { if (!config.self_path) config.self_path = Path.resolve(config_path) + if (!config.https) config.https = { enable: false }; - 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 app = server(config) - logger.logger.warn({ addr: 'http://'+hostport[0]+':'+hostport[1]+'/', version: 'Sinopia/'+pkg.version }, 'http address - @{addr}') + get_listen_addresses().forEach(function(addr) { + var webServer + + if (addr.proto === 'https') { // https + if (!config.https || !config.https.key || !config.https.cert) { + var conf_path = function(file) { + if (!file) return config_path + return Path.resolve(Path.dirname(config_path), file) + } + + logger.logger.fatal([ + 'You need to specify "https.key" and "https.cert" to run https server', + '', + // commands are borrowed from node.js docs + 'To quickly create self-signed certificate, use:', + ' $ openssl genrsa -out ' + conf_path('sinopia-key.pem') + ' 2048', + ' $ openssl req -new -sha256 -key ' + conf_path('sinopia-key.pem') + ' -out ' + conf_path('sinopia-csr.pem'), + ' $ openssl x509 -req -in ' + conf_path('sinopia-csr.pem') + ' -signkey ' + conf_path('sinopia-key.pem') + ' -out ' + conf_path('sinopia-cert.pem'), + '', + 'And then add to config file (' + conf_path() + '):', + ' https:', + ' key: sinopia-key.pem', + ' cert: sinopia-cert.pem', + ].join('\n')) + process.exit(2) + } + + try { + webServer = https.createServer({ + secureProtocol: 'SSLv23_method', // disable insecure SSLv2 and SSLv3 + secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3, + key: fs.readFileSync(config.https.key), + cert: fs.readFileSync(config.https.cert) + }, app) + } catch (err) { // catch errors related to certificate loading + logger.logger.fatal({ err: err }, 'cannot create server: @{err.message}') + process.exit(2) + } + } else { // http + webServer = http.createServer(app); + } + + webServer + .listen(addr.port, addr.host) + .on('error', function(err) { + logger.logger.fatal({ err: err }, 'cannot create server: @{err.message}') + process.exit(2) + }) + + logger.logger.warn({ + addr: URL.format({ + protocol: addr.proto, + hostname: addr.host, + port: addr.port, + pathname: '/', + }), + version: 'Sinopia/'+pkg.version, + }, 'http address - @{addr}') + }) // undocumented stuff for tests if (typeof(process.send) === 'function') { - process.send({ sinopia_started: hostport }) + process.send({ sinopia_started: true }) } } diff --git a/lib/index-web.js b/lib/index-web.js index 9f48f3d22..8e43456bc 100644 --- a/lib/index-web.js +++ b/lib/index-web.js @@ -3,7 +3,6 @@ var Cookies = require('cookies') var express = require('express') var fs = require('fs') var Handlebars = require('handlebars') -var Error = require('http-errors') var renderReadme = require('render-readme') var async = require('async') var Search = require('./search') @@ -34,11 +33,17 @@ module.exports = function(config, auth, storage, package_provider) { 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')) - + if(config.web && config.web.template) { + var template = Handlebars.compile(fs.readFileSync(config.web.template, 'utf8')); + } + else { + 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') + var base = config.url_prefix + ? config.url_prefix.replace(/\/$/, '') + : req.protocol + '://' + req.get('host') res.setHeader('Content-Type', 'text/html') storage.get_local(function(err, packages) { @@ -73,11 +78,9 @@ module.exports = function(config, auth, storage, package_provider) { }) 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') + res.sendFile( config.web && config.web.logo + ? config.web.logo + : __dirname + '/static/logo-sm.png' ) }) app.post('/-/login', function(req, res, next) { @@ -90,13 +93,17 @@ module.exports = function(config, auth, storage, package_provider) { res.cookies.set('token', auth.aes_encrypt(str).toString('base64')) } - var base = config.url_prefix || req.protocol + '://' + req.get('host') + var base = config.url_prefix + ? config.url_prefix.replace(/\/$/, '') + : req.protocol + '://' + req.get('host') res.redirect(base) }) }) app.post('/-/logout', function(req, res, next) { - var base = config.url_prefix || req.protocol + '://' + req.get('host') + var base = config.url_prefix + ? config.url_prefix.replace(/\/$/, '') + : req.protocol + '://' + req.get('host') res.cookies.set('token', '') res.redirect(base) }) diff --git a/lib/index.js b/lib/index.js index 493885d0d..6690f50e3 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,5 +1,4 @@ var express = require('express') -var fs = require('fs') var Error = require('http-errors') var compression = require('compression') var Auth = require('./auth') @@ -18,7 +17,6 @@ module.exports = function(config_hash) { var auth = Auth(config) var packages = PackageProvider(config) var app = express() - var can = Middleware.allow(config, packages) // run in production mode by default, just in case // it shouldn't make any difference anyway diff --git a/lib/local-data.js b/lib/local-data.js index 44ce66c43..bc050562b 100644 --- a/lib/local-data.js +++ b/lib/local-data.js @@ -26,7 +26,7 @@ LocalData.prototype.remove = function(name) { if (i !== -1) { this.data.list.splice(i, 1) } - + this.sync() } diff --git a/lib/local-storage.js b/lib/local-storage.js index 5ae74ab21..4a6da4ebe 100644 --- a/lib/local-storage.js +++ b/lib/local-storage.js @@ -44,7 +44,6 @@ Storage.prototype._internal_error = function(err, file, message) { } Storage.prototype.add_package = function(name, info, callback) { - var self = this var storage = this.storage(name) if (!storage) return callback( Error[404]('this package cannot be added') ) @@ -101,7 +100,7 @@ Storage.prototype.remove_package = function(name, callback) { }) }) }) - + Search.remove(name) this.config.localList.remove(name) } @@ -267,11 +266,6 @@ Storage.prototype.add_tags = function(name, tags, callback) { }, callback) } -// change package info to tag a specific version -function _add_tag(data, version, tag) { - data['dist-tags'][tag] = version -} - // currently supports unpublishing only Storage.prototype.change_package = function(name, metadata, revision, callback) { var self = this diff --git a/lib/middleware.js b/lib/middleware.js index 2ecb99d36..955ca8b00 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -95,8 +95,7 @@ module.exports.allow = function(config, packages) { } next( Error[403](message) ) } else { - next( Error[403]('user ' + req.remote_user.name - + ' not allowed to ' + action + ' it') ) + var message = "can't "+action+" restricted package, you are not logged in" } } }) @@ -154,7 +153,6 @@ module.exports.log = function(req, res, next) { var _cookie = req.headers.cookie if (_cookie != null) req.headers.cookie = '' - var _url = req.url req.url = req.originalUrl req.log.info( { req: req, ip: req.ip } , '@{ip} requested \'@{req.method} @{req.url}\'' ) @@ -183,7 +181,6 @@ module.exports.log = function(req, res, next) { message += ', bytes: @{bytes.in}/@{bytes.out}' } - var _url = req.url req.url = req.originalUrl req.log.warn({ request : { method: req.method, url: req.url }, diff --git a/lib/static/main.css b/lib/static/main.css index 04e88bbba..7e6803f04 100644 --- a/lib/static/main.css +++ b/lib/static/main.css @@ -7241,8 +7241,7 @@ Original style from softwaremaniacs.org (c) Ivan Sagalaev 3) { i++ diff --git a/lib/up-storage.js b/lib/up-storage.js index e434dd384..144f70299 100644 --- a/lib/up-storage.js +++ b/lib/up-storage.js @@ -111,6 +111,7 @@ Storage.prototype.request = function(options, cb) { headers['Accept'] = headers['Accept'] || 'application/json' headers['Accept-Encoding'] = headers['Accept-Encoding'] || 'gzip' headers['User-Agent'] = headers['User-Agent'] || this.userAgent + this._add_proxy_headers(options.req, headers) var method = options.method || 'GET' var uri = options.uri_full || (this.config.url + options.uri) @@ -253,12 +254,12 @@ Storage.prototype.get_package = function(name, options, callback) { 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, + req : options.req, }, function(err, res, body) { if (err) return callback(err) if (res.statusCode === 404) { diff --git a/lib/utils.js b/lib/utils.js index 7a3384793..0eae16727 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -122,6 +122,29 @@ module.exports.get_version = function(object, version) { } } +module.exports.parse_address = function parse_address(addr) { + // + // Allow: + // + // - https:localhost:1234 - protocol + host + port + // - localhost:1234 - host + port + // - 1234 - port + // - http::1234 - protocol + port + // - https://localhost:443/ - full url + https + // - http://[::1]:443/ - ipv6 + // + // protocol : // ( host )|( ipv6 ): port / + var m = /^((https?):(\/\/)?)?((([^\/:]*)|\[([^\[\]]+)\]):)?(\d+)\/?$/.exec(addr) + + if (!m) return null + + return { + proto: m[2] || 'http', + host: m[6] || m[7] || 'localhost', + port: m[8] || '4873', + } +} + // function filters out bad semver versions and sorts the array module.exports.semver_sort = function semver_sort(array) { return array diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 05694a566..8f98f268b 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,90 +1,58 @@ { "name": "sinopia", - "version": "1.0.1", + "version": "1.1.0", "dependencies": { "async": { - "version": "0.9.0", - "from": "async@>=0.9.0 <1.0.0-0", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" + "version": "0.9.0" }, "body-parser": { - "version": "1.12.0", - "from": "body-parser@>=1.9.2 <2.0.0-0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.12.0.tgz", + "version": "1.12.2", "dependencies": { "bytes": { - "version": "1.0.0", - "from": "bytes@1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz" + "version": "1.0.0" }, "content-type": { - "version": "1.0.1", - "from": "content-type@>=1.0.1 <1.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.1.tgz" + "version": "1.0.1" }, "debug": { - "version": "2.1.1", - "from": "debug@>=2.1.1 <2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.1.tgz", + "version": "2.1.3", "dependencies": { "ms": { - "version": "0.6.2", - "from": "ms@0.6.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" + "version": "0.7.0" } } }, "depd": { - "version": "1.0.0", - "from": "depd@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.0.tgz" + "version": "1.0.0" }, "iconv-lite": { - "version": "0.4.7", - "from": "iconv-lite@0.4.7", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.7.tgz" + "version": "0.4.7" }, "on-finished": { "version": "2.2.0", - "from": "on-finished@>=2.2.0 <2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.2.0.tgz", "dependencies": { "ee-first": { - "version": "1.1.0", - "from": "ee-first@1.1.0", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz" + "version": "1.1.0" } } }, "qs": { - "version": "2.3.3", - "from": "qs@2.3.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz" + "version": "2.4.1" }, "raw-body": { - "version": "1.3.3", - "from": "raw-body@1.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.3.3.tgz" + "version": "1.3.3" }, "type-is": { - "version": "1.6.0", - "from": "type-is@>=1.6.0 <1.7.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.0.tgz", + "version": "1.6.1", "dependencies": { "media-typer": { - "version": "0.3.0", - "from": "media-typer@0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + "version": "0.3.0" }, "mime-types": { - "version": "2.0.9", - "from": "mime-types@>=2.0.9 <2.1.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.9.tgz", + "version": "2.0.10", "dependencies": { "mime-db": { - "version": "1.7.0", - "from": "mime-db@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.7.0.tgz" + "version": "1.8.0" } } } @@ -93,427 +61,285 @@ } }, "bunyan": { - "version": "1.3.3", - "from": "bunyan@>=0.22.1 <2.0.0-0", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.3.3.tgz", + "version": "1.3.4", "dependencies": { "dtrace-provider": { "version": "0.4.0", - "from": "dtrace-provider@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.4.0.tgz", "dependencies": { "nan": { - "version": "1.5.3", - "from": "nan@>=1.5.1 <1.6.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-1.5.3.tgz" + "version": "1.5.3" } } }, "mv": { "version": "2.0.3", - "from": "mv@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.0.3.tgz", "dependencies": { "ncp": { - "version": "0.6.0", - "from": "ncp@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz" + "version": "0.6.0" + }, + "rimraf": { + "version": "2.2.8" } } }, "safe-json-stringify": { - "version": "1.0.1", - "from": "safe-json-stringify@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.1.tgz" + "version": "1.0.1" } } }, "commander": { - "version": "2.6.0", - "from": "commander@>=2.3.0 <3.0.0-0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz" + "version": "2.7.1", + "dependencies": { + "graceful-readlink": { + "version": "1.0.1" + } + } }, "compression": { - "version": "1.4.1", - "from": "compression@>=1.2.0 <2.0.0-0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.4.1.tgz", + "version": "1.4.3", "dependencies": { "accepts": { - "version": "1.2.4", - "from": "accepts@>=1.2.4 <1.3.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.4.tgz", + "version": "1.2.5", "dependencies": { "mime-types": { - "version": "2.0.9", - "from": "mime-types@>=2.0.9 <2.1.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.9.tgz", + "version": "2.0.10", "dependencies": { "mime-db": { - "version": "1.7.0", - "from": "mime-db@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.7.0.tgz" + "version": "1.8.0" } } }, "negotiator": { - "version": "0.5.1", - "from": "negotiator@0.5.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.1.tgz" + "version": "0.5.1" } } }, "bytes": { - "version": "1.0.0", - "from": "bytes@1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz" + "version": "1.0.0" }, "compressible": { "version": "2.0.2", - "from": "compressible@>=2.0.2 <2.1.0", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.2.tgz", "dependencies": { "mime-db": { - "version": "1.7.0", - "from": "mime-db@>=1.1.2 <2.0.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.7.0.tgz" + "version": "1.8.0" } } }, "debug": { - "version": "2.1.1", - "from": "debug@>=2.1.1 <2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.1.tgz", + "version": "2.1.3", "dependencies": { "ms": { - "version": "0.6.2", - "from": "ms@0.6.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" + "version": "0.7.0" } } }, "on-headers": { - "version": "1.0.0", - "from": "on-headers@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.0.tgz" + "version": "1.0.0" }, "vary": { - "version": "1.0.0", - "from": "vary@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.0.tgz" + "version": "1.0.0" } } }, "cookies": { "version": "0.5.0", - "from": "cookies@>=0.5.0 <1.0.0-0", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.5.0.tgz", "dependencies": { "keygrip": { - "version": "1.0.1", - "from": "keygrip@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.1.tgz" + "version": "1.0.1" } } }, "crypt3": { "version": "0.1.7", - "from": "crypt3@>=0.1.6 <1.0.0-0", - "resolved": "https://registry.npmjs.org/crypt3/-/crypt3-0.1.7.tgz", "dependencies": { "nan": { - "version": "1.6.2", - "from": "nan@>=1.0.0 <2.0.0 >=1.6.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-1.6.2.tgz" + "version": "1.7.0" } } }, "es6-shim": { - "version": "0.21.1", - "from": "es6-shim@>=0.21.0 <0.22.0", - "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.21.1.tgz" + "version": "0.21.1" }, "express": { "version": "5.0.0-alpha.1", - "from": "express@>=5.0.0-0 <6.0.0-0", - "resolved": "https://registry.npmjs.org/express/-/express-5.0.0-alpha.1.tgz", "dependencies": { "accepts": { "version": "1.1.4", - "from": "accepts@>=1.1.2 <1.2.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.1.4.tgz", "dependencies": { "mime-types": { - "version": "2.0.9", - "from": "mime-types@>=2.0.9 <2.1.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.9.tgz", + "version": "2.0.10", "dependencies": { "mime-db": { - "version": "1.7.0", - "from": "mime-db@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.7.0.tgz" + "version": "1.8.0" } } }, "negotiator": { - "version": "0.4.9", - "from": "negotiator@0.4.9", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.4.9.tgz" + "version": "0.4.9" } } }, "content-disposition": { - "version": "0.5.0", - "from": "content-disposition@0.5.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz" + "version": "0.5.0" }, "cookie-signature": { - "version": "1.0.5", - "from": "cookie-signature@1.0.5", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.5.tgz" + "version": "1.0.5" }, "debug": { - "version": "2.1.1", - "from": "debug@>=2.1.0 <2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.1.tgz", + "version": "2.1.3", "dependencies": { "ms": { - "version": "0.6.2", - "from": "ms@0.6.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" + "version": "0.7.0" } } }, "depd": { - "version": "1.0.0", - "from": "depd@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.0.tgz" + "version": "1.0.0" }, "escape-html": { - "version": "1.0.1", - "from": "escape-html@1.0.1", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz" + "version": "1.0.1" }, "etag": { "version": "1.5.1", - "from": "etag@>=1.5.0 <1.6.0", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.5.1.tgz", "dependencies": { "crc": { - "version": "3.2.1", - "from": "crc@3.2.1", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.2.1.tgz" + "version": "3.2.1" } } }, "finalhandler": { - "version": "0.3.2", - "from": "finalhandler@0.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.3.2.tgz" + "version": "0.3.2" }, "fresh": { - "version": "0.2.4", - "from": "fresh@0.2.4", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz" + "version": "0.2.4" }, "media-typer": { - "version": "0.3.0", - "from": "media-typer@0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + "version": "0.3.0" }, "methods": { - "version": "1.1.0", - "from": "methods@1.1.0", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.0.tgz" + "version": "1.1.0" }, "on-finished": { "version": "2.1.1", - "from": "on-finished@>=2.1.1 <2.2.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.1.1.tgz", "dependencies": { "ee-first": { - "version": "1.1.0", - "from": "ee-first@1.1.0", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz" + "version": "1.1.0" } } }, "parseurl": { - "version": "1.3.0", - "from": "parseurl@>=1.3.0 <1.4.0", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" + "version": "1.3.0" }, "path-to-regexp": { - "version": "0.1.3", - "from": "path-to-regexp@0.1.3", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.3.tgz" + "version": "0.1.3" }, "proxy-addr": { - "version": "1.0.6", - "from": "proxy-addr@>=1.0.3 <1.1.0", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.6.tgz", + "version": "1.0.7", "dependencies": { "forwarded": { - "version": "0.1.0", - "from": "forwarded@>=0.1.0 <0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz" + "version": "0.1.0" }, "ipaddr.js": { - "version": "0.1.8", - "from": "ipaddr.js@0.1.8", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-0.1.8.tgz" + "version": "0.1.9" } } }, "qs": { - "version": "2.3.2", - "from": "qs@2.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.2.tgz" + "version": "2.3.2" }, "range-parser": { - "version": "1.0.2", - "from": "range-parser@>=1.0.2 <1.1.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.2.tgz" + "version": "1.0.2" }, "send": { "version": "0.10.1", - "from": "send@0.10.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.10.1.tgz", "dependencies": { "destroy": { - "version": "1.0.3", - "from": "destroy@1.0.3", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz" + "version": "1.0.3" }, "mime": { - "version": "1.2.11", - "from": "mime@1.2.11", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + "version": "1.2.11" }, "ms": { - "version": "0.6.2", - "from": "ms@0.6.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" + "version": "0.6.2" } } }, "serve-static": { - "version": "1.7.2", - "from": "serve-static@>=1.7.1 <1.8.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.7.2.tgz" + "version": "1.7.2" }, "type-is": { "version": "1.5.7", - "from": "type-is@>=1.5.2 <1.6.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.5.7.tgz", "dependencies": { "mime-types": { - "version": "2.0.9", - "from": "mime-types@>=2.0.9 <2.1.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.9.tgz", + "version": "2.0.10", "dependencies": { "mime-db": { - "version": "1.7.0", - "from": "mime-db@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.7.0.tgz" + "version": "1.8.0" } } } } }, "vary": { - "version": "1.0.0", - "from": "vary@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.0.tgz" + "version": "1.0.0" }, "cookie": { - "version": "0.1.2", - "from": "cookie@0.1.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.2.tgz" + "version": "0.1.2" }, "merge-descriptors": { - "version": "0.0.2", - "from": "merge-descriptors@0.0.2", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-0.0.2.tgz" + "version": "0.0.2" }, "utils-merge": { - "version": "1.0.0", - "from": "utils-merge@1.0.0", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" + "version": "1.0.0" } } }, "express-json5": { "version": "0.1.0", - "from": "express-json5@>=0.1.0 <1.0.0-0", - "resolved": "https://registry.npmjs.org/express-json5/-/express-json5-0.1.0.tgz", "dependencies": { "raw-body": { "version": "1.3.3", - "from": "raw-body@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.3.3.tgz", "dependencies": { "bytes": { - "version": "1.0.0", - "from": "bytes@1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz" + "version": "1.0.0" }, "iconv-lite": { - "version": "0.4.7", - "from": "iconv-lite@0.4.7", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.7.tgz" + "version": "0.4.7" } } } } }, "fs-ext": { - "version": "0.4.3", - "from": "fs-ext@>=0.4.1 <1.0.0-0", - "resolved": "https://registry.npmjs.org/fs-ext/-/fs-ext-0.4.3.tgz", + "version": "0.4.4", "dependencies": { "nan": { - "version": "1.4.3", - "from": "nan@>=1.4.0 <1.5.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-1.4.3.tgz" + "version": "1.6.2" } } }, "handlebars": { "version": "2.0.0", - "from": "handlebars@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-2.0.0.tgz", "dependencies": { "optimist": { "version": "0.3.7", - "from": "optimist@>=0.3.0 <0.4.0", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", "dependencies": { "wordwrap": { - "version": "0.0.2", - "from": "wordwrap@>=0.0.2 <0.1.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" + "version": "0.0.2" } } }, "uglify-js": { "version": "2.3.6", - "from": "uglify-js@>=2.3.0 <2.4.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.3.6.tgz", "dependencies": { "async": { - "version": "0.2.10", - "from": "async@>=0.2.6 <0.3.0", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + "version": "0.2.10" }, "source-map": { "version": "0.1.43", - "from": "source-map@>=0.1.7 <0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", "dependencies": { "amdefine": { - "version": "0.1.0", - "from": "amdefine@>=0.0.4", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.1.0.tgz" + "version": "0.1.0" } } } @@ -522,406 +348,348 @@ } }, "highlight.js": { - "version": "8.4.0", - "from": "highlight.js@>=8.0.0 <9.0.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-8.4.0.tgz" + "version": "8.4.0" }, "http-errors": { "version": "1.3.1", - "from": "http-errors@>=1.2.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", "dependencies": { "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "version": "2.0.1" }, "statuses": { - "version": "1.2.1", - "from": "statuses@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" + "version": "1.2.1" } } }, "jju": { - "version": "1.2.0", - "from": "jju@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.2.0.tgz" + "version": "1.2.0" }, "js-yaml": { - "version": "3.2.6", - "from": "js-yaml@>=3.0.1 <4.0.0-0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.2.6.tgz", + "version": "3.2.7", "dependencies": { "argparse": { - "version": "0.1.16", - "from": "argparse@>=0.1.11 <0.2.0", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "version": "1.0.2", "dependencies": { - "underscore": { - "version": "1.7.0", - "from": "underscore@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + "lodash": { + "version": "3.6.0" }, - "underscore.string": { - "version": "2.4.0", - "from": "underscore.string@>=2.4.0 <2.5.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + "sprintf-js": { + "version": "1.0.2" } } }, "esprima": { - "version": "1.0.4", - "from": "esprima@>=1.0.2 <1.1.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + "version": "2.0.0" } } }, "lunr": { - "version": "0.5.7", - "from": "lunr@>=0.5.2 <1.0.0-0", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-0.5.7.tgz" + "version": "0.5.7" }, "minimatch": { "version": "1.0.0", - "from": "minimatch@>=0.2.14 <2.0.0-0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", "dependencies": { "lru-cache": { - "version": "2.5.0", - "from": "lru-cache@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" + "version": "2.5.0" }, "sigmund": { - "version": "1.0.0", - "from": "sigmund@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + "version": "1.0.0" } } }, "mkdirp": { "version": "0.5.0", - "from": "mkdirp@>=0.3.5 <1.0.0-0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { - "version": "0.0.8", - "from": "minimist@0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + "version": "0.0.8" } } }, "render-readme": { - "version": "1.1.0", - "from": "render-readme@>=0.2.1", - "resolved": "https://registry.npmjs.org/render-readme/-/render-readme-1.1.0.tgz", + "version": "1.3.0", "dependencies": { "markdown-it": { - "version": "3.0.6", - "from": "markdown-it@>=3.0.0 <4.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-3.0.6.tgz", + "version": "4.0.3", "dependencies": { "argparse": { - "version": "0.1.16", - "from": "argparse@>=0.1.11 <0.2.0", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "version": "1.0.2", "dependencies": { - "underscore": { - "version": "1.7.0", - "from": "underscore@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + "lodash": { + "version": "3.6.0" }, - "underscore.string": { - "version": "2.4.0", - "from": "underscore.string@>=2.4.0 <2.5.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" + "sprintf-js": { + "version": "1.0.2" } } }, - "autolinker": { - "version": "0.15.2", - "from": "autolinker@>=0.15.2 <0.16.0", - "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.15.2.tgz" + "entities": { + "version": "1.1.1" + }, + "linkify-it": { + "version": "1.0.0" + }, + "mdurl": { + "version": "1.0.0" + }, + "uc.micro": { + "version": "1.0.0" } } }, "sanitize-html": { "version": "1.6.1", - "from": "sanitize-html@>=1.0.0 <2.0.0 >=1.4.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.6.1.tgz", "dependencies": { "htmlparser2": { "version": "3.8.2", - "from": "htmlparser2@>=3.8.0 <3.9.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.2.tgz", "dependencies": { "domhandler": { - "version": "2.3.0", - "from": "domhandler@>=2.3.0 <2.4.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz" + "version": "2.3.0" }, "domutils": { "version": "1.5.1", - "from": "domutils@>=1.5.0 <1.6.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", "dependencies": { "dom-serializer": { "version": "0.1.0", - "from": "dom-serializer@>=0.0.0 <1.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", "dependencies": { + "domelementtype": { + "version": "1.1.3" + }, "entities": { - "version": "1.1.1", - "from": "entities@>=1.1.1 <1.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + "version": "1.1.1" } } } } }, "domelementtype": { - "version": "1.1.3", - "from": "domelementtype@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz" + "version": "1.3.0" }, "readable-stream": { "version": "1.1.13", - "from": "readable-stream@>=1.1.0 <1.2.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", "dependencies": { "core-util-is": { - "version": "1.0.1", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + "version": "1.0.1" }, "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + "version": "0.0.1" }, "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + "version": "0.10.31" }, "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.1 <2.1.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "version": "2.0.1" } } }, "entities": { - "version": "1.0.0", - "from": "entities@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz" + "version": "1.0.0" } } }, "lodash": { - "version": "2.4.1", - "from": "lodash@>=2.4.1 <2.5.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz" + "version": "2.4.1" }, "regexp-quote": { - "version": "0.0.0", - "from": "regexp-quote@0.0.0", - "resolved": "https://registry.npmjs.org/regexp-quote/-/regexp-quote-0.0.0.tgz" + "version": "0.0.0" } } } } }, "request": { - "version": "2.53.0", - "from": "request@>=2.31.0 <3.0.0-0", - "resolved": "https://registry.npmjs.org/request/-/request-2.53.0.tgz", + "version": "2.54.0", "dependencies": { "bl": { "version": "0.9.4", - "from": "bl@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.4.tgz", "dependencies": { "readable-stream": { "version": "1.0.33", - "from": "readable-stream@>=1.0.26 <1.1.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", "dependencies": { "core-util-is": { - "version": "1.0.1", - "from": "core-util-is@>=1.0.0 <1.1.0", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + "version": "1.0.1" }, "isarray": { - "version": "0.0.1", - "from": "isarray@0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + "version": "0.0.1" }, "string_decoder": { - "version": "0.10.31", - "from": "string_decoder@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + "version": "0.10.31" }, "inherits": { - "version": "2.0.1", - "from": "inherits@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + "version": "2.0.1" } } } } }, "caseless": { - "version": "0.9.0", - "from": "caseless@>=0.9.0 <0.10.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.9.0.tgz" + "version": "0.9.0" }, "forever-agent": { - "version": "0.5.2", - "from": "forever-agent@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + "version": "0.6.0" }, "form-data": { - "version": "0.2.0", - "from": "form-data@>=0.2.0 <0.3.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz" + "version": "0.2.0" }, "json-stringify-safe": { - "version": "5.0.0", - "from": "json-stringify-safe@>=5.0.0 <5.1.0", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" + "version": "5.0.0" }, "mime-types": { - "version": "2.0.9", - "from": "mime-types@>=2.0.9 <2.1.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.9.tgz", + "version": "2.0.10", "dependencies": { "mime-db": { - "version": "1.7.0", - "from": "mime-db@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.7.0.tgz" + "version": "1.8.0" } } }, "node-uuid": { - "version": "1.4.2", - "from": "node-uuid@>=1.4.0 <1.5.0", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.2.tgz" + "version": "1.4.3" }, "qs": { - "version": "2.3.3", - "from": "qs@>=2.3.1 <2.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz" + "version": "2.4.1" }, "tunnel-agent": { - "version": "0.4.0", - "from": "tunnel-agent@>=0.4.0 <0.5.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz" + "version": "0.4.0" }, "tough-cookie": { "version": "0.12.1", - "from": "tough-cookie@>=0.12.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz", "dependencies": { "punycode": { - "version": "1.3.2", - "from": "punycode@>=0.2.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + "version": "1.3.2" } } }, "http-signature": { "version": "0.10.1", - "from": "http-signature@>=0.10.0 <0.11.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", "dependencies": { "assert-plus": { - "version": "0.1.5", - "from": "assert-plus@>=0.1.5 <0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" + "version": "0.1.5" }, "asn1": { - "version": "0.1.11", - "from": "asn1@0.1.11", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" + "version": "0.1.11" }, "ctype": { - "version": "0.5.3", - "from": "ctype@0.5.3", - "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz" + "version": "0.5.3" } } }, "oauth-sign": { - "version": "0.6.0", - "from": "oauth-sign@>=0.6.0 <0.7.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.6.0.tgz" + "version": "0.6.0" }, "hawk": { "version": "2.3.1", - "from": "hawk@>=2.3.0 <2.4.0", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-2.3.1.tgz", "dependencies": { "hoek": { - "version": "2.11.0", - "from": "hoek@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.11.0.tgz" + "version": "2.12.0" }, "boom": { - "version": "2.6.1", - "from": "boom@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.6.1.tgz" + "version": "2.6.1" }, "cryptiles": { - "version": "2.0.4", - "from": "cryptiles@>=2.0.0 <3.0.0", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.4.tgz" + "version": "2.0.4" }, "sntp": { - "version": "1.0.9", - "from": "sntp@>=1.0.0 <2.0.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" + "version": "1.0.9" } } }, "aws-sign2": { - "version": "0.5.0", - "from": "aws-sign2@>=0.5.0 <0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" + "version": "0.5.0" }, "stringstream": { - "version": "0.0.4", - "from": "stringstream@>=0.0.4 <0.1.0", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.4.tgz" + "version": "0.0.4" }, "combined-stream": { "version": "0.0.7", - "from": "combined-stream@>=0.0.5 <0.1.0", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", "dependencies": { "delayed-stream": { - "version": "0.0.5", - "from": "delayed-stream@0.0.5", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" + "version": "0.0.5" } } }, "isstream": { - "version": "0.1.1", - "from": "isstream@>=0.1.1 <0.2.0", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.1.tgz" + "version": "0.1.2" + }, + "har-validator": { + "version": "1.4.0", + "dependencies": { + "bluebird": { + "version": "2.9.16" + }, + "chalk": { + "version": "1.0.0", + "dependencies": { + "ansi-styles": { + "version": "2.0.1" + }, + "escape-string-regexp": { + "version": "1.0.3" + }, + "has-ansi": { + "version": "1.0.3", + "dependencies": { + "ansi-regex": { + "version": "1.1.1" + }, + "get-stdin": { + "version": "4.0.1" + } + } + }, + "strip-ansi": { + "version": "2.0.1", + "dependencies": { + "ansi-regex": { + "version": "1.1.1" + } + } + }, + "supports-color": { + "version": "1.3.1" + } + } + }, + "debug": { + "version": "2.1.3", + "dependencies": { + "ms": { + "version": "0.7.0" + } + } + }, + "is-my-json-valid": { + "version": "2.10.0", + "dependencies": { + "generate-function": { + "version": "2.0.0" + }, + "generate-object-property": { + "version": "1.1.1", + "dependencies": { + "is-property": { + "version": "1.0.2" + } + } + }, + "jsonpointer": { + "version": "1.1.0" + }, + "xtend": { + "version": "4.0.0" + } + } + }, + "require-directory": { + "version": "2.1.0" + } + } } } }, "semver": { - "version": "4.3.0", - "from": "semver@>=2.2.1 <5.0.0-0", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.0.tgz" + "version": "4.3.3" }, "sinopia-htpasswd": { - "version": "0.4.5", - "from": "sinopia-htpasswd@>=0.4.3", - "resolved": "https://registry.npmjs.org/sinopia-htpasswd/-/sinopia-htpasswd-0.4.5.tgz" + "version": "0.4.5" } } } diff --git a/package.yaml b/package.yaml index 813e3de40..d2361cdef 100644 --- a/package.yaml +++ b/package.yaml @@ -1,7 +1,7 @@ # use "yapm install ." if you're installing this from git repository name: sinopia -version: 1.0.1 +version: 1.1.0 description: Private npm repository server author: @@ -55,12 +55,14 @@ devDependencies: # Tools required for testing # rimraf: '>=2.2.5 <3.0.0-0' - mocha: '>=1.17.0 <2.0.0-0' + + # https://github.com/mochajs/mocha/issues/1614 + mocha: '<= 1.20' # # Linting tools # - eslint: '~0.6.0' + eslint: '>= 0.18' # for debugging memory leaks, it'll be require()'d if # installed, but I don't want it to be installed everytime @@ -81,7 +83,6 @@ devDependencies: # not required in runtime unopinionate: '>=0.0.4 <1.0.0-0' onclick: '>=0.1.0 <1.0.0-0' - onscroll: '>=0.0.3 <1.0.0-0' transition-complete: '>=0.0.2 <1.0.0-0' keywords: @@ -94,10 +95,26 @@ keywords: - server scripts: - test: mocha -R dot ./test/functional ./test/unit - test-travis: mocha -R spec ./test/functional ./test/unit - lint: eslint -c ./.eslint.yaml ./lib + test: eslint --reset . && mocha -R dot ./test/functional ./test/unit + test-travis: eslint --reset . && mocha -R spec ./test/functional ./test/unit + test-only: mocha -R dot ./test/functional ./test/unit + lint: eslint --reset . prepublish: js-yaml package.yaml > package.json + clean-shrinkwrap: | + node -e ' + function clean(j) { + if (!j) return + for (var k in j) { + delete j[k].from + delete j[k].resolved + if (j[k].dependencies) clean(j[k].dependencies) + } + } + x = JSON.parse(require("fs").readFileSync("./npm-shrinkwrap.json")) + clean(x.dependencies) + x = JSON.stringify(x, null, " ") + require("fs").writeFileSync("./npm-shrinkwrap.json", x + "\n") + ' # we depend on streams2 stuff # it can be replaced with isaacs/readable-stream, ask if you need to use 0.8 diff --git a/test/.eslintrc b/test/.eslintrc new file mode 100644 index 000000000..b3e16f8ee --- /dev/null +++ b/test/.eslintrc @@ -0,0 +1,5 @@ + +env: + node: true + mocha: true + diff --git a/test/functional/basic.js b/test/functional/basic.js index 6bb526ec5..7b85d89b7 100644 --- a/test/functional/basic.js +++ b/test/functional/basic.js @@ -1,7 +1,6 @@ require('./lib/startup') var assert = require('assert') -var async = require('async') var crypto = require('crypto') function readfile(x) { @@ -132,6 +131,7 @@ module.exports = function() { it('who am I?', function(cb) { server.request({uri:'/-/whoami'}, function(err, res, body) { + assert.equal(err, null) assert.equal(res.statusCode, 200) assert.equal(body.username, 'test') cb() diff --git a/test/functional/gzip.js b/test/functional/gzip.js index 23164a097..6a2442cb8 100644 --- a/test/functional/gzip.js +++ b/test/functional/gzip.js @@ -1,8 +1,6 @@ require('./lib/startup') var assert = require('assert') -var async = require('async') -var crypto = require('crypto') function readfile(x) { return require('fs').readFileSync(__dirname + '/' + x) @@ -34,7 +32,7 @@ module.exports = function() { x.versions['0.0.9'] = x.versions['0.0.1'] require('zlib').gzip(JSON.stringify(x), function(err, buf) { - assert(!err) + assert.equal(err, null) assert.equal(req.headers['accept-encoding'], 'gzip') res.header('content-encoding', 'gzip') res.send(buf) @@ -74,13 +72,14 @@ module.exports = function() { }, json: false, }, function(err, res, body) { + assert.equal(err, null) 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) + assert.equal(err, null) body = JSON.parse(buf) assert.equal(body.name, 'testexp_gzip') assert.equal(Object.keys(body.versions).length, 9) diff --git a/test/functional/incomplete.js b/test/functional/incomplete.js index 87f9e6672..e81f6f091 100644 --- a/test/functional/incomplete.js +++ b/test/functional/incomplete.js @@ -5,8 +5,6 @@ module.exports = function() { var express = process.express describe('Incomplete', function() { - var on_tarball - before(function() { express.get('/testexp-incomplete', function(_, res) { res.send({ @@ -49,12 +47,14 @@ module.exports = function() { }) server.request({uri:'/testexp-incomplete/-/'+type+'.tar.gz'}, function(err, res, body) { + assert.equal(err, null) 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(err, null) assert.equal(body.error, 'internal server error') _cb() }) diff --git a/test/functional/index.js b/test/functional/index.js index db744b4e7..a290b9a6f 100644 --- a/test/functional/index.js +++ b/test/functional/index.js @@ -4,11 +4,6 @@ require('./lib/startup') var assert = require('assert') var async = require('async') var exec = require('child_process').exec -var crypto = require('crypto') - -function readfile(x) { - return require('fs').readFileSync(__dirname + '/' + x) -} describe('Func', function() { var server = process.server @@ -30,7 +25,8 @@ describe('Func', function() { server.debug(function(res, body) { server.pid = body.pid exec('lsof -p ' + Number(server.pid), function(err, result) { - server.fdlist = result + assert.equal(err, null) + server.fdlist = result.replace(/ +/g, ' ') cb() }) }) @@ -67,12 +63,15 @@ describe('Func', function() { 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) { + assert.equal(err, null) + result = result.split('\n').filter(function(q) { if (q.match(/TCP .*->.* \(ESTABLISHED\)/)) return false if (q.match(/\/libcrypt-[^\/]+\.so/)) return false if (q.match(/\/node_modules\/crypt3\/build\/Release/)) return false return true - }).join('\n')) + }).join('\n').replace(/ +/g, ' ') + + assert.equal(server.fdlist, result) cb() }) }, cb) diff --git a/test/functional/lib/package.js b/test/functional/lib/package.js index 7e904452e..8e0156639 100644 --- a/test/functional/lib/package.js +++ b/test/functional/lib/package.js @@ -5,7 +5,7 @@ module.exports = function(name, version) { "version": version || "0.0.0", "dist": { "shasum": "fake", - "tarball": "http://localhost:55551/"+escape(name)+"/-/blahblah" + "tarball": "http://localhost:55551/"+encodeURIComponent(name)+"/-/blahblah" } } } diff --git a/test/functional/lib/startup.js b/test/functional/lib/startup.js index cd52849da..b056346ff 100644 --- a/test/functional/lib/startup.js +++ b/test/functional/lib/startup.js @@ -1,7 +1,5 @@ -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') diff --git a/test/functional/newnpmreg.js b/test/functional/newnpmreg.js index 125c0a167..d5432edb2 100644 --- a/test/functional/newnpmreg.js +++ b/test/functional/newnpmreg.js @@ -23,6 +23,7 @@ module.exports = function() { method: 'PUT', json: JSON.parse(readfile('fixtures/newnpmreg.json')), }, function(err, res, body) { + assert.equal(err, null) assert.equal(res.statusCode, 201) cb() }) @@ -72,6 +73,7 @@ module.exports = function() { it('server1 - readme', function(cb) { server.request({uri:'/-/readme/testpkg-newnpmreg'}, function(err, res, body) { + assert.equal(err, null) assert.equal(res.statusCode, 200) assert.equal(body, '

blah blah blah

\n') cb() @@ -80,6 +82,7 @@ module.exports = function() { it('server2 - readme', function(cb) { server2.request({uri:'/-/readme/testpkg-newnpmreg'}, function(err, res, body) { + assert.equal(err, null) assert.equal(res.statusCode, 200) assert.equal(body, '

blah blah blah

\n') cb() @@ -114,6 +117,7 @@ module.exports = function() { it('server1 - search', function(cb) { server.request({uri:'/-/all'}, function(err, res, body) { + assert.equal(err, null) assert.equal(res.statusCode, 200) check(body) cb() @@ -122,6 +126,7 @@ module.exports = function() { it('server2 - search', function(cb) { server2.request({uri:'/-/all'}, function(err, res, body) { + assert.equal(err, null) assert.equal(res.statusCode, 200) check(body) cb() diff --git a/test/functional/nullstorage.js b/test/functional/nullstorage.js index 5a150e660..cf0bae2d7 100644 --- a/test/functional/nullstorage.js +++ b/test/functional/nullstorage.js @@ -1,7 +1,6 @@ require('./lib/startup') var assert = require('assert') -var async = require('async') var crypto = require('crypto') function readfile(x) { diff --git a/test/functional/race.js b/test/functional/race.js index 6e61d6375..2956e26d1 100644 --- a/test/functional/race.js +++ b/test/functional/race.js @@ -1,12 +1,9 @@ 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 describe('race', function() { before(function(cb) { @@ -33,11 +30,13 @@ module.exports = function() { async.parallel(fns, function(err, res) { var okcount = 0 - , failcount = 0 + var failcount = 0 + + assert.equal(err, null) res.forEach(function(arr) { var resp = arr[0] - , body = arr[1] + var body = arr[1] if (resp.statusCode === 201 && ~body.ok.indexOf('published')) okcount++ if (resp.statusCode === 409 && ~body.error.indexOf('already present')) failcount++ @@ -65,11 +64,12 @@ module.exports = function() { async.parallel(fns, function(err, res) { var okcount = 0 - , failcount = 0 + var failcount = 0 + assert.equal(err, null) res.forEach(function(arr) { var resp = arr[0] - , body = arr[1] + var body = arr[1] if (resp.statusCode === 201 && ~body.ok.indexOf('published')) okcount++ if (resp.statusCode === 409 && ~body.error.indexOf('already present')) failcount++ if (resp.statusCode === 503 && ~body.error.indexOf('unavailable')) failcount++ diff --git a/test/functional/racycrash.js b/test/functional/racycrash.js index 206eec58c..fbea0407f 100644 --- a/test/functional/racycrash.js +++ b/test/functional/racycrash.js @@ -41,12 +41,14 @@ module.exports = function() { } server.request({uri:'/testexp-racycrash/-/test.tar.gz'}, function(err, res, body) { + assert.equal(err, null) assert.equal(body, 'test test test\n') }) function cb() { // test for NOT crashing server.request({uri:'/testexp-racycrash'}, function(err, res, body) { + assert.equal(err, null) assert.equal(res.statusCode, 200) _cb() }) @@ -59,6 +61,7 @@ module.exports = function() { } server.request({uri:'/testexp-racycrash/-/test.tar.gz'}, function(err, res, body) { + assert.equal(err, null) assert.equal(body.error, 'internal server error') cb() }) diff --git a/test/functional/scoped.js b/test/functional/scoped.js index 01d06c8e5..a92679a04 100644 --- a/test/functional/scoped.js +++ b/test/functional/scoped.js @@ -11,7 +11,6 @@ function sha(x) { module.exports = function() { var server = process.server var server2 = process.server2 - var express = process.express describe('test-scoped', function() { before(function(cb) { @@ -23,6 +22,7 @@ module.exports = function() { method: 'PUT', json: JSON.parse(readfile('fixtures/scoped.json')), }, function(err, res, body) { + assert.equal(err, null) assert.equal(res.statusCode, 201) cb() }) diff --git a/test/functional/security.js b/test/functional/security.js index bcc686ef0..2f0230672 100644 --- a/test/functional/security.js +++ b/test/functional/security.js @@ -2,7 +2,6 @@ var assert = require('assert') module.exports = function() { var server = process.server - var server2 = process.server2 describe('Security', function() { before(server.add_package.bind(server, 'testpkg-sec')) @@ -25,11 +24,14 @@ module.exports = function() { it('__proto__, connect stuff', function(cb) { server.request({uri:'/testpkg-sec?__proto__=1'}, function(err, res, body) { + assert.equal(err, null) + // 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(err, null) assert.equal(res.statusCode, 200) cb() }) @@ -38,6 +40,7 @@ module.exports = function() { it('do not return package.json as an attachment', function(cb) { server.request({uri:'/testpkg-sec/-/package.json'}, function(err, res, body) { + assert.equal(err, null) assert.equal(res.statusCode, 403) assert(body.error.match(/invalid filename/)) cb() @@ -46,6 +49,7 @@ module.exports = function() { it('silly things - reading #1', function(cb) { server.request({uri:'/testpkg-sec/-/../../../../../../../../etc/passwd'}, function(err, res, body) { + assert.equal(err, null) assert.equal(res.statusCode, 404) cb() }) @@ -53,6 +57,7 @@ module.exports = function() { 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(err, null) assert.equal(res.statusCode, 403) assert(body.error.match(/invalid filename/)) cb() diff --git a/test/functional/tags.js b/test/functional/tags.js index b71f3c92a..a4a6c26f5 100644 --- a/test/functional/tags.js +++ b/test/functional/tags.js @@ -40,6 +40,7 @@ module.exports = function() { ;['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(err, null) assert.equal(res.statusCode, 200) assert.equal(body.version, '0.1.1alpha') cb() diff --git a/test/unit/listen_addr.js b/test/unit/listen_addr.js new file mode 100644 index 000000000..5b1ae5f6a --- /dev/null +++ b/test/unit/listen_addr.js @@ -0,0 +1,32 @@ +var assert = require('assert') +var parse = require('../../lib/utils').parse_address + +describe('Parse address', function() { + function addTest(what, proto, host, port) { + it(what, function() { + if (proto === null) { + assert.strictEqual(parse(what), null) + } else { + assert.deepEqual(parse(what), { + proto: proto, + host: host, + port: port, + }) + } + }) + } + + addTest('4873', 'http', 'localhost', '4873') + addTest(':4873', 'http', 'localhost', '4873') + addTest('blah:4873', 'http', 'blah', '4873') + addTest('http://:4873', 'http', 'localhost', '4873') + addTest('https::4873', 'https', 'localhost', '4873') + addTest('https:blah:4873', 'https', 'blah', '4873') + addTest('https://blah:4873/', 'https', 'blah', '4873') + addTest('[::1]:4873', 'http', '::1', '4873') + addTest('https:[::1]:4873', 'https', '::1', '4873') + + addTest('blah', null) + addTest('blah://4873', null) + addTest('https://blah:4873///', null) +}) diff --git a/test/unit/toplevel.js b/test/unit/toplevel.js index df01d18a9..4a2e3c3fe 100644 --- a/test/unit/toplevel.js +++ b/test/unit/toplevel.js @@ -38,6 +38,7 @@ describe('toplevel', function() { request({ url: 'http://localhost:' + port + '/', }, function(err, res, body) { + assert.equal(err, null) assert(body.match(/Sinopia<\/title>/)) done() }) @@ -47,6 +48,7 @@ describe('toplevel', function() { request({ url: 'http://localhost:' + port + '/whatever', }, function(err, res, body) { + assert.equal(err, null) assert(body.match(/no such package available/)) done() }) diff --git a/test/unit/utils.js b/test/unit/utils.js index a5f3c4148..c983e874b 100644 --- a/test/unit/utils.js +++ b/test/unit/utils.js @@ -21,11 +21,11 @@ describe('Validate', 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') )