0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-16 21:56:25 -05:00

working on drivers...

This commit is contained in:
Alex Kocharin 2013-06-01 02:57:28 +04:00
parent 0bb1b34b7d
commit d89494b533
8 changed files with 333 additions and 84 deletions

55
lib/drivers/fs.js Normal file
View file

@ -0,0 +1,55 @@
var fs = require('fs');
var Path = require('path');
function make_directories(dest, cb) {
var dir = Path.dirname(dest);
if (dir === '.' || dir === '..') return cb();
fs.mkdir(dir, function(err) {
if (err && err.code === 'ENOENT') {
make_directories(dir, function() {
fs.mkdir(dir, cb);
})
} else {
cb();
}
});
}
function write_file(dest, data, cb) {
var safe_write = function(cb) {
fs.writeFile(dest, data, cb);
}
safe_write(function(err) {
if (err && err.code === 'ENOENT') {
make_directories(dest, function() {
safe_write(cb);
})
} else {
cb(err);
}
});
}
function create(name, contents, callback) {
fs.exists(name, function(exists) {
if (exists) return callback(new Error({code: 'EEXISTS'}));
write_file(name, contents, callback);
});
}
function update(name, contents, callback) {
fs.exists(name, function(exists) {
if (!exists) return callback(new Error({code: 'ENOENT'}));
write_file(name, contents, callback);
});
}
function read(name, callback) {
fs.readFile(name, callback);
}
module.exports.read = read;
module.exports.create = create;
module.exports.update = update;

32
lib/drivers/memory.js Normal file
View file

@ -0,0 +1,32 @@
var store = {};
function create(name, contents, callback) {
if (store[name] != null) {
return callback(new Error({code: 'EEXISTS'}));
}
store[name] = contents;
callback();
}
function update(name, contents, callback) {
if (store[name] == null) {
return callback(new Error({code: 'ENOENT'}));
}
store[name] = contents;
callback();
}
function read(name, callback) {
if (store[name] == null) {
return callback(new Error({code: 'ENOENT'}));
}
callback(null, store[name]);
}
module.exports.read_json = read;
module.exports.read = read;
module.exports.create_json = create;
module.exports.create = create;
module.exports.update_json = update;
module.exports.update = update;

51
lib/error.js Normal file
View file

@ -0,0 +1,51 @@
var util = require('util');
function parse_error_params(params, status, msg) {
if (typeof(params) === 'string') {
return {
msg: params,
status: status,
};
} else if (typeof(params) === 'number') {
return {
msg: msg,
status: params,
};
} else if (typeof(params) === 'object' && params != null) {
if (params.msg == null) params.msg = msg;
if (params.status == null) params.status = status;
return params;
} else {
return {
msg: msg,
status: status,
};
}
}
/*
* Errors caused by malfunctioned code
*/
var AppError = function(params, constr) {
Error.captureStackTrace(this, constr || this);
params = parse_error_params(params, 500, 'Internal server error');
this.msg = params.msg;
this.status = params.status;
};
util.inherits(AppError, Error);
AppError.prototype.name = 'Application Error';
/*
* Errors caused by wrong request
*/
var UserError = function(params, constr) {
params = parse_error_params(params, 404, 'The requested resource was not found');
this.msg = params.msg;
this.status = params.status;
};
util.inherits(UserError, Error);
UserError.prototype.name = 'User Error';
module.exports.AppError = AppError;
module.exports.UserError = UserError;

View file

