config file changes

This commit is contained in:
Alex Kocharin 2013-06-13 18:21:14 +04:00
parent dd1bd01b2e
commit a8fa475dc1
9 changed files with 102 additions and 39 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules node_modules
package.json package.json
npm-debug.log

View File

@ -1,45 +1,31 @@
#!/usr/bin/env node #!/usr/bin/env node
var pkg_file = '../package.yaml';
var fs = require('fs'); var fs = require('fs');
var yaml = require('js-yaml'); var yaml = require('js-yaml');
var commander = require('commander'); var commander = require('commander');
var pkg = yaml.safeLoad(fs.readFileSync('../package.yaml', 'utf8'));
var server = require('../lib/index'); var server = require('../lib/index');
var crypto = require('crypto'); var crypto = require('crypto');
var pkg = require(pkg_file);
commander commander
.option('-l, --listen <[host:]port>', 'host:port number to listen on (default: localhost:4873)', '4873') .option('-l, --listen <[host:]port>', 'host:port number to listen on (default: localhost:4873)', '4873')
.option('-s, --storage <path>', 'path to package cache (default: "~/.npmrepod")') .option('-c, --config <config.yaml>', 'use this configuration file (default: ./config.yaml)')
// todo: need something to do with invalid https certificate, but we just can't use http by default
.option('-u, --uplink <url>', 'parent registry (default: "https://registry.npmjs.org/")')
.option('-c, --config <file.yaml>', 'use this configuration file')
.version(pkg.version) .version(pkg.version)
.parse(process.argv); .parse(process.argv);
var config;
if (commander.config) { if (commander.config) {
var config = yaml.safeLoad(fs.readFileSync(commander.config, 'utf8')); config = yaml.safeLoad(fs.readFileSync(commander.config, 'utf8'));
} else { } else {
var pass = crypto.randomBytes(8).toString('base64').replace(/[=+\/]/g, ''); try {
var config = { config = yaml.safeLoad(fs.readFileSync('./config.yaml', 'utf8'));
users: { } catch(err) {
admin: { var created_config = require('../lib/config_gen')();
password: crypto.createHash('sha1').update(pass).digest('hex') 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);
uplinks: {
npmjs: {
url: 'https://registry.npmjs.org/'
},
},
packages: {
'/.*/': {
publish: ['admin'],
access: ['all'],
proxy: ['npmjs'],
}
}
} }
console.log('starting with default config, use user: "admin", pass: "%s" to authenticate', pass);
} }
if (!config.user_agent) config.user_agent = 'Sinopia/'+pkg.version; if (!config.user_agent) config.user_agent = 'Sinopia/'+pkg.version;

View File

@ -1,4 +1,7 @@
# path to a directory with all packages
storage: ./storage
users: users:
user1: user1:
# require('crypto').createHash('sha1').update('test').digest('hex') # require('crypto').createHash('sha1').update('test').digest('hex')

View File

@ -24,6 +24,7 @@ function Config(config) {
var check_user_or_uplink = function(arg) { var check_user_or_uplink = function(arg) {
assert(arg !== 'all' || arg !== 'owner', 'CONFIG: reserved user/uplink name: ' + 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); assert(users[arg] == null, 'CONFIG: duplicate user/uplink name: ' + arg);
users[arg] = true; users[arg] = true;
}; };
@ -58,10 +59,16 @@ function Config(config) {
for (var i in this.packages) { for (var i in this.packages) {
var check_userlist = function(i, hash, action) { var check_userlist = function(i, hash, action) {
if (hash[action] == null) 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( assert(
typeof(hash[action]) === 'object' && typeof(hash[action]) === 'object' &&
Array.isArray(hash[action]) 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] = flatten(hash[action]);
hash[action].forEach(function(user) { hash[action].forEach(function(user) {
assert( assert(

18
lib/config_def.yaml Normal file
View File

@ -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

16
lib/config_gen.js Normal file
View File

@ -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,
};
}

View File

@ -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) { storage.get_package(req.params.package, function(err, info) {
if (err) return next(err); 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); res.send(info);
}); });
}); });

View File

@ -1,6 +1,7 @@
var storage = wrap(require('./drivers/fs')); var storage = wrap(require('./drivers/fs'));
var UError = require('./error').UserError; var UError = require('./error').UserError;
var info_file = 'package.json'; var info_file = 'package.json';
var fs = require('fs');
function wrap(driver) { function wrap(driver) {
if (typeof(driver.create_json) !== 'function') { if (typeof(driver.create_json) !== 'function') {
@ -24,7 +25,21 @@ function wrap(driver) {
return 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) { storage.create_json(name + '/' + info_file, metadata, function(err) {
if (err && err.code === 'EEXISTS') { if (err && err.code === 'EEXISTS') {
return callback(new UError({ 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) { storage.read_json(name + '/' + info_file, function(err, data) {
// TODO: race condition // TODO: race condition
if (err) return callback(err); 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) { if (name === info_file) {
return callback(new UError({ return callback(new UError({
status: 403, 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) { storage.read(name + '/' + filename, function(err) {
if (err && err.code === 'ENOENT') { if (err && err.code === 'ENOENT') {
return callback(new UError({ 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) { storage.read_json(name + '/' + info_file, function(err) {
if (err && err.code === 'ENOENT') { if (err && err.code === 'ENOENT') {
return callback(new UError({ return callback(new UError({
@ -105,3 +120,5 @@ module.exports.get_package = function(name, callback) {
}); });
} }
module.exports = Storage;

View File

@ -1,7 +1,7 @@
var async = require('async'); var async = require('async');
var semver = require('semver'); var semver = require('semver');
var UError = require('./error').UserError; var UError = require('./error').UserError;
var local = require('./st-local'); var Local = require('./st-local');
var Proxy = require('./st-proxy'); var Proxy = require('./st-proxy');
var utils = require('./utils'); var utils = require('./utils');
@ -13,28 +13,29 @@ function Storage(config) {
for (var p in config.uplinks) { for (var p in config.uplinks) {
this.uplinks[p] = new Proxy(p, config); this.uplinks[p] = new Proxy(p, config);
} }
this.local = new Local(config);
return this; return this;
} }
Storage.prototype.add_package = function(name, metadata, callback) { 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) { 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) { 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) { 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) { Storage.prototype.get_package = function(name, callback) {
var uplinks = [local]; var uplinks = [this.local];
for (var i in this.uplinks) { for (var i in this.uplinks) {
if (this.config.allow_proxy(name, i)) { if (this.config.allow_proxy(name, i)) {
uplinks.push(this.uplinks[i]); uplinks.push(this.uplinks[i]);