diff --git a/lib/fs-storage.js b/lib/fs-storage.js index bfb417d9d..62fd77973 100644 --- a/lib/fs-storage.js +++ b/lib/fs-storage.js @@ -15,7 +15,7 @@ function make_directories(dest, cb) { }); } -function write_file(dest, data, cb) { +function write(dest, data, cb) { var safe_write = function(cb) { fs.writeFile(dest, data, cb); } @@ -34,14 +34,14 @@ function write_file(dest, data, cb) { function create(name, contents, callback) { fs.exists(name, function(exists) { if (exists) return callback(new Error({code: 'EEXISTS'})); - write_file(name, contents, callback); + write(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); + write(name, contents, callback); }); } @@ -86,5 +86,13 @@ Storage.prototype.update_json = function(name, value, cb) { update(this.path + '/' + name, JSON.stringify(value), cb); } +Storage.prototype.write = function(name, value, cb) { + write(this.path + '/' + name, value, cb); +} + +Storage.prototype.write_json = function(name, value, cb) { + write(this.path + '/' + name, JSON.stringify(value), cb); +} + module.exports = Storage; diff --git a/lib/index.js b/lib/index.js index 73f3e824a..5175eb306 100644 --- a/lib/index.js +++ b/lib/index.js @@ -59,6 +59,7 @@ module.exports = function(config_hash) { app.get('/:package/:version?', can('access'), function(req, res, next) { storage.get_package(req.params.package, function(err, info) { if (err) return next(err); + info = utils.filter_tarball_urls(info, req, config); // XXX: in some cases npm calls for /:package and for some cases // for /:package/:version - should investigate that diff --git a/lib/st-local.js b/lib/st-local.js index b84034c39..46185cb33 100644 --- a/lib/st-local.js +++ b/lib/st-local.js @@ -1,3 +1,4 @@ +var semver = require('semver'); var fs_storage = require('./fs-storage'); var UError = require('./error').UserError; var info_file = 'package.json'; @@ -10,8 +11,17 @@ function Storage(config) { return this; } +// returns the minimal package file +function get_boilerplate(name) { + return { + name: name, + versions: {}, + 'dist-tags': {}, + }; +} + Storage.prototype.add_package = function(name, metadata, callback) { - this.storage.create_json(name + '/' + info_file, metadata, function(err) { + this.storage.create_json(name + '/' + info_file, get_boilerplace(name), function(err) { if (err && err.code === 'EEXISTS') { return callback(new UError({ status: 409, @@ -22,10 +32,59 @@ Storage.prototype.add_package = function(name, metadata, callback) { }); } -Storage.prototype.add_version = function(name, version, metadata, tag, callback) { +Storage.prototype._read_create_package = function(name, callback) { var self = this; self.storage.read_json(name + '/' + info_file, function(err, data) { // TODO: race condition + if (err) { + if (err.code === 'ENOENT') { + // if package doesn't exist, we create it here + data = get_boilerplate(name); + } else { + return callback(err); + } + } + callback(null, data); + }); +} + +// synchronize remote package info with the local one +// TODO: readfile called twice +Storage.prototype.update_versions = function(name, newdata, callback) { + var self = this; + self._read_create_package(name, function(err, data) { + if (err) return callback(err); + + var change = false; + for (var ver in newdata.versions) { + if (data.versions[ver] == null) { + change = true; + data.versions[ver] = newdata.versions[ver]; + } + } + for (var tag in newdata['dist-tags']) { + // if tag is updated to reference latter version, that's fine + var need_change = + (data['dist-tags'][tag] == null) || + (!semver.gt(newdata['dist-tags'], data['dist-tags'][tag])); + + if (need_change) { + change = true; + data['dist-tags'][tag] = newdata['dist-tags'][tag]; + } + } + + if (change) { + self.storage.write_json(name + '/' + info_file, data, callback); + } else { + callback(); + } + }); +} + +Storage.prototype.add_version = function(name, version, metadata, tag, callback) { + var self = this; + self._read_create_package(name, function(err, data) { if (err) return callback(err); if (data.versions[version] != null) { diff --git a/lib/storage.js b/lib/storage.js index ea0647af0..fae4e3c78 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -67,10 +67,27 @@ Storage.prototype.add_tarball = function(name, filename, stream, callback) { } Storage.prototype.get_tarball = function(name, filename, callback) { - this.local.get_tarball(name, filename, callback); + // if someone requesting tarball, it means that we should already have some + // information about it, so fetching package info is unnecessary + + // trying local first + this.local.get_tarball(name, filename, function(err, results) { + if (err && err.status !== 404) return callback(err); + if (!err && results != null) return callback(err, results); + + +return callback(err, results); +/*TODO:// local reported 404 + this.local.get_package(name, function(err, info) { + if (err) return callback(err); + + + });*/ + }); } Storage.prototype.get_package = function(name, callback) { + var self = this; var uplinks = [this.local]; for (var i in this.uplinks) { if (this.config.allow_proxy(name, i)) { @@ -124,6 +141,8 @@ Storage.prototype.get_package = function(name, callback) { })); } callback(null, result); + + self.local.update_versions(name, result, function(){}); }); } diff --git a/lib/utils.js b/lib/utils.js index 8f5c6f4a0..2c3f7d9dd 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,4 +1,5 @@ var assert = require('assert'); +var URL = require('url'); // from normalize-package-data/lib/fixer.js module.exports.validate_name = function(name) { @@ -35,3 +36,35 @@ module.exports.validate_metadata = function(object, name) { return object; } +module.exports.filter_tarball_urls = function(pkg, req, config) { + function filter(_url) { + if (!req.headers.host) return _url; + + var url = URL.parse(_url); + if (config.url_prefix != null) { + var result = config.url_prefix.replace(/\/$/, ''); + } else { + var result = req.protocol + '://' + req.headers.host; + } + + path = url.path.replace(/^\//, '').split('/'); + if (path.length >= 3 && path[path.length-2] === '-') { + result += '/' + path[path.length-3]; // package name + result += '/' + path[path.length-2]; // "-" + result += '/' + path[path.length-1]; // tarball name + return result; + } else { + // weird url, just return it + return _url; + } + } + + for (var ver in pkg.versions) { + if (pkg.versions[ver].dist != null + && pkg.versions[ver].dist.tarball != null) { + pkg.versions[ver].dist.tarball = filter(pkg.versions[ver].dist.tarball); + } + } + return pkg; +} +