@ -9,18 +9,42 @@ function validate_name(req, res, next, value, name) {
req.params.package = String(req.params.package); req.params.package = String(req.params.package);
next(); next();
} else { } else {
res.status(403); next(new Error({
return res.send({ status: 403,
error: 'invalid package name' msg: 'invalid package name',
}); }));
} }
}; };
function media(expect) {
return function(req, res, next) {
if (req.headers['content-type'] !== expect) {
next(new Error({
status: 415,
msg: 'wrong content-type, we expect '+expect,
}));
} else {
next();
}
}
}
function expect_json(req, res, next) {
if (typeof(req.body) !== 'object') {
return next({
status: 400,
msg: 'can\'t parse incoming json',
});
}
next();
}
module.exports = function(settings) { module.exports = function(settings) {
var app = express(); var app = express();
app.use(express.logger()); app.use(express.logger());
app.use(express.bodyParser()); app.use(express.bodyParser());
app.param('package', validate_name); app.param('package', validate_name);
app.param('filename', validate_name);
/* app.get('/', function(req, res) { /* app.get('/', function(req, res) {
res.send({ res.send({
@ -28,16 +52,24 @@ module.exports = function(settings) {
}); });
});*/ });*/
app.get('/:package', function(req, res) { app.get('/:package', 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);
if (!info) { res.send(info);
res.status(404); });
return res.send({ });
error: 'package not found'
app.get('/:package/-/:filename', function(req, res, next) {
storage.get_tarball(req.params.package, req.params.filename, function(err, stream) {
if (err) return next(err);
if (!stream) {
return next({
status: 404,
msg: 'package not found'
}); });
} }
res.send(info); res.header('content-type', 'application/octet-stream');
res.send(stream);
}); });
}); });
@ -56,77 +88,67 @@ module.exports = function(settings) {
}); });
// publishing a package // publishing a package
app.put('/:package', function(req, res, next) { app.put('/:package', media('application/json'), expect_json, function(req, res, next) {
var name = req.params.package; var name = req.params.package;
if (req.headers['content-type'] !== 'application/json') { try {
res.status(415); var metadata = utils.validate_metadata(req.body, name);
return res.send({ } catch(err) {
error: 'wrong content-type, we expect application/json', return next({
}); status: 422,
} msg: 'bad incoming package data',
if (typeof(req.body) !== 'object') {
res.status(400);
return res.send({
error: 'can\'t parse incoming json',
}); });
} }
storage.create_package(name, req.body, function(err, created) { storage.add_package(name, metadata, function(err) {
if (err) return next(err); if (err) return next(err);
if (created) { res.status(201);
res.status(201); return res.send({
return res.send({ ok: 'created new package'
ok: 'created new package' });
});
} else {
res.status(409);
return res.send({
error: 'package already exists'
});
}
}); });
}); });
// uploading package tarball // uploading package tarball
app.put('/:package/-/:filename/*', function(req, res, next) { app.put('/:package/-/:filename/*', media('application/octet-stream'), function(req, res, next) {
res.status(201); var name = req.params.package;
return res.send({
ok: 'tarball uploaded successfully' storage.add_tarball(name, req.params.filename, req, function(err) {
if (err) return next(err);
res.status(201);
return res.send({
ok: 'tarball uploaded successfully'
});
}); });
}); });
// adding a version // adding a version
app.put('/:package/:version/-tag/:tag', function(req, res, next) { app.put('/:package/:version/-tag/:tag', media('application/json'), expect_json, function(req, res, next) {
var name = req.params.package; var name = req.params.package;
if (req.headers['content-type'] !== 'application/json') { var version = req.params.version;
res.status(415); var tag = req.params.tag;
return res.send({
error: 'wrong content-type, we expect application/json',
});
}
if (typeof(req.body) !== 'object') {
res.status(400);
return res.send({
error: 'can\'t parse incoming json',
});
}
storage.add_version(req.params.package, req.params.version, req.body, function(err, created) { storage.add_version(name, version, req.body, tag, function(err) {
if (err) return next(err); if (err) return next(err);
if (created) { res.status(201);
res.status(201); return res.send({
return res.send({ ok: 'package published'
ok: 'package published' });
});
} else {
res.status(409);
return res.send({
error: 'this version already exists'
});
}
}); });
}); });
app.use(app.router);
app.use(function(err, req, res, next) {
if (err.status && err.msg && err.status >= 400 && err.status < 600) {
res.status(err.status);
res.send({error: err.msg});
} else {
console.log(err);
console.log(err.stack);
res.status(500);
res.send({error: 'internal server error'});
}
});
return app; return app;
}; };

