0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-01-06 22:40:26 -05:00

basic support for unpublishing individual versions (local only)

This commit is contained in:
Alex Kocharin 2013-10-23 10:15:17 +04:00
parent 6ae26226eb
commit dafcf8647c
4 changed files with 117 additions and 20 deletions

View file

@ -171,48 +171,44 @@ module.exports = function(config_hash) {
// publishing a package // publishing a package
app.put('/:package/:_rev?/:revision?', can('publish'), media('application/json'), expect_json, function(req, res, next) { app.put('/:package/:_rev?/:revision?', can('publish'), media('application/json'), expect_json, function(req, res, next) {
if (req.params._rev != null && req.params._rev != '-rev') return next('route'); if (req.params._rev != null && req.params._rev != '-rev') return next('route')
var name = req.params.package; var name = req.params.package
if (Object.keys(req.body).length == 1 && utils.is_object(req.body.users)) { if (Object.keys(req.body).length == 1 && utils.is_object(req.body.users)) {
return next(new UError({ return next(new UError({
// 501 status is more meaningful, but npm doesn't show error message for 5xx // 501 status is more meaningful, but npm doesn't show error message for 5xx
status: 404, status: 404,
msg: 'npm star|unstar calls are not implemented', msg: 'npm star|unstar calls are not implemented',
})); }))
} }
try { try {
var metadata = utils.validate_metadata(req.body, name); var metadata = utils.validate_metadata(req.body, name)
} catch(err) { } catch(err) {
return next(new UError({ return next(new UError({
status: 422, status: 422,
msg: 'bad incoming package data', msg: 'bad incoming package data',
})); }))
} }
if (req.params._rev) { if (req.params._rev) {
return next(new UError({ storage.change_package(name, metadata, req.params.revision, function(err) {
status: 404, if (err) return next(err)
msg: 'unimplemented yet, work in progress', res.status(201)
}));
/* storage.change_package(name, metadata, req.params.revision, function(err) {
if (err) return next(err);
res.status(201);
return res.send({ return res.send({
ok: 'package changed' ok: 'package changed'
}); })
});*/ })
} else { } else {
storage.add_package(name, metadata, function(err) { storage.add_package(name, metadata, function(err) {
if (err) return next(err); if (err) return next(err)
res.status(201); res.status(201);
return res.send({ return res.send({
ok: 'created new package' ok: 'created new package'
}); })
}); })
} }
}); })
// unpublishing an entire package // unpublishing an entire package
app.delete('/:package/-rev/*', can('publish'), function(req, res, next) { app.delete('/:package/-rev/*', can('publish'), function(req, res, next) {
@ -225,6 +221,17 @@ module.exports = function(config_hash) {
}); });
}); });
// removing a tarball
app.delete('/:package/-/:filename/-rev/:revision', can('publish'), function(req, res, next) {
storage.remove_tarball(req.params.package, req.params.filename, req.params.revision, function(err) {
if (err) return next(err);
res.status(201);
return res.send({
ok: 'tarball removed'
});
});
});
// uploading package tarball // uploading package tarball
app.put('/:package/-/:filename/*', can('publish'), media('application/octet-stream'), function(req, res, next) { app.put('/:package/-/:filename/*', can('publish'), media('application/octet-stream'), function(req, res, next) {
var name = req.params.package; var name = req.params.package;

View file

@ -61,6 +61,7 @@ Storage.prototype.add_package = function(name, metadata, callback) {
Storage.prototype.remove_package = function(name, callback) { Storage.prototype.remove_package = function(name, callback) {
var self = this var self = this
self.logger.info({name: name}, 'unpublishing @{name} (all)')
self.storage.read_json(name + '/' + info_file, function(err, data) { self.storage.read_json(name + '/' + info_file, function(err, data) {
if (err) { if (err) {
if (err.code === 'ENOENT') { if (err.code === 'ENOENT') {
@ -215,6 +216,57 @@ Storage.prototype.add_version = function(name, version, metadata, tag, callback)
}, callback) }, callback)
} }
// currently supports unpublishing only
Storage.prototype.change_package = function(name, metadata, revision, callback) {
var self = this
if (!utils.is_object(metadata.versions) || !utils.is_object(metadata['dist-tags'])) {
return callback(new UError({
status: 422,
msg: 'bad data',
}))
}
self.update_package(name, function updater(data, cb) {
for (var ver in data.versions) {
if (metadata.versions[ver] == null) {
self.logger.info({name: name, version: ver}, 'unpublishing @{name}@@{version}')
delete data.versions[ver]
for (var file in data._attachments) {
if (data._attachments[file].version === ver) {
delete data._attachments[file].version
}
}
}
}
data['dist-tags'] = metadata['dist-tags']
cb()
}, function(err) {
if (err) return callback(err)
callback()
})
}
Storage.prototype.remove_tarball = function(name, filename, revision, callback) {
var self = this
self.update_package(name, function updater(data, cb) {
if (data._attachments[filename]) {
delete data._attachments[filename]
cb()
} else {
cb(new UError({
status: 404,
msg: 'no such file available',
}))
}
}, function(err) {
if (err) return callback(err)
self.storage.unlink(name + '/' + filename, callback)
})
}
Storage.prototype.add_tarball = function(name, filename) { Storage.prototype.add_tarball = function(name, filename) {
var stream = new mystreams.UploadTarballStream() var stream = new mystreams.UploadTarballStream()
, _transform = stream._transform , _transform = stream._transform
@ -411,7 +463,7 @@ Storage.prototype._write_package = function(name, json, callback) {
// calculate revision a la couchdb // calculate revision a la couchdb
if (typeof(json._rev) !== 'string') json._rev = '0-0000000000000000' if (typeof(json._rev) !== 'string') json._rev = '0-0000000000000000'
var rev = json._rev.split('-') var rev = json._rev.split('-')
json._rev = ((+rev[0] || 0) + 1) + '-' + crypto.pseudoRandomBytes(16).toString('hex') json._rev = ((+rev[0] || 0) + 1) + '-' + crypto.pseudoRandomBytes(8).toString('hex')
this.storage.write_json(name + '/' + info_file, json, callback) this.storage.write_json(name + '/' + info_file, json, callback)
} }

View file

@ -132,6 +132,24 @@ Storage.prototype.add_version = function(name, version, metadata, tag, callback)
}); });
} }
//
// Change an existing package (i.e. unpublish one version)
//
// Function changes a package info from local storage and all uplinks with
// write access.
//
// TODO: currently it works only locally
//
// TODO: if a package is uploaded to uplink1, but upload to uplink2 fails,
// we report failure, but package is not removed from uplink1. This might
// require manual intervention.
//
// Used storages: local (write) && uplinks (proxy_publish, write)
//
Storage.prototype.change_package = function(name, metadata, revision, callback) {
return this.local.change_package(name, metadata, revision, callback)
}
// //
// Remove a package from a system // Remove a package from a system
// //
@ -147,7 +165,26 @@ Storage.prototype.add_version = function(name, version, metadata, tag, callback)
// Used storages: local (write) && uplinks (proxy_publish, write) // Used storages: local (write) && uplinks (proxy_publish, write)
// //
Storage.prototype.remove_package = function(name, callback) { Storage.prototype.remove_package = function(name, callback) {
return this.local.remove_package(name, callback); return this.local.remove_package(name, callback)
}
//
// Remove a tarball from a system
//
// Function removes a tarball from local storage and all uplinks with
// write access. Tarball in question should not be linked to in any existing
// versions, i.e. package version should be unpublished first.
//
// TODO: currently it works only locally
//
// TODO: if a package is uploaded to uplink1, but upload to uplink2 fails,
// we report failure, but package is not removed from uplink1. This might
// require manual intervention.
//
// Used storages: local (write) && uplinks (proxy_publish, write)
//
Storage.prototype.remove_tarball = function(name, filename, revision, callback) {
return this.local.remove_tarball(name, filename, revision, callback)
} }
// //

View file

@ -10,6 +10,7 @@ module.exports.validate_name = function(name) {
name !== encodeURIComponent(name) || name !== encodeURIComponent(name) ||
name.toLowerCase() === "node_modules" || name.toLowerCase() === "node_modules" ||
name.toLowerCase() === "__proto__" || name.toLowerCase() === "__proto__" ||
name.toLowerCase() === "package.json" ||
name.toLowerCase() === "favicon.ico" name.toLowerCase() === "favicon.ico"
) { ) {
return false; return false;