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);
next();
} else {
res.status(403);
return res.send({
error: 'invalid package name'
});
next(new Error({
status: 403,
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) {
var app = express();
app.use(express.logger());
app.use(express.bodyParser());
app.param('package', validate_name);
app.param('filename', validate_name);
/* app.get('/', function(req, res) {
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) {
if (err) return next(err);
if (!info) {
res.status(404);
return res.send({
error: 'package not found'
res.send(info);
});
});
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
app.put('/:package', function(req, res, next) {
app.put('/:package', media('application/json'), expect_json, function(req, res, next) {
var name = req.params.package;
if (req.headers['content-type'] !== 'application/json') {
res.status(415);
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',
try {
var metadata = utils.validate_metadata(req.body, name);
} catch(err) {
return next({
status: 422,
msg: 'bad incoming package data',
});
}
storage.create_package(name, req.body, function(err, created) {
storage.add_package(name, metadata, function(err) {
if (err) return next(err);
if (created) {
res.status(201);
return res.send({
ok: 'created new package'
});
} else {
res.status(409);
return res.send({
error: 'package already exists'
});
}
res.status(201);
return res.send({
ok: 'created new package'
});
});
});
// uploading package tarball
app.put('/:package/-/:filename/*', function(req, res, next) {
res.status(201);
return res.send({
ok: 'tarball uploaded successfully'
app.put('/:package/-/:filename/*', media('application/octet-stream'), function(req, res, next) {
var name = req.params.package;
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
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;
if (req.headers['content-type'] !== 'application/json') {
res.status(415);
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',
});
}
var version = req.params.version;
var tag = req.params.tag;
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 (created) {
res.status(201);
return res.send({
ok: 'package published'
});
} else {
res.status(409);
return res.send({
error: 'this version already exists'
});
}
res.status(201);
return res.send({
ok: 'package published'
});
});
});
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;
};

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) {
if (packages[name] == null) {
packages[name] = {
meta: metadata,
versions: {},
function wrap(driver) {
if (typeof(driver.create_json) !== 'function') {
driver.create_json = function(name, value, cb) {
driver.create(name, JSON.stringify(value), cb);
};
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) {
if (packages[name] == null) {
callback(null, false);
} else {
packages[name].versions[version] = metadata;
callback(null, true);
}
module.exports.add_package = function(name, metadata, callback) {
storage.create_json(name + '/' + info_file, metadata, function(err) {
if (err && err.code === 'EEXISTS') {
return callback(new UError({
status: 409,
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) {
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
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;
}