From a8fa475dc1b539d8bac2e735c4aabe50bf5a22ac Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Thu, 13 Jun 2013 18:21:14 +0400 Subject: [PATCH] config file changes --- .gitignore | 1 + bin/sinopia | 38 ++++++++++++-------------------------- config.example.yaml | 3 +++ lib/config.js | 9 ++++++++- lib/config_def.yaml | 18 ++++++++++++++++++ lib/config_gen.js | 16 ++++++++++++++++ lib/index.js | 16 +++++++++++++++- lib/st-local.js | 27 ++++++++++++++++++++++----- lib/storage.js | 13 +++++++------ 9 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 lib/config_def.yaml create mode 100644 lib/config_gen.js diff --git a/.gitignore b/.gitignore index 82259fe76..980bbc020 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules package.json +npm-debug.log diff --git a/bin/sinopia b/bin/sinopia index 43eb764c0..14a007b11 100755 --- a/bin/sinopia +++ b/bin/sinopia @@ -1,45 +1,31 @@ #!/usr/bin/env node +var pkg_file = '../package.yaml'; var fs = require('fs'); var yaml = require('js-yaml'); var commander = require('commander'); -var pkg = yaml.safeLoad(fs.readFileSync('../package.yaml', 'utf8')); var server = require('../lib/index'); var crypto = require('crypto'); +var pkg = require(pkg_file); commander .option('-l, --listen <[host:]port>', 'host:port number to listen on (default: localhost:4873)', '4873') - .option('-s, --storage ', 'path to package cache (default: "~/.npmrepod")') - // todo: need something to do with invalid https certificate, but we just can't use http by default - .option('-u, --uplink ', 'parent registry (default: "https://registry.npmjs.org/")') - .option('-c, --config ', 'use this configuration file') + .option('-c, --config ', 'use this configuration file (default: ./config.yaml)') .version(pkg.version) .parse(process.argv); +var config; if (commander.config) { - var config = yaml.safeLoad(fs.readFileSync(commander.config, 'utf8')); + config = yaml.safeLoad(fs.readFileSync(commander.config, 'utf8')); } else { - var pass = crypto.randomBytes(8).toString('base64').replace(/[=+\/]/g, ''); - var config = { - users: { - admin: { - password: crypto.createHash('sha1').update(pass).digest('hex') - }, - }, - uplinks: { - npmjs: { - url: 'https://registry.npmjs.org/' - }, - }, - packages: { - '/.*/': { - publish: ['admin'], - access: ['all'], - proxy: ['npmjs'], - } - } + try { + config = yaml.safeLoad(fs.readFileSync('./config.yaml', 'utf8')); + } catch(err) { + var created_config = require('../lib/config_gen')(); + config = yaml.safeLoad(created_config.yaml); + console.log('starting with default config, use user: "%s", pass: "%s" to authenticate', created_config.user, created_config.pass); + fs.writeFileSync('./config.yaml', created_config.yaml); } - console.log('starting with default config, use user: "admin", pass: "%s" to authenticate', pass); } if (!config.user_agent) config.user_agent = 'Sinopia/'+pkg.version; diff --git a/config.example.yaml b/config.example.yaml index 2a76cf190..e8d24bf95 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -1,4 +1,7 @@ +# path to a directory with all packages +storage: ./storage + users: user1: # require('crypto').createHash('sha1').update('test').digest('hex') diff --git a/lib/config.js b/lib/config.js index 9a961b308..f52172d55 100644 --- a/lib/config.js +++ b/lib/config.js @@ -24,6 +24,7 @@ function Config(config) { var check_user_or_uplink = function(arg) { assert(arg !== 'all' || arg !== 'owner', '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; }; @@ -58,10 +59,16 @@ function Config(config) { for (var i in this.packages) { var check_userlist = function(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+/); + } + assert( typeof(hash[action]) === 'object' && Array.isArray(hash[action]) - , 'CONFIG: bad "'+i+'" package '+action+' description (array expected)'); + , 'CONFIG: bad "'+i+'" package '+action+' description (array or string expected)'); hash[action] = flatten(hash[action]); hash[action].forEach(function(user) { assert( diff --git a/lib/config_def.yaml b/lib/config_def.yaml new file mode 100644 index 000000000..b47e0ec17 --- /dev/null +++ b/lib/config_def.yaml @@ -0,0 +1,18 @@ +# path to a directory with all packages +storage: ./storage + +users: + admin: + # crypto.createHash('sha1').update(pass).digest('hex') + password: __PASSWORD__ + +uplinks: + npmjs: + url: https://registry.npmjs.org/ + +packages: + '/.*/': + publish: admin + access: all + proxy: npmjs + diff --git a/lib/config_gen.js b/lib/config_gen.js new file mode 100644 index 000000000..11df79074 --- /dev/null +++ b/lib/config_gen.js @@ -0,0 +1,16 @@ +var fs = require('fs'); +var crypto = require('crypto'); + +module.exports = function create_config() { + var pass = crypto.randomBytes(8).toString('base64').replace(/[=+\/]/g, ''); + var pass_digest = crypto.createHash('sha1').update(pass).digest('hex'); + var config = fs.readFileSync(require.resolve('./config_def.yaml'), 'utf8'); + config = config.replace('__PASSWORD__', pass_digest); + + return { + yaml: config, + user: 'admin', + pass: pass, + }; +} + diff --git a/lib/index.js b/lib/index.js index 9caa76936..a8d6d5164 100644 --- a/lib/index.js +++ b/lib/index.js @@ -55,9 +55,23 @@ module.exports = function(config_hash) { }); });*/ - app.get('/:package', can('access'), function(req, res, next) { + // TODO: anonymous user? + app.get('/:package/:version?', can('access'), function(req, res, next) { storage.get_package(req.params.package, function(err, info) { if (err) return next(err); + + // XXX: in some cases npm calls for /:package and for some cases + // for /:package/:version - should investigate that + if (req.params.version) { + if (info.versions[req.params.version] != null) { + info = info.versions[req.params.version]; + } else { + return next(new UError({ + status: 404, + msg: 'version not found: ' + req.params.version + })); + } + } res.send(info); }); }); diff --git a/lib/st-local.js b/lib/st-local.js index 127940abf..5c4e841dc 100644 --- a/lib/st-local.js +++ b/lib/st-local.js @@ -1,6 +1,7 @@ var storage = wrap(require('./drivers/fs')); var UError = require('./error').UserError; var info_file = 'package.json'; +var fs = require('fs'); function wrap(driver) { if (typeof(driver.create_json) !== 'function') { @@ -24,7 +25,21 @@ function wrap(driver) { return driver; } -module.exports.add_package = function(name, metadata, callback) { +function Storage(config) { + if (!(this instanceof Storage)) return new Storage(config); + this.config = config; + + try { + fs.mkdirSync(config.storage); + console.log('created new packages directory: ', config.storage); + } catch(err) { + if (err.code !== 'EEXIST') throw new Error(err); + } + + return this; +} + +Storage.prototype.add_package = function(name, metadata, callback) { storage.create_json(name + '/' + info_file, metadata, function(err) { if (err && err.code === 'EEXISTS') { return callback(new UError({ @@ -36,7 +51,7 @@ module.exports.add_package = function(name, metadata, callback) { }); } -module.exports.add_version = function(name, version, metadata, tag, callback) { +Storage.prototype.add_version = function(name, version, metadata, tag, callback) { storage.read_json(name + '/' + info_file, function(err, data) { // TODO: race condition if (err) return callback(err); @@ -53,7 +68,7 @@ module.exports.add_version = function(name, version, metadata, tag, callback) { }); } -module.exports.add_tarball = function(name, filename, stream, callback) { +Storage.prototype.add_tarball = function(name, filename, stream, callback) { if (name === info_file) { return callback(new UError({ status: 403, @@ -81,7 +96,7 @@ module.exports.add_tarball = function(name, filename, stream, callback) { }); } -module.exports.get_tarball = function(name, filename, callback) { +Storage.prototype.get_tarball = function(name, filename, callback) { storage.read(name + '/' + filename, function(err) { if (err && err.code === 'ENOENT') { return callback(new UError({ @@ -93,7 +108,7 @@ module.exports.get_tarball = function(name, filename, callback) { }); } -module.exports.get_package = function(name, callback) { +Storage.prototype.get_package = function(name, callback) { storage.read_json(name + '/' + info_file, function(err) { if (err && err.code === 'ENOENT') { return callback(new UError({ @@ -105,3 +120,5 @@ module.exports.get_package = function(name, callback) { }); } +module.exports = Storage; + diff --git a/lib/storage.js b/lib/storage.js index 44740d538..ed9a9134b 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -1,7 +1,7 @@ var async = require('async'); var semver = require('semver'); var UError = require('./error').UserError; -var local = require('./st-local'); +var Local = require('./st-local'); var Proxy = require('./st-proxy'); var utils = require('./utils'); @@ -13,28 +13,29 @@ function Storage(config) { for (var p in config.uplinks) { this.uplinks[p] = new Proxy(p, config); } + this.local = new Local(config); return this; } Storage.prototype.add_package = function(name, metadata, callback) { - local.add_package(name, metadata, callback); + this.local.add_package(name, metadata, callback); } Storage.prototype.add_version = function(name, version, metadata, tag, callback) { - local.add_version(name, version, metadata, tag, callback); + this.local.add_version(name, version, metadata, tag, callback); } Storage.prototype.add_tarball = function(name, filename, stream, callback) { - local.add_tarball(name, filename, stream, callback); + this.local.add_tarball(name, filename, stream, callback); } Storage.prototype.get_tarball = function(name, filename, callback) { - local.get_tarball(name, filename, callback); + this.local.get_tarball(name, filename, callback); } Storage.prototype.get_package = function(name, callback) { - var uplinks = [local]; + var uplinks = [this.local]; for (var i in this.uplinks) { if (this.config.allow_proxy(name, i)) { uplinks.push(this.uplinks[i]);