View file

@ -1,5 +0,0 @@
module.exports.add_package = function(name, version, tarball) {
}

View file

@ -1,27 +1,100 @@
var packages = {}; var storage = wrap(require('./drivers/fs'));
var UError = require('./error').UserError;
var info_file = '.package.json';
module.exports.create_package = function(name, metadata, callback) { function wrap(driver) {
if (packages[name] == null) { if (typeof(driver.create_json) !== 'function') {
packages[name] = { driver.create_json = function(name, value, cb) {
meta: metadata, driver.create(name, JSON.stringify(value), cb);
versions: {},
}; };
callback(null, true);
} else {
callback(null, false);
} }
if (typeof(driver.update_json) !== 'function') {
driver.update_json = function(name, value, cb) {
driver.update(name, JSON.stringify(value), cb);
};
}
if (typeof(driver.read_json) !== 'function') {
driver.read_json = function(name, cb) {
driver.read(name, function(err, res) {
if (err) return cb(err);
cb(null, JSON.parse(res));
});
};
}
return driver;
} }
module.exports.add_version = function(name, version, metadata, callback) { module.exports.add_package = function(name, metadata, callback) {
if (packages[name] == null) { storage.create_json(name + '/' + info_file, metadata, function(err) {
callback(null, false); if (err && err.code === 'EEXISTS') {
} else { return callback(new UError({
packages[name].versions[version] = metadata; status: 409,
callback(null, true); msg: 'this package is already present'
} }));
}
callback();
});
}
module.exports.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);
if (data.versions[version] != null) {
return callback(new UError({
status: 409,
msg: 'this version already present'
}));
}
data.versions[version] = metadata;
data['dist-tags'][tag] = version;
storage.update_json(name + '/' + info_file, data, callback);
});
}
module.exports.add_tarball = function(name, filename, stream, callback) {
var data = new Buffer(0);
stream.on('data', function(d) {
var tmp = data;
data = new Buffer(tmp.length+d.length);
tmp.copy(data, 0);
d.copy(data, tmp.length);
});
stream.on('end', function(d) {
storage.create(name + '/' + filename, data, function(err) {
if (err && err.code === 'EEXISTS') {
return callback(new UError({
status: 409,
msg: 'this tarball is already present'
}));
}
callback.apply(null, arguments);
});
});
}
module.exports.get_tarball = function(name, filename, callback) {
storage.read(name + '/' + filename, function(err) {
if (err && err.code === 'ENOENT') {
return callback(new UError({
status: 404,
msg: 'no such package available'
}));
}
callback.apply(null, arguments);
});
} }
module.exports.get_package = function(name, callback) { module.exports.get_package = function(name, callback) {
callback(null, packages[name]); storage.read_json(name + '/' + info_file, function(err) {
if (err && err.code === 'ENOENT') {
return callback(new UError({
status: 404,
msg: 'no such package available'
}));
}
callback.apply(null, arguments);
});
} }

View file

@ -1,3 +1,4 @@
var assert = require('assert');
// from normalize-package-data/lib/fixer.js // from normalize-package-data/lib/fixer.js
module.exports.validate_name = function(name) { module.exports.validate_name = function(name) {
@ -14,3 +15,23 @@ module.exports.validate_name = function(name) {
} }
} }
function is_object(obj) {
return typeof(obj) === 'object' && !Array.isArray(obj);
}
module.exports.validate_metadata = function(object, name) {
assert(is_object(object));
assert.equal(object._id, name);
assert.equal(object.name, name);
if (!is_object(object['dist-tags'])) {
object['dist-tags'] = {};
}
if (!is_object(object['versions'])) {
object['versions'] = {};
}
return object;
}