index.jsView |
---|
3 | 3 | var path = require('path') |
4 | 4 | var fs = require('fs') |
5 | 5 | var crypto = require('crypto') |
6 | 6 | var pkg = require('./package') |
| 7 | +var semver = require('semver') |
| 8 | +var toPull = require('stream-to-pull-stream') |
| 9 | +var proc = require('child_process') |
| 10 | +var pull = require('pull-stream') |
7 | 11 | |
8 | 12 | function pullOnce(data) { |
9 | 13 | var ended |
10 | 14 | return function (abort, cb) { |
39 | 43 | } |
40 | 44 | } |
41 | 45 | } |
42 | 46 | |
| 47 | +function once(cb) { |
| 48 | + var done |
| 49 | + return function (err, result) { |
| 50 | + if (done) { |
| 51 | + if (err) console.trace(err) |
| 52 | + } else { |
| 53 | + done = true |
| 54 | + cb(err, result) |
| 55 | + } |
| 56 | + } |
| 57 | +} |
| 58 | + |
43 | 59 | function pkgLockToRegistryPkgs(pkgLock, wsPort) { |
44 | 60 | |
45 | 61 | var hasNonBlobUrl = false |
46 | 62 | var blobUrlRegex = new RegExp('^http://localhost:' + wsPort + '/blobs/get/&') |
208 | 224 | }) |
209 | 225 | })(0) |
210 | 226 | } |
211 | 227 | |
| 228 | +SsbNpmRegistryServer.prototype.getBlob = function (id, cb) { |
| 229 | + var blobs = this.sbot.blobs |
| 230 | + blobs.size(id, function (err, size) { |
| 231 | + if (typeof size === 'number') cb(null, blobs.get(id)) |
| 232 | + else blobs.want(id, function (err, got) { |
| 233 | + if (err) cb(err) |
| 234 | + else if (!got) cb('missing blob ' + id) |
| 235 | + else cb(null, blobs.get(id)) |
| 236 | + }) |
| 237 | + }) |
| 238 | +} |
| 239 | + |
212 | 240 | SsbNpmRegistryServer.prototype.blobDist = function (id) { |
213 | 241 | var m = /^&([^.]+)\.([a-z0-9]+)$/.exec(id) |
214 | 242 | if (!m) throw new Error('bad blob id: ' + id) |
215 | 243 | return { |
| 244 | + ssbBlobId: id, |
216 | 245 | integrity: m[2] + '-' + m[1], |
217 | 246 | tarball: 'http://localhost:' + this.wsPort + '/blobs/get/' + id |
218 | 247 | } |
219 | 248 | } |
327 | 356 | this.blobsToPush = [] |
328 | 357 | } |
329 | 358 | |
330 | 359 | Req.prototype.serve = function () { |
331 | | - |
| 360 | + console.log(this.req.method, this.req.url, this.req.socket.remoteAddress.replace(/^::ffff:/, '')) |
332 | 361 | var pathname = this.req.url.replace(/\?.*/, '') |
333 | 362 | var m |
334 | 363 | if (pathname === '/') return this.serveHome() |
335 | 364 | if (pathname === '/bootstrap') return this.serveBootstrap() |
413 | 442 | var self = this |
414 | 443 | var parts = pathname.split('/') |
415 | 444 | var pkgName = parts.shift() |
416 | 445 | if (parts[0] === '-rev') return this.respondError(501, 'Unpublish is not supported') |
| 446 | + var spec = parts.shift() |
| 447 | + try { spec = decodeURIComponent(spec) } finally {} |
417 | 448 | if (parts.length > 0) return this.respondError(404) |
418 | 449 | if (self.req.method === 'PUT') return self.publishPkg(pkgName) |
419 | 450 | var obj = { |
420 | 451 | _id: pkgName, |
424 | 455 | } |
425 | 456 | var oldest, newest |
426 | 457 | var getMention = self.server.getMentions({$prefix: 'npm:' + pkgName + ':'}) |
427 | 458 | getMention(null, function next(err, mention) { |
428 | | - if (err === true) return self.respond(200, obj) |
429 | | - if (err) return self.respondError(500, err.stack || err) |
| 459 | + if (err) return done(err === true ? null : err) |
430 | 460 | var data = decodeName(mention.name) |
431 | 461 | if (!data.version) return |
432 | 462 | if (data.distTag) obj['dist-tags'][data.distTag] = data.version |
433 | 463 | obj.versions[data.version] = { |
437 | 467 | dist: self.server.blobDist(mention.link) |
438 | 468 | } |
439 | 469 | getMention(null, next) |
440 | 470 | }) |
| 471 | + function done(err) { |
| 472 | + if (err) return self.respondError(500, err.stack || err) |
| 473 | + if (spec) { |
| 474 | + var version = obj['dist-tags'][spec] |
| 475 | + || semver.maxSatisfying(Object.keys(obj.versions), spec) |
| 476 | + obj = obj.versions[version] |
| 477 | + if (!obj) return self.respondError(404, 'version not found: ' + spec) |
| 478 | + } |
| 479 | + |
| 480 | + self.getPackageJsonFromTarballBlob(obj.dist.ssbBlobId, function (err, pkg) { |
| 481 | + if (err) return self.respondError(500, err.stack || err) |
| 482 | + if (pkg) { |
| 483 | + pkg.dist = obj.dist |
| 484 | + if (!pkg.author) pkg.author = obj.author |
| 485 | + obj = pkg |
| 486 | + } |
| 487 | + self.respond(200, obj) |
| 488 | + }) |
| 489 | + } |
441 | 490 | } |
442 | 491 | |
443 | 492 | Req.prototype.servePrebuild = function (name) { |
444 | 493 | var self = this |
586 | 635 | console.log(msgs.map(function (msg) { return msg.key }).join('\n')) |
587 | 636 | }) |
588 | 637 | }) |
589 | 638 | } |
| 639 | + |
| 640 | +Req.prototype.getPackageJsonFromTarballBlob = function (id, cb) { |
| 641 | + var self = this |
| 642 | + self.server.getBlob(id, function (err, readBlob) { |
| 643 | + if (err) return cb(err) |
| 644 | + cb = once(cb) |
| 645 | + var tar = proc.spawn('tar', ['-zxO', 'package/package.json'], { |
| 646 | + stdio: ['pipe', 'pipe', 'ignore'] |
| 647 | + }) |
| 648 | + tar.on('error', cb) |
| 649 | + tar.on('exit', function (code) { |
| 650 | + if (code) return cb(new Error('tar error: ' + code)) |
| 651 | + }) |
| 652 | + pull(readBlob, toPull(tar.stdin)) |
| 653 | + pull(toPull(tar.stdout), pull.collect(function (err, bufs) { |
| 654 | + if (err) return cb(err) |
| 655 | + var pkg |
| 656 | + try { pkg = JSON.parse(Buffer.concat(bufs)) } |
| 657 | + catch(e) { return cb(e) } |
| 658 | + cb(null, pkg) |
| 659 | + })) |
| 660 | + }) |
| 661 | +} |